Merge pull request #155 from ethereum/decodeLocals

Fix tests
pull/7/head
chriseth 8 years ago committed by GitHub
commit 70c6fc4bc8
  1. 21
      .babelrc
  2. 21
      package.json
  3. 28
      src/solidity/astHelper.js
  4. 81
      src/solidity/decodeInfo.js
  5. 10
      src/solidity/stateDecoder.js
  6. 26
      test/solidity/contracts/simpleContract.js
  7. 71
      test/solidity/decodeInfo.js

@ -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"]
}

@ -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",

@ -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
}

@ -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 <name>)
* @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

@ -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
}

@ -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;
}
`

@ -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()
})
})

Loading…
Cancel
Save