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.
216 lines
5.8 KiB
216 lines
5.8 KiB
9 years ago
|
'use strict'
|
||
8 years ago
|
var ethutil = require('ethereumjs-util')
|
||
|
|
||
7 years ago
|
/*
|
||
|
contains misc util: @TODO should be splitted
|
||
|
- hex convertion
|
||
|
- binary search
|
||
|
- CALL related look up
|
||
|
- sha3 calculation
|
||
|
- swarm hash extraction
|
||
|
- bytecode comparison
|
||
|
*/
|
||
9 years ago
|
module.exports = {
|
||
|
/*
|
||
|
ints: IntArray
|
||
|
*/
|
||
|
hexConvert: function (ints) {
|
||
|
var ret = '0x'
|
||
|
for (var i = 0; i < ints.length; i++) {
|
||
|
var h = ints[i]
|
||
8 years ago
|
if (h) {
|
||
|
ret += (h <= 0xf ? '0' : '') + h.toString(16)
|
||
|
} else {
|
||
|
ret += '00'
|
||
|
}
|
||
9 years ago
|
}
|
||
|
return ret
|
||
|
},
|
||
|
|
||
8 years ago
|
/**
|
||
|
* 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
|
||
|
},
|
||
|
|
||
9 years ago
|
/*
|
||
|
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
|
||
9 years ago
|
},
|
||
|
|
||
|
/*
|
||
|
Binary Search:
|
||
|
Assumes that @arg array is sorted increasingly
|
||
9 years ago
|
return largest i such that array[i] <= target; return -1 if array[0] > target || array is empty
|
||
9 years ago
|
*/
|
||
|
findLowerBound: function (target, array) {
|
||
9 years ago
|
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
|
||
9 years ago
|
},
|
||
|
|
||
9 years ago
|
/*
|
||
|
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
|
||
|
*/
|
||
9 years ago
|
findLowerBoundValue: function (target, array) {
|
||
|
var index = this.findLowerBound(target, array)
|
||
9 years ago
|
return index >= 0 ? array[index] : null
|
||
8 years ago
|
},
|
||
|
|
||
8 years ago
|
/*
|
||
|
Binary Search:
|
||
|
Assumes that @arg array is sorted increasingly
|
||
8 years ago
|
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.
|
||
8 years ago
|
*/
|
||
8 years ago
|
findClosestIndex: function (target, array) {
|
||
|
if (array.length === 0) {
|
||
|
return -1
|
||
|
}
|
||
8 years ago
|
var index = this.findLowerBound(target, array)
|
||
8 years ago
|
if (index < 0) {
|
||
8 years ago
|
return 0
|
||
8 years ago
|
} else if (index >= array.length - 1) {
|
||
8 years ago
|
return array.length - 1
|
||
8 years ago
|
} else {
|
||
|
var middle = (array[index] + array[index + 1]) / 2
|
||
|
return target <= middle ? index : index + 1
|
||
8 years ago
|
}
|
||
|
},
|
||
|
|
||
8 years ago
|
/**
|
||
8 years ago
|
* 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
|
||
|
*/
|
||
8 years ago
|
findCall: findCall,
|
||
8 years ago
|
|
||
8 years ago
|
/**
|
||
|
* 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
|
||
|
*/
|
||
8 years ago
|
buildCallPath: buildCallPath,
|
||
|
|
||
|
/**
|
||
8 years ago
|
* sha3 the given @arg value (left pad to 32 bytes)
|
||
8 years ago
|
*
|
||
|
* @param {String} value - value to sha3
|
||
|
* @return {Object} - return sha3ied value
|
||
|
*/
|
||
8 years ago
|
sha3_256: function (value) {
|
||
8 years ago
|
if (typeof value === 'string' && value.indexOf('0x') !== 0) {
|
||
8 years ago
|
value = '0x' + value
|
||
|
}
|
||
8 years ago
|
var ret = ethutil.bufferToHex(ethutil.setLengthLeft(value, 32))
|
||
|
ret = ethutil.sha3(ret)
|
||
|
return ethutil.bufferToHex(ret)
|
||
8 years ago
|
},
|
||
|
|
||
|
/**
|
||
|
* return a regex which extract the swarmhash from the bytecode.
|
||
|
*
|
||
|
* @return {RegEx}
|
||
|
*/
|
||
|
swarmHashExtraction: function () {
|
||
|
return /a165627a7a72305820([0-9a-f]{64})0029$/
|
||
8 years ago
|
},
|
||
|
|
||
|
/**
|
||
|
* Compare bytecode. return true if the code is equal (handle swarm hash and library references)
|
||
8 years ago
|
* @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)
|
||
8 years ago
|
this will return false if the generated bytecode is empty (asbtract contract cannot be deployed)
|
||
8 years ago
|
*
|
||
|
* @return {bool}
|
||
|
*/
|
||
|
compareByteCode: function (code1, code2) {
|
||
8 years ago
|
if (code1 === code2) return true
|
||
|
if (code2 === '0x') return false // abstract contract. see comment
|
||
|
|
||
8 years ago
|
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
|
||
8 years ago
|
}
|
||
8 years ago
|
}
|
||
|
|
||
8 years ago
|
function replaceLibReference (code, pos) {
|
||
|
return code.substring(0, pos) + '0000000000000000000000000000000000000000' + code.substring(pos + 40)
|
||
|
}
|
||
|
|
||
8 years ago
|
function buildCallPath (index, rootCall) {
|
||
8 years ago
|
var ret = []
|
||
|
findCallInternal(index, rootCall, ret)
|
||
|
return ret
|
||
8 years ago
|
}
|
||
|
|
||
|
function findCall (index, rootCall) {
|
||
8 years ago
|
var ret = buildCallPath(index, rootCall)
|
||
8 years ago
|
return ret[ret.length - 1]
|
||
|
}
|
||
|
|
||
|
function findCallInternal (index, rootCall, callsPath) {
|
||
8 years ago
|
var calls = Object.keys(rootCall.calls)
|
||
|
var ret = rootCall
|
||
8 years ago
|
callsPath.push(rootCall)
|
||
8 years ago
|
for (var k in calls) {
|
||
|
var subCall = rootCall.calls[calls[k]]
|
||
|
if (index >= subCall.start && index <= subCall.return) {
|
||
8 years ago
|
findCallInternal(index, subCall, callsPath)
|
||
8 years ago
|
break
|
||
|
}
|
||
9 years ago
|
}
|
||
8 years ago
|
return ret
|
||
9 years ago
|
}
|