Commit ce3caa69 authored by yann300's avatar yann300

cache the preimages for a debug session (using the initial state)

parent e59e5644
......@@ -11,12 +11,13 @@ class Mapping extends RefType {
}
async decodeFromStorage (location, storageResolver) {
var mappingsPreimages
try {
var mappingsPreimages = await storageResolver.mappingPreimages()
mappingsPreimages = await storageResolver.mappingsLocation()
} catch (e) {
return {
value: '<error> ' + e.message,
type: this.type
value: e.message,
type: this.typeName
}
}
var mapSlot = util.normalizeHex(ethutil.bufferToHex(location.slot))
......
var global = require('../helpers/global')
module.exports = {
extractMappingPreimages: extractMappingPreimages
decodeMappingsKeys: decodeMappingsKeys
}
/**
* Uses the storageViewer to retrieve the storage and returns a mapping containing possible solidity mappping type location.
* like { "<mapping_slot>" : { "<mapping-key1>": preimageOf1 }, { "<mapping-key2>": preimageOf2 }, ... }
*
* @param {Object} address - storageViewer
* @return {Map} - solidity mapping location
*/
async function extractMappingPreimages (storageViewer) {
return new Promise((resolve, reject) => {
storageViewer.storageRange(function (error, storage) {
if (!error) {
decodeMappingsKeys(storage, (error, mappings) => {
if (error) {
reject(error)
} else {
resolve(mappings)
}
})
} else {
reject(error)
}
})
})
}
/**
* Uses the storageViewer to retrieve the storage and returns a mapping containing possible solidity mappping type location.
* extract the mappings location from the storage
* like { "<mapping_slot>" : { "<mapping-key1>": preimageOf1 }, { "<mapping-key2>": preimageOf2 }, ... }
*
* @param {Object} storage - storage given by storage Viewer (basically a mapping hashedkey : {key, value})
......
'use strict'
var traceHelper = require('../helpers/traceHelper')
var util = require('../helpers/global')
var mappingPreimages = require('./mappingPreimages')
/**
* Basically one instance is created for one debugging session.
* (TODO: one instance need to be shared over all the components)
*/
class StorageResolver {
constructor () {
this.storageByAddress = {}
this.preimagesMappingByAddress = {}
this.maxSize = 100
}
......@@ -22,6 +28,33 @@ class StorageResolver {
}
/**
* compute the mappgings type locations for the current address (cached for a debugging session)
* note: that only retrieve the first 100 items.
*
* @param {String} address - contract address
* @param {Object} address - storage
* @return {Function} - callback
*/
initialPreimagesMappings (tx, stepIndex, address, callback) {
if (this.preimagesMappingByAddress[address]) {
return callback(null, this.preimagesMappingByAddress[address])
}
this.storageRange(tx, stepIndex, address, (error, storage) => {
if (error) {
return callback(error)
}
mappingPreimages.decodeMappingsKeys(storage, (error, mappings) => {
if (error) {
callback(error)
} else {
this.preimagesMappingByAddress[address] = mappings
callback(null, mappings)
}
})
})
}
/**
* return a slot value for the given context (address and vm trace index)
*
* @param {String} - slot - slot key
......
'use strict'
var helper = require('../helpers/util')
var mappingPreimagesExtractor = require('./mappingPreimages')
var mappingPreimages = require('./mappingPreimages')
/**
* easier access to the storage resolver
* Basically one instance is created foreach execution step and foreach component that need it.
* (TODO: one instance need to be shared over all the components)
*/
class StorageViewer {
constructor (_context, _storageResolver, _traceManager) {
this.context = _context
this.storageResolver = _storageResolver
// contains [mappingSlot][mappingkey] = preimage
// this map is renewed for each execution step
// this map is shared among all the mapping types
this.mappingsPreimages = null
_traceManager.accumulateStorageChanges(this.context.stepIndex, this.context.address, {}, (error, storageChanges) => {
if (!error) {
this.storageChanges = storageChanges
......@@ -64,12 +65,58 @@ class StorageViewer {
return this.storageResolver.isComplete(address)
}
async mappingPreimages () {
if (!this.mappingsPreimages) {
this.mappingsPreimages = await mappingPreimagesExtractor.extractMappingPreimages(this)
/**
* return all the possible mappings locations for the current context (cached)
*
* @param {Function} callback
*/
async mappingsLocation () {
return new Promise((resolve, reject) => {
if (this.completeMappingsLocation) {
return resolve(this.completeMappingsLocation)
}
this.storageResolver.initialPreimagesMappings(this.context.tx, this.context.stepIndex, this.context.address, (error, initialMappingsLocation) => {
if (error) {
reject(error)
} else {
this.extractMappingsLocationChanges(this.storageChanges, (error, mappingsLocationChanges) => {
if (error) {
return reject(error)
}
this.completeMappingsLocation = Object.assign({}, initialMappingsLocation)
for (var key in mappingsLocationChanges) {
if (!initialMappingsLocation[key]) {
initialMappingsLocation[key] = {}
}
this.completeMappingsLocation[key] = Object.assign({}, initialMappingsLocation[key], mappingsLocationChanges[key])
}
resolve(this.completeMappingsLocation)
})
}
})
})
}
/**
* retrieve mapping location changes from the storage changes.
*
* @param {Function} callback
*/
extractMappingsLocationChanges (storageChanges, callback) {
if (this.mappingsLocationChanges) {
return callback(null, this.mappingsLocationChanges)
}
return this.mappingsPreimages
mappingPreimages.decodeMappingsKeys(storageChanges, (error, mappings) => {
if (!error) {
this.mappingsLocationChanges = mappings
return callback(null, this.mappingsLocationChanges)
} else {
callback(error)
}
})
}
}
module.exports = StorageViewer
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment