github.com/shrimpyuk/bor@v0.2.15-0.20220224151350-fb4ec6020bae/eth/tracers/tracer.go (about) 1 // Copyright 2017 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package tracers 18 19 import ( 20 "encoding/json" 21 "errors" 22 "fmt" 23 "math/big" 24 "sync/atomic" 25 "time" 26 "unsafe" 27 28 "github.com/ethereum/go-ethereum/common" 29 "github.com/ethereum/go-ethereum/common/hexutil" 30 "github.com/ethereum/go-ethereum/core" 31 "github.com/ethereum/go-ethereum/core/vm" 32 "github.com/ethereum/go-ethereum/crypto" 33 "github.com/ethereum/go-ethereum/log" 34 "gopkg.in/olebedev/go-duktape.v3" 35 ) 36 37 // bigIntegerJS is the minified version of https://github.com/peterolson/BigInteger.js. 38 const bigIntegerJS = `var bigInt=function(undefined){"use strict";var BASE=1e7,LOG_BASE=7,MAX_INT=9007199254740992,MAX_INT_ARR=smallToArray(MAX_INT),LOG_MAX_INT=Math.log(MAX_INT);function Integer(v,radix){if(typeof v==="undefined")return Integer[0];if(typeof radix!=="undefined")return+radix===10?parseValue(v):parseBase(v,radix);return parseValue(v)}function BigInteger(value,sign){this.value=value;this.sign=sign;this.isSmall=false}BigInteger.prototype=Object.create(Integer.prototype);function SmallInteger(value){this.value=value;this.sign=value<0;this.isSmall=true}SmallInteger.prototype=Object.create(Integer.prototype);function isPrecise(n){return-MAX_INT<n&&n<MAX_INT}function smallToArray(n){if(n<1e7)return[n];if(n<1e14)return[n%1e7,Math.floor(n/1e7)];return[n%1e7,Math.floor(n/1e7)%1e7,Math.floor(n/1e14)]}function arrayToSmall(arr){trim(arr);var length=arr.length;if(length<4&&compareAbs(arr,MAX_INT_ARR)<0){switch(length){case 0:return 0;case 1:return arr[0];case 2:return arr[0]+arr[1]*BASE;default:return arr[0]+(arr[1]+arr[2]*BASE)*BASE}}return arr}function trim(v){var i=v.length;while(v[--i]===0);v.length=i+1}function createArray(length){var x=new Array(length);var i=-1;while(++i<length){x[i]=0}return x}function truncate(n){if(n>0)return Math.floor(n);return Math.ceil(n)}function add(a,b){var l_a=a.length,l_b=b.length,r=new Array(l_a),carry=0,base=BASE,sum,i;for(i=0;i<l_b;i++){sum=a[i]+b[i]+carry;carry=sum>=base?1:0;r[i]=sum-carry*base}while(i<l_a){sum=a[i]+carry;carry=sum===base?1:0;r[i++]=sum-carry*base}if(carry>0)r.push(carry);return r}function addAny(a,b){if(a.length>=b.length)return add(a,b);return add(b,a)}function addSmall(a,carry){var l=a.length,r=new Array(l),base=BASE,sum,i;for(i=0;i<l;i++){sum=a[i]-base+carry;carry=Math.floor(sum/base);r[i]=sum-carry*base;carry+=1}while(carry>0){r[i++]=carry%base;carry=Math.floor(carry/base)}return r}BigInteger.prototype.add=function(v){var n=parseValue(v);if(this.sign!==n.sign){return this.subtract(n.negate())}var a=this.value,b=n.value;if(n.isSmall){return new BigInteger(addSmall(a,Math.abs(b)),this.sign)}return new BigInteger(addAny(a,b),this.sign)};BigInteger.prototype.plus=BigInteger.prototype.add;SmallInteger.prototype.add=function(v){var n=parseValue(v);var a=this.value;if(a<0!==n.sign){return this.subtract(n.negate())}var b=n.value;if(n.isSmall){if(isPrecise(a+b))return new SmallInteger(a+b);b=smallToArray(Math.abs(b))}return new BigInteger(addSmall(b,Math.abs(a)),a<0)};SmallInteger.prototype.plus=SmallInteger.prototype.add;function subtract(a,b){var a_l=a.length,b_l=b.length,r=new Array(a_l),borrow=0,base=BASE,i,difference;for(i=0;i<b_l;i++){difference=a[i]-borrow-b[i];if(difference<0){difference+=base;borrow=1}else borrow=0;r[i]=difference}for(i=b_l;i<a_l;i++){difference=a[i]-borrow;if(difference<0)difference+=base;else{r[i++]=difference;break}r[i]=difference}for(;i<a_l;i++){r[i]=a[i]}trim(r);return r}function subtractAny(a,b,sign){var value;if(compareAbs(a,b)>=0){value=subtract(a,b)}else{value=subtract(b,a);sign=!sign}value=arrayToSmall(value);if(typeof value==="number"){if(sign)value=-value;return new SmallInteger(value)}return new BigInteger(value,sign)}function subtractSmall(a,b,sign){var l=a.length,r=new Array(l),carry=-b,base=BASE,i,difference;for(i=0;i<l;i++){difference=a[i]+carry;carry=Math.floor(difference/base);difference%=base;r[i]=difference<0?difference+base:difference}r=arrayToSmall(r);if(typeof r==="number"){if(sign)r=-r;return new SmallInteger(r)}return new BigInteger(r,sign)}BigInteger.prototype.subtract=function(v){var n=parseValue(v);if(this.sign!==n.sign){return this.add(n.negate())}var a=this.value,b=n.value;if(n.isSmall)return subtractSmall(a,Math.abs(b),this.sign);return subtractAny(a,b,this.sign)};BigInteger.prototype.minus=BigInteger.prototype.subtract;SmallInteger.prototype.subtract=function(v){var n=parseValue(v);var a=this.value;if(a<0!==n.sign){return this.add(n.negate())}var b=n.value;if(n.isSmall){return new SmallInteger(a-b)}return subtractSmall(b,Math.abs(a),a>=0)};SmallInteger.prototype.minus=SmallInteger.prototype.subtract;BigInteger.prototype.negate=function(){return new BigInteger(this.value,!this.sign)};SmallInteger.prototype.negate=function(){var sign=this.sign;var small=new SmallInteger(-this.value);small.sign=!sign;return small};BigInteger.prototype.abs=function(){return new BigInteger(this.value,false)};SmallInteger.prototype.abs=function(){return new SmallInteger(Math.abs(this.value))};function multiplyLong(a,b){var a_l=a.length,b_l=b.length,l=a_l+b_l,r=createArray(l),base=BASE,product,carry,i,a_i,b_j;for(i=0;i<a_l;++i){a_i=a[i];for(var j=0;j<b_l;++j){b_j=b[j];product=a_i*b_j+r[i+j];carry=Math.floor(product/base);r[i+j]=product-carry*base;r[i+j+1]+=carry}}trim(r);return r}function multiplySmall(a,b){var l=a.length,r=new Array(l),base=BASE,carry=0,product,i;for(i=0;i<l;i++){product=a[i]*b+carry;carry=Math.floor(product/base);r[i]=product-carry*base}while(carry>0){r[i++]=carry%base;carry=Math.floor(carry/base)}return r}function shiftLeft(x,n){var r=[];while(n-- >0)r.push(0);return r.concat(x)}function multiplyKaratsuba(x,y){var n=Math.max(x.length,y.length);if(n<=30)return multiplyLong(x,y);n=Math.ceil(n/2);var b=x.slice(n),a=x.slice(0,n),d=y.slice(n),c=y.slice(0,n);var ac=multiplyKaratsuba(a,c),bd=multiplyKaratsuba(b,d),abcd=multiplyKaratsuba(addAny(a,b),addAny(c,d));var product=addAny(addAny(ac,shiftLeft(subtract(subtract(abcd,ac),bd),n)),shiftLeft(bd,2*n));trim(product);return product}function useKaratsuba(l1,l2){return-.012*l1-.012*l2+15e-6*l1*l2>0}BigInteger.prototype.multiply=function(v){var n=parseValue(v),a=this.value,b=n.value,sign=this.sign!==n.sign,abs;if(n.isSmall){if(b===0)return Integer[0];if(b===1)return this;if(b===-1)return this.negate();abs=Math.abs(b);if(abs<BASE){return new BigInteger(multiplySmall(a,abs),sign)}b=smallToArray(abs)}if(useKaratsuba(a.length,b.length))return new BigInteger(multiplyKaratsuba(a,b),sign);return new BigInteger(multiplyLong(a,b),sign)};BigInteger.prototype.times=BigInteger.prototype.multiply;function multiplySmallAndArray(a,b,sign){if(a<BASE){return new BigInteger(multiplySmall(b,a),sign)}return new BigInteger(multiplyLong(b,smallToArray(a)),sign)}SmallInteger.prototype._multiplyBySmall=function(a){if(isPrecise(a.value*this.value)){return new SmallInteger(a.value*this.value)}return multiplySmallAndArray(Math.abs(a.value),smallToArray(Math.abs(this.value)),this.sign!==a.sign)};BigInteger.prototype._multiplyBySmall=function(a){if(a.value===0)return Integer[0];if(a.value===1)return this;if(a.value===-1)return this.negate();return multiplySmallAndArray(Math.abs(a.value),this.value,this.sign!==a.sign)};SmallInteger.prototype.multiply=function(v){return parseValue(v)._multiplyBySmall(this)};SmallInteger.prototype.times=SmallInteger.prototype.multiply;function square(a){var l=a.length,r=createArray(l+l),base=BASE,product,carry,i,a_i,a_j;for(i=0;i<l;i++){a_i=a[i];for(var j=0;j<l;j++){a_j=a[j];product=a_i*a_j+r[i+j];carry=Math.floor(product/base);r[i+j]=product-carry*base;r[i+j+1]+=carry}}trim(r);return r}BigInteger.prototype.square=function(){return new BigInteger(square(this.value),false)};SmallInteger.prototype.square=function(){var value=this.value*this.value;if(isPrecise(value))return new SmallInteger(value);return new BigInteger(square(smallToArray(Math.abs(this.value))),false)};function divMod1(a,b){var a_l=a.length,b_l=b.length,base=BASE,result=createArray(b.length),divisorMostSignificantDigit=b[b_l-1],lambda=Math.ceil(base/(2*divisorMostSignificantDigit)),remainder=multiplySmall(a,lambda),divisor=multiplySmall(b,lambda),quotientDigit,shift,carry,borrow,i,l,q;if(remainder.length<=a_l)remainder.push(0);divisor.push(0);divisorMostSignificantDigit=divisor[b_l-1];for(shift=a_l-b_l;shift>=0;shift--){quotientDigit=base-1;if(remainder[shift+b_l]!==divisorMostSignificantDigit){quotientDigit=Math.floor((remainder[shift+b_l]*base+remainder[shift+b_l-1])/divisorMostSignificantDigit)}carry=0;borrow=0;l=divisor.length;for(i=0;i<l;i++){carry+=quotientDigit*divisor[i];q=Math.floor(carry/base);borrow+=remainder[shift+i]-(carry-q*base);carry=q;if(borrow<0){remainder[shift+i]=borrow+base;borrow=-1}else{remainder[shift+i]=borrow;borrow=0}}while(borrow!==0){quotientDigit-=1;carry=0;for(i=0;i<l;i++){carry+=remainder[shift+i]-base+divisor[i];if(carry<0){remainder[shift+i]=carry+base;carry=0}else{remainder[shift+i]=carry;carry=1}}borrow+=carry}result[shift]=quotientDigit}remainder=divModSmall(remainder,lambda)[0];return[arrayToSmall(result),arrayToSmall(remainder)]}function divMod2(a,b){var a_l=a.length,b_l=b.length,result=[],part=[],base=BASE,guess,xlen,highx,highy,check;while(a_l){part.unshift(a[--a_l]);trim(part);if(compareAbs(part,b)<0){result.push(0);continue}xlen=part.length;highx=part[xlen-1]*base+part[xlen-2];highy=b[b_l-1]*base+b[b_l-2];if(xlen>b_l){highx=(highx+1)*base}guess=Math.ceil(highx/highy);do{check=multiplySmall(b,guess);if(compareAbs(check,part)<=0)break;guess--}while(guess);result.push(guess);part=subtract(part,check)}result.reverse();return[arrayToSmall(result),arrayToSmall(part)]}function divModSmall(value,lambda){var length=value.length,quotient=createArray(length),base=BASE,i,q,remainder,divisor;remainder=0;for(i=length-1;i>=0;--i){divisor=remainder*base+value[i];q=truncate(divisor/lambda);remainder=divisor-q*lambda;quotient[i]=q|0}return[quotient,remainder|0]}function divModAny(self,v){var value,n=parseValue(v);var a=self.value,b=n.value;var quotient;if(b===0)throw new Error("Cannot divide by zero");if(self.isSmall){if(n.isSmall){return[new SmallInteger(truncate(a/b)),new SmallInteger(a%b)]}return[Integer[0],self]}if(n.isSmall){if(b===1)return[self,Integer[0]];if(b==-1)return[self.negate(),Integer[0]];var abs=Math.abs(b);if(abs<BASE){value=divModSmall(a,abs);quotient=arrayToSmall(value[0]);var remainder=value[1];if(self.sign)remainder=-remainder;if(typeof quotient==="number"){if(self.sign!==n.sign)quotient=-quotient;return[new SmallInteger(quotient),new SmallInteger(remainder)]}return[new BigInteger(quotient,self.sign!==n.sign),new SmallInteger(remainder)]}b=smallToArray(abs)}var comparison=compareAbs(a,b);if(comparison===-1)return[Integer[0],self];if(comparison===0)return[Integer[self.sign===n.sign?1:-1],Integer[0]];if(a.length+b.length<=200)value=divMod1(a,b);else value=divMod2(a,b);quotient=value[0];var qSign=self.sign!==n.sign,mod=value[1],mSign=self.sign;if(typeof quotient==="number"){if(qSign)quotient=-quotient;quotient=new SmallInteger(quotient)}else quotient=new BigInteger(quotient,qSign);if(typeof mod==="number"){if(mSign)mod=-mod;mod=new SmallInteger(mod)}else mod=new BigInteger(mod,mSign);return[quotient,mod]}BigInteger.prototype.divmod=function(v){var result=divModAny(this,v);return{quotient:result[0],remainder:result[1]}};SmallInteger.prototype.divmod=BigInteger.prototype.divmod;BigInteger.prototype.divide=function(v){return divModAny(this,v)[0]};SmallInteger.prototype.over=SmallInteger.prototype.divide=BigInteger.prototype.over=BigInteger.prototype.divide;BigInteger.prototype.mod=function(v){return divModAny(this,v)[1]};SmallInteger.prototype.remainder=SmallInteger.prototype.mod=BigInteger.prototype.remainder=BigInteger.prototype.mod;BigInteger.prototype.pow=function(v){var n=parseValue(v),a=this.value,b=n.value,value,x,y;if(b===0)return Integer[1];if(a===0)return Integer[0];if(a===1)return Integer[1];if(a===-1)return n.isEven()?Integer[1]:Integer[-1];if(n.sign){return Integer[0]}if(!n.isSmall)throw new Error("The exponent "+n.toString()+" is too large.");if(this.isSmall){if(isPrecise(value=Math.pow(a,b)))return new SmallInteger(truncate(value))}x=this;y=Integer[1];while(true){if(b&1===1){y=y.times(x);--b}if(b===0)break;b/=2;x=x.square()}return y};SmallInteger.prototype.pow=BigInteger.prototype.pow;BigInteger.prototype.modPow=function(exp,mod){exp=parseValue(exp);mod=parseValue(mod);if(mod.isZero())throw new Error("Cannot take modPow with modulus 0");var r=Integer[1],base=this.mod(mod);while(exp.isPositive()){if(base.isZero())return Integer[0];if(exp.isOdd())r=r.multiply(base).mod(mod);exp=exp.divide(2);base=base.square().mod(mod)}return r};SmallInteger.prototype.modPow=BigInteger.prototype.modPow;function compareAbs(a,b){if(a.length!==b.length){return a.length>b.length?1:-1}for(var i=a.length-1;i>=0;i--){if(a[i]!==b[i])return a[i]>b[i]?1:-1}return 0}BigInteger.prototype.compareAbs=function(v){var n=parseValue(v),a=this.value,b=n.value;if(n.isSmall)return 1;return compareAbs(a,b)};SmallInteger.prototype.compareAbs=function(v){var n=parseValue(v),a=Math.abs(this.value),b=n.value;if(n.isSmall){b=Math.abs(b);return a===b?0:a>b?1:-1}return-1};BigInteger.prototype.compare=function(v){if(v===Infinity){return-1}if(v===-Infinity){return 1}var n=parseValue(v),a=this.value,b=n.value;if(this.sign!==n.sign){return n.sign?1:-1}if(n.isSmall){return this.sign?-1:1}return compareAbs(a,b)*(this.sign?-1:1)};BigInteger.prototype.compareTo=BigInteger.prototype.compare;SmallInteger.prototype.compare=function(v){if(v===Infinity){return-1}if(v===-Infinity){return 1}var n=parseValue(v),a=this.value,b=n.value;if(n.isSmall){return a==b?0:a>b?1:-1}if(a<0!==n.sign){return a<0?-1:1}return a<0?1:-1};SmallInteger.prototype.compareTo=SmallInteger.prototype.compare;BigInteger.prototype.equals=function(v){return this.compare(v)===0};SmallInteger.prototype.eq=SmallInteger.prototype.equals=BigInteger.prototype.eq=BigInteger.prototype.equals;BigInteger.prototype.notEquals=function(v){return this.compare(v)!==0};SmallInteger.prototype.neq=SmallInteger.prototype.notEquals=BigInteger.prototype.neq=BigInteger.prototype.notEquals;BigInteger.prototype.greater=function(v){return this.compare(v)>0};SmallInteger.prototype.gt=SmallInteger.prototype.greater=BigInteger.prototype.gt=BigInteger.prototype.greater;BigInteger.prototype.lesser=function(v){return this.compare(v)<0};SmallInteger.prototype.lt=SmallInteger.prototype.lesser=BigInteger.prototype.lt=BigInteger.prototype.lesser;BigInteger.prototype.greaterOrEquals=function(v){return this.compare(v)>=0};SmallInteger.prototype.geq=SmallInteger.prototype.greaterOrEquals=BigInteger.prototype.geq=BigInteger.prototype.greaterOrEquals;BigInteger.prototype.lesserOrEquals=function(v){return this.compare(v)<=0};SmallInteger.prototype.leq=SmallInteger.prototype.lesserOrEquals=BigInteger.prototype.leq=BigInteger.prototype.lesserOrEquals;BigInteger.prototype.isEven=function(){return(this.value[0]&1)===0};SmallInteger.prototype.isEven=function(){return(this.value&1)===0};BigInteger.prototype.isOdd=function(){return(this.value[0]&1)===1};SmallInteger.prototype.isOdd=function(){return(this.value&1)===1};BigInteger.prototype.isPositive=function(){return!this.sign};SmallInteger.prototype.isPositive=function(){return this.value>0};BigInteger.prototype.isNegative=function(){return this.sign};SmallInteger.prototype.isNegative=function(){return this.value<0};BigInteger.prototype.isUnit=function(){return false};SmallInteger.prototype.isUnit=function(){return Math.abs(this.value)===1};BigInteger.prototype.isZero=function(){return false};SmallInteger.prototype.isZero=function(){return this.value===0};BigInteger.prototype.isDivisibleBy=function(v){var n=parseValue(v);var value=n.value;if(value===0)return false;if(value===1)return true;if(value===2)return this.isEven();return this.mod(n).equals(Integer[0])};SmallInteger.prototype.isDivisibleBy=BigInteger.prototype.isDivisibleBy;function isBasicPrime(v){var n=v.abs();if(n.isUnit())return false;if(n.equals(2)||n.equals(3)||n.equals(5))return true;if(n.isEven()||n.isDivisibleBy(3)||n.isDivisibleBy(5))return false;if(n.lesser(25))return true}BigInteger.prototype.isPrime=function(){var isPrime=isBasicPrime(this);if(isPrime!==undefined)return isPrime;var n=this.abs(),nPrev=n.prev();var a=[2,3,5,7,11,13,17,19],b=nPrev,d,t,i,x;while(b.isEven())b=b.divide(2);for(i=0;i<a.length;i++){x=bigInt(a[i]).modPow(b,n);if(x.equals(Integer[1])||x.equals(nPrev))continue;for(t=true,d=b;t&&d.lesser(nPrev);d=d.multiply(2)){x=x.square().mod(n);if(x.equals(nPrev))t=false}if(t)return false}return true};SmallInteger.prototype.isPrime=BigInteger.prototype.isPrime;BigInteger.prototype.isProbablePrime=function(iterations){var isPrime=isBasicPrime(this);if(isPrime!==undefined)return isPrime;var n=this.abs();var t=iterations===undefined?5:iterations;for(var i=0;i<t;i++){var a=bigInt.randBetween(2,n.minus(2));if(!a.modPow(n.prev(),n).isUnit())return false}return true};SmallInteger.prototype.isProbablePrime=BigInteger.prototype.isProbablePrime;BigInteger.prototype.modInv=function(n){var t=bigInt.zero,newT=bigInt.one,r=parseValue(n),newR=this.abs(),q,lastT,lastR;while(!newR.equals(bigInt.zero)){q=r.divide(newR);lastT=t;lastR=r;t=newT;r=newR;newT=lastT.subtract(q.multiply(newT));newR=lastR.subtract(q.multiply(newR))}if(!r.equals(1))throw new Error(this.toString()+" and "+n.toString()+" are not co-prime");if(t.compare(0)===-1){t=t.add(n)}if(this.isNegative()){return t.negate()}return t};SmallInteger.prototype.modInv=BigInteger.prototype.modInv;BigInteger.prototype.next=function(){var value=this.value;if(this.sign){return subtractSmall(value,1,this.sign)}return new BigInteger(addSmall(value,1),this.sign)};SmallInteger.prototype.next=function(){var value=this.value;if(value+1<MAX_INT)return new SmallInteger(value+1);return new BigInteger(MAX_INT_ARR,false)};BigInteger.prototype.prev=function(){var value=this.value;if(this.sign){return new BigInteger(addSmall(value,1),true)}return subtractSmall(value,1,this.sign)};SmallInteger.prototype.prev=function(){var value=this.value;if(value-1>-MAX_INT)return new SmallInteger(value-1);return new BigInteger(MAX_INT_ARR,true)};var powersOfTwo=[1];while(2*powersOfTwo[powersOfTwo.length-1]<=BASE)powersOfTwo.push(2*powersOfTwo[powersOfTwo.length-1]);var powers2Length=powersOfTwo.length,highestPower2=powersOfTwo[powers2Length-1];function shift_isSmall(n){return(typeof n==="number"||typeof n==="string")&&+Math.abs(n)<=BASE||n instanceof BigInteger&&n.value.length<=1}BigInteger.prototype.shiftLeft=function(n){if(!shift_isSmall(n)){throw new Error(String(n)+" is too large for shifting.")}n=+n;if(n<0)return this.shiftRight(-n);var result=this;while(n>=powers2Length){result=result.multiply(highestPower2);n-=powers2Length-1}return result.multiply(powersOfTwo[n])};SmallInteger.prototype.shiftLeft=BigInteger.prototype.shiftLeft;BigInteger.prototype.shiftRight=function(n){var remQuo;if(!shift_isSmall(n)){throw new Error(String(n)+" is too large for shifting.")}n=+n;if(n<0)return this.shiftLeft(-n);var result=this;while(n>=powers2Length){if(result.isZero())return result;remQuo=divModAny(result,highestPower2);result=remQuo[1].isNegative()?remQuo[0].prev():remQuo[0];n-=powers2Length-1}remQuo=divModAny(result,powersOfTwo[n]);return remQuo[1].isNegative()?remQuo[0].prev():remQuo[0]};SmallInteger.prototype.shiftRight=BigInteger.prototype.shiftRight;function bitwise(x,y,fn){y=parseValue(y);var xSign=x.isNegative(),ySign=y.isNegative();var xRem=xSign?x.not():x,yRem=ySign?y.not():y;var xDigit=0,yDigit=0;var xDivMod=null,yDivMod=null;var result=[];while(!xRem.isZero()||!yRem.isZero()){xDivMod=divModAny(xRem,highestPower2);xDigit=xDivMod[1].toJSNumber();if(xSign){xDigit=highestPower2-1-xDigit}yDivMod=divModAny(yRem,highestPower2);yDigit=yDivMod[1].toJSNumber();if(ySign){yDigit=highestPower2-1-yDigit}xRem=xDivMod[0];yRem=yDivMod[0];result.push(fn(xDigit,yDigit))}var sum=fn(xSign?1:0,ySign?1:0)!==0?bigInt(-1):bigInt(0);for(var i=result.length-1;i>=0;i-=1){sum=sum.multiply(highestPower2).add(bigInt(result[i]))}return sum}BigInteger.prototype.not=function(){return this.negate().prev()};SmallInteger.prototype.not=BigInteger.prototype.not;BigInteger.prototype.and=function(n){return bitwise(this,n,function(a,b){return a&b})};SmallInteger.prototype.and=BigInteger.prototype.and;BigInteger.prototype.or=function(n){return bitwise(this,n,function(a,b){return a|b})};SmallInteger.prototype.or=BigInteger.prototype.or;BigInteger.prototype.xor=function(n){return bitwise(this,n,function(a,b){return a^b})};SmallInteger.prototype.xor=BigInteger.prototype.xor;var LOBMASK_I=1<<30,LOBMASK_BI=(BASE&-BASE)*(BASE&-BASE)|LOBMASK_I;function roughLOB(n){var v=n.value,x=typeof v==="number"?v|LOBMASK_I:v[0]+v[1]*BASE|LOBMASK_BI;return x&-x}function max(a,b){a=parseValue(a);b=parseValue(b);return a.greater(b)?a:b}function min(a,b){a=parseValue(a);b=parseValue(b);return a.lesser(b)?a:b}function gcd(a,b){a=parseValue(a).abs();b=parseValue(b).abs();if(a.equals(b))return a;if(a.isZero())return b;if(b.isZero())return a;var c=Integer[1],d,t;while(a.isEven()&&b.isEven()){d=Math.min(roughLOB(a),roughLOB(b));a=a.divide(d);b=b.divide(d);c=c.multiply(d)}while(a.isEven()){a=a.divide(roughLOB(a))}do{while(b.isEven()){b=b.divide(roughLOB(b))}if(a.greater(b)){t=b;b=a;a=t}b=b.subtract(a)}while(!b.isZero());return c.isUnit()?a:a.multiply(c)}function lcm(a,b){a=parseValue(a).abs();b=parseValue(b).abs();return a.divide(gcd(a,b)).multiply(b)}function randBetween(a,b){a=parseValue(a);b=parseValue(b);var low=min(a,b),high=max(a,b);var range=high.subtract(low).add(1);if(range.isSmall)return low.add(Math.floor(Math.random()*range));var length=range.value.length-1;var result=[],restricted=true;for(var i=length;i>=0;i--){var top=restricted?range.value[i]:BASE;var digit=truncate(Math.random()*top);result.unshift(digit);if(digit<top)restricted=false}result=arrayToSmall(result);return low.add(typeof result==="number"?new SmallInteger(result):new BigInteger(result,false))}var parseBase=function(text,base){var length=text.length;var i;var absBase=Math.abs(base);for(var i=0;i<length;i++){var c=text[i].toLowerCase();if(c==="-")continue;if(/[a-z0-9]/.test(c)){if(/[0-9]/.test(c)&&+c>=absBase){if(c==="1"&&absBase===1)continue;throw new Error(c+" is not a valid digit in base "+base+".")}else if(c.charCodeAt(0)-87>=absBase){throw new Error(c+" is not a valid digit in base "+base+".")}}}if(2<=base&&base<=36){if(length<=LOG_MAX_INT/Math.log(base)){var result=parseInt(text,base);if(isNaN(result)){throw new Error(c+" is not a valid digit in base "+base+".")}return new SmallInteger(parseInt(text,base))}}base=parseValue(base);var digits=[];var isNegative=text[0]==="-";for(i=isNegative?1:0;i<text.length;i++){var c=text[i].toLowerCase(),charCode=c.charCodeAt(0);if(48<=charCode&&charCode<=57)digits.push(parseValue(c));else if(97<=charCode&&charCode<=122)digits.push(parseValue(c.charCodeAt(0)-87));else if(c==="<"){var start=i;do{i++}while(text[i]!==">");digits.push(parseValue(text.slice(start+1,i)))}else throw new Error(c+" is not a valid character")}return parseBaseFromArray(digits,base,isNegative)};function parseBaseFromArray(digits,base,isNegative){var val=Integer[0],pow=Integer[1],i;for(i=digits.length-1;i>=0;i--){val=val.add(digits[i].times(pow));pow=pow.times(base)}return isNegative?val.negate():val}function stringify(digit){var v=digit.value;if(typeof v==="number")v=[v];if(v.length===1&&v[0]<=35){return"0123456789abcdefghijklmnopqrstuvwxyz".charAt(v[0])}return"<"+v+">"}function toBase(n,base){base=bigInt(base);if(base.isZero()){if(n.isZero())return"0";throw new Error("Cannot convert nonzero numbers to base 0.")}if(base.equals(-1)){if(n.isZero())return"0";if(n.isNegative())return new Array(1-n).join("10");return"1"+new Array(+n).join("01")}var minusSign="";if(n.isNegative()&&base.isPositive()){minusSign="-";n=n.abs()}if(base.equals(1)){if(n.isZero())return"0";return minusSign+new Array(+n+1).join(1)}var out=[];var left=n,divmod;while(left.isNegative()||left.compareAbs(base)>=0){divmod=left.divmod(base);left=divmod.quotient;var digit=divmod.remainder;if(digit.isNegative()){digit=base.minus(digit).abs();left=left.next()}out.push(stringify(digit))}out.push(stringify(left));return minusSign+out.reverse().join("")}BigInteger.prototype.toString=function(radix){if(radix===undefined)radix=10;if(radix!==10)return toBase(this,radix);var v=this.value,l=v.length,str=String(v[--l]),zeros="0000000",digit;while(--l>=0){digit=String(v[l]);str+=zeros.slice(digit.length)+digit}var sign=this.sign?"-":"";return sign+str};SmallInteger.prototype.toString=function(radix){if(radix===undefined)radix=10;if(radix!=10)return toBase(this,radix);return String(this.value)};BigInteger.prototype.toJSON=SmallInteger.prototype.toJSON=function(){return this.toString()};BigInteger.prototype.valueOf=function(){return+this.toString()};BigInteger.prototype.toJSNumber=BigInteger.prototype.valueOf;SmallInteger.prototype.valueOf=function(){return this.value};SmallInteger.prototype.toJSNumber=SmallInteger.prototype.valueOf;function parseStringValue(v){if(isPrecise(+v)){var x=+v;if(x===truncate(x))return new SmallInteger(x);throw"Invalid integer: "+v}var sign=v[0]==="-";if(sign)v=v.slice(1);var split=v.split(/e/i);if(split.length>2)throw new Error("Invalid integer: "+split.join("e"));if(split.length===2){var exp=split[1];if(exp[0]==="+")exp=exp.slice(1);exp=+exp;if(exp!==truncate(exp)||!isPrecise(exp))throw new Error("Invalid integer: "+exp+" is not a valid exponent.");var text=split[0];var decimalPlace=text.indexOf(".");if(decimalPlace>=0){exp-=text.length-decimalPlace-1;text=text.slice(0,decimalPlace)+text.slice(decimalPlace+1)}if(exp<0)throw new Error("Cannot include negative exponent part for integers");text+=new Array(exp+1).join("0");v=text}var isValid=/^([0-9][0-9]*)$/.test(v);if(!isValid)throw new Error("Invalid integer: "+v);var r=[],max=v.length,l=LOG_BASE,min=max-l;while(max>0){r.push(+v.slice(min,max));min-=l;if(min<0)min=0;max-=l}trim(r);return new BigInteger(r,sign)}function parseNumberValue(v){if(isPrecise(v)){if(v!==truncate(v))throw new Error(v+" is not an integer.");return new SmallInteger(v)}return parseStringValue(v.toString())}function parseValue(v){if(typeof v==="number"){return parseNumberValue(v)}if(typeof v==="string"){return parseStringValue(v)}return v}for(var i=0;i<1e3;i++){Integer[i]=new SmallInteger(i);if(i>0)Integer[-i]=new SmallInteger(-i)}Integer.one=Integer[1];Integer.zero=Integer[0];Integer.minusOne=Integer[-1];Integer.max=max;Integer.min=min;Integer.gcd=gcd;Integer.lcm=lcm;Integer.isInstance=function(x){return x instanceof BigInteger||x instanceof SmallInteger};Integer.randBetween=randBetween;Integer.fromArray=function(digits,base,isNegative){return parseBaseFromArray(digits.map(parseValue),parseValue(base||10),isNegative)};return Integer}();if(typeof module!=="undefined"&&module.hasOwnProperty("exports")){module.exports=bigInt}if(typeof define==="function"&&define.amd){define("big-integer",[],function(){return bigInt})}; bigInt` 39 40 // makeSlice convert an unsafe memory pointer with the given type into a Go byte 41 // slice. 42 // 43 // Note, the returned slice uses the same memory area as the input arguments. 44 // If those are duktape stack items, popping them off **will** make the slice 45 // contents change. 46 func makeSlice(ptr unsafe.Pointer, size uint) []byte { 47 var sl = struct { 48 addr uintptr 49 len int 50 cap int 51 }{uintptr(ptr), int(size), int(size)} 52 53 return *(*[]byte)(unsafe.Pointer(&sl)) 54 } 55 56 // popSlice pops a buffer off the JavaScript stack and returns it as a slice. 57 func popSlice(ctx *duktape.Context) []byte { 58 blob := common.CopyBytes(makeSlice(ctx.GetBuffer(-1))) 59 ctx.Pop() 60 return blob 61 } 62 63 // pushBigInt create a JavaScript BigInteger in the VM. 64 func pushBigInt(n *big.Int, ctx *duktape.Context) { 65 ctx.GetGlobalString("bigInt") 66 ctx.PushString(n.String()) 67 ctx.Call(1) 68 } 69 70 // opWrapper provides a JavaScript wrapper around OpCode. 71 type opWrapper struct { 72 op vm.OpCode 73 } 74 75 // pushObject assembles a JSVM object wrapping a swappable opcode and pushes it 76 // onto the VM stack. 77 func (ow *opWrapper) pushObject(vm *duktape.Context) { 78 obj := vm.PushObject() 79 80 vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushInt(int(ow.op)); return 1 }) 81 vm.PutPropString(obj, "toNumber") 82 83 vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushString(ow.op.String()); return 1 }) 84 vm.PutPropString(obj, "toString") 85 86 vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushBoolean(ow.op.IsPush()); return 1 }) 87 vm.PutPropString(obj, "isPush") 88 } 89 90 // memoryWrapper provides a JavaScript wrapper around vm.Memory. 91 type memoryWrapper struct { 92 memory *vm.Memory 93 } 94 95 // slice returns the requested range of memory as a byte slice. 96 func (mw *memoryWrapper) slice(begin, end int64) []byte { 97 if end == begin { 98 return []byte{} 99 } 100 if end < begin || begin < 0 { 101 // TODO(karalabe): We can't js-throw from Go inside duktape inside Go. The Go 102 // runtime goes belly up https://github.com/golang/go/issues/15639. 103 log.Warn("Tracer accessed out of bound memory", "offset", begin, "end", end) 104 return nil 105 } 106 if mw.memory.Len() < int(end) { 107 // TODO(karalabe): We can't js-throw from Go inside duktape inside Go. The Go 108 // runtime goes belly up https://github.com/golang/go/issues/15639. 109 log.Warn("Tracer accessed out of bound memory", "available", mw.memory.Len(), "offset", begin, "size", end-begin) 110 return nil 111 } 112 return mw.memory.GetCopy(begin, end-begin) 113 } 114 115 // getUint returns the 32 bytes at the specified address interpreted as a uint. 116 func (mw *memoryWrapper) getUint(addr int64) *big.Int { 117 if mw.memory.Len() < int(addr)+32 || addr < 0 { 118 // TODO(karalabe): We can't js-throw from Go inside duktape inside Go. The Go 119 // runtime goes belly up https://github.com/golang/go/issues/15639. 120 log.Warn("Tracer accessed out of bound memory", "available", mw.memory.Len(), "offset", addr, "size", 32) 121 return new(big.Int) 122 } 123 return new(big.Int).SetBytes(mw.memory.GetPtr(addr, 32)) 124 } 125 126 // pushObject assembles a JSVM object wrapping a swappable memory and pushes it 127 // onto the VM stack. 128 func (mw *memoryWrapper) pushObject(vm *duktape.Context) { 129 obj := vm.PushObject() 130 131 // Generate the `slice` method which takes two ints and returns a buffer 132 vm.PushGoFunction(func(ctx *duktape.Context) int { 133 blob := mw.slice(int64(ctx.GetInt(-2)), int64(ctx.GetInt(-1))) 134 ctx.Pop2() 135 136 ptr := ctx.PushFixedBuffer(len(blob)) 137 copy(makeSlice(ptr, uint(len(blob))), blob) 138 return 1 139 }) 140 vm.PutPropString(obj, "slice") 141 142 // Generate the `getUint` method which takes an int and returns a bigint 143 vm.PushGoFunction(func(ctx *duktape.Context) int { 144 offset := int64(ctx.GetInt(-1)) 145 ctx.Pop() 146 147 pushBigInt(mw.getUint(offset), ctx) 148 return 1 149 }) 150 vm.PutPropString(obj, "getUint") 151 } 152 153 // stackWrapper provides a JavaScript wrapper around vm.Stack. 154 type stackWrapper struct { 155 stack *vm.Stack 156 } 157 158 // peek returns the nth-from-the-top element of the stack. 159 func (sw *stackWrapper) peek(idx int) *big.Int { 160 if len(sw.stack.Data()) <= idx || idx < 0 { 161 // TODO(karalabe): We can't js-throw from Go inside duktape inside Go. The Go 162 // runtime goes belly up https://github.com/golang/go/issues/15639. 163 log.Warn("Tracer accessed out of bound stack", "size", len(sw.stack.Data()), "index", idx) 164 return new(big.Int) 165 } 166 return sw.stack.Back(idx).ToBig() 167 } 168 169 // pushObject assembles a JSVM object wrapping a swappable stack and pushes it 170 // onto the VM stack. 171 func (sw *stackWrapper) pushObject(vm *duktape.Context) { 172 obj := vm.PushObject() 173 174 vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushInt(len(sw.stack.Data())); return 1 }) 175 vm.PutPropString(obj, "length") 176 177 // Generate the `peek` method which takes an int and returns a bigint 178 vm.PushGoFunction(func(ctx *duktape.Context) int { 179 offset := ctx.GetInt(-1) 180 ctx.Pop() 181 182 pushBigInt(sw.peek(offset), ctx) 183 return 1 184 }) 185 vm.PutPropString(obj, "peek") 186 } 187 188 // dbWrapper provides a JavaScript wrapper around vm.Database. 189 type dbWrapper struct { 190 db vm.StateDB 191 } 192 193 // pushObject assembles a JSVM object wrapping a swappable database and pushes it 194 // onto the VM stack. 195 func (dw *dbWrapper) pushObject(vm *duktape.Context) { 196 obj := vm.PushObject() 197 198 // Push the wrapper for statedb.GetBalance 199 vm.PushGoFunction(func(ctx *duktape.Context) int { 200 pushBigInt(dw.db.GetBalance(common.BytesToAddress(popSlice(ctx))), ctx) 201 return 1 202 }) 203 vm.PutPropString(obj, "getBalance") 204 205 // Push the wrapper for statedb.GetNonce 206 vm.PushGoFunction(func(ctx *duktape.Context) int { 207 ctx.PushInt(int(dw.db.GetNonce(common.BytesToAddress(popSlice(ctx))))) 208 return 1 209 }) 210 vm.PutPropString(obj, "getNonce") 211 212 // Push the wrapper for statedb.GetCode 213 vm.PushGoFunction(func(ctx *duktape.Context) int { 214 code := dw.db.GetCode(common.BytesToAddress(popSlice(ctx))) 215 216 ptr := ctx.PushFixedBuffer(len(code)) 217 copy(makeSlice(ptr, uint(len(code))), code) 218 return 1 219 }) 220 vm.PutPropString(obj, "getCode") 221 222 // Push the wrapper for statedb.GetState 223 vm.PushGoFunction(func(ctx *duktape.Context) int { 224 hash := popSlice(ctx) 225 addr := popSlice(ctx) 226 227 state := dw.db.GetState(common.BytesToAddress(addr), common.BytesToHash(hash)) 228 229 ptr := ctx.PushFixedBuffer(len(state)) 230 copy(makeSlice(ptr, uint(len(state))), state[:]) 231 return 1 232 }) 233 vm.PutPropString(obj, "getState") 234 235 // Push the wrapper for statedb.Exists 236 vm.PushGoFunction(func(ctx *duktape.Context) int { 237 ctx.PushBoolean(dw.db.Exist(common.BytesToAddress(popSlice(ctx)))) 238 return 1 239 }) 240 vm.PutPropString(obj, "exists") 241 } 242 243 // contractWrapper provides a JavaScript wrapper around vm.Contract 244 type contractWrapper struct { 245 contract *vm.Contract 246 } 247 248 // pushObject assembles a JSVM object wrapping a swappable contract and pushes it 249 // onto the VM stack. 250 func (cw *contractWrapper) pushObject(vm *duktape.Context) { 251 obj := vm.PushObject() 252 253 // Push the wrapper for contract.Caller 254 vm.PushGoFunction(func(ctx *duktape.Context) int { 255 ptr := ctx.PushFixedBuffer(20) 256 copy(makeSlice(ptr, 20), cw.contract.Caller().Bytes()) 257 return 1 258 }) 259 vm.PutPropString(obj, "getCaller") 260 261 // Push the wrapper for contract.Address 262 vm.PushGoFunction(func(ctx *duktape.Context) int { 263 ptr := ctx.PushFixedBuffer(20) 264 copy(makeSlice(ptr, 20), cw.contract.Address().Bytes()) 265 return 1 266 }) 267 vm.PutPropString(obj, "getAddress") 268 269 // Push the wrapper for contract.Value 270 vm.PushGoFunction(func(ctx *duktape.Context) int { 271 pushBigInt(cw.contract.Value(), ctx) 272 return 1 273 }) 274 vm.PutPropString(obj, "getValue") 275 276 // Push the wrapper for contract.Input 277 vm.PushGoFunction(func(ctx *duktape.Context) int { 278 blob := cw.contract.Input 279 280 ptr := ctx.PushFixedBuffer(len(blob)) 281 copy(makeSlice(ptr, uint(len(blob))), blob) 282 return 1 283 }) 284 vm.PutPropString(obj, "getInput") 285 } 286 287 type frame struct { 288 typ *string 289 from *common.Address 290 to *common.Address 291 input []byte 292 gas *uint 293 value *big.Int 294 } 295 296 func newFrame() *frame { 297 return &frame{ 298 typ: new(string), 299 from: new(common.Address), 300 to: new(common.Address), 301 gas: new(uint), 302 } 303 } 304 305 func (f *frame) pushObject(vm *duktape.Context) { 306 obj := vm.PushObject() 307 308 vm.PushGoFunction(func(ctx *duktape.Context) int { pushValue(ctx, *f.typ); return 1 }) 309 vm.PutPropString(obj, "getType") 310 311 vm.PushGoFunction(func(ctx *duktape.Context) int { pushValue(ctx, *f.from); return 1 }) 312 vm.PutPropString(obj, "getFrom") 313 314 vm.PushGoFunction(func(ctx *duktape.Context) int { pushValue(ctx, *f.to); return 1 }) 315 vm.PutPropString(obj, "getTo") 316 317 vm.PushGoFunction(func(ctx *duktape.Context) int { pushValue(ctx, f.input); return 1 }) 318 vm.PutPropString(obj, "getInput") 319 320 vm.PushGoFunction(func(ctx *duktape.Context) int { pushValue(ctx, *f.gas); return 1 }) 321 vm.PutPropString(obj, "getGas") 322 323 vm.PushGoFunction(func(ctx *duktape.Context) int { 324 if f.value != nil { 325 pushValue(ctx, f.value) 326 } else { 327 ctx.PushUndefined() 328 } 329 return 1 330 }) 331 vm.PutPropString(obj, "getValue") 332 } 333 334 type frameResult struct { 335 gasUsed *uint 336 output []byte 337 errorValue *string 338 } 339 340 func newFrameResult() *frameResult { 341 return &frameResult{ 342 gasUsed: new(uint), 343 } 344 } 345 346 func (r *frameResult) pushObject(vm *duktape.Context) { 347 obj := vm.PushObject() 348 349 vm.PushGoFunction(func(ctx *duktape.Context) int { pushValue(ctx, *r.gasUsed); return 1 }) 350 vm.PutPropString(obj, "getGasUsed") 351 352 vm.PushGoFunction(func(ctx *duktape.Context) int { pushValue(ctx, r.output); return 1 }) 353 vm.PutPropString(obj, "getOutput") 354 355 vm.PushGoFunction(func(ctx *duktape.Context) int { 356 if r.errorValue != nil { 357 pushValue(ctx, *r.errorValue) 358 } else { 359 ctx.PushUndefined() 360 } 361 return 1 362 }) 363 vm.PutPropString(obj, "getError") 364 } 365 366 // Tracer provides an implementation of Tracer that evaluates a Javascript 367 // function for each VM execution step. 368 type Tracer struct { 369 vm *duktape.Context // Javascript VM instance 370 371 tracerObject int // Stack index of the tracer JavaScript object 372 stateObject int // Stack index of the global state to pull arguments from 373 374 opWrapper *opWrapper // Wrapper around the VM opcode 375 stackWrapper *stackWrapper // Wrapper around the VM stack 376 memoryWrapper *memoryWrapper // Wrapper around the VM memory 377 contractWrapper *contractWrapper // Wrapper around the contract object 378 dbWrapper *dbWrapper // Wrapper around the VM environment 379 380 pcValue *uint // Swappable pc value wrapped by a log accessor 381 gasValue *uint // Swappable gas value wrapped by a log accessor 382 costValue *uint // Swappable cost value wrapped by a log accessor 383 depthValue *uint // Swappable depth value wrapped by a log accessor 384 errorValue *string // Swappable error value wrapped by a log accessor 385 refundValue *uint // Swappable refund value wrapped by a log accessor 386 387 frame *frame // Represents entry into call frame. Fields are swappable 388 frameResult *frameResult // Represents exit from a call frame. Fields are swappable 389 390 ctx map[string]interface{} // Transaction context gathered throughout execution 391 err error // Error, if one has occurred 392 393 interrupt uint32 // Atomic flag to signal execution interruption 394 reason error // Textual reason for the interruption 395 396 activePrecompiles []common.Address // Updated on CaptureStart based on given rules 397 traceSteps bool // When true, will invoke step() on each opcode 398 traceCallFrames bool // When true, will invoke enter() and exit() js funcs 399 } 400 401 // Context contains some contextual infos for a transaction execution that is not 402 // available from within the EVM object. 403 type Context struct { 404 BlockHash common.Hash // Hash of the block the tx is contained within (zero if dangling tx or call) 405 TxIndex int // Index of the transaction within a block (zero if dangling tx or call) 406 TxHash common.Hash // Hash of the transaction being traced (zero if dangling call) 407 } 408 409 // New instantiates a new tracer instance. code specifies a Javascript snippet, 410 // which must evaluate to an expression returning an object with 'step', 'fault' 411 // and 'result' functions. 412 func New(code string, ctx *Context) (*Tracer, error) { 413 // Resolve any tracers by name and assemble the tracer object 414 if tracer, ok := tracer(code); ok { 415 code = tracer 416 } 417 tracer := &Tracer{ 418 vm: duktape.New(), 419 ctx: make(map[string]interface{}), 420 opWrapper: new(opWrapper), 421 stackWrapper: new(stackWrapper), 422 memoryWrapper: new(memoryWrapper), 423 contractWrapper: new(contractWrapper), 424 dbWrapper: new(dbWrapper), 425 pcValue: new(uint), 426 gasValue: new(uint), 427 costValue: new(uint), 428 depthValue: new(uint), 429 refundValue: new(uint), 430 frame: newFrame(), 431 frameResult: newFrameResult(), 432 } 433 if ctx.BlockHash != (common.Hash{}) { 434 tracer.ctx["blockHash"] = ctx.BlockHash 435 436 if ctx.TxHash != (common.Hash{}) { 437 tracer.ctx["txIndex"] = ctx.TxIndex 438 tracer.ctx["txHash"] = ctx.TxHash 439 } 440 } 441 // Set up builtins for this environment 442 tracer.vm.PushGlobalGoFunction("toHex", func(ctx *duktape.Context) int { 443 ctx.PushString(hexutil.Encode(popSlice(ctx))) 444 return 1 445 }) 446 tracer.vm.PushGlobalGoFunction("toWord", func(ctx *duktape.Context) int { 447 var word common.Hash 448 if ptr, size := ctx.GetBuffer(-1); ptr != nil { 449 word = common.BytesToHash(makeSlice(ptr, size)) 450 } else { 451 word = common.HexToHash(ctx.GetString(-1)) 452 } 453 ctx.Pop() 454 copy(makeSlice(ctx.PushFixedBuffer(32), 32), word[:]) 455 return 1 456 }) 457 tracer.vm.PushGlobalGoFunction("toAddress", func(ctx *duktape.Context) int { 458 var addr common.Address 459 if ptr, size := ctx.GetBuffer(-1); ptr != nil { 460 addr = common.BytesToAddress(makeSlice(ptr, size)) 461 } else { 462 addr = common.HexToAddress(ctx.GetString(-1)) 463 } 464 ctx.Pop() 465 copy(makeSlice(ctx.PushFixedBuffer(20), 20), addr[:]) 466 return 1 467 }) 468 tracer.vm.PushGlobalGoFunction("toContract", func(ctx *duktape.Context) int { 469 var from common.Address 470 if ptr, size := ctx.GetBuffer(-2); ptr != nil { 471 from = common.BytesToAddress(makeSlice(ptr, size)) 472 } else { 473 from = common.HexToAddress(ctx.GetString(-2)) 474 } 475 nonce := uint64(ctx.GetInt(-1)) 476 ctx.Pop2() 477 478 contract := crypto.CreateAddress(from, nonce) 479 copy(makeSlice(ctx.PushFixedBuffer(20), 20), contract[:]) 480 return 1 481 }) 482 tracer.vm.PushGlobalGoFunction("toContract2", func(ctx *duktape.Context) int { 483 var from common.Address 484 if ptr, size := ctx.GetBuffer(-3); ptr != nil { 485 from = common.BytesToAddress(makeSlice(ptr, size)) 486 } else { 487 from = common.HexToAddress(ctx.GetString(-3)) 488 } 489 // Retrieve salt hex string from js stack 490 salt := common.HexToHash(ctx.GetString(-2)) 491 // Retrieve code slice from js stack 492 var code []byte 493 if ptr, size := ctx.GetBuffer(-1); ptr != nil { 494 code = common.CopyBytes(makeSlice(ptr, size)) 495 } else { 496 code = common.FromHex(ctx.GetString(-1)) 497 } 498 codeHash := crypto.Keccak256(code) 499 ctx.Pop3() 500 contract := crypto.CreateAddress2(from, salt, codeHash) 501 copy(makeSlice(ctx.PushFixedBuffer(20), 20), contract[:]) 502 return 1 503 }) 504 tracer.vm.PushGlobalGoFunction("isPrecompiled", func(ctx *duktape.Context) int { 505 addr := common.BytesToAddress(popSlice(ctx)) 506 for _, p := range tracer.activePrecompiles { 507 if p == addr { 508 ctx.PushBoolean(true) 509 return 1 510 } 511 } 512 ctx.PushBoolean(false) 513 return 1 514 }) 515 tracer.vm.PushGlobalGoFunction("slice", func(ctx *duktape.Context) int { 516 start, end := ctx.GetInt(-2), ctx.GetInt(-1) 517 ctx.Pop2() 518 519 blob := popSlice(ctx) 520 size := end - start 521 522 if start < 0 || start > end || end > len(blob) { 523 // TODO(karalabe): We can't js-throw from Go inside duktape inside Go. The Go 524 // runtime goes belly up https://github.com/golang/go/issues/15639. 525 log.Warn("Tracer accessed out of bound memory", "available", len(blob), "offset", start, "size", size) 526 ctx.PushFixedBuffer(0) 527 return 1 528 } 529 copy(makeSlice(ctx.PushFixedBuffer(size), uint(size)), blob[start:end]) 530 return 1 531 }) 532 // Push the JavaScript tracer as object #0 onto the JSVM stack and validate it 533 if err := tracer.vm.PevalString("(" + code + ")"); err != nil { 534 log.Warn("Failed to compile tracer", "err", err) 535 return nil, err 536 } 537 tracer.tracerObject = 0 // yeah, nice, eval can't return the index itself 538 539 hasStep := tracer.vm.GetPropString(tracer.tracerObject, "step") 540 tracer.vm.Pop() 541 542 if !tracer.vm.GetPropString(tracer.tracerObject, "fault") { 543 return nil, fmt.Errorf("trace object must expose a function fault()") 544 } 545 tracer.vm.Pop() 546 547 if !tracer.vm.GetPropString(tracer.tracerObject, "result") { 548 return nil, fmt.Errorf("trace object must expose a function result()") 549 } 550 tracer.vm.Pop() 551 552 hasEnter := tracer.vm.GetPropString(tracer.tracerObject, "enter") 553 tracer.vm.Pop() 554 hasExit := tracer.vm.GetPropString(tracer.tracerObject, "exit") 555 tracer.vm.Pop() 556 557 if hasEnter != hasExit { 558 return nil, fmt.Errorf("trace object must expose either both or none of enter() and exit()") 559 } 560 if !hasStep { 561 // If there's no step function, the enter and exit must be present 562 if !hasEnter { 563 return nil, fmt.Errorf("trace object must expose either step() or both enter() and exit()") 564 } 565 } 566 tracer.traceCallFrames = hasEnter 567 tracer.traceSteps = hasStep 568 569 // Tracer is valid, inject the big int library to access large numbers 570 tracer.vm.EvalString(bigIntegerJS) 571 tracer.vm.PutGlobalString("bigInt") 572 573 // Push the global environment state as object #1 into the JSVM stack 574 tracer.stateObject = tracer.vm.PushObject() 575 576 logObject := tracer.vm.PushObject() 577 578 tracer.opWrapper.pushObject(tracer.vm) 579 tracer.vm.PutPropString(logObject, "op") 580 581 tracer.stackWrapper.pushObject(tracer.vm) 582 tracer.vm.PutPropString(logObject, "stack") 583 584 tracer.memoryWrapper.pushObject(tracer.vm) 585 tracer.vm.PutPropString(logObject, "memory") 586 587 tracer.contractWrapper.pushObject(tracer.vm) 588 tracer.vm.PutPropString(logObject, "contract") 589 590 tracer.vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushUint(*tracer.pcValue); return 1 }) 591 tracer.vm.PutPropString(logObject, "getPC") 592 593 tracer.vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushUint(*tracer.gasValue); return 1 }) 594 tracer.vm.PutPropString(logObject, "getGas") 595 596 tracer.vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushUint(*tracer.costValue); return 1 }) 597 tracer.vm.PutPropString(logObject, "getCost") 598 599 tracer.vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushUint(*tracer.depthValue); return 1 }) 600 tracer.vm.PutPropString(logObject, "getDepth") 601 602 tracer.vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushUint(*tracer.refundValue); return 1 }) 603 tracer.vm.PutPropString(logObject, "getRefund") 604 605 tracer.vm.PushGoFunction(func(ctx *duktape.Context) int { 606 if tracer.errorValue != nil { 607 ctx.PushString(*tracer.errorValue) 608 } else { 609 ctx.PushUndefined() 610 } 611 return 1 612 }) 613 tracer.vm.PutPropString(logObject, "getError") 614 615 tracer.vm.PutPropString(tracer.stateObject, "log") 616 617 tracer.frame.pushObject(tracer.vm) 618 tracer.vm.PutPropString(tracer.stateObject, "frame") 619 620 tracer.frameResult.pushObject(tracer.vm) 621 tracer.vm.PutPropString(tracer.stateObject, "frameResult") 622 623 tracer.dbWrapper.pushObject(tracer.vm) 624 tracer.vm.PutPropString(tracer.stateObject, "db") 625 626 return tracer, nil 627 } 628 629 // Stop terminates execution of the tracer at the first opportune moment. 630 func (jst *Tracer) Stop(err error) { 631 jst.reason = err 632 atomic.StoreUint32(&jst.interrupt, 1) 633 } 634 635 // call executes a method on a JS object, catching any errors, formatting and 636 // returning them as error objects. 637 func (jst *Tracer) call(noret bool, method string, args ...string) (json.RawMessage, error) { 638 // Execute the JavaScript call and return any error 639 jst.vm.PushString(method) 640 for _, arg := range args { 641 jst.vm.GetPropString(jst.stateObject, arg) 642 } 643 code := jst.vm.PcallProp(jst.tracerObject, len(args)) 644 defer jst.vm.Pop() 645 646 if code != 0 { 647 err := jst.vm.SafeToString(-1) 648 return nil, errors.New(err) 649 } 650 // No error occurred, extract return value and return 651 if noret { 652 return nil, nil 653 } 654 // Push a JSON marshaller onto the stack. We can't marshal from the out- 655 // side because duktape can crash on large nestings and we can't catch 656 // C++ exceptions ourselves from Go. TODO(karalabe): Yuck, why wrap?! 657 jst.vm.PushString("(JSON.stringify)") 658 jst.vm.Eval() 659 660 jst.vm.Swap(-1, -2) 661 if code = jst.vm.Pcall(1); code != 0 { 662 err := jst.vm.SafeToString(-1) 663 return nil, errors.New(err) 664 } 665 return json.RawMessage(jst.vm.SafeToString(-1)), nil 666 } 667 668 func wrapError(context string, err error) error { 669 return fmt.Errorf("%v in server-side tracer function '%v'", err, context) 670 } 671 672 // CaptureStart implements the Tracer interface to initialize the tracing operation. 673 func (jst *Tracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { 674 jst.ctx["type"] = "CALL" 675 if create { 676 jst.ctx["type"] = "CREATE" 677 } 678 jst.ctx["from"] = from 679 jst.ctx["to"] = to 680 jst.ctx["input"] = input 681 jst.ctx["gas"] = gas 682 jst.ctx["gasPrice"] = env.TxContext.GasPrice 683 jst.ctx["value"] = value 684 685 // Initialize the context 686 jst.ctx["block"] = env.Context.BlockNumber.Uint64() 687 jst.dbWrapper.db = env.StateDB 688 // Update list of precompiles based on current block 689 rules := env.ChainConfig().Rules(env.Context.BlockNumber) 690 jst.activePrecompiles = vm.ActivePrecompiles(rules) 691 692 // Compute intrinsic gas 693 isHomestead := env.ChainConfig().IsHomestead(env.Context.BlockNumber) 694 isIstanbul := env.ChainConfig().IsIstanbul(env.Context.BlockNumber) 695 intrinsicGas, err := core.IntrinsicGas(input, nil, jst.ctx["type"] == "CREATE", isHomestead, isIstanbul) 696 if err != nil { 697 return 698 } 699 jst.ctx["intrinsicGas"] = intrinsicGas 700 } 701 702 // CaptureState implements the Tracer interface to trace a single step of VM execution. 703 func (jst *Tracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) { 704 if !jst.traceSteps { 705 return 706 } 707 if jst.err != nil { 708 return 709 } 710 // If tracing was interrupted, set the error and stop 711 if atomic.LoadUint32(&jst.interrupt) > 0 { 712 jst.err = jst.reason 713 env.Cancel() 714 return 715 } 716 jst.opWrapper.op = op 717 jst.stackWrapper.stack = scope.Stack 718 jst.memoryWrapper.memory = scope.Memory 719 jst.contractWrapper.contract = scope.Contract 720 721 *jst.pcValue = uint(pc) 722 *jst.gasValue = uint(gas) 723 *jst.costValue = uint(cost) 724 *jst.depthValue = uint(depth) 725 *jst.refundValue = uint(env.StateDB.GetRefund()) 726 727 jst.errorValue = nil 728 if err != nil { 729 jst.errorValue = new(string) 730 *jst.errorValue = err.Error() 731 } 732 733 if _, err := jst.call(true, "step", "log", "db"); err != nil { 734 jst.err = wrapError("step", err) 735 } 736 } 737 738 // CaptureFault implements the Tracer interface to trace an execution fault 739 func (jst *Tracer) CaptureFault(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, depth int, err error) { 740 if jst.err != nil { 741 return 742 } 743 // Apart from the error, everything matches the previous invocation 744 jst.errorValue = new(string) 745 *jst.errorValue = err.Error() 746 747 if _, err := jst.call(true, "fault", "log", "db"); err != nil { 748 jst.err = wrapError("fault", err) 749 } 750 } 751 752 // CaptureEnd is called after the call finishes to finalize the tracing. 753 func (jst *Tracer) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) { 754 jst.ctx["output"] = output 755 jst.ctx["time"] = t.String() 756 jst.ctx["gasUsed"] = gasUsed 757 758 if err != nil { 759 jst.ctx["error"] = err.Error() 760 } 761 } 762 763 // CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct). 764 func (jst *Tracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { 765 if !jst.traceCallFrames { 766 return 767 } 768 if jst.err != nil { 769 return 770 } 771 // If tracing was interrupted, set the error and stop 772 if atomic.LoadUint32(&jst.interrupt) > 0 { 773 jst.err = jst.reason 774 return 775 } 776 777 *jst.frame.typ = typ.String() 778 *jst.frame.from = from 779 *jst.frame.to = to 780 jst.frame.input = common.CopyBytes(input) 781 *jst.frame.gas = uint(gas) 782 jst.frame.value = nil 783 if value != nil { 784 jst.frame.value = new(big.Int).SetBytes(value.Bytes()) 785 } 786 787 if _, err := jst.call(true, "enter", "frame"); err != nil { 788 jst.err = wrapError("enter", err) 789 } 790 } 791 792 // CaptureExit is called when EVM exits a scope, even if the scope didn't 793 // execute any code. 794 func (jst *Tracer) CaptureExit(output []byte, gasUsed uint64, err error) { 795 if !jst.traceCallFrames { 796 return 797 } 798 // If tracing was interrupted, set the error and stop 799 if atomic.LoadUint32(&jst.interrupt) > 0 { 800 jst.err = jst.reason 801 return 802 } 803 804 jst.frameResult.output = common.CopyBytes(output) 805 *jst.frameResult.gasUsed = uint(gasUsed) 806 jst.frameResult.errorValue = nil 807 if err != nil { 808 jst.frameResult.errorValue = new(string) 809 *jst.frameResult.errorValue = err.Error() 810 } 811 812 if _, err := jst.call(true, "exit", "frameResult"); err != nil { 813 jst.err = wrapError("exit", err) 814 } 815 } 816 817 // GetResult calls the Javascript 'result' function and returns its value, or any accumulated error 818 func (jst *Tracer) GetResult() (json.RawMessage, error) { 819 // Transform the context into a JavaScript object and inject into the state 820 obj := jst.vm.PushObject() 821 822 for key, val := range jst.ctx { 823 jst.addToObj(obj, key, val) 824 } 825 jst.vm.PutPropString(jst.stateObject, "ctx") 826 827 // Finalize the trace and return the results 828 result, err := jst.call(false, "result", "ctx", "db") 829 if err != nil { 830 jst.err = wrapError("result", err) 831 } 832 // Clean up the JavaScript environment 833 jst.vm.DestroyHeap() 834 jst.vm.Destroy() 835 836 return result, jst.err 837 } 838 839 // addToObj pushes a field to a JS object. 840 func (jst *Tracer) addToObj(obj int, key string, val interface{}) { 841 pushValue(jst.vm, val) 842 jst.vm.PutPropString(obj, key) 843 } 844 845 func pushValue(ctx *duktape.Context, val interface{}) { 846 switch val := val.(type) { 847 case uint64: 848 ctx.PushUint(uint(val)) 849 case string: 850 ctx.PushString(val) 851 case []byte: 852 ptr := ctx.PushFixedBuffer(len(val)) 853 copy(makeSlice(ptr, uint(len(val))), val) 854 case common.Address: 855 ptr := ctx.PushFixedBuffer(20) 856 copy(makeSlice(ptr, 20), val[:]) 857 case *big.Int: 858 pushBigInt(val, ctx) 859 case int: 860 ctx.PushInt(val) 861 case uint: 862 ctx.PushUint(val) 863 case common.Hash: 864 ptr := ctx.PushFixedBuffer(32) 865 copy(makeSlice(ptr, 32), val[:]) 866 default: 867 panic(fmt.Sprintf("unsupported type: %T", val)) 868 } 869 }