From 61f04c1cb1cbad0cab9599fbb812a9ac175aca28 Mon Sep 17 00:00:00 2001 From: Iuri Matias Date: Thu, 8 Feb 2018 14:08:11 -0500 Subject: [PATCH] move execution txFormat to Remix-Lib --- remix-lib/index.js | 4 +- remix-lib/src/execution/txFormat.js | 248 ++++++++++++++++++++++++++++ 2 files changed, 251 insertions(+), 1 deletion(-) create mode 100644 remix-lib/src/execution/txFormat.js diff --git a/remix-lib/index.js b/remix-lib/index.js index 42f19e86a5..68574fd64d 100644 --- a/remix-lib/index.js +++ b/remix-lib/index.js @@ -20,6 +20,7 @@ var Storage = require('./src/storage') var EventsDecoder = require('./src/execution/eventsDecoder') var txExecution = require('./src/execution/txExecution') var txHelper = require('./src/execution/txHelper') +var txFormat = require('./src/execution/txFormat') var executionContext = require('./src/execution/execution-context') if (typeof (module) !== 'undefined' && typeof (module.exports) !== 'undefined') { @@ -59,7 +60,8 @@ function modules () { EventsDecoder: EventsDecoder, txExecution: txExecution, txHelper: txHelper, - executionContext: executionContext + executionContext: executionContext, + txFormat: txFormat } } } diff --git a/remix-lib/src/execution/txFormat.js b/remix-lib/src/execution/txFormat.js new file mode 100644 index 0000000000..a887850555 --- /dev/null +++ b/remix-lib/src/execution/txFormat.js @@ -0,0 +1,248 @@ +'use strict' +var ethJSABI = require('ethereumjs-abi') +var ethJSUtil = require('ethereumjs-util') +var BN = ethJSUtil.BN +var helper = require('./txHelper') +var TreeView = require('remix-debugger').ui.TreeView +var executionContext = require('./executionContext') + +module.exports = { + + /** + * build the transaction data + * + * @param {Object} function abi + * @param {Object} values to encode + * @param {String} contractbyteCode + */ + encodeData: function (funABI, values, contractbyteCode) { + var encoded + var encodedHex + try { + encoded = helper.encodeParams(funABI, values) + encodedHex = encoded.toString('hex') + } catch (e) { + return { error: 'cannot encode arguments' } + } + if (contractbyteCode) { + return { data: contractbyteCode + encodedHex } + } else { + return { data: Buffer.concat([helper.encodeFunctionId(funABI), encoded]).toString('hex') } + } + }, + + /** + * build the transaction data + * + * @param {String} contractName + * @param {Object} contract - abi definition of the current contract. + * @param {Object} contracts - map of all compiled contracts. + * @param {Bool} isConstructor - isConstructor. + * @param {Object} funAbi - abi definition of the function to call. null if building data for the ctor. + * @param {Object} params - input paramater of the function to call + * @param {Object} udapp - udapp + * @param {Function} callback - callback + * @param {Function} callbackStep - callbackStep + */ + buildData: function (contractName, contract, contracts, isConstructor, funAbi, params, udapp, callback, callbackStep) { + var funArgs = '' + var data = '' + var dataHex = '' + + if (params.indexOf('0x') === 0) { + dataHex = params.replace('0x', '') + data = Buffer.from(dataHex, 'hex') + } else { + try { + funArgs = JSON.parse('[' + params + ']') + } catch (e) { + callback('Error encoding arguments: ' + e) + return + } + if (!isConstructor || funArgs.length > 0) { + try { + data = helper.encodeParams(funAbi, funArgs) + dataHex = data.toString('hex') + } catch (e) { + callback('Error encoding arguments: ' + e) + return + } + } + if (data.slice(0, 9) === 'undefined') { + dataHex = data.slice(9) + } + if (data.slice(0, 2) === '0x') { + dataHex = data.slice(2) + } + } + var contractBytecode + if (isConstructor) { + contractBytecode = contract.evm.bytecode.object + var bytecodeToDeploy = contract.evm.bytecode.object + if (bytecodeToDeploy.indexOf('_') >= 0) { + this.linkBytecode(contract, contracts, udapp, (err, bytecode) => { + if (err) { + callback('Error deploying required libraries: ' + err) + } else { + bytecodeToDeploy = bytecode + dataHex + return callback(null, {dataHex: bytecodeToDeploy, funAbi, funArgs, contractBytecode, contractName: contractName}) + } + }, callbackStep) + return + } else { + dataHex = bytecodeToDeploy + dataHex + } + } else { + dataHex = Buffer.concat([helper.encodeFunctionId(funAbi), data]).toString('hex') + } + callback(null, { dataHex, funAbi, funArgs, contractBytecode, contractName: contractName }) + }, + + atAddress: function () {}, + + linkBytecode: function (contract, contracts, udapp, callback, callbackStep) { + if (contract.evm.bytecode.object.indexOf('_') < 0) { + return callback(null, contract.evm.bytecode.object) + } + var libraryRefMatch = contract.evm.bytecode.object.match(/__([^_]{1,36})__/) + if (!libraryRefMatch) { + return callback('Invalid bytecode format.') + } + var libraryName = libraryRefMatch[1] + // file_name:library_name + var libRef = libraryName.match(/(.*):(.*)/) + if (!libRef) { + return callback('Cannot extract library reference ' + libraryName) + } + if (!contracts[libRef[1]] || !contracts[libRef[1]][libRef[2]]) { + return callback('Cannot find library reference ' + libraryName) + } + var libraryShortName = libRef[2] + var library = contracts[libRef[1]][libraryShortName] + if (!library) { + return callback('Library ' + libraryName + ' not found.') + } + this.deployLibrary(libraryName, libraryShortName, library, contracts, udapp, (err, address) => { + if (err) { + return callback(err) + } + var hexAddress = address.toString('hex') + if (hexAddress.slice(0, 2) === '0x') { + hexAddress = hexAddress.slice(2) + } + contract.evm.bytecode.object = this.linkLibraryStandard(libraryShortName, hexAddress, contract) + contract.evm.bytecode.object = this.linkLibrary(libraryName, hexAddress, contract.evm.bytecode.object) + this.linkBytecode(contract, contracts, udapp, callback, callbackStep) + }, callbackStep) + }, + + deployLibrary: function (libraryName, libraryShortName, library, contracts, udapp, callback, callbackStep) { + var address = library.address + if (address) { + return callback(null, address) + } + var bytecode = library.evm.bytecode.object + if (bytecode.indexOf('_') >= 0) { + this.linkBytecode(library, contracts, udapp, (err, bytecode) => { + if (err) callback(err) + else this.deployLibrary(libraryName, libraryShortName, library, contracts, udapp, callback, callbackStep) + }, callbackStep) + } else { + callbackStep(`creation of library ${libraryName} pending...`) + var data = {dataHex: bytecode, funAbi: {type: 'constructor'}, funArgs: [], contractBytecode: bytecode, contractName: libraryShortName} + udapp.runTx({ data: data, useCall: false }, (err, txResult) => { + if (err) { + return callback(err) + } + var address = executionContext.isVM() ? txResult.result.createdAddress : txResult.result.contractAddress + library.address = address + callback(err, address) + }) + } + }, + + linkLibraryStandardFromlinkReferences: function (libraryName, address, bytecode, linkReferences) { + for (var file in linkReferences) { + for (var libName in linkReferences[file]) { + if (libraryName === libName) { + bytecode = this.setLibraryAddress(address, bytecode, linkReferences[file][libName]) + } + } + } + return bytecode + }, + + linkLibraryStandard: function (libraryName, address, contract) { + return this.linkLibraryStandardFromlinkReferences(libraryName, address, contract.evm.bytecode.object, contract.evm.bytecode.linkReferences) + }, + + setLibraryAddress: function (address, bytecodeToLink, positions) { + if (positions) { + for (var pos of positions) { + var regpos = bytecodeToLink.match(new RegExp(`(.{${2 * pos.start}})(.{${2 * pos.length}})(.*)`)) + if (regpos) { + bytecodeToLink = regpos[1] + address + regpos[3] + } + } + } + return bytecodeToLink + }, + + linkLibrary: function (libraryName, address, bytecodeToLink) { + var libLabel = '__' + libraryName + Array(39 - libraryName.length).join('_') + if (bytecodeToLink.indexOf(libLabel) === -1) return bytecodeToLink + + address = Array(40 - address.length + 1).join('0') + address + while (bytecodeToLink.indexOf(libLabel) >= 0) { + bytecodeToLink = bytecodeToLink.replace(libLabel, address) + } + return bytecodeToLink + }, + + decodeResponseToTreeView: function (response, fnabi) { + var treeView = new TreeView({ + extractData: (item, parent, key) => { + var ret = {} + if (BN.isBN(item)) { + ret.self = item.toString(10) + ret.children = [] + } else { + ret = treeView.extractDataDefault(item, parent, key) + } + return ret + } + }) + return treeView.render(this.decodeResponse(response, fnabi)) + }, + + decodeResponse: function (response, fnabi) { + // Only decode if there supposed to be fields + if (fnabi.outputs && fnabi.outputs.length > 0) { + try { + var i + + var outputTypes = [] + for (i = 0; i < fnabi.outputs.length; i++) { + outputTypes.push(fnabi.outputs[i].type) + } + + // decode data + var decodedObj = ethJSABI.rawDecode(outputTypes, response) + + // format decoded data + decodedObj = ethJSABI.stringify(outputTypes, decodedObj) + var json = {} + for (i = 0; i < outputTypes.length; i++) { + var name = fnabi.outputs[i].name + json[i] = outputTypes[i] + ': ' + (name ? name + ' ' + decodedObj[i] : decodedObj[i]) + } + + return json + } catch (e) { + return { error: 'Failed to decode output: ' + e } + } + } + return {} + } +} +