|
|
@ -1,7 +1,6 @@ |
|
|
|
'use strict' |
|
|
|
'use strict' |
|
|
|
var remixLib = require('remix-lib') |
|
|
|
var remixLib = require('remix-lib') |
|
|
|
var traceHelper = remixLib.helpers.trace |
|
|
|
var traceHelper = remixLib.helpers.trace |
|
|
|
var global = remixLib.global |
|
|
|
|
|
|
|
var mappingPreimages = require('./mappingPreimages') |
|
|
|
var mappingPreimages = require('./mappingPreimages') |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
@ -9,10 +8,12 @@ var mappingPreimages = require('./mappingPreimages') |
|
|
|
* (TODO: one instance need to be shared over all the components) |
|
|
|
* (TODO: one instance need to be shared over all the components) |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
class StorageResolver { |
|
|
|
class StorageResolver { |
|
|
|
constructor () { |
|
|
|
constructor (options) { |
|
|
|
this.storageByAddress = {} |
|
|
|
this.storageByAddress = {} |
|
|
|
this.preimagesMappingByAddress = {} |
|
|
|
this.preimagesMappingByAddress = {} |
|
|
|
this.maxSize = 100 |
|
|
|
this.maxSize = 100 |
|
|
|
|
|
|
|
this.web3 = options.web3 |
|
|
|
|
|
|
|
this.zeroSlot = '0x0000000000000000000000000000000000000000000000000000000000000000' |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
@ -25,7 +26,7 @@ class StorageResolver { |
|
|
|
* @param {Function} - callback - contains a map: [hashedKey] = {key, hashedKey, value} |
|
|
|
* @param {Function} - callback - contains a map: [hashedKey] = {key, hashedKey, value} |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
storageRange (tx, stepIndex, address, callback) { |
|
|
|
storageRange (tx, stepIndex, address, callback) { |
|
|
|
storageRangeInternal(this, zeroSlot, tx, stepIndex, address, callback) |
|
|
|
this.storageRangeInternal(this, this.zeroSlot, tx, stepIndex, address, callback) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
@ -37,6 +38,7 @@ class StorageResolver { |
|
|
|
* @return {Function} - callback |
|
|
|
* @return {Function} - callback |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
initialPreimagesMappings (tx, stepIndex, address, callback) { |
|
|
|
initialPreimagesMappings (tx, stepIndex, address, callback) { |
|
|
|
|
|
|
|
const self = this |
|
|
|
if (this.preimagesMappingByAddress[address]) { |
|
|
|
if (this.preimagesMappingByAddress[address]) { |
|
|
|
return callback(null, this.preimagesMappingByAddress[address]) |
|
|
|
return callback(null, this.preimagesMappingByAddress[address]) |
|
|
|
} |
|
|
|
} |
|
|
@ -44,7 +46,7 @@ class StorageResolver { |
|
|
|
if (error) { |
|
|
|
if (error) { |
|
|
|
return callback(error) |
|
|
|
return callback(error) |
|
|
|
} |
|
|
|
} |
|
|
|
mappingPreimages.decodeMappingsKeys(storage, (error, mappings) => { |
|
|
|
mappingPreimages.decodeMappingsKeys(self.web3, storage, (error, mappings) => { |
|
|
|
if (error) { |
|
|
|
if (error) { |
|
|
|
callback(error) |
|
|
|
callback(error) |
|
|
|
} else { |
|
|
|
} else { |
|
|
@ -65,7 +67,7 @@ class StorageResolver { |
|
|
|
* @param {Function} - callback - {key, hashedKey, value} - |
|
|
|
* @param {Function} - callback - {key, hashedKey, value} - |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
storageSlot (slot, tx, stepIndex, address, callback) { |
|
|
|
storageSlot (slot, tx, stepIndex, address, callback) { |
|
|
|
storageRangeInternal(this, slot, tx, stepIndex, address, function (error, storage) { |
|
|
|
this.storageRangeInternal(this, slot, tx, stepIndex, address, function (error, storage) { |
|
|
|
if (error) { |
|
|
|
if (error) { |
|
|
|
callback(error) |
|
|
|
callback(error) |
|
|
|
} else { |
|
|
|
} else { |
|
|
@ -83,7 +85,6 @@ class StorageResolver { |
|
|
|
isComplete (address) { |
|
|
|
isComplete (address) { |
|
|
|
return this.storageByAddress[address] && this.storageByAddress[address].complete |
|
|
|
return this.storageByAddress[address] && this.storageByAddress[address].complete |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* retrieve the storage and ensure at least @arg slot is cached. |
|
|
|
* retrieve the storage and ensure at least @arg slot is cached. |
|
|
@ -91,38 +92,36 @@ class StorageResolver { |
|
|
|
* even if the next 1000 items are not in the cache. |
|
|
|
* even if the next 1000 items are not in the cache. |
|
|
|
* - If @arg slot is not cached, the corresponding value will be resolved and the next 1000 slots. |
|
|
|
* - If @arg slot is not cached, the corresponding value will be resolved and the next 1000 slots. |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
function storageRangeInternal (self, slotKey, tx, stepIndex, address, callback) { |
|
|
|
storageRangeInternal (self, slotKey, tx, stepIndex, address, callback) { |
|
|
|
var cached = fromCache(self, address) |
|
|
|
var cached = this.fromCache(self, address) |
|
|
|
if (cached && cached.storage[slotKey]) { // we have the current slot in the cache and maybe the next 1000...
|
|
|
|
if (cached && cached.storage[slotKey]) { // we have the current slot in the cache and maybe the next 1000...
|
|
|
|
return callback(null, cached.storage) |
|
|
|
return callback(null, cached.storage) |
|
|
|
} |
|
|
|
} |
|
|
|
storageRangeWeb3Call(tx, address, slotKey, self.maxSize, (error, storage, nextKey) => { |
|
|
|
this.storageRangeWeb3Call(tx, address, slotKey, self.maxSize, (error, storage, nextKey) => { |
|
|
|
if (error) { |
|
|
|
if (error) { |
|
|
|
return callback(error) |
|
|
|
return callback(error) |
|
|
|
} |
|
|
|
} |
|
|
|
if (!storage[slotKey] && slotKey !== zeroSlot) { // we don't cache the zero slot (could lead to inconsistency)
|
|
|
|
if (!storage[slotKey] && slotKey !== self.zeroSlot) { // we don't cache the zero slot (could lead to inconsistency)
|
|
|
|
storage[slotKey] = { |
|
|
|
storage[slotKey] = { |
|
|
|
key: slotKey, |
|
|
|
key: slotKey, |
|
|
|
value: zeroSlot |
|
|
|
value: self.zeroSlot |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
toCache(self, address, storage) |
|
|
|
self.toCache(self, address, storage) |
|
|
|
if (slotKey === zeroSlot && !nextKey) { // only working if keys are sorted !!
|
|
|
|
if (slotKey === self.zeroSlot && !nextKey) { // only working if keys are sorted !!
|
|
|
|
self.storageByAddress[address].complete = true |
|
|
|
self.storageByAddress[address].complete = true |
|
|
|
} |
|
|
|
} |
|
|
|
callback(null, storage) |
|
|
|
callback(null, storage) |
|
|
|
}) |
|
|
|
}) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
var zeroSlot = '0x0000000000000000000000000000000000000000000000000000000000000000' |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* retrieve the storage from the cache. if @arg slot is defined, return only the desired slot, if not return the entire known storage |
|
|
|
* retrieve the storage from the cache. if @arg slot is defined, return only the desired slot, if not return the entire known storage |
|
|
|
* |
|
|
|
* |
|
|
|
* @param {String} address - contract address |
|
|
|
* @param {String} address - contract address |
|
|
|
* @return {String} - either the entire known storage or a single value |
|
|
|
* @return {String} - either the entire known storage or a single value |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
function fromCache (self, address) { |
|
|
|
fromCache (self, address) { |
|
|
|
if (!self.storageByAddress[address]) { |
|
|
|
if (!self.storageByAddress[address]) { |
|
|
|
return null |
|
|
|
return null |
|
|
|
} |
|
|
|
} |
|
|
@ -135,18 +134,18 @@ function fromCache (self, address) { |
|
|
|
* @param {String} address - contract address |
|
|
|
* @param {String} address - contract address |
|
|
|
* @param {Object} storage - result of `storageRangeAtInternal`, contains {key, hashedKey, value} |
|
|
|
* @param {Object} storage - result of `storageRangeAtInternal`, contains {key, hashedKey, value} |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
function toCache (self, address, storage) { |
|
|
|
toCache (self, address, storage) { |
|
|
|
if (!self.storageByAddress[address]) { |
|
|
|
if (!self.storageByAddress[address]) { |
|
|
|
self.storageByAddress[address] = {} |
|
|
|
self.storageByAddress[address] = {} |
|
|
|
} |
|
|
|
} |
|
|
|
self.storageByAddress[address].storage = Object.assign(self.storageByAddress[address].storage || {}, storage) |
|
|
|
self.storageByAddress[address].storage = Object.assign(self.storageByAddress[address].storage || {}, storage) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
function storageRangeWeb3Call (tx, address, start, maxSize, callback) { |
|
|
|
storageRangeWeb3Call (tx, address, start, maxSize, callback) { |
|
|
|
if (traceHelper.isContractCreation(address)) { |
|
|
|
if (traceHelper.isContractCreation(address)) { |
|
|
|
callback(null, {}, null) |
|
|
|
callback(null, {}, null) |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
global.web3Debug.debug.storageRangeAt( |
|
|
|
this.web3.debug.storageRangeAt( |
|
|
|
tx.blockHash, tx.transactionIndex === undefined ? tx.hash : tx.transactionIndex, |
|
|
|
tx.blockHash, tx.transactionIndex === undefined ? tx.hash : tx.transactionIndex, |
|
|
|
address, |
|
|
|
address, |
|
|
|
start, |
|
|
|
start, |
|
|
@ -162,5 +161,6 @@ function storageRangeWeb3Call (tx, address, start, maxSize, callback) { |
|
|
|
}) |
|
|
|
}) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
module.exports = StorageResolver |
|
|
|
module.exports = StorageResolver |
|
|
|