gitlab.com/flarenetwork/coreth@v0.1.1/eth/tracers/tracer_test.go (about) 1 // (c) 2019-2020, Ava Labs, Inc. 2 // 3 // This file is a derived work, based on the go-ethereum library whose original 4 // notices appear below. 5 // 6 // It is distributed under a license compatible with the licensing terms of the 7 // original code from which it is derived. 8 // 9 // Much love to the original authors for their work. 10 // ********** 11 // Copyright 2017 The go-ethereum Authors 12 // This file is part of the go-ethereum library. 13 // 14 // The go-ethereum library is free software: you can redistribute it and/or modify 15 // it under the terms of the GNU Lesser General Public License as published by 16 // the Free Software Foundation, either version 3 of the License, or 17 // (at your option) any later version. 18 // 19 // The go-ethereum library is distributed in the hope that it will be useful, 20 // but WITHOUT ANY WARRANTY; without even the implied warranty of 21 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 // GNU Lesser General Public License for more details. 23 // 24 // You should have received a copy of the GNU Lesser General Public License 25 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 26 27 package tracers 28 29 import ( 30 "encoding/json" 31 "errors" 32 "math/big" 33 "testing" 34 "time" 35 36 "github.com/ethereum/go-ethereum/common" 37 "gitlab.com/flarenetwork/coreth/core/state" 38 "gitlab.com/flarenetwork/coreth/core/vm" 39 "gitlab.com/flarenetwork/coreth/params" 40 ) 41 42 type account struct{} 43 44 func (account) SubBalance(amount *big.Int) {} 45 func (account) AddBalance(amount *big.Int) {} 46 func (account) SetAddress(common.Address) {} 47 func (account) Value() *big.Int { return nil } 48 func (account) SetBalance(*big.Int) {} 49 func (account) SetNonce(uint64) {} 50 func (account) Balance() *big.Int { return nil } 51 func (account) Address() common.Address { return common.Address{} } 52 func (account) SetCode(common.Hash, []byte) {} 53 func (account) ForEachStorage(cb func(key, value common.Hash) bool) {} 54 55 type dummyStatedb struct { 56 state.StateDB 57 } 58 59 func (*dummyStatedb) GetRefund() uint64 { return 1337 } 60 func (*dummyStatedb) GetBalance(addr common.Address) *big.Int { return new(big.Int) } 61 62 type vmContext struct { 63 blockCtx vm.BlockContext 64 txCtx vm.TxContext 65 } 66 67 func testCtx() *vmContext { 68 return &vmContext{blockCtx: vm.BlockContext{BlockNumber: big.NewInt(1)}, txCtx: vm.TxContext{GasPrice: big.NewInt(100000)}} 69 } 70 71 func runTrace(tracer *Tracer, vmctx *vmContext, chaincfg *params.ChainConfig) (json.RawMessage, error) { 72 env := vm.NewEVM(vmctx.blockCtx, vmctx.txCtx, &dummyStatedb{}, chaincfg, vm.Config{Debug: true, Tracer: tracer}) 73 var ( 74 startGas uint64 = 10000 75 value = big.NewInt(0) 76 ) 77 contract := vm.NewContract(account{}, account{}, value, startGas) 78 contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x1, 0x0} 79 80 tracer.CaptureStart(env, contract.Caller(), contract.Address(), false, []byte{}, startGas, value) 81 ret, err := env.Interpreter().Run(contract, []byte{}, false) 82 tracer.CaptureEnd(ret, startGas-contract.Gas, 1, err) 83 if err != nil { 84 return nil, err 85 } 86 return tracer.GetResult() 87 } 88 89 func TestTracer(t *testing.T) { 90 execTracer := func(code string) ([]byte, string) { 91 t.Helper() 92 tracer, err := New(code, new(Context)) 93 if err != nil { 94 t.Fatal(err) 95 } 96 ret, err := runTrace(tracer, &vmContext{ 97 blockCtx: vm.BlockContext{BlockNumber: big.NewInt(1)}, 98 txCtx: vm.TxContext{GasPrice: big.NewInt(100000)}, 99 }, params.TestChainConfig) 100 if err != nil { 101 return nil, err.Error() // Stringify to allow comparison without nil checks 102 } 103 return ret, "" 104 } 105 for i, tt := range []struct { 106 code string 107 want string 108 fail string 109 }{ 110 { // tests that we don't panic on bad arguments to memory access 111 code: "{depths: [], step: function(log) { this.depths.push(log.memory.slice(-1,-2)); }, fault: function() {}, result: function() { return this.depths; }}", 112 want: `[{},{},{}]`, 113 }, { // tests that we don't panic on bad arguments to stack peeks 114 code: "{depths: [], step: function(log) { this.depths.push(log.stack.peek(-1)); }, fault: function() {}, result: function() { return this.depths; }}", 115 want: `["0","0","0"]`, 116 }, { // tests that we don't panic on bad arguments to memory getUint 117 code: "{ depths: [], step: function(log, db) { this.depths.push(log.memory.getUint(-64));}, fault: function() {}, result: function() { return this.depths; }}", 118 want: `["0","0","0"]`, 119 }, { // tests some general counting 120 code: "{count: 0, step: function() { this.count += 1; }, fault: function() {}, result: function() { return this.count; }}", 121 want: `3`, 122 }, { // tests that depth is reported correctly 123 code: "{depths: [], step: function(log) { this.depths.push(log.stack.length()); }, fault: function() {}, result: function() { return this.depths; }}", 124 want: `[0,1,2]`, 125 }, { // tests to-string of opcodes 126 code: "{opcodes: [], step: function(log) { this.opcodes.push(log.op.toString()); }, fault: function() {}, result: function() { return this.opcodes; }}", 127 want: `["PUSH1","PUSH1","STOP"]`, 128 }, { // tests intrinsic gas 129 code: "{depths: [], step: function() {}, fault: function() {}, result: function(ctx) { return ctx.gasPrice+'.'+ctx.gasUsed+'.'+ctx.intrinsicGas; }}", 130 want: `"100000.6.21000"`, 131 }, { // tests too deep object / serialization crash 132 code: "{step: function() {}, fault: function() {}, result: function() { var o={}; var x=o; for (var i=0; i<1000; i++){ o.foo={}; o=o.foo; } return x; }}", 133 fail: "RangeError: json encode recursion limit in server-side tracer function 'result'", 134 }, 135 } { 136 if have, err := execTracer(tt.code); tt.want != string(have) || tt.fail != err { 137 t.Errorf("testcase %d: expected return value to be '%s' got '%s', error to be '%s' got '%s'\n\tcode: %v", i, tt.want, string(have), tt.fail, err, tt.code) 138 } 139 } 140 } 141 142 func TestHalt(t *testing.T) { 143 t.Skip("duktape doesn't support abortion") 144 145 timeout := errors.New("stahp") 146 tracer, err := New("{step: function() { while(1); }, result: function() { return null; }}", new(Context)) 147 if err != nil { 148 t.Fatal(err) 149 } 150 go func() { 151 time.Sleep(1 * time.Second) 152 tracer.Stop(timeout) 153 }() 154 if _, err = runTrace(tracer, testCtx(), params.TestChainConfig); err.Error() != "stahp in server-side tracer function 'step'" { 155 t.Errorf("Expected timeout error, got %v", err) 156 } 157 } 158 159 func TestHaltBetweenSteps(t *testing.T) { 160 tracer, err := New("{step: function() {}, fault: function() {}, result: function() { return null; }}", new(Context)) 161 if err != nil { 162 t.Fatal(err) 163 } 164 env := vm.NewEVM(vm.BlockContext{BlockNumber: big.NewInt(1)}, vm.TxContext{}, &dummyStatedb{}, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer}) 165 scope := &vm.ScopeContext{ 166 Contract: vm.NewContract(&account{}, &account{}, big.NewInt(0), 0), 167 } 168 tracer.CaptureState(env, 0, 0, 0, 0, scope, nil, 0, nil) 169 timeout := errors.New("stahp") 170 tracer.Stop(timeout) 171 tracer.CaptureState(env, 0, 0, 0, 0, scope, nil, 0, nil) 172 173 if _, err := tracer.GetResult(); err.Error() != timeout.Error() { 174 t.Errorf("Expected timeout error, got %v", err) 175 } 176 } 177 178 // TestNoStepExec tests a regular value transfer (no exec), and accessing the statedb 179 // in 'result' 180 func TestNoStepExec(t *testing.T) { 181 runEmptyTrace := func(tracer *Tracer, vmctx *vmContext) (json.RawMessage, error) { 182 env := vm.NewEVM(vmctx.blockCtx, vmctx.txCtx, &dummyStatedb{}, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer}) 183 startGas := uint64(10000) 184 contract := vm.NewContract(account{}, account{}, big.NewInt(0), startGas) 185 tracer.CaptureStart(env, contract.Caller(), contract.Address(), false, []byte{}, startGas, big.NewInt(0)) 186 tracer.CaptureEnd(nil, startGas-contract.Gas, 1, nil) 187 return tracer.GetResult() 188 } 189 execTracer := func(code string) []byte { 190 t.Helper() 191 tracer, err := New(code, new(Context)) 192 if err != nil { 193 t.Fatal(err) 194 } 195 ret, err := runEmptyTrace(tracer, &vmContext{ 196 blockCtx: vm.BlockContext{BlockNumber: big.NewInt(1)}, 197 txCtx: vm.TxContext{GasPrice: big.NewInt(100000)}, 198 }) 199 if err != nil { 200 t.Fatal(err) 201 } 202 return ret 203 } 204 for i, tt := range []struct { 205 code string 206 want string 207 }{ 208 { // tests that we don't panic on accessing the db methods 209 code: "{depths: [], step: function() {}, fault: function() {}, result: function(ctx, db){ return db.getBalance(ctx.to)} }", 210 want: `"0"`, 211 }, 212 } { 213 if have := execTracer(tt.code); tt.want != string(have) { 214 t.Errorf("testcase %d: expected return value to be %s got %s\n\tcode: %v", i, tt.want, string(have), tt.code) 215 } 216 } 217 } 218 219 func TestIsPrecompile(t *testing.T) { 220 chaincfg := ¶ms.ChainConfig{ChainID: big.NewInt(1), HomesteadBlock: big.NewInt(0), DAOForkBlock: nil, DAOForkSupport: false, EIP150Block: big.NewInt(0), EIP150Hash: common.Hash{}, EIP155Block: big.NewInt(0), EIP158Block: big.NewInt(0), ByzantiumBlock: big.NewInt(100), ConstantinopleBlock: big.NewInt(0), PetersburgBlock: big.NewInt(0), IstanbulBlock: big.NewInt(200), MuirGlacierBlock: big.NewInt(0), ApricotPhase2BlockTimestamp: big.NewInt(300), ApricotPhase3BlockTimestamp: big.NewInt(0)} 221 chaincfg.ByzantiumBlock = big.NewInt(100) 222 chaincfg.IstanbulBlock = big.NewInt(200) 223 chaincfg.ApricotPhase3BlockTimestamp = big.NewInt(300) 224 txCtx := vm.TxContext{GasPrice: big.NewInt(100000)} 225 tracer, err := New("{addr: toAddress('0000000000000000000000000000000000000009'), res: null, step: function() { this.res = isPrecompiled(this.addr); }, fault: function() {}, result: function() { return this.res; }}", new(Context)) 226 if err != nil { 227 t.Fatal(err) 228 } 229 230 blockCtx := vm.BlockContext{BlockNumber: big.NewInt(150), Time: big.NewInt(150)} 231 res, err := runTrace(tracer, &vmContext{blockCtx, txCtx}, chaincfg) 232 if err != nil { 233 t.Error(err) 234 } 235 if string(res) != "false" { 236 t.Errorf("Tracer should not consider blake2f as precompile in byzantium") 237 } 238 239 tracer, _ = New("{addr: toAddress('0000000000000000000000000000000000000009'), res: null, step: function() { this.res = isPrecompiled(this.addr); }, fault: function() {}, result: function() { return this.res; }}", new(Context)) 240 blockCtx = vm.BlockContext{BlockNumber: big.NewInt(250), Time: big.NewInt(250)} 241 res, err = runTrace(tracer, &vmContext{blockCtx, txCtx}, chaincfg) 242 if err != nil { 243 t.Error(err) 244 } 245 if string(res) != "true" { 246 t.Errorf("Tracer should consider blake2f as precompile in istanbul") 247 } 248 }