forked from cerc-io/plugeth
Merge Geth v1.10.21 as well as update to Plugeth-Utils v0.0.18
This commit is contained in:
+12
-5
@@ -179,6 +179,7 @@ type TraceCallConfig struct {
|
||||
Timeout *string
|
||||
Reexec *uint64
|
||||
StateOverrides *ethapi.StateOverride
|
||||
BlockOverrides *ethapi.BlockOverrides
|
||||
}
|
||||
|
||||
// StdTraceConfig holds extra parameters to standard-json trace functions.
|
||||
@@ -806,7 +807,6 @@ func (api *API) TraceTransaction(ctx context.Context, hash common.Hash, config *
|
||||
// TraceCall lets you trace a given eth_call. It collects the structured logs
|
||||
// created during the execution of EVM if the given transaction was added on
|
||||
// top of the provided block and returns them as a JSON object.
|
||||
// You can provide -2 as a block number to trace on top of the pending block.
|
||||
func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, config *TraceCallConfig) (interface{}, error) {
|
||||
// Try to retrieve the specified block
|
||||
var (
|
||||
@@ -816,6 +816,14 @@ func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, bloc
|
||||
if hash, ok := blockNrOrHash.Hash(); ok {
|
||||
block, err = api.blockByHash(ctx, hash)
|
||||
} else if number, ok := blockNrOrHash.Number(); ok {
|
||||
if number == rpc.PendingBlockNumber {
|
||||
// We don't have access to the miner here. For tracing 'future' transactions,
|
||||
// it can be done with block- and state-overrides instead, which offers
|
||||
// more flexibility and stability than trying to trace on 'pending', since
|
||||
// the contents of 'pending' is unstable and probably not a true representation
|
||||
// of what the next actual block is likely to contain.
|
||||
return nil, errors.New("tracing on top of pending is not supported")
|
||||
}
|
||||
block, err = api.blockByNumber(ctx, number)
|
||||
} else {
|
||||
return nil, errors.New("invalid arguments; neither block nor hash specified")
|
||||
@@ -832,18 +840,19 @@ func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, bloc
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Apply the customized state rules if required.
|
||||
vmctx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil)
|
||||
// Apply the customization rules if required.
|
||||
if config != nil {
|
||||
if err := config.StateOverrides.Apply(statedb); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
config.BlockOverrides.Apply(&vmctx)
|
||||
}
|
||||
// Execute the trace
|
||||
msg, err := args.ToMessage(api.backend.RPCGasCap(), block.BaseFee())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vmctx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil)
|
||||
|
||||
var traceConfig *TraceConfig
|
||||
if config != nil {
|
||||
@@ -917,9 +926,7 @@ func APIs(backend Backend) []rpc.API {
|
||||
return []rpc.API{
|
||||
{
|
||||
Namespace: "debug",
|
||||
Version: "1.0",
|
||||
Service: NewAPI(backend),
|
||||
Public: false,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
+68
-39
@@ -196,13 +196,12 @@ func TestTraceCall(t *testing.T) {
|
||||
tx, _ := types.SignTx(types.NewTransaction(uint64(i), accounts[1].addr, big.NewInt(1000), params.TxGas, b.BaseFee(), nil), signer, accounts[0].key)
|
||||
b.AddTx(tx)
|
||||
}))
|
||||
|
||||
var testSuite = []struct {
|
||||
blockNumber rpc.BlockNumber
|
||||
call ethapi.TransactionArgs
|
||||
config *TraceCallConfig
|
||||
expectErr error
|
||||
expect interface{}
|
||||
expect string
|
||||
}{
|
||||
// Standard JSON trace upon the genesis, plain transfer.
|
||||
{
|
||||
@@ -214,12 +213,7 @@ func TestTraceCall(t *testing.T) {
|
||||
},
|
||||
config: nil,
|
||||
expectErr: nil,
|
||||
expect: &logger.ExecutionResult{
|
||||
Gas: params.TxGas,
|
||||
Failed: false,
|
||||
ReturnValue: "",
|
||||
StructLogs: []logger.StructLogRes{},
|
||||
},
|
||||
expect: `{"gas":21000,"failed":false,"returnValue":"","structLogs":[]}`,
|
||||
},
|
||||
// Standard JSON trace upon the head, plain transfer.
|
||||
{
|
||||
@@ -231,12 +225,7 @@ func TestTraceCall(t *testing.T) {
|
||||
},
|
||||
config: nil,
|
||||
expectErr: nil,
|
||||
expect: &logger.ExecutionResult{
|
||||
Gas: params.TxGas,
|
||||
Failed: false,
|
||||
ReturnValue: "",
|
||||
StructLogs: []logger.StructLogRes{},
|
||||
},
|
||||
expect: `{"gas":21000,"failed":false,"returnValue":"","structLogs":[]}`,
|
||||
},
|
||||
// Standard JSON trace upon the non-existent block, error expects
|
||||
{
|
||||
@@ -248,7 +237,7 @@ func TestTraceCall(t *testing.T) {
|
||||
},
|
||||
config: nil,
|
||||
expectErr: fmt.Errorf("block #%d not found", genBlocks+1),
|
||||
expect: nil,
|
||||
//expect: nil,
|
||||
},
|
||||
// Standard JSON trace upon the latest block
|
||||
{
|
||||
@@ -260,14 +249,9 @@ func TestTraceCall(t *testing.T) {
|
||||
},
|
||||
config: nil,
|
||||
expectErr: nil,
|
||||
expect: &logger.ExecutionResult{
|
||||
Gas: params.TxGas,
|
||||
Failed: false,
|
||||
ReturnValue: "",
|
||||
StructLogs: []logger.StructLogRes{},
|
||||
},
|
||||
expect: `{"gas":21000,"failed":false,"returnValue":"","structLogs":[]}`,
|
||||
},
|
||||
// Standard JSON trace upon the pending block
|
||||
// Tracing on 'pending' should fail:
|
||||
{
|
||||
blockNumber: rpc.PendingBlockNumber,
|
||||
call: ethapi.TransactionArgs{
|
||||
@@ -276,36 +260,48 @@ func TestTraceCall(t *testing.T) {
|
||||
Value: (*hexutil.Big)(big.NewInt(1000)),
|
||||
},
|
||||
config: nil,
|
||||
expectErr: nil,
|
||||
expect: &logger.ExecutionResult{
|
||||
Gas: params.TxGas,
|
||||
Failed: false,
|
||||
ReturnValue: "",
|
||||
StructLogs: []logger.StructLogRes{},
|
||||
expectErr: errors.New("tracing on top of pending is not supported"),
|
||||
},
|
||||
{
|
||||
blockNumber: rpc.LatestBlockNumber,
|
||||
call: ethapi.TransactionArgs{
|
||||
From: &accounts[0].addr,
|
||||
Input: &hexutil.Bytes{0x43}, // blocknumber
|
||||
},
|
||||
config: &TraceCallConfig{
|
||||
BlockOverrides: ðapi.BlockOverrides{Number: (*hexutil.Big)(big.NewInt(0x1337))},
|
||||
},
|
||||
expectErr: nil,
|
||||
expect: ` {"gas":53018,"failed":false,"returnValue":"","structLogs":[
|
||||
{"pc":0,"op":"NUMBER","gas":24946984,"gasCost":2,"depth":1,"stack":[]},
|
||||
{"pc":1,"op":"STOP","gas":24946982,"gasCost":0,"depth":1,"stack":["0x1337"]}]}`,
|
||||
},
|
||||
}
|
||||
for _, testspec := range testSuite {
|
||||
for i, testspec := range testSuite {
|
||||
result, err := api.TraceCall(context.Background(), testspec.call, rpc.BlockNumberOrHash{BlockNumber: &testspec.blockNumber}, testspec.config)
|
||||
if testspec.expectErr != nil {
|
||||
if err == nil {
|
||||
t.Errorf("Expect error %v, get nothing", testspec.expectErr)
|
||||
t.Errorf("test %d: expect error %v, got nothing", i, testspec.expectErr)
|
||||
continue
|
||||
}
|
||||
if !reflect.DeepEqual(err, testspec.expectErr) {
|
||||
t.Errorf("Error mismatch, want %v, get %v", testspec.expectErr, err)
|
||||
t.Errorf("test %d: error mismatch, want %v, git %v", i, testspec.expectErr, err)
|
||||
}
|
||||
} else {
|
||||
if err != nil {
|
||||
t.Errorf("Expect no error, get %v", err)
|
||||
t.Errorf("test %d: expect no error, got %v", i, err)
|
||||
continue
|
||||
}
|
||||
var have *logger.ExecutionResult
|
||||
if err := json.Unmarshal(result.(json.RawMessage), &have); err != nil {
|
||||
t.Errorf("failed to unmarshal result %v", err)
|
||||
t.Errorf("test %d: failed to unmarshal result %v", i, err)
|
||||
}
|
||||
if !reflect.DeepEqual(have, testspec.expect) {
|
||||
t.Errorf("Result mismatch, want %v, get %v", testspec.expect, have)
|
||||
var want *logger.ExecutionResult
|
||||
if err := json.Unmarshal([]byte(testspec.expect), &want); err != nil {
|
||||
t.Errorf("test %d: failed to unmarshal result %v", i, err)
|
||||
}
|
||||
if !reflect.DeepEqual(have, want) {
|
||||
t.Errorf("test %d: result mismatch, want %v, got %v", i, testspec.expect, string(result.(json.RawMessage)))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -446,7 +442,7 @@ func TestTracingWithOverrides(t *testing.T) {
|
||||
type res struct {
|
||||
Gas int
|
||||
Failed bool
|
||||
returnValue string
|
||||
ReturnValue string
|
||||
}
|
||||
var testSuite = []struct {
|
||||
blockNumber rpc.BlockNumber
|
||||
@@ -457,7 +453,7 @@ func TestTracingWithOverrides(t *testing.T) {
|
||||
}{
|
||||
// Call which can only succeed if state is state overridden
|
||||
{
|
||||
blockNumber: rpc.PendingBlockNumber,
|
||||
blockNumber: rpc.LatestBlockNumber,
|
||||
call: ethapi.TransactionArgs{
|
||||
From: &randomAccounts[0].addr,
|
||||
To: &randomAccounts[1].addr,
|
||||
@@ -472,7 +468,7 @@ func TestTracingWithOverrides(t *testing.T) {
|
||||
},
|
||||
// Invalid call without state overriding
|
||||
{
|
||||
blockNumber: rpc.PendingBlockNumber,
|
||||
blockNumber: rpc.LatestBlockNumber,
|
||||
call: ethapi.TransactionArgs{
|
||||
From: &randomAccounts[0].addr,
|
||||
To: &randomAccounts[1].addr,
|
||||
@@ -498,7 +494,7 @@ func TestTracingWithOverrides(t *testing.T) {
|
||||
// }
|
||||
// }
|
||||
{
|
||||
blockNumber: rpc.PendingBlockNumber,
|
||||
blockNumber: rpc.LatestBlockNumber,
|
||||
call: ethapi.TransactionArgs{
|
||||
From: &randomAccounts[0].addr,
|
||||
To: &randomAccounts[2].addr,
|
||||
@@ -515,6 +511,39 @@ func TestTracingWithOverrides(t *testing.T) {
|
||||
},
|
||||
want: `{"gas":23347,"failed":false,"returnValue":"000000000000000000000000000000000000000000000000000000000000007b"}`,
|
||||
},
|
||||
{ // Override blocknumber
|
||||
blockNumber: rpc.LatestBlockNumber,
|
||||
call: ethapi.TransactionArgs{
|
||||
From: &accounts[0].addr,
|
||||
// BLOCKNUMBER PUSH1 MSTORE
|
||||
Input: newRPCBytes(common.Hex2Bytes("4360005260206000f3")),
|
||||
//&hexutil.Bytes{0x43}, // blocknumber
|
||||
},
|
||||
config: &TraceCallConfig{
|
||||
BlockOverrides: ðapi.BlockOverrides{Number: (*hexutil.Big)(big.NewInt(0x1337))},
|
||||
},
|
||||
want: `{"gas":59537,"failed":false,"returnValue":"0000000000000000000000000000000000000000000000000000000000001337"}`,
|
||||
},
|
||||
{ // Override blocknumber, and query a blockhash
|
||||
blockNumber: rpc.LatestBlockNumber,
|
||||
call: ethapi.TransactionArgs{
|
||||
From: &accounts[0].addr,
|
||||
Input: &hexutil.Bytes{
|
||||
0x60, 0x00, 0x40, // BLOCKHASH(0)
|
||||
0x60, 0x00, 0x52, // STORE memory offset 0
|
||||
0x61, 0x13, 0x36, 0x40, // BLOCKHASH(0x1336)
|
||||
0x60, 0x20, 0x52, // STORE memory offset 32
|
||||
0x61, 0x13, 0x37, 0x40, // BLOCKHASH(0x1337)
|
||||
0x60, 0x40, 0x52, // STORE memory offset 64
|
||||
0x60, 0x60, 0x60, 0x00, 0xf3, // RETURN (0-96)
|
||||
|
||||
}, // blocknumber
|
||||
},
|
||||
config: &TraceCallConfig{
|
||||
BlockOverrides: ðapi.BlockOverrides{Number: (*hexutil.Big)(big.NewInt(0x1337))},
|
||||
},
|
||||
want: `{"gas":72666,"failed":false,"returnValue":"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"}`,
|
||||
},
|
||||
}
|
||||
for i, tc := range testSuite {
|
||||
result, err := api.TraceCall(context.Background(), tc.call, rpc.BlockNumberOrHash{BlockNumber: &tc.blockNumber}, tc.config)
|
||||
|
||||
+8
-15
@@ -55,11 +55,7 @@ type fromBufFn = func(vm *goja.Runtime, buf goja.Value, allowString bool) ([]byt
|
||||
|
||||
func toBuf(vm *goja.Runtime, bufType goja.Value, val []byte) (goja.Value, error) {
|
||||
// bufType is usually Uint8Array. This is equivalent to `new Uint8Array(val)` in JS.
|
||||
res, err := vm.New(bufType, vm.ToValue(val))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return vm.ToValue(res), nil
|
||||
return vm.New(bufType, vm.ToValue(vm.NewArrayBuffer(val)))
|
||||
}
|
||||
|
||||
func fromBuf(vm *goja.Runtime, bufType goja.Value, buf goja.Value, allowString bool) ([]byte, error) {
|
||||
@@ -70,6 +66,7 @@ func fromBuf(vm *goja.Runtime, bufType goja.Value, buf goja.Value, allowString b
|
||||
break
|
||||
}
|
||||
return common.FromHex(obj.String()), nil
|
||||
|
||||
case "Array":
|
||||
var b []byte
|
||||
if err := vm.ExportTo(buf, &b); err != nil {
|
||||
@@ -81,10 +78,7 @@ func fromBuf(vm *goja.Runtime, bufType goja.Value, buf goja.Value, allowString b
|
||||
if !obj.Get("constructor").SameAs(bufType) {
|
||||
break
|
||||
}
|
||||
var b []byte
|
||||
if err := vm.ExportTo(buf, &b); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b := obj.Get("buffer").Export().(goja.ArrayBuffer).Bytes()
|
||||
return b, nil
|
||||
}
|
||||
return nil, fmt.Errorf("invalid buffer type")
|
||||
@@ -554,10 +548,10 @@ func (mo *memoryObj) slice(begin, end int64) ([]byte, error) {
|
||||
return []byte{}, nil
|
||||
}
|
||||
if end < begin || begin < 0 {
|
||||
return nil, fmt.Errorf("Tracer accessed out of bound memory: offset %d, end %d", begin, end)
|
||||
return nil, fmt.Errorf("tracer accessed out of bound memory: offset %d, end %d", begin, end)
|
||||
}
|
||||
if mo.memory.Len() < int(end) {
|
||||
return nil, fmt.Errorf("Tracer accessed out of bound memory: available %d, offset %d, size %d", mo.memory.Len(), begin, end-begin)
|
||||
return nil, fmt.Errorf("tracer accessed out of bound memory: available %d, offset %d, size %d", mo.memory.Len(), begin, end-begin)
|
||||
}
|
||||
return mo.memory.GetCopy(begin, end-begin), nil
|
||||
}
|
||||
@@ -579,7 +573,7 @@ func (mo *memoryObj) GetUint(addr int64) goja.Value {
|
||||
// getUint returns the 32 bytes at the specified address interpreted as a uint.
|
||||
func (mo *memoryObj) getUint(addr int64) (*big.Int, error) {
|
||||
if mo.memory.Len() < int(addr)+32 || addr < 0 {
|
||||
return nil, fmt.Errorf("Tracer accessed out of bound memory: available %d, offset %d, size %d", mo.memory.Len(), addr, 32)
|
||||
return nil, fmt.Errorf("tracer accessed out of bound memory: available %d, offset %d, size %d", mo.memory.Len(), addr, 32)
|
||||
}
|
||||
return new(big.Int).SetBytes(mo.memory.GetPtr(addr, 32)), nil
|
||||
}
|
||||
@@ -619,7 +613,7 @@ func (s *stackObj) Peek(idx int) goja.Value {
|
||||
// peek returns the nth-from-the-top element of the stack.
|
||||
func (s *stackObj) peek(idx int) (*big.Int, error) {
|
||||
if len(s.stack.Data()) <= idx || idx < 0 {
|
||||
return nil, fmt.Errorf("Tracer accessed out of bound stack: size %d, index %d", len(s.stack.Data()), idx)
|
||||
return nil, fmt.Errorf("tracer accessed out of bound stack: size %d, index %d", len(s.stack.Data()), idx)
|
||||
}
|
||||
return s.stack.Back(idx).ToBig(), nil
|
||||
}
|
||||
@@ -765,7 +759,7 @@ func (co *contractObj) GetValue() goja.Value {
|
||||
}
|
||||
|
||||
func (co *contractObj) GetInput() goja.Value {
|
||||
input := co.contract.Input
|
||||
input := common.CopyBytes(co.contract.Input)
|
||||
res, err := co.toBuf(co.vm, input)
|
||||
if err != nil {
|
||||
co.vm.Interrupt(err)
|
||||
@@ -884,7 +878,6 @@ func (r *callframeResult) GetError() goja.Value {
|
||||
return r.vm.ToValue(r.err.Error())
|
||||
}
|
||||
return goja.Undefined()
|
||||
|
||||
}
|
||||
|
||||
func (r *callframeResult) setupObject() *goja.Object {
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
return false;
|
||||
},
|
||||
|
||||
// store save the given indentifier and datasize.
|
||||
// store save the given identifier and datasize.
|
||||
store: function(id, size){
|
||||
var key = "" + toHex(id) + "-" + size;
|
||||
this.ids[key] = this.ids[key] + 1 || 1;
|
||||
|
||||
@@ -103,15 +103,15 @@ func TestTracer(t *testing.T) {
|
||||
{ // tests that we don't panic on bad arguments to memory access
|
||||
code: "{depths: [], step: function(log) { this.depths.push(log.memory.slice(-1,-2)); }, fault: function() {}, result: function() { return this.depths; }}",
|
||||
want: ``,
|
||||
fail: "Tracer accessed out of bound memory: offset -1, end -2 at step (<eval>:1:53(15)) in server-side tracer function 'step'",
|
||||
fail: "tracer accessed out of bound memory: offset -1, end -2 at step (<eval>:1:53(15)) in server-side tracer function 'step'",
|
||||
}, { // tests that we don't panic on bad arguments to stack peeks
|
||||
code: "{depths: [], step: function(log) { this.depths.push(log.stack.peek(-1)); }, fault: function() {}, result: function() { return this.depths; }}",
|
||||
want: ``,
|
||||
fail: "Tracer accessed out of bound stack: size 0, index -1 at step (<eval>:1:53(13)) in server-side tracer function 'step'",
|
||||
fail: "tracer accessed out of bound stack: size 0, index -1 at step (<eval>:1:53(13)) in server-side tracer function 'step'",
|
||||
}, { // tests that we don't panic on bad arguments to memory getUint
|
||||
code: "{ depths: [], step: function(log, db) { this.depths.push(log.memory.getUint(-64));}, fault: function() {}, result: function() { return this.depths; }}",
|
||||
want: ``,
|
||||
fail: "Tracer accessed out of bound memory: available 0, offset -64, size 32 at step (<eval>:1:58(13)) in server-side tracer function 'step'",
|
||||
fail: "tracer accessed out of bound memory: available 0, offset -64, size 32 at step (<eval>:1:58(13)) in server-side tracer function 'step'",
|
||||
}, { // tests some general counting
|
||||
code: "{count: 0, step: function() { this.count += 1; }, fault: function() {}, result: function() { return this.count; }}",
|
||||
want: `3`,
|
||||
@@ -232,7 +232,7 @@ func TestIsPrecompile(t *testing.T) {
|
||||
t.Error(err)
|
||||
}
|
||||
if string(res) != "false" {
|
||||
t.Errorf("Tracer should not consider blake2f as precompile in byzantium")
|
||||
t.Errorf("tracer should not consider blake2f as precompile in byzantium")
|
||||
}
|
||||
|
||||
tracer, _ = newJsTracer("{addr: toAddress('0000000000000000000000000000000000000009'), res: null, step: function() { this.res = isPrecompiled(this.addr); }, fault: function() {}, result: function() { return this.res; }}", nil)
|
||||
@@ -242,7 +242,7 @@ func TestIsPrecompile(t *testing.T) {
|
||||
t.Error(err)
|
||||
}
|
||||
if string(res) != "true" {
|
||||
t.Errorf("Tracer should consider blake2f as precompile in istanbul")
|
||||
t.Errorf("tracer should consider blake2f as precompile in istanbul")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ type Storage map[common.Hash]common.Hash
|
||||
|
||||
// Copy duplicates the current storage.
|
||||
func (s Storage) Copy() Storage {
|
||||
cpy := make(Storage)
|
||||
cpy := make(Storage, len(s))
|
||||
for key, value := range s {
|
||||
cpy[key] = value
|
||||
}
|
||||
@@ -224,7 +224,7 @@ func (l *StructLogger) CaptureEnd(output []byte, gasUsed uint64, t time.Duration
|
||||
l.output = output
|
||||
l.err = err
|
||||
if l.cfg.Debug {
|
||||
fmt.Printf("0x%x\n", output)
|
||||
fmt.Printf("%#x\n", output)
|
||||
if err != nil {
|
||||
fmt.Printf(" error: %v\n", err)
|
||||
}
|
||||
@@ -346,11 +346,11 @@ func NewMarkdownLogger(cfg *Config, writer io.Writer) *mdLogger {
|
||||
func (t *mdLogger) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
|
||||
t.env = env
|
||||
if !create {
|
||||
fmt.Fprintf(t.out, "From: `%v`\nTo: `%v`\nData: `0x%x`\nGas: `%d`\nValue `%v` wei\n",
|
||||
fmt.Fprintf(t.out, "From: `%v`\nTo: `%v`\nData: `%#x`\nGas: `%d`\nValue `%v` wei\n",
|
||||
from.String(), to.String(),
|
||||
input, gas, value)
|
||||
} else {
|
||||
fmt.Fprintf(t.out, "From: `%v`\nCreate at: `%v`\nData: `0x%x`\nGas: `%d`\nValue `%v` wei\n",
|
||||
fmt.Fprintf(t.out, "From: `%v`\nCreate at: `%v`\nData: `%#x`\nGas: `%d`\nValue `%v` wei\n",
|
||||
from.String(), to.String(),
|
||||
input, gas, value)
|
||||
}
|
||||
@@ -387,7 +387,7 @@ func (t *mdLogger) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope
|
||||
}
|
||||
|
||||
func (t *mdLogger) CaptureEnd(output []byte, gasUsed uint64, tm time.Duration, err error) {
|
||||
fmt.Fprintf(t.out, "\nOutput: `0x%x`\nConsumed gas: `%d`\nError: `%v`\n",
|
||||
fmt.Fprintf(t.out, "\nOutput: `%#x`\nConsumed gas: `%d`\nError: `%v`\n",
|
||||
output, gasUsed, err)
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
// Copyright 2022 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package native
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"math/big"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts/abi"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/eth/tracers"
|
||||
)
|
||||
|
||||
func init() {
|
||||
register("revertReasonTracer", newRevertReasonTracer)
|
||||
}
|
||||
|
||||
var revertSelector = crypto.Keccak256([]byte("Error(string)"))[:4]
|
||||
|
||||
// revertReasonTracer is a go implementation of the Tracer interface which
|
||||
// track the error message or revert reason return by the contract.
|
||||
type revertReasonTracer struct {
|
||||
env *vm.EVM
|
||||
revertReason string // The revert reason return from the tx, if tx success, empty string return
|
||||
interrupt uint32 // Atomic flag to signal execution interruption
|
||||
reason error // Textual reason for the interruption
|
||||
}
|
||||
|
||||
// newRevertReasonTracer returns a new revert reason tracer.
|
||||
func newRevertReasonTracer(_ *tracers.Context) tracers.Tracer {
|
||||
return &revertReasonTracer{}
|
||||
}
|
||||
|
||||
// CaptureStart implements the EVMLogger interface to initialize the tracing operation.
|
||||
func (t *revertReasonTracer) CaptureStart(env *vm.EVM, _ common.Address, _ common.Address, _ bool, _ []byte, _ uint64, _ *big.Int) {
|
||||
t.env = env
|
||||
}
|
||||
|
||||
// CaptureEnd is called after the call finishes to finalize the tracing.
|
||||
func (t *revertReasonTracer) CaptureEnd(output []byte, _ uint64, _ time.Duration, err error) {
|
||||
if err != nil {
|
||||
if err == vm.ErrExecutionReverted && len(output) > 4 && bytes.Equal(output[:4], revertSelector) {
|
||||
errMsg, _ := abi.UnpackRevert(output)
|
||||
t.revertReason = err.Error() + ": " + errMsg
|
||||
} else {
|
||||
t.revertReason = err.Error()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CaptureState implements the EVMLogger interface to trace a single step of VM execution.
|
||||
func (t *revertReasonTracer) CaptureState(_ uint64, _ vm.OpCode, _, _ uint64, _ *vm.ScopeContext, _ []byte, _ int, _ error) {
|
||||
}
|
||||
|
||||
// CaptureFault implements the EVMLogger interface to trace an execution fault.
|
||||
func (t *revertReasonTracer) CaptureFault(_ uint64, _ vm.OpCode, _, _ uint64, _ *vm.ScopeContext, _ int, _ error) {
|
||||
}
|
||||
|
||||
// CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct).
|
||||
func (t *revertReasonTracer) CaptureEnter(_ vm.OpCode, _ common.Address, _ common.Address, _ []byte, _ uint64, _ *big.Int) {
|
||||
// Skip if tracing was interrupted
|
||||
if atomic.LoadUint32(&t.interrupt) > 0 {
|
||||
t.env.Cancel()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// CaptureExit is called when EVM exits a scope, even if the scope didn't
|
||||
// execute any code.
|
||||
func (t *revertReasonTracer) CaptureExit(_ []byte, _ uint64, _ error) {}
|
||||
|
||||
func (t *revertReasonTracer) CaptureTxStart(_ uint64) {}
|
||||
|
||||
func (t *revertReasonTracer) CaptureTxEnd(_ uint64) {}
|
||||
|
||||
// GetResult returns an error message json object.
|
||||
func (t *revertReasonTracer) GetResult() (json.RawMessage, error) {
|
||||
res, err := json.Marshal(t.revertReason)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return res, t.reason
|
||||
}
|
||||
|
||||
// Stop terminates execution of the tracer at the first opportune moment.
|
||||
func (t *revertReasonTracer) Stop(err error) {
|
||||
t.reason = err
|
||||
atomic.StoreUint32(&t.interrupt, 1)
|
||||
}
|
||||
@@ -21,7 +21,6 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
@@ -32,20 +31,6 @@ import (
|
||||
"github.com/ethereum/go-ethereum/tests"
|
||||
)
|
||||
|
||||
// callTrace is the result of a callTracer run.
|
||||
type callTrace struct {
|
||||
Type string `json:"type"`
|
||||
From common.Address `json:"from"`
|
||||
To common.Address `json:"to"`
|
||||
Input hexutil.Bytes `json:"input"`
|
||||
Output hexutil.Bytes `json:"output"`
|
||||
Gas *hexutil.Uint64 `json:"gas,omitempty"`
|
||||
GasUsed *hexutil.Uint64 `json:"gasUsed,omitempty"`
|
||||
Value *hexutil.Big `json:"value,omitempty"`
|
||||
Error string `json:"error,omitempty"`
|
||||
Calls []callTrace `json:"calls,omitempty"`
|
||||
}
|
||||
|
||||
func BenchmarkTransactionTrace(b *testing.B) {
|
||||
key, _ := crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
from := crypto.PubkeyToAddress(key.PublicKey)
|
||||
|
||||
Reference in New Issue
Block a user