Merge Geth v1.10.21 as well as update to Plugeth-Utils v0.0.18

This commit is contained in:
philip-morlier
2022-07-27 10:38:42 -07:00
380 changed files with 6936 additions and 6262 deletions
+12 -5
View File
@@ -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
View File
@@ -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: &ethapi.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: &ethapi.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: &ethapi.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
View File
@@ -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;
+5 -5
View File
@@ -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")
}
}
+5 -5
View File
@@ -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)
}
+108
View File
@@ -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)
}
-15
View File
@@ -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)