From d2161740a81d2692ca3487ff2150942cbc7b1a6a Mon Sep 17 00:00:00 2001 From: aniket-engg Date: Mon, 14 Dec 2020 18:53:19 +0530 Subject: [PATCH] trace analyser, cache and helper updated --- libs/remix-debug/src/trace/traceAnalyser.ts | 229 ++++++++++---------- libs/remix-debug/src/trace/traceCache.ts | 199 +++++++++-------- libs/remix-debug/src/trace/traceHelper.ts | 2 +- 3 files changed, 224 insertions(+), 206 deletions(-) diff --git a/libs/remix-debug/src/trace/traceAnalyser.ts b/libs/remix-debug/src/trace/traceAnalyser.ts index 139054414f..d57a6ca5cb 100644 --- a/libs/remix-debug/src/trace/traceAnalyser.ts +++ b/libs/remix-debug/src/trace/traceAnalyser.ts @@ -1,139 +1,144 @@ 'use strict' const traceHelper = require('./traceHelper') -function TraceAnalyser (_cache) { - this.traceCache = _cache - this.trace = null -} +export class TraceAnalyser { -TraceAnalyser.prototype.analyse = function (trace, tx) { - this.trace = trace - this.traceCache.pushStoreChanges(0, tx.to) - let context = { - storageContext: [tx.to], - currentCallIndex: 0, - lastCallIndex: 0 - } - const callStack = [tx.to] - this.traceCache.pushCall(trace[0], 0, callStack[0], callStack.slice(0)) - if (traceHelper.isContractCreation(tx.to)) { - this.traceCache.pushContractCreation(tx.to, tx.input) + traceCache + trace + + constructor (_cache) { + this.traceCache = _cache + this.trace = null } - this.buildCalldata(0, this.trace[0], tx, true) - for (let k = 0; k < this.trace.length; k++) { - const step = this.trace[k] - this.buildMemory(k, step) - context = this.buildDepth(k, step, tx, callStack, context) - context = this.buildStorage(k, step, context) - this.buildReturnValues(k, step) - } - return true -} -TraceAnalyser.prototype.buildReturnValues = function (index, step) { - if (traceHelper.isReturnInstruction(step)) { - let offset = 2 * parseInt(step.stack[step.stack.length - 1], 16) - const size = 2 * parseInt(step.stack[step.stack.length - 2], 16) - const memory = this.trace[this.traceCache.memoryChanges[this.traceCache.memoryChanges.length - 1]].memory - const noOfReturnParams = size / 64 - const memoryInString = memory.join('') - let returnParamsObj = [] - for (let i = 0; i < noOfReturnParams; i++) { - returnParamsObj.push('0x' + memoryInString.substring(offset, offset + 64)) - offset += 64 + analyse (trace, tx) { + this.trace = trace + this.traceCache.pushStoreChanges(0, tx.to) + let context = { + storageContext: [tx.to], + currentCallIndex: 0, + lastCallIndex: 0 } + const callStack = [tx.to] + this.traceCache.pushCall(trace[0], 0, callStack[0], callStack.slice(0)) + if (traceHelper.isContractCreation(tx.to)) { + this.traceCache.pushContractCreation(tx.to, tx.input) + } + this.buildCalldata(0, this.trace[0], tx, true) + for (let k = 0; k < this.trace.length; k++) { + const step = this.trace[k] + this.buildMemory(k, step) + context = this.buildDepth(k, step, tx, callStack, context) + context = this.buildStorage(k, step, context) + this.buildReturnValues(k, step) + } + return true + } - this.traceCache.pushReturnValue(index, returnParamsObj) + buildReturnValues (index, step) { + if (traceHelper.isReturnInstruction(step)) { + let offset = 2 * parseInt(step.stack[step.stack.length - 1], 16) + const size = 2 * parseInt(step.stack[step.stack.length - 2], 16) + const memory = this.trace[this.traceCache.memoryChanges[this.traceCache.memoryChanges.length - 1]].memory + const noOfReturnParams = size / 64 + const memoryInString = memory.join('') + let returnParamsObj = [] + for (let i = 0; i < noOfReturnParams; i++) { + returnParamsObj.push('0x' + memoryInString.substring(offset, offset + 64)) + offset += 64 + } + + this.traceCache.pushReturnValue(index, returnParamsObj) + } } -} -TraceAnalyser.prototype.buildCalldata = function (index, step, tx, newContext) { - let calldata = '' - if (index === 0) { - calldata = tx.input - this.traceCache.pushCallDataChanges(index, calldata) - } else if (!newContext) { - const lastCall = this.traceCache.callsData[this.traceCache.callDataChanges[this.traceCache.callDataChanges.length - 2]] - this.traceCache.pushCallDataChanges(index + 1, lastCall) - } else { - const memory = this.trace[this.traceCache.memoryChanges[this.traceCache.memoryChanges.length - 1]].memory - const callStep = this.trace[index] - const stack = callStep.stack - let offset = '' - let size = '' - if (callStep.op === 'DELEGATECALL') { - offset = 2 * parseInt(stack[stack.length - 3], 16) - size = 2 * parseInt(stack[stack.length - 4], 16) + buildCalldata (index, step, tx, newContext) { + let calldata = '' + if (index === 0) { + calldata = tx.input + this.traceCache.pushCallDataChanges(index, calldata) + } else if (!newContext) { + const lastCall = this.traceCache.callsData[this.traceCache.callDataChanges[this.traceCache.callDataChanges.length - 2]] + this.traceCache.pushCallDataChanges(index + 1, lastCall) } else { - offset = 2 * parseInt(stack[stack.length - 4], 16) - size = 2 * parseInt(stack[stack.length - 5], 16) + const memory = this.trace[this.traceCache.memoryChanges[this.traceCache.memoryChanges.length - 1]].memory + const callStep = this.trace[index] + const stack = callStep.stack + let offset = 0 + let size = 0 + if (callStep.op === 'DELEGATECALL') { + offset = 2 * parseInt(stack[stack.length - 3], 16) + size = 2 * parseInt(stack[stack.length - 4], 16) + } else { + offset = 2 * parseInt(stack[stack.length - 4], 16) + size = 2 * parseInt(stack[stack.length - 5], 16) + } + calldata = '0x' + memory.join('').substr(offset, size) + this.traceCache.pushCallDataChanges(index + 1, calldata) } - calldata = '0x' + memory.join('').substr(offset, size) - this.traceCache.pushCallDataChanges(index + 1, calldata) } -} -TraceAnalyser.prototype.buildMemory = function (index, step) { - if (step.memory) { - this.traceCache.pushMemoryChanges(index) + buildMemory (index, step) { + if (step.memory) { + this.traceCache.pushMemoryChanges(index) + } } -} -TraceAnalyser.prototype.buildStorage = function (index, step, context) { - if (traceHelper.newContextStorage(step) && !traceHelper.isCallToPrecompiledContract(index, this.trace)) { - const calledAddress = traceHelper.resolveCalledAddress(index, this.trace) - if (calledAddress) { - context.storageContext.push(calledAddress) - } else { - console.log('unable to build storage changes. ' + index + ' does not match with a CALL. storage changes will be corrupted') + buildStorage (index, step, context) { + if (traceHelper.newContextStorage(step) && !traceHelper.isCallToPrecompiledContract(index, this.trace)) { + const calledAddress = traceHelper.resolveCalledAddress(index, this.trace) + if (calledAddress) { + context.storageContext.push(calledAddress) + } else { + console.log('unable to build storage changes. ' + index + ' does not match with a CALL. storage changes will be corrupted') + } + this.traceCache.pushStoreChanges(index + 1, context.storageContext[context.storageContext.length - 1]) + } else if (traceHelper.isSSTOREInstruction(step)) { + this.traceCache.pushStoreChanges(index + 1, context.storageContext[context.storageContext.length - 1], step.stack[step.stack.length - 1], step.stack[step.stack.length - 2]) + } else if (traceHelper.isReturnInstruction(step) || traceHelper.isStopInstruction(step)) { + context.storageContext.pop() + this.traceCache.pushStoreChanges(index + 1, context.storageContext[context.storageContext.length - 1]) + } else if (traceHelper.isRevertInstruction(step)) { + context.storageContext.pop() + this.traceCache.resetStoreChanges() } - this.traceCache.pushStoreChanges(index + 1, context.storageContext[context.storageContext.length - 1]) - } else if (traceHelper.isSSTOREInstruction(step)) { - this.traceCache.pushStoreChanges(index + 1, context.storageContext[context.storageContext.length - 1], step.stack[step.stack.length - 1], step.stack[step.stack.length - 2]) - } else if (traceHelper.isReturnInstruction(step) || traceHelper.isStopInstruction(step)) { - context.storageContext.pop() - this.traceCache.pushStoreChanges(index + 1, context.storageContext[context.storageContext.length - 1]) - } else if (traceHelper.isRevertInstruction(step)) { - context.storageContext.pop() - this.traceCache.resetStoreChanges() + return context } - return context -} -TraceAnalyser.prototype.buildDepth = function (index, step, tx, callStack, context) { - if (traceHelper.isCallInstruction(step) && !traceHelper.isCallToPrecompiledContract(index, this.trace)) { - let newAddress - if (traceHelper.isCreateInstruction(step)) { - newAddress = traceHelper.contractCreationToken(index) - callStack.push(newAddress) - const lastMemoryChange = this.traceCache.memoryChanges[this.traceCache.memoryChanges.length - 1] - this.traceCache.pushContractCreationFromMemory(index, newAddress, this.trace, lastMemoryChange) - } else { - newAddress = traceHelper.resolveCalledAddress(index, this.trace) - if (newAddress) { + buildDepth (index, step, tx, callStack, context) { + if (traceHelper.isCallInstruction(step) && !traceHelper.isCallToPrecompiledContract(index, this.trace)) { + let newAddress + if (traceHelper.isCreateInstruction(step)) { + newAddress = traceHelper.contractCreationToken(index) callStack.push(newAddress) + const lastMemoryChange = this.traceCache.memoryChanges[this.traceCache.memoryChanges.length - 1] + this.traceCache.pushContractCreationFromMemory(index, newAddress, this.trace, lastMemoryChange) } else { - console.log('unable to build depth changes. ' + index + ' does not match with a CALL. depth changes will be corrupted') + newAddress = traceHelper.resolveCalledAddress(index, this.trace) + if (newAddress) { + callStack.push(newAddress) + } else { + console.log('unable to build depth changes. ' + index + ' does not match with a CALL. depth changes will be corrupted') + } } - } - this.traceCache.pushCall(step, index + 1, newAddress, callStack.slice(0)) - this.buildCalldata(index, step, tx, true) - this.traceCache.pushSteps(index, context.currentCallIndex) - context.lastCallIndex = context.currentCallIndex - context.currentCallIndex = 0 - } else if (traceHelper.isReturnInstruction(step) || traceHelper.isStopInstruction(step) || step.error || step.invalidDepthChange) { - if (index < this.trace.length) { - callStack.pop() - this.traceCache.pushCall(step, index + 1, null, callStack.slice(0), step.error || step.invalidDepthChange) - this.buildCalldata(index, step, tx, false) + this.traceCache.pushCall(step, index + 1, newAddress, callStack.slice(0)) + this.buildCalldata(index, step, tx, true) + this.traceCache.pushSteps(index, context.currentCallIndex) + context.lastCallIndex = context.currentCallIndex + context.currentCallIndex = 0 + } else if (traceHelper.isReturnInstruction(step) || traceHelper.isStopInstruction(step) || step.error || step.invalidDepthChange) { + if (index < this.trace.length) { + callStack.pop() + this.traceCache.pushCall(step, index + 1, null, callStack.slice(0), step.error || step.invalidDepthChange) + this.buildCalldata(index, step, tx, false) + this.traceCache.pushSteps(index, context.currentCallIndex) + context.currentCallIndex = context.lastCallIndex + 1 + } + } else { this.traceCache.pushSteps(index, context.currentCallIndex) - context.currentCallIndex = context.lastCallIndex + 1 + context.currentCallIndex++ } - } else { - this.traceCache.pushSteps(index, context.currentCallIndex) - context.currentCallIndex++ + return context } - return context } -module.exports = TraceAnalyser diff --git a/libs/remix-debug/src/trace/traceCache.ts b/libs/remix-debug/src/trace/traceCache.ts index 63c5bc9038..2c379cce3e 100644 --- a/libs/remix-debug/src/trace/traceCache.ts +++ b/libs/remix-debug/src/trace/traceCache.ts @@ -2,116 +2,129 @@ const remixLib = require('@remix-project/remix-lib') const helper = remixLib.util -function TraceCache () { - this.init() -} +export class TraceCache { -TraceCache.prototype.init = function () { - // ...Changes contains index in the vmtrace of the corresponding changes - - this.returnValues = {} - this.currentCall = null - this.callsTree = null - this.callsData = {} - this.contractCreation = {} - this.steps = {} - this.addresses = [] - - this.callDataChanges = [] - this.memoryChanges = [] - this.storageChanges = [] - this.sstore = {} // all sstore occurence in the trace -} + returnValues + currentCall + callsTree + callsData + contractCreation + steps + addresses + callDataChanges + memoryChanges + storageChanges + sstore -TraceCache.prototype.pushSteps = function (index, currentCallIndex) { - this.steps[index] = currentCallIndex -} + constructor() { + this.init() + } -TraceCache.prototype.pushCallDataChanges = function (value, calldata) { - this.callDataChanges.push(value) - this.callsData[value] = calldata -} -TraceCache.prototype.pushMemoryChanges = function (value) { - this.memoryChanges.push(value) -} + init () { + // ...Changes contains index in the vmtrace of the corresponding changes -// outOfGas has been removed because gas left logging is apparently made differently -// in the vm/geth/eth. TODO add the error property (with about the error in all clients) -TraceCache.prototype.pushCall = function (step, index, address, callStack, reverted) { - let validReturnStep = step.op === 'RETURN' || step.op === 'STOP' - if ((validReturnStep || reverted) && (this.currentCall)) { - this.currentCall.call.return = index - 1 - if (!validReturnStep) { - this.currentCall.call.reverted = reverted - } - var parent = this.currentCall.parent - this.currentCall = parent ? { call: parent.call, parent: parent.parent } : null - return + this.returnValues = {} + this.currentCall = null + this.callsTree = null + this.callsData = {} + this.contractCreation = {} + this.steps = {} + this.addresses = [] + this.callDataChanges = [] + this.memoryChanges = [] + this.storageChanges = [] + this.sstore = {} // all sstore occurence in the trace } - let call = { - op: step.op, - address: address, - callStack: callStack, - calls: {}, - start: index + + pushSteps (index, currentCallIndex) { + this.steps[index] = currentCallIndex } - this.addresses.push(address) - if (this.currentCall) { - this.currentCall.call.calls[index] = call - } else { - this.callsTree = { call: call } + + pushCallDataChanges (value, calldata) { + this.callDataChanges.push(value) + this.callsData[value] = calldata } - this.currentCall = { call: call, parent: this.currentCall } -} -TraceCache.prototype.pushReturnValue = function (step, value) { - this.returnValues[step] = value -} + pushMemoryChanges (value) { + this.memoryChanges.push(value) + } -TraceCache.prototype.pushContractCreationFromMemory = function (index, token, trace, lastMemoryChange) { - const memory = trace[lastMemoryChange].memory - const stack = trace[index].stack - const offset = 2 * parseInt(stack[stack.length - 2], 16) - const size = 2 * parseInt(stack[stack.length - 3], 16) - this.contractCreation[token] = '0x' + memory.join('').substr(offset, size) -} + // outOfGas has been removed because gas left logging is apparently made differently + // in the vm/geth/eth. TODO add the error property (with about the error in all clients) + pushCall (step, index, address, callStack, reverted) { + let validReturnStep = step.op === 'RETURN' || step.op === 'STOP' + if ((validReturnStep || reverted) && (this.currentCall)) { + this.currentCall.call.return = index - 1 + if (!validReturnStep) { + this.currentCall.call.reverted = reverted + } + var parent = this.currentCall.parent + this.currentCall = parent ? { call: parent.call, parent: parent.parent } : null + return + } + let call = { + op: step.op, + address: address, + callStack: callStack, + calls: {}, + start: index + } + this.addresses.push(address) + if (this.currentCall) { + this.currentCall.call.calls[index] = call + } else { + this.callsTree = { call: call } + } + this.currentCall = { call: call, parent: this.currentCall } + } -TraceCache.prototype.pushContractCreation = function (token, code) { - this.contractCreation[token] = code -} + pushReturnValue (step, value) { + this.returnValues[step] = value + } -TraceCache.prototype.resetStoreChanges = function (index, address, key, value) { - this.sstore = {} - this.storageChanges = [] -} + pushContractCreationFromMemory (index, token, trace, lastMemoryChange) { + const memory = trace[lastMemoryChange].memory + const stack = trace[index].stack + const offset = 2 * parseInt(stack[stack.length - 2], 16) + const size = 2 * parseInt(stack[stack.length - 3], 16) + this.contractCreation[token] = '0x' + memory.join('').substr(offset, size) + } -TraceCache.prototype.pushStoreChanges = function (index, address, key, value) { - this.sstore[index] = { - 'address': address, - 'key': key, - 'value': value, - 'hashedKey': helper.sha3_256(key) + pushContractCreation (token, code) { + this.contractCreation[token] = code + } + + resetStoreChanges (index, address, key, value) { + this.sstore = {} + this.storageChanges = [] } - this.storageChanges.push(index) -} -TraceCache.prototype.accumulateStorageChanges = function (index, address, storage) { - const ret = Object.assign({}, storage) - for (var k in this.storageChanges) { - const changesIndex = this.storageChanges[k] - if (changesIndex > index) { - return ret + pushStoreChanges (index, address, key, value) { + this.sstore[index] = { + 'address': address, + 'key': key, + 'value': value, + 'hashedKey': helper.sha3_256(key) } - var sstore = this.sstore[changesIndex] - if (sstore.address === address && sstore.key) { - ret[sstore.hashedKey] = { - key: sstore.key, - value: sstore.value + this.storageChanges.push(index) + } + + accumulateStorageChanges (index, address, storage) { + const ret = Object.assign({}, storage) + for (var k in this.storageChanges) { + const changesIndex = this.storageChanges[k] + if (changesIndex > index) { + return ret + } + var sstore = this.sstore[changesIndex] + if (sstore.address === address && sstore.key) { + ret[sstore.hashedKey] = { + key: sstore.key, + value: sstore.value + } } } + return ret } - return ret } - -module.exports = TraceCache diff --git a/libs/remix-debug/src/trace/traceHelper.ts b/libs/remix-debug/src/trace/traceHelper.ts index b97cd86557..08da638db6 100644 --- a/libs/remix-debug/src/trace/traceHelper.ts +++ b/libs/remix-debug/src/trace/traceHelper.ts @@ -2,7 +2,7 @@ const remixLib = require('@remix-project/remix-lib') const ui = remixLib.helpers.ui -module.exports = { +export = { // vmTraceIndex has to point to a CALL, CODECALL, ... resolveCalledAddress: function (vmTraceIndex, trace) { const step = trace[vmTraceIndex]