remix-project mirror
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
remix-project/remix-lib/src/util.js

253 lines
6.9 KiB

'use strict'
var ethutil = require('ethereumjs-util')
/*
contains misc util: @TODO should be splitted
- hex convertion
- binary search
- CALL related look up
- sha3 calculation
- swarm hash extraction
- bytecode comparison
*/
module.exports = {
/*
ints: IntArray
*/
hexConvert: function (ints) {
var ret = '0x'
for (var i = 0; i < ints.length; i++) {
var h = ints[i]
if (h) {
ret += (h <= 0xf ? '0' : '') + h.toString(16)
} else {
ret += '00'
}
}
return ret
},
/**
* Converts a hex string to an array of integers.
*/
hexToIntArray: function (hexString) {
if (hexString.slice(0, 2) === '0x') {
hexString = hexString.slice(2)
}
var integers = []
for (var 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) {
var ret = []
for (var k in bnList) {
var v = bnList[k]
if (ethutil.BN.isBN(v)) {
ret.push('0x' + v.toString('hex', 64))
} else {
ret.push('0x' + (new ethutil.BN(v)).toString('hex', 64)) // TEMP FIX TO REMOVE ONCE https://github.com/ethereumjs/ethereumjs-vm/pull/293 is released
}
}
return ret
},
/*
ints: list of IntArrays
*/
hexListConvert: function (intsList) {
var ret = []
for (var k in intsList) {
ret.push(this.hexConvert(intsList[k]))
}
return ret
},
/*
ints: ints: IntArray
*/
formatMemory: function (mem) {
var hexMem = this.hexConvert(mem).substr(2)
var ret = []
for (var k = 0; k < hexMem.length; k += 32) {
var 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) {
var start = 0
var length = array.length
while (length > 0) {
var half = length >> 1
var middle = start + half
if (array[middle] <= target) {
length = length - 1 - half
start = middle + 1
} else {
length = half
}
}
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) {
var 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.
*/
findClosestIndex: function (target, array) {
if (array.length === 0) {
return -1
}
var index = this.findLowerBound(target, array)
if (index < 0) {
return 0
} else if (index >= array.length - 1) {
return array.length - 1
} else {
var 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
*/
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,
/**
* sha3 the given @arg value (left pad to 32 bytes)
*
* @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
}
var ret = ethutil.bufferToHex(ethutil.setLengthLeft(value, 32))
ret = ethutil.sha3(ret)
return ethutil.bufferToHex(ret)
},
/**
* return a regex which extract the swarmhash from the bytecode.
*
* @return {RegEx}
*/
swarmHashExtraction: function () {
return /a165627a7a72305820([0-9a-f]{64})0029$/
},
/**
* 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
var pos = -1
while ((pos = code2.search(/__(.*)__/)) !== -1) {
code2 = replaceLibReference(code2, pos)
code1 = replaceLibReference(code1, pos)
}
code1 = code1.replace(this.swarmHashExtraction(), '')
code2 = code2.replace(this.swarmHashExtraction(), '')
if (code1 && code2 && code1.indexOf(code2) === 0) {
return true
}
return false
},
groupBy: groupBy,
concatWithSeperator: concatWithSeperator,
escapeRegExp: escapeRegExp
}
function replaceLibReference (code, pos) {
return code.substring(0, pos) + '0000000000000000000000000000000000000000' + code.substring(pos + 40)
}
function buildCallPath (index, rootCall) {
var ret = []
findCallInternal(index, rootCall, ret)
return ret
}
function findCall (index, rootCall) {
var ret = buildCallPath(index, rootCall)
return ret[ret.length - 1]
}
function findCallInternal (index, rootCall, callsPath) {
var calls = Object.keys(rootCall.calls)
var ret = rootCall
callsPath.push(rootCall)
for (var k in calls) {
var subCall = rootCall.calls[calls[k]]
if (index >= subCall.start && index <= subCall.return) {
findCallInternal(index, subCall, callsPath)
break
}
}
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]
var 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, '\\$&')
}