Commit 34bc1cd6 authored by aniket-engg's avatar aniket-engg Committed by Aniket

providers, init, util, txRunner

parent 142d6478
...@@ -9,6 +9,7 @@ const Web3VMProvider = require('../web3Provider/web3VmProvider') ...@@ -9,6 +9,7 @@ const Web3VMProvider = require('../web3Provider/web3VmProvider')
const LogsManager = require('./logsManager.js') const LogsManager = require('./logsManager.js')
declare let ethereum: any;
let web3 let web3
if (typeof window !== 'undefined' && typeof window['ethereum'] !== 'undefined') { if (typeof window !== 'undefined' && typeof window['ethereum'] !== 'undefined') {
var injectedProvider = window['ethereum'] var injectedProvider = window['ethereum']
......
'use strict' 'use strict'
import { Transaction } from 'ethereumjs-tx' import { Transaction } from 'ethereumjs-tx'
import { Block } from 'ethereumjs-block' import { Block } from 'ethereumjs-block'
import { BN } from 'ethereumjs-util' import { BN, bufferToHex } from 'ethereumjs-util'
import { ExecutionContext } from './execution-context' import { ExecutionContext } from './execution-context'
const EventManager = require('../eventManager') const EventManager = require('../eventManager')
......
'use strict' 'use strict'
const Web3 = require('web3') import Web3 from 'web3'
module.exports = { export function loadWeb3 (url = 'http://localhost:8545') {
loadWeb3: function (url) { const web3 = new Web3()
if (!url) url = 'http://localhost:8545' web3.setProvider(new Web3.providers.HttpProvider(url))
const web3 = new Web3() this.extend(web3)
web3.setProvider(new web3.providers.HttpProvider(url)) return web3
this.extend(web3) }
return web3
},
extendWeb3: function (web3) { export function extendWeb3 (web3) {
this.extend(web3) this.extend(web3)
}, }
setProvider: function (web3, url) { export function setProvider (web3, url) {
web3.setProvider(new web3.providers.HttpProvider(url)) web3.setProvider(new web3.providers.HttpProvider(url))
}, }
web3DebugNode: function (network) { export function web3DebugNode (network) {
if (web3DebugNodes[network]) { const web3DebugNodes = {
return this.loadWeb3(web3DebugNodes[network]) 'Main': 'https://gethmainnet.komputing.org',
} 'Rinkeby': 'https://remix-rinkeby.ethdevops.io',
return null 'Ropsten': 'https://remix-ropsten.ethdevops.io',
}, 'Goerli': 'https://remix-goerli.ethdevops.io',
'Kovan': 'https://remix-kovan.ethdevops.io'
}
if (web3DebugNodes[network]) {
return this.loadWeb3(web3DebugNodes[network])
}
return null
}
extend: function (web3) { export function extend (web3) {
if (!web3.extend) { if (!web3.extend) {
return return
} }
...@@ -65,12 +70,3 @@ module.exports = { ...@@ -65,12 +70,3 @@ module.exports = {
}) })
} }
} }
}
const web3DebugNodes = {
'Main': 'https://gethmainnet.komputing.org',
'Rinkeby': 'https://remix-rinkeby.ethdevops.io',
'Ropsten': 'https://remix-ropsten.ethdevops.io',
'Goerli': 'https://remix-goerli.ethdevops.io',
'Kovan': 'https://remix-kovan.ethdevops.io'
}
...@@ -10,146 +10,152 @@ import { BN, bufferToHex, keccak, setLengthLeft } from 'ethereumjs-util' ...@@ -10,146 +10,152 @@ import { BN, bufferToHex, keccak, setLengthLeft } from 'ethereumjs-util'
- swarm hash extraction - swarm hash extraction
- bytecode comparison - bytecode comparison
*/ */
module.exports = {
/* /*
ints: IntArray ints: IntArray
*/ */
hexConvert: function (ints) { export function hexConvert (ints) {
let ret = '0x' let ret = '0x'
for (let i = 0; i < ints.length; i++) { for (let i = 0; i < ints.length; i++) {
const h = ints[i] const h = ints[i]
if (h) { if (h) {
ret += (h <= 0xf ? '0' : '') + h.toString(16) ret += (h <= 0xf ? '0' : '') + h.toString(16)
} else { } else {
ret += '00' ret += '00'
}
} }
return ret }
}, return ret
}
/** /**
* Converts a hex string to an array of integers. * Converts a hex string to an array of integers.
*/ */
hexToIntArray: function (hexString) { export function hexToIntArray (hexString) {
if (hexString.slice(0, 2) === '0x') { if (hexString.slice(0, 2) === '0x') {
hexString = hexString.slice(2) hexString = hexString.slice(2)
} }
const integers = [] const integers = []
for (let i = 0; i < hexString.length; i += 2) { for (let i = 0; i < hexString.length; i += 2) {
integers.push(parseInt(hexString.slice(i, i + 2), 16)) integers.push(parseInt(hexString.slice(i, i + 2), 16))
} }
return integers return integers
}, }
/* /*
ints: list of BNs ints: list of BNs
*/ */
hexListFromBNs: function (bnList) { export function hexListFromBNs (bnList) {
const ret = [] const ret = []
for (let k in bnList) { for (let k in bnList) {
const v = bnList[k] const v = bnList[k]
if (BN.isBN(v)) { if (BN.isBN(v)) {
ret.push('0x' + v.toString('hex', 64)) ret.push('0x' + v.toString('hex', 64))
} else { } else {
ret.push('0x' + (new BN(v)).toString('hex', 64)) // TEMP FIX TO REMOVE ONCE https://github.com/ethereumjs/ethereumjs-vm/pull/293 is released ret.push('0x' + (new BN(v)).toString('hex', 64)) // TEMP FIX TO REMOVE ONCE https://github.com/ethereumjs/ethereumjs-vm/pull/293 is released
}
} }
return ret }
}, return ret
}
/* /*
ints: list of IntArrays ints: list of IntArrays
*/ */
hexListConvert: function (intsList) { export function hexListConvert (intsList) {
const ret = [] const ret = []
for (let k in intsList) { for (let k in intsList) {
ret.push(this.hexConvert(intsList[k])) ret.push(this.hexConvert(intsList[k]))
} }
return ret return ret
}, }
/* /*
ints: ints: IntArray ints: ints: IntArray
*/ */
formatMemory: function (mem) { export function formatMemory (mem) {
const hexMem = this.hexConvert(mem).substr(2) const hexMem = this.hexConvert(mem).substr(2)
const ret = [] const ret = []
for (let k = 0; k < hexMem.length; k += 32) { for (let k = 0; k < hexMem.length; k += 32) {
const row = hexMem.substr(k, 32) const row = hexMem.substr(k, 32)
ret.push(row) ret.push(row)
} }
return ret return ret
}, }
/* /*
Binary Search: Binary Search:
Assumes that @arg array is sorted increasingly Assumes that @arg array is sorted increasingly
return largest i such that array[i] <= target; return -1 if array[0] > target || array is empty return largest i such that array[i] <= target; return -1 if array[0] > target || array is empty
*/ */
findLowerBound: function (target, array) { export function findLowerBound (target, array) {
let start = 0 let start = 0
let length = array.length let length = array.length
while (length > 0) { while (length > 0) {
const half = length >> 1 const half = length >> 1
const middle = start + half const middle = start + half
if (array[middle] <= target) { if (array[middle] <= target) {
length = length - 1 - half length = length - 1 - half
start = middle + 1 start = middle + 1
} else { } else {
length = half length = half
}
} }
return start - 1 }
}, return start - 1
}
/* /*
Binary Search: Binary Search:
Assumes that @arg array is sorted increasingly Assumes that @arg array is sorted increasingly
return largest array[i] such that array[i] <= target; return null if array[0] > target || array is empty return largest array[i] such that array[i] <= target; return null if array[0] > target || array is empty
*/ */
findLowerBoundValue: function (target, array) { export function findLowerBoundValue (target, array) {
const index = this.findLowerBound(target, array) const index = this.findLowerBound(target, array)
return index >= 0 ? array[index] : null return index >= 0 ? array[index] : null
}, }
/* /*
Binary Search: Binary Search:
Assumes that @arg array is sorted increasingly Assumes that @arg array is sorted increasingly
return Return i such that |array[i] - target| is smallest among all i and -1 for an empty array. return Return i such that |array[i] - target| is smallest among all i and -1 for an empty array.
Returns the smallest i for multiple candidates. Returns the smallest i for multiple candidates.
*/
export function findClosestIndex (target, array): number {
if (array.length === 0) {
return -1
}
const index = this.findLowerBound(target, array)
if (index < 0) {
return 0
} else if (index >= array.length - 1) {
return array.length - 1
} else {
const middle = (array[index] + array[index + 1]) / 2
return target <= middle ? index : index + 1
}
}
/**
* Find the call from @args rootCall which contains @args index (recursive)
*
* @param {Int} index - index of the vmtrace
* @param {Object} rootCall - call tree, built by the trace analyser
* @return {Object} - return the call which include the @args index
*/ */
findClosestIndex: function (target, array) { export function findCall (index, rootCall) {
if (array.length === 0) { const ret = buildCallPath(index, rootCall)
return -1 return ret[ret.length - 1]
} }
const index = this.findLowerBound(target, array)
if (index < 0) {
return 0
} else if (index >= array.length - 1) {
return array.length - 1
} else {
const middle = (array[index] + array[index + 1]) / 2
return target <= middle ? index : index + 1
}
},
/** /**
* Find the call from @args rootCall which contains @args index (recursive) * Find calls path from @args rootCall which leads to @args index (recursive)
* *
* @param {Int} index - index of the vmtrace * @param {Int} index - index of the vmtrace
* @param {Object} rootCall - call tree, built by the trace analyser * @param {Object} rootCall - call tree, built by the trace analyser
* @return {Object} - return the call which include the @args index * @return {Array} - return the calls path to @args index
*/ */
findCall: findCall, export function buildCallPath (index, rootCall) {
const ret = []
/** findCallInternal(index, rootCall, ret)
* Find calls path from @args rootCall which leads to @args index (recursive) return ret
* }
* @param {Int} index - index of the vmtrace
* @param {Object} rootCall - call tree, built by the trace analyser
* @return {Array} - return the calls path to @args index
*/
buildCallPath: buildCallPath,
/** /**
* sha3 the given @arg value (left pad to 32 bytes) * sha3 the given @arg value (left pad to 32 bytes)
...@@ -157,113 +163,117 @@ module.exports = { ...@@ -157,113 +163,117 @@ module.exports = {
* @param {String} value - value to sha3 * @param {String} value - value to sha3
* @return {Object} - return sha3ied value * @return {Object} - return sha3ied value
*/ */
sha3_256: function (value) { export function sha3_256 (value) {
if (typeof value === 'string' && value.indexOf('0x') !== 0) { if (typeof value === 'string' && value.indexOf('0x') !== 0) {
value = '0x' + value value = '0x' + value
} }
let ret: any = bufferToHex(setLengthLeft(value, 32)) let ret: any = bufferToHex(setLengthLeft(value, 32))
ret = keccak(ret) ret = keccak(ret)
return bufferToHex(ret) return bufferToHex(ret)
}, }
/** /**
* return a regex which extract the swarmhash from the bytecode. * return a regex which extract the swarmhash from the bytecode.
* *
* @return {RegEx} * @return {RegEx}
*/ */
swarmHashExtraction: function () { export function swarmHashExtraction () {
return /a165627a7a72305820([0-9a-f]{64})0029$/ return /a165627a7a72305820([0-9a-f]{64})0029$/
}, }
/** /**
* return a regex which extract the swarmhash from the bytecode, from POC 0.3 * return a regex which extract the swarmhash from the bytecode, from POC 0.3
* *
* @return {RegEx} * @return {RegEx}
*/ */
swarmHashExtractionPOC31: function () { export function swarmHashExtractionPOC31 () {
return /a265627a7a72315820([0-9a-f]{64})64736f6c6343([0-9a-f]{6})0032$/ return /a265627a7a72315820([0-9a-f]{64})64736f6c6343([0-9a-f]{6})0032$/
}, }
/** /**
* return a regex which extract the swarmhash from the bytecode, from POC 0.3 * return a regex which extract the swarmhash from the bytecode, from POC 0.3
* *
* @return {RegEx} * @return {RegEx}
*/ */
swarmHashExtractionPOC32: function () { export function swarmHashExtractionPOC32 () {
return /a265627a7a72305820([0-9a-f]{64})64736f6c6343([0-9a-f]{6})0032$/ return /a265627a7a72305820([0-9a-f]{64})64736f6c6343([0-9a-f]{6})0032$/
}, }
/** /**
* return a regex which extract the cbor encoded metadata : {"ipfs": <IPFS hash>, "solc": <compiler version>} from the bytecode. * return a regex which extract the cbor encoded metadata : {"ipfs": <IPFS hash>, "solc": <compiler version>} from the bytecode.
* ref https://solidity.readthedocs.io/en/v0.6.6/metadata.html?highlight=ipfs#encoding-of-the-metadata-hash-in-the-bytecode * ref https://solidity.readthedocs.io/en/v0.6.6/metadata.html?highlight=ipfs#encoding-of-the-metadata-hash-in-the-bytecode
* @return {RegEx} * @return {RegEx}
*/ */
cborEncodedValueExtraction: function () { export function cborEncodedValueExtraction () {
return /64697066735822([0-9a-f]{68})64736f6c6343([0-9a-f]{6})0033$/ return /64697066735822([0-9a-f]{68})64736f6c6343([0-9a-f]{6})0033$/
}, }
extractcborMetadata: function (value) { export function extractcborMetadata (value) {
return value.replace(this.cborEncodedValueExtraction(), '') return value.replace(this.cborEncodedValueExtraction(), '')
}, }
extractSwarmHash: function (value) { export function extractSwarmHash (value) {
value = value.replace(this.swarmHashExtraction(), '') value = value.replace(this.swarmHashExtraction(), '')
value = value.replace(this.swarmHashExtractionPOC31(), '') value = value.replace(this.swarmHashExtractionPOC31(), '')
value = value.replace(this.swarmHashExtractionPOC32(), '') value = value.replace(this.swarmHashExtractionPOC32(), '')
return value return value
}, }
/** /**
* Compare bytecode. return true if the code is equal (handle swarm hash and library references) * Compare bytecode. return true if the code is equal (handle swarm hash and library references)
* @param {String} code1 - the bytecode that is actually deployed (contains resolved library reference and a potentially different swarmhash) * @param {String} code1 - the bytecode that is actually deployed (contains resolved library reference and a potentially different swarmhash)
* @param {String} code2 - the bytecode generated by the compiler (contains unresolved library reference and a potentially different swarmhash) * @param {String} code2 - the bytecode generated by the compiler (contains unresolved library reference and a potentially different swarmhash)
this will return false if the generated bytecode is empty (asbtract contract cannot be deployed) this will return false if the generated bytecode is empty (asbtract contract cannot be deployed)
* *
* @return {bool} * @return {bool}
*/ */
compareByteCode: function (code1, code2) { export function compareByteCode (code1, code2) {
if (code1 === code2) return true if (code1 === code2) return true
if (code2 === '0x') return false // abstract contract. see comment if (code2 === '0x') return false // abstract contract. see comment
if (code2.substr(2, 46) === '7300000000000000000000000000000000000000003014') { if (code2.substr(2, 46) === '7300000000000000000000000000000000000000003014') {
// testing the following signature: PUSH20 00..00 ADDRESS EQ // testing the following signature: PUSH20 00..00 ADDRESS EQ
// in the context of a library, that slot contains the address of the library (pushed by the compiler to avoid calling library other than with a DELEGATECALL) // in the context of a library, that slot contains the address of the library (pushed by the compiler to avoid calling library other than with a DELEGATECALL)
// if code2 is not a library, well we still suppose that the comparison remain relevant even if we remove some information from `code1` // if code2 is not a library, well we still suppose that the comparison remain relevant even if we remove some information from `code1`
code1 = replaceLibReference(code1, 4) code1 = replaceLibReference(code1, 4)
} }
let pos = -1 let pos = -1
while ((pos = code2.search(/__(.*)__/)) !== -1) { while ((pos = code2.search(/__(.*)__/)) !== -1) {
code2 = replaceLibReference(code2, pos) code2 = replaceLibReference(code2, pos)
code1 = replaceLibReference(code1, pos) code1 = replaceLibReference(code1, pos)
} }
code1 = this.extractSwarmHash(code1) code1 = this.extractSwarmHash(code1)
code1 = this.extractcborMetadata(code1) code1 = this.extractcborMetadata(code1)
code2 = this.extractSwarmHash(code2) code2 = this.extractSwarmHash(code2)
code2 = this.extractcborMetadata(code2) code2 = this.extractcborMetadata(code2)
if (code1 && code2 && code1.indexOf(code2) === 0) { if (code1 && code2 && code1.indexOf(code2) === 0) {
return true return true
} }
return false return false
}, }
groupBy: groupBy, /* util extracted out from remix-ide. @TODO split this file, cause it mix real util fn with solidity related stuff ... */
concatWithSeperator: concatWithSeperator, export function groupBy (arr, key) {
escapeRegExp: escapeRegExp return arr.reduce((sum, item) => {
const groupByVal = item[key]
const groupedItems = sum[groupByVal] || []
groupedItems.push(item)
sum[groupByVal] = groupedItems
return sum
}, {})
} }
function replaceLibReference (code, pos) { export function concatWithSeperator (list, seperator) {
return code.substring(0, pos) + '0000000000000000000000000000000000000000' + code.substring(pos + 40) return list.reduce((sum, item) => sum + item + seperator, '').slice(0, -seperator.length)
} }
function buildCallPath (index, rootCall) { export function escapeRegExp (str) {
const ret = [] return str.replace(/[-[\]/{}()+?.\\^$|]/g, '\\$&')
findCallInternal(index, rootCall, ret)
return ret
} }
function findCall (index, rootCall) {
const ret = buildCallPath(index, rootCall) function replaceLibReference (code, pos) {
return ret[ret.length - 1] return code.substring(0, pos) + '0000000000000000000000000000000000000000' + code.substring(pos + 40)
} }
function findCallInternal (index, rootCall, callsPath) { function findCallInternal (index, rootCall, callsPath) {
...@@ -279,22 +289,3 @@ function findCallInternal (index, rootCall, callsPath) { ...@@ -279,22 +289,3 @@ function findCallInternal (index, rootCall, callsPath) {
} }
return ret return ret
} }
/* util extracted out from remix-ide. @TODO split this file, cause it mix real util fn with solidity related stuff ... */
function groupBy (arr, key) {
return arr.reduce((sum, item) => {
const groupByVal = item[key]
const groupedItems = sum[groupByVal] || []
groupedItems.push(item)
sum[groupByVal] = groupedItems
return sum
}, {})
}
function concatWithSeperator (list, seperator) {
return list.reduce((sum, item) => sum + item + seperator, '').slice(0, -seperator.length)
}
function escapeRegExp (str) {
return str.replace(/[-[\]/{}()+?.\\^$|]/g, '\\$&')
}
function dummyProvider () { export class dummyProvider {
this.eth = {} eth
this.debug = {} debug
this.eth.getCode = (address, cb) => { return this.getCode(address, cb) } providers
this.eth.getTransaction = (hash, cb) => { return this.getTransaction(hash, cb) } currentProvider
this.eth.getTransactionFromBlock = (blockNumber, txIndex, cb) => { return this.getTransactionFromBlock(blockNumber, txIndex, cb) }
this.eth.getBlockNumber = (cb) => { return this.getBlockNumber(cb) } constructor() {
this.debug.traceTransaction = (hash, options, cb) => { return this.traceTransaction(hash, options, cb) } this.eth = {}
this.debug.storageRangeAt = (blockNumber, txIndex, address, start, end, maxLength, cb) => { return this.storageRangeAt(blockNumber, txIndex, address, start, end, maxLength, cb) } this.debug = {}
this.providers = { 'HttpProvider': function (url) {} } this.eth.getCode = (address, cb) => { return this.getCode(address, cb) }
this.currentProvider = {'host': ''} this.eth.getTransaction = (hash, cb) => { return this.getTransaction(hash, cb) }
} this.eth.getTransactionFromBlock = (blockNumber, txIndex, cb) => { return this.getTransactionFromBlock(blockNumber, txIndex, cb) }
this.eth.getBlockNumber = (cb) => { return this.getBlockNumber(cb) }
this.debug.traceTransaction = (hash, options, cb) => { return this.traceTransaction(hash, options, cb) }
this.debug.storageRangeAt = (blockNumber, txIndex, address, start, end, maxLength, cb) => { return this.storageRangeAt(blockNumber, txIndex, address, start, end, maxLength, cb) }
this.providers = { 'HttpProvider': function (url) {} }
this.currentProvider = {'host': ''}
}
dummyProvider.prototype.getCode = function (address, cb) { getCode (address, cb) {
cb(null, '') cb(null, '')
} }
dummyProvider.prototype.setProvider = function (provider) {} setProvider (provider) {}
dummyProvider.prototype.traceTransaction = function (txHash, options, cb) { traceTransaction (txHash, options, cb) {
if (cb) { if (cb) {
cb(null, {}) cb(null, {})
}
return {}
} }
return {}
}
dummyProvider.prototype.storageRangeAt = function (blockNumber, txIndex, address, start, end, maxLength, cb) { storageRangeAt (blockNumber, txIndex, address, start, end, maxLength, cb) {
if (cb) { if (cb) {
cb(null, {}) cb(null, {})
}
return {}
} }
return {}
}
dummyProvider.prototype.getBlockNumber = function (cb) { cb(null, '') } getBlockNumber (cb) { cb(null, '') }
dummyProvider.prototype.getTransaction = function (txHash, cb) { getTransaction (txHash, cb) {
if (cb) { if (cb) {
cb(null, {}) cb(null, {})
}
return {}
} }
return {}
}
dummyProvider.prototype.getTransactionFromBlock = function (blockNumber, txIndex, cb) { getTransactionFromBlock (blockNumber, txIndex, cb) {
if (cb) { if (cb) {
cb(null, {}) cb(null, {})
}
return {}
} }
return {}
} }
module.exports = dummyProvider
const Web3VMProvider = require('./web3VmProvider') import { Web3VmProvider } from './web3VmProvider'
const init = require('../init') import { loadWeb3, extendWeb3 } from '../init'
function Web3Providers () { export class Web3Providers {
this.modes = {}
}
Web3Providers.prototype.addProvider = function (type, obj) { modes
if (type === 'INTERNAL') { constructor() {
const web3 = init.loadWeb3() this.modes = {}
this.addWeb3(type, web3)
} else if (type === 'vm') {
this.addVM(type, obj)
} else {
init.extendWeb3(obj)
this.addWeb3(type, obj)
} }
}
Web3Providers.prototype.get = function (type, cb) { addProvider (type, obj) {
if (this.modes[type]) { if (type === 'INTERNAL') {
return cb(null, this.modes[type]) const web3 = loadWeb3()
this.addWeb3(type, web3)
} else if (type === 'vm') {
this.addVM(type, obj)
} else {
extendWeb3(obj)
this.addWeb3(type, obj)
}
} }
cb('error: this provider has not been setup (' + type + ')', null)
}
Web3Providers.prototype.addWeb3 = function (type, web3) { get (type, cb) {
this.modes[type] = web3 if (this.modes[type]) {
} return cb(null, this.modes[type])
}
cb('error: this provider has not been setup (' + type + ')', null)
}
Web3Providers.prototype.addVM = function (type, vm) { addWeb3 (type, web3) {
const vmProvider = new Web3VMProvider() this.modes[type] = web3
vmProvider.setVM(vm) }
this.modes[type] = vmProvider
}
module.exports = Web3Providers addVM (type, vm) {
const vmProvider = new Web3VmProvider()
vmProvider.setVM(vm)
this.modes[type] = vmProvider
}
}
const util = require('../util') import { hexConvert, hexListFromBNs, formatMemory } from '../util'
const uiutil = require('../helpers/uiHelper') import { normalizeHexAddress } from '../helpers/uiHelper'
const ethutil = require('ethereumjs-util') import { toChecksumAddress, BN, toBuffer, } from 'ethereumjs-util'
const Web3 = require('web3') const Web3 = require('web3')
function web3VmProvider () { export class Web3VmProvider {
this.web3 = new Web3()
this.vm = null
this.vmTraces = {}
this.txs = {}
this.txsReceipt = {}
this.processingHash = null
this.processingAddress = null
this.processingIndex = null
this.previousDepth = 0
this.incr = 0
this.eth = {}
this.debug = {}
this.eth.getCode = (...args) => this.getCode(...args)
this.eth.getTransaction = (...args) => this.getTransaction(...args)
this.eth.getTransactionReceipt = (...args) => this.getTransactionReceipt(...args)
this.eth.getTransactionFromBlock = (...args) => this.getTransactionFromBlock(...args)
this.eth.getBlockNumber = (...args) => this.getBlockNumber(...args)
this.debug.traceTransaction = (...args) => this.traceTransaction(...args)
this.debug.storageRangeAt = (...args) => this.storageRangeAt(...args)
this.debug.preimage = (...args) => this.preimage(...args)
this.providers = { 'HttpProvider': function (url) {} }
this.currentProvider = { 'host': 'vm provider' }
this.storageCache = {}
this.lastProcessedStorageTxHash = {}
this.sha3Preimages = {}
// util
this.sha3 = (...args) => this.web3.utils.sha3(...args)
this.toHex = (...args) => this.web3.utils.toHex(...args)
this.toAscii = (...args) => this.web3.utils.hexToAscii(...args)
this.fromAscii = (...args) => this.web3.utils.asciiToHex(...args)
this.fromDecimal = (...args) => this.web3.utils.numberToHex(...args)
this.fromWei = (...args) => this.web3.utils.fromWei(...args)
this.toWei = (...args) => this.web3.utils.toWei(...args)
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) { web3
if (this.vm === vm) return vm
this.vm = vm vmTraces
this.vm.on('step', (data) => { txs
this.pushTrace(this, data) txsReceipt
}) processingHash
this.vm.on('afterTx', (data) => { processingAddress
this.txProcessed(this, data) processingIndex
}) previousDepth
this.vm.on('beforeTx', (data) => { incr
this.txWillProcess(this, data) eth
}) debug
} providers
currentProvider
storageCache
lastProcessedStorageTxHash
sha3Preimages
sha3
toHex
toAscii
fromAscii
fromDecimal
fromWei
toWei
toBigNumber
isAddress
utils
web3VmProvider.prototype.releaseCurrentHash = function () { constructor () {
const ret = this.processingHash this.web3 = new Web3()
this.processingHash = undefined this.vm = null
return ret this.vmTraces = {}
} this.txs = {}
this.txsReceipt = {}
web3VmProvider.prototype.txWillProcess = function (self, data) { this.processingHash = null
self.incr++ this.processingAddress = null
self.processingHash = util.hexConvert(data.hash()) this.processingIndex = null
self.vmTraces[self.processingHash] = { this.previousDepth = 0
gas: '0x0', this.incr = 0
return: '0x0', this.eth = {}
structLogs: [] this.debug = {}
} this.eth.getCode = (...args) => this.getCode(...args)
let tx = {} this.eth.getTransaction = (...args) => this.getTransaction(...args)
tx.hash = self.processingHash this.eth.getTransactionReceipt = (...args) => this.getTransactionReceipt(...args)
tx.from = ethutil.toChecksumAddress(util.hexConvert(data.getSenderAddress())) this.eth.getTransactionFromBlock = (...args) => this.getTransactionFromBlock(...args)
if (data.to && data.to.length) { this.eth.getBlockNumber = (...args) => this.getBlockNumber(...args)
tx.to = ethutil.toChecksumAddress(util.hexConvert(data.to)) this.debug.traceTransaction = (...args) => this.traceTransaction(...args)
this.debug.storageRangeAt = (...args) => this.storageRangeAt(...args)
this.debug.preimage = (...args) => this.preimage(...args)
this.providers = { 'HttpProvider': function (url) {} }
this.currentProvider = { 'host': 'vm provider' }
this.storageCache = {}
this.lastProcessedStorageTxHash = {}
this.sha3Preimages = {}
// util
this.sha3 = (...args) => this.web3.utils.sha3(...args)
this.toHex = (...args) => this.web3.utils.toHex(...args)
this.toAscii = (...args) => this.web3.utils.hexToAscii(...args)
this.fromAscii = (...args) => this.web3.utils.asciiToHex(...args)
this.fromDecimal = (...args) => this.web3.utils.numberToHex(...args)
this.fromWei = (...args) => this.web3.utils.fromWei(...args)
this.toWei = (...args) => this.web3.utils.toWei(...args)
this.toBigNumber = (...args) => this.web3.utils.toBN(...args)
this.isAddress = (...args) => this.web3.utils.isAddress(...args)
this.utils = Web3.utils || []
} }
this.processingAddress = tx.to
tx.data = util.hexConvert(data.data) setVM (vm) {
tx.input = util.hexConvert(data.input) if (this.vm === vm) return
tx.gas = (new ethutil.BN(util.hexConvert(data.gas).replace('0x', ''), 16)).toString(10) this.vm = vm
if (data.value) { this.vm.on('step', (data) => {
tx.value = util.hexConvert(data.value) this.pushTrace(this, data)
} })
self.txs[self.processingHash] = tx this.vm.on('afterTx', (data) => {
self.txsReceipt[self.processingHash] = tx this.txProcessed(this, data)
self.storageCache[self.processingHash] = {} })
if (tx.to) { this.vm.on('beforeTx', (data) => {
const account = ethutil.toBuffer(tx.to) this.txWillProcess(this, data)
self.vm.stateManager.dumpStorage(account, (storage) => {
self.storageCache[self.processingHash][tx.to] = storage
self.lastProcessedStorageTxHash[tx.to] = self.processingHash
}) })
} }
this.processingIndex = 0
}
web3VmProvider.prototype.txProcessed = function (self, data) { releaseCurrentHash () {
const lastOp = self.vmTraces[self.processingHash].structLogs[self.processingIndex - 1] const ret = this.processingHash
if (lastOp) { this.processingHash = undefined
lastOp.error = lastOp.op !== 'RETURN' && lastOp.op !== 'STOP' && lastOp.op !== 'SELFDESTRUCT' return ret
} }
self.vmTraces[self.processingHash].gas = '0x' + data.gasUsed.toString(16)
const logs = [] txWillProcess (self, data) {
for (let l in data.execResult.logs) { self.incr++
const log = data.execResult.logs[l] self.processingHash = hexConvert(data.hash())
const topics = [] self.vmTraces[self.processingHash] = {
if (log[1].length > 0) { gas: '0x0',
for (var k in log[1]) { return: '0x0',
topics.push('0x' + log[1][k].toString('hex')) structLogs: []
}
} else {
topics.push('0x')
} }
logs.push({ let tx = {}
address: '0x' + log[0].toString('hex'), tx['hash'] = self.processingHash
data: '0x' + log[2].toString('hex'), tx['from'] = toChecksumAddress(hexConvert(data.getSenderAddress()))
topics: topics, if (data.to && data.to.length) {
rawVMResponse: log tx['to'] = toChecksumAddress(hexConvert(data.to))
}) }
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'] = hexConvert(data.value)
}
self.txs[self.processingHash] = tx
self.txsReceipt[self.processingHash] = tx
self.storageCache[self.processingHash] = {}
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
})
}
this.processingIndex = 0
} }
self.txsReceipt[self.processingHash].logs = logs
self.txsReceipt[self.processingHash].transactionHash = self.processingHash
const status = data.execResult.exceptionError ? 0 : 1
self.txsReceipt[self.processingHash].status = `0x${status}`
if (data.createdAddress) { txProcessed (self, data) {
const address = util.hexConvert(data.createdAddress) const lastOp = self.vmTraces[self.processingHash].structLogs[self.processingIndex - 1]
self.vmTraces[self.processingHash].return = ethutil.toChecksumAddress(address) if (lastOp) {
self.txsReceipt[self.processingHash].contractAddress = ethutil.toChecksumAddress(address) lastOp.error = lastOp.op !== 'RETURN' && lastOp.op !== 'STOP' && lastOp.op !== 'SELFDESTRUCT'
} else if (data.execResult.returnValue) { }
self.vmTraces[self.processingHash].return = util.hexConvert(data.execResult.returnValue) self.vmTraces[self.processingHash].gas = '0x' + data.gasUsed.toString(16)
} else {
self.vmTraces[self.processingHash].return = '0x'
}
this.processingIndex = null
this.processingAddress = null
this.previousDepth = 0
}
web3VmProvider.prototype.pushTrace = function (self, data) { const logs = []
const depth = data.depth + 1 // geth starts the depth from 1 for (let l in data.execResult.logs) {
if (!self.processingHash) { const log = data.execResult.logs[l]
console.log('no tx processing') const topics = []
return if (log[1].length > 0) {
} for (var k in log[1]) {
let previousopcode topics.push('0x' + log[1][k].toString('hex'))
if (self.vmTraces[self.processingHash] && self.vmTraces[self.processingHash].structLogs[this.processingIndex - 1]) { }
previousopcode = self.vmTraces[self.processingHash].structLogs[this.processingIndex - 1] } else {
} topics.push('0x')
}
logs.push({
address: '0x' + log[0].toString('hex'),
data: '0x' + log[2].toString('hex'),
topics: topics,
rawVMResponse: log
})
}
self.txsReceipt[self.processingHash].logs = logs
self.txsReceipt[self.processingHash].transactionHash = self.processingHash
const status = data.execResult.exceptionError ? 0 : 1
self.txsReceipt[self.processingHash].status = `0x${status}`
if (this.previousDepth > depth && previousopcode) { if (data.createdAddress) {
// returning from context, set error it is not STOP, RETURN const address = hexConvert(data.createdAddress)
previousopcode.invalidDepthChange = previousopcode.op !== 'RETURN' && previousopcode.op !== 'STOP' self.vmTraces[self.processingHash].return = toChecksumAddress(address)
} self.txsReceipt[self.processingHash].contractAddress = toChecksumAddress(address)
const step = { } else if (data.execResult.returnValue) {
stack: util.hexListFromBNs(data.stack), self.vmTraces[self.processingHash].return = hexConvert(data.execResult.returnValue)
memory: util.formatMemory(data.memory),
storage: data.storage,
op: data.opcode.name,
pc: data.pc,
gasCost: data.opcode.fee.toString(),
gas: data.gasLeft.toString(),
depth: depth,
error: data.error === false ? undefined : data.error
}
self.vmTraces[self.processingHash].structLogs.push(step)
if (step.op === 'CREATE' || step.op === 'CALL') {
if (step.op === 'CREATE') {
this.processingAddress = '(Contract Creation - Step ' + this.processingIndex + ')'
this.storageCache[this.processingHash][this.processingAddress] = {}
this.lastProcessedStorageTxHash[this.processingAddress] = this.processingHash
} else { } else {
this.processingAddress = uiutil.normalizeHexAddress(step.stack[step.stack.length - 2]) self.vmTraces[self.processingHash].return = '0x'
this.processingAddress = ethutil.toChecksumAddress(this.processingAddress)
if (!self.storageCache[self.processingHash][this.processingAddress]) {
const account = ethutil.toBuffer(this.processingAddress)
self.vm.stateManager.dumpStorage(account, function (storage) {
self.storageCache[self.processingHash][self.processingAddress] = storage
self.lastProcessedStorageTxHash[self.processingAddress] = self.processingHash
})
}
} }
this.processingIndex = null
this.processingAddress = null
this.previousDepth = 0
} }
if (previousopcode && previousopcode.op === 'SHA3') {
const preimage = getSha3Input(previousopcode.stack, previousopcode.memory) pushTrace (self, data) {
const imageHash = step.stack[step.stack.length - 1].replace('0x', '') const depth = data.depth + 1 // geth starts the depth from 1
self.sha3Preimages[imageHash] = { if (!self.processingHash) {
'preimage': preimage console.log('no tx processing')
return
}
let previousopcode
if (self.vmTraces[self.processingHash] && self.vmTraces[self.processingHash].structLogs[this.processingIndex - 1]) {
previousopcode = self.vmTraces[self.processingHash].structLogs[this.processingIndex - 1]
} }
}
this.processingIndex++ if (this.previousDepth > depth && previousopcode) {
this.previousDepth = depth // returning from context, set error it is not STOP, RETURN
} previousopcode.invalidDepthChange = previousopcode.op !== 'RETURN' && previousopcode.op !== 'STOP'
}
const step = {
stack: hexListFromBNs(data.stack),
memory: formatMemory(data.memory),
storage: data.storage,
op: data.opcode.name,
pc: data.pc,
gasCost: data.opcode.fee.toString(),
gas: data.gasLeft.toString(),
depth: depth,
error: data.error === false ? undefined : data.error
}
self.vmTraces[self.processingHash].structLogs.push(step)
if (step.op === 'CREATE' || step.op === 'CALL') {
if (step.op === 'CREATE') {
this.processingAddress = '(Contract Creation - Step ' + this.processingIndex + ')'
this.storageCache[this.processingHash][this.processingAddress] = {}
this.lastProcessedStorageTxHash[this.processingAddress] = this.processingHash
} else {
this.processingAddress = normalizeHexAddress(step.stack[step.stack.length - 2])
this.processingAddress = toChecksumAddress(this.processingAddress)
if (!self.storageCache[self.processingHash][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
})
}
}
}
if (previousopcode && previousopcode.op === 'SHA3') {
const preimage = this.getSha3Input(previousopcode.stack, previousopcode.memory)
const imageHash = step.stack[step.stack.length - 1].replace('0x', '')
self.sha3Preimages[imageHash] = {
'preimage': preimage
}
}
web3VmProvider.prototype.getCode = function (address, cb) { this.processingIndex++
address = ethutil.toChecksumAddress(address) this.previousDepth = depth
const account = ethutil.toBuffer(address) }
this.vm.stateManager.getContractCode(account, (error, result) => {
cb(error, util.hexConvert(result)) getCode (address, cb) {
}) address = toChecksumAddress(address)
} const account = toBuffer(address)
this.vm.stateManager.getContractCode(account, (error, 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 (this.vmTraces[txHash]) {
if (cb) {
cb(null, this.vmTraces[txHash])
}
return this.vmTraces[txHash]
}
if (cb) { if (cb) {
cb(null, this.vmTraces[txHash]) cb('unable to retrieve traces ' + txHash, null)
} }
return this.vmTraces[txHash]
} }
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 // we don't use the range params here
address = ethutil.toChecksumAddress(address) address = toChecksumAddress(address)
if (txIndex === 'latest') { if (txIndex === 'latest') {
txIndex = this.lastProcessedStorageTxHash[address] txIndex = this.lastProcessedStorageTxHash[address]
} }
if (this.storageCache[txIndex] && this.storageCache[txIndex][address]) { if (this.storageCache[txIndex] && this.storageCache[txIndex][address]) {
const storage = this.storageCache[txIndex][address] const storage = this.storageCache[txIndex][address]
return cb(null, { return cb(null, {
storage: JSON.parse(JSON.stringify(storage)), storage: JSON.parse(JSON.stringify(storage)),
nextKey: null nextKey: null
}) })
}
cb('unable to retrieve storage ' + txIndex + ' ' + address)
} }
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 (this.txs[txHash]) {
if (cb) {
cb(null, this.txs[txHash])
}
return this.txs[txHash]
}
if (cb) { if (cb) {
cb(null, this.txs[txHash]) cb('unable to retrieve tx ' + txHash, null)
} }
return this.txs[txHash]
} }
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 // same as getTransaction but return the created address also
if (this.txsReceipt[txHash]) { if (this.txsReceipt[txHash]) {
if (cb) {
cb(null, this.txsReceipt[txHash])
}
return this.txsReceipt[txHash]
}
if (cb) { if (cb) {
cb(null, this.txsReceipt[txHash]) cb('unable to retrieve txReceipt ' + txHash, null)
} }
return this.txsReceipt[txHash]
}
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' const mes = 'not supposed to be needed by remix in vmmode'
console.log(mes) console.log(mes)
if (cb) { if (cb) {
cb(mes, null) cb(mes, null)
}
} }
}
web3VmProvider.prototype.preimage = function (hashedKey, cb) { preimage (hashedKey, cb) {
hashedKey = hashedKey.replace('0x', '') hashedKey = hashedKey.replace('0x', '')
cb(null, this.sha3Preimages[hashedKey] !== undefined ? this.sha3Preimages[hashedKey].preimage : null) 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 memoryStart = stack[stack.length - 1]
let memoryLength = stack[stack.length - 2] 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 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 memoryLength = parseInt(memLengthDec) * 2
let i = Math.floor(memoryStart / 32) let i = Math.floor(memoryStart / 32)
const maxIndex = Math.floor(memoryLength / 32) + i const maxIndex = Math.floor(memoryLength / 32) + i
if (!memory[i]) { if (!memory[i]) {
return emptyFill(memoryLength) return this.emptyFill(memoryLength)
} }
let sha3Input = memory[i].slice(memoryStart - 32 * i) let sha3Input = memory[i].slice(memoryStart - 32 * i)
i++
while (i < maxIndex) {
sha3Input += memory[i] ? memory[i] : emptyFill(32)
i++ i++
while (i < maxIndex) {
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) : this.emptyFill(leftSize)
}
return sha3Input
} }
if (sha3Input.length < memoryLength) {
const leftSize = memoryLength - sha3Input.length
sha3Input += memory[i] ? memory[i].slice(0, leftSize) : emptyFill(leftSize)
}
return sha3Input
}
function emptyFill (size) { emptyFill (size) {
return (new Array(size)).join('0') return (new Array(size)).join('0')
}
} }
module.exports = web3VmProvider
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