remove global web3 object from remix-core

pull/7/head
Iuri Matias 7 years ago
parent f71916c539
commit 7aa65f8611
  1. 16
      remix-core/src/code/codeManager.js
  2. 120
      remix-core/src/code/codeResolver.js
  3. 10
      remix-core/src/storage/mappingPreimages.js
  4. 204
      remix-core/src/storage/storageResolver.js
  5. 3
      remix-core/src/storage/storageViewer.js
  6. 8
      remix-core/src/trace/traceManager.js
  7. 7
      remix-core/src/trace/traceRetriever.js

@ -3,7 +3,7 @@ var remixLib = require('remix-lib')
var EventManager = remixLib.EventManager
var traceHelper = remixLib.helpers.trace
var SourceMappingDecoder = remixLib.SourceMappingDecoder
var codeResolver = require('./codeResolver')
var CodeResolver = require('./codeResolver')
/*
resolve contract code referenced by vmtrace in order to be used by asm listview.
@ -16,7 +16,7 @@ function CodeManager (_traceManager) {
this.event = new EventManager()
this.isLoading = false
this.traceManager = _traceManager
this.codeResolver = codeResolver
this.codeResolver = new CodeResolver({web3: this.traceManager.web3})
}
/**
@ -57,12 +57,13 @@ CodeManager.prototype.resolveStep = function (stepIndex, tx) {
* @param {Function} cb - callback function, return the bytecode
*/
CodeManager.prototype.getCode = function (address, cb) {
const self = this
if (traceHelper.isContractCreation(address)) {
var codes = codeResolver.getExecutingCodeFromCache(address)
var codes = this.codeResolver.getExecutingCodeFromCache(address)
if (!codes) {
this.traceManager.getContractCreationCode(address, function (error, hexCode) {
if (!error) {
codes = codeResolver.cacheExecutingCode(address, hexCode)
codes = self.codeResolver.cacheExecutingCode(address, hexCode)
cb(null, codes)
}
})
@ -70,7 +71,7 @@ CodeManager.prototype.getCode = function (address, cb) {
cb(null, codes)
}
} else {
codeResolver.resolveCode(address, function (address, code) {
this.codeResolver.resolveCode(address, function (address, code) {
cb(null, code)
})
}
@ -111,12 +112,13 @@ CodeManager.prototype.getFunctionFromStep = function (stepIndex, sourceMap, ast)
* @param {Function} callback - instruction index
*/
CodeManager.prototype.getInstructionIndex = function (address, step, callback) {
const self = this
this.traceManager.getCurrentPC(step, function (error, pc) {
if (error) {
console.log(error)
callback('Cannot retrieve current PC for ' + step, null)
} else {
var itemIndex = codeResolver.getInstructionIndex(address, pc)
var itemIndex = self.codeResolver.getInstructionIndex(address, pc)
callback(null, itemIndex)
}
})
@ -132,7 +134,7 @@ CodeManager.prototype.getInstructionIndex = function (address, step, callback) {
* @return {Object} return the ast node of the function
*/
CodeManager.prototype.getFunctionFromPC = function (address, pc, sourceMap, ast) {
var instIndex = codeResolver.getInstructionIndex(address, pc)
var instIndex = this.codeResolver.getInstructionIndex(address, pc)
return SourceMappingDecoder.findNodeAtInstructionIndex('FunctionDefinition', instIndex, sourceMap, ast)
}

@ -1,72 +1,74 @@
'use strict'
var codeUtils = require('./codeUtils')
var remixLib = require('remix-lib')
var global = remixLib.global
module.exports = {
bytecodeByAddress: {}, // bytes code by contract addesses
instructionsByAddress: {}, // assembly items instructions list by contract addesses
instructionsIndexByBytesOffset: {}, // mapping between bytes offset and instructions index.
function CodeResolver (options) {
this.web3 = options.web3
clear: function () {
this.bytecodeByAddress = {}
this.instructionsByAddress = {}
this.instructionsIndexByBytesOffset = {}
},
resolveCode: function (address, callBack) {
var cache = this.getExecutingCodeFromCache(address)
if (cache) {
callBack(address, cache)
return
}
var self = this
this.loadCode(address, function (code) {
callBack(address, self.cacheExecutingCode(address, code))
})
},
this.bytecodeByAddress = {} // bytes code by contract addesses
this.instructionsByAddress = {} // assembly items instructions list by contract addesses
this.instructionsIndexByBytesOffset = {} // mapping between bytes offset and instructions index.
}
loadCode: function (address, callback) {
console.log('loading new code from web3 ' + address)
global.web3.eth.getCode(address, function (error, result) {
if (error) {
console.log(error)
} else {
callback(result)
}
})
},
CodeResolver.prototype.clear = function () {
this.bytecodeByAddress = {}
this.instructionsByAddress = {}
this.instructionsIndexByBytesOffset = {}
}
cacheExecutingCode: function (address, hexCode) {
var codes = this.formatCode(hexCode)
this.bytecodeByAddress[address] = hexCode
this.instructionsByAddress[address] = codes.code
this.instructionsIndexByBytesOffset[address] = codes.instructionsIndexByBytesOffset
return this.getExecutingCodeFromCache(address)
},
CodeResolver.prototype.resolveCode = function (address, callBack) {
var cache = this.getExecutingCodeFromCache(address)
if (cache) {
callBack(address, cache)
return
}
formatCode: function (hexCode) {
var code = codeUtils.nameOpCodes(new Buffer(hexCode.substring(2), 'hex'))
return {
code: code[0],
instructionsIndexByBytesOffset: code[1]
}
},
var self = this
this.loadCode(address, function (code) {
callBack(address, self.cacheExecutingCode(address, code))
})
}
getExecutingCodeFromCache: function (address) {
if (this.instructionsByAddress[address]) {
return {
instructions: this.instructionsByAddress[address],
instructionsIndexByBytesOffset: this.instructionsIndexByBytesOffset[address],
bytecode: this.bytecodeByAddress[address]
}
CodeResolver.prototype.loadCode = function (address, callback) {
console.log('loading new code from web3 ' + address)
this.web3.eth.getCode(address, function (error, result) {
if (error) {
console.log(error)
} else {
return null
callback(result)
}
},
})
}
getInstructionIndex: function (address, pc) {
return this.getExecutingCodeFromCache(address).instructionsIndexByBytesOffset[pc]
CodeResolver.prototype.cacheExecutingCode = function (address, hexCode) {
var codes = this.formatCode(hexCode)
this.bytecodeByAddress[address] = hexCode
this.instructionsByAddress[address] = codes.code
this.instructionsIndexByBytesOffset[address] = codes.instructionsIndexByBytesOffset
return this.getExecutingCodeFromCache(address)
}
CodeResolver.prototype.formatCode = function (hexCode) {
var code = codeUtils.nameOpCodes(new Buffer(hexCode.substring(2), 'hex'))
return {
code: code[0],
instructionsIndexByBytesOffset: code[1]
}
}
CodeResolver.prototype.getExecutingCodeFromCache = function (address) {
if (this.instructionsByAddress[address]) {
return {
instructions: this.instructionsByAddress[address],
instructionsIndexByBytesOffset: this.instructionsIndexByBytesOffset[address],
bytecode: this.bytecodeByAddress[address]
}
} else {
return null
}
}
CodeResolver.prototype.getInstructionIndex = function (address, pc) {
return this.getExecutingCodeFromCache(address).instructionsIndexByBytesOffset[pc]
}
module.exports = CodeResolver

@ -1,5 +1,3 @@
var remixLib = require('remix-lib')
var global = remixLib.global
module.exports = {
decodeMappingsKeys: decodeMappingsKeys
@ -13,12 +11,12 @@ module.exports = {
* @param {Function} callback - calback
* @return {Map} - solidity mapping location (e.g { "<mapping_slot>" : { "<mapping-key1>": preimageOf1 }, { "<mapping-key2>": preimageOf2 }, ... })
*/
async function decodeMappingsKeys (storage, callback) {
async function decodeMappingsKeys (web3, storage, callback) {
var ret = {}
for (var hashedLoc in storage) {
var preimage
try {
preimage = await getPreimage(storage[hashedLoc].key)
preimage = await getPreimage(web3, storage[hashedLoc].key)
} catch (e) {
}
if (preimage) {
@ -42,9 +40,9 @@ async function decodeMappingsKeys (storage, callback) {
* @param {String} key - key to retrieve the preimage of
* @return {String} - preimage of the given key
*/
function getPreimage (key) {
function getPreimage (web3, key) {
return new Promise((resolve, reject) => {
global.web3Debug.debug.preimage(key.indexOf('0x') === 0 ? key : '0x' + key, function (error, preimage) {
web3.debug.preimage(key.indexOf('0x') === 0 ? key : '0x' + key, function (error, preimage) {
if (error) {
resolve(null)
} else {

@ -1,7 +1,6 @@
'use strict'
var remixLib = require('remix-lib')
var traceHelper = remixLib.helpers.trace
var global = remixLib.global
var mappingPreimages = require('./mappingPreimages')
/**
@ -9,34 +8,37 @@ var mappingPreimages = require('./mappingPreimages')
* (TODO: one instance need to be shared over all the components)
*/
class StorageResolver {
constructor () {
constructor (options) {
this.storageByAddress = {}
this.preimagesMappingByAddress = {}
this.maxSize = 100
this.web3 = options.web3
this.zeroSlot = '0x0000000000000000000000000000000000000000000000000000000000000000'
}
/**
* 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}
*/
* 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)
this.storageRangeInternal(this, this.zeroSlot, tx, stepIndex, address, callback)
}
/**
* 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
*/
* 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) {
const self = this
if (this.preimagesMappingByAddress[address]) {
return callback(null, this.preimagesMappingByAddress[address])
}
@ -44,7 +46,7 @@ class StorageResolver {
if (error) {
return callback(error)
}
mappingPreimages.decodeMappingsKeys(storage, (error, mappings) => {
mappingPreimages.decodeMappingsKeys(self.web3, storage, (error, mappings) => {
if (error) {
callback(error)
} else {
@ -56,16 +58,16 @@ class StorageResolver {
}
/**
* 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} -
*/
* 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) {
this.storageRangeInternal(this, slot, tx, stepIndex, address, function (error, storage) {
if (error) {
callback(error)
} else {
@ -75,91 +77,89 @@ class StorageResolver {
}
/**
* 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
*/
* 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, nextKey) => {
if (error) {
return callback(error)
/**
* 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.
*/
storageRangeInternal (self, slotKey, tx, stepIndex, address, callback) {
var cached = this.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)
}
if (!storage[slotKey] && slotKey !== zeroSlot) { // we don't cache the zero slot (could lead to inconsistency)
storage[slotKey] = {
key: slotKey,
value: zeroSlot
this.storageRangeWeb3Call(tx, address, slotKey, self.maxSize, (error, storage, nextKey) => {
if (error) {
return callback(error)
}
}
toCache(self, address, storage)
if (slotKey === zeroSlot && !nextKey) { // only working if keys are sorted !!
self.storageByAddress[address].complete = true
}
callback(null, storage)
})
}
var zeroSlot = '0x0000000000000000000000000000000000000000000000000000000000000000'
if (!storage[slotKey] && slotKey !== self.zeroSlot) { // we don't cache the zero slot (could lead to inconsistency)
storage[slotKey] = {
key: slotKey,
value: self.zeroSlot
}
}
self.toCache(self, address, storage)
if (slotKey === self.zeroSlot && !nextKey) { // only working if keys are sorted !!
self.storageByAddress[address].complete = true
}
callback(null, 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
* @return {String} - either the entire known storage or a single value
*/
function fromCache (self, address) {
if (!self.storageByAddress[address]) {
return null
/**
* 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
*/
fromCache (self, address) {
if (!self.storageByAddress[address]) {
return null
}
return self.storageByAddress[address]
}
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] = {}
/**
* store the result of `storageRangeAtInternal`
*
* @param {String} address - contract address
* @param {Object} storage - result of `storageRangeAtInternal`, contains {key, hashedKey, value}
*/
toCache (self, address, storage) {
if (!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) {
if (traceHelper.isContractCreation(address)) {
callback(null, {}, null)
} else {
global.web3Debug.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.nextKey)
} else {
callback('the storage has not been provided')
}
})
storageRangeWeb3Call (tx, address, start, maxSize, callback) {
if (traceHelper.isContractCreation(address)) {
callback(null, {}, null)
} else {
this.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.nextKey)
} else {
callback('the storage has not been provided')
}
})
}
}
}

@ -12,6 +12,7 @@ class StorageViewer {
constructor (_context, _storageResolver, _traceManager) {
this.context = _context
this.storageResolver = _storageResolver
this.web3 = this.storageResolver.web3
this.initialMappingsLocationPromise = null
this.currentMappingsLocationPromise = null
_traceManager.accumulateStorageChanges(this.context.stepIndex, this.context.address, {}, (error, storageChanges) => {
@ -117,7 +118,7 @@ class StorageViewer {
if (this.mappingsLocationChanges) {
return callback(null, this.mappingsLocationChanges)
}
mappingPreimages.decodeMappingsKeys(storageChanges, (error, mappings) => {
mappingPreimages.decodeMappingsKeys(this.web3, storageChanges, (error, mappings) => {
if (!error) {
this.mappingsLocationChanges = mappings
return callback(null, this.mappingsLocationChanges)

@ -6,14 +6,14 @@ var TraceStepManager = require('./traceStepManager')
var remixLib = require('remix-lib')
var traceHelper = remixLib.helpers.trace
var util = remixLib.util
var global = remixLib.global
function TraceManager () {
function TraceManager (options) {
this.web3 = options.web3
this.isLoading = false
this.trace = null
this.traceCache = new TraceCache()
this.traceAnalyser = new TraceAnalyser(this.traceCache)
this.traceRetriever = new TraceRetriever()
this.traceRetriever = new TraceRetriever({web3: this.web3})
this.traceStepManager = new TraceStepManager(this.traceAnalyser)
this.tx
}
@ -22,7 +22,7 @@ function TraceManager () {
TraceManager.prototype.resolveTrace = function (tx, callback) {
this.tx = tx
this.init()
if (!global.web3) callback('web3 not loaded', false)
if (!this.web3) callback('web3 not loaded', false)
this.isLoading = true
var self = this
this.traceRetriever.getTrace(tx.hash, function (error, result) {

@ -1,8 +1,7 @@
'use strict'
var remixLib = require('remix-lib')
var global = remixLib.global
function TraceRetriever () {
function TraceRetriever (options) {
this.web3 = options.web3
}
TraceRetriever.prototype.getTrace = function (txHash, callback) {
@ -12,7 +11,7 @@ TraceRetriever.prototype.getTrace = function (txHash, callback) {
disableStack: false,
fullStorage: false
}
global.web3Debug.debug.traceTransaction(txHash, options, function (error, result) {
this.web3.debug.traceTransaction(txHash, options, function (error, result) {
callback(error, result)
})
}

Loading…
Cancel
Save