gitee.com/liu-zhao234568/cntest@v1.0.0/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 "gitee.com/liu-zhao234568/cntest/common" 27 "gitee.com/liu-zhao234568/cntest/core/state" 28 "gitee.com/liu-zhao234568/cntest/core/vm" 29 "gitee.com/liu-zhao234568/cntest/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) SetCode(common.Hash, []byte) {} 43 func (account) ForEachStorage(cb func(key, value common.Hash) bool) {} 44 45 type dummyStatedb struct { 46 state.StateDB 47 } 48 49 func (*dummyStatedb) GetRefund() uint64 { return 1337 } 50 func (*dummyStatedb) GetBalance(addr common.Address) *big.Int { return new(big.Int) } 51 52 type vmContext struct { 53 blockCtx vm.BlockContext 54 txCtx vm.TxContext 55 } 56 57 func testCtx() *vmContext { 58 return &vmContext{blockCtx: vm.BlockContext{BlockNumber: big.NewInt(1)}, txCtx: vm.TxContext{GasPrice: big.NewInt(100000)}} 59 } 60 61 func runTrace(tracer *Tracer, vmctx *vmContext, chaincfg *params.ChainConfig) (json.RawMessage, error) { 62 env := vm.NewEVM(vmctx.blockCtx, vmctx.txCtx, &dummyStatedb{}, chaincfg, vm.Config{Debug: true, Tracer: tracer}) 63 var ( 64 startGas uint64 = 10000 65 value = big.NewInt(0) 66 ) 67 contract := vm.NewContract(account{}, account{}, value, startGas) 68 contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x1, 0x0} 69 70 tracer.CaptureStart(env, contract.Caller(), contract.Address(), false, []byte{}, startGas, value) 71 ret, err := env.Interpreter().Run(contract, []byte{}, false) 72 tracer.CaptureEnd(ret, startGas-contract.Gas, 1, err) 73 if err != nil { 74 return nil, err 75 } 76 return tracer.GetResult() 77 } 78 79 func TestTracer(t *testing.T) { 80 execTracer := func(code string) ([]byte, string) { 81 t.Helper() 82 tracer, err := New(code, new(Context)) 83 if err != nil { 84 t.Fatal(err) 85 } 86 ret, err := runTrace(tracer, &vmContext{ 87 blockCtx: vm.BlockContext{BlockNumber: big.NewInt(1)}, 88 txCtx: vm.TxContext{GasPrice: big.NewInt(100000)}, 89 }, params.TestChainConfig) 90 if err != nil { 91 return nil, err.Error() // Stringify to allow comparison without nil checks 92 } 93 return ret, "" 94 } 95 for i, tt := range []struct { 96 code string 97 want string 98 fail string 99 }{ 100 { // tests that we don't panic on bad arguments to memory access 101 code: "{depths: [], step: function(log) { this.depths.push(log.memory.slice(-1,-2)); }, fault: function() {}, result: function() { return this.depths; }}", 102 want: `[{},{},{}]`, 103 }, { // tests that we don't panic on bad arguments to stack peeks 104 code: "{depths: [], step: function(log) { this.depths.push(log.stack.peek(-1)); }, fault: function() {}, result: function() { return this.depths; }}", 105 want: `["0","0","0"]`, 106 }, { // tests that we don't panic on bad arguments to memory getUint 107 code: "{ depths: [], step: function(log, db) { this.depths.push(log.memory.getUint(-64));}, fault: function() {}, result: function() { return this.depths; }}", 108 want: `["0","0","0"]`, 109 }, { // tests some general counting 110 code: "{count: 0, step: function() { this.count += 1; }, fault: function() {}, result: function() { return this.count; }}", 111 want: `3`, 112 }, { // tests that depth is reported correctly 113 code: "{depths: [], step: function(log) { this.depths.push(log.stack.length()); }, fault: function() {}, result: function() { return this.depths; }}", 114 want: `[0,1,2]`, 115 }, { // tests to-string of opcodes 116 code: "{opcodes: [], step: function(log) { this.opcodes.push(log.op.toString()); }, fault: function() {}, result: function() { return this.opcodes; }}", 117 want: `["PUSH1","PUSH1","STOP"]`, 118 }, { // tests intrinsic gas 119 code: "{depths: [], step: function() {}, fault: function() {}, result: function(ctx) { return ctx.gasPrice+'.'+ctx.gasUsed+'.'+ctx.intrinsicGas; }}", 120 want: `"100000.6.21000"`, 121 }, { // tests too deep object / serialization crash 122 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; }}", 123 fail: "RangeError: json encode recursion limit in server-side tracer function 'result'", 124 }, 125 } { 126 if have, err := execTracer(tt.code); tt.want != string(have) || tt.fail != err { 127 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) 128 } 129 } 130 } 131 132 func TestHalt(t *testing.T) { 133 t.Skip("duktape doesn't support abortion") 134 135 timeout := errors.New("stahp") 136 tracer, err := New("{step: function() { while(1); }, result: function() { return null; }}", new(Context)) 137 if err != nil { 138 t.Fatal(err) 139 } 140 go func() { 141 time.Sleep(1 * time.Second) 142 tracer.Stop(timeout) 143 }() 144 if _, err = runTrace(tracer, testCtx(), params.TestChainConfig); err.Error() != "stahp in server-side tracer function 'step'" { 145 t.Errorf("Expected timeout error, got %v", err) 146 } 147 } 148 149 func TestHaltBetweenSteps(t *testing.T) { 150 tracer, err := New("{step: function() {}, fault: function() {}, result: function() { return null; }}", new(Context)) 151 if err != nil { 152 t.Fatal(err) 153 } 154 env := vm.NewEVM(vm.BlockContext{BlockNumber: big.NewInt(1)}, vm.TxContext{}, &dummyStatedb{}, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer}) 155 scope := &vm.ScopeContext{ 156 Contract: vm.NewContract(&account{}, &account{}, big.NewInt(0), 0), 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 tracer, err := New(code, new(Context)) 182 if err != nil { 183 t.Fatal(err) 184 } 185 ret, err := runEmptyTrace(tracer, &vmContext{ 186 blockCtx: vm.BlockContext{BlockNumber: big.NewInt(1)}, 187 txCtx: vm.TxContext{GasPrice: big.NewInt(100000)}, 188 }) 189 if err != nil { 190 t.Fatal(err) 191 } 192 return ret 193 } 194 for i, tt := range []struct { 195 code string 196 want string 197 }{ 198 { // tests that we don't panic on accessing the db methods 199 code: "{depths: [], step: function() {}, fault: function() {}, result: function(ctx, db){ return db.getBalance(ctx.to)} }", 200 want: `"0"`, 201 }, 202 } { 203 if have := execTracer(tt.code); tt.want != string(have) { 204 t.Errorf("testcase %d: expected return value to be %s got %s\n\tcode: %v", i, tt.want, string(have), tt.code) 205 } 206 } 207 } 208 209 func TestIsPrecompile(t *testing.T) { 210 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), BerlinBlock: big.NewInt(300), LondonBlock: big.NewInt(0), CatalystBlock: nil, Ethash: new(params.EthashConfig), Clique: nil} 211 chaincfg.ByzantiumBlock = big.NewInt(100) 212 chaincfg.IstanbulBlock = big.NewInt(200) 213 chaincfg.BerlinBlock = big.NewInt(300) 214 txCtx := vm.TxContext{GasPrice: big.NewInt(100000)} 215 tracer, err := New("{addr: toAddress('0000000000000000000000000000000000000009'), res: null, step: function() { this.res = isPrecompiled(this.addr); }, fault: function() {}, result: function() { return this.res; }}", new(Context)) 216 if err != nil { 217 t.Fatal(err) 218 } 219 220 blockCtx := vm.BlockContext{BlockNumber: big.NewInt(150)} 221 res, err := runTrace(tracer, &vmContext{blockCtx, txCtx}, chaincfg) 222 if err != nil { 223 t.Error(err) 224 } 225 if string(res) != "false" { 226 t.Errorf("Tracer should not consider blake2f as precompile in byzantium") 227 } 228 229 tracer, _ = New("{addr: toAddress('0000000000000000000000000000000000000009'), res: null, step: function() { this.res = isPrecompiled(this.addr); }, fault: function() {}, result: function() { return this.res; }}", new(Context)) 230 blockCtx = vm.BlockContext{BlockNumber: big.NewInt(250)} 231 res, err = runTrace(tracer, &vmContext{blockCtx, txCtx}, chaincfg) 232 if err != nil { 233 t.Error(err) 234 } 235 if string(res) != "true" { 236 t.Errorf("Tracer should consider blake2f as precompile in istanbul") 237 } 238 }