providers, init, util, txRunner

toaster-react
aniket-engg 4 years ago committed by Aniket
parent 142d6478d9
commit 34bc1cd6ab
  1. 1
      libs/remix-lib/src/execution/execution-context.ts
  2. 2
      libs/remix-lib/src/execution/txRunner.ts
  3. 58
      libs/remix-lib/src/init.ts
  4. 449
      libs/remix-lib/src/util.ts
  5. 81
      libs/remix-lib/src/web3Provider/dummyProvider.ts
  6. 60
      libs/remix-lib/src/web3Provider/web3Providers.ts
  7. 551
      libs/remix-lib/src/web3Provider/web3VmProvider.ts

@ -9,6 +9,7 @@ const Web3VMProvider = require('../web3Provider/web3VmProvider')
const LogsManager = require('./logsManager.js')
declare let ethereum: any;
let web3
if (typeof window !== 'undefined' && typeof window['ethereum'] !== 'undefined') {
var injectedProvider = window['ethereum']

@ -1,7 +1,7 @@
'use strict'
import { Transaction } from 'ethereumjs-tx'
import { Block } from 'ethereumjs-block'
import { BN } from 'ethereumjs-util'
import { BN, bufferToHex } from 'ethereumjs-util'
import { ExecutionContext } from './execution-context'
const EventManager = require('../eventManager')

@ -1,31 +1,36 @@
'use strict'
const Web3 = require('web3')
import Web3 from 'web3'
module.exports = {
loadWeb3: function (url) {
if (!url) url = 'http://localhost:8545'
const web3 = new Web3()
web3.setProvider(new web3.providers.HttpProvider(url))
this.extend(web3)
return web3
},
export function loadWeb3 (url = 'http://localhost:8545') {
const web3 = new Web3()
web3.setProvider(new Web3.providers.HttpProvider(url))
this.extend(web3)
return web3
}
extendWeb3: function (web3) {
this.extend(web3)
},
export function extendWeb3 (web3) {
this.extend(web3)
}
setProvider: function (web3, url) {
web3.setProvider(new web3.providers.HttpProvider(url))
},
export function setProvider (web3, url) {
web3.setProvider(new web3.providers.HttpProvider(url))
}
web3DebugNode: function (network) {
if (web3DebugNodes[network]) {
return this.loadWeb3(web3DebugNodes[network])
}
return null
},
export function web3DebugNode (network) {
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'
}
if (web3DebugNodes[network]) {
return this.loadWeb3(web3DebugNodes[network])
}
return null
}
extend: function (web3) {
export function extend (web3) {
if (!web3.extend) {
return
}
@ -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'
- swarm hash extraction
- bytecode comparison
*/
module.exports = {
/*
ints: IntArray
*/
hexConvert: function (ints) {
let ret = '0x'
for (let i = 0; i < ints.length; i++) {
const h = ints[i]
if (h) {
ret += (h <= 0xf ? '0' : '') + h.toString(16)
} else {
ret += '00'
}
export function hexConvert (ints) {
let ret = '0x'
for (let i = 0; i < ints.length; i++) {
const h = ints[i]
if (h) {
ret += (h <= 0xf ? '0' : '') + h.toString(16)
} else {
ret += '00'
}
return ret
},
}
return ret
}
/**
* Converts a hex string to an array of integers.
*/
hexToIntArray: function (hexString) {
if (hexString.slice(0, 2) === '0x') {
hexString = hexString.slice(2)
}
const integers = []
for (let i = 0; i < hexString.length; i += 2) {
integers.push(parseInt(hexString.slice(i, i + 2), 16))
}
return integers
},
export function hexToIntArray (hexString) {
if (hexString.slice(0, 2) === '0x') {
hexString = hexString.slice(2)
}
const integers = []
for (let i = 0; i < hexString.length; i += 2) {
integers.push(parseInt(hexString.slice(i, i + 2), 16))
}
return integers
}
/*
ints: list of BNs
*/
hexListFromBNs: function (bnList) {
const ret = []
for (let k in bnList) {
const v = bnList[k]
if (BN.isBN(v)) {
ret.push('0x' + v.toString('hex', 64))
} 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
}
export function hexListFromBNs (bnList) {
const ret = []
for (let k in bnList) {
const v = bnList[k]
if (BN.isBN(v)) {
ret.push('0x' + v.toString('hex', 64))
} 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
}
return ret
},
}
return ret
}
/*
ints: list of IntArrays
*/
hexListConvert: function (intsList) {
const ret = []
for (let k in intsList) {
ret.push(this.hexConvert(intsList[k]))
}
return ret
},
/*
ints: list of IntArrays
*/
export function hexListConvert (intsList) {
const ret = []
for (let k in intsList) {
ret.push(this.hexConvert(intsList[k]))
}
return ret
}
/*
ints: ints: IntArray
*/
formatMemory: function (mem) {
const hexMem = this.hexConvert(mem).substr(2)
const ret = []
for (let k = 0; k < hexMem.length; k += 32) {
const row = hexMem.substr(k, 32)
ret.push(row)
}
return ret
},
/*
ints: ints: IntArray
*/
export function formatMemory (mem) {
const hexMem = this.hexConvert(mem).substr(2)
const ret = []
for (let k = 0; k < hexMem.length; k += 32) {
const row = hexMem.substr(k, 32)
ret.push(row)
}
return ret
}
/*
Binary Search:
Assumes that @arg array is sorted increasingly
return largest i such that array[i] <= target; return -1 if array[0] > target || array is empty
*/
findLowerBound: function (target, array) {
let start = 0
let length = array.length
while (length > 0) {
const half = length >> 1
const middle = start + half
if (array[middle] <= target) {
length = length - 1 - half
start = middle + 1
} else {
length = half
}
/*
Binary Search:
Assumes that @arg array is sorted increasingly
return largest i such that array[i] <= target; return -1 if array[0] > target || array is empty
*/
export function findLowerBound (target, array) {
let start = 0
let length = array.length
while (length > 0) {
const half = length >> 1
const middle = start + half
if (array[middle] <= target) {
length = length - 1 - half
start = middle + 1
} else {
length = half
}
return start - 1
},
}
return start - 1
}
/*
Binary Search:
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
*/
findLowerBoundValue: function (target, array) {
const index = this.findLowerBound(target, array)
return index >= 0 ? array[index] : null
},
/*
Binary Search:
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
*/
export function findLowerBoundValue (target, array) {
const index = this.findLowerBound(target, array)
return index >= 0 ? array[index] : null
}
/*
Binary Search:
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.
Returns the smallest i for multiple candidates.
/*
Binary Search:
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.
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) {
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
}
},
export function findCall (index, rootCall) {
const ret = buildCallPath(index, rootCall)
return ret[ret.length - 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
*/
findCall: findCall,
/**
* Find calls path from @args rootCall which leads to @args index (recursive)
*
* @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,
* Find calls path from @args rootCall which leads to @args index (recursive)
*
* @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
*/
export function buildCallPath (index, rootCall) {
const ret = []
findCallInternal(index, rootCall, ret)
return ret
}
/**
* sha3 the given @arg value (left pad to 32 bytes)
@ -157,113 +163,117 @@ module.exports = {
* @param {String} value - value to sha3
* @return {Object} - return sha3ied value
*/
sha3_256: function (value) {
if (typeof value === 'string' && value.indexOf('0x') !== 0) {
value = '0x' + value
}
let ret: any = bufferToHex(setLengthLeft(value, 32))
ret = keccak(ret)
return bufferToHex(ret)
},
export function sha3_256 (value) {
if (typeof value === 'string' && value.indexOf('0x') !== 0) {
value = '0x' + value
}
let ret: any = bufferToHex(setLengthLeft(value, 32))
ret = keccak(ret)
return bufferToHex(ret)
}
/**
* return a regex which extract the swarmhash from the bytecode.
*
* @return {RegEx}
*/
swarmHashExtraction: function () {
return /a165627a7a72305820([0-9a-f]{64})0029$/
},
/**
* return a regex which extract the swarmhash from the bytecode.
*
* @return {RegEx}
*/
export function swarmHashExtraction () {
return /a165627a7a72305820([0-9a-f]{64})0029$/
}
/**
* return a regex which extract the swarmhash from the bytecode, from POC 0.3
*
* @return {RegEx}
*/
swarmHashExtractionPOC31: function () {
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 {RegEx}
*/
export function swarmHashExtractionPOC31 () {
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 {RegEx}
*/
swarmHashExtractionPOC32: function () {
return /a265627a7a72305820([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 {RegEx}
*/
export function swarmHashExtractionPOC32 () {
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.
* ref https://solidity.readthedocs.io/en/v0.6.6/metadata.html?highlight=ipfs#encoding-of-the-metadata-hash-in-the-bytecode
* @return {RegEx}
*/
cborEncodedValueExtraction: function () {
return /64697066735822([0-9a-f]{68})64736f6c6343([0-9a-f]{6})0033$/
},
/**
* 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
* @return {RegEx}
*/
export function cborEncodedValueExtraction () {
return /64697066735822([0-9a-f]{68})64736f6c6343([0-9a-f]{6})0033$/
}
extractcborMetadata: function (value) {
return value.replace(this.cborEncodedValueExtraction(), '')
},
export function extractcborMetadata (value) {
return value.replace(this.cborEncodedValueExtraction(), '')
}
extractSwarmHash: function (value) {
value = value.replace(this.swarmHashExtraction(), '')
value = value.replace(this.swarmHashExtractionPOC31(), '')
value = value.replace(this.swarmHashExtractionPOC32(), '')
return value
},
export function extractSwarmHash (value) {
value = value.replace(this.swarmHashExtraction(), '')
value = value.replace(this.swarmHashExtractionPOC31(), '')
value = value.replace(this.swarmHashExtractionPOC32(), '')
return value
}
/**
* 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} 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)
*
* @return {bool}
*/
compareByteCode: function (code1, code2) {
if (code1 === code2) return true
if (code2 === '0x') return false // abstract contract. see comment
/**
* 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} 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)
*
* @return {bool}
*/
export function compareByteCode (code1, code2) {
if (code1 === code2) return true
if (code2 === '0x') return false // abstract contract. see comment
if (code2.substr(2, 46) === '7300000000000000000000000000000000000000003014') {
// 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)
// 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)
}
let pos = -1
while ((pos = code2.search(/__(.*)__/)) !== -1) {
code2 = replaceLibReference(code2, pos)
code1 = replaceLibReference(code1, pos)
}
code1 = this.extractSwarmHash(code1)
code1 = this.extractcborMetadata(code1)
code2 = this.extractSwarmHash(code2)
code2 = this.extractcborMetadata(code2)
if (code2.substr(2, 46) === '7300000000000000000000000000000000000000003014') {
// 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)
// 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)
}
let pos = -1
while ((pos = code2.search(/__(.*)__/)) !== -1) {
code2 = replaceLibReference(code2, pos)
code1 = replaceLibReference(code1, pos)
}
code1 = this.extractSwarmHash(code1)
code1 = this.extractcborMetadata(code1)
code2 = this.extractSwarmHash(code2)
code2 = this.extractcborMetadata(code2)
if (code1 && code2 && code1.indexOf(code2) === 0) {
return true
}
return false
},
groupBy: groupBy,
concatWithSeperator: concatWithSeperator,
escapeRegExp: escapeRegExp
if (code1 && code2 && code1.indexOf(code2) === 0) {
return true
}
return false
}
/* util extracted out from remix-ide. @TODO split this file, cause it mix real util fn with solidity related stuff ... */
export 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 replaceLibReference (code, pos) {
return code.substring(0, pos) + '0000000000000000000000000000000000000000' + code.substring(pos + 40)
export function concatWithSeperator (list, seperator) {
return list.reduce((sum, item) => sum + item + seperator, '').slice(0, -seperator.length)
}
function buildCallPath (index, rootCall) {
const ret = []
findCallInternal(index, rootCall, ret)
return ret
export function escapeRegExp (str) {
return str.replace(/[-[\]/{}()+?.\\^$|]/g, '\\$&')
}
function findCall (index, rootCall) {
const ret = buildCallPath(index, rootCall)
return ret[ret.length - 1]
function replaceLibReference (code, pos) {
return code.substring(0, pos) + '0000000000000000000000000000000000000000' + code.substring(pos + 40)
}
function findCallInternal (index, rootCall, callsPath) {
@ -279,22 +289,3 @@ function findCallInternal (index, rootCall, callsPath) {
}
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, '\\$&')
}

@ -1,50 +1,55 @@
function dummyProvider () {
this.eth = {}
this.debug = {}
this.eth.getCode = (address, cb) => { return this.getCode(address, cb) }
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': ''}
}
export class dummyProvider {
eth
debug
providers
currentProvider
constructor() {
this.eth = {}
this.debug = {}
this.eth.getCode = (address, cb) => { return this.getCode(address, cb) }
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) {
cb(null, '')
}
getCode (address, cb) {
cb(null, '')
}
dummyProvider.prototype.setProvider = function (provider) {}
setProvider (provider) {}
dummyProvider.prototype.traceTransaction = function (txHash, options, cb) {
if (cb) {
cb(null, {})
traceTransaction (txHash, options, cb) {
if (cb) {
cb(null, {})
}
return {}
}
return {}
}
dummyProvider.prototype.storageRangeAt = function (blockNumber, txIndex, address, start, end, maxLength, cb) {
if (cb) {
cb(null, {})
storageRangeAt (blockNumber, txIndex, address, start, end, maxLength, cb) {
if (cb) {
cb(null, {})
}
return {}
}
return {}
}
dummyProvider.prototype.getBlockNumber = function (cb) { cb(null, '') }
getBlockNumber (cb) { cb(null, '') }
dummyProvider.prototype.getTransaction = function (txHash, cb) {
if (cb) {
cb(null, {})
getTransaction (txHash, cb) {
if (cb) {
cb(null, {})
}
return {}
}
return {}
}
dummyProvider.prototype.getTransactionFromBlock = function (blockNumber, txIndex, cb) {
if (cb) {
cb(null, {})
getTransactionFromBlock (blockNumber, txIndex, cb) {
if (cb) {
cb(null, {})
}
return {}
}
return {}
}
module.exports = dummyProvider

@ -1,37 +1,39 @@
const Web3VMProvider = require('./web3VmProvider')
const init = require('../init')
import { Web3VmProvider } from './web3VmProvider'
import { loadWeb3, extendWeb3 } from '../init'
function Web3Providers () {
this.modes = {}
}
export class Web3Providers {
Web3Providers.prototype.addProvider = function (type, obj) {
if (type === 'INTERNAL') {
const web3 = init.loadWeb3()
this.addWeb3(type, web3)
} else if (type === 'vm') {
this.addVM(type, obj)
} else {
init.extendWeb3(obj)
this.addWeb3(type, obj)
modes
constructor() {
this.modes = {}
}
}
Web3Providers.prototype.get = function (type, cb) {
if (this.modes[type]) {
return cb(null, this.modes[type])
addProvider (type, obj) {
if (type === 'INTERNAL') {
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) {
this.modes[type] = web3
}
get (type, cb) {
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) {
const vmProvider = new Web3VMProvider()
vmProvider.setVM(vm)
this.modes[type] = vmProvider
}
addWeb3 (type, web3) {
this.modes[type] = web3
}
module.exports = Web3Providers
addVM (type, vm) {
const vmProvider = new Web3VmProvider()
vmProvider.setVM(vm)
this.modes[type] = vmProvider
}
}

@ -1,309 +1,338 @@
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 () {
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 || []
}
export class Web3VmProvider {
web3VmProvider.prototype.setVM = function (vm) {
if (this.vm === vm) return
this.vm = vm
this.vm.on('step', (data) => {
this.pushTrace(this, data)
})
this.vm.on('afterTx', (data) => {
this.txProcessed(this, data)
})
this.vm.on('beforeTx', (data) => {
this.txWillProcess(this, data)
})
}
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
web3VmProvider.prototype.releaseCurrentHash = function () {
const ret = this.processingHash
this.processingHash = undefined
return ret
}
web3VmProvider.prototype.txWillProcess = function (self, data) {
self.incr++
self.processingHash = util.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()))
if (data.to && data.to.length) {
tx.to = ethutil.toChecksumAddress(util.hexConvert(data.to))
constructor () {
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 || []
}
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)
if (data.value) {
tx.value = util.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)
self.vm.stateManager.dumpStorage(account, (storage) => {
self.storageCache[self.processingHash][tx.to] = storage
self.lastProcessedStorageTxHash[tx.to] = self.processingHash
setVM (vm) {
if (this.vm === vm) return
this.vm = vm
this.vm.on('step', (data) => {
this.pushTrace(this, data)
})
this.vm.on('afterTx', (data) => {
this.txProcessed(this, data)
})
this.vm.on('beforeTx', (data) => {
this.txWillProcess(this, data)
})
}
this.processingIndex = 0
}
web3VmProvider.prototype.txProcessed = function (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'
releaseCurrentHash () {
const ret = this.processingHash
this.processingHash = undefined
return ret
}
self.vmTraces[self.processingHash].gas = '0x' + data.gasUsed.toString(16)
const logs = []
for (let l in data.execResult.logs) {
const log = data.execResult.logs[l]
const topics = []
if (log[1].length > 0) {
for (var k in log[1]) {
topics.push('0x' + log[1][k].toString('hex'))
}
} else {
topics.push('0x')
txWillProcess (self, data) {
self.incr++
self.processingHash = hexConvert(data.hash())
self.vmTraces[self.processingHash] = {
gas: '0x0',
return: '0x0',
structLogs: []
}
logs.push({
address: '0x' + log[0].toString('hex'),
data: '0x' + log[2].toString('hex'),
topics: topics,
rawVMResponse: log
})
let tx = {}
tx['hash'] = self.processingHash
tx['from'] = toChecksumAddress(hexConvert(data.getSenderAddress()))
if (data.to && data.to.length) {
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) {
const address = util.hexConvert(data.createdAddress)
self.vmTraces[self.processingHash].return = ethutil.toChecksumAddress(address)
self.txsReceipt[self.processingHash].contractAddress = ethutil.toChecksumAddress(address)
} else if (data.execResult.returnValue) {
self.vmTraces[self.processingHash].return = util.hexConvert(data.execResult.returnValue)
} else {
self.vmTraces[self.processingHash].return = '0x'
}
this.processingIndex = null
this.processingAddress = null
this.previousDepth = 0
}
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'
}
self.vmTraces[self.processingHash].gas = '0x' + data.gasUsed.toString(16)
web3VmProvider.prototype.pushTrace = function (self, data) {
const depth = data.depth + 1 // geth starts the depth from 1
if (!self.processingHash) {
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]
}
const logs = []
for (let l in data.execResult.logs) {
const log = data.execResult.logs[l]
const topics = []
if (log[1].length > 0) {
for (var k in log[1]) {
topics.push('0x' + log[1][k].toString('hex'))
}
} 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) {
// returning from context, set error it is not STOP, RETURN
previousopcode.invalidDepthChange = previousopcode.op !== 'RETURN' && previousopcode.op !== 'STOP'
}
const step = {
stack: util.hexListFromBNs(data.stack),
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
if (data.createdAddress) {
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 = hexConvert(data.execResult.returnValue)
} else {
this.processingAddress = uiutil.normalizeHexAddress(step.stack[step.stack.length - 2])
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
})
}
self.vmTraces[self.processingHash].return = '0x'
}
this.processingIndex = null
this.processingAddress = null
this.previousDepth = 0
}
if (previousopcode && previousopcode.op === 'SHA3') {
const preimage = getSha3Input(previousopcode.stack, previousopcode.memory)
const imageHash = step.stack[step.stack.length - 1].replace('0x', '')
self.sha3Preimages[imageHash] = {
'preimage': preimage
pushTrace (self, data) {
const depth = data.depth + 1 // geth starts the depth from 1
if (!self.processingHash) {
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++
this.previousDepth = depth
}
if (this.previousDepth > depth && previousopcode) {
// 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) {
address = ethutil.toChecksumAddress(address)
const account = ethutil.toBuffer(address)
this.vm.stateManager.getContractCode(account, (error, result) => {
cb(error, util.hexConvert(result))
})
}
this.processingIndex++
this.previousDepth = depth
}
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) {
if (this.vmTraces[txHash]) {
traceTransaction (txHash, options, cb) {
if (this.vmTraces[txHash]) {
if (cb) {
cb(null, this.vmTraces[txHash])
}
return this.vmTraces[txHash]
}
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
// we don't use the range params here
address = ethutil.toChecksumAddress(address)
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 = toChecksumAddress(address)
if (txIndex === 'latest') {
txIndex = this.lastProcessedStorageTxHash[address]
}
if (txIndex === 'latest') {
txIndex = this.lastProcessedStorageTxHash[address]
}
if (this.storageCache[txIndex] && this.storageCache[txIndex][address]) {
const storage = this.storageCache[txIndex][address]
return cb(null, {
storage: JSON.parse(JSON.stringify(storage)),
nextKey: null
})
if (this.storageCache[txIndex] && this.storageCache[txIndex][address]) {
const storage = this.storageCache[txIndex][address]
return cb(null, {
storage: JSON.parse(JSON.stringify(storage)),
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) {
if (this.txs[txHash]) {
getTransaction (txHash, cb) {
if (this.txs[txHash]) {
if (cb) {
cb(null, this.txs[txHash])
}
return this.txs[txHash]
}
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) {
// same as getTransaction but return the created address also
if (this.txsReceipt[txHash]) {
getTransactionReceipt (txHash, cb) {
// same as getTransaction but return the created address also
if (this.txsReceipt[txHash]) {
if (cb) {
cb(null, this.txsReceipt[txHash])
}
return this.txsReceipt[txHash]
}
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) {
const mes = 'not supposed to be needed by remix in vmmode'
console.log(mes)
if (cb) {
cb(mes, null)
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) {
hashedKey = hashedKey.replace('0x', '')
cb(null, this.sha3Preimages[hashedKey] !== undefined ? this.sha3Preimages[hashedKey].preimage : null)
}
preimage (hashedKey, cb) {
hashedKey = hashedKey.replace('0x', '')
cb(null, this.sha3Preimages[hashedKey] !== undefined ? this.sha3Preimages[hashedKey].preimage : null)
}
function 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)
memoryStart = parseInt(memStartDec) * 2
const memLengthDec = (new ethutil.BN(memoryLength.replace('0x', ''), 16).toString(10))
memoryLength = parseInt(memLengthDec) * 2
getSha3Input (stack, memory) {
let memoryStart = stack[stack.length - 1]
let memoryLength = stack[stack.length - 2]
const memStartDec = (new BN(memoryStart.replace('0x', ''), 16)).toString(10)
memoryStart = parseInt(memStartDec) * 2
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)
}
let sha3Input = memory[i].slice(memoryStart - 32 * i)
i++
while (i < maxIndex) {
sha3Input += memory[i] ? memory[i] : emptyFill(32)
let i = Math.floor(memoryStart / 32)
const maxIndex = Math.floor(memoryLength / 32) + i
if (!memory[i]) {
return this.emptyFill(memoryLength)
}
let sha3Input = memory[i].slice(memoryStart - 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) {
return (new Array(size)).join('0')
emptyFill (size) {
return (new Array(size)).join('0')
}
}
module.exports = web3VmProvider

Loading…
Cancel
Save