diff --git a/.babelrc b/.babelrc new file mode 100644 index 0000000000..f9fc678369 --- /dev/null +++ b/.babelrc @@ -0,0 +1,21 @@ +{ + "plugins": ["check-es2015-constants", + "transform-es2015-arrow-functions", + "transform-es2015-block-scoped-functions", + "transform-es2015-block-scoping", + "transform-es2015-classes", + "transform-es2015-computed-properties", + "transform-es2015-destructuring", + "transform-es2015-duplicate-keys", + "transform-es2015-for-of", + "transform-es2015-function-name", + "transform-es2015-literals", + "transform-es2015-object-super", + "transform-es2015-parameters", + "transform-es2015-shorthand-properties", + "transform-es2015-spread", + "transform-es2015-sticky-regex", + "transform-es2015-template-literals", + "transform-es2015-unicode-regex", + "transform-regenerator"] +} \ No newline at end of file diff --git a/package.json b/package.json index e8950866b2..3447a6196e 100644 --- a/package.json +++ b/package.json @@ -18,8 +18,25 @@ }, "devDependencies": { "babel-cli": "^6.16.0", - "babel-plugin-transform-es2015-block-scoping": "^6.15.0", + "babel-plugin-check-es2015-constants": "^6.8.0", + "babel-plugin-transform-es2015-arrow-functions": "^6.8.0", + "babel-plugin-transform-es2015-block-scoped-functions": "^6.8.0", + "babel-plugin-transform-es2015-block-scoping": "^6.18.0", + "babel-plugin-transform-es2015-classes": "^6.18.0", + "babel-plugin-transform-es2015-computed-properties": "^6.8.0", + "babel-plugin-transform-es2015-destructuring": "^6.18.0", + "babel-plugin-transform-es2015-duplicate-keys": "^6.8.0", + "babel-plugin-transform-es2015-for-of": "^6.18.0", + "babel-plugin-transform-es2015-function-name": "^6.9.0", + "babel-plugin-transform-es2015-literals": "^6.8.0", + "babel-plugin-transform-es2015-object-super": "^6.8.0", + "babel-plugin-transform-es2015-parameters": "^6.18.0", + "babel-plugin-transform-es2015-shorthand-properties": "^6.18.0", + "babel-plugin-transform-es2015-spread": "^6.8.0", + "babel-plugin-transform-es2015-sticky-regex": "^6.8.0", "babel-plugin-transform-es2015-template-literals": "^6.8.0", + "babel-plugin-transform-es2015-unicode-regex": "^6.11.0", + "babel-plugin-transform-regenerator": "^6.16.1", "browserify": "^13.0.1", "ethereumjs-util": "^4.5.0", "http-server": "^0.9.0", @@ -36,7 +53,7 @@ "start_node": "./runNode.sh", "start_eth": "npm run warning_message; eth -j --rpccorsdomain '*'", "start_geth": "npm run warning_message; geth --rpc --rpcapi 'web3,eth,debug' --rpcport 8545 --rpccorsdomain '*'", - "build": "mkdir build; browserify src/index.js -g yo-yoify -o build/app.js; babel --plugins babel-plugin-transform-es2015-template-literals,babel-plugin-transform-es2015-block-scoping build/app.js --out-file build/app.js", + "build": "mkdir build; browserify src/index.js -g yo-yoify -o build/app.js; babel build/app.js --out-file build/app.js", "test": "standard && tape ./test/tests.js", "serve": "http-server .", "nightwatch_local": "nightwatch --config nightwatch.js --env local", diff --git a/src/solidity/astHelper.js b/src/solidity/astHelper.js index 90ab05046d..d543dca8a9 100644 --- a/src/solidity/astHelper.js +++ b/src/solidity/astHelper.js @@ -37,11 +37,14 @@ function getLinearizedBaseContracts (id, contractsById) { * * @param {String} contractName - contract for which state var should be resolved * @param {Object} sourcesList - sources list (containing root AST node) + * @param {Object} [contracts] - map of contract definitions (contains contractsById, contractsByName) * @return {Object} - return an object containing: stateItems - list of all the children node of the @arg contractName * stateVariables - list of all the variable declaration of the @arg contractName */ -function extractState (contractName, sourcesList) { - var contracts = extractContractDefinitions(sourcesList) +function extractStateDefinitions (contractName, sourcesList, contracts) { + if (!contracts) { + contracts = extractContractDefinitions(sourcesList) + } var node = contracts.contractsByName[contractName] if (node) { var stateItems = [] @@ -59,15 +62,32 @@ function extractState (contractName, sourcesList) { } } return { - stateItems: stateItems, + stateDefinitions: stateItems, stateVariables: stateVar } } return null } +/** + * return state var and type definition of all the contracts from the given @args sourcesList + * + * @param {Object} sourcesList - sources list (containing root AST node) + * @return {Object} - returns a mapping between contract name and contract state + */ +function extractStatesDefinitions (sourcesList) { + var contracts = extractContractDefinitions(sourcesList) + var ret = {} + for (var contract in contracts.contractsById) { + var name = contracts.contractsById[contract].attributes.name + ret[name] = extractStateDefinitions(name, sourcesList, contracts) + } + return ret +} + module.exports = { - extractState: extractState, + extractStatesDefinitions: extractStatesDefinitions, + extractStateDefinitions: extractStateDefinitions, extractContractDefinitions: extractContractDefinitions, getLinearizedBaseContracts: getLinearizedBaseContracts } diff --git a/src/solidity/decodeInfo.js b/src/solidity/decodeInfo.js index be0665031a..65b7582097 100644 --- a/src/solidity/decodeInfo.js +++ b/src/solidity/decodeInfo.js @@ -90,9 +90,11 @@ function String (type) { * ArrayType decode the given @arg type * * @param {String} type - type given by the AST (e.g int256[] storage ref, int256[] storage ref[] storage ref) + * @param {Object} stateDefinitions - all state definitions given by the AST (including struct and enum type declaration) for all contracts + * @param {String} contractName - contract the @args typeName belongs to * @return {Object} returns decoded info about the current type: { storageBytes, typeName, arraySize, subArray} */ -function Array (type, stateDefinitions) { +function Array (type, stateDefinitions, contractName) { var arraySize var match = type.match(/(.*)\[(.*?)\]( storage ref| storage pointer| memory| calldata)?$/) if (!match || match.length < 3) { @@ -100,7 +102,7 @@ function Array (type, stateDefinitions) { return null } arraySize = match[2] === '' ? 'dynamic' : parseInt(match[2]) - var underlyingType = parseType(match[1], stateDefinitions) + var underlyingType = parseType(match[1], stateDefinitions, contractName) if (underlyingType === null) { console.log('unable to parse type ' + type) return null @@ -112,10 +114,13 @@ function Array (type, stateDefinitions) { * Enum decode the given @arg type * * @param {String} type - type given by the AST (e.g enum enumDef) + * @param {Object} stateDefinitions - all state definitions given by the AST (including struct and enum type declaration) for all contracts + * @param {String} contractName - contract the @args typeName belongs to * @return {Object} returns decoded info about the current type: { storageBytes, typeName, enum} */ -function Enum (type, stateDefinitions) { - var enumDef = getEnum(type, stateDefinitions) +function Enum (type, stateDefinitions, contractName) { + var match = type.match(/enum (.*)/) + var enumDef = getEnum(match[1], stateDefinitions, contractName) if (enumDef === null) { console.log('unable to retrieve decode info of ' + type) return null @@ -127,14 +132,16 @@ function Enum (type, stateDefinitions) { * Struct decode the given @arg type * * @param {String} type - type given by the AST (e.g struct structDef storage ref) + * @param {Object} stateDefinitions - all state definitions given by the AST (including struct and enum type declaration) for all contracts + * @param {String} contractName - contract the @args typeName belongs to * @return {Object} returns decoded info about the current type: { storageBytes, typeName, members} */ -function Struct (type, stateDefinitions) { +function Struct (type, stateDefinitions, contractName) { var match = type.match(/struct (.*?)( storage ref| storage pointer| memory| calldata)?$/) if (!match) { return null } - var memberDetails = getStructMembers(match[1], stateDefinitions) // type is used to extract the ast struct definition + var memberDetails = getStructMembers(match[1], stateDefinitions, contractName) // type is used to extract the ast struct definition if (!memberDetails) return null return new StructType(memberDetails) } @@ -143,14 +150,23 @@ function Struct (type, stateDefinitions) { * retrieve enum declaration of the given @arg type * * @param {String} type - type given by the AST (e.g enum enumDef) - * @param {Object} stateDefinitions - all state declarations given by the AST (including struct and enum type declaration) + * @param {Object} stateDefinitions - all state declarations given by the AST (including struct and enum type declaration) for all contracts + * @param {String} contractName - contract the @args typeName belongs to * @return {Array} - containing all value declaration of the current enum type */ -function getEnum (type, stateDefinitions) { - for (var k in stateDefinitions) { - var dec = stateDefinitions[k] - if (dec.attributes && dec.attributes.name && type === 'enum ' + dec.attributes.name) { - return dec +function getEnum (type, stateDefinitions, contractName) { + var split = type.split('.') + if (!split.length) { + type = contractName + '.' + type + } else { + contractName = split[0] + } + var state = stateDefinitions[contractName] + if (state) { + for (var dec of state.stateDefinitions) { + if (dec.attributes && dec.attributes.name && type === contractName + '.' + dec.attributes.name) { + return dec + } } } return null @@ -160,17 +176,26 @@ function getEnum (type, stateDefinitions) { * retrieve memebers declared in the given @arg tye * * @param {String} typeName - name of the struct type (e.g struct ) - * @param {Object} stateDefinitions - all state definition given by the AST (including struct and enum type declaration) + * @param {Object} stateDefinitions - all state definition given by the AST (including struct and enum type declaration) for all contracts + * @param {String} contractName - contract the @args typeName belongs to * @return {Array} containing all members of the current struct type */ -function getStructMembers (typeName, stateDefinitions) { - for (var k in stateDefinitions) { - var dec = stateDefinitions[k] - if (dec.name === 'StructDefinition' && typeName === dec.attributes.name) { - var offsets = computeOffsets(dec.children, stateDefinitions) - return { - members: offsets.typesOffsets, - storageSlots: offsets.endLocation.slot +function getStructMembers (type, stateDefinitions, contractName) { + var split = type.split('.') + if (!split.length) { + type = contractName + '.' + type + } else { + contractName = split[0] + } + var state = stateDefinitions[contractName] + if (state) { + for (var dec of state.stateDefinitions) { + if (dec.name === 'StructDefinition' && type === contractName + '.' + dec.attributes.name) { + var offsets = computeOffsets(dec.children, stateDefinitions, contractName) + return { + members: offsets.typesOffsets, + storageSlots: offsets.endLocation.slot + } } } } @@ -198,10 +223,11 @@ function typeClass (fullType) { * parse the type and return an object representing the type * * @param {Object} type - type name given by the ast node - * @param {Object} stateDefinitions - all state stateDefinitions given by the AST (including struct and enum type declaration) + * @param {Object} stateDefinitions - all state stateDefinitions given by the AST (including struct and enum type declaration) for all contracts + * @param {String} contractName - contract the @args typeName belongs to * @return {Object} - return the corresponding decoder or null on error */ -function parseType (type, stateDefinitions) { +function parseType (type, stateDefinitions, contractName) { var decodeInfos = { 'address': Address, 'array': Array, @@ -219,17 +245,18 @@ function parseType (type, stateDefinitions) { console.log('unable to retrieve decode info of ' + type) return null } - return decodeInfos[currentType](type, stateDefinitions) + return decodeInfos[currentType](type, stateDefinitions, contractName) } /** * compute offset (slot offset and byte offset of the @arg list of types) * * @param {Array} types - list of types - * @param {Object} stateItems - all state definitions given by the AST (including struct and enum type declaration) + * @param {Object} stateDefinitions - all state definitions given by the AST (including struct and enum type declaration) for all contracts + * @param {String} contractName - contract the @args typeName belongs to * @return {Array} - return an array of types item: {name, type, location}. location defines the byte offset and slot offset */ -function computeOffsets (types, stateItems, cb) { +function computeOffsets (types, stateDefinitions, contractName) { var ret = [] var location = { offset: 0, @@ -237,7 +264,7 @@ function computeOffsets (types, stateItems, cb) { } for (var i in types) { var variable = types[i] - var type = parseType(variable.attributes.type, stateItems) + var type = parseType(variable.attributes.type, stateDefinitions, contractName) if (!type) { console.log('unable to retrieve decode info of ' + variable.attributes.type) return null diff --git a/src/solidity/stateDecoder.js b/src/solidity/stateDecoder.js index 7737cff7d2..be77fe6b60 100644 --- a/src/solidity/stateDecoder.js +++ b/src/solidity/stateDecoder.js @@ -25,12 +25,12 @@ function decodeState (stateVars, storageContent) { * @return {Object} - return the location of all contract variables in the storage */ function extractStateVariables (contractName, sourcesList) { - var state = astHelper.extractState(contractName, sourcesList) - var ret = [] - if (!state) { - return ret + var states = astHelper.extractStatesDefinitions(sourcesList) + if (!states[contractName]) { + return [] } - var offsets = decodeInfo.computeOffsets(state.stateVariables, state.stateItems) + var types = states[contractName].stateVariables + var offsets = decodeInfo.computeOffsets(types, states, contractName) return offsets.typesOffsets } diff --git a/test/solidity/contracts/simpleContract.js b/test/solidity/contracts/simpleContract.js new file mode 100644 index 0000000000..bffa2bfe88 --- /dev/null +++ b/test/solidity/contracts/simpleContract.js @@ -0,0 +1,26 @@ +'use strict' +module.exports = ` + contract simpleContract { + struct structDef { + uint8 ui; + string str; + } + enum enumDef { + first, + second, + third + } + structDef structDec; + structDef[3] array; + enumDef enumDec; + } + + contract test1 { + struct str { + } + } + + contract test2 { + test1.str a; + } +` diff --git a/test/solidity/decodeInfo.js b/test/solidity/decodeInfo.js index 28cded2404..ac7419e555 100644 --- a/test/solidity/decodeInfo.js +++ b/test/solidity/decodeInfo.js @@ -3,55 +3,82 @@ var tape = require('tape') var compiler = require('solc') var index = require('../../src/index') var contracts = require('./contracts/miscContracts') +var simplecontracts = require('./contracts/simpleContract') tape('solidity', function (t) { t.test('astHelper, decodeInfo', function (st) { var output = compiler.compile(contracts, 0) - var stateDec = index.solidity.astHelper.extractState('contractUint', output.sources).stateItems - var decodeInfo = index.solidity.decodeInfo.parseType(stateDec[0].attributes.type, stateDec) + var state = index.solidity.astHelper.extractStateDefinitions('contractUint', output.sources) + var states = index.solidity.astHelper.extractStatesDefinitions(output.sources) + var stateDef = state.stateDefinitions + var decodeInfo = index.solidity.decodeInfo.parseType(stateDef[0].attributes.type, states, 'contractUint') checkDecodeInfo(st, decodeInfo, 1, 1, 'uint') - decodeInfo = index.solidity.decodeInfo.parseType(stateDec[2].attributes.type, stateDec) + decodeInfo = index.solidity.decodeInfo.parseType(stateDef[2].attributes.type, states, 'contractUint') checkDecodeInfo(st, decodeInfo, 1, 32, 'uint') - decodeInfo = index.solidity.decodeInfo.parseType(stateDec[3].attributes.type, stateDec) + decodeInfo = index.solidity.decodeInfo.parseType(stateDef[3].attributes.type, states, 'contractUint') checkDecodeInfo(st, decodeInfo, 1, 32, 'uint') - decodeInfo = index.solidity.decodeInfo.parseType(stateDec[4].attributes.type, stateDec) + decodeInfo = index.solidity.decodeInfo.parseType(stateDef[4].attributes.type, states, 'contractUint') checkDecodeInfo(st, decodeInfo, 1, 16, 'bytesX') - stateDec = index.solidity.astHelper.extractState('contractStructAndArray', output.sources).stateItems - decodeInfo = index.solidity.decodeInfo.parseType(stateDec[1].attributes.type, stateDec) + state = index.solidity.astHelper.extractStateDefinitions('contractStructAndArray', output.sources) + stateDef = state.stateDefinitions + decodeInfo = index.solidity.decodeInfo.parseType(stateDef[1].attributes.type, states, 'contractStructAndArray') checkDecodeInfo(st, decodeInfo, 2, 32, 'struct') - decodeInfo = index.solidity.decodeInfo.parseType(stateDec[2].attributes.type, stateDec) + decodeInfo = index.solidity.decodeInfo.parseType(stateDef[2].attributes.type, states, 'contractStructAndArray') checkDecodeInfo(st, decodeInfo, 6, 32, 'array') - decodeInfo = index.solidity.decodeInfo.parseType(stateDec[3].attributes.type, stateDec) + decodeInfo = index.solidity.decodeInfo.parseType(stateDef[3].attributes.type, states, 'contractStructAndArray') checkDecodeInfo(st, decodeInfo, 2, 32, 'array') - stateDec = index.solidity.astHelper.extractState('contractArray', output.sources).stateItems - decodeInfo = index.solidity.decodeInfo.parseType(stateDec[0].attributes.type, stateDec) + state = index.solidity.astHelper.extractStateDefinitions('contractArray', output.sources) + stateDef = state.stateDefinitions + decodeInfo = index.solidity.decodeInfo.parseType(stateDef[0].attributes.type, states, 'contractArray') checkDecodeInfo(st, decodeInfo, 1, 32, 'array') - decodeInfo = index.solidity.decodeInfo.parseType(stateDec[1].attributes.type, stateDec) + decodeInfo = index.solidity.decodeInfo.parseType(stateDef[1].attributes.type, states, 'contractArray') checkDecodeInfo(st, decodeInfo, 1, 32, 'array') - decodeInfo = index.solidity.decodeInfo.parseType(stateDec[2].attributes.type, stateDec) + decodeInfo = index.solidity.decodeInfo.parseType(stateDef[2].attributes.type, states, 'contractArray') checkDecodeInfo(st, decodeInfo, 4, 32, 'array') - stateDec = index.solidity.astHelper.extractState('contractEnum', output.sources).stateItems - decodeInfo = index.solidity.decodeInfo.parseType(stateDec[1].attributes.type, stateDec) + state = index.solidity.astHelper.extractStateDefinitions('contractEnum', output.sources) + stateDef = state.stateDefinitions + decodeInfo = index.solidity.decodeInfo.parseType(stateDef[1].attributes.type, states, 'contractEnum') checkDecodeInfo(st, decodeInfo, 1, 2, 'enum') - stateDec = index.solidity.astHelper.extractState('contractSmallVariable', output.sources).stateItems - decodeInfo = index.solidity.decodeInfo.parseType(stateDec[0].attributes.type, stateDec) + state = index.solidity.astHelper.extractStateDefinitions('contractSmallVariable', output.sources) + stateDef = state.stateDefinitions + decodeInfo = index.solidity.decodeInfo.parseType(stateDef[0].attributes.type, states, 'contractSmallVariable') checkDecodeInfo(st, decodeInfo, 1, 1, 'int') - decodeInfo = index.solidity.decodeInfo.parseType(stateDec[1].attributes.type, stateDec) + decodeInfo = index.solidity.decodeInfo.parseType(stateDef[1].attributes.type, states, 'contractSmallVariable') checkDecodeInfo(st, decodeInfo, 1, 1, 'uint') - decodeInfo = index.solidity.decodeInfo.parseType(stateDec[2].attributes.type, stateDec) + decodeInfo = index.solidity.decodeInfo.parseType(stateDef[2].attributes.type, states, 'contractSmallVariable') checkDecodeInfo(st, decodeInfo, 1, 2, 'uint') - decodeInfo = index.solidity.decodeInfo.parseType(stateDec[3].attributes.type, stateDec) + decodeInfo = index.solidity.decodeInfo.parseType(stateDef[3].attributes.type, states, 'contractSmallVariable') checkDecodeInfo(st, decodeInfo, 1, 4, 'int') - decodeInfo = index.solidity.decodeInfo.parseType(stateDec[4].attributes.type, stateDec) + decodeInfo = index.solidity.decodeInfo.parseType(stateDef[4].attributes.type, states, 'contractSmallVariable') checkDecodeInfo(st, decodeInfo, 1, 32, 'uint') - decodeInfo = index.solidity.decodeInfo.parseType(stateDec[5].attributes.type, stateDec) + decodeInfo = index.solidity.decodeInfo.parseType(stateDef[5].attributes.type, states, 'contractSmallVariable') checkDecodeInfo(st, decodeInfo, 1, 2, 'int') + output = compiler.compile(simplecontracts, 0) + + state = index.solidity.astHelper.extractStateDefinitions('simpleContract', output.sources) + states = index.solidity.astHelper.extractStatesDefinitions(output.sources) + stateDef = state.stateDefinitions + decodeInfo = index.solidity.decodeInfo.parseType(stateDef[2].attributes.type, states, 'simpleContract') + checkDecodeInfo(st, decodeInfo, 2, 32, 'struct') + decodeInfo = index.solidity.decodeInfo.parseType(stateDef[3].attributes.type, states, 'simpleContract') + checkDecodeInfo(st, decodeInfo, 6, 32, 'array') + decodeInfo = index.solidity.decodeInfo.parseType(stateDef[4].attributes.type, states, 'simpleContract') + checkDecodeInfo(st, decodeInfo, 1, 1, 'enum') + + state = index.solidity.astHelper.extractStateDefinitions('test2', output.sources) + stateDef = state.stateDefinitions + decodeInfo = index.solidity.decodeInfo.parseType(stateDef[0].attributes.type, states, 'test1') + checkDecodeInfo(st, decodeInfo, 0, 32, 'struct') + + state = index.solidity.stateDecoder.extractStateVariables('test2', output.sources) + checkDecodeInfo(st, decodeInfo, 0, 32, 'struct') + st.end() }) })