github.com/ImPedro29/bor@v0.2.7/eth/tracers/tracer_test.go (about)

     1  // Copyright 2017 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package tracers
    18  
    19  import (
    20  	"encoding/json"
    21  	"errors"
    22  	"math/big"
    23  	"testing"
    24  	"time"
    25  
    26  	"github.com/ethereum/go-ethereum/common"
    27  	"github.com/ethereum/go-ethereum/core/state"
    28  	"github.com/ethereum/go-ethereum/core/vm"
    29  	"github.com/ethereum/go-ethereum/params"
    30  )
    31  
    32  type account struct{}
    33  
    34  func (account) SubBalance(amount *big.Int)                          {}
    35  func (account) AddBalance(amount *big.Int)                          {}
    36  func (account) SetAddress(common.Address)                           {}
    37  func (account) Value() *big.Int                                     { return nil }
    38  func (account) SetBalance(*big.Int)                                 {}
    39  func (account) SetNonce(uint64)                                     {}
    40  func (account) Balance() *big.Int                                   { return nil }
    41  func (account) Address() common.Address                             { return common.Address{} }
    42  func (account) ReturnGas(*big.Int)                                  {}
    43  func (account) SetCode(common.Hash, []byte)                         {}
    44  func (account) ForEachStorage(cb func(key, value common.Hash) bool) {}
    45  
    46  type dummyStatedb struct {
    47  	state.StateDB
    48  }
    49  
    50  func (*dummyStatedb) GetRefund() uint64                       { return 1337 }
    51  func (*dummyStatedb) GetBalance(addr common.Address) *big.Int { return new(big.Int) }
    52  
    53  type vmContext struct {
    54  	blockCtx vm.BlockContext
    55  	txCtx    vm.TxContext
    56  }
    57  
    58  func testCtx() *vmContext {
    59  	return &vmContext{blockCtx: vm.BlockContext{BlockNumber: big.NewInt(1)}, txCtx: vm.TxContext{GasPrice: big.NewInt(100000)}}
    60  }
    61  
    62  func runTrace(tracer *Tracer, vmctx *vmContext) (json.RawMessage, error) {
    63  	env := vm.NewEVM(vmctx.blockCtx, vmctx.txCtx, &dummyStatedb{}, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer})
    64  	var (
    65  		startGas uint64 = 10000
    66  		value           = big.NewInt(0)
    67  	)
    68  	contract := vm.NewContract(account{}, account{}, value, startGas)
    69  	contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x1, 0x0}
    70  
    71  	tracer.CaptureStart(env, contract.Caller(), contract.Address(), false, []byte{}, startGas, value)
    72  	ret, err := env.Interpreter().Run(contract, []byte{}, false)
    73  	tracer.CaptureEnd(ret, startGas-contract.Gas, 1, err)
    74  	if err != nil {
    75  		return nil, err
    76  	}
    77  	return tracer.GetResult()
    78  }
    79  
    80  func TestTracer(t *testing.T) {
    81  	execTracer := func(code string) []byte {
    82  		t.Helper()
    83  		ctx := &vmContext{blockCtx: vm.BlockContext{BlockNumber: big.NewInt(1)}, txCtx: vm.TxContext{GasPrice: big.NewInt(100000)}}
    84  		tracer, err := New(code, ctx.txCtx)
    85  		if err != nil {
    86  			t.Fatal(err)
    87  		}
    88  		ret, err := runTrace(tracer, ctx)
    89  		if err != nil {
    90  			t.Fatal(err)
    91  		}
    92  		return ret
    93  	}
    94  	for i, tt := range []struct {
    95  		code string
    96  		want string
    97  	}{
    98  		{ // tests that we don't panic on bad arguments to memory access
    99  			code: "{depths: [], step: function(log) { this.depths.push(log.memory.slice(-1,-2)); }, fault: function() {}, result: function() { return this.depths; }}",
   100  			want: `[{},{},{}]`,
   101  		}, { // tests that we don't panic on bad arguments to stack peeks
   102  			code: "{depths: [], step: function(log) { this.depths.push(log.stack.peek(-1)); }, fault: function() {}, result: function() { return this.depths; }}",
   103  			want: `["0","0","0"]`,
   104  		}, { //  tests that we don't panic on bad arguments to memory getUint
   105  			code: "{ depths: [], step: function(log, db) { this.depths.push(log.memory.getUint(-64));}, fault: function() {}, result: function() { return this.depths; }}",
   106  			want: `["0","0","0"]`,
   107  		}, { // tests some general counting
   108  			code: "{count: 0, step: function() { this.count += 1; }, fault: function() {}, result: function() { return this.count; }}",
   109  			want: `3`,
   110  		}, { // tests that depth is reported correctly
   111  			code: "{depths: [], step: function(log) { this.depths.push(log.stack.length()); }, fault: function() {}, result: function() { return this.depths; }}",
   112  			want: `[0,1,2]`,
   113  		}, { // tests to-string of opcodes
   114  			code: "{opcodes: [], step: function(log) { this.opcodes.push(log.op.toString()); }, fault: function() {}, result: function() { return this.opcodes; }}",
   115  			want: `["PUSH1","PUSH1","STOP"]`,
   116  		}, { // tests intrinsic gas
   117  			code: "{depths: [], step: function() {}, fault: function() {}, result: function(ctx) { return ctx.gasPrice+'.'+ctx.gasUsed+'.'+ctx.intrinsicGas; }}",
   118  			want: `"100000.6.21000"`,
   119  		},
   120  	} {
   121  		if have := execTracer(tt.code); tt.want != string(have) {
   122  			t.Errorf("testcase %d: expected return value to be %s got %s\n\tcode: %v", i, tt.want, string(have), tt.code)
   123  		}
   124  	}
   125  }
   126  
   127  func TestHalt(t *testing.T) {
   128  	t.Skip("duktape doesn't support abortion")
   129  
   130  	timeout := errors.New("stahp")
   131  	vmctx := testCtx()
   132  	tracer, err := New("{step: function() { while(1); }, result: function() { return null; }}", vmctx.txCtx)
   133  	if err != nil {
   134  		t.Fatal(err)
   135  	}
   136  
   137  	go func() {
   138  		time.Sleep(1 * time.Second)
   139  		tracer.Stop(timeout)
   140  	}()
   141  
   142  	if _, err = runTrace(tracer, vmctx); err.Error() != "stahp    in server-side tracer function 'step'" {
   143  		t.Errorf("Expected timeout error, got %v", err)
   144  	}
   145  }
   146  
   147  func TestHaltBetweenSteps(t *testing.T) {
   148  	vmctx := testCtx()
   149  	tracer, err := New("{step: function() {}, fault: function() {}, result: function() { return null; }}", vmctx.txCtx)
   150  	if err != nil {
   151  		t.Fatal(err)
   152  	}
   153  	env := vm.NewEVM(vm.BlockContext{BlockNumber: big.NewInt(1)}, vm.TxContext{}, &dummyStatedb{}, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer})
   154  	scope := &vm.ScopeContext{
   155  		Contract: vm.NewContract(&account{}, &account{}, big.NewInt(0), 0),
   156  	}
   157  
   158  	tracer.CaptureState(env, 0, 0, 0, 0, scope, nil, 0, nil)
   159  	timeout := errors.New("stahp")
   160  	tracer.Stop(timeout)
   161  	tracer.CaptureState(env, 0, 0, 0, 0, scope, nil, 0, nil)
   162  
   163  	if _, err := tracer.GetResult(); err.Error() != timeout.Error() {
   164  		t.Errorf("Expected timeout error, got %v", err)
   165  	}
   166  }
   167  
   168  // TestNoStepExec tests a regular value transfer (no exec), and accessing the statedb
   169  // in 'result'
   170  func TestNoStepExec(t *testing.T) {
   171  	runEmptyTrace := func(tracer *Tracer, vmctx *vmContext) (json.RawMessage, error) {
   172  		env := vm.NewEVM(vmctx.blockCtx, vmctx.txCtx, &dummyStatedb{}, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer})
   173  		startGas := uint64(10000)
   174  		contract := vm.NewContract(account{}, account{}, big.NewInt(0), startGas)
   175  		tracer.CaptureStart(env, contract.Caller(), contract.Address(), false, []byte{}, startGas, big.NewInt(0))
   176  		tracer.CaptureEnd(nil, startGas-contract.Gas, 1, nil)
   177  		return tracer.GetResult()
   178  	}
   179  	execTracer := func(code string) []byte {
   180  		t.Helper()
   181  		ctx := &vmContext{blockCtx: vm.BlockContext{BlockNumber: big.NewInt(1)}, txCtx: vm.TxContext{GasPrice: big.NewInt(100000)}}
   182  		tracer, err := New(code, ctx.txCtx)
   183  		if err != nil {
   184  			t.Fatal(err)
   185  		}
   186  		ret, err := runEmptyTrace(tracer, ctx)
   187  		if err != nil {
   188  			t.Fatal(err)
   189  		}
   190  		return ret
   191  	}
   192  	for i, tt := range []struct {
   193  		code string
   194  		want string
   195  	}{
   196  		{ // tests that we don't panic on accessing the db methods
   197  			code: "{depths: [], step: function() {}, fault: function() {},  result: function(ctx, db){ return db.getBalance(ctx.to)} }",
   198  			want: `"0"`,
   199  		},
   200  	} {
   201  		if have := execTracer(tt.code); tt.want != string(have) {
   202  			t.Errorf("testcase %d: expected return value to be %s got %s\n\tcode: %v", i, tt.want, string(have), tt.code)
   203  		}
   204  	}
   205  }