pull/7/head
yann300 8 years ago
parent 6a07727010
commit fc1fa94c67
  1. 28
      src/solidity/astHelper.js
  2. 81
      src/solidity/decodeInfo.js
  3. 10
      src/solidity/stateDecoder.js
  4. 26
      test/solidity/contracts/simpleContract.js
  5. 67
      test/solidity/decodeInfo.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
}

@ -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,78 @@ 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')
st.end()
})
})

Loading…
Cancel
Save