commit
21218bf362
@ -0,0 +1,132 @@ |
||||
'use strict' |
||||
var traceHelper = require('../helpers/traceHelper') |
||||
var util = require('../helpers/global') |
||||
|
||||
class StorageResolver { |
||||
constructor () { |
||||
this.storageByAddress = {} |
||||
this.maxSize = 100 |
||||
} |
||||
|
||||
/** |
||||
* returns the storage for the given context (address and vm trace index) |
||||
* returns the range 0x0 => this.maxSize |
||||
* |
||||
* @param {Object} - tx - transaction |
||||
* @param {Int} - stepIndex - Index of the stop in the vm trace |
||||
* @param {String} - address - lookup address |
||||
* @param {Function} - callback - contains a map: [hashedKey] = {key, hashedKey, value} |
||||
*/ |
||||
storageRange (tx, stepIndex, address, callback) { |
||||
storageRangeInternal(this, zeroSlot, tx, stepIndex, address, callback) |
||||
} |
||||
|
||||
/** |
||||
* return a slot value for the given context (address and vm trace index) |
||||
* |
||||
* @param {String} - slot - slot key |
||||
* @param {Object} - tx - transaction |
||||
* @param {Int} - stepIndex - Index of the stop in the vm trace |
||||
* @param {String} - address - lookup address |
||||
* @param {Function} - callback - {key, hashedKey, value} - |
||||
*/ |
||||
storageSlot (slot, tx, stepIndex, address, callback) { |
||||
storageRangeInternal(this, slot, tx, stepIndex, address, function (error, storage) { |
||||
if (error) { |
||||
callback(error) |
||||
} else { |
||||
callback(null, storage[slot] !== undefined ? storage[slot] : null) |
||||
} |
||||
}) |
||||
} |
||||
|
||||
/** |
||||
* return True if the storage at @arg address is complete |
||||
* |
||||
* @param {String} address - contract address |
||||
* @return {Bool} - return True if the storage at @arg address is complete |
||||
*/ |
||||
isComplete (address) { |
||||
return this.storageByAddress[address] && this.storageByAddress[address].complete |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 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, slotKey, tx, stepIndex, address, callback) { |
||||
var cached = fromCache(self, address) |
||||
if (cached && cached.storage[slotKey]) { // we have the current slot in the cache and maybe the next 1000...
|
||||
return callback(null, cached.storage) |
||||
} |
||||
storageRangeWeb3Call(tx, address, slotKey, self.maxSize, (error, storage, complete) => { |
||||
if (error) { |
||||
return callback(error) |
||||
} |
||||
if (!storage[slotKey]) { |
||||
storage[slotKey] = { |
||||
key: slotKey, |
||||
value: zeroSlot |
||||
} |
||||
} |
||||
toCache(self, address, storage) |
||||
if (slotKey === zeroSlot && Object.keys(storage).length < self.maxSize) { // only working if keys are sorted !!
|
||||
self.storageByAddress[address].complete = true |
||||
} |
||||
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 |
||||
* |
||||
* @param {String} address - contract address |
||||
* @return {String} - either the entire known storage or a single value |
||||
*/ |
||||
function fromCache (self, address) { |
||||
if (!self.storageByAddress[address]) { |
||||
return null |
||||
} |
||||
return self.storageByAddress[address] |
||||
} |
||||
|
||||
/** |
||||
* store the result of `storageRangeAtInternal` |
||||
* |
||||
* @param {String} address - contract address |
||||
* @param {Object} storage - result of `storageRangeAtInternal`, contains {key, hashedKey, value} |
||||
*/ |
||||
function toCache (self, address, storage) { |
||||
if (!self.storageByAddress[address]) { |
||||
self.storageByAddress[address] = {} |
||||
} |
||||
self.storageByAddress[address].storage = Object.assign(self.storageByAddress[address].storage || {}, storage) |
||||
} |
||||
|
||||
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') |
||||
} |
||||
}) |
||||
} |
||||
} |
||||
|
||||
module.exports = StorageResolver |
@ -0,0 +1,63 @@ |
||||
'use strict' |
||||
var helper = require('../helpers/util') |
||||
|
||||
class StorageViewer { |
||||
constructor (_context, _storageResolver, _traceManager) { |
||||
this.context = _context |
||||
this.storageResolver = _storageResolver |
||||
_traceManager.accumulateStorageChanges(this.context.stepIndex, this.context.address, {}, (error, storageChanges) => { |
||||
if (!error) { |
||||
this.storageChanges = storageChanges |
||||
} else { |
||||
console.log(error) |
||||
} |
||||
}) |
||||
} |
||||
|
||||
/** |
||||
* return the storage for the current context (address and vm trace index) |
||||
* by default now returns the range 0 => 1000 |
||||
* |
||||
* @param {Function} - callback - contains a map: [hashedKey] = {key, hashedKey, value} |
||||
*/ |
||||
storageRange (callback) { |
||||
this.storageResolver.storageRange(this.context.tx, this.context.stepIndex, this.context.address, (error, storage) => { |
||||
if (error) { |
||||
callback(error) |
||||
} else { |
||||
callback(null, Object.assign({}, storage, this.storageChanges)) |
||||
} |
||||
}) |
||||
} |
||||
|
||||
/** |
||||
* return a slot value for the current context (address and vm trace index) |
||||
* @param {String} - slot - slot key (not hashed key!) |
||||
* @param {Function} - callback - {key, hashedKey, value} - |
||||
*/ |
||||
storageSlot (slot, callback) { |
||||
var hashed = helper.sha3_256(slot) |
||||
if (this.storageChanges[hashed]) { |
||||
return callback(null, this.storageChanges[hashed]) |
||||
} |
||||
this.storageResolver.storageSlot(hashed, this.context.tx, this.context.stepIndex, this.context.address, (error, storage) => { |
||||
if (error) { |
||||
callback(error) |
||||
} else { |
||||
callback(null, storage[hashed] !== undefined ? storage[hashed] : null) |
||||
} |
||||
}) |
||||
} |
||||
|
||||
/** |
||||
* return True if the storage at @arg address is complete |
||||
* |
||||
* @param {String} address - contract address |
||||
* @return {Bool} - return True if the storage at @arg address is complete |
||||
*/ |
||||
isComplete (address) { |
||||
return this.storageResolver.isComplete(address) |
||||
} |
||||
} |
||||
|
||||
module.exports = StorageViewer |
@ -0,0 +1,38 @@ |
||||
'use strict' |
||||
var util = require('../../src/helpers/util') |
||||
|
||||
class MockStorageResolver { |
||||
constructor (_storage) { |
||||
this.storage = {} |
||||
for (var k in _storage) { |
||||
var hashed = util.sha3_256(k) |
||||
this.storage[hashed] = { |
||||
hashed: hashed, |
||||
key: k, |
||||
value: _storage[k] |
||||
} |
||||
} |
||||
} |
||||
|
||||
storageRange (callback) { |
||||
callback(null, this.storage) |
||||
} |
||||
|
||||
storageSlot (slot, callback) { |
||||
var hashed = util.sha3_256(slot) |
||||
callback(null, this.storage[hashed]) |
||||
} |
||||
|
||||
isComplete (address) { |
||||
return true |
||||
} |
||||
|
||||
fromCache (address, slotKey) { |
||||
return this.storage[slotKey] |
||||
} |
||||
|
||||
toCache (address, storage, complete) { |
||||
} |
||||
} |
||||
|
||||
module.exports = MockStorageResolver |
Loading…
Reference in new issue