|
|
@ -7,28 +7,31 @@ class StorageResolver { |
|
|
|
constructor (_traceManager) { |
|
|
|
constructor (_traceManager) { |
|
|
|
this.traceManager = _traceManager |
|
|
|
this.traceManager = _traceManager |
|
|
|
this.storageByAddress = {} |
|
|
|
this.storageByAddress = {} |
|
|
|
|
|
|
|
this.maxSize = 100 |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* return the storage for the current context (address and vm trace index) |
|
|
|
* return the storage for the given context (address and vm trace index) |
|
|
|
* by default now returns the range 0 => 1000 |
|
|
|
* returns the range 0 => this.maxSize |
|
|
|
|
|
|
|
* |
|
|
|
* @param {Object} - tx - transaction |
|
|
|
* @param {Object} - tx - transaction |
|
|
|
* @param {Int} - stepIndex - Index of the stop in the vm trace |
|
|
|
* @param {Int} - stepIndex - Index of the stop in the vm trace |
|
|
|
* @param {Function} - callback - contains a map: [hashedKey] = {key, hashedKey, value} |
|
|
|
* @param {Function} - callback - contains a map: [hashedKey] = {key, hashedKey, value} |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
storageRange (tx, stepIndex, callback) { |
|
|
|
storageRange (tx, stepIndex, callback) { |
|
|
|
storageRangeInternal(this, '0x0', true, tx, stepIndex, callback) |
|
|
|
storageRangeInternal(this, '0x0', tx, stepIndex, true, callback) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* return a slot value for the current context (address and vm trace index) |
|
|
|
* return a slot value for the given context (address and vm trace index) |
|
|
|
|
|
|
|
* |
|
|
|
* @param {String} - slot - slot key |
|
|
|
* @param {String} - slot - slot key |
|
|
|
* @param {Object} - tx - transaction |
|
|
|
* @param {Object} - tx - transaction |
|
|
|
* @param {Int} - stepIndex - Index of the stop in the vm trace |
|
|
|
* @param {Int} - stepIndex - Index of the stop in the vm trace |
|
|
|
* @param {Function} - callback - {key, hashedKey, value} - |
|
|
|
* @param {Function} - callback - {key, hashedKey, value} - |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
storageSlot (slot, tx, stepIndex, callback) { |
|
|
|
storageSlot (slot, tx, stepIndex, callback) { |
|
|
|
storageRangeInternal(this, slot, false, tx, stepIndex, function (error, storage) { |
|
|
|
storageRangeInternal(this, slot, tx, stepIndex, false, function (error, storage) { |
|
|
|
if (error) { |
|
|
|
if (error) { |
|
|
|
callback(error) |
|
|
|
callback(error) |
|
|
|
} else { |
|
|
|
} else { |
|
|
@ -49,71 +52,49 @@ class StorageResolver { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
function resolveAddress (self, stepIndex, callback) { |
|
|
|
/** |
|
|
|
self.traceManager.getCurrentCalledAddressAt(stepIndex, (error, result) => { |
|
|
|
* retrieve the storage and ensure at least @arg slot is cached. |
|
|
|
|
|
|
|
* - If @arg slot is already cached, the storage will be returned from 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. |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
function storageRangeInternal (self, slot, tx, stepIndex, fullStorage, callback) { |
|
|
|
|
|
|
|
resolveAddress(self, stepIndex, (error, address) => { |
|
|
|
if (error) { |
|
|
|
if (error) { |
|
|
|
callback(error) |
|
|
|
return callback(error) |
|
|
|
} else { |
|
|
|
|
|
|
|
callback(null, result) |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
}) |
|
|
|
self.traceManager.accumulateStorageChanges(stepIndex, address, {}, (error, storageChanges) => { |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function storageRangeWeb3Call (tx, address, start, fullStorage, callback) { |
|
|
|
|
|
|
|
var maxSize = fullStorage ? 1000 : 100 |
|
|
|
|
|
|
|
util.web3.debug.storageRangeAt( |
|
|
|
|
|
|
|
tx.blockHash, tx.transactionIndex === undefined ? tx.hash : tx.transactionIndex, |
|
|
|
|
|
|
|
address, |
|
|
|
|
|
|
|
start, |
|
|
|
|
|
|
|
maxSize, |
|
|
|
|
|
|
|
(error, result) => { |
|
|
|
|
|
|
|
if (error) { |
|
|
|
if (error) { |
|
|
|
callback(error) |
|
|
|
return callback(error) |
|
|
|
} else if (result.storage) { |
|
|
|
|
|
|
|
callback(null, result.storage, result.complete) |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
callback('the storage has not been provided') |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
}) |
|
|
|
if (!fullStorage && storageChanges[slot]) { |
|
|
|
} |
|
|
|
return callback(null, storageChanges) |
|
|
|
|
|
|
|
|
|
|
|
function storageRangeInternal (self, start, fullStorage, tx, stepIndex, callback) { |
|
|
|
|
|
|
|
resolveAddress(self, stepIndex, (error, address) => { |
|
|
|
|
|
|
|
if (error) { |
|
|
|
|
|
|
|
callback(error) |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
if (traceHelper.isContractCreation(address)) { |
|
|
|
|
|
|
|
callback(null, {}) |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
if (!util.web3.debug.storageRangeAt) { |
|
|
|
|
|
|
|
callback('no storageRangeAt endpoint found') |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
var cached = fromCache(self, address, start) |
|
|
|
|
|
|
|
if (cached) { |
|
|
|
|
|
|
|
self.traceManager.accumulateStorageChanges(stepIndex, address, cached, callback)
|
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
storageRangeWeb3Call(tx, address, start, fullStorage, (error, storage, complete) => { |
|
|
|
|
|
|
|
if (error) { |
|
|
|
|
|
|
|
callback(error) |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
toCache(self, address, storage, fullStorage, complete) |
|
|
|
|
|
|
|
self.traceManager.accumulateStorageChanges(stepIndex, address, storage, callback) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
var cached = fromCache(self, address, slot) |
|
|
|
|
|
|
|
if (cached && cached[slot]) { // we have the current slot in the cache and maybe the next 1000 ...
|
|
|
|
|
|
|
|
return callback(null, Object.assign(cached, storageChanges)) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
storageRangeWeb3Call(tx, address, slot, self.maxSize, (error, storage, complete) => { |
|
|
|
|
|
|
|
if (error) { |
|
|
|
|
|
|
|
return callback(error) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
toCache(self, address, storage) |
|
|
|
|
|
|
|
if (slot === '0x0' && Object.keys(storage).length < self.maxSize) { |
|
|
|
|
|
|
|
self.storageByAddress[address].complete = true |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
callback(null, Object.assign(storage, storageChanges)) |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
}) |
|
|
|
}) |
|
|
|
}) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* 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 |
|
|
|
* @param {String} slotKey - key of the value to return |
|
|
|
* @param {String} slotKey - key of the value to return |
|
|
|
* @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, hashedKey) { |
|
|
|
function fromCache (self, address, hashedKey) { |
|
|
|
if (!self.storageByAddress[address]) { |
|
|
|
if (!self.storageByAddress[address]) { |
|
|
|
return null |
|
|
|
return null |
|
|
@ -126,16 +107,43 @@ function fromCache (self, address, hashedKey) { |
|
|
|
* |
|
|
|
* |
|
|
|
* @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} |
|
|
|
* @param {Bool} complete - True if the storage is complete |
|
|
|
|
|
|
|
*/ |
|
|
|
*/ |
|
|
|
function toCache (self, address, storage, fullStorageRequest, complete) { |
|
|
|
function 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) |
|
|
|
if (Object.keys(storage).length < 1000 && fullStorageRequest && complete) { |
|
|
|
} |
|
|
|
self.storageByAddress[address].complete = complete |
|
|
|
|
|
|
|
|
|
|
|
function storageRangeWeb3Call (tx, address, start, maxSize, callback) { |
|
|
|
|
|
|
|
if (traceHelper.isContractCreation(address)) { |
|
|
|
|
|
|
|
callback(null, {}, true) |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
util.web3.debug.storageRangeAt( |
|
|
|
|
|
|
|
tx.blockHash, tx.transactionIndex === undefined ? tx.hash : tx.transactionIndex, |
|
|
|
|
|
|
|
address, |
|
|
|
|
|
|
|
start, |
|
|
|
|
|
|
|
maxSize, |
|
|
|
|
|
|
|
(error, result) => { |
|
|
|
|
|
|
|
if (error) { |
|
|
|
|
|
|
|
callback(error) |
|
|
|
|
|
|
|
} else if (result.storage) { |
|
|
|
|
|
|
|
callback(null, result.storage, result.complete) |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
callback('the storage has not been provided') |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function resolveAddress (self, stepIndex, callback) { |
|
|
|
|
|
|
|
self.traceManager.getCurrentCalledAddressAt(stepIndex, (error, result) => { |
|
|
|
|
|
|
|
if (error) { |
|
|
|
|
|
|
|
callback(error) |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
callback(null, result) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
module.exports = StorageResolver |
|
|
|
module.exports = StorageResolver |
|
|
|