From 34bc1cd6ab4e5304090d680208c1d84fb8723c06 Mon Sep 17 00:00:00 2001 From: aniket-engg Date: Wed, 2 Dec 2020 16:15:34 +0530 Subject: [PATCH] providers, init, util, txRunner --- .../src/execution/execution-context.ts | 1 + libs/remix-lib/src/execution/txRunner.ts | 2 +- libs/remix-lib/src/init.ts | 58 +- libs/remix-lib/src/util.ts | 449 +++++++------- .../src/web3Provider/dummyProvider.ts | 81 +-- .../src/web3Provider/web3Providers.ts | 60 +- .../src/web3Provider/web3VmProvider.ts | 551 +++++++++--------- 7 files changed, 613 insertions(+), 589 deletions(-) diff --git a/libs/remix-lib/src/execution/execution-context.ts b/libs/remix-lib/src/execution/execution-context.ts index a40b550b60..27dd2557b2 100644 --- a/libs/remix-lib/src/execution/execution-context.ts +++ b/libs/remix-lib/src/execution/execution-context.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'] diff --git a/libs/remix-lib/src/execution/txRunner.ts b/libs/remix-lib/src/execution/txRunner.ts index 0b325add04..082cc04832 100644 --- a/libs/remix-lib/src/execution/txRunner.ts +++ b/libs/remix-lib/src/execution/txRunner.ts @@ -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') diff --git a/libs/remix-lib/src/init.ts b/libs/remix-lib/src/init.ts index 9c709e3efd..e233509da6 100644 --- a/libs/remix-lib/src/init.ts +++ b/libs/remix-lib/src/init.ts @@ -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' -} diff --git a/libs/remix-lib/src/util.ts b/libs/remix-lib/src/util.ts index 97b792d147..329a545136 100644 --- a/libs/remix-lib/src/util.ts +++ b/libs/remix-lib/src/util.ts @@ -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": , "solc": } 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": , "solc": } 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, '\\$&') -} diff --git a/libs/remix-lib/src/web3Provider/dummyProvider.ts b/libs/remix-lib/src/web3Provider/dummyProvider.ts index 122601f880..d6fbea95a6 100644 --- a/libs/remix-lib/src/web3Provider/dummyProvider.ts +++ b/libs/remix-lib/src/web3Provider/dummyProvider.ts @@ -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 diff --git a/libs/remix-lib/src/web3Provider/web3Providers.ts b/libs/remix-lib/src/web3Provider/web3Providers.ts index d76f50c790..047725d82d 100644 --- a/libs/remix-lib/src/web3Provider/web3Providers.ts +++ b/libs/remix-lib/src/web3Provider/web3Providers.ts @@ -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 + } +} diff --git a/libs/remix-lib/src/web3Provider/web3VmProvider.ts b/libs/remix-lib/src/web3Provider/web3VmProvider.ts index 7d5e86f19c..2e1e278a4c 100644 --- a/libs/remix-lib/src/web3Provider/web3VmProvider.ts +++ b/libs/remix-lib/src/web3Provider/web3VmProvider.ts @@ -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