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  }