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