diff --git a/remix-lib/src/execution/txFormat.js b/remix-lib/src/execution/txFormat.js index 8010973c57..fd3a3904de 100644 --- a/remix-lib/src/execution/txFormat.js +++ b/remix-lib/src/execution/txFormat.js @@ -32,18 +32,143 @@ module.exports = { }, /** - * 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 {Function} callback - callback - * @param {Function} callbackStep - callbackStep - * @param {Function} callbackDeployLibrary - callbackDeployLibrary - */ + * encode function / constructor parameters + * + * @param {Object} params - input paramater of the function to call + * @param {Object} funAbi - abi definition of the function to call. null if building data for the ctor. + * @param {Function} callback - callback + */ + encodeParams: function (params, funAbi, callback) { + var data = '' + var dataHex = '' + var funArgs + if (params.indexOf('raw:0x') === 0) { + // in that case we consider that the input is already encoded and *does not* contain the method signature + dataHex = params.replace('raw:0x', '') + data = Buffer.from(dataHex, 'hex') + } else { + try { + params = params.replace(/(^|,\s+|,)(\d+)(\s+,|,|$)/g, '$1"$2"$3') // replace non quoted number by quoted number + params = params.replace(/(^|,\s+|,)(0[xX][0-9a-fA-F]+)(\s+,|,|$)/g, '$1"$2"$3') // replace non quoted hex string by quoted hex string + funArgs = JSON.parse('[' + params + ']') + } catch (e) { + callback('Error encoding arguments: ' + e) + return + } + if (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) + } + } + callback(null, { data: data, dataHex: dataHex, funArgs: funArgs }) + }, + + /** + * encode function call (function id + encoded parameters) + * + * @param {Object} params - input paramater of the function to call + * @param {Object} funAbi - abi definition of the function to call. null if building data for the ctor. + * @param {Function} callback - callback + */ + encodeFunctionCall: function (params, funAbi, callback) { + this.encodeParams(params, funAbi, (error, encodedParam) => { + if (error) return callback(error) + callback(null, { dataHex: helper.encodeFunctionId(funAbi) + encodedParam.dataHex, funAbi, funArgs: encodedParam.funArgs }) + }) + }, + + /** + * encode constructor creation and link with provided libraries if needed + * + * @param {Object} contract - input paramater of the function to call + * @param {Object} params - input paramater of the function to call + * @param {Object} funAbi - abi definition of the function to call. null if building data for the ctor. + * @param {Object} linkLibraries - contains {linkReferences} object which list all the addresses to be linked + * @param {Object} linkReferences - given by the compiler, contains the proper linkReferences + * @param {Function} callback - callback + */ + encodeConstructorCallAndLinkLibraries: function (contract, params, funAbi, linkLibraries, linkReferences, callback) { + this.encodeParams(params, funAbi, (error, encodedParam) => { + if (error) return callback(error) + var bytecodeToDeploy = contract.evm.bytecode.object + if (bytecodeToDeploy.indexOf('_') >= 0) { + if (linkLibraries && linkReferences) { + for (var libFile in linkLibraries) { + for (var lib in linkLibraries[libFile]) { + var address = linkLibraries[libFile][lib] + if (!ethJSUtil.isValidAddress(address)) return callback(address + ' is not a valid address. Please check the provided address is valid.') + bytecodeToDeploy = this.linkLibraryStandardFromlinkReferences(lib, address.replace('0x', ''), bytecodeToDeploy, linkReferences) + } + } + } + } + if (bytecodeToDeploy.indexOf('_') >= 0) { + return callback('Failed to link some libraries') + } + return callback(null, { dataHex: bytecodeToDeploy + encodedParam.dataHex, funAbi, funArgs: encodedParam.funArgs, contractBytecode: contract.evm.bytecode.object }) + }) + }, + + /** + * encode constructor creation and deploy librairies if needed + * + * @param {String} contractName - current contract name + * @param {Object} contract - input paramater of the function to call + * @param {Object} contracts - map of all compiled contracts. + * @param {Object} params - input paramater of the function to call + * @param {Object} funAbi - abi definition of the function to call. null if building data for the ctor. + * @param {Function} callback - callback + * @param {Function} callbackStep - callbackStep + * @param {Function} callbackDeployLibrary - callbackDeployLibrary + * @param {Function} callback - callback + */ + encodeConstructorCallAndDeployLibraries: function (contractName, contract, contracts, params, funAbi, callback, callbackStep, callbackDeployLibrary) { + this.encodeParams(params, funAbi, (error, encodedParam) => { + if (error) return callback(error) + var dataHex = '' + var contractBytecode = contract.evm.bytecode.object + var bytecodeToDeploy = contract.evm.bytecode.object + if (bytecodeToDeploy.indexOf('_') >= 0) { + this.linkBytecode(contract, contracts, (err, bytecode) => { + if (err) { + callback('Error deploying required libraries: ' + err) + } else { + bytecodeToDeploy = bytecode + dataHex + return callback(null, {dataHex: bytecodeToDeploy, funAbi, funArgs: encodedParam.funArgs, contractBytecode, contractName: contractName}) + } + }, callbackStep, callbackDeployLibrary) + return + } else { + dataHex = bytecodeToDeploy + encodedParam.dataHex + } + callback(null, {dataHex: bytecodeToDeploy, funAbi, funArgs: encodedParam.funArgs, contractBytecode, contractName: contractName}) + }) + }, + + /** + * (DEPRECATED) 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 {Function} callback - callback + * @param {Function} callbackStep - callbackStep + * @param {Function} callbackDeployLibrary - callbackDeployLibrary + */ buildData: function (contractName, contract, contracts, isConstructor, funAbi, params, callback, callbackStep, callbackDeployLibrary) { var funArgs = '' var data = '' diff --git a/remix-lib/test/txFormat.js b/remix-lib/test/txFormat.js index e8d702a90e..b6a908f124 100644 --- a/remix-lib/test/txFormat.js +++ b/remix-lib/test/txFormat.js @@ -5,6 +5,8 @@ var compiler = require('solc') var compilerInput = require('../src/helpers/compilerHelper').compilerInput var executionContext = require('../src/execution/execution-context') +/* tape *********************************************************** */ + var context tape('ContractParameters - (TxFormat.buildData) - format input parameters', function (t) { var output = compiler.compileStandardWrapper(compilerInput(uintContract)) @@ -29,11 +31,12 @@ function testWithInput (st, params, expected) { }, () => {}, () => {}) } +/* tape *********************************************************** */ + tape('ContractParameters - (TxFormat.buildData) - link Libraries', function (t) { executionContext.setContext('vm') - var output = compiler.compileStandardWrapper(compilerInput(deploySimpleLib)) - output = JSON.parse(output) - var contract = output.contracts['test.sol']['testContractLinkLibrary'] + var compileData = compiler.compileStandardWrapper(compilerInput(deploySimpleLib)) + var fakeDeployedContracts = { lib1: '0xf7a10e525d4b168f45f74db1b61f63d3e7619e11', lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2: '0xf7a10e525d4b168f45f74db1b61f63d3e7619e33', @@ -46,11 +49,22 @@ tape('ContractParameters - (TxFormat.buildData) - link Libraries', function (t) } }) } // fake - context = { output, contract } + t.test('(TxFormat.buildData and link library (standard way))', function (st) { st.plan(6) + var output = JSON.parse(compileData) + var contract = output.contracts['test.sol']['testContractLinkLibrary'] + context = { output, contract } testLinkLibrary(st, fakeDeployedContracts, callbackDeployLibraries) }) + + t.test('(TxFormat.encodeConstructorCallAndLinkLibraries and link library (standard way))', function (st) { + st.plan(12) + var output = JSON.parse(compileData) + var contract = output.contracts['test.sol']['testContractLinkLibrary'] + context = { output, contract } + testLinkLibrary2(st, callbackDeployLibraries) + }) }) function testLinkLibrary (st, fakeDeployedContracts, callbackDeployLibraries) { @@ -73,6 +87,68 @@ function testLinkLibrary (st, fakeDeployedContracts, callbackDeployLibraries) { }, callbackDeployLibraries) } +function testLinkLibrary2 (st, callbackDeployLibraries) { + var librariesReference = { + 'test.sol': { + 'lib1': '0xf7a10e525d4b168f45f74db1b61f63d3e7619e11', + 'lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2': '0xf7a10e525d4b168f45f74db1b61f63d3e7619e33' + } + } + var data = '608060405234801561001057600080fd5b5061026b806100206000396000f300608060405260043610610041576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680636d4ce63c14610046575b600080fd5b34801561005257600080fd5b5061005b61005d565b005b73f7a10e525d4b168f45f74db1b61f63d3e7619e116344733ae16040518163ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040160006040518083038186803b1580156100bd57600080fd5b505af41580156100d1573d6000803e3d6000fd5b5050505073f7a10e525d4b168f45f74db1b61f63d3e7619e336344733ae16040518163ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040160006040518083038186803b15801561013557600080fd5b505af4158015610149573d6000803e3d6000fd5b5050505073f7a10e525d4b168f45f74db1b61f63d3e7619e336344733ae16040518163ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040160006040518083038186803b1580156101ad57600080fd5b505af41580156101c1573d6000803e3d6000fd5b5050505073f7a10e525d4b168f45f74db1b61f63d3e7619e116344733ae16040518163ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040160006040518083038186803b15801561022557600080fd5b505af4158015610239573d6000803e3d6000fd5b505050505600a165627a7a7230582090b0bbf59fc9b1f13331039529d287ce9a9e712ce22dc71025510293d18104bc0029' + + var deployMsg = ['creation of library test.sol:lib1 pending...', + 'creation of library test.sol:lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2 pending...'] + txFormat.encodeConstructorCallAndLinkLibraries(context.contract, '', context.contract.abi[0], librariesReference, context.contract.evm.bytecode.linkReferences, (error, result) => { + console.log(error, result) + st.equal(data, result.dataHex) + var linkedbyteCode = result.dataHex + var libReference = context.contract.evm.bytecode.linkReferences['test.sol']['lib1'] + st.equal(linkedbyteCode.substr(2 * libReference[0].start, 40), librariesReference['test.sol']['lib1'].replace('0x', '')) + st.equal(linkedbyteCode.substr(2 * libReference[1].start, 40), librariesReference['test.sol']['lib1'].replace('0x', '')) + + libReference = context.contract.evm.bytecode.linkReferences['test.sol']['lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2'] + st.equal(linkedbyteCode.substr(2 * libReference[0].start, 40), librariesReference['test.sol']['lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2'].replace('0x', '')) + st.equal(linkedbyteCode.substr(2 * libReference[1].start, 40), librariesReference['test.sol']['lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2'].replace('0x', '')) + }) + + txFormat.encodeConstructorCallAndDeployLibraries('testContractLinkLibrary', context.contract, context.output.contracts, '', context.contract.abi[0], (error, result) => { + console.log(error, result) + st.equal(data, result.dataHex) + var linkedbyteCode = result.dataHex + var libReference = context.contract.evm.bytecode.linkReferences['test.sol']['lib1'] + st.equal(linkedbyteCode.substr(2 * libReference[0].start, 40), librariesReference['test.sol']['lib1'].replace('0x', '')) + st.equal(linkedbyteCode.substr(2 * libReference[1].start, 40), librariesReference['test.sol']['lib1'].replace('0x', '')) + + libReference = context.contract.evm.bytecode.linkReferences['test.sol']['lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2'] + st.equal(linkedbyteCode.substr(2 * libReference[0].start, 40), librariesReference['test.sol']['lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2'].replace('0x', '')) + st.equal(linkedbyteCode.substr(2 * libReference[1].start, 40), librariesReference['test.sol']['lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2_lib2'].replace('0x', '')) + }, (msg) => { + st.equal(msg, deployMsg[0]) + deployMsg.shift() + }, callbackDeployLibraries) +} + +/* tape *********************************************************** */ + +tape('EncodeParameter', function (t) { + t.test('(TxFormat.encodeFunctionCall)', function (st) { + st.plan(1) + encodeFunctionCallTest(st) + }) +}) + +function encodeFunctionCallTest (st) { + var output = compiler.compileStandardWrapper(compilerInput(encodeFunctionCall)) + output = JSON.parse(output) + var contract = output.contracts['test.sol']['testContractLinkLibrary'] + txFormat.encodeFunctionCall('123, "test string"', contract.abi[0], (error, encoded) => { + console.log(error) + st.equal(encoded.dataHex, '0x805da4ad000000000000000000000000000000000000000000000000000000000000007b0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000b7465737420737472696e67000000000000000000000000000000000000000000') + }) +} + +/* *********************************************************** */ + var uintContract = `contract uintContractTest { uint _tp; address _ap; @@ -103,3 +179,10 @@ contract testContractLinkLibrary { } }` +var encodeFunctionCall = `pragma solidity ^0.4.4; + +contract testContractLinkLibrary { + function get (uint _p, string _o) { + + } + }`