|
|
|
@ -1,9 +1,39 @@ |
|
|
|
|
const util = require('../util') |
|
|
|
|
const uiutil = require('../helpers/uiHelper') |
|
|
|
|
const ethutil = require('ethereumjs-util') |
|
|
|
|
import { hexConvert, hexListFromBNs, formatMemory } from '../util' |
|
|
|
|
import { normalizeHexAddress } from '../helpers/uiHelper' |
|
|
|
|
import { toChecksumAddress, BN, toBuffer, } from 'ethereumjs-util' |
|
|
|
|
const Web3 = require('web3') |
|
|
|
|
|
|
|
|
|
function web3VmProvider () { |
|
|
|
|
export class Web3VmProvider { |
|
|
|
|
|
|
|
|
|
web3 |
|
|
|
|
vm |
|
|
|
|
vmTraces |
|
|
|
|
txs |
|
|
|
|
txsReceipt |
|
|
|
|
processingHash |
|
|
|
|
processingAddress |
|
|
|
|
processingIndex |
|
|
|
|
previousDepth |
|
|
|
|
incr |
|
|
|
|
eth |
|
|
|
|
debug |
|
|
|
|
providers |
|
|
|
|
currentProvider |
|
|
|
|
storageCache |
|
|
|
|
lastProcessedStorageTxHash |
|
|
|
|
sha3Preimages |
|
|
|
|
sha3 |
|
|
|
|
toHex |
|
|
|
|
toAscii |
|
|
|
|
fromAscii |
|
|
|
|
fromDecimal |
|
|
|
|
fromWei |
|
|
|
|
toWei |
|
|
|
|
toBigNumber |
|
|
|
|
isAddress |
|
|
|
|
utils |
|
|
|
|
|
|
|
|
|
constructor () { |
|
|
|
|
this.web3 = new Web3() |
|
|
|
|
this.vm = null |
|
|
|
|
this.vmTraces = {} |
|
|
|
@ -40,9 +70,9 @@ function web3VmProvider () { |
|
|
|
|
this.toBigNumber = (...args) => this.web3.utils.toBN(...args) |
|
|
|
|
this.isAddress = (...args) => this.web3.utils.isAddress(...args) |
|
|
|
|
this.utils = Web3.utils || [] |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
web3VmProvider.prototype.setVM = function (vm) { |
|
|
|
|
setVM (vm) { |
|
|
|
|
if (this.vm === vm) return |
|
|
|
|
this.vm = vm |
|
|
|
|
this.vm.on('step', (data) => { |
|
|
|
@ -54,49 +84,49 @@ web3VmProvider.prototype.setVM = function (vm) { |
|
|
|
|
this.vm.on('beforeTx', (data) => { |
|
|
|
|
this.txWillProcess(this, data) |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
web3VmProvider.prototype.releaseCurrentHash = function () { |
|
|
|
|
releaseCurrentHash () { |
|
|
|
|
const ret = this.processingHash |
|
|
|
|
this.processingHash = undefined |
|
|
|
|
return ret |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
web3VmProvider.prototype.txWillProcess = function (self, data) { |
|
|
|
|
txWillProcess (self, data) { |
|
|
|
|
self.incr++ |
|
|
|
|
self.processingHash = util.hexConvert(data.hash()) |
|
|
|
|
self.processingHash = hexConvert(data.hash()) |
|
|
|
|
self.vmTraces[self.processingHash] = { |
|
|
|
|
gas: '0x0', |
|
|
|
|
return: '0x0', |
|
|
|
|
structLogs: [] |
|
|
|
|
} |
|
|
|
|
let tx = {} |
|
|
|
|
tx.hash = self.processingHash |
|
|
|
|
tx.from = ethutil.toChecksumAddress(util.hexConvert(data.getSenderAddress())) |
|
|
|
|
tx['hash'] = self.processingHash |
|
|
|
|
tx['from'] = toChecksumAddress(hexConvert(data.getSenderAddress())) |
|
|
|
|
if (data.to && data.to.length) { |
|
|
|
|
tx.to = ethutil.toChecksumAddress(util.hexConvert(data.to)) |
|
|
|
|
tx['to'] = toChecksumAddress(hexConvert(data.to)) |
|
|
|
|
} |
|
|
|
|
this.processingAddress = tx.to |
|
|
|
|
tx.data = util.hexConvert(data.data) |
|
|
|
|
tx.input = util.hexConvert(data.input) |
|
|
|
|
tx.gas = (new ethutil.BN(util.hexConvert(data.gas).replace('0x', ''), 16)).toString(10) |
|
|
|
|
this.processingAddress = tx['to'] |
|
|
|
|
tx['data'] = hexConvert(data.data) |
|
|
|
|
tx['input'] = hexConvert(data.input) |
|
|
|
|
tx['gas'] = (new BN(hexConvert(data.gas).replace('0x', ''), 16)).toString(10) |
|
|
|
|
if (data.value) { |
|
|
|
|
tx.value = util.hexConvert(data.value) |
|
|
|
|
tx['value'] = hexConvert(data.value) |
|
|
|
|
} |
|
|
|
|
self.txs[self.processingHash] = tx |
|
|
|
|
self.txsReceipt[self.processingHash] = tx |
|
|
|
|
self.storageCache[self.processingHash] = {} |
|
|
|
|
if (tx.to) { |
|
|
|
|
const account = ethutil.toBuffer(tx.to) |
|
|
|
|
if (tx['to']) { |
|
|
|
|
const account = toBuffer(tx['to']) |
|
|
|
|
self.vm.stateManager.dumpStorage(account, (storage) => { |
|
|
|
|
self.storageCache[self.processingHash][tx.to] = storage |
|
|
|
|
self.lastProcessedStorageTxHash[tx.to] = self.processingHash |
|
|
|
|
self.storageCache[self.processingHash][tx['to']] = storage |
|
|
|
|
self.lastProcessedStorageTxHash[tx['to']] = self.processingHash |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
this.processingIndex = 0 |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
web3VmProvider.prototype.txProcessed = function (self, data) { |
|
|
|
|
txProcessed (self, data) { |
|
|
|
|
const lastOp = self.vmTraces[self.processingHash].structLogs[self.processingIndex - 1] |
|
|
|
|
if (lastOp) { |
|
|
|
|
lastOp.error = lastOp.op !== 'RETURN' && lastOp.op !== 'STOP' && lastOp.op !== 'SELFDESTRUCT' |
|
|
|
@ -127,20 +157,20 @@ web3VmProvider.prototype.txProcessed = function (self, data) { |
|
|
|
|
self.txsReceipt[self.processingHash].status = `0x${status}` |
|
|
|
|
|
|
|
|
|
if (data.createdAddress) { |
|
|
|
|
const address = util.hexConvert(data.createdAddress) |
|
|
|
|
self.vmTraces[self.processingHash].return = ethutil.toChecksumAddress(address) |
|
|
|
|
self.txsReceipt[self.processingHash].contractAddress = ethutil.toChecksumAddress(address) |
|
|
|
|
const address = hexConvert(data.createdAddress) |
|
|
|
|
self.vmTraces[self.processingHash].return = toChecksumAddress(address) |
|
|
|
|
self.txsReceipt[self.processingHash].contractAddress = toChecksumAddress(address) |
|
|
|
|
} else if (data.execResult.returnValue) { |
|
|
|
|
self.vmTraces[self.processingHash].return = util.hexConvert(data.execResult.returnValue) |
|
|
|
|
self.vmTraces[self.processingHash].return = hexConvert(data.execResult.returnValue) |
|
|
|
|
} else { |
|
|
|
|
self.vmTraces[self.processingHash].return = '0x' |
|
|
|
|
} |
|
|
|
|
this.processingIndex = null |
|
|
|
|
this.processingAddress = null |
|
|
|
|
this.previousDepth = 0 |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
web3VmProvider.prototype.pushTrace = function (self, data) { |
|
|
|
|
pushTrace (self, data) { |
|
|
|
|
const depth = data.depth + 1 // geth starts the depth from 1
|
|
|
|
|
if (!self.processingHash) { |
|
|
|
|
console.log('no tx processing') |
|
|
|
@ -156,8 +186,8 @@ web3VmProvider.prototype.pushTrace = function (self, data) { |
|
|
|
|
previousopcode.invalidDepthChange = previousopcode.op !== 'RETURN' && previousopcode.op !== 'STOP' |
|
|
|
|
} |
|
|
|
|
const step = { |
|
|
|
|
stack: util.hexListFromBNs(data.stack), |
|
|
|
|
memory: util.formatMemory(data.memory), |
|
|
|
|
stack: hexListFromBNs(data.stack), |
|
|
|
|
memory: formatMemory(data.memory), |
|
|
|
|
storage: data.storage, |
|
|
|
|
op: data.opcode.name, |
|
|
|
|
pc: data.pc, |
|
|
|
@ -173,10 +203,10 @@ web3VmProvider.prototype.pushTrace = function (self, data) { |
|
|
|
|
this.storageCache[this.processingHash][this.processingAddress] = {} |
|
|
|
|
this.lastProcessedStorageTxHash[this.processingAddress] = this.processingHash |
|
|
|
|
} else { |
|
|
|
|
this.processingAddress = uiutil.normalizeHexAddress(step.stack[step.stack.length - 2]) |
|
|
|
|
this.processingAddress = ethutil.toChecksumAddress(this.processingAddress) |
|
|
|
|
this.processingAddress = normalizeHexAddress(step.stack[step.stack.length - 2]) |
|
|
|
|
this.processingAddress = toChecksumAddress(this.processingAddress) |
|
|
|
|
if (!self.storageCache[self.processingHash][this.processingAddress]) { |
|
|
|
|
const account = ethutil.toBuffer(this.processingAddress) |
|
|
|
|
const account = toBuffer(this.processingAddress) |
|
|
|
|
self.vm.stateManager.dumpStorage(account, function (storage) { |
|
|
|
|
self.storageCache[self.processingHash][self.processingAddress] = storage |
|
|
|
|
self.lastProcessedStorageTxHash[self.processingAddress] = self.processingHash |
|
|
|
@ -185,7 +215,7 @@ web3VmProvider.prototype.pushTrace = function (self, data) { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if (previousopcode && previousopcode.op === 'SHA3') { |
|
|
|
|
const preimage = getSha3Input(previousopcode.stack, previousopcode.memory) |
|
|
|
|
const preimage = this.getSha3Input(previousopcode.stack, previousopcode.memory) |
|
|
|
|
const imageHash = step.stack[step.stack.length - 1].replace('0x', '') |
|
|
|
|
self.sha3Preimages[imageHash] = { |
|
|
|
|
'preimage': preimage |
|
|
|
@ -194,19 +224,19 @@ web3VmProvider.prototype.pushTrace = function (self, data) { |
|
|
|
|
|
|
|
|
|
this.processingIndex++ |
|
|
|
|
this.previousDepth = depth |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
web3VmProvider.prototype.getCode = function (address, cb) { |
|
|
|
|
address = ethutil.toChecksumAddress(address) |
|
|
|
|
const account = ethutil.toBuffer(address) |
|
|
|
|
getCode (address, cb) { |
|
|
|
|
address = toChecksumAddress(address) |
|
|
|
|
const account = toBuffer(address) |
|
|
|
|
this.vm.stateManager.getContractCode(account, (error, result) => { |
|
|
|
|
cb(error, util.hexConvert(result)) |
|
|
|
|
cb(error, hexConvert(result)) |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
web3VmProvider.prototype.setProvider = function (provider) {} |
|
|
|
|
setProvider (provider) {} |
|
|
|
|
|
|
|
|
|
web3VmProvider.prototype.traceTransaction = function (txHash, options, cb) { |
|
|
|
|
traceTransaction (txHash, options, cb) { |
|
|
|
|
if (this.vmTraces[txHash]) { |
|
|
|
|
if (cb) { |
|
|
|
|
cb(null, this.vmTraces[txHash]) |
|
|
|
@ -216,11 +246,11 @@ web3VmProvider.prototype.traceTransaction = function (txHash, options, cb) { |
|
|
|
|
if (cb) { |
|
|
|
|
cb('unable to retrieve traces ' + txHash, null) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
web3VmProvider.prototype.storageRangeAt = function (blockNumber, txIndex, address, start, maxLength, cb) { // txIndex is the hash in the case of the VM
|
|
|
|
|
storageRangeAt (blockNumber, txIndex, address, start, maxLength, cb) { // txIndex is the hash in the case of the VM
|
|
|
|
|
// we don't use the range params here
|
|
|
|
|
address = ethutil.toChecksumAddress(address) |
|
|
|
|
address = toChecksumAddress(address) |
|
|
|
|
|
|
|
|
|
if (txIndex === 'latest') { |
|
|
|
|
txIndex = this.lastProcessedStorageTxHash[address] |
|
|
|
@ -234,11 +264,11 @@ web3VmProvider.prototype.storageRangeAt = function (blockNumber, txIndex, addres |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
cb('unable to retrieve storage ' + txIndex + ' ' + address) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
web3VmProvider.prototype.getBlockNumber = function (cb) { cb(null, 'vm provider') } |
|
|
|
|
getBlockNumber (cb) { cb(null, 'vm provider') } |
|
|
|
|
|
|
|
|
|
web3VmProvider.prototype.getTransaction = function (txHash, cb) { |
|
|
|
|
getTransaction (txHash, cb) { |
|
|
|
|
if (this.txs[txHash]) { |
|
|
|
|
if (cb) { |
|
|
|
|
cb(null, this.txs[txHash]) |
|
|
|
@ -248,9 +278,9 @@ web3VmProvider.prototype.getTransaction = function (txHash, cb) { |
|
|
|
|
if (cb) { |
|
|
|
|
cb('unable to retrieve tx ' + txHash, null) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
web3VmProvider.prototype.getTransactionReceipt = function (txHash, cb) { |
|
|
|
|
getTransactionReceipt (txHash, cb) { |
|
|
|
|
// same as getTransaction but return the created address also
|
|
|
|
|
if (this.txsReceipt[txHash]) { |
|
|
|
|
if (cb) { |
|
|
|
@ -261,49 +291,48 @@ web3VmProvider.prototype.getTransactionReceipt = function (txHash, cb) { |
|
|
|
|
if (cb) { |
|
|
|
|
cb('unable to retrieve txReceipt ' + txHash, null) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
web3VmProvider.prototype.getTransactionFromBlock = function (blockNumber, txIndex, cb) { |
|
|
|
|
getTransactionFromBlock (blockNumber, txIndex, cb) { |
|
|
|
|
const mes = 'not supposed to be needed by remix in vmmode' |
|
|
|
|
console.log(mes) |
|
|
|
|
if (cb) { |
|
|
|
|
cb(mes, null) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
web3VmProvider.prototype.preimage = function (hashedKey, cb) { |
|
|
|
|
preimage (hashedKey, cb) { |
|
|
|
|
hashedKey = hashedKey.replace('0x', '') |
|
|
|
|
cb(null, this.sha3Preimages[hashedKey] !== undefined ? this.sha3Preimages[hashedKey].preimage : null) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function getSha3Input (stack, memory) { |
|
|
|
|
getSha3Input (stack, memory) { |
|
|
|
|
let memoryStart = stack[stack.length - 1] |
|
|
|
|
let memoryLength = stack[stack.length - 2] |
|
|
|
|
const memStartDec = (new ethutil.BN(memoryStart.replace('0x', ''), 16)).toString(10) |
|
|
|
|
const memStartDec = (new BN(memoryStart.replace('0x', ''), 16)).toString(10) |
|
|
|
|
memoryStart = parseInt(memStartDec) * 2 |
|
|
|
|
const memLengthDec = (new ethutil.BN(memoryLength.replace('0x', ''), 16).toString(10)) |
|
|
|
|
const memLengthDec = (new BN(memoryLength.replace('0x', ''), 16).toString(10)) |
|
|
|
|
memoryLength = parseInt(memLengthDec) * 2 |
|
|
|
|
|
|
|
|
|
let i = Math.floor(memoryStart / 32) |
|
|
|
|
const maxIndex = Math.floor(memoryLength / 32) + i |
|
|
|
|
if (!memory[i]) { |
|
|
|
|
return emptyFill(memoryLength) |
|
|
|
|
return this.emptyFill(memoryLength) |
|
|
|
|
} |
|
|
|
|
let sha3Input = memory[i].slice(memoryStart - 32 * i) |
|
|
|
|
i++ |
|
|
|
|
while (i < maxIndex) { |
|
|
|
|
sha3Input += memory[i] ? memory[i] : emptyFill(32) |
|
|
|
|
sha3Input += memory[i] ? memory[i] : this.emptyFill(32) |
|
|
|
|
i++ |
|
|
|
|
} |
|
|
|
|
if (sha3Input.length < memoryLength) { |
|
|
|
|
const leftSize = memoryLength - sha3Input.length |
|
|
|
|
sha3Input += memory[i] ? memory[i].slice(0, leftSize) : emptyFill(leftSize) |
|
|
|
|
sha3Input += memory[i] ? memory[i].slice(0, leftSize) : this.emptyFill(leftSize) |
|
|
|
|
} |
|
|
|
|
return sha3Input |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function emptyFill (size) { |
|
|
|
|
emptyFill (size) { |
|
|
|
|
return (new Array(size)).join('0') |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
module.exports = web3VmProvider |
|
|
|
|