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