From ad11691daf0dbc83e8684b9f5b190c5f21bfbd6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Szil=C3=A1gyi?= Date: Mon, 8 Nov 2021 15:44:11 +0200 Subject: [PATCH 01/30] params: begin v1.10.13 release cycle --- params/version.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/params/version.go b/params/version.go index c28ded936..9d392e64e 100644 --- a/params/version.go +++ b/params/version.go @@ -21,10 +21,10 @@ import ( ) const ( - VersionMajor = 1 // Major version component of the current release - VersionMinor = 10 // Minor version component of the current release - VersionPatch = 12 // Patch version component of the current release - VersionMeta = "stable" // Version metadata to append to the version string + VersionMajor = 1 // Major version component of the current release + VersionMinor = 10 // Minor version component of the current release + VersionPatch = 13 // Patch version component of the current release + VersionMeta = "unstable" // Version metadata to append to the version string ) // Version holds the textual version string. From 9489853321bb221694f5262772c656413be073af Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Mon, 8 Nov 2021 15:25:35 +0100 Subject: [PATCH 02/30] core: check effective tip in txpool pricelimit validation (#23855) The price limit is supposed to exclude transactions with too low fee amount. Before EIP-1559, it was sufficient to check the limit against the gas price of the transaction. After 1559, it is more complicated because the concept of 'transaction gas price' does not really exist. When mining, the price limit is used to exclude transactions below a certain effective fee amount. This change makes it apply the same check earlier, in tx validation. Transactions below the specified fee amount cannot enter the pool. Fixes #23837 --- core/tx_pool.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/core/tx_pool.go b/core/tx_pool.go index 3329d736a..0e3844bcb 100644 --- a/core/tx_pool.go +++ b/core/tx_pool.go @@ -621,8 +621,9 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error { if err != nil { return ErrInvalidSender } - // Drop non-local transactions under our own minimal accepted gas price or tip - if !local && tx.GasTipCapIntCmp(pool.gasPrice) < 0 { + // Drop non-local transactions under our own minimal accepted gas price or tip. + pendingBaseFee := pool.priced.urgent.baseFee + if !local && tx.EffectiveGasTipIntCmp(pool.gasPrice, pendingBaseFee) < 0 { return ErrUnderpriced } // Ensure the transaction adheres to nonce ordering From 6b9c77f06016eab761592baf5176ec343e400dc5 Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Tue, 9 Nov 2021 12:09:35 +0100 Subject: [PATCH 03/30] eth/tracers: package restructuring (#23857) * eth/tracers: restructure tracer package * core/vm/runtime: load js tracers * eth/tracers: mv bigint js code to own file * eth/tracers: add method docs for native tracers * eth/tracers: minor doc fix * core,eth: cancel evm on nativecalltracer stop * core/vm: fix failing test Co-authored-by: Sina Mahmoodi --- cmd/geth/main.go | 3 +- core/vm/access_list_tracer.go | 4 +- core/vm/interpreter.go | 6 +- core/vm/logger.go | 24 +- core/vm/logger_json.go | 10 +- core/vm/logger_test.go | 3 +- core/vm/runtime/runtime_test.go | 9 +- eth/tracers/api_test.go | 337 +++---- .../tracetest}/calltrace_test.go | 160 +++- .../testdata/call_tracer/create.json | 0 .../testdata/call_tracer/deep_calls.json | 0 .../testdata/call_tracer/delegatecall.json | 0 .../inner_create_oog_outer_throw.json | 0 .../testdata/call_tracer/inner_instafail.json | 0 .../call_tracer/inner_throw_outer_revert.json | 0 .../tracetest}/testdata/call_tracer/oog.json | 0 .../testdata/call_tracer/revert.json | 0 .../testdata/call_tracer/revert_reason.json | 0 .../testdata/call_tracer/selfdestruct.json | 0 .../testdata/call_tracer/simple.json | 0 .../testdata/call_tracer/throw.json | 0 .../testdata/call_tracer_legacy/create.json | 0 .../call_tracer_legacy/deep_calls.json | 0 .../call_tracer_legacy/delegatecall.json | 0 .../inner_create_oog_outer_throw.json | 0 .../call_tracer_legacy/inner_instafail.json | 0 .../inner_throw_outer_revert.json | 0 .../testdata/call_tracer_legacy/oog.json | 0 .../testdata/call_tracer_legacy/revert.json | 0 .../call_tracer_legacy/revert_reason.json | 0 .../call_tracer_legacy/selfdestruct.json | 0 .../testdata/call_tracer_legacy/simple.json | 0 .../testdata/call_tracer_legacy/throw.json | 0 eth/tracers/{tracer.go => js/bigint.go} | 842 +---------------- .../{ => js}/internal/tracers/4byte_tracer.js | 0 .../internal/tracers/4byte_tracer_legacy.js | 0 .../{ => js}/internal/tracers/assets.go | 0 .../internal/tracers/bigram_tracer.js | 0 .../internal/tracers/call_tracer_js.js | 0 .../internal/tracers/call_tracer_legacy.js | 0 .../internal/tracers/evmdis_tracer.js | 0 .../{ => js}/internal/tracers/noop_tracer.js | 0 .../internal/tracers/opcount_tracer.js | 0 .../internal/tracers/prestate_tracer.js | 0 .../{ => js}/internal/tracers/tracers.go | 0 .../internal/tracers/trigram_tracer.js | 0 .../internal/tracers/unigram_tracer.js | 0 eth/tracers/js/tracer.go | 880 ++++++++++++++++++ eth/tracers/{ => js}/tracer_test.go | 59 +- eth/tracers/native/call.go | 24 +- eth/tracers/native/noop.go | 36 +- eth/tracers/native/tracer.go | 79 ++ eth/tracers/tracers.go | 74 +- eth/tracers/tracers_test.go | 232 ----- 54 files changed, 1393 insertions(+), 1389 deletions(-) rename eth/tracers/{testing => internal/tracetest}/calltrace_test.go (56%) rename eth/tracers/{ => internal/tracetest}/testdata/call_tracer/create.json (100%) rename eth/tracers/{ => internal/tracetest}/testdata/call_tracer/deep_calls.json (100%) rename eth/tracers/{ => internal/tracetest}/testdata/call_tracer/delegatecall.json (100%) rename eth/tracers/{ => internal/tracetest}/testdata/call_tracer/inner_create_oog_outer_throw.json (100%) rename eth/tracers/{ => internal/tracetest}/testdata/call_tracer/inner_instafail.json (100%) rename eth/tracers/{ => internal/tracetest}/testdata/call_tracer/inner_throw_outer_revert.json (100%) rename eth/tracers/{ => internal/tracetest}/testdata/call_tracer/oog.json (100%) rename eth/tracers/{ => internal/tracetest}/testdata/call_tracer/revert.json (100%) rename eth/tracers/{ => internal/tracetest}/testdata/call_tracer/revert_reason.json (100%) rename eth/tracers/{ => internal/tracetest}/testdata/call_tracer/selfdestruct.json (100%) rename eth/tracers/{ => internal/tracetest}/testdata/call_tracer/simple.json (100%) rename eth/tracers/{ => internal/tracetest}/testdata/call_tracer/throw.json (100%) rename eth/tracers/{ => internal/tracetest}/testdata/call_tracer_legacy/create.json (100%) rename eth/tracers/{ => internal/tracetest}/testdata/call_tracer_legacy/deep_calls.json (100%) rename eth/tracers/{ => internal/tracetest}/testdata/call_tracer_legacy/delegatecall.json (100%) rename eth/tracers/{ => internal/tracetest}/testdata/call_tracer_legacy/inner_create_oog_outer_throw.json (100%) rename eth/tracers/{ => internal/tracetest}/testdata/call_tracer_legacy/inner_instafail.json (100%) rename eth/tracers/{ => internal/tracetest}/testdata/call_tracer_legacy/inner_throw_outer_revert.json (100%) rename eth/tracers/{ => internal/tracetest}/testdata/call_tracer_legacy/oog.json (100%) rename eth/tracers/{ => internal/tracetest}/testdata/call_tracer_legacy/revert.json (100%) rename eth/tracers/{ => internal/tracetest}/testdata/call_tracer_legacy/revert_reason.json (100%) rename eth/tracers/{ => internal/tracetest}/testdata/call_tracer_legacy/selfdestruct.json (100%) rename eth/tracers/{ => internal/tracetest}/testdata/call_tracer_legacy/simple.json (100%) rename eth/tracers/{ => internal/tracetest}/testdata/call_tracer_legacy/throw.json (100%) rename eth/tracers/{tracer.go => js/bigint.go} (50%) rename eth/tracers/{ => js}/internal/tracers/4byte_tracer.js (100%) rename eth/tracers/{ => js}/internal/tracers/4byte_tracer_legacy.js (100%) rename eth/tracers/{ => js}/internal/tracers/assets.go (100%) rename eth/tracers/{ => js}/internal/tracers/bigram_tracer.js (100%) rename eth/tracers/{ => js}/internal/tracers/call_tracer_js.js (100%) rename eth/tracers/{ => js}/internal/tracers/call_tracer_legacy.js (100%) rename eth/tracers/{ => js}/internal/tracers/evmdis_tracer.js (100%) rename eth/tracers/{ => js}/internal/tracers/noop_tracer.js (100%) rename eth/tracers/{ => js}/internal/tracers/opcount_tracer.js (100%) rename eth/tracers/{ => js}/internal/tracers/prestate_tracer.js (100%) rename eth/tracers/{ => js}/internal/tracers/tracers.go (100%) rename eth/tracers/{ => js}/internal/tracers/trigram_tracer.js (100%) rename eth/tracers/{ => js}/internal/tracers/unigram_tracer.js (100%) create mode 100644 eth/tracers/js/tracer.go rename eth/tracers/{ => js}/tracer_test.go (75%) create mode 100644 eth/tracers/native/tracer.go diff --git a/cmd/geth/main.go b/cmd/geth/main.go index c70127af8..16c3be53b 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -40,7 +40,8 @@ import ( "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/node" - // Force-load the native, to trigger registration + // Force-load the tracer engines to trigger registration + _ "github.com/ethereum/go-ethereum/eth/tracers/js" _ "github.com/ethereum/go-ethereum/eth/tracers/native" "gopkg.in/urfave/cli.v1" diff --git a/core/vm/access_list_tracer.go b/core/vm/access_list_tracer.go index 11b4e2942..1368e4c99 100644 --- a/core/vm/access_list_tracer.go +++ b/core/vm/access_list_tracer.go @@ -141,7 +141,7 @@ func (a *AccessListTracer) CaptureStart(env *EVM, from common.Address, to common } // CaptureState captures all opcodes that touch storage or addresses and adds them to the accesslist. -func (a *AccessListTracer) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error) { +func (a *AccessListTracer) CaptureState(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error) { stack := scope.Stack if (op == SLOAD || op == SSTORE) && stack.len() >= 1 { slot := common.Hash(stack.data[stack.len()-1].Bytes32()) @@ -161,7 +161,7 @@ func (a *AccessListTracer) CaptureState(env *EVM, pc uint64, op OpCode, gas, cos } } -func (*AccessListTracer) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, depth int, err error) { +func (*AccessListTracer) CaptureFault(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, depth int, err error) { } func (*AccessListTracer) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) {} diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 92d33388f..4315750ba 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -169,9 +169,9 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( defer func() { if err != nil { if !logged { - in.cfg.Tracer.CaptureState(in.evm, pcCopy, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, err) + in.cfg.Tracer.CaptureState(pcCopy, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, err) } else { - in.cfg.Tracer.CaptureFault(in.evm, pcCopy, op, gasCopy, cost, callContext, in.evm.depth, err) + in.cfg.Tracer.CaptureFault(pcCopy, op, gasCopy, cost, callContext, in.evm.depth, err) } } }() @@ -253,7 +253,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( } if in.cfg.Debug { - in.cfg.Tracer.CaptureState(in.evm, pc, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, err) + in.cfg.Tracer.CaptureState(pc, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, err) logged = true } diff --git a/core/vm/logger.go b/core/vm/logger.go index 048b84ff6..98ff967ee 100644 --- a/core/vm/logger.go +++ b/core/vm/logger.go @@ -105,10 +105,10 @@ func (s *StructLog) ErrorString() string { // if you need to retain them beyond the current call. type EVMLogger interface { CaptureStart(env *EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) - CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error) + CaptureState(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error) CaptureEnter(typ OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) CaptureExit(output []byte, gasUsed uint64, err error) - CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, depth int, err error) + CaptureFault(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, depth int, err error) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) } @@ -119,6 +119,7 @@ type EVMLogger interface { // contract their storage. type StructLogger struct { cfg LogConfig + env *EVM storage map[common.Address]Storage logs []StructLog @@ -147,12 +148,13 @@ func (l *StructLogger) Reset() { // CaptureStart implements the EVMLogger interface to initialize the tracing operation. func (l *StructLogger) CaptureStart(env *EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { + l.env = env } // CaptureState logs a new structured log message and pushes it out to the environment // // CaptureState also tracks SLOAD/SSTORE ops to track storage change. -func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error) { +func (l *StructLogger) CaptureState(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error) { memory := scope.Memory stack := scope.Stack contract := scope.Contract @@ -186,7 +188,7 @@ func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost ui if op == SLOAD && stack.len() >= 1 { var ( address = common.Hash(stack.data[stack.len()-1].Bytes32()) - value = env.StateDB.GetState(contract.Address(), address) + value = l.env.StateDB.GetState(contract.Address(), address) ) l.storage[contract.Address()][address] = value storage = l.storage[contract.Address()].Copy() @@ -206,13 +208,13 @@ func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost ui copy(rdata, rData) } // create a new snapshot of the EVM. - log := StructLog{pc, op, gas, cost, mem, memory.Len(), stck, rdata, storage, depth, env.StateDB.GetRefund(), err} + log := StructLog{pc, op, gas, cost, mem, memory.Len(), stck, rdata, storage, depth, l.env.StateDB.GetRefund(), err} l.logs = append(l.logs, log) } // CaptureFault implements the EVMLogger interface to trace an execution fault // while running an opcode. -func (l *StructLogger) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, depth int, err error) { +func (l *StructLogger) CaptureFault(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, depth int, err error) { } // CaptureEnd is called after the call finishes to finalize the tracing. @@ -291,12 +293,13 @@ func WriteLogs(writer io.Writer, logs []*types.Log) { type mdLogger struct { out io.Writer cfg *LogConfig + env *EVM } // NewMarkdownLogger creates a logger which outputs information in a format adapted // for human readability, and is also a valid markdown table func NewMarkdownLogger(cfg *LogConfig, writer io.Writer) *mdLogger { - l := &mdLogger{writer, cfg} + l := &mdLogger{out: writer, cfg: cfg} if l.cfg == nil { l.cfg = &LogConfig{} } @@ -304,6 +307,7 @@ func NewMarkdownLogger(cfg *LogConfig, writer io.Writer) *mdLogger { } func (t *mdLogger) CaptureStart(env *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", from.String(), to.String(), @@ -321,7 +325,7 @@ func (t *mdLogger) CaptureStart(env *EVM, from common.Address, to common.Address } // CaptureState also tracks SLOAD/SSTORE ops to track storage change. -func (t *mdLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error) { +func (t *mdLogger) CaptureState(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error) { stack := scope.Stack fmt.Fprintf(t.out, "| %4d | %10v | %3d |", pc, op, cost) @@ -334,14 +338,14 @@ func (t *mdLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64 b := fmt.Sprintf("[%v]", strings.Join(a, ",")) fmt.Fprintf(t.out, "%10v |", b) } - fmt.Fprintf(t.out, "%10v |", env.StateDB.GetRefund()) + fmt.Fprintf(t.out, "%10v |", t.env.StateDB.GetRefund()) fmt.Fprintln(t.out, "") if err != nil { fmt.Fprintf(t.out, "Error: %v\n", err) } } -func (t *mdLogger) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, depth int, err error) { +func (t *mdLogger) CaptureFault(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, depth int, err error) { fmt.Fprintf(t.out, "\nError: at pc=%d, op=%v: %v\n", pc, op, err) } diff --git a/core/vm/logger_json.go b/core/vm/logger_json.go index 479a00c0a..364ce738a 100644 --- a/core/vm/logger_json.go +++ b/core/vm/logger_json.go @@ -29,12 +29,13 @@ import ( type JSONLogger struct { encoder *json.Encoder cfg *LogConfig + env *EVM } // NewJSONLogger creates a new EVM tracer that prints execution steps as JSON objects // into the provided stream. func NewJSONLogger(cfg *LogConfig, writer io.Writer) *JSONLogger { - l := &JSONLogger{json.NewEncoder(writer), cfg} + l := &JSONLogger{encoder: json.NewEncoder(writer), cfg: cfg} if l.cfg == nil { l.cfg = &LogConfig{} } @@ -42,12 +43,13 @@ func NewJSONLogger(cfg *LogConfig, writer io.Writer) *JSONLogger { } func (l *JSONLogger) CaptureStart(env *EVM, from, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { + l.env = env } -func (l *JSONLogger) CaptureFault(*EVM, uint64, OpCode, uint64, uint64, *ScopeContext, int, error) {} +func (l *JSONLogger) CaptureFault(uint64, OpCode, uint64, uint64, *ScopeContext, int, error) {} // CaptureState outputs state information on the logger. -func (l *JSONLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error) { +func (l *JSONLogger) CaptureState(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error) { memory := scope.Memory stack := scope.Stack @@ -58,7 +60,7 @@ func (l *JSONLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint GasCost: cost, MemorySize: memory.Len(), Depth: depth, - RefundCounter: env.StateDB.GetRefund(), + RefundCounter: l.env.StateDB.GetRefund(), Err: err, } if l.cfg.EnableMemory { diff --git a/core/vm/logger_test.go b/core/vm/logger_test.go index 730d8374b..7726c90bd 100644 --- a/core/vm/logger_test.go +++ b/core/vm/logger_test.go @@ -62,7 +62,8 @@ func TestStoreCapture(t *testing.T) { scope.Stack.push(uint256.NewInt(1)) scope.Stack.push(new(uint256.Int)) var index common.Hash - logger.CaptureState(env, 0, SSTORE, 0, 0, scope, nil, 0, nil) + logger.CaptureStart(env, common.Address{}, contract.Address(), false, nil, 0, nil) + logger.CaptureState(0, SSTORE, 0, 0, scope, nil, 0, nil) if len(logger.storage[contract.Address()]) == 0 { t.Fatalf("expected exactly 1 changed value on address %x, got %d", contract.Address(), len(logger.storage[contract.Address()])) diff --git a/core/vm/runtime/runtime_test.go b/core/vm/runtime/runtime_test.go index 9f4bafbc7..fea7817ff 100644 --- a/core/vm/runtime/runtime_test.go +++ b/core/vm/runtime/runtime_test.go @@ -35,6 +35,9 @@ import ( "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/eth/tracers" "github.com/ethereum/go-ethereum/params" + + // force-load js tracers to trigger registration + _ "github.com/ethereum/go-ethereum/eth/tracers/js" ) func TestDefaults(t *testing.T) { @@ -330,12 +333,12 @@ type stepCounter struct { func (s *stepCounter) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { } -func (s *stepCounter) CaptureFault(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, depth int, err error) { +func (s *stepCounter) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, depth int, err error) { } func (s *stepCounter) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) {} -func (s *stepCounter) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) { +func (s *stepCounter) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) { s.steps++ // Enable this for more output //s.inner.CaptureState(env, pc, op, gas, cost, memory, stack, rStack, contract, depth, err) @@ -511,7 +514,7 @@ func BenchmarkSimpleLoop(b *testing.B) { // TestEip2929Cases contains various testcases that are used for // EIP-2929 about gas repricings func TestEip2929Cases(t *testing.T) { - + t.Skip("Test only useful for generating documentation") id := 1 prettyPrint := func(comment string, code []byte) { diff --git a/eth/tracers/api_test.go b/eth/tracers/api_test.go index ff5675e9e..a3c0a7249 100644 --- a/eth/tracers/api_test.go +++ b/eth/tracers/api_test.go @@ -306,147 +306,6 @@ func TestTraceCall(t *testing.T) { } } -func TestOverriddenTraceCall(t *testing.T) { - t.Parallel() - - // Initialize test accounts - accounts := newAccounts(3) - genesis := &core.Genesis{Alloc: core.GenesisAlloc{ - accounts[0].addr: {Balance: big.NewInt(params.Ether)}, - accounts[1].addr: {Balance: big.NewInt(params.Ether)}, - accounts[2].addr: {Balance: big.NewInt(params.Ether)}, - }} - genBlocks := 10 - signer := types.HomesteadSigner{} - api := NewAPI(newTestBackend(t, genBlocks, genesis, func(i int, b *core.BlockGen) { - // Transfer from account[0] to account[1] - // value: 1000 wei - // fee: 0 wei - 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) - })) - randomAccounts, tracer := newAccounts(3), "callTracerJs" - - var testSuite = []struct { - blockNumber rpc.BlockNumber - call ethapi.TransactionArgs - config *TraceCallConfig - expectErr error - expect *callTrace - }{ - // Succcessful call with state overriding - { - blockNumber: rpc.PendingBlockNumber, - call: ethapi.TransactionArgs{ - From: &randomAccounts[0].addr, - To: &randomAccounts[1].addr, - Value: (*hexutil.Big)(big.NewInt(1000)), - }, - config: &TraceCallConfig{ - Tracer: &tracer, - StateOverrides: ðapi.StateOverride{ - randomAccounts[0].addr: ethapi.OverrideAccount{Balance: newRPCBalance(new(big.Int).Mul(big.NewInt(1), big.NewInt(params.Ether)))}, - }, - }, - expectErr: nil, - expect: &callTrace{ - Type: "CALL", - From: randomAccounts[0].addr, - To: randomAccounts[1].addr, - Gas: newRPCUint64(24979000), - GasUsed: newRPCUint64(0), - Value: (*hexutil.Big)(big.NewInt(1000)), - }, - }, - // Invalid call without state overriding - { - blockNumber: rpc.PendingBlockNumber, - call: ethapi.TransactionArgs{ - From: &randomAccounts[0].addr, - To: &randomAccounts[1].addr, - Value: (*hexutil.Big)(big.NewInt(1000)), - }, - config: &TraceCallConfig{ - Tracer: &tracer, - }, - expectErr: core.ErrInsufficientFunds, - expect: nil, - }, - // Successful simple contract call - // - // // SPDX-License-Identifier: GPL-3.0 - // - // pragma solidity >=0.7.0 <0.8.0; - // - // /** - // * @title Storage - // * @dev Store & retrieve value in a variable - // */ - // contract Storage { - // uint256 public number; - // constructor() { - // number = block.number; - // } - // } - { - blockNumber: rpc.PendingBlockNumber, - call: ethapi.TransactionArgs{ - From: &randomAccounts[0].addr, - To: &randomAccounts[2].addr, - Data: newRPCBytes(common.Hex2Bytes("8381f58a")), // call number() - }, - config: &TraceCallConfig{ - Tracer: &tracer, - StateOverrides: ðapi.StateOverride{ - randomAccounts[2].addr: ethapi.OverrideAccount{ - Code: newRPCBytes(common.Hex2Bytes("6080604052348015600f57600080fd5b506004361060285760003560e01c80638381f58a14602d575b600080fd5b60336049565b6040518082815260200191505060405180910390f35b6000548156fea2646970667358221220eab35ffa6ab2adfe380772a48b8ba78e82a1b820a18fcb6f59aa4efb20a5f60064736f6c63430007040033")), - StateDiff: newStates([]common.Hash{{}}, []common.Hash{common.BigToHash(big.NewInt(123))}), - }, - }, - }, - expectErr: nil, - expect: &callTrace{ - Type: "CALL", - From: randomAccounts[0].addr, - To: randomAccounts[2].addr, - Input: hexutil.Bytes(common.Hex2Bytes("8381f58a")), - Output: hexutil.Bytes(common.BigToHash(big.NewInt(123)).Bytes()), - Gas: newRPCUint64(24978936), - GasUsed: newRPCUint64(2283), - Value: (*hexutil.Big)(big.NewInt(0)), - }, - }, - } - 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("test %d: want error %v, have nothing", i, testspec.expectErr) - continue - } - if !errors.Is(err, testspec.expectErr) { - t.Errorf("test %d: error mismatch, want %v, have %v", i, testspec.expectErr, err) - } - } else { - if err != nil { - t.Errorf("test %d: want no error, have %v", i, err) - continue - } - ret := new(callTrace) - if err := json.Unmarshal(result.(json.RawMessage), ret); err != nil { - t.Fatalf("test %d: failed to unmarshal trace result: %v", i, err) - } - if !jsonEqual(ret, testspec.expect) { - // uncomment this for easier debugging - //have, _ := json.MarshalIndent(ret, "", " ") - //want, _ := json.MarshalIndent(testspec.expect, "", " ") - //t.Fatalf("trace mismatch: \nhave %+v\nwant %+v", string(have), string(want)) - t.Fatalf("trace mismatch: \nhave %+v\nwant %+v", ret, testspec.expect) - } - } - } -} - func TestTraceTransaction(t *testing.T) { t.Parallel() @@ -503,90 +362,177 @@ func TestTraceBlock(t *testing.T) { var testSuite = []struct { blockNumber rpc.BlockNumber config *TraceConfig - expect interface{} + want string expectErr error }{ // Trace genesis block, expect error { blockNumber: rpc.BlockNumber(0), - config: nil, - expect: nil, expectErr: errors.New("genesis is not traceable"), }, // Trace head block { blockNumber: rpc.BlockNumber(genBlocks), - config: nil, - expectErr: nil, - expect: []*txTraceResult{ - { - Result: ðapi.ExecutionResult{ - Gas: params.TxGas, - Failed: false, - ReturnValue: "", - StructLogs: []ethapi.StructLogRes{}, - }, - }, - }, + want: `[{"result":{"gas":21000,"failed":false,"returnValue":"","structLogs":[]}}]`, }, // Trace non-existent block { blockNumber: rpc.BlockNumber(genBlocks + 1), - config: nil, expectErr: fmt.Errorf("block #%d not found", genBlocks+1), - expect: nil, }, // Trace latest block { blockNumber: rpc.LatestBlockNumber, - config: nil, - expectErr: nil, - expect: []*txTraceResult{ - { - Result: ðapi.ExecutionResult{ - Gas: params.TxGas, - Failed: false, - ReturnValue: "", - StructLogs: []ethapi.StructLogRes{}, - }, - }, - }, + want: `[{"result":{"gas":21000,"failed":false,"returnValue":"","structLogs":[]}}]`, }, // Trace pending block { blockNumber: rpc.PendingBlockNumber, - config: nil, - expectErr: nil, - expect: []*txTraceResult{ - { - Result: ðapi.ExecutionResult{ - Gas: params.TxGas, - Failed: false, - ReturnValue: "", - StructLogs: []ethapi.StructLogRes{}, + want: `[{"result":{"gas":21000,"failed":false,"returnValue":"","structLogs":[]}}]`, + }, + } + for i, tc := range testSuite { + result, err := api.TraceBlockByNumber(context.Background(), tc.blockNumber, tc.config) + if tc.expectErr != nil { + if err == nil { + t.Errorf("test %d, want error %v", i, tc.expectErr) + continue + } + if !reflect.DeepEqual(err, tc.expectErr) { + t.Errorf("test %d: error mismatch, want %v, get %v", i, tc.expectErr, err) + } + continue + } + if err != nil { + t.Errorf("test %d, want no error, have %v", i, err) + continue + } + have, _ := json.Marshal(result) + want := tc.want + if string(have) != want { + t.Errorf("test %d, result mismatch, have\n%v\n, want\n%v\n", i, string(have), want) + } + } +} + +func TestTracingWithOverrides(t *testing.T) { + t.Parallel() + // Initialize test accounts + accounts := newAccounts(3) + genesis := &core.Genesis{Alloc: core.GenesisAlloc{ + accounts[0].addr: {Balance: big.NewInt(params.Ether)}, + accounts[1].addr: {Balance: big.NewInt(params.Ether)}, + accounts[2].addr: {Balance: big.NewInt(params.Ether)}, + }} + genBlocks := 10 + signer := types.HomesteadSigner{} + api := NewAPI(newTestBackend(t, genBlocks, genesis, func(i int, b *core.BlockGen) { + // Transfer from account[0] to account[1] + // value: 1000 wei + // fee: 0 wei + 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) + })) + randomAccounts := newAccounts(3) + type res struct { + Gas int + Failed bool + returnValue string + } + var testSuite = []struct { + blockNumber rpc.BlockNumber + call ethapi.TransactionArgs + config *TraceCallConfig + expectErr error + want string + }{ + // Call which can only succeed if state is state overridden + { + blockNumber: rpc.PendingBlockNumber, + call: ethapi.TransactionArgs{ + From: &randomAccounts[0].addr, + To: &randomAccounts[1].addr, + Value: (*hexutil.Big)(big.NewInt(1000)), + }, + config: &TraceCallConfig{ + StateOverrides: ðapi.StateOverride{ + randomAccounts[0].addr: ethapi.OverrideAccount{Balance: newRPCBalance(new(big.Int).Mul(big.NewInt(1), big.NewInt(params.Ether)))}, + }, + }, + want: `{"gas":21000,"failed":false,"returnValue":""}`, + }, + // Invalid call without state overriding + { + blockNumber: rpc.PendingBlockNumber, + call: ethapi.TransactionArgs{ + From: &randomAccounts[0].addr, + To: &randomAccounts[1].addr, + Value: (*hexutil.Big)(big.NewInt(1000)), + }, + config: &TraceCallConfig{}, + expectErr: core.ErrInsufficientFunds, + }, + // Successful simple contract call + // + // // SPDX-License-Identifier: GPL-3.0 + // + // pragma solidity >=0.7.0 <0.8.0; + // + // /** + // * @title Storage + // * @dev Store & retrieve value in a variable + // */ + // contract Storage { + // uint256 public number; + // constructor() { + // number = block.number; + // } + // } + { + blockNumber: rpc.PendingBlockNumber, + call: ethapi.TransactionArgs{ + From: &randomAccounts[0].addr, + To: &randomAccounts[2].addr, + Data: newRPCBytes(common.Hex2Bytes("8381f58a")), // call number() + }, + config: &TraceCallConfig{ + //Tracer: &tracer, + StateOverrides: ðapi.StateOverride{ + randomAccounts[2].addr: ethapi.OverrideAccount{ + Code: newRPCBytes(common.Hex2Bytes("6080604052348015600f57600080fd5b506004361060285760003560e01c80638381f58a14602d575b600080fd5b60336049565b6040518082815260200191505060405180910390f35b6000548156fea2646970667358221220eab35ffa6ab2adfe380772a48b8ba78e82a1b820a18fcb6f59aa4efb20a5f60064736f6c63430007040033")), + StateDiff: newStates([]common.Hash{{}}, []common.Hash{common.BigToHash(big.NewInt(123))}), }, }, }, + want: `{"gas":23347,"failed":false,"returnValue":"000000000000000000000000000000000000000000000000000000000000007b"}`, }, } - for _, testspec := range testSuite { - result, err := api.TraceBlockByNumber(context.Background(), testspec.blockNumber, testspec.config) - if testspec.expectErr != nil { + for i, tc := range testSuite { + result, err := api.TraceCall(context.Background(), tc.call, rpc.BlockNumberOrHash{BlockNumber: &tc.blockNumber}, tc.config) + if tc.expectErr != nil { if err == nil { - t.Errorf("Expect error %v, get nothing", testspec.expectErr) + t.Errorf("test %d: want error %v, have nothing", i, tc.expectErr) continue } - if !reflect.DeepEqual(err, testspec.expectErr) { - t.Errorf("Error mismatch, want %v, get %v", testspec.expectErr, err) - } - } else { - if err != nil { - t.Errorf("Expect no error, get %v", err) - continue - } - if !reflect.DeepEqual(result, testspec.expect) { - t.Errorf("Result mismatch, want %v, get %v", testspec.expect, result) + if !errors.Is(err, tc.expectErr) { + t.Errorf("test %d: error mismatch, want %v, have %v", i, tc.expectErr, err) } + continue + } + if err != nil { + t.Errorf("test %d: want no error, have %v", i, err) + continue + } + // Turn result into res-struct + var ( + have res + want res + ) + resBytes, _ := json.Marshal(result) + json.Unmarshal(resBytes, &have) + json.Unmarshal([]byte(tc.want), &want) + if !reflect.DeepEqual(have, want) { + t.Errorf("test %d, result mismatch, have\n%v\n, want\n%v\n", i, string(resBytes), want) } } } @@ -617,11 +563,6 @@ func newRPCBalance(balance *big.Int) **hexutil.Big { return &rpcBalance } -func newRPCUint64(number uint64) *hexutil.Uint64 { - rpcUint64 := hexutil.Uint64(number) - return &rpcUint64 -} - func newRPCBytes(bytes []byte) *hexutil.Bytes { rpcBytes := hexutil.Bytes(bytes) return &rpcBytes diff --git a/eth/tracers/testing/calltrace_test.go b/eth/tracers/internal/tracetest/calltrace_test.go similarity index 56% rename from eth/tracers/testing/calltrace_test.go rename to eth/tracers/internal/tracetest/calltrace_test.go index 03db904f4..7521a98f2 100644 --- a/eth/tracers/testing/calltrace_test.go +++ b/eth/tracers/internal/tracetest/calltrace_test.go @@ -1,4 +1,20 @@ -package testing +// Copyright 2021 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 . + +package tracetest import ( "encoding/json" @@ -17,14 +33,67 @@ import ( "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/eth/tracers" + "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/tests" - // Force-load the native, to trigger registration + // Force-load native and js pacakges, to trigger registration + _ "github.com/ethereum/go-ethereum/eth/tracers/js" _ "github.com/ethereum/go-ethereum/eth/tracers/native" ) +// To generate a new callTracer test, copy paste the makeTest method below into +// a Geth console and call it with a transaction hash you which to export. + +/* +// makeTest generates a callTracer test by running a prestate reassembled and a +// call trace run, assembling all the gathered information into a test case. +var makeTest = function(tx, rewind) { + // Generate the genesis block from the block, transaction and prestate data + var block = eth.getBlock(eth.getTransaction(tx).blockHash); + var genesis = eth.getBlock(block.parentHash); + + delete genesis.gasUsed; + delete genesis.logsBloom; + delete genesis.parentHash; + delete genesis.receiptsRoot; + delete genesis.sha3Uncles; + delete genesis.size; + delete genesis.transactions; + delete genesis.transactionsRoot; + delete genesis.uncles; + + genesis.gasLimit = genesis.gasLimit.toString(); + genesis.number = genesis.number.toString(); + genesis.timestamp = genesis.timestamp.toString(); + + genesis.alloc = debug.traceTransaction(tx, {tracer: "prestateTracer", rewind: rewind}); + for (var key in genesis.alloc) { + genesis.alloc[key].nonce = genesis.alloc[key].nonce.toString(); + } + genesis.config = admin.nodeInfo.protocols.eth.config; + + // Generate the call trace and produce the test input + var result = debug.traceTransaction(tx, {tracer: "callTracer", rewind: rewind}); + delete result.time; + + console.log(JSON.stringify({ + genesis: genesis, + context: { + number: block.number.toString(), + difficulty: block.difficulty, + timestamp: block.timestamp.toString(), + gasLimit: block.gasLimit.toString(), + miner: block.miner, + }, + input: eth.getRawTransaction(tx), + result: result, + }, null, 2)); +} +*/ + type callContext struct { Number math.HexOrDecimal64 `json:"number"` Difficulty *math.HexOrDecimal256 `json:"difficulty"` @@ -70,7 +139,7 @@ func TestCallTracerNative(t *testing.T) { } func testCallTracer(tracerName string, dirPath string, t *testing.T) { - files, err := ioutil.ReadDir(filepath.Join("..", "testdata", dirPath)) + files, err := ioutil.ReadDir(filepath.Join("testdata", dirPath)) if err != nil { t.Fatalf("failed to retrieve tracer test suite: %v", err) } @@ -87,7 +156,7 @@ func testCallTracer(tracerName string, dirPath string, t *testing.T) { tx = new(types.Transaction) ) // Call tracer test found, read if from disk - if blob, err := ioutil.ReadFile(filepath.Join("..", "testdata", dirPath, file.Name())); err != nil { + if blob, err := ioutil.ReadFile(filepath.Join("testdata", dirPath, file.Name())); err != nil { t.Fatalf("failed to read testcase: %v", err) } else if err := json.Unmarshal(blob, test); err != nil { t.Fatalf("failed to parse testcase: %v", err) @@ -175,7 +244,7 @@ func camel(str string) string { return strings.Join(pieces, "") } func BenchmarkTracers(b *testing.B) { - files, err := ioutil.ReadDir(filepath.Join("..", "testdata", "call_tracer")) + files, err := ioutil.ReadDir(filepath.Join("testdata", "call_tracer")) if err != nil { b.Fatalf("failed to retrieve tracer test suite: %v", err) } @@ -185,7 +254,7 @@ func BenchmarkTracers(b *testing.B) { } file := file // capture range variable b.Run(camel(strings.TrimSuffix(file.Name(), ".json")), func(b *testing.B) { - blob, err := ioutil.ReadFile(filepath.Join("..", "testdata", "call_tracer", file.Name())) + blob, err := ioutil.ReadFile(filepath.Join("testdata", "call_tracer", file.Name())) if err != nil { b.Fatalf("failed to read testcase: %v", err) } @@ -244,3 +313,82 @@ func benchTracer(tracerName string, test *callTracerTest, b *testing.B) { statedb.RevertToSnapshot(snap) } } + +// TestZeroValueToNotExitCall tests the calltracer(s) on the following: +// Tx to A, A calls B with zero value. B does not already exist. +// Expected: that enter/exit is invoked and the inner call is shown in the result +func TestZeroValueToNotExitCall(t *testing.T) { + var to = common.HexToAddress("0x00000000000000000000000000000000deadbeef") + privkey, err := crypto.HexToECDSA("0000000000000000deadbeef00000000000000000000000000000000deadbeef") + if err != nil { + t.Fatalf("err %v", err) + } + signer := types.NewEIP155Signer(big.NewInt(1)) + tx, err := types.SignNewTx(privkey, signer, &types.LegacyTx{ + GasPrice: big.NewInt(0), + Gas: 50000, + To: &to, + }) + if err != nil { + t.Fatalf("err %v", err) + } + origin, _ := signer.Sender(tx) + txContext := vm.TxContext{ + Origin: origin, + GasPrice: big.NewInt(1), + } + context := vm.BlockContext{ + CanTransfer: core.CanTransfer, + Transfer: core.Transfer, + Coinbase: common.Address{}, + BlockNumber: new(big.Int).SetUint64(8000000), + Time: new(big.Int).SetUint64(5), + Difficulty: big.NewInt(0x30000), + GasLimit: uint64(6000000), + } + var code = []byte{ + byte(vm.PUSH1), 0x0, byte(vm.DUP1), byte(vm.DUP1), byte(vm.DUP1), // in and outs zero + byte(vm.DUP1), byte(vm.PUSH1), 0xff, byte(vm.GAS), // value=0,address=0xff, gas=GAS + byte(vm.CALL), + } + var alloc = core.GenesisAlloc{ + to: core.GenesisAccount{ + Nonce: 1, + Code: code, + }, + origin: core.GenesisAccount{ + Nonce: 0, + Balance: big.NewInt(500000000000000), + }, + } + _, statedb := tests.MakePreState(rawdb.NewMemoryDatabase(), alloc, false) + // Create the tracer, the EVM environment and run it + tracer, err := tracers.New("callTracer", nil) + if err != nil { + t.Fatalf("failed to create call tracer: %v", err) + } + evm := vm.NewEVM(context, txContext, statedb, params.MainnetChainConfig, vm.Config{Debug: true, Tracer: tracer}) + msg, err := tx.AsMessage(signer, nil) + if err != nil { + t.Fatalf("failed to prepare transaction for tracing: %v", err) + } + st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas())) + if _, err = st.TransitionDb(); err != nil { + t.Fatalf("failed to execute transaction: %v", err) + } + // Retrieve the trace result and compare against the etalon + res, err := tracer.GetResult() + if err != nil { + t.Fatalf("failed to retrieve trace result: %v", err) + } + have := new(callTrace) + if err := json.Unmarshal(res, have); err != nil { + t.Fatalf("failed to unmarshal trace result: %v", err) + } + wantStr := `{"type":"CALL","from":"0x682a80a6f560eec50d54e63cbeda1c324c5f8d1b","to":"0x00000000000000000000000000000000deadbeef","value":"0x0","gas":"0x7148","gasUsed":"0x2d0","input":"0x","output":"0x","calls":[{"type":"CALL","from":"0x00000000000000000000000000000000deadbeef","to":"0x00000000000000000000000000000000000000ff","value":"0x0","gas":"0x6cbf","gasUsed":"0x0","input":"0x","output":"0x"}]}` + want := new(callTrace) + json.Unmarshal([]byte(wantStr), want) + if !jsonEqual(have, want) { + t.Error("have != want") + } +} diff --git a/eth/tracers/testdata/call_tracer/create.json b/eth/tracers/internal/tracetest/testdata/call_tracer/create.json similarity index 100% rename from eth/tracers/testdata/call_tracer/create.json rename to eth/tracers/internal/tracetest/testdata/call_tracer/create.json diff --git a/eth/tracers/testdata/call_tracer/deep_calls.json b/eth/tracers/internal/tracetest/testdata/call_tracer/deep_calls.json similarity index 100% rename from eth/tracers/testdata/call_tracer/deep_calls.json rename to eth/tracers/internal/tracetest/testdata/call_tracer/deep_calls.json diff --git a/eth/tracers/testdata/call_tracer/delegatecall.json b/eth/tracers/internal/tracetest/testdata/call_tracer/delegatecall.json similarity index 100% rename from eth/tracers/testdata/call_tracer/delegatecall.json rename to eth/tracers/internal/tracetest/testdata/call_tracer/delegatecall.json diff --git a/eth/tracers/testdata/call_tracer/inner_create_oog_outer_throw.json b/eth/tracers/internal/tracetest/testdata/call_tracer/inner_create_oog_outer_throw.json similarity index 100% rename from eth/tracers/testdata/call_tracer/inner_create_oog_outer_throw.json rename to eth/tracers/internal/tracetest/testdata/call_tracer/inner_create_oog_outer_throw.json diff --git a/eth/tracers/testdata/call_tracer/inner_instafail.json b/eth/tracers/internal/tracetest/testdata/call_tracer/inner_instafail.json similarity index 100% rename from eth/tracers/testdata/call_tracer/inner_instafail.json rename to eth/tracers/internal/tracetest/testdata/call_tracer/inner_instafail.json diff --git a/eth/tracers/testdata/call_tracer/inner_throw_outer_revert.json b/eth/tracers/internal/tracetest/testdata/call_tracer/inner_throw_outer_revert.json similarity index 100% rename from eth/tracers/testdata/call_tracer/inner_throw_outer_revert.json rename to eth/tracers/internal/tracetest/testdata/call_tracer/inner_throw_outer_revert.json diff --git a/eth/tracers/testdata/call_tracer/oog.json b/eth/tracers/internal/tracetest/testdata/call_tracer/oog.json similarity index 100% rename from eth/tracers/testdata/call_tracer/oog.json rename to eth/tracers/internal/tracetest/testdata/call_tracer/oog.json diff --git a/eth/tracers/testdata/call_tracer/revert.json b/eth/tracers/internal/tracetest/testdata/call_tracer/revert.json similarity index 100% rename from eth/tracers/testdata/call_tracer/revert.json rename to eth/tracers/internal/tracetest/testdata/call_tracer/revert.json diff --git a/eth/tracers/testdata/call_tracer/revert_reason.json b/eth/tracers/internal/tracetest/testdata/call_tracer/revert_reason.json similarity index 100% rename from eth/tracers/testdata/call_tracer/revert_reason.json rename to eth/tracers/internal/tracetest/testdata/call_tracer/revert_reason.json diff --git a/eth/tracers/testdata/call_tracer/selfdestruct.json b/eth/tracers/internal/tracetest/testdata/call_tracer/selfdestruct.json similarity index 100% rename from eth/tracers/testdata/call_tracer/selfdestruct.json rename to eth/tracers/internal/tracetest/testdata/call_tracer/selfdestruct.json diff --git a/eth/tracers/testdata/call_tracer/simple.json b/eth/tracers/internal/tracetest/testdata/call_tracer/simple.json similarity index 100% rename from eth/tracers/testdata/call_tracer/simple.json rename to eth/tracers/internal/tracetest/testdata/call_tracer/simple.json diff --git a/eth/tracers/testdata/call_tracer/throw.json b/eth/tracers/internal/tracetest/testdata/call_tracer/throw.json similarity index 100% rename from eth/tracers/testdata/call_tracer/throw.json rename to eth/tracers/internal/tracetest/testdata/call_tracer/throw.json diff --git a/eth/tracers/testdata/call_tracer_legacy/create.json b/eth/tracers/internal/tracetest/testdata/call_tracer_legacy/create.json similarity index 100% rename from eth/tracers/testdata/call_tracer_legacy/create.json rename to eth/tracers/internal/tracetest/testdata/call_tracer_legacy/create.json diff --git a/eth/tracers/testdata/call_tracer_legacy/deep_calls.json b/eth/tracers/internal/tracetest/testdata/call_tracer_legacy/deep_calls.json similarity index 100% rename from eth/tracers/testdata/call_tracer_legacy/deep_calls.json rename to eth/tracers/internal/tracetest/testdata/call_tracer_legacy/deep_calls.json diff --git a/eth/tracers/testdata/call_tracer_legacy/delegatecall.json b/eth/tracers/internal/tracetest/testdata/call_tracer_legacy/delegatecall.json similarity index 100% rename from eth/tracers/testdata/call_tracer_legacy/delegatecall.json rename to eth/tracers/internal/tracetest/testdata/call_tracer_legacy/delegatecall.json diff --git a/eth/tracers/testdata/call_tracer_legacy/inner_create_oog_outer_throw.json b/eth/tracers/internal/tracetest/testdata/call_tracer_legacy/inner_create_oog_outer_throw.json similarity index 100% rename from eth/tracers/testdata/call_tracer_legacy/inner_create_oog_outer_throw.json rename to eth/tracers/internal/tracetest/testdata/call_tracer_legacy/inner_create_oog_outer_throw.json diff --git a/eth/tracers/testdata/call_tracer_legacy/inner_instafail.json b/eth/tracers/internal/tracetest/testdata/call_tracer_legacy/inner_instafail.json similarity index 100% rename from eth/tracers/testdata/call_tracer_legacy/inner_instafail.json rename to eth/tracers/internal/tracetest/testdata/call_tracer_legacy/inner_instafail.json diff --git a/eth/tracers/testdata/call_tracer_legacy/inner_throw_outer_revert.json b/eth/tracers/internal/tracetest/testdata/call_tracer_legacy/inner_throw_outer_revert.json similarity index 100% rename from eth/tracers/testdata/call_tracer_legacy/inner_throw_outer_revert.json rename to eth/tracers/internal/tracetest/testdata/call_tracer_legacy/inner_throw_outer_revert.json diff --git a/eth/tracers/testdata/call_tracer_legacy/oog.json b/eth/tracers/internal/tracetest/testdata/call_tracer_legacy/oog.json similarity index 100% rename from eth/tracers/testdata/call_tracer_legacy/oog.json rename to eth/tracers/internal/tracetest/testdata/call_tracer_legacy/oog.json diff --git a/eth/tracers/testdata/call_tracer_legacy/revert.json b/eth/tracers/internal/tracetest/testdata/call_tracer_legacy/revert.json similarity index 100% rename from eth/tracers/testdata/call_tracer_legacy/revert.json rename to eth/tracers/internal/tracetest/testdata/call_tracer_legacy/revert.json diff --git a/eth/tracers/testdata/call_tracer_legacy/revert_reason.json b/eth/tracers/internal/tracetest/testdata/call_tracer_legacy/revert_reason.json similarity index 100% rename from eth/tracers/testdata/call_tracer_legacy/revert_reason.json rename to eth/tracers/internal/tracetest/testdata/call_tracer_legacy/revert_reason.json diff --git a/eth/tracers/testdata/call_tracer_legacy/selfdestruct.json b/eth/tracers/internal/tracetest/testdata/call_tracer_legacy/selfdestruct.json similarity index 100% rename from eth/tracers/testdata/call_tracer_legacy/selfdestruct.json rename to eth/tracers/internal/tracetest/testdata/call_tracer_legacy/selfdestruct.json diff --git a/eth/tracers/testdata/call_tracer_legacy/simple.json b/eth/tracers/internal/tracetest/testdata/call_tracer_legacy/simple.json similarity index 100% rename from eth/tracers/testdata/call_tracer_legacy/simple.json rename to eth/tracers/internal/tracetest/testdata/call_tracer_legacy/simple.json diff --git a/eth/tracers/testdata/call_tracer_legacy/throw.json b/eth/tracers/internal/tracetest/testdata/call_tracer_legacy/throw.json similarity index 100% rename from eth/tracers/testdata/call_tracer_legacy/throw.json rename to eth/tracers/internal/tracetest/testdata/call_tracer_legacy/throw.json diff --git a/eth/tracers/tracer.go b/eth/tracers/js/bigint.go similarity index 50% rename from eth/tracers/tracer.go rename to eth/tracers/js/bigint.go index 4fee7ed96..9aeb33042 100644 --- a/eth/tracers/tracer.go +++ b/eth/tracers/js/bigint.go @@ -1,4 +1,4 @@ -// Copyright 2017 The go-ethereum Authors +// Copyright 2021 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 @@ -14,845 +14,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -package tracers - -import ( - "encoding/json" - "errors" - "fmt" - "math/big" - "sync/atomic" - "time" - "unsafe" - - "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/vm" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/log" - "gopkg.in/olebedev/go-duktape.v3" -) +package js // bigIntegerJS is the minified version of https://github.com/peterolson/BigInteger.js. const bigIntegerJS = `var bigInt=function(undefined){"use strict";var BASE=1e7,LOG_BASE=7,MAX_INT=9007199254740992,MAX_INT_ARR=smallToArray(MAX_INT),LOG_MAX_INT=Math.log(MAX_INT);function Integer(v,radix){if(typeof v==="undefined")return Integer[0];if(typeof radix!=="undefined")return+radix===10?parseValue(v):parseBase(v,radix);return parseValue(v)}function BigInteger(value,sign){this.value=value;this.sign=sign;this.isSmall=false}BigInteger.prototype=Object.create(Integer.prototype);function SmallInteger(value){this.value=value;this.sign=value<0;this.isSmall=true}SmallInteger.prototype=Object.create(Integer.prototype);function isPrecise(n){return-MAX_INT0)return Math.floor(n);return Math.ceil(n)}function add(a,b){var l_a=a.length,l_b=b.length,r=new Array(l_a),carry=0,base=BASE,sum,i;for(i=0;i=base?1:0;r[i]=sum-carry*base}while(i0)r.push(carry);return r}function addAny(a,b){if(a.length>=b.length)return add(a,b);return add(b,a)}function addSmall(a,carry){var l=a.length,r=new Array(l),base=BASE,sum,i;for(i=0;i0){r[i++]=carry%base;carry=Math.floor(carry/base)}return r}BigInteger.prototype.add=function(v){var n=parseValue(v);if(this.sign!==n.sign){return this.subtract(n.negate())}var a=this.value,b=n.value;if(n.isSmall){return new BigInteger(addSmall(a,Math.abs(b)),this.sign)}return new BigInteger(addAny(a,b),this.sign)};BigInteger.prototype.plus=BigInteger.prototype.add;SmallInteger.prototype.add=function(v){var n=parseValue(v);var a=this.value;if(a<0!==n.sign){return this.subtract(n.negate())}var b=n.value;if(n.isSmall){if(isPrecise(a+b))return new SmallInteger(a+b);b=smallToArray(Math.abs(b))}return new BigInteger(addSmall(b,Math.abs(a)),a<0)};SmallInteger.prototype.plus=SmallInteger.prototype.add;function subtract(a,b){var a_l=a.length,b_l=b.length,r=new Array(a_l),borrow=0,base=BASE,i,difference;for(i=0;i=0){value=subtract(a,b)}else{value=subtract(b,a);sign=!sign}value=arrayToSmall(value);if(typeof value==="number"){if(sign)value=-value;return new SmallInteger(value)}return new BigInteger(value,sign)}function subtractSmall(a,b,sign){var l=a.length,r=new Array(l),carry=-b,base=BASE,i,difference;for(i=0;i=0)};SmallInteger.prototype.minus=SmallInteger.prototype.subtract;BigInteger.prototype.negate=function(){return new BigInteger(this.value,!this.sign)};SmallInteger.prototype.negate=function(){var sign=this.sign;var small=new SmallInteger(-this.value);small.sign=!sign;return small};BigInteger.prototype.abs=function(){return new BigInteger(this.value,false)};SmallInteger.prototype.abs=function(){return new SmallInteger(Math.abs(this.value))};function multiplyLong(a,b){var a_l=a.length,b_l=b.length,l=a_l+b_l,r=createArray(l),base=BASE,product,carry,i,a_i,b_j;for(i=0;i0){r[i++]=carry%base;carry=Math.floor(carry/base)}return r}function shiftLeft(x,n){var r=[];while(n-- >0)r.push(0);return r.concat(x)}function multiplyKaratsuba(x,y){var n=Math.max(x.length,y.length);if(n<=30)return multiplyLong(x,y);n=Math.ceil(n/2);var b=x.slice(n),a=x.slice(0,n),d=y.slice(n),c=y.slice(0,n);var ac=multiplyKaratsuba(a,c),bd=multiplyKaratsuba(b,d),abcd=multiplyKaratsuba(addAny(a,b),addAny(c,d));var product=addAny(addAny(ac,shiftLeft(subtract(subtract(abcd,ac),bd),n)),shiftLeft(bd,2*n));trim(product);return product}function useKaratsuba(l1,l2){return-.012*l1-.012*l2+15e-6*l1*l2>0}BigInteger.prototype.multiply=function(v){var n=parseValue(v),a=this.value,b=n.value,sign=this.sign!==n.sign,abs;if(n.isSmall){if(b===0)return Integer[0];if(b===1)return this;if(b===-1)return this.negate();abs=Math.abs(b);if(abs=0;shift--){quotientDigit=base-1;if(remainder[shift+b_l]!==divisorMostSignificantDigit){quotientDigit=Math.floor((remainder[shift+b_l]*base+remainder[shift+b_l-1])/divisorMostSignificantDigit)}carry=0;borrow=0;l=divisor.length;for(i=0;ib_l){highx=(highx+1)*base}guess=Math.ceil(highx/highy);do{check=multiplySmall(b,guess);if(compareAbs(check,part)<=0)break;guess--}while(guess);result.push(guess);part=subtract(part,check)}result.reverse();return[arrayToSmall(result),arrayToSmall(part)]}function divModSmall(value,lambda){var length=value.length,quotient=createArray(length),base=BASE,i,q,remainder,divisor;remainder=0;for(i=length-1;i>=0;--i){divisor=remainder*base+value[i];q=truncate(divisor/lambda);remainder=divisor-q*lambda;quotient[i]=q|0}return[quotient,remainder|0]}function divModAny(self,v){var value,n=parseValue(v);var a=self.value,b=n.value;var quotient;if(b===0)throw new Error("Cannot divide by zero");if(self.isSmall){if(n.isSmall){return[new SmallInteger(truncate(a/b)),new SmallInteger(a%b)]}return[Integer[0],self]}if(n.isSmall){if(b===1)return[self,Integer[0]];if(b==-1)return[self.negate(),Integer[0]];var abs=Math.abs(b);if(absb.length?1:-1}for(var i=a.length-1;i>=0;i--){if(a[i]!==b[i])return a[i]>b[i]?1:-1}return 0}BigInteger.prototype.compareAbs=function(v){var n=parseValue(v),a=this.value,b=n.value;if(n.isSmall)return 1;return compareAbs(a,b)};SmallInteger.prototype.compareAbs=function(v){var n=parseValue(v),a=Math.abs(this.value),b=n.value;if(n.isSmall){b=Math.abs(b);return a===b?0:a>b?1:-1}return-1};BigInteger.prototype.compare=function(v){if(v===Infinity){return-1}if(v===-Infinity){return 1}var n=parseValue(v),a=this.value,b=n.value;if(this.sign!==n.sign){return n.sign?1:-1}if(n.isSmall){return this.sign?-1:1}return compareAbs(a,b)*(this.sign?-1:1)};BigInteger.prototype.compareTo=BigInteger.prototype.compare;SmallInteger.prototype.compare=function(v){if(v===Infinity){return-1}if(v===-Infinity){return 1}var n=parseValue(v),a=this.value,b=n.value;if(n.isSmall){return a==b?0:a>b?1:-1}if(a<0!==n.sign){return a<0?-1:1}return a<0?1:-1};SmallInteger.prototype.compareTo=SmallInteger.prototype.compare;BigInteger.prototype.equals=function(v){return this.compare(v)===0};SmallInteger.prototype.eq=SmallInteger.prototype.equals=BigInteger.prototype.eq=BigInteger.prototype.equals;BigInteger.prototype.notEquals=function(v){return this.compare(v)!==0};SmallInteger.prototype.neq=SmallInteger.prototype.notEquals=BigInteger.prototype.neq=BigInteger.prototype.notEquals;BigInteger.prototype.greater=function(v){return this.compare(v)>0};SmallInteger.prototype.gt=SmallInteger.prototype.greater=BigInteger.prototype.gt=BigInteger.prototype.greater;BigInteger.prototype.lesser=function(v){return this.compare(v)<0};SmallInteger.prototype.lt=SmallInteger.prototype.lesser=BigInteger.prototype.lt=BigInteger.prototype.lesser;BigInteger.prototype.greaterOrEquals=function(v){return this.compare(v)>=0};SmallInteger.prototype.geq=SmallInteger.prototype.greaterOrEquals=BigInteger.prototype.geq=BigInteger.prototype.greaterOrEquals;BigInteger.prototype.lesserOrEquals=function(v){return this.compare(v)<=0};SmallInteger.prototype.leq=SmallInteger.prototype.lesserOrEquals=BigInteger.prototype.leq=BigInteger.prototype.lesserOrEquals;BigInteger.prototype.isEven=function(){return(this.value[0]&1)===0};SmallInteger.prototype.isEven=function(){return(this.value&1)===0};BigInteger.prototype.isOdd=function(){return(this.value[0]&1)===1};SmallInteger.prototype.isOdd=function(){return(this.value&1)===1};BigInteger.prototype.isPositive=function(){return!this.sign};SmallInteger.prototype.isPositive=function(){return this.value>0};BigInteger.prototype.isNegative=function(){return this.sign};SmallInteger.prototype.isNegative=function(){return this.value<0};BigInteger.prototype.isUnit=function(){return false};SmallInteger.prototype.isUnit=function(){return Math.abs(this.value)===1};BigInteger.prototype.isZero=function(){return false};SmallInteger.prototype.isZero=function(){return this.value===0};BigInteger.prototype.isDivisibleBy=function(v){var n=parseValue(v);var value=n.value;if(value===0)return false;if(value===1)return true;if(value===2)return this.isEven();return this.mod(n).equals(Integer[0])};SmallInteger.prototype.isDivisibleBy=BigInteger.prototype.isDivisibleBy;function isBasicPrime(v){var n=v.abs();if(n.isUnit())return false;if(n.equals(2)||n.equals(3)||n.equals(5))return true;if(n.isEven()||n.isDivisibleBy(3)||n.isDivisibleBy(5))return false;if(n.lesser(25))return true}BigInteger.prototype.isPrime=function(){var isPrime=isBasicPrime(this);if(isPrime!==undefined)return isPrime;var n=this.abs(),nPrev=n.prev();var a=[2,3,5,7,11,13,17,19],b=nPrev,d,t,i,x;while(b.isEven())b=b.divide(2);for(i=0;i-MAX_INT)return new SmallInteger(value-1);return new BigInteger(MAX_INT_ARR,true)};var powersOfTwo=[1];while(2*powersOfTwo[powersOfTwo.length-1]<=BASE)powersOfTwo.push(2*powersOfTwo[powersOfTwo.length-1]);var powers2Length=powersOfTwo.length,highestPower2=powersOfTwo[powers2Length-1];function shift_isSmall(n){return(typeof n==="number"||typeof n==="string")&&+Math.abs(n)<=BASE||n instanceof BigInteger&&n.value.length<=1}BigInteger.prototype.shiftLeft=function(n){if(!shift_isSmall(n)){throw new Error(String(n)+" is too large for shifting.")}n=+n;if(n<0)return this.shiftRight(-n);var result=this;while(n>=powers2Length){result=result.multiply(highestPower2);n-=powers2Length-1}return result.multiply(powersOfTwo[n])};SmallInteger.prototype.shiftLeft=BigInteger.prototype.shiftLeft;BigInteger.prototype.shiftRight=function(n){var remQuo;if(!shift_isSmall(n)){throw new Error(String(n)+" is too large for shifting.")}n=+n;if(n<0)return this.shiftLeft(-n);var result=this;while(n>=powers2Length){if(result.isZero())return result;remQuo=divModAny(result,highestPower2);result=remQuo[1].isNegative()?remQuo[0].prev():remQuo[0];n-=powers2Length-1}remQuo=divModAny(result,powersOfTwo[n]);return remQuo[1].isNegative()?remQuo[0].prev():remQuo[0]};SmallInteger.prototype.shiftRight=BigInteger.prototype.shiftRight;function bitwise(x,y,fn){y=parseValue(y);var xSign=x.isNegative(),ySign=y.isNegative();var xRem=xSign?x.not():x,yRem=ySign?y.not():y;var xDigit=0,yDigit=0;var xDivMod=null,yDivMod=null;var result=[];while(!xRem.isZero()||!yRem.isZero()){xDivMod=divModAny(xRem,highestPower2);xDigit=xDivMod[1].toJSNumber();if(xSign){xDigit=highestPower2-1-xDigit}yDivMod=divModAny(yRem,highestPower2);yDigit=yDivMod[1].toJSNumber();if(ySign){yDigit=highestPower2-1-yDigit}xRem=xDivMod[0];yRem=yDivMod[0];result.push(fn(xDigit,yDigit))}var sum=fn(xSign?1:0,ySign?1:0)!==0?bigInt(-1):bigInt(0);for(var i=result.length-1;i>=0;i-=1){sum=sum.multiply(highestPower2).add(bigInt(result[i]))}return sum}BigInteger.prototype.not=function(){return this.negate().prev()};SmallInteger.prototype.not=BigInteger.prototype.not;BigInteger.prototype.and=function(n){return bitwise(this,n,function(a,b){return a&b})};SmallInteger.prototype.and=BigInteger.prototype.and;BigInteger.prototype.or=function(n){return bitwise(this,n,function(a,b){return a|b})};SmallInteger.prototype.or=BigInteger.prototype.or;BigInteger.prototype.xor=function(n){return bitwise(this,n,function(a,b){return a^b})};SmallInteger.prototype.xor=BigInteger.prototype.xor;var LOBMASK_I=1<<30,LOBMASK_BI=(BASE&-BASE)*(BASE&-BASE)|LOBMASK_I;function roughLOB(n){var v=n.value,x=typeof v==="number"?v|LOBMASK_I:v[0]+v[1]*BASE|LOBMASK_BI;return x&-x}function max(a,b){a=parseValue(a);b=parseValue(b);return a.greater(b)?a:b}function min(a,b){a=parseValue(a);b=parseValue(b);return a.lesser(b)?a:b}function gcd(a,b){a=parseValue(a).abs();b=parseValue(b).abs();if(a.equals(b))return a;if(a.isZero())return b;if(b.isZero())return a;var c=Integer[1],d,t;while(a.isEven()&&b.isEven()){d=Math.min(roughLOB(a),roughLOB(b));a=a.divide(d);b=b.divide(d);c=c.multiply(d)}while(a.isEven()){a=a.divide(roughLOB(a))}do{while(b.isEven()){b=b.divide(roughLOB(b))}if(a.greater(b)){t=b;b=a;a=t}b=b.subtract(a)}while(!b.isZero());return c.isUnit()?a:a.multiply(c)}function lcm(a,b){a=parseValue(a).abs();b=parseValue(b).abs();return a.divide(gcd(a,b)).multiply(b)}function randBetween(a,b){a=parseValue(a);b=parseValue(b);var low=min(a,b),high=max(a,b);var range=high.subtract(low).add(1);if(range.isSmall)return low.add(Math.floor(Math.random()*range));var length=range.value.length-1;var result=[],restricted=true;for(var i=length;i>=0;i--){var top=restricted?range.value[i]:BASE;var digit=truncate(Math.random()*top);result.unshift(digit);if(digit=absBase){if(c==="1"&&absBase===1)continue;throw new Error(c+" is not a valid digit in base "+base+".")}else if(c.charCodeAt(0)-87>=absBase){throw new Error(c+" is not a valid digit in base "+base+".")}}}if(2<=base&&base<=36){if(length<=LOG_MAX_INT/Math.log(base)){var result=parseInt(text,base);if(isNaN(result)){throw new Error(c+" is not a valid digit in base "+base+".")}return new SmallInteger(parseInt(text,base))}}base=parseValue(base);var digits=[];var isNegative=text[0]==="-";for(i=isNegative?1:0;i");digits.push(parseValue(text.slice(start+1,i)))}else throw new Error(c+" is not a valid character")}return parseBaseFromArray(digits,base,isNegative)};function parseBaseFromArray(digits,base,isNegative){var val=Integer[0],pow=Integer[1],i;for(i=digits.length-1;i>=0;i--){val=val.add(digits[i].times(pow));pow=pow.times(base)}return isNegative?val.negate():val}function stringify(digit){var v=digit.value;if(typeof v==="number")v=[v];if(v.length===1&&v[0]<=35){return"0123456789abcdefghijklmnopqrstuvwxyz".charAt(v[0])}return"<"+v+">"}function toBase(n,base){base=bigInt(base);if(base.isZero()){if(n.isZero())return"0";throw new Error("Cannot convert nonzero numbers to base 0.")}if(base.equals(-1)){if(n.isZero())return"0";if(n.isNegative())return new Array(1-n).join("10");return"1"+new Array(+n).join("01")}var minusSign="";if(n.isNegative()&&base.isPositive()){minusSign="-";n=n.abs()}if(base.equals(1)){if(n.isZero())return"0";return minusSign+new Array(+n+1).join(1)}var out=[];var left=n,divmod;while(left.isNegative()||left.compareAbs(base)>=0){divmod=left.divmod(base);left=divmod.quotient;var digit=divmod.remainder;if(digit.isNegative()){digit=base.minus(digit).abs();left=left.next()}out.push(stringify(digit))}out.push(stringify(left));return minusSign+out.reverse().join("")}BigInteger.prototype.toString=function(radix){if(radix===undefined)radix=10;if(radix!==10)return toBase(this,radix);var v=this.value,l=v.length,str=String(v[--l]),zeros="0000000",digit;while(--l>=0){digit=String(v[l]);str+=zeros.slice(digit.length)+digit}var sign=this.sign?"-":"";return sign+str};SmallInteger.prototype.toString=function(radix){if(radix===undefined)radix=10;if(radix!=10)return toBase(this,radix);return String(this.value)};BigInteger.prototype.toJSON=SmallInteger.prototype.toJSON=function(){return this.toString()};BigInteger.prototype.valueOf=function(){return+this.toString()};BigInteger.prototype.toJSNumber=BigInteger.prototype.valueOf;SmallInteger.prototype.valueOf=function(){return this.value};SmallInteger.prototype.toJSNumber=SmallInteger.prototype.valueOf;function parseStringValue(v){if(isPrecise(+v)){var x=+v;if(x===truncate(x))return new SmallInteger(x);throw"Invalid integer: "+v}var sign=v[0]==="-";if(sign)v=v.slice(1);var split=v.split(/e/i);if(split.length>2)throw new Error("Invalid integer: "+split.join("e"));if(split.length===2){var exp=split[1];if(exp[0]==="+")exp=exp.slice(1);exp=+exp;if(exp!==truncate(exp)||!isPrecise(exp))throw new Error("Invalid integer: "+exp+" is not a valid exponent.");var text=split[0];var decimalPlace=text.indexOf(".");if(decimalPlace>=0){exp-=text.length-decimalPlace-1;text=text.slice(0,decimalPlace)+text.slice(decimalPlace+1)}if(exp<0)throw new Error("Cannot include negative exponent part for integers");text+=new Array(exp+1).join("0");v=text}var isValid=/^([0-9][0-9]*)$/.test(v);if(!isValid)throw new Error("Invalid integer: "+v);var r=[],max=v.length,l=LOG_BASE,min=max-l;while(max>0){r.push(+v.slice(min,max));min-=l;if(min<0)min=0;max-=l}trim(r);return new BigInteger(r,sign)}function parseNumberValue(v){if(isPrecise(v)){if(v!==truncate(v))throw new Error(v+" is not an integer.");return new SmallInteger(v)}return parseStringValue(v.toString())}function parseValue(v){if(typeof v==="number"){return parseNumberValue(v)}if(typeof v==="string"){return parseStringValue(v)}return v}for(var i=0;i<1e3;i++){Integer[i]=new SmallInteger(i);if(i>0)Integer[-i]=new SmallInteger(-i)}Integer.one=Integer[1];Integer.zero=Integer[0];Integer.minusOne=Integer[-1];Integer.max=max;Integer.min=min;Integer.gcd=gcd;Integer.lcm=lcm;Integer.isInstance=function(x){return x instanceof BigInteger||x instanceof SmallInteger};Integer.randBetween=randBetween;Integer.fromArray=function(digits,base,isNegative){return parseBaseFromArray(digits.map(parseValue),parseValue(base||10),isNegative)};return Integer}();if(typeof module!=="undefined"&&module.hasOwnProperty("exports")){module.exports=bigInt}if(typeof define==="function"&&define.amd){define("big-integer",[],function(){return bigInt})}; bigInt` - -// makeSlice convert an unsafe memory pointer with the given type into a Go byte -// slice. -// -// Note, the returned slice uses the same memory area as the input arguments. -// If those are duktape stack items, popping them off **will** make the slice -// contents change. -func makeSlice(ptr unsafe.Pointer, size uint) []byte { - var sl = struct { - addr uintptr - len int - cap int - }{uintptr(ptr), int(size), int(size)} - - return *(*[]byte)(unsafe.Pointer(&sl)) -} - -// popSlice pops a buffer off the JavaScript stack and returns it as a slice. -func popSlice(ctx *duktape.Context) []byte { - blob := common.CopyBytes(makeSlice(ctx.GetBuffer(-1))) - ctx.Pop() - return blob -} - -// pushBigInt create a JavaScript BigInteger in the VM. -func pushBigInt(n *big.Int, ctx *duktape.Context) { - ctx.GetGlobalString("bigInt") - ctx.PushString(n.String()) - ctx.Call(1) -} - -// opWrapper provides a JavaScript wrapper around OpCode. -type opWrapper struct { - op vm.OpCode -} - -// pushObject assembles a JSVM object wrapping a swappable opcode and pushes it -// onto the VM stack. -func (ow *opWrapper) pushObject(vm *duktape.Context) { - obj := vm.PushObject() - - vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushInt(int(ow.op)); return 1 }) - vm.PutPropString(obj, "toNumber") - - vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushString(ow.op.String()); return 1 }) - vm.PutPropString(obj, "toString") - - vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushBoolean(ow.op.IsPush()); return 1 }) - vm.PutPropString(obj, "isPush") -} - -// memoryWrapper provides a JavaScript wrapper around vm.Memory. -type memoryWrapper struct { - memory *vm.Memory -} - -// slice returns the requested range of memory as a byte slice. -func (mw *memoryWrapper) slice(begin, end int64) []byte { - if end == begin { - return []byte{} - } - if end < begin || begin < 0 { - // TODO(karalabe): We can't js-throw from Go inside duktape inside Go. The Go - // runtime goes belly up https://github.com/golang/go/issues/15639. - log.Warn("Tracer accessed out of bound memory", "offset", begin, "end", end) - return nil - } - if mw.memory.Len() < int(end) { - // TODO(karalabe): We can't js-throw from Go inside duktape inside Go. The Go - // runtime goes belly up https://github.com/golang/go/issues/15639. - log.Warn("Tracer accessed out of bound memory", "available", mw.memory.Len(), "offset", begin, "size", end-begin) - return nil - } - return mw.memory.GetCopy(begin, end-begin) -} - -// getUint returns the 32 bytes at the specified address interpreted as a uint. -func (mw *memoryWrapper) getUint(addr int64) *big.Int { - if mw.memory.Len() < int(addr)+32 || addr < 0 { - // TODO(karalabe): We can't js-throw from Go inside duktape inside Go. The Go - // runtime goes belly up https://github.com/golang/go/issues/15639. - log.Warn("Tracer accessed out of bound memory", "available", mw.memory.Len(), "offset", addr, "size", 32) - return new(big.Int) - } - return new(big.Int).SetBytes(mw.memory.GetPtr(addr, 32)) -} - -// pushObject assembles a JSVM object wrapping a swappable memory and pushes it -// onto the VM stack. -func (mw *memoryWrapper) pushObject(vm *duktape.Context) { - obj := vm.PushObject() - - // Generate the `slice` method which takes two ints and returns a buffer - vm.PushGoFunction(func(ctx *duktape.Context) int { - blob := mw.slice(int64(ctx.GetInt(-2)), int64(ctx.GetInt(-1))) - ctx.Pop2() - - ptr := ctx.PushFixedBuffer(len(blob)) - copy(makeSlice(ptr, uint(len(blob))), blob) - return 1 - }) - vm.PutPropString(obj, "slice") - - // Generate the `getUint` method which takes an int and returns a bigint - vm.PushGoFunction(func(ctx *duktape.Context) int { - offset := int64(ctx.GetInt(-1)) - ctx.Pop() - - pushBigInt(mw.getUint(offset), ctx) - return 1 - }) - vm.PutPropString(obj, "getUint") -} - -// stackWrapper provides a JavaScript wrapper around vm.Stack. -type stackWrapper struct { - stack *vm.Stack -} - -// peek returns the nth-from-the-top element of the stack. -func (sw *stackWrapper) peek(idx int) *big.Int { - if len(sw.stack.Data()) <= idx || idx < 0 { - // TODO(karalabe): We can't js-throw from Go inside duktape inside Go. The Go - // runtime goes belly up https://github.com/golang/go/issues/15639. - log.Warn("Tracer accessed out of bound stack", "size", len(sw.stack.Data()), "index", idx) - return new(big.Int) - } - return sw.stack.Back(idx).ToBig() -} - -// pushObject assembles a JSVM object wrapping a swappable stack and pushes it -// onto the VM stack. -func (sw *stackWrapper) pushObject(vm *duktape.Context) { - obj := vm.PushObject() - - vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushInt(len(sw.stack.Data())); return 1 }) - vm.PutPropString(obj, "length") - - // Generate the `peek` method which takes an int and returns a bigint - vm.PushGoFunction(func(ctx *duktape.Context) int { - offset := ctx.GetInt(-1) - ctx.Pop() - - pushBigInt(sw.peek(offset), ctx) - return 1 - }) - vm.PutPropString(obj, "peek") -} - -// dbWrapper provides a JavaScript wrapper around vm.Database. -type dbWrapper struct { - db vm.StateDB -} - -// pushObject assembles a JSVM object wrapping a swappable database and pushes it -// onto the VM stack. -func (dw *dbWrapper) pushObject(vm *duktape.Context) { - obj := vm.PushObject() - - // Push the wrapper for statedb.GetBalance - vm.PushGoFunction(func(ctx *duktape.Context) int { - pushBigInt(dw.db.GetBalance(common.BytesToAddress(popSlice(ctx))), ctx) - return 1 - }) - vm.PutPropString(obj, "getBalance") - - // Push the wrapper for statedb.GetNonce - vm.PushGoFunction(func(ctx *duktape.Context) int { - ctx.PushInt(int(dw.db.GetNonce(common.BytesToAddress(popSlice(ctx))))) - return 1 - }) - vm.PutPropString(obj, "getNonce") - - // Push the wrapper for statedb.GetCode - vm.PushGoFunction(func(ctx *duktape.Context) int { - code := dw.db.GetCode(common.BytesToAddress(popSlice(ctx))) - - ptr := ctx.PushFixedBuffer(len(code)) - copy(makeSlice(ptr, uint(len(code))), code) - return 1 - }) - vm.PutPropString(obj, "getCode") - - // Push the wrapper for statedb.GetState - vm.PushGoFunction(func(ctx *duktape.Context) int { - hash := popSlice(ctx) - addr := popSlice(ctx) - - state := dw.db.GetState(common.BytesToAddress(addr), common.BytesToHash(hash)) - - ptr := ctx.PushFixedBuffer(len(state)) - copy(makeSlice(ptr, uint(len(state))), state[:]) - return 1 - }) - vm.PutPropString(obj, "getState") - - // Push the wrapper for statedb.Exists - vm.PushGoFunction(func(ctx *duktape.Context) int { - ctx.PushBoolean(dw.db.Exist(common.BytesToAddress(popSlice(ctx)))) - return 1 - }) - vm.PutPropString(obj, "exists") -} - -// contractWrapper provides a JavaScript wrapper around vm.Contract -type contractWrapper struct { - contract *vm.Contract -} - -// pushObject assembles a JSVM object wrapping a swappable contract and pushes it -// onto the VM stack. -func (cw *contractWrapper) pushObject(vm *duktape.Context) { - obj := vm.PushObject() - - // Push the wrapper for contract.Caller - vm.PushGoFunction(func(ctx *duktape.Context) int { - ptr := ctx.PushFixedBuffer(20) - copy(makeSlice(ptr, 20), cw.contract.Caller().Bytes()) - return 1 - }) - vm.PutPropString(obj, "getCaller") - - // Push the wrapper for contract.Address - vm.PushGoFunction(func(ctx *duktape.Context) int { - ptr := ctx.PushFixedBuffer(20) - copy(makeSlice(ptr, 20), cw.contract.Address().Bytes()) - return 1 - }) - vm.PutPropString(obj, "getAddress") - - // Push the wrapper for contract.Value - vm.PushGoFunction(func(ctx *duktape.Context) int { - pushBigInt(cw.contract.Value(), ctx) - return 1 - }) - vm.PutPropString(obj, "getValue") - - // Push the wrapper for contract.Input - vm.PushGoFunction(func(ctx *duktape.Context) int { - blob := cw.contract.Input - - ptr := ctx.PushFixedBuffer(len(blob)) - copy(makeSlice(ptr, uint(len(blob))), blob) - return 1 - }) - vm.PutPropString(obj, "getInput") -} - -type frame struct { - typ *string - from *common.Address - to *common.Address - input []byte - gas *uint - value *big.Int -} - -func newFrame() *frame { - return &frame{ - typ: new(string), - from: new(common.Address), - to: new(common.Address), - gas: new(uint), - } -} - -func (f *frame) pushObject(vm *duktape.Context) { - obj := vm.PushObject() - - vm.PushGoFunction(func(ctx *duktape.Context) int { pushValue(ctx, *f.typ); return 1 }) - vm.PutPropString(obj, "getType") - - vm.PushGoFunction(func(ctx *duktape.Context) int { pushValue(ctx, *f.from); return 1 }) - vm.PutPropString(obj, "getFrom") - - vm.PushGoFunction(func(ctx *duktape.Context) int { pushValue(ctx, *f.to); return 1 }) - vm.PutPropString(obj, "getTo") - - vm.PushGoFunction(func(ctx *duktape.Context) int { pushValue(ctx, f.input); return 1 }) - vm.PutPropString(obj, "getInput") - - vm.PushGoFunction(func(ctx *duktape.Context) int { pushValue(ctx, *f.gas); return 1 }) - vm.PutPropString(obj, "getGas") - - vm.PushGoFunction(func(ctx *duktape.Context) int { - if f.value != nil { - pushValue(ctx, f.value) - } else { - ctx.PushUndefined() - } - return 1 - }) - vm.PutPropString(obj, "getValue") -} - -type frameResult struct { - gasUsed *uint - output []byte - errorValue *string -} - -func newFrameResult() *frameResult { - return &frameResult{ - gasUsed: new(uint), - } -} - -func (r *frameResult) pushObject(vm *duktape.Context) { - obj := vm.PushObject() - - vm.PushGoFunction(func(ctx *duktape.Context) int { pushValue(ctx, *r.gasUsed); return 1 }) - vm.PutPropString(obj, "getGasUsed") - - vm.PushGoFunction(func(ctx *duktape.Context) int { pushValue(ctx, r.output); return 1 }) - vm.PutPropString(obj, "getOutput") - - vm.PushGoFunction(func(ctx *duktape.Context) int { - if r.errorValue != nil { - pushValue(ctx, *r.errorValue) - } else { - ctx.PushUndefined() - } - return 1 - }) - vm.PutPropString(obj, "getError") -} - -// jsTracer provides an implementation of Tracer that evaluates a Javascript -// function for each VM execution step. -type jsTracer struct { - vm *duktape.Context // Javascript VM instance - - tracerObject int // Stack index of the tracer JavaScript object - stateObject int // Stack index of the global state to pull arguments from - - opWrapper *opWrapper // Wrapper around the VM opcode - stackWrapper *stackWrapper // Wrapper around the VM stack - memoryWrapper *memoryWrapper // Wrapper around the VM memory - contractWrapper *contractWrapper // Wrapper around the contract object - dbWrapper *dbWrapper // Wrapper around the VM environment - - pcValue *uint // Swappable pc value wrapped by a log accessor - gasValue *uint // Swappable gas value wrapped by a log accessor - costValue *uint // Swappable cost value wrapped by a log accessor - depthValue *uint // Swappable depth value wrapped by a log accessor - errorValue *string // Swappable error value wrapped by a log accessor - refundValue *uint // Swappable refund value wrapped by a log accessor - - frame *frame // Represents entry into call frame. Fields are swappable - frameResult *frameResult // Represents exit from a call frame. Fields are swappable - - ctx map[string]interface{} // Transaction context gathered throughout execution - err error // Error, if one has occurred - - interrupt uint32 // Atomic flag to signal execution interruption - reason error // Textual reason for the interruption - - activePrecompiles []common.Address // Updated on CaptureStart based on given rules - traceSteps bool // When true, will invoke step() on each opcode - traceCallFrames bool // When true, will invoke enter() and exit() js funcs -} - -// Context contains some contextual infos for a transaction execution that is not -// available from within the EVM object. -type Context struct { - BlockHash common.Hash // Hash of the block the tx is contained within (zero if dangling tx or call) - TxIndex int // Index of the transaction within a block (zero if dangling tx or call) - TxHash common.Hash // Hash of the transaction being traced (zero if dangling call) -} - -// New instantiates a new tracer instance. code specifies a Javascript snippet, -// which must evaluate to an expression returning an object with 'step', 'fault' -// and 'result' functions. -func newJsTracer(code string, ctx *Context) (*jsTracer, error) { - tracer := &jsTracer{ - vm: duktape.New(), - ctx: make(map[string]interface{}), - opWrapper: new(opWrapper), - stackWrapper: new(stackWrapper), - memoryWrapper: new(memoryWrapper), - contractWrapper: new(contractWrapper), - dbWrapper: new(dbWrapper), - pcValue: new(uint), - gasValue: new(uint), - costValue: new(uint), - depthValue: new(uint), - refundValue: new(uint), - frame: newFrame(), - frameResult: newFrameResult(), - } - if ctx.BlockHash != (common.Hash{}) { - tracer.ctx["blockHash"] = ctx.BlockHash - - if ctx.TxHash != (common.Hash{}) { - tracer.ctx["txIndex"] = ctx.TxIndex - tracer.ctx["txHash"] = ctx.TxHash - } - } - // Set up builtins for this environment - tracer.vm.PushGlobalGoFunction("toHex", func(ctx *duktape.Context) int { - ctx.PushString(hexutil.Encode(popSlice(ctx))) - return 1 - }) - tracer.vm.PushGlobalGoFunction("toWord", func(ctx *duktape.Context) int { - var word common.Hash - if ptr, size := ctx.GetBuffer(-1); ptr != nil { - word = common.BytesToHash(makeSlice(ptr, size)) - } else { - word = common.HexToHash(ctx.GetString(-1)) - } - ctx.Pop() - copy(makeSlice(ctx.PushFixedBuffer(32), 32), word[:]) - return 1 - }) - tracer.vm.PushGlobalGoFunction("toAddress", func(ctx *duktape.Context) int { - var addr common.Address - if ptr, size := ctx.GetBuffer(-1); ptr != nil { - addr = common.BytesToAddress(makeSlice(ptr, size)) - } else { - addr = common.HexToAddress(ctx.GetString(-1)) - } - ctx.Pop() - copy(makeSlice(ctx.PushFixedBuffer(20), 20), addr[:]) - return 1 - }) - tracer.vm.PushGlobalGoFunction("toContract", func(ctx *duktape.Context) int { - var from common.Address - if ptr, size := ctx.GetBuffer(-2); ptr != nil { - from = common.BytesToAddress(makeSlice(ptr, size)) - } else { - from = common.HexToAddress(ctx.GetString(-2)) - } - nonce := uint64(ctx.GetInt(-1)) - ctx.Pop2() - - contract := crypto.CreateAddress(from, nonce) - copy(makeSlice(ctx.PushFixedBuffer(20), 20), contract[:]) - return 1 - }) - tracer.vm.PushGlobalGoFunction("toContract2", func(ctx *duktape.Context) int { - var from common.Address - if ptr, size := ctx.GetBuffer(-3); ptr != nil { - from = common.BytesToAddress(makeSlice(ptr, size)) - } else { - from = common.HexToAddress(ctx.GetString(-3)) - } - // Retrieve salt hex string from js stack - salt := common.HexToHash(ctx.GetString(-2)) - // Retrieve code slice from js stack - var code []byte - if ptr, size := ctx.GetBuffer(-1); ptr != nil { - code = common.CopyBytes(makeSlice(ptr, size)) - } else { - code = common.FromHex(ctx.GetString(-1)) - } - codeHash := crypto.Keccak256(code) - ctx.Pop3() - contract := crypto.CreateAddress2(from, salt, codeHash) - copy(makeSlice(ctx.PushFixedBuffer(20), 20), contract[:]) - return 1 - }) - tracer.vm.PushGlobalGoFunction("isPrecompiled", func(ctx *duktape.Context) int { - addr := common.BytesToAddress(popSlice(ctx)) - for _, p := range tracer.activePrecompiles { - if p == addr { - ctx.PushBoolean(true) - return 1 - } - } - ctx.PushBoolean(false) - return 1 - }) - tracer.vm.PushGlobalGoFunction("slice", func(ctx *duktape.Context) int { - start, end := ctx.GetInt(-2), ctx.GetInt(-1) - ctx.Pop2() - - blob := popSlice(ctx) - size := end - start - - if start < 0 || start > end || end > len(blob) { - // TODO(karalabe): We can't js-throw from Go inside duktape inside Go. The Go - // runtime goes belly up https://github.com/golang/go/issues/15639. - log.Warn("Tracer accessed out of bound memory", "available", len(blob), "offset", start, "size", size) - ctx.PushFixedBuffer(0) - return 1 - } - copy(makeSlice(ctx.PushFixedBuffer(size), uint(size)), blob[start:end]) - return 1 - }) - // Push the JavaScript tracer as object #0 onto the JSVM stack and validate it - if err := tracer.vm.PevalString("(" + code + ")"); err != nil { - log.Warn("Failed to compile tracer", "err", err) - return nil, err - } - tracer.tracerObject = 0 // yeah, nice, eval can't return the index itself - - hasStep := tracer.vm.GetPropString(tracer.tracerObject, "step") - tracer.vm.Pop() - - if !tracer.vm.GetPropString(tracer.tracerObject, "fault") { - return nil, fmt.Errorf("trace object must expose a function fault()") - } - tracer.vm.Pop() - - if !tracer.vm.GetPropString(tracer.tracerObject, "result") { - return nil, fmt.Errorf("trace object must expose a function result()") - } - tracer.vm.Pop() - - hasEnter := tracer.vm.GetPropString(tracer.tracerObject, "enter") - tracer.vm.Pop() - hasExit := tracer.vm.GetPropString(tracer.tracerObject, "exit") - tracer.vm.Pop() - if hasEnter != hasExit { - return nil, fmt.Errorf("trace object must expose either both or none of enter() and exit()") - } - tracer.traceCallFrames = hasEnter && hasExit - tracer.traceSteps = hasStep - - // Tracer is valid, inject the big int library to access large numbers - tracer.vm.EvalString(bigIntegerJS) - tracer.vm.PutGlobalString("bigInt") - - // Push the global environment state as object #1 into the JSVM stack - tracer.stateObject = tracer.vm.PushObject() - - logObject := tracer.vm.PushObject() - - tracer.opWrapper.pushObject(tracer.vm) - tracer.vm.PutPropString(logObject, "op") - - tracer.stackWrapper.pushObject(tracer.vm) - tracer.vm.PutPropString(logObject, "stack") - - tracer.memoryWrapper.pushObject(tracer.vm) - tracer.vm.PutPropString(logObject, "memory") - - tracer.contractWrapper.pushObject(tracer.vm) - tracer.vm.PutPropString(logObject, "contract") - - tracer.vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushUint(*tracer.pcValue); return 1 }) - tracer.vm.PutPropString(logObject, "getPC") - - tracer.vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushUint(*tracer.gasValue); return 1 }) - tracer.vm.PutPropString(logObject, "getGas") - - tracer.vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushUint(*tracer.costValue); return 1 }) - tracer.vm.PutPropString(logObject, "getCost") - - tracer.vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushUint(*tracer.depthValue); return 1 }) - tracer.vm.PutPropString(logObject, "getDepth") - - tracer.vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushUint(*tracer.refundValue); return 1 }) - tracer.vm.PutPropString(logObject, "getRefund") - - tracer.vm.PushGoFunction(func(ctx *duktape.Context) int { - if tracer.errorValue != nil { - ctx.PushString(*tracer.errorValue) - } else { - ctx.PushUndefined() - } - return 1 - }) - tracer.vm.PutPropString(logObject, "getError") - - tracer.vm.PutPropString(tracer.stateObject, "log") - - tracer.frame.pushObject(tracer.vm) - tracer.vm.PutPropString(tracer.stateObject, "frame") - - tracer.frameResult.pushObject(tracer.vm) - tracer.vm.PutPropString(tracer.stateObject, "frameResult") - - tracer.dbWrapper.pushObject(tracer.vm) - tracer.vm.PutPropString(tracer.stateObject, "db") - - return tracer, nil -} - -// Stop terminates execution of the tracer at the first opportune moment. -func (jst *jsTracer) Stop(err error) { - jst.reason = err - atomic.StoreUint32(&jst.interrupt, 1) -} - -// call executes a method on a JS object, catching any errors, formatting and -// returning them as error objects. -func (jst *jsTracer) call(noret bool, method string, args ...string) (json.RawMessage, error) { - // Execute the JavaScript call and return any error - jst.vm.PushString(method) - for _, arg := range args { - jst.vm.GetPropString(jst.stateObject, arg) - } - code := jst.vm.PcallProp(jst.tracerObject, len(args)) - defer jst.vm.Pop() - - if code != 0 { - err := jst.vm.SafeToString(-1) - return nil, errors.New(err) - } - // No error occurred, extract return value and return - if noret { - return nil, nil - } - // Push a JSON marshaller onto the stack. We can't marshal from the out- - // side because duktape can crash on large nestings and we can't catch - // C++ exceptions ourselves from Go. TODO(karalabe): Yuck, why wrap?! - jst.vm.PushString("(JSON.stringify)") - jst.vm.Eval() - - jst.vm.Swap(-1, -2) - if code = jst.vm.Pcall(1); code != 0 { - err := jst.vm.SafeToString(-1) - return nil, errors.New(err) - } - return json.RawMessage(jst.vm.SafeToString(-1)), nil -} - -func wrapError(context string, err error) error { - return fmt.Errorf("%v in server-side tracer function '%v'", err, context) -} - -// CaptureStart implements the Tracer interface to initialize the tracing operation. -func (jst *jsTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { - jst.ctx["type"] = "CALL" - if create { - jst.ctx["type"] = "CREATE" - } - jst.ctx["from"] = from - jst.ctx["to"] = to - jst.ctx["input"] = input - jst.ctx["gas"] = gas - jst.ctx["gasPrice"] = env.TxContext.GasPrice - jst.ctx["value"] = value - - // Initialize the context - jst.ctx["block"] = env.Context.BlockNumber.Uint64() - jst.dbWrapper.db = env.StateDB - // Update list of precompiles based on current block - rules := env.ChainConfig().Rules(env.Context.BlockNumber) - jst.activePrecompiles = vm.ActivePrecompiles(rules) - - // Compute intrinsic gas - isHomestead := env.ChainConfig().IsHomestead(env.Context.BlockNumber) - isIstanbul := env.ChainConfig().IsIstanbul(env.Context.BlockNumber) - intrinsicGas, err := core.IntrinsicGas(input, nil, jst.ctx["type"] == "CREATE", isHomestead, isIstanbul) - if err != nil { - return - } - jst.ctx["intrinsicGas"] = intrinsicGas -} - -// CaptureState implements the Tracer interface to trace a single step of VM execution. -func (jst *jsTracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) { - if !jst.traceSteps { - return - } - if jst.err != nil { - return - } - // If tracing was interrupted, set the error and stop - if atomic.LoadUint32(&jst.interrupt) > 0 { - jst.err = jst.reason - env.Cancel() - return - } - jst.opWrapper.op = op - jst.stackWrapper.stack = scope.Stack - jst.memoryWrapper.memory = scope.Memory - jst.contractWrapper.contract = scope.Contract - - *jst.pcValue = uint(pc) - *jst.gasValue = uint(gas) - *jst.costValue = uint(cost) - *jst.depthValue = uint(depth) - *jst.refundValue = uint(env.StateDB.GetRefund()) - - jst.errorValue = nil - if err != nil { - jst.errorValue = new(string) - *jst.errorValue = err.Error() - } - - if _, err := jst.call(true, "step", "log", "db"); err != nil { - jst.err = wrapError("step", err) - } -} - -// CaptureFault implements the Tracer interface to trace an execution fault -func (jst *jsTracer) CaptureFault(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, depth int, err error) { - if jst.err != nil { - return - } - // Apart from the error, everything matches the previous invocation - jst.errorValue = new(string) - *jst.errorValue = err.Error() - - if _, err := jst.call(true, "fault", "log", "db"); err != nil { - jst.err = wrapError("fault", err) - } -} - -// CaptureEnd is called after the call finishes to finalize the tracing. -func (jst *jsTracer) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) { - jst.ctx["output"] = output - jst.ctx["time"] = t.String() - jst.ctx["gasUsed"] = gasUsed - - if err != nil { - jst.ctx["error"] = err.Error() - } -} - -// CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct). -func (jst *jsTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { - if !jst.traceCallFrames { - return - } - if jst.err != nil { - return - } - // If tracing was interrupted, set the error and stop - if atomic.LoadUint32(&jst.interrupt) > 0 { - jst.err = jst.reason - return - } - - *jst.frame.typ = typ.String() - *jst.frame.from = from - *jst.frame.to = to - jst.frame.input = common.CopyBytes(input) - *jst.frame.gas = uint(gas) - jst.frame.value = nil - if value != nil { - jst.frame.value = new(big.Int).SetBytes(value.Bytes()) - } - - if _, err := jst.call(true, "enter", "frame"); err != nil { - jst.err = wrapError("enter", err) - } -} - -// CaptureExit is called when EVM exits a scope, even if the scope didn't -// execute any code. -func (jst *jsTracer) CaptureExit(output []byte, gasUsed uint64, err error) { - if !jst.traceCallFrames { - return - } - // If tracing was interrupted, set the error and stop - if atomic.LoadUint32(&jst.interrupt) > 0 { - jst.err = jst.reason - return - } - - jst.frameResult.output = common.CopyBytes(output) - *jst.frameResult.gasUsed = uint(gasUsed) - jst.frameResult.errorValue = nil - if err != nil { - jst.frameResult.errorValue = new(string) - *jst.frameResult.errorValue = err.Error() - } - - if _, err := jst.call(true, "exit", "frameResult"); err != nil { - jst.err = wrapError("exit", err) - } -} - -// GetResult calls the Javascript 'result' function and returns its value, or any accumulated error -func (jst *jsTracer) GetResult() (json.RawMessage, error) { - // Transform the context into a JavaScript object and inject into the state - obj := jst.vm.PushObject() - - for key, val := range jst.ctx { - jst.addToObj(obj, key, val) - } - jst.vm.PutPropString(jst.stateObject, "ctx") - - // Finalize the trace and return the results - result, err := jst.call(false, "result", "ctx", "db") - if err != nil { - jst.err = wrapError("result", err) - } - // Clean up the JavaScript environment - jst.vm.DestroyHeap() - jst.vm.Destroy() - - return result, jst.err -} - -// addToObj pushes a field to a JS object. -func (jst *jsTracer) addToObj(obj int, key string, val interface{}) { - pushValue(jst.vm, val) - jst.vm.PutPropString(obj, key) -} - -func pushValue(ctx *duktape.Context, val interface{}) { - switch val := val.(type) { - case uint64: - ctx.PushUint(uint(val)) - case string: - ctx.PushString(val) - case []byte: - ptr := ctx.PushFixedBuffer(len(val)) - copy(makeSlice(ptr, uint(len(val))), val) - case common.Address: - ptr := ctx.PushFixedBuffer(20) - copy(makeSlice(ptr, 20), val[:]) - case *big.Int: - pushBigInt(val, ctx) - case int: - ctx.PushInt(val) - case uint: - ctx.PushUint(val) - case common.Hash: - ptr := ctx.PushFixedBuffer(32) - copy(makeSlice(ptr, 32), val[:]) - default: - panic(fmt.Sprintf("unsupported type: %T", val)) - } -} diff --git a/eth/tracers/internal/tracers/4byte_tracer.js b/eth/tracers/js/internal/tracers/4byte_tracer.js similarity index 100% rename from eth/tracers/internal/tracers/4byte_tracer.js rename to eth/tracers/js/internal/tracers/4byte_tracer.js diff --git a/eth/tracers/internal/tracers/4byte_tracer_legacy.js b/eth/tracers/js/internal/tracers/4byte_tracer_legacy.js similarity index 100% rename from eth/tracers/internal/tracers/4byte_tracer_legacy.js rename to eth/tracers/js/internal/tracers/4byte_tracer_legacy.js diff --git a/eth/tracers/internal/tracers/assets.go b/eth/tracers/js/internal/tracers/assets.go similarity index 100% rename from eth/tracers/internal/tracers/assets.go rename to eth/tracers/js/internal/tracers/assets.go diff --git a/eth/tracers/internal/tracers/bigram_tracer.js b/eth/tracers/js/internal/tracers/bigram_tracer.js similarity index 100% rename from eth/tracers/internal/tracers/bigram_tracer.js rename to eth/tracers/js/internal/tracers/bigram_tracer.js diff --git a/eth/tracers/internal/tracers/call_tracer_js.js b/eth/tracers/js/internal/tracers/call_tracer_js.js similarity index 100% rename from eth/tracers/internal/tracers/call_tracer_js.js rename to eth/tracers/js/internal/tracers/call_tracer_js.js diff --git a/eth/tracers/internal/tracers/call_tracer_legacy.js b/eth/tracers/js/internal/tracers/call_tracer_legacy.js similarity index 100% rename from eth/tracers/internal/tracers/call_tracer_legacy.js rename to eth/tracers/js/internal/tracers/call_tracer_legacy.js diff --git a/eth/tracers/internal/tracers/evmdis_tracer.js b/eth/tracers/js/internal/tracers/evmdis_tracer.js similarity index 100% rename from eth/tracers/internal/tracers/evmdis_tracer.js rename to eth/tracers/js/internal/tracers/evmdis_tracer.js diff --git a/eth/tracers/internal/tracers/noop_tracer.js b/eth/tracers/js/internal/tracers/noop_tracer.js similarity index 100% rename from eth/tracers/internal/tracers/noop_tracer.js rename to eth/tracers/js/internal/tracers/noop_tracer.js diff --git a/eth/tracers/internal/tracers/opcount_tracer.js b/eth/tracers/js/internal/tracers/opcount_tracer.js similarity index 100% rename from eth/tracers/internal/tracers/opcount_tracer.js rename to eth/tracers/js/internal/tracers/opcount_tracer.js diff --git a/eth/tracers/internal/tracers/prestate_tracer.js b/eth/tracers/js/internal/tracers/prestate_tracer.js similarity index 100% rename from eth/tracers/internal/tracers/prestate_tracer.js rename to eth/tracers/js/internal/tracers/prestate_tracer.js diff --git a/eth/tracers/internal/tracers/tracers.go b/eth/tracers/js/internal/tracers/tracers.go similarity index 100% rename from eth/tracers/internal/tracers/tracers.go rename to eth/tracers/js/internal/tracers/tracers.go diff --git a/eth/tracers/internal/tracers/trigram_tracer.js b/eth/tracers/js/internal/tracers/trigram_tracer.js similarity index 100% rename from eth/tracers/internal/tracers/trigram_tracer.js rename to eth/tracers/js/internal/tracers/trigram_tracer.js diff --git a/eth/tracers/internal/tracers/unigram_tracer.js b/eth/tracers/js/internal/tracers/unigram_tracer.js similarity index 100% rename from eth/tracers/internal/tracers/unigram_tracer.js rename to eth/tracers/js/internal/tracers/unigram_tracer.js diff --git a/eth/tracers/js/tracer.go b/eth/tracers/js/tracer.go new file mode 100644 index 000000000..b8e035e6f --- /dev/null +++ b/eth/tracers/js/tracer.go @@ -0,0 +1,880 @@ +// Copyright 2017 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 . + +// package js is a collection of tracers written in javascript. +package js + +import ( + "encoding/json" + "errors" + "fmt" + "math/big" + "strings" + "sync/atomic" + "time" + "unicode" + "unsafe" + + "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/vm" + "github.com/ethereum/go-ethereum/crypto" + tracers2 "github.com/ethereum/go-ethereum/eth/tracers" + "github.com/ethereum/go-ethereum/eth/tracers/js/internal/tracers" + "github.com/ethereum/go-ethereum/log" + "gopkg.in/olebedev/go-duktape.v3" +) + +// camel converts a snake cased input string into a camel cased output. +func camel(str string) string { + pieces := strings.Split(str, "_") + for i := 1; i < len(pieces); i++ { + pieces[i] = string(unicode.ToUpper(rune(pieces[i][0]))) + pieces[i][1:] + } + return strings.Join(pieces, "") +} + +var assetTracers = make(map[string]string) + +// init retrieves the JavaScript transaction tracers included in go-ethereum. +func init() { + for _, file := range tracers.AssetNames() { + name := camel(strings.TrimSuffix(file, ".js")) + assetTracers[name] = string(tracers.MustAsset(file)) + } + tracers2.RegisterLookup(true, newJsTracer) +} + +// makeSlice convert an unsafe memory pointer with the given type into a Go byte +// slice. +// +// Note, the returned slice uses the same memory area as the input arguments. +// If those are duktape stack items, popping them off **will** make the slice +// contents change. +func makeSlice(ptr unsafe.Pointer, size uint) []byte { + var sl = struct { + addr uintptr + len int + cap int + }{uintptr(ptr), int(size), int(size)} + + return *(*[]byte)(unsafe.Pointer(&sl)) +} + +// popSlice pops a buffer off the JavaScript stack and returns it as a slice. +func popSlice(ctx *duktape.Context) []byte { + blob := common.CopyBytes(makeSlice(ctx.GetBuffer(-1))) + ctx.Pop() + return blob +} + +// pushBigInt create a JavaScript BigInteger in the VM. +func pushBigInt(n *big.Int, ctx *duktape.Context) { + ctx.GetGlobalString("bigInt") + ctx.PushString(n.String()) + ctx.Call(1) +} + +// opWrapper provides a JavaScript wrapper around OpCode. +type opWrapper struct { + op vm.OpCode +} + +// pushObject assembles a JSVM object wrapping a swappable opcode and pushes it +// onto the VM stack. +func (ow *opWrapper) pushObject(vm *duktape.Context) { + obj := vm.PushObject() + + vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushInt(int(ow.op)); return 1 }) + vm.PutPropString(obj, "toNumber") + + vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushString(ow.op.String()); return 1 }) + vm.PutPropString(obj, "toString") + + vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushBoolean(ow.op.IsPush()); return 1 }) + vm.PutPropString(obj, "isPush") +} + +// memoryWrapper provides a JavaScript wrapper around vm.Memory. +type memoryWrapper struct { + memory *vm.Memory +} + +// slice returns the requested range of memory as a byte slice. +func (mw *memoryWrapper) slice(begin, end int64) []byte { + if end == begin { + return []byte{} + } + if end < begin || begin < 0 { + // TODO(karalabe): We can't js-throw from Go inside duktape inside Go. The Go + // runtime goes belly up https://github.com/golang/go/issues/15639. + log.Warn("Tracer accessed out of bound memory", "offset", begin, "end", end) + return nil + } + if mw.memory.Len() < int(end) { + // TODO(karalabe): We can't js-throw from Go inside duktape inside Go. The Go + // runtime goes belly up https://github.com/golang/go/issues/15639. + log.Warn("Tracer accessed out of bound memory", "available", mw.memory.Len(), "offset", begin, "size", end-begin) + return nil + } + return mw.memory.GetCopy(begin, end-begin) +} + +// getUint returns the 32 bytes at the specified address interpreted as a uint. +func (mw *memoryWrapper) getUint(addr int64) *big.Int { + if mw.memory.Len() < int(addr)+32 || addr < 0 { + // TODO(karalabe): We can't js-throw from Go inside duktape inside Go. The Go + // runtime goes belly up https://github.com/golang/go/issues/15639. + log.Warn("Tracer accessed out of bound memory", "available", mw.memory.Len(), "offset", addr, "size", 32) + return new(big.Int) + } + return new(big.Int).SetBytes(mw.memory.GetPtr(addr, 32)) +} + +// pushObject assembles a JSVM object wrapping a swappable memory and pushes it +// onto the VM stack. +func (mw *memoryWrapper) pushObject(vm *duktape.Context) { + obj := vm.PushObject() + + // Generate the `slice` method which takes two ints and returns a buffer + vm.PushGoFunction(func(ctx *duktape.Context) int { + blob := mw.slice(int64(ctx.GetInt(-2)), int64(ctx.GetInt(-1))) + ctx.Pop2() + + ptr := ctx.PushFixedBuffer(len(blob)) + copy(makeSlice(ptr, uint(len(blob))), blob) + return 1 + }) + vm.PutPropString(obj, "slice") + + // Generate the `getUint` method which takes an int and returns a bigint + vm.PushGoFunction(func(ctx *duktape.Context) int { + offset := int64(ctx.GetInt(-1)) + ctx.Pop() + + pushBigInt(mw.getUint(offset), ctx) + return 1 + }) + vm.PutPropString(obj, "getUint") +} + +// stackWrapper provides a JavaScript wrapper around vm.Stack. +type stackWrapper struct { + stack *vm.Stack +} + +// peek returns the nth-from-the-top element of the stack. +func (sw *stackWrapper) peek(idx int) *big.Int { + if len(sw.stack.Data()) <= idx || idx < 0 { + // TODO(karalabe): We can't js-throw from Go inside duktape inside Go. The Go + // runtime goes belly up https://github.com/golang/go/issues/15639. + log.Warn("Tracer accessed out of bound stack", "size", len(sw.stack.Data()), "index", idx) + return new(big.Int) + } + return sw.stack.Back(idx).ToBig() +} + +// pushObject assembles a JSVM object wrapping a swappable stack and pushes it +// onto the VM stack. +func (sw *stackWrapper) pushObject(vm *duktape.Context) { + obj := vm.PushObject() + + vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushInt(len(sw.stack.Data())); return 1 }) + vm.PutPropString(obj, "length") + + // Generate the `peek` method which takes an int and returns a bigint + vm.PushGoFunction(func(ctx *duktape.Context) int { + offset := ctx.GetInt(-1) + ctx.Pop() + + pushBigInt(sw.peek(offset), ctx) + return 1 + }) + vm.PutPropString(obj, "peek") +} + +// dbWrapper provides a JavaScript wrapper around vm.Database. +type dbWrapper struct { + db vm.StateDB +} + +// pushObject assembles a JSVM object wrapping a swappable database and pushes it +// onto the VM stack. +func (dw *dbWrapper) pushObject(vm *duktape.Context) { + obj := vm.PushObject() + + // Push the wrapper for statedb.GetBalance + vm.PushGoFunction(func(ctx *duktape.Context) int { + pushBigInt(dw.db.GetBalance(common.BytesToAddress(popSlice(ctx))), ctx) + return 1 + }) + vm.PutPropString(obj, "getBalance") + + // Push the wrapper for statedb.GetNonce + vm.PushGoFunction(func(ctx *duktape.Context) int { + ctx.PushInt(int(dw.db.GetNonce(common.BytesToAddress(popSlice(ctx))))) + return 1 + }) + vm.PutPropString(obj, "getNonce") + + // Push the wrapper for statedb.GetCode + vm.PushGoFunction(func(ctx *duktape.Context) int { + code := dw.db.GetCode(common.BytesToAddress(popSlice(ctx))) + + ptr := ctx.PushFixedBuffer(len(code)) + copy(makeSlice(ptr, uint(len(code))), code) + return 1 + }) + vm.PutPropString(obj, "getCode") + + // Push the wrapper for statedb.GetState + vm.PushGoFunction(func(ctx *duktape.Context) int { + hash := popSlice(ctx) + addr := popSlice(ctx) + + state := dw.db.GetState(common.BytesToAddress(addr), common.BytesToHash(hash)) + + ptr := ctx.PushFixedBuffer(len(state)) + copy(makeSlice(ptr, uint(len(state))), state[:]) + return 1 + }) + vm.PutPropString(obj, "getState") + + // Push the wrapper for statedb.Exists + vm.PushGoFunction(func(ctx *duktape.Context) int { + ctx.PushBoolean(dw.db.Exist(common.BytesToAddress(popSlice(ctx)))) + return 1 + }) + vm.PutPropString(obj, "exists") +} + +// contractWrapper provides a JavaScript wrapper around vm.Contract +type contractWrapper struct { + contract *vm.Contract +} + +// pushObject assembles a JSVM object wrapping a swappable contract and pushes it +// onto the VM stack. +func (cw *contractWrapper) pushObject(vm *duktape.Context) { + obj := vm.PushObject() + + // Push the wrapper for contract.Caller + vm.PushGoFunction(func(ctx *duktape.Context) int { + ptr := ctx.PushFixedBuffer(20) + copy(makeSlice(ptr, 20), cw.contract.Caller().Bytes()) + return 1 + }) + vm.PutPropString(obj, "getCaller") + + // Push the wrapper for contract.Address + vm.PushGoFunction(func(ctx *duktape.Context) int { + ptr := ctx.PushFixedBuffer(20) + copy(makeSlice(ptr, 20), cw.contract.Address().Bytes()) + return 1 + }) + vm.PutPropString(obj, "getAddress") + + // Push the wrapper for contract.Value + vm.PushGoFunction(func(ctx *duktape.Context) int { + pushBigInt(cw.contract.Value(), ctx) + return 1 + }) + vm.PutPropString(obj, "getValue") + + // Push the wrapper for contract.Input + vm.PushGoFunction(func(ctx *duktape.Context) int { + blob := cw.contract.Input + + ptr := ctx.PushFixedBuffer(len(blob)) + copy(makeSlice(ptr, uint(len(blob))), blob) + return 1 + }) + vm.PutPropString(obj, "getInput") +} + +type frame struct { + typ *string + from *common.Address + to *common.Address + input []byte + gas *uint + value *big.Int +} + +func newFrame() *frame { + return &frame{ + typ: new(string), + from: new(common.Address), + to: new(common.Address), + gas: new(uint), + } +} + +func (f *frame) pushObject(vm *duktape.Context) { + obj := vm.PushObject() + + vm.PushGoFunction(func(ctx *duktape.Context) int { pushValue(ctx, *f.typ); return 1 }) + vm.PutPropString(obj, "getType") + + vm.PushGoFunction(func(ctx *duktape.Context) int { pushValue(ctx, *f.from); return 1 }) + vm.PutPropString(obj, "getFrom") + + vm.PushGoFunction(func(ctx *duktape.Context) int { pushValue(ctx, *f.to); return 1 }) + vm.PutPropString(obj, "getTo") + + vm.PushGoFunction(func(ctx *duktape.Context) int { pushValue(ctx, f.input); return 1 }) + vm.PutPropString(obj, "getInput") + + vm.PushGoFunction(func(ctx *duktape.Context) int { pushValue(ctx, *f.gas); return 1 }) + vm.PutPropString(obj, "getGas") + + vm.PushGoFunction(func(ctx *duktape.Context) int { + if f.value != nil { + pushValue(ctx, f.value) + } else { + ctx.PushUndefined() + } + return 1 + }) + vm.PutPropString(obj, "getValue") +} + +type frameResult struct { + gasUsed *uint + output []byte + errorValue *string +} + +func newFrameResult() *frameResult { + return &frameResult{ + gasUsed: new(uint), + } +} + +func (r *frameResult) pushObject(vm *duktape.Context) { + obj := vm.PushObject() + + vm.PushGoFunction(func(ctx *duktape.Context) int { pushValue(ctx, *r.gasUsed); return 1 }) + vm.PutPropString(obj, "getGasUsed") + + vm.PushGoFunction(func(ctx *duktape.Context) int { pushValue(ctx, r.output); return 1 }) + vm.PutPropString(obj, "getOutput") + + vm.PushGoFunction(func(ctx *duktape.Context) int { + if r.errorValue != nil { + pushValue(ctx, *r.errorValue) + } else { + ctx.PushUndefined() + } + return 1 + }) + vm.PutPropString(obj, "getError") +} + +// jsTracer provides an implementation of Tracer that evaluates a Javascript +// function for each VM execution step. +type jsTracer struct { + vm *duktape.Context // Javascript VM instance + env *vm.EVM // EVM instance executing the code being traced + + tracerObject int // Stack index of the tracer JavaScript object + stateObject int // Stack index of the global state to pull arguments from + + opWrapper *opWrapper // Wrapper around the VM opcode + stackWrapper *stackWrapper // Wrapper around the VM stack + memoryWrapper *memoryWrapper // Wrapper around the VM memory + contractWrapper *contractWrapper // Wrapper around the contract object + dbWrapper *dbWrapper // Wrapper around the VM environment + + pcValue *uint // Swappable pc value wrapped by a log accessor + gasValue *uint // Swappable gas value wrapped by a log accessor + costValue *uint // Swappable cost value wrapped by a log accessor + depthValue *uint // Swappable depth value wrapped by a log accessor + errorValue *string // Swappable error value wrapped by a log accessor + refundValue *uint // Swappable refund value wrapped by a log accessor + + frame *frame // Represents entry into call frame. Fields are swappable + frameResult *frameResult // Represents exit from a call frame. Fields are swappable + + ctx map[string]interface{} // Transaction context gathered throughout execution + err error // Error, if one has occurred + + interrupt uint32 // Atomic flag to signal execution interruption + reason error // Textual reason for the interruption + + activePrecompiles []common.Address // Updated on CaptureStart based on given rules + traceSteps bool // When true, will invoke step() on each opcode + traceCallFrames bool // When true, will invoke enter() and exit() js funcs +} + +// New instantiates a new tracer instance. code specifies a Javascript snippet, +// which must evaluate to an expression returning an object with 'step', 'fault' +// and 'result' functions. +func newJsTracer(code string, ctx *tracers2.Context) (tracers2.Tracer, error) { + if c, ok := assetTracers[code]; ok { + code = c + } + if ctx == nil { + ctx = new(tracers2.Context) + } + tracer := &jsTracer{ + vm: duktape.New(), + ctx: make(map[string]interface{}), + opWrapper: new(opWrapper), + stackWrapper: new(stackWrapper), + memoryWrapper: new(memoryWrapper), + contractWrapper: new(contractWrapper), + dbWrapper: new(dbWrapper), + pcValue: new(uint), + gasValue: new(uint), + costValue: new(uint), + depthValue: new(uint), + refundValue: new(uint), + frame: newFrame(), + frameResult: newFrameResult(), + } + if ctx.BlockHash != (common.Hash{}) { + tracer.ctx["blockHash"] = ctx.BlockHash + + if ctx.TxHash != (common.Hash{}) { + tracer.ctx["txIndex"] = ctx.TxIndex + tracer.ctx["txHash"] = ctx.TxHash + } + } + // Set up builtins for this environment + tracer.vm.PushGlobalGoFunction("toHex", func(ctx *duktape.Context) int { + ctx.PushString(hexutil.Encode(popSlice(ctx))) + return 1 + }) + tracer.vm.PushGlobalGoFunction("toWord", func(ctx *duktape.Context) int { + var word common.Hash + if ptr, size := ctx.GetBuffer(-1); ptr != nil { + word = common.BytesToHash(makeSlice(ptr, size)) + } else { + word = common.HexToHash(ctx.GetString(-1)) + } + ctx.Pop() + copy(makeSlice(ctx.PushFixedBuffer(32), 32), word[:]) + return 1 + }) + tracer.vm.PushGlobalGoFunction("toAddress", func(ctx *duktape.Context) int { + var addr common.Address + if ptr, size := ctx.GetBuffer(-1); ptr != nil { + addr = common.BytesToAddress(makeSlice(ptr, size)) + } else { + addr = common.HexToAddress(ctx.GetString(-1)) + } + ctx.Pop() + copy(makeSlice(ctx.PushFixedBuffer(20), 20), addr[:]) + return 1 + }) + tracer.vm.PushGlobalGoFunction("toContract", func(ctx *duktape.Context) int { + var from common.Address + if ptr, size := ctx.GetBuffer(-2); ptr != nil { + from = common.BytesToAddress(makeSlice(ptr, size)) + } else { + from = common.HexToAddress(ctx.GetString(-2)) + } + nonce := uint64(ctx.GetInt(-1)) + ctx.Pop2() + + contract := crypto.CreateAddress(from, nonce) + copy(makeSlice(ctx.PushFixedBuffer(20), 20), contract[:]) + return 1 + }) + tracer.vm.PushGlobalGoFunction("toContract2", func(ctx *duktape.Context) int { + var from common.Address + if ptr, size := ctx.GetBuffer(-3); ptr != nil { + from = common.BytesToAddress(makeSlice(ptr, size)) + } else { + from = common.HexToAddress(ctx.GetString(-3)) + } + // Retrieve salt hex string from js stack + salt := common.HexToHash(ctx.GetString(-2)) + // Retrieve code slice from js stack + var code []byte + if ptr, size := ctx.GetBuffer(-1); ptr != nil { + code = common.CopyBytes(makeSlice(ptr, size)) + } else { + code = common.FromHex(ctx.GetString(-1)) + } + codeHash := crypto.Keccak256(code) + ctx.Pop3() + contract := crypto.CreateAddress2(from, salt, codeHash) + copy(makeSlice(ctx.PushFixedBuffer(20), 20), contract[:]) + return 1 + }) + tracer.vm.PushGlobalGoFunction("isPrecompiled", func(ctx *duktape.Context) int { + addr := common.BytesToAddress(popSlice(ctx)) + for _, p := range tracer.activePrecompiles { + if p == addr { + ctx.PushBoolean(true) + return 1 + } + } + ctx.PushBoolean(false) + return 1 + }) + tracer.vm.PushGlobalGoFunction("slice", func(ctx *duktape.Context) int { + start, end := ctx.GetInt(-2), ctx.GetInt(-1) + ctx.Pop2() + + blob := popSlice(ctx) + size := end - start + + if start < 0 || start > end || end > len(blob) { + // TODO(karalabe): We can't js-throw from Go inside duktape inside Go. The Go + // runtime goes belly up https://github.com/golang/go/issues/15639. + log.Warn("Tracer accessed out of bound memory", "available", len(blob), "offset", start, "size", size) + ctx.PushFixedBuffer(0) + return 1 + } + copy(makeSlice(ctx.PushFixedBuffer(size), uint(size)), blob[start:end]) + return 1 + }) + // Push the JavaScript tracer as object #0 onto the JSVM stack and validate it + if err := tracer.vm.PevalString("(" + code + ")"); err != nil { + log.Warn("Failed to compile tracer", "err", err) + return nil, err + } + tracer.tracerObject = 0 // yeah, nice, eval can't return the index itself + + hasStep := tracer.vm.GetPropString(tracer.tracerObject, "step") + tracer.vm.Pop() + + if !tracer.vm.GetPropString(tracer.tracerObject, "fault") { + return nil, fmt.Errorf("trace object must expose a function fault()") + } + tracer.vm.Pop() + + if !tracer.vm.GetPropString(tracer.tracerObject, "result") { + return nil, fmt.Errorf("trace object must expose a function result()") + } + tracer.vm.Pop() + + hasEnter := tracer.vm.GetPropString(tracer.tracerObject, "enter") + tracer.vm.Pop() + hasExit := tracer.vm.GetPropString(tracer.tracerObject, "exit") + tracer.vm.Pop() + if hasEnter != hasExit { + return nil, fmt.Errorf("trace object must expose either both or none of enter() and exit()") + } + tracer.traceCallFrames = hasEnter && hasExit + tracer.traceSteps = hasStep + + // Tracer is valid, inject the big int library to access large numbers + tracer.vm.EvalString(bigIntegerJS) + tracer.vm.PutGlobalString("bigInt") + + // Push the global environment state as object #1 into the JSVM stack + tracer.stateObject = tracer.vm.PushObject() + + logObject := tracer.vm.PushObject() + + tracer.opWrapper.pushObject(tracer.vm) + tracer.vm.PutPropString(logObject, "op") + + tracer.stackWrapper.pushObject(tracer.vm) + tracer.vm.PutPropString(logObject, "stack") + + tracer.memoryWrapper.pushObject(tracer.vm) + tracer.vm.PutPropString(logObject, "memory") + + tracer.contractWrapper.pushObject(tracer.vm) + tracer.vm.PutPropString(logObject, "contract") + + tracer.vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushUint(*tracer.pcValue); return 1 }) + tracer.vm.PutPropString(logObject, "getPC") + + tracer.vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushUint(*tracer.gasValue); return 1 }) + tracer.vm.PutPropString(logObject, "getGas") + + tracer.vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushUint(*tracer.costValue); return 1 }) + tracer.vm.PutPropString(logObject, "getCost") + + tracer.vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushUint(*tracer.depthValue); return 1 }) + tracer.vm.PutPropString(logObject, "getDepth") + + tracer.vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushUint(*tracer.refundValue); return 1 }) + tracer.vm.PutPropString(logObject, "getRefund") + + tracer.vm.PushGoFunction(func(ctx *duktape.Context) int { + if tracer.errorValue != nil { + ctx.PushString(*tracer.errorValue) + } else { + ctx.PushUndefined() + } + return 1 + }) + tracer.vm.PutPropString(logObject, "getError") + + tracer.vm.PutPropString(tracer.stateObject, "log") + + tracer.frame.pushObject(tracer.vm) + tracer.vm.PutPropString(tracer.stateObject, "frame") + + tracer.frameResult.pushObject(tracer.vm) + tracer.vm.PutPropString(tracer.stateObject, "frameResult") + + tracer.dbWrapper.pushObject(tracer.vm) + tracer.vm.PutPropString(tracer.stateObject, "db") + + return tracer, nil +} + +// Stop terminates execution of the tracer at the first opportune moment. +func (jst *jsTracer) Stop(err error) { + jst.reason = err + atomic.StoreUint32(&jst.interrupt, 1) +} + +// call executes a method on a JS object, catching any errors, formatting and +// returning them as error objects. +func (jst *jsTracer) call(noret bool, method string, args ...string) (json.RawMessage, error) { + // Execute the JavaScript call and return any error + jst.vm.PushString(method) + for _, arg := range args { + jst.vm.GetPropString(jst.stateObject, arg) + } + code := jst.vm.PcallProp(jst.tracerObject, len(args)) + defer jst.vm.Pop() + + if code != 0 { + err := jst.vm.SafeToString(-1) + return nil, errors.New(err) + } + // No error occurred, extract return value and return + if noret { + return nil, nil + } + // Push a JSON marshaller onto the stack. We can't marshal from the out- + // side because duktape can crash on large nestings and we can't catch + // C++ exceptions ourselves from Go. TODO(karalabe): Yuck, why wrap?! + jst.vm.PushString("(JSON.stringify)") + jst.vm.Eval() + + jst.vm.Swap(-1, -2) + if code = jst.vm.Pcall(1); code != 0 { + err := jst.vm.SafeToString(-1) + return nil, errors.New(err) + } + return json.RawMessage(jst.vm.SafeToString(-1)), nil +} + +func wrapError(context string, err error) error { + return fmt.Errorf("%v in server-side tracer function '%v'", err, context) +} + +// CaptureStart implements the Tracer interface to initialize the tracing operation. +func (jst *jsTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { + jst.env = env + jst.ctx["type"] = "CALL" + if create { + jst.ctx["type"] = "CREATE" + } + jst.ctx["from"] = from + jst.ctx["to"] = to + jst.ctx["input"] = input + jst.ctx["gas"] = gas + jst.ctx["gasPrice"] = env.TxContext.GasPrice + jst.ctx["value"] = value + + // Initialize the context + jst.ctx["block"] = env.Context.BlockNumber.Uint64() + jst.dbWrapper.db = env.StateDB + // Update list of precompiles based on current block + rules := env.ChainConfig().Rules(env.Context.BlockNumber) + jst.activePrecompiles = vm.ActivePrecompiles(rules) + + // Compute intrinsic gas + isHomestead := env.ChainConfig().IsHomestead(env.Context.BlockNumber) + isIstanbul := env.ChainConfig().IsIstanbul(env.Context.BlockNumber) + intrinsicGas, err := core.IntrinsicGas(input, nil, jst.ctx["type"] == "CREATE", isHomestead, isIstanbul) + if err != nil { + return + } + jst.ctx["intrinsicGas"] = intrinsicGas +} + +// CaptureState implements the Tracer interface to trace a single step of VM execution. +func (jst *jsTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) { + if !jst.traceSteps { + return + } + if jst.err != nil { + return + } + // If tracing was interrupted, set the error and stop + if atomic.LoadUint32(&jst.interrupt) > 0 { + jst.err = jst.reason + jst.env.Cancel() + return + } + jst.opWrapper.op = op + jst.stackWrapper.stack = scope.Stack + jst.memoryWrapper.memory = scope.Memory + jst.contractWrapper.contract = scope.Contract + + *jst.pcValue = uint(pc) + *jst.gasValue = uint(gas) + *jst.costValue = uint(cost) + *jst.depthValue = uint(depth) + *jst.refundValue = uint(jst.env.StateDB.GetRefund()) + + jst.errorValue = nil + if err != nil { + jst.errorValue = new(string) + *jst.errorValue = err.Error() + } + + if _, err := jst.call(true, "step", "log", "db"); err != nil { + jst.err = wrapError("step", err) + } +} + +// CaptureFault implements the Tracer interface to trace an execution fault +func (jst *jsTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, depth int, err error) { + if jst.err != nil { + return + } + // Apart from the error, everything matches the previous invocation + jst.errorValue = new(string) + *jst.errorValue = err.Error() + + if _, err := jst.call(true, "fault", "log", "db"); err != nil { + jst.err = wrapError("fault", err) + } +} + +// CaptureEnd is called after the call finishes to finalize the tracing. +func (jst *jsTracer) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) { + jst.ctx["output"] = output + jst.ctx["time"] = t.String() + jst.ctx["gasUsed"] = gasUsed + + if err != nil { + jst.ctx["error"] = err.Error() + } +} + +// CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct). +func (jst *jsTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { + if !jst.traceCallFrames { + return + } + if jst.err != nil { + return + } + // If tracing was interrupted, set the error and stop + if atomic.LoadUint32(&jst.interrupt) > 0 { + jst.err = jst.reason + return + } + + *jst.frame.typ = typ.String() + *jst.frame.from = from + *jst.frame.to = to + jst.frame.input = common.CopyBytes(input) + *jst.frame.gas = uint(gas) + jst.frame.value = nil + if value != nil { + jst.frame.value = new(big.Int).SetBytes(value.Bytes()) + } + + if _, err := jst.call(true, "enter", "frame"); err != nil { + jst.err = wrapError("enter", err) + } +} + +// CaptureExit is called when EVM exits a scope, even if the scope didn't +// execute any code. +func (jst *jsTracer) CaptureExit(output []byte, gasUsed uint64, err error) { + if !jst.traceCallFrames { + return + } + // If tracing was interrupted, set the error and stop + if atomic.LoadUint32(&jst.interrupt) > 0 { + jst.err = jst.reason + return + } + + jst.frameResult.output = common.CopyBytes(output) + *jst.frameResult.gasUsed = uint(gasUsed) + jst.frameResult.errorValue = nil + if err != nil { + jst.frameResult.errorValue = new(string) + *jst.frameResult.errorValue = err.Error() + } + + if _, err := jst.call(true, "exit", "frameResult"); err != nil { + jst.err = wrapError("exit", err) + } +} + +// GetResult calls the Javascript 'result' function and returns its value, or any accumulated error +func (jst *jsTracer) GetResult() (json.RawMessage, error) { + // Transform the context into a JavaScript object and inject into the state + obj := jst.vm.PushObject() + + for key, val := range jst.ctx { + jst.addToObj(obj, key, val) + } + jst.vm.PutPropString(jst.stateObject, "ctx") + + // Finalize the trace and return the results + result, err := jst.call(false, "result", "ctx", "db") + if err != nil { + jst.err = wrapError("result", err) + } + // Clean up the JavaScript environment + jst.vm.DestroyHeap() + jst.vm.Destroy() + + return result, jst.err +} + +// addToObj pushes a field to a JS object. +func (jst *jsTracer) addToObj(obj int, key string, val interface{}) { + pushValue(jst.vm, val) + jst.vm.PutPropString(obj, key) +} + +func pushValue(ctx *duktape.Context, val interface{}) { + switch val := val.(type) { + case uint64: + ctx.PushUint(uint(val)) + case string: + ctx.PushString(val) + case []byte: + ptr := ctx.PushFixedBuffer(len(val)) + copy(makeSlice(ptr, uint(len(val))), val) + case common.Address: + ptr := ctx.PushFixedBuffer(20) + copy(makeSlice(ptr, 20), val[:]) + case *big.Int: + pushBigInt(val, ctx) + case int: + ctx.PushInt(val) + case uint: + ctx.PushUint(val) + case common.Hash: + ptr := ctx.PushFixedBuffer(32) + copy(makeSlice(ptr, 32), val[:]) + default: + panic(fmt.Sprintf("unsupported type: %T", val)) + } +} diff --git a/eth/tracers/tracer_test.go b/eth/tracers/js/tracer_test.go similarity index 75% rename from eth/tracers/tracer_test.go rename to eth/tracers/js/tracer_test.go index 0e78f34b6..cf0a4aa82 100644 --- a/eth/tracers/tracer_test.go +++ b/eth/tracers/js/tracer_test.go @@ -14,7 +14,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -package tracers +package js import ( "encoding/json" @@ -26,6 +26,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/eth/tracers" "github.com/ethereum/go-ethereum/params" ) @@ -58,13 +59,13 @@ func testCtx() *vmContext { return &vmContext{blockCtx: vm.BlockContext{BlockNumber: big.NewInt(1)}, txCtx: vm.TxContext{GasPrice: big.NewInt(100000)}} } -func runTrace(tracer Tracer, vmctx *vmContext, chaincfg *params.ChainConfig) (json.RawMessage, error) { - env := vm.NewEVM(vmctx.blockCtx, vmctx.txCtx, &dummyStatedb{}, chaincfg, vm.Config{Debug: true, Tracer: tracer}) +func runTrace(tracer tracers.Tracer, vmctx *vmContext, chaincfg *params.ChainConfig) (json.RawMessage, error) { var ( + env = vm.NewEVM(vmctx.blockCtx, vmctx.txCtx, &dummyStatedb{}, chaincfg, vm.Config{Debug: true, Tracer: tracer}) startGas uint64 = 10000 value = big.NewInt(0) + contract = vm.NewContract(account{}, account{}, value, startGas) ) - contract := vm.NewContract(account{}, account{}, value, startGas) contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x1, 0x0} tracer.CaptureStart(env, contract.Caller(), contract.Address(), false, []byte{}, startGas, value) @@ -79,14 +80,11 @@ func runTrace(tracer Tracer, vmctx *vmContext, chaincfg *params.ChainConfig) (js func TestTracer(t *testing.T) { execTracer := func(code string) ([]byte, string) { t.Helper() - tracer, err := New(code, new(Context)) + tracer, err := newJsTracer(code, nil) if err != nil { t.Fatal(err) } - ret, err := runTrace(tracer, &vmContext{ - blockCtx: vm.BlockContext{BlockNumber: big.NewInt(1)}, - txCtx: vm.TxContext{GasPrice: big.NewInt(100000)}, - }, params.TestChainConfig) + ret, err := runTrace(tracer, testCtx(), params.TestChainConfig) if err != nil { return nil, err.Error() // Stringify to allow comparison without nil checks } @@ -131,9 +129,8 @@ func TestTracer(t *testing.T) { func TestHalt(t *testing.T) { t.Skip("duktape doesn't support abortion") - timeout := errors.New("stahp") - tracer, err := New("{step: function() { while(1); }, result: function() { return null; }}", new(Context)) + tracer, err := newJsTracer("{step: function() { while(1); }, result: function() { return null; }, fault: function(){}}", nil) if err != nil { t.Fatal(err) } @@ -147,18 +144,19 @@ func TestHalt(t *testing.T) { } func TestHaltBetweenSteps(t *testing.T) { - tracer, err := New("{step: function() {}, fault: function() {}, result: function() { return null; }}", new(Context)) + tracer, err := newJsTracer("{step: function() {}, fault: function() {}, result: function() { return null; }}", nil) if err != nil { t.Fatal(err) } - env := vm.NewEVM(vm.BlockContext{BlockNumber: big.NewInt(1)}, vm.TxContext{}, &dummyStatedb{}, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer}) + env := vm.NewEVM(vm.BlockContext{BlockNumber: big.NewInt(1)}, vm.TxContext{GasPrice: big.NewInt(1)}, &dummyStatedb{}, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer}) scope := &vm.ScopeContext{ Contract: vm.NewContract(&account{}, &account{}, big.NewInt(0), 0), } - tracer.CaptureState(env, 0, 0, 0, 0, scope, nil, 0, nil) + tracer.CaptureStart(env, common.Address{}, common.Address{}, false, []byte{}, 0, big.NewInt(0)) + tracer.CaptureState(0, 0, 0, 0, scope, nil, 0, nil) timeout := errors.New("stahp") tracer.Stop(timeout) - tracer.CaptureState(env, 0, 0, 0, 0, scope, nil, 0, nil) + tracer.CaptureState(0, 0, 0, 0, scope, nil, 0, nil) if _, err := tracer.GetResult(); err.Error() != timeout.Error() { t.Errorf("Expected timeout error, got %v", err) @@ -168,24 +166,16 @@ func TestHaltBetweenSteps(t *testing.T) { // TestNoStepExec tests a regular value transfer (no exec), and accessing the statedb // in 'result' func TestNoStepExec(t *testing.T) { - runEmptyTrace := func(tracer Tracer, vmctx *vmContext) (json.RawMessage, error) { - env := vm.NewEVM(vmctx.blockCtx, vmctx.txCtx, &dummyStatedb{}, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer}) - startGas := uint64(10000) - contract := vm.NewContract(account{}, account{}, big.NewInt(0), startGas) - tracer.CaptureStart(env, contract.Caller(), contract.Address(), false, []byte{}, startGas, big.NewInt(0)) - tracer.CaptureEnd(nil, startGas-contract.Gas, 1, nil) - return tracer.GetResult() - } execTracer := func(code string) []byte { t.Helper() - tracer, err := New(code, new(Context)) + tracer, err := newJsTracer(code, nil) if err != nil { t.Fatal(err) } - ret, err := runEmptyTrace(tracer, &vmContext{ - blockCtx: vm.BlockContext{BlockNumber: big.NewInt(1)}, - txCtx: vm.TxContext{GasPrice: big.NewInt(100000)}, - }) + env := vm.NewEVM(vm.BlockContext{BlockNumber: big.NewInt(1)}, vm.TxContext{GasPrice: big.NewInt(100)}, &dummyStatedb{}, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer}) + tracer.CaptureStart(env, common.Address{}, common.Address{}, false, []byte{}, 1000, big.NewInt(0)) + tracer.CaptureEnd(nil, 0, 1, nil) + ret, err := tracer.GetResult() if err != nil { t.Fatal(err) } @@ -212,7 +202,7 @@ func TestIsPrecompile(t *testing.T) { chaincfg.IstanbulBlock = big.NewInt(200) chaincfg.BerlinBlock = big.NewInt(300) txCtx := vm.TxContext{GasPrice: big.NewInt(100000)} - tracer, err := New("{addr: toAddress('0000000000000000000000000000000000000009'), res: null, step: function() { this.res = isPrecompiled(this.addr); }, fault: function() {}, result: function() { return this.res; }}", new(Context)) + tracer, err := newJsTracer("{addr: toAddress('0000000000000000000000000000000000000009'), res: null, step: function() { this.res = isPrecompiled(this.addr); }, fault: function() {}, result: function() { return this.res; }}", nil) if err != nil { t.Fatal(err) } @@ -226,7 +216,7 @@ func TestIsPrecompile(t *testing.T) { t.Errorf("Tracer should not consider blake2f as precompile in byzantium") } - tracer, _ = New("{addr: toAddress('0000000000000000000000000000000000000009'), res: null, step: function() { this.res = isPrecompiled(this.addr); }, fault: function() {}, result: function() { return this.res; }}", new(Context)) + tracer, _ = newJsTracer("{addr: toAddress('0000000000000000000000000000000000000009'), res: null, step: function() { this.res = isPrecompiled(this.addr); }, fault: function() {}, result: function() { return this.res; }}", nil) blockCtx = vm.BlockContext{BlockNumber: big.NewInt(250)} res, err = runTrace(tracer, &vmContext{blockCtx, txCtx}, chaincfg) if err != nil { @@ -239,23 +229,20 @@ func TestIsPrecompile(t *testing.T) { func TestEnterExit(t *testing.T) { // test that either both or none of enter() and exit() are defined - if _, err := New("{step: function() {}, fault: function() {}, result: function() { return null; }, enter: function() {}}", new(Context)); err == nil { + if _, err := newJsTracer("{step: function() {}, fault: function() {}, result: function() { return null; }, enter: function() {}}", new(tracers.Context)); err == nil { t.Fatal("tracer creation should've failed without exit() definition") } - if _, err := New("{step: function() {}, fault: function() {}, result: function() { return null; }, enter: function() {}, exit: function() {}}", new(Context)); err != nil { + if _, err := newJsTracer("{step: function() {}, fault: function() {}, result: function() { return null; }, enter: function() {}, exit: function() {}}", new(tracers.Context)); err != nil { t.Fatal(err) } - // test that the enter and exit method are correctly invoked and the values passed - tracer, err := New("{enters: 0, exits: 0, enterGas: 0, gasUsed: 0, step: function() {}, fault: function() {}, result: function() { return {enters: this.enters, exits: this.exits, enterGas: this.enterGas, gasUsed: this.gasUsed} }, enter: function(frame) { this.enters++; this.enterGas = frame.getGas(); }, exit: function(res) { this.exits++; this.gasUsed = res.getGasUsed(); }}", new(Context)) + tracer, err := newJsTracer("{enters: 0, exits: 0, enterGas: 0, gasUsed: 0, step: function() {}, fault: function() {}, result: function() { return {enters: this.enters, exits: this.exits, enterGas: this.enterGas, gasUsed: this.gasUsed} }, enter: function(frame) { this.enters++; this.enterGas = frame.getGas(); }, exit: function(res) { this.exits++; this.gasUsed = res.getGasUsed(); }}", new(tracers.Context)) if err != nil { t.Fatal(err) } - scope := &vm.ScopeContext{ Contract: vm.NewContract(&account{}, &account{}, big.NewInt(0), 0), } - tracer.CaptureEnter(vm.CALL, scope.Contract.Caller(), scope.Contract.Address(), []byte{}, 1000, new(big.Int)) tracer.CaptureExit([]byte{}, 400, nil) diff --git a/eth/tracers/native/call.go b/eth/tracers/native/call.go index 8f22baa10..16ea75aa4 100644 --- a/eth/tracers/native/call.go +++ b/eth/tracers/native/call.go @@ -31,7 +31,7 @@ import ( ) func init() { - tracers.RegisterNativeTracer("callTracer", NewCallTracer) + register("callTracer", newCallTracer) } type callFrame struct { @@ -48,21 +48,24 @@ type callFrame struct { } type callTracer struct { + env *vm.EVM callstack []callFrame interrupt uint32 // Atomic flag to signal execution interruption reason error // Textual reason for the interruption } -// NewCallTracer returns a native go tracer which tracks +// newCallTracer returns a native go tracer which tracks // call frames of a tx, and implements vm.EVMLogger. -func NewCallTracer() tracers.Tracer { +func newCallTracer() tracers.Tracer { // First callframe contains tx context info // and is populated on start and end. t := &callTracer{callstack: make([]callFrame, 1)} return t } +// CaptureStart implements the EVMLogger interface to initialize the tracing operation. func (t *callTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { + t.env = env t.callstack[0] = callFrame{ Type: "CALL", From: addrToHex(from), @@ -76,6 +79,7 @@ func (t *callTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Ad } } +// CaptureEnd is called after the call finishes to finalize the tracing. func (t *callTracer) CaptureEnd(output []byte, gasUsed uint64, _ time.Duration, err error) { t.callstack[0].GasUsed = uintToHex(gasUsed) if err != nil { @@ -88,16 +92,19 @@ func (t *callTracer) CaptureEnd(output []byte, gasUsed uint64, _ time.Duration, } } -func (t *callTracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) { +// CaptureState implements the EVMLogger interface to trace a single step of VM execution. +func (t *callTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) { } -func (t *callTracer) CaptureFault(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, _ *vm.ScopeContext, depth int, err error) { +// CaptureFault implements the EVMLogger interface to trace an execution fault. +func (t *callTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, _ *vm.ScopeContext, depth int, err error) { } +// CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct). func (t *callTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { // Skip if tracing was interrupted if atomic.LoadUint32(&t.interrupt) > 0 { - // TODO: env.Cancel() + t.env.Cancel() return } @@ -112,6 +119,8 @@ func (t *callTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common. t.callstack = append(t.callstack, call) } +// CaptureExit is called when EVM exits a scope, even if the scope didn't +// execute any code. func (t *callTracer) CaptureExit(output []byte, gasUsed uint64, err error) { size := len(t.callstack) if size <= 1 { @@ -134,6 +143,8 @@ func (t *callTracer) CaptureExit(output []byte, gasUsed uint64, err error) { t.callstack[size-1].Calls = append(t.callstack[size-1].Calls, call) } +// GetResult returns the json-encoded nested list of call traces, and any +// error arising from the encoding or forceful termination (via `Stop`). func (t *callTracer) GetResult() (json.RawMessage, error) { if len(t.callstack) != 1 { return nil, errors.New("incorrect number of top-level calls") @@ -145,6 +156,7 @@ func (t *callTracer) GetResult() (json.RawMessage, error) { return json.RawMessage(res), t.reason } +// Stop terminates execution of the tracer at the first opportune moment. func (t *callTracer) Stop(err error) { t.reason = err atomic.StoreUint32(&t.interrupt, 1) diff --git a/eth/tracers/native/noop.go b/eth/tracers/native/noop.go index 554bb18f1..ee110ef7d 100644 --- a/eth/tracers/native/noop.go +++ b/eth/tracers/native/noop.go @@ -1,3 +1,19 @@ +// Copyright 2021 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 . + package native import ( @@ -11,36 +27,48 @@ import ( ) func init() { - tracers.RegisterNativeTracer("noopTracerNative", NewNoopTracer) + register("noopTracerNative", newNoopTracer) } +// noopTracer is a go implementation of the Tracer interface which +// performs no action. It's mostly useful for testing purposes. type noopTracer struct{} -func NewNoopTracer() tracers.Tracer { +// newNoopTracer returns a new noop tracer. +func newNoopTracer() tracers.Tracer { return &noopTracer{} } +// CaptureStart implements the EVMLogger interface to initialize the tracing operation. func (t *noopTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { } +// CaptureEnd is called after the call finishes to finalize the tracing. func (t *noopTracer) CaptureEnd(output []byte, gasUsed uint64, _ time.Duration, err error) { } -func (t *noopTracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) { +// CaptureState implements the EVMLogger interface to trace a single step of VM execution. +func (t *noopTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) { } -func (t *noopTracer) CaptureFault(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, _ *vm.ScopeContext, depth int, err error) { +// CaptureFault implements the EVMLogger interface to trace an execution fault. +func (t *noopTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, _ *vm.ScopeContext, depth int, err error) { } +// CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct). func (t *noopTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { } +// CaptureExit is called when EVM exits a scope, even if the scope didn't +// execute any code. func (t *noopTracer) CaptureExit(output []byte, gasUsed uint64, err error) { } +// GetResult returns an empty json object. func (t *noopTracer) GetResult() (json.RawMessage, error) { return json.RawMessage(`{}`), nil } +// Stop terminates execution of the tracer at the first opportune moment. func (t *noopTracer) Stop(err error) { } diff --git a/eth/tracers/native/tracer.go b/eth/tracers/native/tracer.go new file mode 100644 index 000000000..3158654f3 --- /dev/null +++ b/eth/tracers/native/tracer.go @@ -0,0 +1,79 @@ +// Copyright 2021 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 . + +/* +Package native is a collection of tracers written in go. + +In order to add a native tracer and have it compiled into the binary, a new +file needs to be added to this folder, containing an implementation of the +`eth.tracers.Tracer` interface. + +Aside from implementing the tracer, it also needs to register itself, using the +`register` method -- and this needs to be done in the package initialization. + +Example: + +```golang +func init() { + register("noopTracerNative", newNoopTracer) +} +``` +*/ +package native + +import ( + "errors" + + "github.com/ethereum/go-ethereum/eth/tracers" +) + +// init registers itself this packages as a lookup for tracers. +func init() { + tracers.RegisterLookup(false, lookup) +} + +/* +ctors is a map of package-local tracer constructors. + +We cannot be certain about the order of init-functions within a package, +The go spec (https://golang.org/ref/spec#Package_initialization) says + +> To ensure reproducible initialization behavior, build systems +> are encouraged to present multiple files belonging to the same +> package in lexical file name order to a compiler. + +Hence, we cannot make the map in init, but must make it upon first use. +*/ +var ctors map[string]func() tracers.Tracer + +// register is used by native tracers to register their presence. +func register(name string, ctor func() tracers.Tracer) { + if ctors == nil { + ctors = make(map[string]func() tracers.Tracer) + } + ctors[name] = ctor +} + +// lookup returns a tracer, if one can be matched to the given name. +func lookup(name string, ctx *tracers.Context) (tracers.Tracer, error) { + if ctors == nil { + ctors = make(map[string]func() tracers.Tracer) + } + if ctor, ok := ctors[name]; ok { + return ctor(), nil + } + return nil, errors.New("no tracer found") +} diff --git a/eth/tracers/tracers.go b/eth/tracers/tracers.go index 79534c636..e7073e7d2 100644 --- a/eth/tracers/tracers.go +++ b/eth/tracers/tracers.go @@ -14,18 +14,25 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -// Package tracers is a collection of JavaScript transaction tracers. +// Package tracers is a manager for transaction tracing engines. package tracers import ( "encoding/json" - "strings" - "unicode" + "errors" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/vm" - "github.com/ethereum/go-ethereum/eth/tracers/internal/tracers" ) +// Context contains some contextual infos for a transaction execution that is not +// available from within the EVM object. +type Context struct { + BlockHash common.Hash // Hash of the block the tx is contained within (zero if dangling tx or call) + TxIndex int // Index of the transaction within a block (zero if dangling tx or call) + TxHash common.Hash // Hash of the transaction being traced (zero if dangling call) +} + // Tracer interface extends vm.EVMLogger and additionally // allows collecting the tracing result. type Tracer interface { @@ -35,50 +42,31 @@ type Tracer interface { Stop(err error) } +type lookupFunc func(string, *Context) (Tracer, error) + var ( - nativeTracers map[string]func() Tracer = make(map[string]func() Tracer) - jsTracers = make(map[string]string) + lookups []lookupFunc ) -// RegisterNativeTracer makes native tracers which adhere -// to the `Tracer` interface available to the rest of the codebase. -// It is typically invoked in the `init()` function, e.g. see the `native/call.go`. -func RegisterNativeTracer(name string, ctor func() Tracer) { - nativeTracers[name] = ctor +// RegisterLookup registers a method as a lookup for tracers, meaning that +// users can invoke a named tracer through that lookup. If 'wildcard' is true, +// then the lookup will be placed last. This is typically meant for interpreted +// engines (js) which can evaluate dynamic user-supplied code. +func RegisterLookup(wildcard bool, lookup lookupFunc) { + if wildcard { + lookups = append(lookups, lookup) + } else { + lookups = append([]lookupFunc{lookup}, lookups...) + } } -// New returns a new instance of a tracer, -// 1. If 'code' is the name of a registered native tracer, then that tracer -// is instantiated and returned -// 2. If 'code' is the name of a registered js-tracer, then that tracer is -// instantiated and returned -// 3. Otherwise, the code is interpreted as the js code of a js-tracer, and -// is evaluated and returned. +// New returns a new instance of a tracer, by iterating through the +// registered lookups. func New(code string, ctx *Context) (Tracer, error) { - // Resolve native tracer - if fn, ok := nativeTracers[code]; ok { - return fn(), nil - } - // Resolve js-tracers by name and assemble the tracer object - if tracer, ok := jsTracers[code]; ok { - code = tracer - } - return newJsTracer(code, ctx) -} - -// camel converts a snake cased input string into a camel cased output. -func camel(str string) string { - pieces := strings.Split(str, "_") - for i := 1; i < len(pieces); i++ { - pieces[i] = string(unicode.ToUpper(rune(pieces[i][0]))) + pieces[i][1:] - } - return strings.Join(pieces, "") -} - -// init retrieves the JavaScript transaction tracers included in go-ethereum. -func init() { - for _, file := range tracers.AssetNames() { - name := camel(strings.TrimSuffix(file, ".js")) - jsTracers[name] = string(tracers.MustAsset(file)) + for _, lookup := range lookups { + if tracer, err := lookup(code, ctx); err == nil { + return tracer, nil + } } + return nil, errors.New("tracer not found") } diff --git a/eth/tracers/tracers_test.go b/eth/tracers/tracers_test.go index a027caa96..915cab10d 100644 --- a/eth/tracers/tracers_test.go +++ b/eth/tracers/tracers_test.go @@ -17,11 +17,7 @@ package tracers import ( - "crypto/ecdsa" - "crypto/rand" - "encoding/json" "math/big" - "reflect" "testing" "github.com/ethereum/go-ethereum/common" @@ -35,56 +31,6 @@ import ( "github.com/ethereum/go-ethereum/tests" ) -// To generate a new callTracer test, copy paste the makeTest method below into -// a Geth console and call it with a transaction hash you which to export. - -/* -// makeTest generates a callTracer test by running a prestate reassembled and a -// call trace run, assembling all the gathered information into a test case. -var makeTest = function(tx, rewind) { - // Generate the genesis block from the block, transaction and prestate data - var block = eth.getBlock(eth.getTransaction(tx).blockHash); - var genesis = eth.getBlock(block.parentHash); - - delete genesis.gasUsed; - delete genesis.logsBloom; - delete genesis.parentHash; - delete genesis.receiptsRoot; - delete genesis.sha3Uncles; - delete genesis.size; - delete genesis.transactions; - delete genesis.transactionsRoot; - delete genesis.uncles; - - genesis.gasLimit = genesis.gasLimit.toString(); - genesis.number = genesis.number.toString(); - genesis.timestamp = genesis.timestamp.toString(); - - genesis.alloc = debug.traceTransaction(tx, {tracer: "prestateTracer", rewind: rewind}); - for (var key in genesis.alloc) { - genesis.alloc[key].nonce = genesis.alloc[key].nonce.toString(); - } - genesis.config = admin.nodeInfo.protocols.eth.config; - - // Generate the call trace and produce the test input - var result = debug.traceTransaction(tx, {tracer: "callTracer", rewind: rewind}); - delete result.time; - - console.log(JSON.stringify({ - genesis: genesis, - context: { - number: block.number.toString(), - difficulty: block.difficulty, - timestamp: block.timestamp.toString(), - gasLimit: block.gasLimit.toString(), - miner: block.miner, - }, - input: eth.getRawTransaction(tx), - result: result, - }, null, 2)); -} -*/ - // callTrace is the result of a callTracer run. type callTrace struct { Type string `json:"type"` @@ -99,184 +45,6 @@ type callTrace struct { Calls []callTrace `json:"calls,omitempty"` } -// TestZeroValueToNotExitCall tests the calltracer(s) on the following: -// Tx to A, A calls B with zero value. B does not already exist. -// Expected: that enter/exit is invoked and the inner call is shown in the result -func TestZeroValueToNotExitCall(t *testing.T) { - var to = common.HexToAddress("0x00000000000000000000000000000000deadbeef") - privkey, err := crypto.HexToECDSA("0000000000000000deadbeef00000000000000000000000000000000deadbeef") - if err != nil { - t.Fatalf("err %v", err) - } - signer := types.NewEIP155Signer(big.NewInt(1)) - tx, err := types.SignNewTx(privkey, signer, &types.LegacyTx{ - GasPrice: big.NewInt(0), - Gas: 50000, - To: &to, - }) - if err != nil { - t.Fatalf("err %v", err) - } - origin, _ := signer.Sender(tx) - txContext := vm.TxContext{ - Origin: origin, - GasPrice: big.NewInt(1), - } - context := vm.BlockContext{ - CanTransfer: core.CanTransfer, - Transfer: core.Transfer, - Coinbase: common.Address{}, - BlockNumber: new(big.Int).SetUint64(8000000), - Time: new(big.Int).SetUint64(5), - Difficulty: big.NewInt(0x30000), - GasLimit: uint64(6000000), - } - var code = []byte{ - byte(vm.PUSH1), 0x0, byte(vm.DUP1), byte(vm.DUP1), byte(vm.DUP1), // in and outs zero - byte(vm.DUP1), byte(vm.PUSH1), 0xff, byte(vm.GAS), // value=0,address=0xff, gas=GAS - byte(vm.CALL), - } - var alloc = core.GenesisAlloc{ - to: core.GenesisAccount{ - Nonce: 1, - Code: code, - }, - origin: core.GenesisAccount{ - Nonce: 0, - Balance: big.NewInt(500000000000000), - }, - } - _, statedb := tests.MakePreState(rawdb.NewMemoryDatabase(), alloc, false) - // Create the tracer, the EVM environment and run it - tracer, err := New("callTracerJs", new(Context)) - if err != nil { - t.Fatalf("failed to create call tracer: %v", err) - } - evm := vm.NewEVM(context, txContext, statedb, params.MainnetChainConfig, vm.Config{Debug: true, Tracer: tracer}) - msg, err := tx.AsMessage(signer, nil) - if err != nil { - t.Fatalf("failed to prepare transaction for tracing: %v", err) - } - st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas())) - if _, err = st.TransitionDb(); err != nil { - t.Fatalf("failed to execute transaction: %v", err) - } - // Retrieve the trace result and compare against the etalon - res, err := tracer.GetResult() - if err != nil { - t.Fatalf("failed to retrieve trace result: %v", err) - } - have := new(callTrace) - if err := json.Unmarshal(res, have); err != nil { - t.Fatalf("failed to unmarshal trace result: %v", err) - } - wantStr := `{"type":"CALL","from":"0x682a80a6f560eec50d54e63cbeda1c324c5f8d1b","to":"0x00000000000000000000000000000000deadbeef","value":"0x0","gas":"0x7148","gasUsed":"0x2d0","input":"0x","output":"0x","calls":[{"type":"CALL","from":"0x00000000000000000000000000000000deadbeef","to":"0x00000000000000000000000000000000000000ff","value":"0x0","gas":"0x6cbf","gasUsed":"0x0","input":"0x","output":"0x"}]}` - want := new(callTrace) - json.Unmarshal([]byte(wantStr), want) - if !jsonEqual(have, want) { - t.Error("have != want") - } -} - -func TestPrestateTracerCreate2(t *testing.T) { - unsignedTx := types.NewTransaction(1, common.HexToAddress("0x00000000000000000000000000000000deadbeef"), - new(big.Int), 5000000, big.NewInt(1), []byte{}) - - privateKeyECDSA, err := ecdsa.GenerateKey(crypto.S256(), rand.Reader) - if err != nil { - t.Fatalf("err %v", err) - } - signer := types.NewEIP155Signer(big.NewInt(1)) - tx, err := types.SignTx(unsignedTx, signer, privateKeyECDSA) - if err != nil { - t.Fatalf("err %v", err) - } - /** - This comes from one of the test-vectors on the Skinny Create2 - EIP - - address 0x00000000000000000000000000000000deadbeef - salt 0x00000000000000000000000000000000000000000000000000000000cafebabe - init_code 0xdeadbeef - gas (assuming no mem expansion): 32006 - result: 0x60f3f640a8508fC6a86d45DF051962668E1e8AC7 - */ - origin, _ := signer.Sender(tx) - txContext := vm.TxContext{ - Origin: origin, - GasPrice: big.NewInt(1), - } - context := vm.BlockContext{ - CanTransfer: core.CanTransfer, - Transfer: core.Transfer, - Coinbase: common.Address{}, - BlockNumber: new(big.Int).SetUint64(8000000), - Time: new(big.Int).SetUint64(5), - Difficulty: big.NewInt(0x30000), - GasLimit: uint64(6000000), - } - alloc := core.GenesisAlloc{} - - // The code pushes 'deadbeef' into memory, then the other params, and calls CREATE2, then returns - // the address - alloc[common.HexToAddress("0x00000000000000000000000000000000deadbeef")] = core.GenesisAccount{ - Nonce: 1, - Code: hexutil.MustDecode("0x63deadbeef60005263cafebabe6004601c6000F560005260206000F3"), - Balance: big.NewInt(1), - } - alloc[origin] = core.GenesisAccount{ - Nonce: 1, - Code: []byte{}, - Balance: big.NewInt(500000000000000), - } - _, statedb := tests.MakePreState(rawdb.NewMemoryDatabase(), alloc, false) - - // Create the tracer, the EVM environment and run it - tracer, err := New("prestateTracer", new(Context)) - if err != nil { - t.Fatalf("failed to create call tracer: %v", err) - } - evm := vm.NewEVM(context, txContext, statedb, params.MainnetChainConfig, vm.Config{Debug: true, Tracer: tracer}) - - msg, err := tx.AsMessage(signer, nil) - if err != nil { - t.Fatalf("failed to prepare transaction for tracing: %v", err) - } - st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas())) - if _, err = st.TransitionDb(); err != nil { - t.Fatalf("failed to execute transaction: %v", err) - } - // Retrieve the trace result and compare against the etalon - res, err := tracer.GetResult() - if err != nil { - t.Fatalf("failed to retrieve trace result: %v", err) - } - ret := make(map[string]interface{}) - if err := json.Unmarshal(res, &ret); err != nil { - t.Fatalf("failed to unmarshal trace result: %v", err) - } - if _, has := ret["0x60f3f640a8508fc6a86d45df051962668e1e8ac7"]; !has { - t.Fatalf("Expected 0x60f3f640a8508fc6a86d45df051962668e1e8ac7 in result") - } -} - -// jsonEqual is similar to reflect.DeepEqual, but does a 'bounce' via json prior to -// comparison -func jsonEqual(x, y interface{}) bool { - xTrace := new(callTrace) - yTrace := new(callTrace) - if xj, err := json.Marshal(x); err == nil { - json.Unmarshal(xj, xTrace) - } else { - return false - } - if yj, err := json.Marshal(y); err == nil { - json.Unmarshal(yj, yTrace) - } else { - return false - } - return reflect.DeepEqual(xTrace, yTrace) -} - func BenchmarkTransactionTrace(b *testing.B) { key, _ := crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") from := crypto.PubkeyToAddress(key.PublicKey) From 0efed7f58baed63dab5351667e677a688458a3d7 Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Tue, 9 Nov 2021 14:45:34 +0100 Subject: [PATCH 04/30] cmd/devp2p/internal/ethtest: clarify protocol version in tests (#23872) Debugging recent geth failures in hive, it took a while to realize that it's because geth doesn't support eth/65 any longer. This PR makes such failures a bit more easy to figure out. --- cmd/devp2p/internal/ethtest/helpers.go | 2 +- cmd/devp2p/internal/ethtest/suite.go | 88 +++++++++++++------------- 2 files changed, 45 insertions(+), 45 deletions(-) diff --git a/cmd/devp2p/internal/ethtest/helpers.go b/cmd/devp2p/internal/ethtest/helpers.go index 88d8e143c..e695cd42d 100644 --- a/cmd/devp2p/internal/ethtest/helpers.go +++ b/cmd/devp2p/internal/ethtest/helpers.go @@ -131,7 +131,7 @@ func (c *Conn) handshake() error { } c.negotiateEthProtocol(msg.Caps) if c.negotiatedProtoVersion == 0 { - return fmt.Errorf("unexpected eth protocol version") + return fmt.Errorf("could not negotiate protocol (remote caps: %v, local eth version: %v)", msg.Caps, c.ourHighestProtoVersion) } return nil default: diff --git a/cmd/devp2p/internal/ethtest/suite.go b/cmd/devp2p/internal/ethtest/suite.go index bbc955cd7..28ba4aa76 100644 --- a/cmd/devp2p/internal/ethtest/suite.go +++ b/cmd/devp2p/internal/ethtest/suite.go @@ -52,35 +52,35 @@ func NewSuite(dest *enode.Node, chainfile string, genesisfile string) (*Suite, e func (s *Suite) AllEthTests() []utesting.Test { return []utesting.Test{ // status - {Name: "TestStatus", Fn: s.TestStatus}, + {Name: "TestStatus65", Fn: s.TestStatus65}, {Name: "TestStatus66", Fn: s.TestStatus66}, // get block headers - {Name: "TestGetBlockHeaders", Fn: s.TestGetBlockHeaders}, + {Name: "TestGetBlockHeaders65", Fn: s.TestGetBlockHeaders65}, {Name: "TestGetBlockHeaders66", Fn: s.TestGetBlockHeaders66}, {Name: "TestSimultaneousRequests66", Fn: s.TestSimultaneousRequests66}, {Name: "TestSameRequestID66", Fn: s.TestSameRequestID66}, {Name: "TestZeroRequestID66", Fn: s.TestZeroRequestID66}, // get block bodies - {Name: "TestGetBlockBodies", Fn: s.TestGetBlockBodies}, + {Name: "TestGetBlockBodies65", Fn: s.TestGetBlockBodies65}, {Name: "TestGetBlockBodies66", Fn: s.TestGetBlockBodies66}, // broadcast - {Name: "TestBroadcast", Fn: s.TestBroadcast}, + {Name: "TestBroadcast65", Fn: s.TestBroadcast65}, {Name: "TestBroadcast66", Fn: s.TestBroadcast66}, - {Name: "TestLargeAnnounce", Fn: s.TestLargeAnnounce}, + {Name: "TestLargeAnnounce65", Fn: s.TestLargeAnnounce65}, {Name: "TestLargeAnnounce66", Fn: s.TestLargeAnnounce66}, - {Name: "TestOldAnnounce", Fn: s.TestOldAnnounce}, + {Name: "TestOldAnnounce65", Fn: s.TestOldAnnounce65}, {Name: "TestOldAnnounce66", Fn: s.TestOldAnnounce66}, - {Name: "TestBlockHashAnnounce", Fn: s.TestBlockHashAnnounce}, + {Name: "TestBlockHashAnnounce65", Fn: s.TestBlockHashAnnounce65}, {Name: "TestBlockHashAnnounce66", Fn: s.TestBlockHashAnnounce66}, // malicious handshakes + status - {Name: "TestMaliciousHandshake", Fn: s.TestMaliciousHandshake}, - {Name: "TestMaliciousStatus", Fn: s.TestMaliciousStatus}, + {Name: "TestMaliciousHandshake65", Fn: s.TestMaliciousHandshake65}, + {Name: "TestMaliciousStatus65", Fn: s.TestMaliciousStatus65}, {Name: "TestMaliciousHandshake66", Fn: s.TestMaliciousHandshake66}, {Name: "TestMaliciousStatus66", Fn: s.TestMaliciousStatus66}, // test transactions - {Name: "TestTransaction", Fn: s.TestTransaction}, + {Name: "TestTransaction65", Fn: s.TestTransaction65}, {Name: "TestTransaction66", Fn: s.TestTransaction66}, - {Name: "TestMaliciousTx", Fn: s.TestMaliciousTx}, + {Name: "TestMaliciousTx65", Fn: s.TestMaliciousTx65}, {Name: "TestMaliciousTx66", Fn: s.TestMaliciousTx66}, {Name: "TestLargeTxRequest66", Fn: s.TestLargeTxRequest66}, {Name: "TestNewPooledTxs66", Fn: s.TestNewPooledTxs66}, @@ -89,17 +89,17 @@ func (s *Suite) AllEthTests() []utesting.Test { func (s *Suite) EthTests() []utesting.Test { return []utesting.Test{ - {Name: "TestStatus", Fn: s.TestStatus}, - {Name: "TestGetBlockHeaders", Fn: s.TestGetBlockHeaders}, - {Name: "TestGetBlockBodies", Fn: s.TestGetBlockBodies}, - {Name: "TestBroadcast", Fn: s.TestBroadcast}, - {Name: "TestLargeAnnounce", Fn: s.TestLargeAnnounce}, - {Name: "TestOldAnnounce", Fn: s.TestOldAnnounce}, - {Name: "TestBlockHashAnnounce", Fn: s.TestBlockHashAnnounce}, - {Name: "TestMaliciousHandshake", Fn: s.TestMaliciousHandshake}, - {Name: "TestMaliciousStatus", Fn: s.TestMaliciousStatus}, - {Name: "TestTransaction", Fn: s.TestTransaction}, - {Name: "TestMaliciousTx", Fn: s.TestMaliciousTx}, + {Name: "TestStatus65", Fn: s.TestStatus65}, + {Name: "TestGetBlockHeaders65", Fn: s.TestGetBlockHeaders65}, + {Name: "TestGetBlockBodies65", Fn: s.TestGetBlockBodies65}, + {Name: "TestBroadcast65", Fn: s.TestBroadcast65}, + {Name: "TestLargeAnnounce65", Fn: s.TestLargeAnnounce65}, + {Name: "TestOldAnnounce65", Fn: s.TestOldAnnounce65}, + {Name: "TestBlockHashAnnounce65", Fn: s.TestBlockHashAnnounce65}, + {Name: "TestMaliciousHandshake65", Fn: s.TestMaliciousHandshake65}, + {Name: "TestMaliciousStatus65", Fn: s.TestMaliciousStatus65}, + {Name: "TestTransaction65", Fn: s.TestTransaction65}, + {Name: "TestMaliciousTx65", Fn: s.TestMaliciousTx65}, } } @@ -130,9 +130,9 @@ var ( eth65 = false // indicates whether suite should negotiate eth65 connection or below. ) -// TestStatus attempts to connect to the given node and exchange +// TestStatus65 attempts to connect to the given node and exchange // a status message with it. -func (s *Suite) TestStatus(t *utesting.T) { +func (s *Suite) TestStatus65(t *utesting.T) { conn, err := s.dial() if err != nil { t.Fatalf("dial failed: %v", err) @@ -156,9 +156,9 @@ func (s *Suite) TestStatus66(t *utesting.T) { } } -// TestGetBlockHeaders tests whether the given node can respond to +// TestGetBlockHeaders65 tests whether the given node can respond to // a `GetBlockHeaders` request accurately. -func (s *Suite) TestGetBlockHeaders(t *utesting.T) { +func (s *Suite) TestGetBlockHeaders65(t *utesting.T) { conn, err := s.dial() if err != nil { t.Fatalf("dial failed: %v", err) @@ -392,9 +392,9 @@ func (s *Suite) TestZeroRequestID66(t *utesting.T) { } } -// TestGetBlockBodies tests whether the given node can respond to +// TestGetBlockBodies65 tests whether the given node can respond to // a `GetBlockBodies` request and that the response is accurate. -func (s *Suite) TestGetBlockBodies(t *utesting.T) { +func (s *Suite) TestGetBlockBodies65(t *utesting.T) { conn, err := s.dial() if err != nil { t.Fatalf("dial failed: %v", err) @@ -460,9 +460,9 @@ func (s *Suite) TestGetBlockBodies66(t *utesting.T) { } } -// TestBroadcast tests whether a block announcement is correctly +// TestBroadcast65 tests whether a block announcement is correctly // propagated to the given node's peer(s). -func (s *Suite) TestBroadcast(t *utesting.T) { +func (s *Suite) TestBroadcast65(t *utesting.T) { if err := s.sendNextBlock(eth65); err != nil { t.Fatalf("block broadcast failed: %v", err) } @@ -476,8 +476,8 @@ func (s *Suite) TestBroadcast66(t *utesting.T) { } } -// TestLargeAnnounce tests the announcement mechanism with a large block. -func (s *Suite) TestLargeAnnounce(t *utesting.T) { +// TestLargeAnnounce65 tests the announcement mechanism with a large block. +func (s *Suite) TestLargeAnnounce65(t *utesting.T) { nextBlock := len(s.chain.blocks) blocks := []*NewBlock{ { @@ -569,8 +569,8 @@ func (s *Suite) TestLargeAnnounce66(t *utesting.T) { } } -// TestOldAnnounce tests the announcement mechanism with an old block. -func (s *Suite) TestOldAnnounce(t *utesting.T) { +// TestOldAnnounce65 tests the announcement mechanism with an old block. +func (s *Suite) TestOldAnnounce65(t *utesting.T) { if err := s.oldAnnounce(eth65); err != nil { t.Fatal(err) } @@ -584,9 +584,9 @@ func (s *Suite) TestOldAnnounce66(t *utesting.T) { } } -// TestBlockHashAnnounce sends a new block hash announcement and expects +// TestBlockHashAnnounce65 sends a new block hash announcement and expects // the node to perform a `GetBlockHeaders` request. -func (s *Suite) TestBlockHashAnnounce(t *utesting.T) { +func (s *Suite) TestBlockHashAnnounce65(t *utesting.T) { if err := s.hashAnnounce(eth65); err != nil { t.Fatalf("block hash announcement failed: %v", err) } @@ -600,8 +600,8 @@ func (s *Suite) TestBlockHashAnnounce66(t *utesting.T) { } } -// TestMaliciousHandshake tries to send malicious data during the handshake. -func (s *Suite) TestMaliciousHandshake(t *utesting.T) { +// TestMaliciousHandshake65 tries to send malicious data during the handshake. +func (s *Suite) TestMaliciousHandshake65(t *utesting.T) { if err := s.maliciousHandshakes(t, eth65); err != nil { t.Fatal(err) } @@ -614,8 +614,8 @@ func (s *Suite) TestMaliciousHandshake66(t *utesting.T) { } } -// TestMaliciousStatus sends a status package with a large total difficulty. -func (s *Suite) TestMaliciousStatus(t *utesting.T) { +// TestMaliciousStatus65 sends a status package with a large total difficulty. +func (s *Suite) TestMaliciousStatus65(t *utesting.T) { conn, err := s.dial() if err != nil { t.Fatalf("dial failed: %v", err) @@ -641,9 +641,9 @@ func (s *Suite) TestMaliciousStatus66(t *utesting.T) { } } -// TestTransaction sends a valid transaction to the node and +// TestTransaction65 sends a valid transaction to the node and // checks if the transaction gets propagated. -func (s *Suite) TestTransaction(t *utesting.T) { +func (s *Suite) TestTransaction65(t *utesting.T) { if err := s.sendSuccessfulTxs(t, eth65); err != nil { t.Fatal(err) } @@ -657,9 +657,9 @@ func (s *Suite) TestTransaction66(t *utesting.T) { } } -// TestMaliciousTx sends several invalid transactions and tests whether +// TestMaliciousTx65 sends several invalid transactions and tests whether // the node will propagate them. -func (s *Suite) TestMaliciousTx(t *utesting.T) { +func (s *Suite) TestMaliciousTx65(t *utesting.T) { if err := s.sendMaliciousTxs(t, eth65); err != nil { t.Fatal(err) } From fb7da82ddece5a810793ee00a3263b18c989a2b1 Mon Sep 17 00:00:00 2001 From: Ikko Ashimine Date: Wed, 10 Nov 2021 22:18:12 +0900 Subject: [PATCH 05/30] p2p: fix typo in v4wire.go Neigbors -> Neighbors --- p2p/discover/v4wire/v4wire.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/p2p/discover/v4wire/v4wire.go b/p2p/discover/v4wire/v4wire.go index 23e713441..bc537a4cf 100644 --- a/p2p/discover/v4wire/v4wire.go +++ b/p2p/discover/v4wire/v4wire.go @@ -102,7 +102,7 @@ type ( } ) -// This number is the maximum number of neighbor nodes in a Neigbors packet. +// This number is the maximum number of neighbor nodes in a Neighbors packet. const MaxNeighbors = 12 // This code computes the MaxNeighbors constant value. From f32feeb260054d7c90c1efd57dfec6b717717e18 Mon Sep 17 00:00:00 2001 From: Andrei Maiboroda Date: Thu, 11 Nov 2021 15:00:58 +0100 Subject: [PATCH 06/30] core/vm: implement EIP-2681: Limit account nonce to 2^64-1 (#23853) This retroactively implements requirements or EIP-2681 for the account nonce upper limit. --- cmd/evm/internal/t8ntool/transaction.go | 2 ++ core/error.go | 4 +++ core/state_processor_test.go | 39 ++++++++++++++++--------- core/state_transition.go | 3 ++ core/vm/errors.go | 1 + core/vm/evm.go | 3 ++ 6 files changed, 39 insertions(+), 13 deletions(-) diff --git a/cmd/evm/internal/t8ntool/transaction.go b/cmd/evm/internal/t8ntool/transaction.go index bc9bc42ed..576d1db84 100644 --- a/cmd/evm/internal/t8ntool/transaction.go +++ b/cmd/evm/internal/t8ntool/transaction.go @@ -154,6 +154,8 @@ func Transaction(ctx *cli.Context) error { } // Validate <256bit fields switch { + case tx.Nonce()+1 < tx.Nonce(): + r.Error = errors.New("nonce exceeds 2^64-1") case tx.Value().BitLen() > 256: r.Error = errors.New("value exceeds 256 bits") case tx.GasPrice().BitLen() > 256: diff --git a/core/error.go b/core/error.go index 594f3a26e..51ebefc13 100644 --- a/core/error.go +++ b/core/error.go @@ -51,6 +51,10 @@ var ( // next one expected based on the local chain. ErrNonceTooHigh = errors.New("nonce too high") + // ErrNonceMax is returned if the nonce of a transaction sender account has + // maximum allowed value and would become invalid if incremented. + ErrNonceMax = errors.New("nonce has max value") + // ErrGasLimitReached is returned by the gas pool if the amount of gas required // by a transaction is higher than what's left in the block. ErrGasLimitReached = errors.New("gas limit reached") diff --git a/core/state_processor_test.go b/core/state_processor_test.go index 13a9eb810..aa8e4bebf 100644 --- a/core/state_processor_test.go +++ b/core/state_processor_test.go @@ -17,10 +17,12 @@ package core import ( + "crypto/ecdsa" "math/big" "testing" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/consensus/misc" @@ -54,11 +56,12 @@ func TestStateProcessorErrors(t *testing.T) { LondonBlock: big.NewInt(0), Ethash: new(params.EthashConfig), } - signer = types.LatestSigner(config) - testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + signer = types.LatestSigner(config) + key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + key2, _ = crypto.HexToECDSA("0202020202020202020202020202020202020202020202020202002020202020") ) - var makeTx = func(nonce uint64, to common.Address, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *types.Transaction { - tx, _ := types.SignTx(types.NewTransaction(nonce, to, amount, gasLimit, gasPrice, data), signer, testKey) + var makeTx = func(key *ecdsa.PrivateKey, nonce uint64, to common.Address, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *types.Transaction { + tx, _ := types.SignTx(types.NewTransaction(nonce, to, amount, gasLimit, gasPrice, data), signer, key) return tx } var mkDynamicTx = func(nonce uint64, to common.Address, gasLimit uint64, gasTipCap, gasFeeCap *big.Int) *types.Transaction { @@ -69,7 +72,7 @@ func TestStateProcessorErrors(t *testing.T) { Gas: gasLimit, To: &to, Value: big.NewInt(0), - }), signer, testKey) + }), signer, key1) return tx } { // Tests against a 'recent' chain definition @@ -82,6 +85,10 @@ func TestStateProcessorErrors(t *testing.T) { Balance: big.NewInt(1000000000000000000), // 1 ether Nonce: 0, }, + common.HexToAddress("0xfd0810DD14796680f72adf1a371963d0745BCc64"): GenesisAccount{ + Balance: big.NewInt(1000000000000000000), // 1 ether + Nonce: math.MaxUint64, + }, }, } genesis = gspec.MustCommit(db) @@ -97,32 +104,38 @@ func TestStateProcessorErrors(t *testing.T) { }{ { // ErrNonceTooLow txs: []*types.Transaction{ - makeTx(0, common.Address{}, big.NewInt(0), params.TxGas, big.NewInt(875000000), nil), - makeTx(0, common.Address{}, big.NewInt(0), params.TxGas, big.NewInt(875000000), nil), + makeTx(key1, 0, common.Address{}, big.NewInt(0), params.TxGas, big.NewInt(875000000), nil), + makeTx(key1, 0, common.Address{}, big.NewInt(0), params.TxGas, big.NewInt(875000000), nil), }, want: "could not apply tx 1 [0x0026256b3939ed97e2c4a6f3fce8ecf83bdcfa6d507c47838c308a1fb0436f62]: nonce too low: address 0x71562b71999873DB5b286dF957af199Ec94617F7, tx: 0 state: 1", }, { // ErrNonceTooHigh txs: []*types.Transaction{ - makeTx(100, common.Address{}, big.NewInt(0), params.TxGas, big.NewInt(875000000), nil), + makeTx(key1, 100, common.Address{}, big.NewInt(0), params.TxGas, big.NewInt(875000000), nil), }, want: "could not apply tx 0 [0xdebad714ca7f363bd0d8121c4518ad48fa469ca81b0a081be3d10c17460f751b]: nonce too high: address 0x71562b71999873DB5b286dF957af199Ec94617F7, tx: 100 state: 0", }, + { // ErrNonceMax + txs: []*types.Transaction{ + makeTx(key2, math.MaxUint64, common.Address{}, big.NewInt(0), params.TxGas, big.NewInt(875000000), nil), + }, + want: "could not apply tx 0 [0x84ea18d60eb2bb3b040e3add0eb72f757727122cc257dd858c67cb6591a85986]: nonce has max value: address 0xfd0810DD14796680f72adf1a371963d0745BCc64, nonce: 18446744073709551615", + }, { // ErrGasLimitReached txs: []*types.Transaction{ - makeTx(0, common.Address{}, big.NewInt(0), 21000000, big.NewInt(875000000), nil), + makeTx(key1, 0, common.Address{}, big.NewInt(0), 21000000, big.NewInt(875000000), nil), }, want: "could not apply tx 0 [0xbd49d8dadfd47fb846986695f7d4da3f7b2c48c8da82dbc211a26eb124883de9]: gas limit reached", }, { // ErrInsufficientFundsForTransfer txs: []*types.Transaction{ - makeTx(0, common.Address{}, big.NewInt(1000000000000000000), params.TxGas, big.NewInt(875000000), nil), + makeTx(key1, 0, common.Address{}, big.NewInt(1000000000000000000), params.TxGas, big.NewInt(875000000), nil), }, want: "could not apply tx 0 [0x98c796b470f7fcab40aaef5c965a602b0238e1034cce6fb73823042dd0638d74]: insufficient funds for gas * price + value: address 0x71562b71999873DB5b286dF957af199Ec94617F7 have 1000000000000000000 want 1000018375000000000", }, { // ErrInsufficientFunds txs: []*types.Transaction{ - makeTx(0, common.Address{}, big.NewInt(0), params.TxGas, big.NewInt(900000000000000000), nil), + makeTx(key1, 0, common.Address{}, big.NewInt(0), params.TxGas, big.NewInt(900000000000000000), nil), }, want: "could not apply tx 0 [0x4a69690c4b0cd85e64d0d9ea06302455b01e10a83db964d60281739752003440]: insufficient funds for gas * price + value: address 0x71562b71999873DB5b286dF957af199Ec94617F7 have 1000000000000000000 want 18900000000000000000000", }, @@ -132,13 +145,13 @@ func TestStateProcessorErrors(t *testing.T) { // multiplication len(data) +gas_per_byte overflows uint64. Not testable at the moment { // ErrIntrinsicGas txs: []*types.Transaction{ - makeTx(0, common.Address{}, big.NewInt(0), params.TxGas-1000, big.NewInt(875000000), nil), + makeTx(key1, 0, common.Address{}, big.NewInt(0), params.TxGas-1000, big.NewInt(875000000), nil), }, want: "could not apply tx 0 [0xcf3b049a0b516cb4f9274b3e2a264359e2ba53b2fb64b7bda2c634d5c9d01fca]: intrinsic gas too low: have 20000, want 21000", }, { // ErrGasLimitReached txs: []*types.Transaction{ - makeTx(0, common.Address{}, big.NewInt(0), params.TxGas*1000, big.NewInt(875000000), nil), + makeTx(key1, 0, common.Address{}, big.NewInt(0), params.TxGas*1000, big.NewInt(875000000), nil), }, want: "could not apply tx 0 [0xbd49d8dadfd47fb846986695f7d4da3f7b2c48c8da82dbc211a26eb124883de9]: gas limit reached", }, diff --git a/core/state_transition.go b/core/state_transition.go index 6a09f6adc..135a9c6db 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -222,6 +222,9 @@ func (st *StateTransition) preCheck() error { } else if stNonce > msgNonce { return fmt.Errorf("%w: address %v, tx: %d state: %d", ErrNonceTooLow, st.msg.From().Hex(), msgNonce, stNonce) + } else if stNonce+1 < stNonce { + return fmt.Errorf("%w: address %v, nonce: %d", ErrNonceMax, + st.msg.From().Hex(), stNonce) } // Make sure the sender is an EOA if codeHash := st.state.GetCodeHash(st.msg.From()); codeHash != emptyCodeHash && codeHash != (common.Hash{}) { diff --git a/core/vm/errors.go b/core/vm/errors.go index c7cfeae53..565eecdd7 100644 --- a/core/vm/errors.go +++ b/core/vm/errors.go @@ -35,6 +35,7 @@ var ( ErrReturnDataOutOfBounds = errors.New("return data out of bounds") ErrGasUintOverflow = errors.New("gas uint64 overflow") ErrInvalidCode = errors.New("invalid code: must not begin with 0xef") + ErrNonceUintOverflow = errors.New("nonce uint64 overflow") ) // ErrStackUnderflow wraps an evm error when the items on the stack less diff --git a/core/vm/evm.go b/core/vm/evm.go index 07e961915..618bbcf17 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -424,6 +424,9 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, return nil, common.Address{}, gas, ErrInsufficientBalance } nonce := evm.StateDB.GetNonce(caller.Address()) + if nonce+1 < nonce { + return nil, common.Address{}, gas, ErrNonceUintOverflow + } evm.StateDB.SetNonce(caller.Address(), nonce+1) // We add this to the access list _before_ taking a snapshot. Even if the creation fails, // the access-list change should not be rolled back From c57df9ca286aff90672044dc199014fba59bed11 Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi <1591639+s1na@users.noreply.github.com> Date: Thu, 11 Nov 2021 15:04:06 +0100 Subject: [PATCH 07/30] core/rawdb: add slow path for getting legacy logs (#23879) * eth/tracers: add slow path for getting legacy logs * core/rawdb: fix test --- core/rawdb/accessors_chain.go | 24 ++++++++++++++++++++++-- core/rawdb/accessors_chain_test.go | 2 +- eth/api_backend.go | 2 +- 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/core/rawdb/accessors_chain.go b/core/rawdb/accessors_chain.go index 7f26c3a42..4028191b7 100644 --- a/core/rawdb/accessors_chain.go +++ b/core/rawdb/accessors_chain.go @@ -669,7 +669,7 @@ func deriveLogFields(receipts []*receiptLogs, hash common.Hash, number uint64, t // ReadLogs retrieves the logs for all transactions in a block. The log fields // are populated with metadata. In case the receipts or the block body // are not found, a nil is returned. -func ReadLogs(db ethdb.Reader, hash common.Hash, number uint64) [][]*types.Log { +func ReadLogs(db ethdb.Reader, hash common.Hash, number uint64, config *params.ChainConfig) [][]*types.Log { // Retrieve the flattened receipt slice data := ReadReceiptsRLP(db, hash, number) if len(data) == 0 { @@ -677,7 +677,12 @@ func ReadLogs(db ethdb.Reader, hash common.Hash, number uint64) [][]*types.Log { } receipts := []*receiptLogs{} if err := rlp.DecodeBytes(data, &receipts); err != nil { - log.Error("Invalid receipt array RLP", "hash", hash, "err", err) + // Receipts might be in the legacy format, try decoding that. + // TODO: to be removed after users migrated + if logs := readLegacyLogs(db, hash, number, config); logs != nil { + return logs + } + log.Error("Invalid receipt array RLP", "hash", "err", err) return nil } @@ -697,6 +702,21 @@ func ReadLogs(db ethdb.Reader, hash common.Hash, number uint64) [][]*types.Log { return logs } +// readLegacyLogs is a temporary workaround for when trying to read logs +// from a block which has its receipt stored in the legacy format. It'll +// be removed after users have migrated their freezer databases. +func readLegacyLogs(db ethdb.Reader, hash common.Hash, number uint64, config *params.ChainConfig) [][]*types.Log { + receipts := ReadReceipts(db, hash, number, config) + if receipts == nil { + return nil + } + logs := make([][]*types.Log, len(receipts)) + for i, receipt := range receipts { + logs[i] = receipt.Logs + } + return logs +} + // ReadBlock retrieves an entire block corresponding to the hash, assembling it // back from the stored header and body. If either the header or body could not // be retrieved nil is returned. diff --git a/core/rawdb/accessors_chain_test.go b/core/rawdb/accessors_chain_test.go index 4b173c55e..50b0d5390 100644 --- a/core/rawdb/accessors_chain_test.go +++ b/core/rawdb/accessors_chain_test.go @@ -744,7 +744,7 @@ func TestReadLogs(t *testing.T) { // Insert the receipt slice into the database and check presence WriteReceipts(db, hash, 0, receipts) - logs := ReadLogs(db, hash, 0) + logs := ReadLogs(db, hash, 0, params.TestChainConfig) if len(logs) == 0 { t.Fatalf("no logs returned") } diff --git a/eth/api_backend.go b/eth/api_backend.go index a0704876a..6a19fb36a 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -187,7 +187,7 @@ func (b *EthAPIBackend) GetLogs(ctx context.Context, hash common.Hash) ([][]*typ if number == nil { return nil, errors.New("failed to get block number from hash") } - logs := rawdb.ReadLogs(db, hash, *number) + logs := rawdb.ReadLogs(db, hash, *number, b.eth.blockchain.Config()) if logs == nil { return nil, errors.New("failed to get logs for block") } From 5358e491f3583a79755b55e6aac9aeae1aa5cee7 Mon Sep 17 00:00:00 2001 From: meowsbits Date: Thu, 11 Nov 2021 08:07:11 -0800 Subject: [PATCH 08/30] cmd/devp2p: update TTL max for Cloudflare (#23885) This was apparently recently changed by Cloudflare, and began returning an error: 'TTL must be between 60 and 86400 seconds, or 1 for Automatic' Date: 2021-11-10 15:25:20-08:00 Signed-off-by: meows --- cmd/devp2p/dns_cloudflare.go | 3 ++- cmd/devp2p/dnscmd.go | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/cmd/devp2p/dns_cloudflare.go b/cmd/devp2p/dns_cloudflare.go index 596254df9..d67aaea1a 100644 --- a/cmd/devp2p/dns_cloudflare.go +++ b/cmd/devp2p/dns_cloudflare.go @@ -133,7 +133,8 @@ func (c *cloudflareClient) uploadRecords(name string, records map[string]string) log.Info(fmt.Sprintf("Creating %s = %q", path, val)) ttl := rootTTL if path != name { - ttl = treeNodeTTL // Max TTL permitted by Cloudflare + ttl = treeNodeTTLCloudflare // Max TTL permitted by Cloudflare + } record := cloudflare.DNSRecord{Type: "TXT", Name: path, Content: val, TTL: ttl} _, err = c.CreateDNSRecord(context.Background(), c.zoneID, record) diff --git a/cmd/devp2p/dnscmd.go b/cmd/devp2p/dnscmd.go index 66deef56e..85f28b8cb 100644 --- a/cmd/devp2p/dnscmd.go +++ b/cmd/devp2p/dnscmd.go @@ -115,8 +115,9 @@ var ( ) const ( - rootTTL = 30 * 60 // 30 min - treeNodeTTL = 4 * 7 * 24 * 60 * 60 // 4 weeks + rootTTL = 30 * 60 // 30 min + treeNodeTTL = 4 * 7 * 24 * 60 * 60 // 4 weeks + treeNodeTTLCloudflare = 24 * 60 * 60 // 1 day ) // dnsSync performs dnsSyncCommand. From e9294a7fe9ba69837d605b589b2825d920d8b1bb Mon Sep 17 00:00:00 2001 From: Ward Bradt Date: Thu, 11 Nov 2021 14:20:46 -0500 Subject: [PATCH 09/30] eth/tracers: add golang 4byte tracer (#23882) * native 4byte tracer * Update eth/tracers/native/4byte.go Co-authored-by: Martin Holst Swende * Update eth/tracers/native/4byte.go Co-authored-by: Martin Holst Swende * goimports * eth/tracers: make 4byte tracer not care about create Co-authored-by: Martin Holst Swende --- .../js/internal/tracers/4byte_tracer.js | 7 +- eth/tracers/js/internal/tracers/assets.go | 6 +- eth/tracers/native/4byte.go | 148 ++++++++++++++++++ 3 files changed, 157 insertions(+), 4 deletions(-) create mode 100644 eth/tracers/native/4byte.go diff --git a/eth/tracers/js/internal/tracers/4byte_tracer.js b/eth/tracers/js/internal/tracers/4byte_tracer.js index 9ec3209f8..07dde6c32 100644 --- a/eth/tracers/js/internal/tracers/4byte_tracer.js +++ b/eth/tracers/js/internal/tracers/4byte_tracer.js @@ -37,11 +37,16 @@ this.ids[key] = this.ids[key] + 1 || 1; }, - enter: function(frame) { + enter: function(frame, log) { // Skip any pre-compile invocations, those are just fancy opcodes if (isPrecompiled(frame.getTo())) { return; } + var type = frame.getType() + if (type !== "CALL" && type !== "STATICCALL" && + type !== "DELEGATECALL" && type !== "CALLCODE"){ + return; + } var input = frame.getInput() if (input.length >= 4) { this.store(slice(input, 0, 4), input.length - 4); diff --git a/eth/tracers/js/internal/tracers/assets.go b/eth/tracers/js/internal/tracers/assets.go index 37e370240..9e9cf55b5 100644 --- a/eth/tracers/js/internal/tracers/assets.go +++ b/eth/tracers/js/internal/tracers/assets.go @@ -1,6 +1,6 @@ // Code generated by go-bindata. DO NOT EDIT. // sources: -// 4byte_tracer.js (2.224kB) +// 4byte_tracer.js (2.375kB) // 4byte_tracer_legacy.js (2.933kB) // bigram_tracer.js (1.712kB) // call_tracer_js.js (3.497kB) @@ -79,7 +79,7 @@ func (fi bindataFileInfo) Sys() interface{} { return nil } -var __4byte_tracerJs = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x8c\x55\x5b\x6f\x22\x39\x13\x7d\x86\x5f\x71\xc4\x13\x68\x9a\x4b\x73\x09\x97\xf9\x32\x12\xdf\x28\x99\x41\xca\x66\x22\x42\x34\x8a\x56\xfb\x60\xda\xd5\xdd\xde\x18\xbb\x65\xbb\xb9\x6c\x26\xff\x7d\x65\x37\xe4\x36\xbb\xda\x79\x02\xec\xaa\x73\xaa\x4e\x1d\x17\xdd\x2e\x3e\xeb\xe2\x60\x44\x96\x3b\xf4\x7b\xf1\x18\xab\x9c\x90\xe9\x36\xb9\x9c\x0c\x95\x1b\xcc\x4b\x97\x6b\x63\xeb\xdd\x2e\x56\xb9\xb0\x48\x85\x24\x08\x8b\x82\x19\x07\x9d\xc2\xbd\x8b\x97\x62\x6d\x98\x39\x74\xea\xdd\x6e\x95\xf3\x8f\xd7\x1e\x21\x35\x44\xb0\x3a\x75\x3b\x66\x68\x86\x83\x2e\x91\x30\x05\x43\x5c\x58\x67\xc4\xba\x74\x04\xe1\xc0\x14\xef\x6a\x83\x8d\xe6\x22\x3d\x78\x48\xe1\x50\x2a\x4e\x26\x50\x3b\x32\x1b\x7b\xaa\xe3\xcb\xf5\x1d\xae\xc8\x5a\x32\xf8\x42\x8a\x0c\x93\xb8\x29\xd7\x52\x24\xb8\x12\x09\x29\x4b\x60\x16\x85\x3f\xb1\x39\x71\xac\x03\x9c\x4f\xbc\xf4\xa5\xdc\x1e\x4b\xc1\xa5\x2e\x15\x67\x4e\x68\x15\x81\x84\xaf\x1c\x5b\x32\x56\x68\x85\xc1\x89\xea\x08\x18\x41\x1b\x0f\xd2\x64\xce\x37\x60\xa0\x0b\x9f\xd7\x02\x53\x07\x48\xe6\x5e\x52\x7f\x41\x90\x97\xbe\x39\x84\x0a\x34\xb9\x2e\x08\x2e\x67\xce\x77\xbd\x13\x52\x62\x4d\x28\x2d\xa5\xa5\x8c\x3c\xda\xba\x74\xf8\xbe\x58\x7d\xfd\x76\xb7\xc2\xfc\xfa\x1e\xdf\xe7\xcb\xe5\xfc\x7a\x75\xff\x11\x3b\xe1\x72\x5d\x3a\xd0\x96\x2a\x28\xb1\x29\xa4\x20\x8e\x1d\x33\x86\x29\x77\x80\x4e\x3d\xc2\x6f\x17\xcb\xcf\x5f\xe7\xd7\xab\xf9\xff\x17\x57\x8b\xd5\x3d\xb4\xc1\xe5\x62\x75\x7d\x71\x7b\x8b\xcb\x6f\x4b\xcc\x71\x33\x5f\xae\x16\x9f\xef\xae\xe6\x4b\xdc\xdc\x2d\x6f\xbe\xdd\x5e\x74\x70\x4b\xbe\x2a\xf2\xf9\xff\xad\x79\x1a\xa6\x67\x08\x9c\x1c\x13\xd2\x9e\x94\xb8\xd7\x25\x6c\xae\x4b\xc9\x91\xb3\x2d\xc1\x50\x42\x62\x4b\x1c\x0c\x89\x2e\x0e\xbf\x3c\x54\x8f\xc5\xa4\x56\x59\xe8\xf9\x5f\x0d\x89\x45\x0a\xa5\x5d\x04\x4b\x84\xff\xe5\xce\x15\xb3\x6e\x77\xb7\xdb\x75\x32\x55\x76\xb4\xc9\xba\xb2\x82\xb3\xdd\x4f\x9d\xba\xc7\x1c\xae\x0f\x8e\x56\x86\x25\x64\x60\x89\x99\x24\x27\x1b\x9a\x09\x17\x6d\xc1\x49\x39\x91\x0a\x32\x36\xf2\x26\x45\xa2\xa5\xa4\xc4\x59\x5f\xc1\x26\x04\x16\xda\xba\x76\x61\x74\x42\xd6\x0a\x95\xf9\xc6\xb1\x70\x6f\x02\xb1\x21\x97\x6b\x6e\xf1\x0a\xee\x7d\x37\x56\xfc\x45\x27\x35\x6c\x59\x54\x63\xe4\xcc\xb1\x08\x56\x87\xee\x61\xc8\xdb\x8c\x38\xac\xc8\x14\x73\xa5\xa1\xf0\x96\xd6\x84\x0d\x73\x89\x37\x3b\xcb\x98\x50\xd6\xfd\x04\xe8\x71\x4e\x13\xb9\xd8\xb3\x4d\x21\x69\xe6\xbf\x03\x9f\xc0\x69\x5d\x66\x1d\xe7\x25\x58\x19\xa6\x2c\x4b\xbc\xb9\x9b\x68\xf4\xf6\xfd\x78\x48\xa3\xe9\x98\x06\x23\xce\x7a\x93\xc1\xd9\xb4\x9f\x8e\x06\x93\xb3\x78\x18\xd3\xd9\x34\x1d\x8e\x69\x3a\x1e\xac\xfb\xc9\xe8\x8c\xc6\x6c\xd2\x1b\x0f\xd6\x31\xb1\xde\x24\xe5\xe3\xd1\x38\xa6\x29\xa7\x46\x84\xc7\x00\x6c\x66\x68\xbc\x52\xba\xf1\xd4\xaa\xd8\x1f\xab\x0f\xa0\xb7\xef\x8f\x79\xd2\x9f\x8e\xa9\x1d\xf7\x27\x33\xc4\xd1\xcb\xcd\x60\x92\x24\xc3\xc9\x20\x6e\xf7\x66\xe8\xbf\x3a\x1f\xf5\x87\xe9\x60\x32\x99\xb6\xa7\x67\x6f\x13\x18\x4f\x47\xd3\x74\x3a\x6d\xf7\x27\xef\xa0\x92\xfe\x24\xe6\xf1\x94\x3c\x54\x5c\x1d\x3f\xd5\x1f\xeb\x35\xbf\x70\xb8\x05\xcb\x32\x43\x19\x73\x54\x4d\x2d\x54\x1c\x2e\x52\xbf\x2c\x3a\xf5\x9a\xff\x3e\xc3\xe3\x53\x54\x0f\x39\xd6\x79\xc7\x5b\xef\xeb\x60\x48\xe1\x9f\xa1\x50\xcf\x43\x0e\x8e\xf1\xda\xfb\x59\x74\xea\xb5\x10\x3f\x43\x5a\xaa\x4a\x63\xc1\xa3\x30\xa6\xd6\x63\xbd\x56\xdb\x32\x83\x07\x3a\xe0\x1c\x8d\x06\x3e\xc0\xe9\xaf\xb4\x6f\x0a\xde\xc2\x07\x34\xda\xfe\xc4\x47\x7e\xac\xd7\x6a\x2e\x17\xb6\x23\xb8\xfd\xfd\x81\x0e\x7f\xe0\x1c\x6f\x7f\x7f\x40\x8c\x1f\x3f\x10\x7f\xac\xd7\x42\x99\xa4\x9c\x97\xff\x99\x33\x35\x6c\x43\x2d\x78\xc6\x6e\x17\xb7\x0f\xa2\x08\x6b\xac\x30\xd4\x4e\xf4\xa6\x08\x8b\x5f\x6d\x75\x12\x56\xa3\x8d\xe0\x72\xed\x57\xaa\x21\xfc\x59\x5a\x87\x94\xa9\xe4\x00\x5d\x24\x9a\x93\xad\xd7\x6a\x22\x45\x53\xd8\x1b\x43\xc7\x64\x5e\x11\x74\x32\x72\x2b\xdd\x6c\xb5\x2a\xa6\x9a\x21\x57\x1a\xe5\xab\x7f\x3a\xb6\x2a\x54\x51\x3a\x9c\xe3\x39\x7c\xe1\x0f\x9a\xad\x13\xa6\xff\xd5\x91\xa4\x32\x97\xe3\xd3\x39\x86\x47\xa0\xd0\x6c\xd0\xb1\x69\xfd\x5b\xae\x02\x23\xf4\x22\x0c\x5b\x11\xde\xa4\xb5\x31\x6c\x1d\x29\x2b\x29\xf6\xc2\xbd\x57\x62\x49\xb6\x94\xae\xf5\x32\xd3\x94\x95\xd2\xf9\x45\xed\x55\x78\xf0\xab\x34\x3f\xee\x56\x96\xb8\x92\x49\xd0\x9e\x92\xd2\x03\xf8\xc7\xc5\xd4\x51\x0b\xa4\xd5\xd6\xab\x85\xfc\x57\x2c\x52\x67\x11\xf8\xfa\x15\x83\x09\x94\x3f\x51\x30\x29\x03\xcd\x51\xdb\x6a\x5d\xae\xc9\x3b\xca\x91\x61\xfe\xff\x42\x6f\x8f\x9e\xaa\xe4\xb4\x01\xce\xe7\xa4\x42\x31\x79\x02\x3e\xbe\x79\xff\xf0\xc2\x3e\xaa\x55\xe7\xaf\x6a\x4a\xdc\xfe\xc5\x01\x27\xf7\xea\xd2\xff\x91\x25\x4c\x4a\xef\x58\x30\x69\xf5\x71\x16\x89\xdb\x77\x7e\x79\x1e\xcf\xc1\xcf\x33\x79\x9f\xde\x1e\xb6\x8e\x3e\xa8\xda\x78\x36\x70\x65\xd9\xa7\xfa\xdf\x01\x00\x00\xff\xff\xf6\xa8\xa1\xb9\xb0\x08\x00\x00") +var __4byte_tracerJs = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x8c\x55\x5b\x4f\xdb\x48\x14\x7e\x4e\x7e\xc5\xb7\x79\xa8\x88\xea\xdc\x03\xb9\x74\xa9\x94\xa5\xa1\x8d\xc4\x02\x0a\x46\x15\x5a\xed\xc3\xc4\x73\x6c\xcf\xe2\xcc\x58\x33\x63\x48\x96\xf2\xdf\x57\x33\x76\x48\xa0\xad\xb6\x4f\x84\x73\xf9\xce\xe5\x3b\xf3\xb9\xd3\xc1\x99\xca\xb7\x5a\x24\xa9\x45\xbf\xdb\x1b\x21\x4c\x09\x89\x6a\x91\x4d\x49\x53\xb1\xc6\xac\xb0\xa9\xd2\xa6\xde\xe9\x20\x4c\x85\x41\x2c\x32\x82\x30\xc8\x99\xb6\x50\x31\xec\x9b\xf8\x4c\xac\x34\xd3\xdb\x76\xbd\xd3\x29\x73\x7e\xe8\x76\x08\xb1\x26\x82\x51\xb1\x7d\x64\x9a\xa6\xd8\xaa\x02\x11\x93\xd0\xc4\x85\xb1\x5a\xac\x0a\x4b\x10\x16\x4c\xf2\x8e\xd2\x58\x2b\x2e\xe2\xad\x83\x14\x16\x85\xe4\xa4\x7d\x69\x4b\x7a\x6d\x76\x7d\x7c\xbe\xbc\xc5\x05\x19\x43\x1a\x9f\x49\x92\x66\x19\xae\x8b\x55\x26\x22\x5c\x88\x88\xa4\x21\x30\x83\xdc\x59\x4c\x4a\x1c\x2b\x0f\xe7\x12\xcf\x5d\x2b\x37\x55\x2b\x38\x57\x85\xe4\xcc\x0a\x25\x03\x90\x70\x9d\xe3\x81\xb4\x11\x4a\x62\xb0\x2b\x55\x01\x06\x50\xda\x81\x1c\x31\xeb\x06\xd0\x50\xb9\xcb\x6b\x82\xc9\x2d\x32\x66\xf7\xa9\xbf\xb0\x90\xfd\xdc\x1c\x42\xfa\x32\xa9\xca\x09\x36\x65\xd6\x4d\xfd\x28\xb2\x0c\x2b\x42\x61\x28\x2e\xb2\xc0\xa1\xad\x0a\x8b\xaf\x8b\xf0\xcb\xd5\x6d\x88\xd9\xe5\x1d\xbe\xce\x96\xcb\xd9\x65\x78\xf7\x01\x8f\xc2\xa6\xaa\xb0\xa0\x07\x2a\xa1\xc4\x3a\xcf\x04\x71\x3c\x32\xad\x99\xb4\x5b\xa8\xd8\x21\xfc\x39\x5f\x9e\x7d\x99\x5d\x86\xb3\x3f\x16\x17\x8b\xf0\x0e\x4a\xe3\x7c\x11\x5e\xce\x6f\x6e\x70\x7e\xb5\xc4\x0c\xd7\xb3\x65\xb8\x38\xbb\xbd\x98\x2d\x71\x7d\xbb\xbc\xbe\xba\x99\xb7\x71\x43\xae\x2b\x72\xf9\xff\xbf\xf3\xd8\xb3\xa7\x09\x9c\x2c\x13\x99\xd9\x6d\xe2\x4e\x15\x30\xa9\x2a\x32\x8e\x94\x3d\x10\x34\x45\x24\x1e\x88\x83\x21\x52\xf9\xf6\x97\x49\x75\x58\x2c\x53\x32\xf1\x33\xff\xf4\x20\xb1\x88\x21\x95\x0d\x60\x88\xf0\x7b\x6a\x6d\x3e\xed\x74\x1e\x1f\x1f\xdb\x89\x2c\xda\x4a\x27\x9d\xac\x84\x33\x9d\x8f\xed\xba\xc3\x1c\xae\xb6\x96\x42\xcd\x22\xd2\x30\xc4\x74\x94\x92\xf1\xc3\x78\x47\x4b\x70\x92\x56\xc4\x82\xb4\x09\xdc\x91\x22\x52\x59\x46\x91\x35\xae\x83\xb5\x0f\xcc\x95\xb1\xad\x5c\xab\x88\x8c\x11\x32\x71\x83\x63\x61\x5f\x05\x62\x4d\x36\x55\xdc\xe0\x00\xee\xed\x34\x46\xfc\x4b\xbb\x6d\x98\x22\x2f\x69\xe4\xcc\xb2\x00\x46\xf9\xe9\xa1\xc9\x9d\x19\x71\x18\x91\x48\x66\x0b\x4d\xfe\x2d\xad\x08\x6b\x66\x23\x77\xec\x2c\x61\x42\x1a\xfb\x1d\xa0\xc3\xd9\x31\x32\xdf\xb0\x75\x9e\xd1\xd4\xfd\x06\x3e\x82\xd3\xaa\x48\xda\xd6\xad\x20\xd4\x4c\x1a\x16\xb9\xe3\x3e\x42\xa3\xbb\xe9\xf7\x86\x74\x3c\x19\xd1\xe0\x98\xb3\xee\x78\x70\x32\xe9\xc7\xc7\x83\xf1\x49\x6f\xd8\xa3\x93\x49\x3c\x1c\xd1\x64\x34\x58\xf5\xa3\xe3\x13\x1a\xb1\x71\x77\x34\x58\xf5\x88\x75\xc7\x31\x1f\x1d\x8f\x7a\x34\xe1\xd4\x08\xf0\xe4\x81\xf5\x14\x8d\x83\x4d\x37\x9e\x9b\x65\xf5\xa7\xf2\x0f\xd0\xdd\xf4\x47\x3c\xea\x4f\x46\xd4\xea\xf5\xc7\x53\xf4\x82\xbd\x67\x30\x8e\xa2\xe1\x78\xd0\x6b\x75\xa7\xe8\x1f\xd8\x8f\xfb\xc3\x78\x30\x1e\x4f\x5a\x93\x93\xd7\x09\x8c\xc7\xc7\x93\x78\x32\x69\xf5\xc7\x6f\xa0\xa2\xfe\xb8\xc7\x7b\x13\x72\x50\xbd\xd2\xfc\x5c\x7f\xaa\xd7\x9c\xe0\x70\x03\x96\x24\x9a\x12\x66\xa9\x64\xcd\x77\xec\x1d\xb1\x13\x8b\x76\xbd\xe6\x7e\x4f\xf1\xf4\x1c\xd4\x7d\x8e\xb1\xee\xe2\x8d\xbb\x6b\x7f\x90\xc2\x3d\x43\x21\x5f\x48\xf6\x17\xe3\x76\xef\xb8\x68\xd7\x6b\x3e\x7e\x8a\xb8\x90\xe5\x8e\x05\x0f\x3c\x4d\xcd\xa7\x7a\xad\xf6\xc0\x34\xee\x69\x8b\x53\x34\x1a\x78\x0f\xab\xbe\xd0\xe6\x48\xf0\x26\xde\xa3\xd1\x72\x16\x17\xf9\xa1\x5e\xab\xd9\x54\x98\xb6\xe0\xe6\xaf\x7b\xda\xfe\x8d\x53\xbc\xfe\xff\x3d\x7a\xf8\xf6\x0d\xbd\x0f\xf5\x9a\x6f\x93\xa4\x75\xeb\x7f\xa9\x19\x6b\xb6\xa6\x00\x99\x4a\x9a\x70\x65\x3b\x1d\xdc\xdc\x8b\xdc\x6b\x59\xae\xa9\x15\xa9\x75\xee\xd5\x5f\x3e\xa8\xc8\xeb\xa3\x09\x60\x53\xe5\x74\x55\x13\xfe\x29\x8c\x45\xcc\x64\xb4\x85\xca\x23\xc5\xc9\xd4\x6b\x35\x11\xe3\x48\x98\x6b\x4d\x55\x32\x2f\xab\xb4\x13\xb2\xa1\x3a\x6a\x36\xcb\x4a\x35\x4d\xb6\xd0\xd2\x8d\xf0\x5c\xcd\x6b\xb7\x39\xe1\x14\xfb\xe8\x6d\x4e\x47\xcd\x0a\xd1\x3b\x7f\x3b\x3d\x45\xe3\x6c\x76\x71\xd1\xc0\xbb\x77\xd8\x9b\x6e\xc2\x59\xb8\x38\xdb\x39\x1c\xfa\xde\xf7\x69\x7e\x31\xff\x3c\x0b\xe7\x3f\x48\x73\xa6\xb3\xab\x4f\xf3\x46\xf3\x27\x1d\x09\x99\x17\xf6\xb0\xa5\x85\x33\xbc\xf4\xe4\xdd\xed\x8c\x64\x62\x53\x7c\x3c\xc5\xb0\x1a\xcd\x73\xe0\xe9\x3d\x32\x4e\x62\xca\xc0\x00\xdd\x00\xc3\x66\x80\x57\x69\x2d\x0c\x9b\x55\xc9\x92\xa1\x8d\xb0\x6f\x09\x5a\x92\x29\x32\xdb\xdc\x9f\x5a\xcc\x8a\xcc\xba\xef\x87\xe3\xe5\xde\x29\x7c\x5a\x49\x3e\x8b\x6c\xc1\x32\xd0\x86\xa2\xc2\x01\xb8\x37\xcf\x64\xc5\x0e\xe2\x52\x8c\x6b\x3e\xff\xa0\x4a\xa6\x92\x00\x7c\x75\x50\x41\xfb\x92\xdf\x95\x60\x59\xe6\xcb\x54\x6c\x97\x2a\xbe\x22\x77\xe8\x96\x34\x73\x9f\x31\xf5\x50\x9d\x7a\xb9\x4e\xe3\xe1\x5c\x4e\x2c\x24\xcb\x76\xc0\x95\x14\x39\x3d\xf0\x32\x59\x2b\xed\x07\x3d\x45\x76\xb3\xbf\xc9\xdd\xa3\x52\x85\xfb\xbe\x46\x2c\xcb\xdc\x43\x02\xcb\x8c\xaa\xb8\x88\xec\xa6\xfd\xcb\x7c\xbc\x04\xbf\x70\xf2\x36\xbd\x35\x6c\x56\x77\x50\x8e\xf1\xf2\xae\xca\x97\xf4\x5c\xff\x2f\x00\x00\xff\xff\x6e\x82\xd6\x8b\x47\x09\x00\x00") func _4byte_tracerJsBytes() ([]byte, error) { return bindataRead( @@ -95,7 +95,7 @@ func _4byte_tracerJs() (*asset, error) { } info := bindataFileInfo{name: "4byte_tracer.js", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)} - a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x6b, 0xa8, 0x46, 0xa2, 0x3a, 0x2b, 0xaa, 0xb9, 0xb9, 0xba, 0xe2, 0x22, 0x10, 0xe, 0xe7, 0x4c, 0x24, 0xfc, 0x4c, 0x85, 0xeb, 0x96, 0x48, 0xe8, 0x7f, 0xc8, 0xe0, 0xd0, 0xd, 0x26, 0xa1, 0xb2}} + a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xdb, 0xff, 0x9f, 0xb0, 0x9e, 0x1a, 0xd7, 0xd3, 0xcb, 0xf6, 0x6e, 0xf5, 0xf7, 0xbc, 0xe7, 0xbc, 0xd6, 0x7c, 0x37, 0x16, 0xd7, 0x4f, 0x85, 0xb3, 0x1f, 0xfd, 0x66, 0x3e, 0x91, 0xed, 0x86, 0x27}} return a, nil } diff --git a/eth/tracers/native/4byte.go b/eth/tracers/native/4byte.go new file mode 100644 index 000000000..082a60355 --- /dev/null +++ b/eth/tracers/native/4byte.go @@ -0,0 +1,148 @@ +// Copyright 2021 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 . + +package native + +import ( + "encoding/json" + "math/big" + "strconv" + "sync/atomic" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/eth/tracers" +) + +func init() { + register("4byte", newFourByteTracer) +} + +// fourByteTracer searches for 4byte-identifiers, and collects them for post-processing. +// It collects the methods identifiers along with the size of the supplied data, so +// a reversed signature can be matched against the size of the data. +// +// Example: +// > debug.traceTransaction( "0x214e597e35da083692f5386141e69f47e973b2c56e7a8073b1ea08fd7571e9de", {tracer: "4byte"}) +// { +// 0x27dc297e-128: 1, +// 0x38cc4831-0: 2, +// 0x524f3889-96: 1, +// 0xadf59f99-288: 1, +// 0xc281d19e-0: 1 +// } +type fourByteTracer struct { + env *vm.EVM + ids map[string]int // ids aggregates the 4byte ids found + interrupt uint32 // Atomic flag to signal execution interruption + reason error // Textual reason for the interruption + activePrecompiles []common.Address // Updated on CaptureStart based on given rules +} + +// newFourByteTracer returns a native go tracer which collects +// 4 byte-identifiers of a tx, and implements vm.EVMLogger. +func newFourByteTracer() tracers.Tracer { + t := &fourByteTracer{ + ids: make(map[string]int), + } + return t +} + +// isPrecompiled returns whether the addr is a precompile. Logic borrowed from newJsTracer in eth/tracers/js/tracer.go +func (t *fourByteTracer) isPrecompiled(addr common.Address) bool { + for _, p := range t.activePrecompiles { + if p == addr { + return true + } + } + return false +} + +// store saves the given identifier and datasize. +func (t *fourByteTracer) store(id []byte, size int) { + key := bytesToHex(id) + "-" + strconv.Itoa(size) + t.ids[key] += 1 +} + +// CaptureStart implements the EVMLogger interface to initialize the tracing operation. +func (t *fourByteTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { + t.env = env + + // Update list of precompiles based on current block + rules := env.ChainConfig().Rules(env.Context.BlockNumber) + t.activePrecompiles = vm.ActivePrecompiles(rules) + + // Save the outer calldata also + if len(input) >= 4 { + t.store(input[0:4], len(input)-4) + } +} + +// CaptureState implements the EVMLogger interface to trace a single step of VM execution. +func (t *fourByteTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) { +} + +// CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct). +func (t *fourByteTracer) CaptureEnter(op vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { + // Skip if tracing was interrupted + if atomic.LoadUint32(&t.interrupt) > 0 { + t.env.Cancel() + return + } + if len(input) < 4 { + return + } + // primarily we want to avoid CREATE/CREATE2/SELFDESTRUCT + if op != vm.DELEGATECALL && op != vm.STATICCALL && + op != vm.CALL && op != vm.CALLCODE { + return + } + // Skip any pre-compile invocations, those are just fancy opcodes + if t.isPrecompiled(to) { + return + } + t.store(input[0:4], len(input)-4) +} + +// CaptureExit is called when EVM exits a scope, even if the scope didn't +// execute any code. +func (t *fourByteTracer) CaptureExit(output []byte, gasUsed uint64, err error) { +} + +// CaptureFault implements the EVMLogger interface to trace an execution fault. +func (t *fourByteTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, depth int, err error) { +} + +// CaptureEnd is called after the call finishes to finalize the tracing. +func (t *fourByteTracer) CaptureEnd(output []byte, gasUsed uint64, _ time.Duration, err error) { +} + +// GetResult returns the json-encoded nested list of call traces, and any +// error arising from the encoding or forceful termination (via `Stop`). +func (t *fourByteTracer) GetResult() (json.RawMessage, error) { + res, err := json.Marshal(t.ids) + if err != nil { + return nil, err + } + return res, t.reason +} + +// Stop terminates execution of the tracer at the first opportune moment. +func (t *fourByteTracer) Stop(err error) { + t.reason = err + atomic.StoreUint32(&t.interrupt, 1) +} From abc74a5ffeb7e211954178e5d7b8543d5bd3d3cc Mon Sep 17 00:00:00 2001 From: Joshua Colvin Date: Fri, 12 Nov 2021 07:50:08 -0700 Subject: [PATCH 10/30] accounts/abi/bind/backends: fix race condition in simulated backend (#23898) Now that `SimulatedBackend.SuggestGasPrice` inspects member values, a lock needs to be added to prevent a race condition. --- accounts/abi/bind/backends/simulated.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go index 6854c9624..27d40f1d6 100644 --- a/accounts/abi/bind/backends/simulated.go +++ b/accounts/abi/bind/backends/simulated.go @@ -462,6 +462,9 @@ func (b *SimulatedBackend) PendingNonceAt(ctx context.Context, account common.Ad // SuggestGasPrice implements ContractTransactor.SuggestGasPrice. Since the simulated // chain doesn't have miners, we just return a gas price of 1 for any call. func (b *SimulatedBackend) SuggestGasPrice(ctx context.Context) (*big.Int, error) { + b.mu.Lock() + defer b.mu.Unlock() + if b.pendingBlock.Header().BaseFee != nil { return b.pendingBlock.Header().BaseFee, nil } From b0b708bf23238a496bde3f563af1661857942fa2 Mon Sep 17 00:00:00 2001 From: lightclient <14004106+lightclient@users.noreply.github.com> Date: Tue, 16 Nov 2021 00:43:58 -0700 Subject: [PATCH 11/30] cmd/evm: add `gasUsed` to t8n `result` (#23919) * cmd/evm: add gas used accumulator to t8n result * cmd/evm: update t8n tests to include gas used field --- cmd/evm/internal/t8ntool/execution.go | 2 ++ cmd/evm/testdata/1/exp.json | 5 +++-- cmd/evm/testdata/13/exp2.json | 3 ++- cmd/evm/testdata/14/exp.json | 5 +++-- cmd/evm/testdata/14/exp2.json | 5 +++-- cmd/evm/testdata/14/exp_berlin.json | 5 +++-- cmd/evm/testdata/19/exp_arrowglacier.json | 5 +++-- cmd/evm/testdata/19/exp_london.json | 5 +++-- cmd/evm/testdata/3/exp.json | 3 ++- cmd/evm/testdata/5/exp.json | 3 ++- 10 files changed, 26 insertions(+), 15 deletions(-) diff --git a/cmd/evm/internal/t8ntool/execution.go b/cmd/evm/internal/t8ntool/execution.go index cedf96627..0109b3379 100644 --- a/cmd/evm/internal/t8ntool/execution.go +++ b/cmd/evm/internal/t8ntool/execution.go @@ -55,6 +55,7 @@ type ExecutionResult struct { Receipts types.Receipts `json:"receipts"` Rejected []*rejectedTx `json:"rejected,omitempty"` Difficulty *math.HexOrDecimal256 `json:"currentDifficulty" gencodec:"required"` + GasUsed math.HexOrDecimal64 `json:"gasUsed"` } type ommer struct { @@ -255,6 +256,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, Receipts: receipts, Rejected: rejectedTxs, Difficulty: (*math.HexOrDecimal256)(vmContext.Difficulty), + GasUsed: (math.HexOrDecimal64)(gasUsed), } return statedb, execRs, nil } diff --git a/cmd/evm/testdata/1/exp.json b/cmd/evm/testdata/1/exp.json index 17d2f8267..896627e49 100644 --- a/cmd/evm/testdata/1/exp.json +++ b/cmd/evm/testdata/1/exp.json @@ -38,6 +38,7 @@ "error": "nonce too low: address 0x8A8eAFb1cf62BfBeb1741769DAE1a9dd47996192, tx: 0 state: 1" } ], - "currentDifficulty": "0x20000" + "currentDifficulty": "0x20000", + "gasUsed": "0x5208" } -} \ No newline at end of file +} diff --git a/cmd/evm/testdata/13/exp2.json b/cmd/evm/testdata/13/exp2.json index 01ab59e84..fa5d4157f 100644 --- a/cmd/evm/testdata/13/exp2.json +++ b/cmd/evm/testdata/13/exp2.json @@ -33,6 +33,7 @@ "transactionIndex": "0x1" } ], - "currentDifficulty": "0x20000" + "currentDifficulty": "0x20000", + "gasUsed": "0x109a0" } } diff --git a/cmd/evm/testdata/14/exp.json b/cmd/evm/testdata/14/exp.json index bbe6a1317..92e9ea2b6 100644 --- a/cmd/evm/testdata/14/exp.json +++ b/cmd/evm/testdata/14/exp.json @@ -6,6 +6,7 @@ "logsHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "currentDifficulty": "0x2000020000000", - "receipts": [] + "receipts": [], + "gasUsed": "0x0" } -} \ No newline at end of file +} diff --git a/cmd/evm/testdata/14/exp2.json b/cmd/evm/testdata/14/exp2.json index 195c738d9..dba19fa20 100644 --- a/cmd/evm/testdata/14/exp2.json +++ b/cmd/evm/testdata/14/exp2.json @@ -6,6 +6,7 @@ "logsHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "receipts": [], - "currentDifficulty": "0x1ff8020000000" + "currentDifficulty": "0x1ff8020000000", + "gasUsed": "0x0" } -} \ No newline at end of file +} diff --git a/cmd/evm/testdata/14/exp_berlin.json b/cmd/evm/testdata/14/exp_berlin.json index e56478831..a849e6875 100644 --- a/cmd/evm/testdata/14/exp_berlin.json +++ b/cmd/evm/testdata/14/exp_berlin.json @@ -6,6 +6,7 @@ "logsHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "receipts": [], - "currentDifficulty": "0x1ff9000000000" + "currentDifficulty": "0x1ff9000000000", + "gasUsed": "0x0" } -} \ No newline at end of file +} diff --git a/cmd/evm/testdata/19/exp_arrowglacier.json b/cmd/evm/testdata/19/exp_arrowglacier.json index 4c5f8e0fb..bc31f7bab 100644 --- a/cmd/evm/testdata/19/exp_arrowglacier.json +++ b/cmd/evm/testdata/19/exp_arrowglacier.json @@ -6,6 +6,7 @@ "logsHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "currentDifficulty": "0x2000000200000", - "receipts": [] + "receipts": [], + "gasUsed": "0x0" } -} \ No newline at end of file +} diff --git a/cmd/evm/testdata/19/exp_london.json b/cmd/evm/testdata/19/exp_london.json index 9dc1b9d4f..60e7cbb9a 100644 --- a/cmd/evm/testdata/19/exp_london.json +++ b/cmd/evm/testdata/19/exp_london.json @@ -6,6 +6,7 @@ "logsHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "currentDifficulty": "0x2000080000000", - "receipts": [] + "receipts": [], + "gasUsed": "0x0" } -} \ No newline at end of file +} diff --git a/cmd/evm/testdata/3/exp.json b/cmd/evm/testdata/3/exp.json index ade09e9ac..36b8812c8 100644 --- a/cmd/evm/testdata/3/exp.json +++ b/cmd/evm/testdata/3/exp.json @@ -32,6 +32,7 @@ "transactionIndex": "0x0" } ], - "currentDifficulty": "0x20000" + "currentDifficulty": "0x20000", + "gasUsed": "0x521f" } } diff --git a/cmd/evm/testdata/5/exp.json b/cmd/evm/testdata/5/exp.json index 6340d4cc3..d521ee815 100644 --- a/cmd/evm/testdata/5/exp.json +++ b/cmd/evm/testdata/5/exp.json @@ -17,6 +17,7 @@ "logsHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "receipts": [], - "currentDifficulty": "0x20000" + "currentDifficulty": "0x20000", + "gasUsed": "0x0" } } From 05acc272b5f1a5da42784f7eb1851eca1a928229 Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi <1591639+s1na@users.noreply.github.com> Date: Tue, 16 Nov 2021 08:44:57 +0100 Subject: [PATCH 12/30] eth/tracers: make native 4byte default, remove js version (#23916) --- .../js/internal/tracers/4byte_tracer.js | 70 ------------------- eth/tracers/js/internal/tracers/assets.go | 23 ------ eth/tracers/native/4byte.go | 4 +- 3 files changed, 2 insertions(+), 95 deletions(-) delete mode 100644 eth/tracers/js/internal/tracers/4byte_tracer.js diff --git a/eth/tracers/js/internal/tracers/4byte_tracer.js b/eth/tracers/js/internal/tracers/4byte_tracer.js deleted file mode 100644 index 07dde6c32..000000000 --- a/eth/tracers/js/internal/tracers/4byte_tracer.js +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2017 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 . - -// 4byteTracer searches for 4byte-identifiers, and collects them for post-processing. -// It collects the methods identifiers along with the size of the supplied data, so -// a reversed signature can be matched against the size of the data. -// -// Example: -// > debug.traceTransaction( "0x214e597e35da083692f5386141e69f47e973b2c56e7a8073b1ea08fd7571e9de", {tracer: "4byteTracer"}) -// { -// 0x27dc297e-128: 1, -// 0x38cc4831-0: 2, -// 0x524f3889-96: 1, -// 0xadf59f99-288: 1, -// 0xc281d19e-0: 1 -// } -{ - // ids aggregates the 4byte ids found. - ids : {}, - - // store save the given indentifier and datasize. - store: function(id, size){ - var key = "" + toHex(id) + "-" + size; - this.ids[key] = this.ids[key] + 1 || 1; - }, - - enter: function(frame, log) { - // Skip any pre-compile invocations, those are just fancy opcodes - if (isPrecompiled(frame.getTo())) { - return; - } - var type = frame.getType() - if (type !== "CALL" && type !== "STATICCALL" && - type !== "DELEGATECALL" && type !== "CALLCODE"){ - return; - } - var input = frame.getInput() - if (input.length >= 4) { - this.store(slice(input, 0, 4), input.length - 4); - } - }, - - exit: function(frameResult) {}, - - // fault is invoked when the actual execution of an opcode fails. - fault: function(log, db) {}, - - // result is invoked when all the opcodes have been iterated over and returns - // the final result of the tracing. - result: function(ctx) { - // Save the outer calldata also - if (ctx.input.length >= 4) { - this.store(slice(ctx.input, 0, 4), ctx.input.length-4) - } - return this.ids; - }, -} diff --git a/eth/tracers/js/internal/tracers/assets.go b/eth/tracers/js/internal/tracers/assets.go index 9e9cf55b5..caeccb7f3 100644 --- a/eth/tracers/js/internal/tracers/assets.go +++ b/eth/tracers/js/internal/tracers/assets.go @@ -1,6 +1,5 @@ // Code generated by go-bindata. DO NOT EDIT. // sources: -// 4byte_tracer.js (2.375kB) // 4byte_tracer_legacy.js (2.933kB) // bigram_tracer.js (1.712kB) // call_tracer_js.js (3.497kB) @@ -79,26 +78,6 @@ func (fi bindataFileInfo) Sys() interface{} { return nil } -var __4byte_tracerJs = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x8c\x55\x5b\x4f\xdb\x48\x14\x7e\x4e\x7e\xc5\xb7\x79\xa8\x88\xea\xdc\x03\xb9\x74\xa9\x94\xa5\xa1\x8d\xc4\x02\x0a\x46\x15\x5a\xed\xc3\xc4\x73\x6c\xcf\xe2\xcc\x58\x33\x63\x48\x96\xf2\xdf\x57\x33\x76\x48\xa0\xad\xb6\x4f\x84\x73\xf9\xce\xe5\x3b\xf3\xb9\xd3\xc1\x99\xca\xb7\x5a\x24\xa9\x45\xbf\xdb\x1b\x21\x4c\x09\x89\x6a\x91\x4d\x49\x53\xb1\xc6\xac\xb0\xa9\xd2\xa6\xde\xe9\x20\x4c\x85\x41\x2c\x32\x82\x30\xc8\x99\xb6\x50\x31\xec\x9b\xf8\x4c\xac\x34\xd3\xdb\x76\xbd\xd3\x29\x73\x7e\xe8\x76\x08\xb1\x26\x82\x51\xb1\x7d\x64\x9a\xa6\xd8\xaa\x02\x11\x93\xd0\xc4\x85\xb1\x5a\xac\x0a\x4b\x10\x16\x4c\xf2\x8e\xd2\x58\x2b\x2e\xe2\xad\x83\x14\x16\x85\xe4\xa4\x7d\x69\x4b\x7a\x6d\x76\x7d\x7c\xbe\xbc\xc5\x05\x19\x43\x1a\x9f\x49\x92\x66\x19\xae\x8b\x55\x26\x22\x5c\x88\x88\xa4\x21\x30\x83\xdc\x59\x4c\x4a\x1c\x2b\x0f\xe7\x12\xcf\x5d\x2b\x37\x55\x2b\x38\x57\x85\xe4\xcc\x0a\x25\x03\x90\x70\x9d\xe3\x81\xb4\x11\x4a\x62\xb0\x2b\x55\x01\x06\x50\xda\x81\x1c\x31\xeb\x06\xd0\x50\xb9\xcb\x6b\x82\xc9\x2d\x32\x66\xf7\xa9\xbf\xb0\x90\xfd\xdc\x1c\x42\xfa\x32\xa9\xca\x09\x36\x65\xd6\x4d\xfd\x28\xb2\x0c\x2b\x42\x61\x28\x2e\xb2\xc0\xa1\xad\x0a\x8b\xaf\x8b\xf0\xcb\xd5\x6d\x88\xd9\xe5\x1d\xbe\xce\x96\xcb\xd9\x65\x78\xf7\x01\x8f\xc2\xa6\xaa\xb0\xa0\x07\x2a\xa1\xc4\x3a\xcf\x04\x71\x3c\x32\xad\x99\xb4\x5b\xa8\xd8\x21\xfc\x39\x5f\x9e\x7d\x99\x5d\x86\xb3\x3f\x16\x17\x8b\xf0\x0e\x4a\xe3\x7c\x11\x5e\xce\x6f\x6e\x70\x7e\xb5\xc4\x0c\xd7\xb3\x65\xb8\x38\xbb\xbd\x98\x2d\x71\x7d\xbb\xbc\xbe\xba\x99\xb7\x71\x43\xae\x2b\x72\xf9\xff\xbf\xf3\xd8\xb3\xa7\x09\x9c\x2c\x13\x99\xd9\x6d\xe2\x4e\x15\x30\xa9\x2a\x32\x8e\x94\x3d\x10\x34\x45\x24\x1e\x88\x83\x21\x52\xf9\xf6\x97\x49\x75\x58\x2c\x53\x32\xf1\x33\xff\xf4\x20\xb1\x88\x21\x95\x0d\x60\x88\xf0\x7b\x6a\x6d\x3e\xed\x74\x1e\x1f\x1f\xdb\x89\x2c\xda\x4a\x27\x9d\xac\x84\x33\x9d\x8f\xed\xba\xc3\x1c\xae\xb6\x96\x42\xcd\x22\xd2\x30\xc4\x74\x94\x92\xf1\xc3\x78\x47\x4b\x70\x92\x56\xc4\x82\xb4\x09\xdc\x91\x22\x52\x59\x46\x91\x35\xae\x83\xb5\x0f\xcc\x95\xb1\xad\x5c\xab\x88\x8c\x11\x32\x71\x83\x63\x61\x5f\x05\x62\x4d\x36\x55\xdc\xe0\x00\xee\xed\x34\x46\xfc\x4b\xbb\x6d\x98\x22\x2f\x69\xe4\xcc\xb2\x00\x46\xf9\xe9\xa1\xc9\x9d\x19\x71\x18\x91\x48\x66\x0b\x4d\xfe\x2d\xad\x08\x6b\x66\x23\x77\xec\x2c\x61\x42\x1a\xfb\x1d\xa0\xc3\xd9\x31\x32\xdf\xb0\x75\x9e\xd1\xd4\xfd\x06\x3e\x82\xd3\xaa\x48\xda\xd6\xad\x20\xd4\x4c\x1a\x16\xb9\xe3\x3e\x42\xa3\xbb\xe9\xf7\x86\x74\x3c\x19\xd1\xe0\x98\xb3\xee\x78\x70\x32\xe9\xc7\xc7\x83\xf1\x49\x6f\xd8\xa3\x93\x49\x3c\x1c\xd1\x64\x34\x58\xf5\xa3\xe3\x13\x1a\xb1\x71\x77\x34\x58\xf5\x88\x75\xc7\x31\x1f\x1d\x8f\x7a\x34\xe1\xd4\x08\xf0\xe4\x81\xf5\x14\x8d\x83\x4d\x37\x9e\x9b\x65\xf5\xa7\xf2\x0f\xd0\xdd\xf4\x47\x3c\xea\x4f\x46\xd4\xea\xf5\xc7\x53\xf4\x82\xbd\x67\x30\x8e\xa2\xe1\x78\xd0\x6b\x75\xa7\xe8\x1f\xd8\x8f\xfb\xc3\x78\x30\x1e\x4f\x5a\x93\x93\xd7\x09\x8c\xc7\xc7\x93\x78\x32\x69\xf5\xc7\x6f\xa0\xa2\xfe\xb8\xc7\x7b\x13\x72\x50\xbd\xd2\xfc\x5c\x7f\xaa\xd7\x9c\xe0\x70\x03\x96\x24\x9a\x12\x66\xa9\x64\xcd\x77\xec\x1d\xb1\x13\x8b\x76\xbd\xe6\x7e\x4f\xf1\xf4\x1c\xd4\x7d\x8e\xb1\xee\xe2\x8d\xbb\x6b\x7f\x90\xc2\x3d\x43\x21\x5f\x48\xf6\x17\xe3\x76\xef\xb8\x68\xd7\x6b\x3e\x7e\x8a\xb8\x90\xe5\x8e\x05\x0f\x3c\x4d\xcd\xa7\x7a\xad\xf6\xc0\x34\xee\x69\x8b\x53\x34\x1a\x78\x0f\xab\xbe\xd0\xe6\x48\xf0\x26\xde\xa3\xd1\x72\x16\x17\xf9\xa1\x5e\xab\xd9\x54\x98\xb6\xe0\xe6\xaf\x7b\xda\xfe\x8d\x53\xbc\xfe\xff\x3d\x7a\xf8\xf6\x0d\xbd\x0f\xf5\x9a\x6f\x93\xa4\x75\xeb\x7f\xa9\x19\x6b\xb6\xa6\x00\x99\x4a\x9a\x70\x65\x3b\x1d\xdc\xdc\x8b\xdc\x6b\x59\xae\xa9\x15\xa9\x75\xee\xd5\x5f\x3e\xa8\xc8\xeb\xa3\x09\x60\x53\xe5\x74\x55\x13\xfe\x29\x8c\x45\xcc\x64\xb4\x85\xca\x23\xc5\xc9\xd4\x6b\x35\x11\xe3\x48\x98\x6b\x4d\x55\x32\x2f\xab\xb4\x13\xb2\xa1\x3a\x6a\x36\xcb\x4a\x35\x4d\xb6\xd0\xd2\x8d\xf0\x5c\xcd\x6b\xb7\x39\xe1\x14\xfb\xe8\x6d\x4e\x47\xcd\x0a\xd1\x3b\x7f\x3b\x3d\x45\xe3\x6c\x76\x71\xd1\xc0\xbb\x77\xd8\x9b\x6e\xc2\x59\xb8\x38\xdb\x39\x1c\xfa\xde\xf7\x69\x7e\x31\xff\x3c\x0b\xe7\x3f\x48\x73\xa6\xb3\xab\x4f\xf3\x46\xf3\x27\x1d\x09\x99\x17\xf6\xb0\xa5\x85\x33\xbc\xf4\xe4\xdd\xed\x8c\x64\x62\x53\x7c\x3c\xc5\xb0\x1a\xcd\x73\xe0\xe9\x3d\x32\x4e\x62\xca\xc0\x00\xdd\x00\xc3\x66\x80\x57\x69\x2d\x0c\x9b\x55\xc9\x92\xa1\x8d\xb0\x6f\x09\x5a\x92\x29\x32\xdb\xdc\x9f\x5a\xcc\x8a\xcc\xba\xef\x87\xe3\xe5\xde\x29\x7c\x5a\x49\x3e\x8b\x6c\xc1\x32\xd0\x86\xa2\xc2\x01\xb8\x37\xcf\x64\xc5\x0e\xe2\x52\x8c\x6b\x3e\xff\xa0\x4a\xa6\x92\x00\x7c\x75\x50\x41\xfb\x92\xdf\x95\x60\x59\xe6\xcb\x54\x6c\x97\x2a\xbe\x22\x77\xe8\x96\x34\x73\x9f\x31\xf5\x50\x9d\x7a\xb9\x4e\xe3\xe1\x5c\x4e\x2c\x24\xcb\x76\xc0\x95\x14\x39\x3d\xf0\x32\x59\x2b\xed\x07\x3d\x45\x76\xb3\xbf\xc9\xdd\xa3\x52\x85\xfb\xbe\x46\x2c\xcb\xdc\x43\x02\xcb\x8c\xaa\xb8\x88\xec\xa6\xfd\xcb\x7c\xbc\x04\xbf\x70\xf2\x36\xbd\x35\x6c\x56\x77\x50\x8e\xf1\xf2\xae\xca\x97\xf4\x5c\xff\x2f\x00\x00\xff\xff\x6e\x82\xd6\x8b\x47\x09\x00\x00") - -func _4byte_tracerJsBytes() ([]byte, error) { - return bindataRead( - __4byte_tracerJs, - "4byte_tracer.js", - ) -} - -func _4byte_tracerJs() (*asset, error) { - bytes, err := _4byte_tracerJsBytes() - if err != nil { - return nil, err - } - - info := bindataFileInfo{name: "4byte_tracer.js", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)} - a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xdb, 0xff, 0x9f, 0xb0, 0x9e, 0x1a, 0xd7, 0xd3, 0xcb, 0xf6, 0x6e, 0xf5, 0xf7, 0xbc, 0xe7, 0xbc, 0xd6, 0x7c, 0x37, 0x16, 0xd7, 0x4f, 0x85, 0xb3, 0x1f, 0xfd, 0x66, 0x3e, 0x91, 0xed, 0x86, 0x27}} - return a, nil -} - var __4byte_tracer_legacyJs = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x94\x56\x5b\x6f\xdb\x4a\x0e\x7e\xb6\x7f\x05\xd7\x2f\xb5\x51\x59\x8e\x2f\x89\x2f\xd9\x16\xf0\xe6\xa4\x6d\x80\x9c\x24\x88\xdd\x3d\x28\x16\xfb\x30\x9e\xa1\xac\xd9\xc8\x33\xc2\x0c\xe5\x4b\x73\xf2\xdf\x17\x1c\x49\x89\x93\xd3\x62\xbb\x4f\x96\x47\xc3\x8f\x1f\xc9\x8f\xa4\x7a\x3d\xb8\xb0\xf9\xc1\xe9\x75\x4a\x30\x38\xe9\x8f\x61\x99\x22\xac\x6d\x17\x29\x45\x87\xc5\x06\xe6\x05\xa5\xd6\xf9\x66\xaf\x07\xcb\x54\x7b\x48\x74\x86\xa0\x3d\xe4\xc2\x11\xd8\x04\xe8\xcd\xfd\x4c\xaf\x9c\x70\x87\xb8\xd9\xeb\x95\x36\x3f\x7c\xcd\x08\x89\x43\x04\x6f\x13\xda\x09\x87\x33\x38\xd8\x02\xa4\x30\xe0\x50\x69\x4f\x4e\xaf\x0a\x42\xd0\x04\xc2\xa8\x9e\x75\xb0\xb1\x4a\x27\x07\x86\xd4\x04\x85\x51\xe8\x82\x6b\x42\xb7\xf1\x35\x8f\xcf\x37\x5f\xe1\x1a\xbd\x47\x07\x9f\xd1\xa0\x13\x19\xdc\x15\xab\x4c\x4b\xb8\xd6\x12\x8d\x47\x10\x1e\x72\x3e\xf1\x29\x2a\x58\x05\x38\x36\xfc\xc4\x54\x16\x15\x15\xf8\x64\x0b\xa3\x04\x69\x6b\x22\x40\xcd\xcc\x61\x8b\xce\x6b\x6b\x60\x58\xbb\xaa\x00\x23\xb0\x8e\x41\xda\x82\x38\x00\x07\x36\x67\xbb\x0e\x08\x73\x80\x4c\xd0\x8b\xe9\x2f\x24\xe4\x25\x6e\x05\xda\x04\x37\xa9\xcd\x11\x28\x15\xc4\x51\xef\x74\x96\xc1\x0a\xa1\xf0\x98\x14\x59\xc4\x68\xab\x82\xe0\x8f\xab\xe5\x97\xdb\xaf\x4b\x98\xdf\x7c\x83\x3f\xe6\xf7\xf7\xf3\x9b\xe5\xb7\x73\xd8\x69\x4a\x6d\x41\x80\x5b\x2c\xa1\xf4\x26\xcf\x34\x2a\xd8\x09\xe7\x84\xa1\x03\xd8\x84\x11\x7e\xbf\xbc\xbf\xf8\x32\xbf\x59\xce\xff\x71\x75\x7d\xb5\xfc\x06\xd6\xc1\xa7\xab\xe5\xcd\xe5\x62\x01\x9f\x6e\xef\x61\x0e\x77\xf3\xfb\xe5\xd5\xc5\xd7\xeb\xf9\x3d\xdc\x7d\xbd\xbf\xbb\x5d\x5c\xc6\xb0\x40\x66\x85\x6c\xff\xbf\x73\x9e\x84\xea\x39\x04\x85\x24\x74\xe6\xeb\x4c\x7c\xb3\x05\xf8\xd4\x16\x99\x82\x54\x6c\x11\x1c\x4a\xd4\x5b\x54\x20\x40\xda\xfc\xf0\xcb\x45\x65\x2c\x91\x59\xb3\x0e\x31\xff\x54\x90\x70\x95\x80\xb1\x14\x81\x47\x84\xbf\xa7\x44\xf9\xac\xd7\xdb\xed\x76\xf1\xda\x14\xb1\x75\xeb\x5e\x56\xc2\xf9\xde\xc7\xb8\xc9\x98\xa3\xd5\x81\x70\xe9\x84\x44\x07\x1e\x85\x93\x29\xfa\x10\x4c\x78\xd1\xd5\x0a\x0d\xe9\x44\xa3\xf3\x11\x8b\x14\xa4\xcd\x32\x94\xe4\x99\xc1\x26\x5c\xcc\xad\xa7\x6e\xee\xac\x44\xef\xb5\x59\x73\xe0\x70\x45\xaf\x2e\xc2\x06\x29\xb5\xca\xc3\x11\xdc\xdb\x68\xbc\xfe\x8e\x75\x36\x7c\x91\x97\x65\x54\x82\x44\x04\xde\x86\xe8\xc1\x21\xcb\x0c\x15\x78\xbd\x36\x82\x0a\x87\xa1\x97\x56\x08\x1b\x41\x92\xc5\x2e\xd6\x42\x1b\x4f\x7f\x01\x64\x9c\xba\x22\x97\x7b\xb1\xc9\x33\x9c\xf1\x33\xc0\x47\x50\xb8\x2a\xd6\x31\x71\x0a\x96\x4e\x18\x2f\x24\x8b\xbb\x0d\xad\x93\xfd\xa0\x3f\xc2\xd3\xe9\x18\x87\xa7\x4a\x9c\x4c\x86\x67\xd3\x41\x72\x3a\x9c\x9c\xf5\x47\x7d\x3c\x9b\x26\xa3\x31\x4e\xc7\xc3\xd5\x40\x9e\x9e\xe1\x58\x4c\x4e\xc6\xc3\x55\x1f\xc5\xc9\x24\x51\xe3\xd3\x71\x1f\xa7\x0a\x5b\x11\x3c\x06\x60\x37\x83\xd6\x51\xa6\x5b\x4f\x9d\xd2\xfb\x63\xf9\x03\x70\xb2\x1f\x8c\x95\x1c\x4c\xc7\xd8\xed\x0f\x26\x33\xe8\x47\x2f\x6f\x86\x13\x29\x47\x93\x61\xbf\x7b\x32\x83\xc1\xd1\xf9\xe9\x60\x94\x0c\x27\x93\x69\x77\x7a\xf6\xda\x40\xa8\xe4\x74\x9a\x4c\xa7\xdd\xc1\xe4\x0d\x94\x1c\x4c\xfa\xaa\x3f\x45\x86\xea\x97\xc7\x4f\xcd\xc7\x66\x83\x07\x8e\xf2\x20\xd6\x6b\x87\x6b\x41\x58\x56\x2d\x30\x0e\x2f\x12\x1e\x16\x71\xb3\xc1\xcf\x33\x78\x7c\x8a\x9a\xc1\x46\x8a\x2c\x5b\x1e\x72\x56\x35\x15\xce\x78\x78\x97\x88\xcc\xe3\xbb\xa0\x0b\x63\x4d\x97\x2f\x78\x1e\x1f\x01\x2f\x47\x7c\xe8\x6a\xa3\x70\x1f\x2e\xf0\x51\xa2\x9d\x27\x1e\xb3\x62\x13\x10\x45\xc2\xd3\xe4\xdd\x56\x64\x05\xbe\x8b\x40\xc7\x18\xc3\x06\x37\x5c\x54\xe1\x28\x6e\x36\x6a\x97\x33\x48\x0a\x53\x56\xca\xe6\x9e\x5c\xe7\xb1\xd9\x68\xf8\x9d\x26\x99\x1e\x1d\x48\xe1\x11\x5a\x17\xf3\xeb\xeb\xd6\x0c\x5e\xfe\x5c\xdc\xfe\x76\xd9\x9a\x35\x1b\x0d\x76\xb9\x16\x2c\x6d\xa5\x5c\x04\x5b\x91\x45\xa5\xbb\xea\xc7\x7f\x0f\x0f\xb6\xa0\xfa\xd7\x7f\x67\xb3\x32\x5e\x18\x9e\x43\xaf\x07\x9e\x84\x7c\x80\x9c\x1c\x90\x2d\xcd\x9a\xcf\xae\x7f\xbb\xbc\xbe\xfc\x3c\x5f\x5e\xbe\xa2\xb0\x58\xce\x97\x57\x17\xe5\xd1\x5f\x49\xfc\x1f\xfe\x07\x3f\xf3\xdf\x68\x3c\x35\x9f\x6f\x85\x9a\x9c\x37\x1b\x75\xd5\x3c\xf1\x9c\xf2\x3c\x8d\xc2\x18\xd1\x3c\x3c\xb9\x2c\x55\x6b\x86\x3e\xe7\x8e\xe1\x0e\x8a\x9b\x8d\x70\xff\x28\xdf\x5a\x45\xa1\xb9\x42\x86\xb7\xc2\xc1\x03\x1e\xe0\x03\xb4\x5a\xf0\x1e\xc8\x7e\xc1\x7d\x5b\xab\x0e\xbc\x87\x56\x97\x4f\xf8\xe6\x79\xb3\xd1\xa0\x54\xfb\x58\x2b\xff\xaf\x07\x3c\xfc\x1b\x3e\xc0\xeb\xff\xef\xa1\x0f\x7f\xfe\x09\xfd\x57\x34\x31\xe7\x85\xa1\xcd\xd6\x3e\xa0\x0a\x92\xe1\x01\x70\x00\x9b\x4b\xab\xaa\x8d\xc1\x11\xfc\xf3\x77\xc0\x3d\xca\x82\xd0\x07\xba\x98\x1f\xb1\xcd\xec\x3a\x02\xb5\xea\x00\xb3\xed\xf5\x60\xf1\xa0\xf3\xb0\xb8\x4a\x14\x5f\xc2\xf0\x46\x34\x96\x40\x1b\x42\x67\x44\x16\xa4\xed\xab\xf8\x24\xd5\x7c\x6b\xf5\x31\x6a\x6c\xf3\x98\xec\x82\x9c\x36\xeb\x76\xa7\xc3\x31\xea\x04\xda\x7f\x93\x54\xfa\xaa\xd2\x7f\x5e\x15\xe3\xd8\x75\xee\xb0\x2b\xed\x26\x0f\x5f\x19\x66\x6b\x65\xd8\xc3\x3e\x02\x4a\x2d\xef\x6f\x87\xf0\x9f\xc2\x13\x24\xc2\xc8\x67\xa2\x15\xbe\xf6\x77\x0e\x2b\x63\xd5\x26\x3b\x57\xca\xa1\xf7\x81\x51\x50\x42\xcc\x6d\xd6\xee\x77\x5e\xc8\xf5\xcf\x3a\x9d\xce\xcf\x48\x7d\x16\x61\xf7\xbf\x0a\xbc\x5e\x62\x55\xfc\xda\x2c\xbe\xc3\x07\x78\xe3\x41\x12\x57\xad\x13\x87\x5e\xbd\x4d\xda\xcf\x19\x08\xd7\x3f\x7e\x80\x51\xe5\xb2\x84\xb8\x4d\x92\x1f\x61\xbc\xb1\x2f\x65\x12\x14\x17\x22\x62\xd1\xbb\x43\xec\x79\x6d\xb5\x03\x48\x54\x61\xbd\x87\x51\x27\x0a\xd4\xba\xa3\x4e\x15\x4f\x2d\x9d\x44\x14\x19\x1d\x6b\x67\x97\x56\xdf\x07\x42\x52\x21\xb2\x4a\x2e\xfc\xad\x63\x13\x10\xa6\x56\x54\x52\x6e\xee\x46\xb0\xff\xa1\x86\xa0\x76\xe1\xd0\xff\xc8\x07\x27\x8f\xfd\xd4\xe2\x0a\x3b\x7f\x85\xdc\x60\x84\x4e\xf0\x47\x8f\xdd\x56\x2d\x56\x0d\xcd\x00\x57\xce\x42\xce\x7f\x05\x5c\x2d\x2e\xde\x1e\x61\xa9\x36\xca\xf3\x23\x52\x92\xf6\x2f\xa2\xae\x9b\xd9\x16\x3c\x3f\xb9\x86\xdc\xc0\x20\x32\x6f\xab\xaa\x48\xda\xc7\xda\xe4\x05\xc5\x19\x9a\x35\xa5\xc7\x15\x3a\x4a\x7a\x99\xe9\xe7\xcb\x11\x9c\x44\x21\xd1\x6f\xcd\xbb\xa3\xce\xeb\x29\x53\xf7\x73\xd9\xc1\x4f\xcd\xff\x06\x00\x00\xff\xff\x8e\xc8\x27\x72\x75\x0b\x00\x00") func _4byte_tracer_legacyJsBytes() ([]byte, error) { @@ -390,7 +369,6 @@ func AssetNames() []string { // _bindata is a table, holding each asset generator, mapped to its name. var _bindata = map[string]func() (*asset, error){ - "4byte_tracer.js": _4byte_tracerJs, "4byte_tracer_legacy.js": _4byte_tracer_legacyJs, "bigram_tracer.js": bigram_tracerJs, "call_tracer_js.js": call_tracer_jsJs, @@ -447,7 +425,6 @@ type bintree struct { } var _bintree = &bintree{nil, map[string]*bintree{ - "4byte_tracer.js": {_4byte_tracerJs, map[string]*bintree{}}, "4byte_tracer_legacy.js": {_4byte_tracer_legacyJs, map[string]*bintree{}}, "bigram_tracer.js": {bigram_tracerJs, map[string]*bintree{}}, "call_tracer_js.js": {call_tracer_jsJs, map[string]*bintree{}}, diff --git a/eth/tracers/native/4byte.go b/eth/tracers/native/4byte.go index 082a60355..e60e82de4 100644 --- a/eth/tracers/native/4byte.go +++ b/eth/tracers/native/4byte.go @@ -29,7 +29,7 @@ import ( ) func init() { - register("4byte", newFourByteTracer) + register("4byteTracer", newFourByteTracer) } // fourByteTracer searches for 4byte-identifiers, and collects them for post-processing. @@ -37,7 +37,7 @@ func init() { // a reversed signature can be matched against the size of the data. // // Example: -// > debug.traceTransaction( "0x214e597e35da083692f5386141e69f47e973b2c56e7a8073b1ea08fd7571e9de", {tracer: "4byte"}) +// > debug.traceTransaction( "0x214e597e35da083692f5386141e69f47e973b2c56e7a8073b1ea08fd7571e9de", {tracer: "4byteTracer"}) // { // 0x27dc297e-128: 1, // 0x38cc4831-0: 2, From b7a6409cc10238d63e6a45f32654a69acf1310e0 Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Tue, 16 Nov 2021 10:33:56 +0100 Subject: [PATCH 13/30] core/rawdb: better error message in freezer (#23901) * core/rawdb: better error message in freezer * Apply suggestions from code review --- core/rawdb/freezer_batch.go | 4 ++-- core/rawdb/freezer_test.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/rawdb/freezer_batch.go b/core/rawdb/freezer_batch.go index 8297c0ac1..762fa8f25 100644 --- a/core/rawdb/freezer_batch.go +++ b/core/rawdb/freezer_batch.go @@ -116,7 +116,7 @@ func (batch *freezerTableBatch) reset() { // existing data. func (batch *freezerTableBatch) Append(item uint64, data interface{}) error { if item != batch.curItem { - return errOutOrderInsertion + return fmt.Errorf("%w: have %d want %d", errOutOrderInsertion, item, batch.curItem) } // Encode the item. @@ -136,7 +136,7 @@ func (batch *freezerTableBatch) Append(item uint64, data interface{}) error { // existing data. func (batch *freezerTableBatch) AppendRaw(item uint64, blob []byte) error { if item != batch.curItem { - return errOutOrderInsertion + return fmt.Errorf("%w: have %d want %d", errOutOrderInsertion, item, batch.curItem) } encItem := blob diff --git a/core/rawdb/freezer_test.go b/core/rawdb/freezer_test.go index 7359131c8..fa84f8030 100644 --- a/core/rawdb/freezer_test.go +++ b/core/rawdb/freezer_test.go @@ -246,7 +246,7 @@ func TestFreezerConcurrentModifyTruncate(t *testing.T) { if truncateErr != nil { t.Fatal("concurrent truncate failed:", err) } - if !(modifyErr == nil || modifyErr == errOutOrderInsertion) { + if !(errors.Is(modifyErr, nil) || errors.Is(modifyErr, errOutOrderInsertion)) { t.Fatal("wrong error from concurrent modify:", modifyErr) } checkAncientCount(t, f, "test", 10) From 33f28138095faada5cbf027849993808a9dba785 Mon Sep 17 00:00:00 2001 From: Jesse Tane Date: Tue, 16 Nov 2021 07:45:02 -0500 Subject: [PATCH 14/30] cmd/geth: add flag --dev.gaslimit for dev mode (#23686) * cmd, core: add flag --dev.gaslimit to allow configuring initial block gas limit in dev mode * core: use provided gaslimit Co-authored-by: Martin Holst Swende --- cmd/geth/main.go | 1 + cmd/geth/usage.go | 1 + cmd/utils/flags.go | 7 ++++++- console/console_test.go | 2 +- core/genesis.go | 4 ++-- miner/miner_test.go | 2 +- 6 files changed, 12 insertions(+), 5 deletions(-) diff --git a/cmd/geth/main.go b/cmd/geth/main.go index 16c3be53b..8584238f5 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -140,6 +140,7 @@ var ( utils.MainnetFlag, utils.DeveloperFlag, utils.DeveloperPeriodFlag, + utils.DeveloperGasLimitFlag, utils.RopstenFlag, utils.SepoliaFlag, utils.RinkebyFlag, diff --git a/cmd/geth/usage.go b/cmd/geth/usage.go index 38f690f17..c63c62fd3 100644 --- a/cmd/geth/usage.go +++ b/cmd/geth/usage.go @@ -75,6 +75,7 @@ var AppHelpFlagGroups = []flags.FlagGroup{ Flags: []cli.Flag{ utils.DeveloperFlag, utils.DeveloperPeriodFlag, + utils.DeveloperGasLimitFlag, }, }, { diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 52554fbe5..25453148c 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -167,6 +167,11 @@ var ( Name: "dev.period", Usage: "Block period to use in developer mode (0 = mine only if transaction pending)", } + DeveloperGasLimitFlag = cli.Uint64Flag{ + Name: "dev.gaslimit", + Usage: "Initial block gas limit", + Value: 11500000, + } IdentityFlag = cli.StringFlag{ Name: "identity", Usage: "Custom node name", @@ -1661,7 +1666,7 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { log.Info("Using developer account", "address", developer.Address) // Create a new developer genesis block or reuse existing one - cfg.Genesis = core.DeveloperGenesisBlock(uint64(ctx.GlobalInt(DeveloperPeriodFlag.Name)), developer.Address) + cfg.Genesis = core.DeveloperGenesisBlock(uint64(ctx.GlobalInt(DeveloperPeriodFlag.Name)), ctx.GlobalUint64(DeveloperGasLimitFlag.Name), developer.Address) if ctx.GlobalIsSet(DataDirFlag.Name) { // Check if we have an already initialized chain and fall back to // that if so. Otherwise we need to generate a new genesis spec. diff --git a/console/console_test.go b/console/console_test.go index f6ab78141..71c80c20f 100644 --- a/console/console_test.go +++ b/console/console_test.go @@ -99,7 +99,7 @@ func newTester(t *testing.T, confOverride func(*ethconfig.Config)) *tester { t.Fatalf("failed to create node: %v", err) } ethConf := ðconfig.Config{ - Genesis: core.DeveloperGenesisBlock(15, common.Address{}), + Genesis: core.DeveloperGenesisBlock(15, 11_500_000, common.Address{}), Miner: miner.Config{ Etherbase: common.HexToAddress(testAddress), }, diff --git a/core/genesis.go b/core/genesis.go index 37cc96fe6..85d01ec87 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -416,7 +416,7 @@ func DefaultSepoliaGenesisBlock() *Genesis { } // DeveloperGenesisBlock returns the 'geth --dev' genesis block. -func DeveloperGenesisBlock(period uint64, faucet common.Address) *Genesis { +func DeveloperGenesisBlock(period uint64, gasLimit uint64, faucet common.Address) *Genesis { // Override the default period to the user requested one config := *params.AllCliqueProtocolChanges config.Clique = ¶ms.CliqueConfig{ @@ -428,7 +428,7 @@ func DeveloperGenesisBlock(period uint64, faucet common.Address) *Genesis { return &Genesis{ Config: &config, ExtraData: append(append(make([]byte, 32), faucet[:]...), make([]byte, crypto.SignatureLength)...), - GasLimit: 11500000, + GasLimit: gasLimit, BaseFee: big.NewInt(params.InitialBaseFee), Difficulty: big.NewInt(1), Alloc: map[common.Address]GenesisAccount{ diff --git a/miner/miner_test.go b/miner/miner_test.go index da1e472db..4b5bff1df 100644 --- a/miner/miner_test.go +++ b/miner/miner_test.go @@ -237,7 +237,7 @@ func createMiner(t *testing.T) (*Miner, *event.TypeMux) { // Create chainConfig memdb := memorydb.New() chainDB := rawdb.NewDatabase(memdb) - genesis := core.DeveloperGenesisBlock(15, common.HexToAddress("12345")) + genesis := core.DeveloperGenesisBlock(15, 11_500_000, common.HexToAddress("12345")) chainConfig, _, err := core.SetupGenesisBlock(chainDB, genesis) if err != nil { t.Fatalf("can't create new chain config: %v", err) From fa9671851272129c9cba90ed4385d5b0558d34a1 Mon Sep 17 00:00:00 2001 From: lightclient <14004106+lightclient@users.noreply.github.com> Date: Wed, 17 Nov 2021 05:50:08 -0700 Subject: [PATCH 15/30] cmd/evm: rename t8n result to match types.Header (ReceiptRoot->ReceiptsRoot) (#23924) --- cmd/evm/internal/t8ntool/execution.go | 2 +- cmd/evm/testdata/1/exp.json | 2 +- cmd/evm/testdata/13/exp2.json | 2 +- cmd/evm/testdata/14/exp.json | 2 +- cmd/evm/testdata/14/exp2.json | 2 +- cmd/evm/testdata/14/exp_berlin.json | 2 +- cmd/evm/testdata/19/exp_arrowglacier.json | 2 +- cmd/evm/testdata/19/exp_london.json | 2 +- cmd/evm/testdata/3/exp.json | 2 +- cmd/evm/testdata/5/exp.json | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/cmd/evm/internal/t8ntool/execution.go b/cmd/evm/internal/t8ntool/execution.go index 0109b3379..dfdde4217 100644 --- a/cmd/evm/internal/t8ntool/execution.go +++ b/cmd/evm/internal/t8ntool/execution.go @@ -49,7 +49,7 @@ type Prestate struct { type ExecutionResult struct { StateRoot common.Hash `json:"stateRoot"` TxRoot common.Hash `json:"txRoot"` - ReceiptRoot common.Hash `json:"receiptRoot"` + ReceiptRoot common.Hash `json:"receiptsRoot"` LogsHash common.Hash `json:"logsHash"` Bloom types.Bloom `json:"logsBloom" gencodec:"required"` Receipts types.Receipts `json:"receipts"` diff --git a/cmd/evm/testdata/1/exp.json b/cmd/evm/testdata/1/exp.json index 896627e49..7d3805012 100644 --- a/cmd/evm/testdata/1/exp.json +++ b/cmd/evm/testdata/1/exp.json @@ -15,7 +15,7 @@ "result": { "stateRoot": "0x84208a19bc2b46ada7445180c1db162be5b39b9abc8c0a54b05d32943eae4e13", "txRoot": "0xc4761fd7b87ff2364c7c60b6c5c8d02e522e815328aaea3f20e3b7b7ef52c42d", - "receiptRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2", + "receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2", "logsHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "receipts": [ diff --git a/cmd/evm/testdata/13/exp2.json b/cmd/evm/testdata/13/exp2.json index fa5d4157f..ba8c9f865 100644 --- a/cmd/evm/testdata/13/exp2.json +++ b/cmd/evm/testdata/13/exp2.json @@ -2,7 +2,7 @@ "result": { "stateRoot": "0xe4b924a6adb5959fccf769d5b7bb2f6359e26d1e76a2443c5a91a36d826aef61", "txRoot": "0x013509c8563d41c0ae4bf38f2d6d19fc6512a1d0d6be045079c8c9f68bf45f9d", - "receiptRoot": "0xa532a08aa9f62431d6fe5d924951b8efb86ed3c54d06fee77788c3767dd13420", + "receiptsRoot": "0xa532a08aa9f62431d6fe5d924951b8efb86ed3c54d06fee77788c3767dd13420", "logsHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "receipts": [ diff --git a/cmd/evm/testdata/14/exp.json b/cmd/evm/testdata/14/exp.json index 92e9ea2b6..9bf5635f5 100644 --- a/cmd/evm/testdata/14/exp.json +++ b/cmd/evm/testdata/14/exp.json @@ -2,7 +2,7 @@ "result": { "stateRoot": "0x6f058887ca01549716789c380ede95aecc510e6d1fdc4dbf67d053c7c07f4bdc", "txRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", - "receiptRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", "logsHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "currentDifficulty": "0x2000020000000", diff --git a/cmd/evm/testdata/14/exp2.json b/cmd/evm/testdata/14/exp2.json index dba19fa20..9c9025381 100644 --- a/cmd/evm/testdata/14/exp2.json +++ b/cmd/evm/testdata/14/exp2.json @@ -2,7 +2,7 @@ "result": { "stateRoot": "0x6f058887ca01549716789c380ede95aecc510e6d1fdc4dbf67d053c7c07f4bdc", "txRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", - "receiptRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", "logsHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "receipts": [], diff --git a/cmd/evm/testdata/14/exp_berlin.json b/cmd/evm/testdata/14/exp_berlin.json index a849e6875..c2bf95311 100644 --- a/cmd/evm/testdata/14/exp_berlin.json +++ b/cmd/evm/testdata/14/exp_berlin.json @@ -2,7 +2,7 @@ "result": { "stateRoot": "0x6f058887ca01549716789c380ede95aecc510e6d1fdc4dbf67d053c7c07f4bdc", "txRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", - "receiptRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", "logsHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "receipts": [], diff --git a/cmd/evm/testdata/19/exp_arrowglacier.json b/cmd/evm/testdata/19/exp_arrowglacier.json index bc31f7bab..9cf56ffaf 100644 --- a/cmd/evm/testdata/19/exp_arrowglacier.json +++ b/cmd/evm/testdata/19/exp_arrowglacier.json @@ -2,7 +2,7 @@ "result": { "stateRoot": "0x6f058887ca01549716789c380ede95aecc510e6d1fdc4dbf67d053c7c07f4bdc", "txRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", - "receiptRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", "logsHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "currentDifficulty": "0x2000000200000", diff --git a/cmd/evm/testdata/19/exp_london.json b/cmd/evm/testdata/19/exp_london.json index 60e7cbb9a..a06bc8ca6 100644 --- a/cmd/evm/testdata/19/exp_london.json +++ b/cmd/evm/testdata/19/exp_london.json @@ -2,7 +2,7 @@ "result": { "stateRoot": "0x6f058887ca01549716789c380ede95aecc510e6d1fdc4dbf67d053c7c07f4bdc", "txRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", - "receiptRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", "logsHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "currentDifficulty": "0x2000080000000", diff --git a/cmd/evm/testdata/3/exp.json b/cmd/evm/testdata/3/exp.json index 36b8812c8..71b3d2f55 100644 --- a/cmd/evm/testdata/3/exp.json +++ b/cmd/evm/testdata/3/exp.json @@ -15,7 +15,7 @@ "result": { "stateRoot": "0xb7341da3f9f762a6884eaa186c32942734c146b609efee11c4b0214c44857ea1", "txRoot": "0x75e61774a2ff58cbe32653420256c7f44bc715715a423b0b746d5c622979af6b", - "receiptRoot": "0xd0d26df80374a327c025d405ebadc752b1bbd089d864801ae78ab704bcad8086", + "receiptsRoot": "0xd0d26df80374a327c025d405ebadc752b1bbd089d864801ae78ab704bcad8086", "logsHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "receipts": [ diff --git a/cmd/evm/testdata/5/exp.json b/cmd/evm/testdata/5/exp.json index d521ee815..7d715672c 100644 --- a/cmd/evm/testdata/5/exp.json +++ b/cmd/evm/testdata/5/exp.json @@ -13,7 +13,7 @@ "result": { "stateRoot": "0xa7312add33811645c6aa65d928a1a4f49d65d448801912c069a0aa8fe9c1f393", "txRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", - "receiptRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", "logsHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "receipts": [], From 16341e05636fd088aa04a27fca6dc5cda5dbab8f Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Wed, 17 Nov 2021 07:44:41 -0600 Subject: [PATCH 16/30] ethclient: fix tx sender cache miss detection (#23877) This fixes a bug in TransactionSender where it would return the zero address for transactions where the sender address wasn't cached already. Co-authored-by: Felix Lange --- ethclient/ethclient.go | 2 + ethclient/ethclient_test.go | 161 +++++++++++++++++++++++++++--------- ethclient/signer.go | 2 +- 3 files changed, 124 insertions(+), 41 deletions(-) diff --git a/ethclient/ethclient.go b/ethclient/ethclient.go index 18e0a4941..37680807d 100644 --- a/ethclient/ethclient.go +++ b/ethclient/ethclient.go @@ -233,6 +233,8 @@ func (ec *Client) TransactionSender(ctx context.Context, tx *types.Transaction, if err == nil { return sender, nil } + + // It was not found in cache, ask the server. var meta struct { Hash common.Hash From common.Address diff --git a/ethclient/ethclient_test.go b/ethclient/ethclient_test.go index a958c1e32..d56febc91 100644 --- a/ethclient/ethclient_test.go +++ b/ethclient/ethclient_test.go @@ -187,9 +187,34 @@ var ( testBalance = big.NewInt(2e15) ) +var genesis = &core.Genesis{ + Config: params.AllEthashProtocolChanges, + Alloc: core.GenesisAlloc{testAddr: {Balance: testBalance}}, + ExtraData: []byte("test genesis"), + Timestamp: 9000, + BaseFee: big.NewInt(params.InitialBaseFee), +} + +var testTx1 = types.MustSignNewTx(testKey, types.LatestSigner(genesis.Config), &types.LegacyTx{ + Nonce: 0, + Value: big.NewInt(12), + GasPrice: big.NewInt(params.InitialBaseFee), + Gas: params.TxGas, + To: &common.Address{2}, +}) + +var testTx2 = types.MustSignNewTx(testKey, types.LatestSigner(genesis.Config), &types.LegacyTx{ + Nonce: 1, + Value: big.NewInt(8), + GasPrice: big.NewInt(params.InitialBaseFee), + Gas: params.TxGas, + To: &common.Address{2}, +}) + func newTestBackend(t *testing.T) (*node.Node, []*types.Block) { // Generate test chain. - genesis, blocks := generateTestChain() + blocks := generateTestChain() + // Create node n, err := node.New(&node.Config{}) if err != nil { @@ -212,25 +237,22 @@ func newTestBackend(t *testing.T) (*node.Node, []*types.Block) { return n, blocks } -func generateTestChain() (*core.Genesis, []*types.Block) { +func generateTestChain() []*types.Block { db := rawdb.NewMemoryDatabase() - config := params.AllEthashProtocolChanges - genesis := &core.Genesis{ - Config: config, - Alloc: core.GenesisAlloc{testAddr: {Balance: testBalance}}, - ExtraData: []byte("test genesis"), - Timestamp: 9000, - BaseFee: big.NewInt(params.InitialBaseFee), - } generate := func(i int, g *core.BlockGen) { g.OffsetTime(5) g.SetExtra([]byte("test")) + if i == 1 { + // Test transactions are included in block #2. + g.AddTx(testTx1) + g.AddTx(testTx2) + } } gblock := genesis.ToBlock(db) engine := ethash.NewFaker() - blocks, _ := core.GenerateChain(config, gblock, engine, db, 1, generate) + blocks, _ := core.GenerateChain(genesis.Config, gblock, engine, db, 2, generate) blocks = append([]*types.Block{gblock}, blocks...) - return genesis, blocks + return blocks } func TestEthClient(t *testing.T) { @@ -242,30 +264,33 @@ func TestEthClient(t *testing.T) { tests := map[string]struct { test func(t *testing.T) }{ - "TestHeader": { + "Header": { func(t *testing.T) { testHeader(t, chain, client) }, }, - "TestBalanceAt": { + "BalanceAt": { func(t *testing.T) { testBalanceAt(t, client) }, }, - "TestTxInBlockInterrupted": { + "TxInBlockInterrupted": { func(t *testing.T) { testTransactionInBlockInterrupted(t, client) }, }, - "TestChainID": { + "ChainID": { func(t *testing.T) { testChainID(t, client) }, }, - "TestGetBlock": { + "GetBlock": { func(t *testing.T) { testGetBlock(t, client) }, }, - "TestStatusFunctions": { + "StatusFunctions": { func(t *testing.T) { testStatusFunctions(t, client) }, }, - "TestCallContract": { + "CallContract": { func(t *testing.T) { testCallContract(t, client) }, }, - "TestAtFunctions": { + "AtFunctions": { func(t *testing.T) { testAtFunctions(t, client) }, }, + "TransactionSender": { + func(t *testing.T) { testTransactionSender(t, client) }, + }, } t.Parallel() @@ -321,6 +346,11 @@ func testBalanceAt(t *testing.T, client *rpc.Client) { want *big.Int wantErr error }{ + "valid_account_genesis": { + account: testAddr, + block: big.NewInt(0), + want: testBalance, + }, "valid_account": { account: testAddr, block: big.NewInt(1), @@ -358,23 +388,25 @@ func testBalanceAt(t *testing.T, client *rpc.Client) { func testTransactionInBlockInterrupted(t *testing.T, client *rpc.Client) { ec := NewClient(client) - // Get current block by number + // Get current block by number. block, err := ec.BlockByNumber(context.Background(), nil) if err != nil { t.Fatalf("unexpected error: %v", err) } - // Test tx in block interupted + + // Test tx in block interupted. ctx, cancel := context.WithCancel(context.Background()) cancel() - tx, err := ec.TransactionInBlock(ctx, block.Hash(), 1) + tx, err := ec.TransactionInBlock(ctx, block.Hash(), 0) if tx != nil { t.Fatal("transaction should be nil") } if err == nil || err == ethereum.NotFound { t.Fatal("error should not be nil/notfound") } - // Test tx in block not found - if _, err := ec.TransactionInBlock(context.Background(), block.Hash(), 1); err != ethereum.NotFound { + + // Test tx in block not found. + if _, err := ec.TransactionInBlock(context.Background(), block.Hash(), 20); err != ethereum.NotFound { t.Fatal("error should be ethereum.NotFound") } } @@ -392,12 +424,13 @@ func testChainID(t *testing.T, client *rpc.Client) { func testGetBlock(t *testing.T, client *rpc.Client) { ec := NewClient(client) + // Get current block number blockNumber, err := ec.BlockNumber(context.Background()) if err != nil { t.Fatalf("unexpected error: %v", err) } - if blockNumber != 1 { + if blockNumber != 2 { t.Fatalf("BlockNumber returned wrong number: %d", blockNumber) } // Get current block by number @@ -445,6 +478,7 @@ func testStatusFunctions(t *testing.T, client *rpc.Client) { if progress != nil { t.Fatalf("unexpected progress: %v", progress) } + // NetworkID networkID, err := ec.NetworkID(context.Background()) if err != nil { @@ -453,20 +487,22 @@ func testStatusFunctions(t *testing.T, client *rpc.Client) { if networkID.Cmp(big.NewInt(0)) != 0 { t.Fatalf("unexpected networkID: %v", networkID) } - // SuggestGasPrice (should suggest 1 Gwei) + + // SuggestGasPrice gasPrice, err := ec.SuggestGasPrice(context.Background()) if err != nil { t.Fatalf("unexpected error: %v", err) } - if gasPrice.Cmp(big.NewInt(1875000000)) != 0 { // 1 gwei tip + 0.875 basefee after a 1 gwei fee empty block + if gasPrice.Cmp(big.NewInt(1000000000)) != 0 { t.Fatalf("unexpected gas price: %v", gasPrice) } - // SuggestGasTipCap (should suggest 1 Gwei) + + // SuggestGasTipCap gasTipCap, err := ec.SuggestGasTipCap(context.Background()) if err != nil { t.Fatalf("unexpected error: %v", err) } - if gasTipCap.Cmp(big.NewInt(1000000000)) != 0 { + if gasTipCap.Cmp(big.NewInt(234375000)) != 0 { t.Fatalf("unexpected gas tip cap: %v", gasTipCap) } } @@ -500,9 +536,11 @@ func testCallContract(t *testing.T, client *rpc.Client) { func testAtFunctions(t *testing.T, client *rpc.Client) { ec := NewClient(client) + // send a transaction for some interesting pending status sendTransaction(ec) time.Sleep(100 * time.Millisecond) + // Check pending transaction count pending, err := ec.PendingTransactionCount(context.Background()) if err != nil { @@ -561,23 +599,66 @@ func testAtFunctions(t *testing.T, client *rpc.Client) { } } +func testTransactionSender(t *testing.T, client *rpc.Client) { + ec := NewClient(client) + ctx := context.Background() + + // Retrieve testTx1 via RPC. + block2, err := ec.HeaderByNumber(ctx, big.NewInt(2)) + if err != nil { + t.Fatal("can't get block 1:", err) + } + tx1, err := ec.TransactionInBlock(ctx, block2.Hash(), 0) + if err != nil { + t.Fatal("can't get tx:", err) + } + if tx1.Hash() != testTx1.Hash() { + t.Fatalf("wrong tx hash %v, want %v", tx1.Hash(), testTx1.Hash()) + } + + // The sender address is cached in tx1, so no additional RPC should be required in + // TransactionSender. Ensure the server is not asked by canceling the context here. + canceledCtx, cancel := context.WithCancel(context.Background()) + cancel() + sender1, err := ec.TransactionSender(canceledCtx, tx1, block2.Hash(), 0) + if err != nil { + t.Fatal(err) + } + if sender1 != testAddr { + t.Fatal("wrong sender:", sender1) + } + + // Now try to get the sender of testTx2, which was not fetched through RPC. + // TransactionSender should query the server here. + sender2, err := ec.TransactionSender(ctx, testTx2, block2.Hash(), 1) + if err != nil { + t.Fatal(err) + } + if sender2 != testAddr { + t.Fatal("wrong sender:", sender2) + } +} + func sendTransaction(ec *Client) error { - // Retrieve chainID chainID, err := ec.ChainID(context.Background()) if err != nil { return err } - // Create transaction - tx := types.NewTransaction(0, common.Address{1}, big.NewInt(1), 22000, big.NewInt(params.InitialBaseFee), nil) + nonce, err := ec.PendingNonceAt(context.Background(), testAddr) + if err != nil { + return err + } + signer := types.LatestSignerForChainID(chainID) - signature, err := crypto.Sign(signer.Hash(tx).Bytes(), testKey) + tx, err := types.SignNewTx(testKey, signer, &types.LegacyTx{ + Nonce: nonce, + To: &common.Address{2}, + Value: big.NewInt(1), + Gas: 22000, + GasPrice: big.NewInt(params.InitialBaseFee), + }) if err != nil { return err } - signedTx, err := tx.WithSignature(signer, signature) - if err != nil { - return err - } - // Send transaction - return ec.SendTransaction(context.Background(), signedTx) + return ec.SendTransaction(context.Background(), tx) } diff --git a/ethclient/signer.go b/ethclient/signer.go index 9de020b35..f827d4eb5 100644 --- a/ethclient/signer.go +++ b/ethclient/signer.go @@ -45,7 +45,7 @@ func (s *senderFromServer) Equal(other types.Signer) bool { } func (s *senderFromServer) Sender(tx *types.Transaction) (common.Address, error) { - if s.blockhash == (common.Hash{}) { + if s.addr == (common.Address{}) { return common.Address{}, errNotCached } return s.addr, nil From ab31fbbde1fc2ec8de99cde86e5b66d991b8ef43 Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Thu, 18 Nov 2021 09:50:52 +0100 Subject: [PATCH 17/30] core/vm: don't use iota for opcode definitions --- core/vm/opcodes.go | 123 ++++++++++++++++++++++++--------------------- 1 file changed, 66 insertions(+), 57 deletions(-) diff --git a/core/vm/opcodes.go b/core/vm/opcodes.go index 286307ae9..a6c89d833 100644 --- a/core/vm/opcodes.go +++ b/core/vm/opcodes.go @@ -39,68 +39,68 @@ func (op OpCode) IsStaticJump() bool { // 0x0 range - arithmetic ops. const ( - STOP OpCode = iota - ADD - MUL - SUB - DIV - SDIV - MOD - SMOD - ADDMOD - MULMOD - EXP - SIGNEXTEND + STOP OpCode = 0x0 + ADD OpCode = 0x1 + MUL OpCode = 0x2 + SUB OpCode = 0x3 + DIV OpCode = 0x4 + SDIV OpCode = 0x5 + MOD OpCode = 0x6 + SMOD OpCode = 0x7 + ADDMOD OpCode = 0x8 + MULMOD OpCode = 0x9 + EXP OpCode = 0xa + SIGNEXTEND OpCode = 0xb ) // 0x10 range - comparison ops. const ( - LT OpCode = iota + 0x10 - GT - SLT - SGT - EQ - ISZERO - AND - OR - XOR - NOT - BYTE - SHL - SHR - SAR + LT OpCode = 0x10 + GT OpCode = 0x11 + SLT OpCode = 0x12 + SGT OpCode = 0x13 + EQ OpCode = 0x14 + ISZERO OpCode = 0x15 + AND OpCode = 0x16 + OR OpCode = 0x17 + XOR OpCode = 0x18 + NOT OpCode = 0x19 + BYTE OpCode = 0x1a + SHL OpCode = 0x1b + SHR OpCode = 0x1c + SAR OpCode = 0x1d SHA3 OpCode = 0x20 ) // 0x30 range - closure state. const ( - ADDRESS OpCode = 0x30 + iota - BALANCE - ORIGIN - CALLER - CALLVALUE - CALLDATALOAD - CALLDATASIZE - CALLDATACOPY - CODESIZE - CODECOPY - GASPRICE - EXTCODESIZE - EXTCODECOPY - RETURNDATASIZE - RETURNDATACOPY - EXTCODEHASH + ADDRESS OpCode = 0x30 + BALANCE OpCode = 0x31 + ORIGIN OpCode = 0x32 + CALLER OpCode = 0x33 + CALLVALUE OpCode = 0x34 + CALLDATALOAD OpCode = 0x35 + CALLDATASIZE OpCode = 0x36 + CALLDATACOPY OpCode = 0x37 + CODESIZE OpCode = 0x38 + CODECOPY OpCode = 0x39 + GASPRICE OpCode = 0x3a + EXTCODESIZE OpCode = 0x3b + EXTCODECOPY OpCode = 0x3c + RETURNDATASIZE OpCode = 0x3d + RETURNDATACOPY OpCode = 0x3e + EXTCODEHASH OpCode = 0x3f ) // 0x40 range - block operations. const ( - BLOCKHASH OpCode = 0x40 + iota - COINBASE - TIMESTAMP - NUMBER - DIFFICULTY - GASLIMIT + BLOCKHASH OpCode = 0x40 + COINBASE OpCode = 0x41 + TIMESTAMP OpCode = 0x42 + NUMBER OpCode = 0x43 + DIFFICULTY OpCode = 0x44 + GASLIMIT OpCode = 0x45 CHAINID OpCode = 0x46 SELFBALANCE OpCode = 0x47 BASEFEE OpCode = 0x48 @@ -122,7 +122,7 @@ const ( JUMPDEST OpCode = 0x5b ) -// 0x60 range. +// 0x60 range - pushes. const ( PUSH1 OpCode = 0x60 + iota PUSH2 @@ -156,7 +156,11 @@ const ( PUSH30 PUSH31 PUSH32 - DUP1 +) + +// 0x80 range - dups. +const ( + DUP1 = 0x80 + iota DUP2 DUP3 DUP4 @@ -172,7 +176,11 @@ const ( DUP14 DUP15 DUP16 - SWAP1 +) + +// 0x90 range - swaps. +const ( + SWAP1 = 0x90 + iota SWAP2 SWAP3 SWAP4 @@ -208,12 +216,13 @@ const ( // 0xf0 range - closures. const ( - CREATE OpCode = 0xf0 + iota - CALL - CALLCODE - RETURN - DELEGATECALL - CREATE2 + CREATE OpCode = 0xf0 + CALL OpCode = 0xf1 + CALLCODE OpCode = 0xf2 + RETURN OpCode = 0xf3 + DELEGATECALL OpCode = 0xf4 + CREATE2 OpCode = 0xf5 + STATICCALL OpCode = 0xfa REVERT OpCode = 0xfd SELFDESTRUCT OpCode = 0xff From c52def7f114aa48f50ed9956bc9661550300addb Mon Sep 17 00:00:00 2001 From: courtier Date: Thu, 18 Nov 2021 19:20:36 +0100 Subject: [PATCH 18/30] eth/gasprice: sanitize max header and block history (#23886) Fixes #23452 --- eth/gasprice/gasprice.go | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/eth/gasprice/gasprice.go b/eth/gasprice/gasprice.go index 8feb5ef24..00128a5dc 100644 --- a/eth/gasprice/gasprice.go +++ b/eth/gasprice/gasprice.go @@ -87,8 +87,7 @@ func NewOracle(backend OracleBackend, params Config) *Oracle { if percent < 0 { percent = 0 log.Warn("Sanitizing invalid gasprice oracle sample percentile", "provided", params.Percentile, "updated", percent) - } - if percent > 100 { + } else if percent > 100 { percent = 100 log.Warn("Sanitizing invalid gasprice oracle sample percentile", "provided", params.Percentile, "updated", percent) } @@ -104,6 +103,16 @@ func NewOracle(backend OracleBackend, params Config) *Oracle { } else if ignorePrice.Int64() > 0 { log.Info("Gasprice oracle is ignoring threshold set", "threshold", ignorePrice) } + maxHeaderHistory := params.MaxHeaderHistory + if maxHeaderHistory < 1 { + maxHeaderHistory = 1 + log.Warn("Sanitizing invalid gasprice oracle max header history", "provided", params.MaxHeaderHistory, "updated", maxHeaderHistory) + } + maxBlockHistory := params.MaxBlockHistory + if maxBlockHistory < 1 { + maxBlockHistory = 1 + log.Warn("Sanitizing invalid gasprice oracle max block history", "provided", params.MaxBlockHistory, "updated", maxBlockHistory) + } cache, _ := lru.New(2048) headEvent := make(chan core.ChainHeadEvent, 1) @@ -125,8 +134,8 @@ func NewOracle(backend OracleBackend, params Config) *Oracle { ignorePrice: ignorePrice, checkBlocks: blocks, percentile: percent, - maxHeaderHistory: params.MaxHeaderHistory, - maxBlockHistory: params.MaxBlockHistory, + maxHeaderHistory: maxHeaderHistory, + maxBlockHistory: maxBlockHistory, historyCache: cache, } } From e761255ba7e19ec7c380f008f9035da3caa77994 Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Fri, 19 Nov 2021 10:53:20 +0100 Subject: [PATCH 19/30] cmd/evm: make t9n intrinsicGas output hex, fixes #23883 (#23889) --- cmd/evm/internal/t8ntool/transaction.go | 4 ++-- cmd/evm/testdata/15/exp2.json | 4 ++-- cmd/evm/testdata/16/exp.json | 4 ++-- cmd/evm/testdata/17/exp.json | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/cmd/evm/internal/t8ntool/transaction.go b/cmd/evm/internal/t8ntool/transaction.go index 576d1db84..e4fe24108 100644 --- a/cmd/evm/internal/t8ntool/transaction.go +++ b/cmd/evm/internal/t8ntool/transaction.go @@ -48,7 +48,7 @@ func (r *result) MarshalJSON() ([]byte, error) { Error string `json:"error,omitempty"` Address *common.Address `json:"address,omitempty"` Hash *common.Hash `json:"hash,omitempty"` - IntrinsicGas uint64 `json:"intrinsicGas,omitempty"` + IntrinsicGas hexutil.Uint64 `json:"intrinsicGas,omitempty"` } var out xx if r.Error != nil { @@ -60,7 +60,7 @@ func (r *result) MarshalJSON() ([]byte, error) { if r.Hash != (common.Hash{}) { out.Hash = &r.Hash } - out.IntrinsicGas = r.IntrinsicGas + out.IntrinsicGas = hexutil.Uint64(r.IntrinsicGas) return json.Marshal(out) } diff --git a/cmd/evm/testdata/15/exp2.json b/cmd/evm/testdata/15/exp2.json index 2c49326ce..dd5e8a358 100644 --- a/cmd/evm/testdata/15/exp2.json +++ b/cmd/evm/testdata/15/exp2.json @@ -2,11 +2,11 @@ { "address": "0xd02d72e067e77158444ef2020ff2d325f929b363", "hash": "0xa98a24882ea90916c6a86da650fbc6b14238e46f0af04a131ce92be897507476", - "intrinsicGas": 21000 + "intrinsicGas": "0x5208" }, { "address": "0xd02d72e067e77158444ef2020ff2d325f929b363", "hash": "0x36bad80acce7040c45fd32764b5c2b2d2e6f778669fb41791f73f546d56e739a", - "intrinsicGas": 21000 + "intrinsicGas": "0x5208" } ] diff --git a/cmd/evm/testdata/16/exp.json b/cmd/evm/testdata/16/exp.json index 075c977f2..137ade651 100644 --- a/cmd/evm/testdata/16/exp.json +++ b/cmd/evm/testdata/16/exp.json @@ -2,12 +2,12 @@ { "address": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b", "hash": "0x7cc3d1a8540a44736750f03bb4d85c0113be4b3472a71bf82241a3b261b479e6", - "intrinsicGas": 21000 + "intrinsicGas": "0x5208" }, { "error": "intrinsic gas too low: have 82, want 21000", "address": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b", "hash": "0x3b2d2609e4361562edb9169314f4c05afc6dbf5d706bf9dda5abe242ab76a22b", - "intrinsicGas": 21000 + "intrinsicGas": "0x5208" } ] \ No newline at end of file diff --git a/cmd/evm/testdata/17/exp.json b/cmd/evm/testdata/17/exp.json index 1c6c54723..485906041 100644 --- a/cmd/evm/testdata/17/exp.json +++ b/cmd/evm/testdata/17/exp.json @@ -3,13 +3,13 @@ "error": "value exceeds 256 bits", "address": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b", "hash": "0xfbd91685dcbf8172f0e8c53e2ddbb4d26707840da6b51a74371f62a33868fd82", - "intrinsicGas": 21000 + "intrinsicGas": "0x5208" }, { "error": "gasPrice exceeds 256 bits", "address": "0x1b57ccef1fe5fb73f1e64530fb4ebd9cf1655964", "hash": "0x45dc05035cada83748e4c1fe617220106b331eca054f44c2304d5654a9fb29d5", - "intrinsicGas": 21000 + "intrinsicGas": "0x5208" }, { "error": "invalid transaction v, r, s values", From e0761432a43f3c991e870b12b1f49a785202a881 Mon Sep 17 00:00:00 2001 From: Anatole <62328077+a2br@users.noreply.github.com> Date: Mon, 22 Nov 2021 02:53:16 +0100 Subject: [PATCH 20/30] eth: fix typo in comment (#23941) --- eth/api.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eth/api.go b/eth/api.go index 3ec72c58e..f81dfa922 100644 --- a/eth/api.go +++ b/eth/api.go @@ -170,7 +170,7 @@ func (api *PrivateAdminAPI) ExportChain(file string, first *uint64, last *uint64 last = &head } if _, err := os.Stat(file); err == nil { - // File already exists. Allowing overwrite could be a DoS vecotor, + // File already exists. Allowing overwrite could be a DoS vector, // since the 'file' may point to arbitrary paths on the drive return false, errors.New("location would overwrite an existing file") } From 6f2c3f2114f6ce32b4a2d3791136973c12aa482e Mon Sep 17 00:00:00 2001 From: lightclient <14004106+lightclient@users.noreply.github.com> Date: Mon, 22 Nov 2021 00:07:17 -0700 Subject: [PATCH 21/30] cmd/geth: add ancient flag to db inspect (#23946) --- cmd/geth/dbcmd.go | 1 + 1 file changed, 1 insertion(+) diff --git a/cmd/geth/dbcmd.go b/cmd/geth/dbcmd.go index e1e0d77f0..c2c42276b 100644 --- a/cmd/geth/dbcmd.go +++ b/cmd/geth/dbcmd.go @@ -77,6 +77,7 @@ Remove blockchain and state databases`, ArgsUsage: " ", Flags: []cli.Flag{ utils.DataDirFlag, + utils.AncientFlag, utils.SyncModeFlag, utils.MainnetFlag, utils.RopstenFlag, From 5d4bcbc14f142bdb84479a2c4739d2a1091e5235 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Mon, 22 Nov 2021 08:49:18 +0100 Subject: [PATCH 22/30] trie: more tests for stacktrie (#23936) --- trie/stacktrie_test.go | 160 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 160 insertions(+) diff --git a/trie/stacktrie_test.go b/trie/stacktrie_test.go index bd2574d5d..fb39e4252 100644 --- a/trie/stacktrie_test.go +++ b/trie/stacktrie_test.go @@ -26,6 +26,166 @@ import ( "github.com/ethereum/go-ethereum/ethdb/memorydb" ) +func TestStackTrieInsertAndHash(t *testing.T) { + type KeyValueHash struct { + K string // Hex string for key. + V string // Value, directly converted to bytes. + H string // Expected root hash after insert of (K, V) to an existing trie. + } + tests := [][]KeyValueHash{ + { // {0:0, 7:0, f:0} + {"00", "v_______________________0___0", "5cb26357b95bb9af08475be00243ceb68ade0b66b5cd816b0c18a18c612d2d21"}, + {"70", "v_______________________0___1", "8ff64309574f7a437a7ad1628e690eb7663cfde10676f8a904a8c8291dbc1603"}, + {"f0", "v_______________________0___2", "9e3a01bd8d43efb8e9d4b5506648150b8e3ed1caea596f84ee28e01a72635470"}, + }, + { // {1:0cc, e:{1:fc, e:fc}} + {"10cc", "v_______________________1___0", "233e9b257843f3dfdb1cce6676cdaf9e595ac96ee1b55031434d852bc7ac9185"}, + {"e1fc", "v_______________________1___1", "39c5e908ae83d0c78520c7c7bda0b3782daf594700e44546e93def8f049cca95"}, + {"eefc", "v_______________________1___2", "d789567559fd76fe5b7d9cc42f3750f942502ac1c7f2a466e2f690ec4b6c2a7c"}, + }, + { // {b:{a:ac, b:ac}, d:acc} + {"baac", "v_______________________2___0", "8be1c86ba7ec4c61e14c1a9b75055e0464c2633ae66a055a24e75450156a5d42"}, + {"bbac", "v_______________________2___1", "8495159b9895a7d88d973171d737c0aace6fe6ac02a4769fff1bc43bcccce4cc"}, + {"dacc", "v_______________________2___2", "9bcfc5b220a27328deb9dc6ee2e3d46c9ebc9c69e78acda1fa2c7040602c63ca"}, + }, + { // {0:0cccc, 2:456{0:0, 2:2} + {"00cccc", "v_______________________3___0", "e57dc2785b99ce9205080cb41b32ebea7ac3e158952b44c87d186e6d190a6530"}, + {"245600", "v_______________________3___1", "0335354adbd360a45c1871a842452287721b64b4234dfe08760b243523c998db"}, + {"245622", "v_______________________3___2", "9e6832db0dca2b5cf81c0e0727bfde6afc39d5de33e5720bccacc183c162104e"}, + }, + { // {1:4567{1:1c, 3:3c}, 3:0cccccc} + {"1456711c", "v_______________________4___0", "f2389e78d98fed99f3e63d6d1623c1d4d9e8c91cb1d585de81fbc7c0e60d3529"}, + {"1456733c", "v_______________________4___1", "101189b3fab852be97a0120c03d95eefcf984d3ed639f2328527de6def55a9c0"}, + {"30cccccc", "v_______________________4___2", "3780ce111f98d15751dfde1eb21080efc7d3914b429e5c84c64db637c55405b3"}, + }, + { // 8800{1:f, 2:e, 3:d} + {"88001f", "v_______________________5___0", "e817db50d84f341d443c6f6593cafda093fc85e773a762421d47daa6ac993bd5"}, + {"88002e", "v_______________________5___1", "d6e3e6047bdc110edd296a4d63c030aec451bee9d8075bc5a198eee8cda34f68"}, + {"88003d", "v_______________________5___2", "b6bdf8298c703342188e5f7f84921a402042d0e5fb059969dd53a6b6b1fb989e"}, + }, + { // 0{1:fc, 2:ec, 4:dc} + {"01fc", "v_______________________6___0", "693268f2ca80d32b015f61cd2c4dba5a47a6b52a14c34f8e6945fad684e7a0d5"}, + {"02ec", "v_______________________6___1", "e24ddd44469310c2b785a2044618874bf486d2f7822603a9b8dce58d6524d5de"}, + {"04dc", "v_______________________6___2", "33fc259629187bbe54b92f82f0cd8083b91a12e41a9456b84fc155321e334db7"}, + }, + { // f{0:fccc, f:ff{0:f, f:f}} + {"f0fccc", "v_______________________7___0", "b0966b5aa469a3e292bc5fcfa6c396ae7a657255eef552ea7e12f996de795b90"}, + {"ffff0f", "v_______________________7___1", "3b1ca154ec2a3d96d8d77bddef0abfe40a53a64eb03cecf78da9ec43799fa3d0"}, + {"ffffff", "v_______________________7___2", "e75463041f1be8252781be0ace579a44ea4387bf5b2739f4607af676f7719678"}, + }, + { // ff{0:f{0:f, f:f}, f:fcc} + {"ff0f0f", "v_______________________8___0", "0928af9b14718ec8262ab89df430f1e5fbf66fac0fed037aff2b6767ae8c8684"}, + {"ff0fff", "v_______________________8___1", "d870f4d3ce26b0bf86912810a1960693630c20a48ba56be0ad04bc3e9ddb01e6"}, + {"ffffcc", "v_______________________8___2", "4239f10dd9d9915ecf2e047d6a576bdc1733ed77a30830f1bf29deaf7d8e966f"}, + }, + { + {"123d", "x___________________________0", "fc453d88b6f128a77c448669710497380fa4588abbea9f78f4c20c80daa797d0"}, + {"123e", "x___________________________1", "5af48f2d8a9a015c1ff7fa8b8c7f6b676233bd320e8fb57fd7933622badd2cec"}, + {"123f", "x___________________________2", "1164d7299964e74ac40d761f9189b2a3987fae959800d0f7e29d3aaf3eae9e15"}, + }, + { + {"123d", "x___________________________0", "fc453d88b6f128a77c448669710497380fa4588abbea9f78f4c20c80daa797d0"}, + {"123e", "x___________________________1", "5af48f2d8a9a015c1ff7fa8b8c7f6b676233bd320e8fb57fd7933622badd2cec"}, + {"124a", "x___________________________2", "661a96a669869d76b7231380da0649d013301425fbea9d5c5fae6405aa31cfce"}, + }, + { + {"123d", "x___________________________0", "fc453d88b6f128a77c448669710497380fa4588abbea9f78f4c20c80daa797d0"}, + {"123e", "x___________________________1", "5af48f2d8a9a015c1ff7fa8b8c7f6b676233bd320e8fb57fd7933622badd2cec"}, + {"13aa", "x___________________________2", "6590120e1fd3ffd1a90e8de5bb10750b61079bb0776cca4414dd79a24e4d4356"}, + }, + { + {"123d", "x___________________________0", "fc453d88b6f128a77c448669710497380fa4588abbea9f78f4c20c80daa797d0"}, + {"123e", "x___________________________1", "5af48f2d8a9a015c1ff7fa8b8c7f6b676233bd320e8fb57fd7933622badd2cec"}, + {"2aaa", "x___________________________2", "f869b40e0c55eace1918332ef91563616fbf0755e2b946119679f7ef8e44b514"}, + }, + { + {"1234da", "x___________________________0", "1c4b4462e9f56a80ca0f5d77c0d632c41b0102290930343cf1791e971a045a79"}, + {"1234ea", "x___________________________1", "2f502917f3ba7d328c21c8b45ee0f160652e68450332c166d4ad02d1afe31862"}, + {"1234fa", "x___________________________2", "4f4e368ab367090d5bc3dbf25f7729f8bd60df84de309b4633a6b69ab66142c0"}, + }, + { + {"1234da", "x___________________________0", "1c4b4462e9f56a80ca0f5d77c0d632c41b0102290930343cf1791e971a045a79"}, + {"1234ea", "x___________________________1", "2f502917f3ba7d328c21c8b45ee0f160652e68450332c166d4ad02d1afe31862"}, + {"1235aa", "x___________________________2", "21840121d11a91ac8bbad9a5d06af902a5c8d56a47b85600ba813814b7bfcb9b"}, + }, + { + {"1234da", "x___________________________0", "1c4b4462e9f56a80ca0f5d77c0d632c41b0102290930343cf1791e971a045a79"}, + {"1234ea", "x___________________________1", "2f502917f3ba7d328c21c8b45ee0f160652e68450332c166d4ad02d1afe31862"}, + {"124aaa", "x___________________________2", "ea4040ddf6ae3fbd1524bdec19c0ab1581015996262006632027fa5cf21e441e"}, + }, + { + {"1234da", "x___________________________0", "1c4b4462e9f56a80ca0f5d77c0d632c41b0102290930343cf1791e971a045a79"}, + {"1234ea", "x___________________________1", "2f502917f3ba7d328c21c8b45ee0f160652e68450332c166d4ad02d1afe31862"}, + {"13aaaa", "x___________________________2", "e4beb66c67e44f2dd8ba36036e45a44ff68f8d52942472b1911a45f886a34507"}, + }, + { + {"1234da", "x___________________________0", "1c4b4462e9f56a80ca0f5d77c0d632c41b0102290930343cf1791e971a045a79"}, + {"1234ea", "x___________________________1", "2f502917f3ba7d328c21c8b45ee0f160652e68450332c166d4ad02d1afe31862"}, + {"2aaaaa", "x___________________________2", "5f5989b820ff5d76b7d49e77bb64f26602294f6c42a1a3becc669cd9e0dc8ec9"}, + }, + { + {"000000", "x___________________________0", "3b32b7af0bddc7940e7364ee18b5a59702c1825e469452c8483b9c4e0218b55a"}, + {"1234da", "x___________________________1", "3ab152a1285dca31945566f872c1cc2f17a770440eda32aeee46a5e91033dde2"}, + {"1234ea", "x___________________________2", "0cccc87f96ddef55563c1b3be3c64fff6a644333c3d9cd99852cb53b6412b9b8"}, + {"1234fa", "x___________________________3", "65bb3aafea8121111d693ffe34881c14d27b128fd113fa120961f251fe28428d"}, + }, + { + {"000000", "x___________________________0", "3b32b7af0bddc7940e7364ee18b5a59702c1825e469452c8483b9c4e0218b55a"}, + {"1234da", "x___________________________1", "3ab152a1285dca31945566f872c1cc2f17a770440eda32aeee46a5e91033dde2"}, + {"1234ea", "x___________________________2", "0cccc87f96ddef55563c1b3be3c64fff6a644333c3d9cd99852cb53b6412b9b8"}, + {"1235aa", "x___________________________3", "f670e4d2547c533c5f21e0045442e2ecb733f347ad6d29ef36e0f5ba31bb11a8"}, + }, + { + {"000000", "x___________________________0", "3b32b7af0bddc7940e7364ee18b5a59702c1825e469452c8483b9c4e0218b55a"}, + {"1234da", "x___________________________1", "3ab152a1285dca31945566f872c1cc2f17a770440eda32aeee46a5e91033dde2"}, + {"1234ea", "x___________________________2", "0cccc87f96ddef55563c1b3be3c64fff6a644333c3d9cd99852cb53b6412b9b8"}, + {"124aaa", "x___________________________3", "c17464123050a9a6f29b5574bb2f92f6d305c1794976b475b7fb0316b6335598"}, + }, + { + {"000000", "x___________________________0", "3b32b7af0bddc7940e7364ee18b5a59702c1825e469452c8483b9c4e0218b55a"}, + {"1234da", "x___________________________1", "3ab152a1285dca31945566f872c1cc2f17a770440eda32aeee46a5e91033dde2"}, + {"1234ea", "x___________________________2", "0cccc87f96ddef55563c1b3be3c64fff6a644333c3d9cd99852cb53b6412b9b8"}, + {"13aaaa", "x___________________________3", "aa8301be8cb52ea5cd249f5feb79fb4315ee8de2140c604033f4b3fff78f0105"}, + }, + { + {"0000", "x___________________________0", "cb8c09ad07ae882136f602b3f21f8733a9f5a78f1d2525a8d24d1c13258000b2"}, + {"123d", "x___________________________1", "8f09663deb02f08958136410dc48565e077f76bb6c9d8c84d35fc8913a657d31"}, + {"123e", "x___________________________2", "0d230561e398c579e09a9f7b69ceaf7d3970f5a436fdb28b68b7a37c5bdd6b80"}, + {"123f", "x___________________________3", "80f7bad1893ca57e3443bb3305a517723a74d3ba831bcaca22a170645eb7aafb"}, + }, + { + {"0000", "x___________________________0", "cb8c09ad07ae882136f602b3f21f8733a9f5a78f1d2525a8d24d1c13258000b2"}, + {"123d", "x___________________________1", "8f09663deb02f08958136410dc48565e077f76bb6c9d8c84d35fc8913a657d31"}, + {"123e", "x___________________________2", "0d230561e398c579e09a9f7b69ceaf7d3970f5a436fdb28b68b7a37c5bdd6b80"}, + {"124a", "x___________________________3", "383bc1bb4f019e6bc4da3751509ea709b58dd1ac46081670834bae072f3e9557"}, + }, + { + {"0000", "x___________________________0", "cb8c09ad07ae882136f602b3f21f8733a9f5a78f1d2525a8d24d1c13258000b2"}, + {"123d", "x___________________________1", "8f09663deb02f08958136410dc48565e077f76bb6c9d8c84d35fc8913a657d31"}, + {"123e", "x___________________________2", "0d230561e398c579e09a9f7b69ceaf7d3970f5a436fdb28b68b7a37c5bdd6b80"}, + {"13aa", "x___________________________3", "ff0dc70ce2e5db90ee42a4c2ad12139596b890e90eb4e16526ab38fa465b35cf"}, + }, + } + st := NewStackTrie(nil) + for i, test := range tests { + // The StackTrie does not allow Insert(), Hash(), Insert(), ... + // so we will create new trie for every sequence length of inserts. + for l := 1; l <= len(test); l++ { + st.Reset() + for j := 0; j < l; j++ { + kv := &test[j] + if err := st.TryUpdate(common.FromHex(kv.K), []byte(kv.V)); err != nil { + t.Fatal(err) + } + } + expected := common.HexToHash(test[l-1].H) + if h := st.Hash(); h != expected { + t.Errorf("%d(%d): root hash mismatch: %x, expected %x", i, l, h, expected) + } + } + } +} + func TestSizeBug(t *testing.T) { st := NewStackTrie(nil) nt, _ := New(common.Hash{}, NewDatabase(memorydb.New())) From 441c7f2b0fb264981483850e9d155f7b0e908439 Mon Sep 17 00:00:00 2001 From: lightclient <14004106+lightclient@users.noreply.github.com> Date: Mon, 22 Nov 2021 01:25:35 -0700 Subject: [PATCH 23/30] cmd/evm: add b11r tool (#23843) evm block-builder (a.k.a b11r) is a utility to help assemble blocks, for use during the test-creation process. --- cmd/evm/internal/t8ntool/block.go | 380 ++++++++++++++++++++++++ cmd/evm/internal/t8ntool/flags.go | 41 ++- cmd/evm/internal/t8ntool/gen_header.go | 135 +++++++++ cmd/evm/internal/t8ntool/transaction.go | 2 +- cmd/evm/internal/t8ntool/transition.go | 50 +--- cmd/evm/internal/t8ntool/utils.go | 54 ++++ cmd/evm/main.go | 20 ++ cmd/evm/t8n_test.go | 124 +++++++- cmd/evm/testdata/20/exp.json | 4 + cmd/evm/testdata/20/header.json | 14 + cmd/evm/testdata/20/ommers.json | 1 + cmd/evm/testdata/20/readme.md | 11 + cmd/evm/testdata/20/txs.rlp | 1 + cmd/evm/testdata/21/clique.json | 6 + cmd/evm/testdata/21/exp-clique.json | 4 + cmd/evm/testdata/21/exp.json | 4 + cmd/evm/testdata/21/header.json | 11 + cmd/evm/testdata/21/ommers.json | 1 + cmd/evm/testdata/21/readme.md | 23 ++ cmd/evm/testdata/21/txs.rlp | 1 + cmd/evm/testdata/22/exp-clique.json | 4 + cmd/evm/testdata/22/exp.json | 4 + cmd/evm/testdata/22/header.json | 11 + cmd/evm/testdata/22/ommers.json | 1 + cmd/evm/testdata/22/readme.md | 11 + cmd/evm/testdata/22/txs.rlp | 1 + 26 files changed, 881 insertions(+), 38 deletions(-) create mode 100644 cmd/evm/internal/t8ntool/block.go create mode 100644 cmd/evm/internal/t8ntool/gen_header.go create mode 100644 cmd/evm/internal/t8ntool/utils.go create mode 100644 cmd/evm/testdata/20/exp.json create mode 100644 cmd/evm/testdata/20/header.json create mode 100644 cmd/evm/testdata/20/ommers.json create mode 100644 cmd/evm/testdata/20/readme.md create mode 100644 cmd/evm/testdata/20/txs.rlp create mode 100644 cmd/evm/testdata/21/clique.json create mode 100644 cmd/evm/testdata/21/exp-clique.json create mode 100644 cmd/evm/testdata/21/exp.json create mode 100644 cmd/evm/testdata/21/header.json create mode 100644 cmd/evm/testdata/21/ommers.json create mode 100644 cmd/evm/testdata/21/readme.md create mode 100644 cmd/evm/testdata/21/txs.rlp create mode 100644 cmd/evm/testdata/22/exp-clique.json create mode 100644 cmd/evm/testdata/22/exp.json create mode 100644 cmd/evm/testdata/22/header.json create mode 100644 cmd/evm/testdata/22/ommers.json create mode 100644 cmd/evm/testdata/22/readme.md create mode 100644 cmd/evm/testdata/22/txs.rlp diff --git a/cmd/evm/internal/t8ntool/block.go b/cmd/evm/internal/t8ntool/block.go new file mode 100644 index 000000000..d4edd33bd --- /dev/null +++ b/cmd/evm/internal/t8ntool/block.go @@ -0,0 +1,380 @@ +// Copyright 2021 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see . + +package t8ntool + +import ( + "crypto/ecdsa" + "encoding/json" + "errors" + "fmt" + "math/big" + "os" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/common/math" + "github.com/ethereum/go-ethereum/consensus/clique" + "github.com/ethereum/go-ethereum/consensus/ethash" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/rlp" + "gopkg.in/urfave/cli.v1" +) + +//go:generate gencodec -type header -field-override headerMarshaling -out gen_header.go +type header struct { + ParentHash common.Hash `json:"parentHash"` + OmmerHash *common.Hash `json:"sha3Uncles"` + Coinbase *common.Address `json:"miner"` + Root common.Hash `json:"stateRoot" gencodec:"required"` + TxHash *common.Hash `json:"transactionsRoot"` + ReceiptHash *common.Hash `json:"receiptsRoot"` + Bloom types.Bloom `json:"logsBloom"` + Difficulty *big.Int `json:"difficulty"` + Number *big.Int `json:"number" gencodec:"required"` + GasLimit uint64 `json:"gasLimit" gencodec:"required"` + GasUsed uint64 `json:"gasUsed"` + Time uint64 `json:"timestamp" gencodec:"required"` + Extra []byte `json:"extraData"` + MixDigest common.Hash `json:"mixHash"` + Nonce *types.BlockNonce `json:"nonce"` + BaseFee *big.Int `json:"baseFeePerGas" rlp:"optional"` +} + +type headerMarshaling struct { + Difficulty *math.HexOrDecimal256 + Number *math.HexOrDecimal256 + GasLimit math.HexOrDecimal64 + GasUsed math.HexOrDecimal64 + Time math.HexOrDecimal64 + Extra hexutil.Bytes + BaseFee *math.HexOrDecimal256 +} + +type bbInput struct { + Header *header `json:"header,omitempty"` + OmmersRlp []string `json:"ommers,omitempty"` + TxRlp string `json:"txs,omitempty"` + Clique *cliqueInput `json:"clique,omitempty"` + + Ethash bool `json:"-"` + EthashDir string `json:"-"` + PowMode ethash.Mode `json:"-"` + Txs []*types.Transaction `json:"-"` + Ommers []*types.Header `json:"-"` +} + +type cliqueInput struct { + Key *ecdsa.PrivateKey + Voted *common.Address + Authorize *bool + Vanity common.Hash +} + +// UnmarshalJSON implements json.Unmarshaler interface. +func (c *cliqueInput) UnmarshalJSON(input []byte) error { + var x struct { + Key *common.Hash `json:"secretKey"` + Voted *common.Address `json:"voted"` + Authorize *bool `json:"authorize"` + Vanity common.Hash `json:"vanity"` + } + if err := json.Unmarshal(input, &x); err != nil { + return err + } + if x.Key == nil { + return errors.New("missing required field 'secretKey' for cliqueInput") + } + if ecdsaKey, err := crypto.ToECDSA(x.Key[:]); err != nil { + return err + } else { + c.Key = ecdsaKey + } + c.Voted = x.Voted + c.Authorize = x.Authorize + c.Vanity = x.Vanity + return nil +} + +// ToBlock converts i into a *types.Block +func (i *bbInput) ToBlock() *types.Block { + header := &types.Header{ + ParentHash: i.Header.ParentHash, + UncleHash: types.EmptyUncleHash, + Coinbase: common.Address{}, + Root: i.Header.Root, + TxHash: types.EmptyRootHash, + ReceiptHash: types.EmptyRootHash, + Bloom: i.Header.Bloom, + Difficulty: common.Big0, + Number: i.Header.Number, + GasLimit: i.Header.GasLimit, + GasUsed: i.Header.GasUsed, + Time: i.Header.Time, + Extra: i.Header.Extra, + MixDigest: i.Header.MixDigest, + BaseFee: i.Header.BaseFee, + } + + // Fill optional values. + if i.Header.OmmerHash != nil { + header.UncleHash = *i.Header.OmmerHash + } else if len(i.Ommers) != 0 { + // Calculate the ommer hash if none is provided and there are ommers to hash + header.UncleHash = types.CalcUncleHash(i.Ommers) + } + if i.Header.Coinbase != nil { + header.Coinbase = *i.Header.Coinbase + } + if i.Header.TxHash != nil { + header.TxHash = *i.Header.TxHash + } + if i.Header.ReceiptHash != nil { + header.ReceiptHash = *i.Header.ReceiptHash + } + if i.Header.Nonce != nil { + header.Nonce = *i.Header.Nonce + } + if header.Difficulty != nil { + header.Difficulty = i.Header.Difficulty + } + return types.NewBlockWithHeader(header).WithBody(i.Txs, i.Ommers) +} + +// SealBlock seals the given block using the configured engine. +func (i *bbInput) SealBlock(block *types.Block) (*types.Block, error) { + switch { + case i.Ethash: + return i.sealEthash(block) + case i.Clique != nil: + return i.sealClique(block) + default: + return block, nil + } +} + +// sealEthash seals the given block using ethash. +func (i *bbInput) sealEthash(block *types.Block) (*types.Block, error) { + if i.Header.Nonce != nil { + return nil, NewError(ErrorConfig, fmt.Errorf("sealing with ethash will overwrite provided nonce")) + } + ethashConfig := ethash.Config{ + PowMode: i.PowMode, + DatasetDir: i.EthashDir, + CacheDir: i.EthashDir, + DatasetsInMem: 1, + DatasetsOnDisk: 2, + CachesInMem: 2, + CachesOnDisk: 3, + } + engine := ethash.New(ethashConfig, nil, true) + defer engine.Close() + // Use a buffered chan for results. + // If the testmode is used, the sealer will return quickly, and complain + // "Sealing result is not read by miner" if it cannot write the result. + results := make(chan *types.Block, 1) + if err := engine.Seal(nil, block, results, nil); err != nil { + panic(fmt.Sprintf("failed to seal block: %v", err)) + } + found := <-results + return block.WithSeal(found.Header()), nil +} + +// sealClique seals the given block using clique. +func (i *bbInput) sealClique(block *types.Block) (*types.Block, error) { + // If any clique value overwrites an explicit header value, fail + // to avoid silently building a block with unexpected values. + if i.Header.Extra != nil { + return nil, NewError(ErrorConfig, fmt.Errorf("sealing with clique will overwrite provided extra data")) + } + header := block.Header() + if i.Clique.Voted != nil { + if i.Header.Coinbase != nil { + return nil, NewError(ErrorConfig, fmt.Errorf("sealing with clique and voting will overwrite provided coinbase")) + } + header.Coinbase = *i.Clique.Voted + } + if i.Clique.Authorize != nil { + if i.Header.Nonce != nil { + return nil, NewError(ErrorConfig, fmt.Errorf("sealing with clique and voting will overwrite provided nonce")) + } + if *i.Clique.Authorize { + header.Nonce = [8]byte{} + } else { + header.Nonce = [8]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff} + } + } + // Extra is fixed 32 byte vanity and 65 byte signature + header.Extra = make([]byte, 32+65) + copy(header.Extra[0:32], i.Clique.Vanity.Bytes()[:]) + + // Sign the seal hash and fill in the rest of the extra data + h := clique.SealHash(header) + sighash, err := crypto.Sign(h[:], i.Clique.Key) + if err != nil { + return nil, err + } + copy(header.Extra[32:], sighash) + block = block.WithSeal(header) + return block, nil +} + +// BuildBlock constructs a block from the given inputs. +func BuildBlock(ctx *cli.Context) error { + // Configure the go-ethereum logger + glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false))) + glogger.Verbosity(log.Lvl(ctx.Int(VerbosityFlag.Name))) + log.Root().SetHandler(glogger) + + baseDir, err := createBasedir(ctx) + if err != nil { + return NewError(ErrorIO, fmt.Errorf("failed creating output basedir: %v", err)) + } + inputData, err := readInput(ctx) + if err != nil { + return err + } + block := inputData.ToBlock() + block, err = inputData.SealBlock(block) + if err != nil { + return err + } + return dispatchBlock(ctx, baseDir, block) +} + +func readInput(ctx *cli.Context) (*bbInput, error) { + var ( + headerStr = ctx.String(InputHeaderFlag.Name) + ommersStr = ctx.String(InputOmmersFlag.Name) + txsStr = ctx.String(InputTxsRlpFlag.Name) + cliqueStr = ctx.String(SealCliqueFlag.Name) + ethashOn = ctx.Bool(SealEthashFlag.Name) + ethashDir = ctx.String(SealEthashDirFlag.Name) + ethashMode = ctx.String(SealEthashModeFlag.Name) + inputData = &bbInput{} + ) + if ethashOn && cliqueStr != "" { + return nil, NewError(ErrorConfig, fmt.Errorf("both ethash and clique sealing specified, only one may be chosen")) + } + if ethashOn { + inputData.Ethash = ethashOn + inputData.EthashDir = ethashDir + switch ethashMode { + case "normal": + inputData.PowMode = ethash.ModeNormal + case "test": + inputData.PowMode = ethash.ModeTest + case "fake": + inputData.PowMode = ethash.ModeFake + default: + return nil, NewError(ErrorConfig, fmt.Errorf("unknown pow mode: %s, supported modes: test, fake, normal", ethashMode)) + } + } + if headerStr == stdinSelector || ommersStr == stdinSelector || txsStr == stdinSelector || cliqueStr == stdinSelector { + decoder := json.NewDecoder(os.Stdin) + if err := decoder.Decode(inputData); err != nil { + return nil, NewError(ErrorJson, fmt.Errorf("failed unmarshaling stdin: %v", err)) + } + } + if cliqueStr != stdinSelector && cliqueStr != "" { + var clique cliqueInput + if err := readFile(cliqueStr, "clique", &clique); err != nil { + return nil, err + } + inputData.Clique = &clique + } + if headerStr != stdinSelector { + var env header + if err := readFile(headerStr, "header", &env); err != nil { + return nil, err + } + inputData.Header = &env + } + if ommersStr != stdinSelector && ommersStr != "" { + var ommers []string + if err := readFile(ommersStr, "ommers", &ommers); err != nil { + return nil, err + } + inputData.OmmersRlp = ommers + } + if txsStr != stdinSelector { + var txs string + if err := readFile(txsStr, "txs", &txs); err != nil { + return nil, err + } + inputData.TxRlp = txs + } + // Deserialize rlp txs and ommers + var ( + ommers = []*types.Header{} + txs = []*types.Transaction{} + ) + if inputData.TxRlp != "" { + if err := rlp.DecodeBytes(common.FromHex(inputData.TxRlp), &txs); err != nil { + return nil, NewError(ErrorRlp, fmt.Errorf("unable to decode transaction from rlp data: %v", err)) + } + inputData.Txs = txs + } + for _, str := range inputData.OmmersRlp { + type extblock struct { + Header *types.Header + Txs []*types.Transaction + Ommers []*types.Header + } + var ommer *extblock + if err := rlp.DecodeBytes(common.FromHex(str), &ommer); err != nil { + return nil, NewError(ErrorRlp, fmt.Errorf("unable to decode ommer from rlp data: %v", err)) + } + ommers = append(ommers, ommer.Header) + } + inputData.Ommers = ommers + + return inputData, nil +} + +// dispatchOutput writes the output data to either stderr or stdout, or to the specified +// files +func dispatchBlock(ctx *cli.Context, baseDir string, block *types.Block) error { + raw, _ := rlp.EncodeToBytes(block) + + type blockInfo struct { + Rlp hexutil.Bytes `json:"rlp"` + Hash common.Hash `json:"hash"` + } + var enc blockInfo + enc.Rlp = raw + enc.Hash = block.Hash() + + b, err := json.MarshalIndent(enc, "", " ") + if err != nil { + return NewError(ErrorJson, fmt.Errorf("failed marshalling output: %v", err)) + } + switch dest := ctx.String(OutputBlockFlag.Name); dest { + case "stdout": + os.Stdout.Write(b) + os.Stdout.WriteString("\n") + case "stderr": + os.Stderr.Write(b) + os.Stderr.WriteString("\n") + default: + if err := saveFile(baseDir, dest, enc); err != nil { + return err + } + } + return nil +} diff --git a/cmd/evm/internal/t8ntool/flags.go b/cmd/evm/internal/t8ntool/flags.go index 05b6ed164..7db39479c 100644 --- a/cmd/evm/internal/t8ntool/flags.go +++ b/cmd/evm/internal/t8ntool/flags.go @@ -68,6 +68,14 @@ var ( "\t - into the file ", Value: "result.json", } + OutputBlockFlag = cli.StringFlag{ + Name: "output.block", + Usage: "Determines where to put the `block` after building.\n" + + "\t`stdout` - into the stdout output\n" + + "\t`stderr` - into the stderr output\n" + + "\t - into the file ", + Value: "block.json", + } InputAllocFlag = cli.StringFlag{ Name: "input.alloc", Usage: "`stdin` or file name of where to find the prestate alloc to use.", @@ -81,10 +89,41 @@ var ( InputTxsFlag = cli.StringFlag{ Name: "input.txs", Usage: "`stdin` or file name of where to find the transactions to apply. " + - "If the file prefix is '.rlp', then the data is interpreted as an RLP list of signed transactions." + + "If the file extension is '.rlp', then the data is interpreted as an RLP list of signed transactions." + "The '.rlp' format is identical to the output.body format.", Value: "txs.json", } + InputHeaderFlag = cli.StringFlag{ + Name: "input.header", + Usage: "`stdin` or file name of where to find the block header to use.", + Value: "header.json", + } + InputOmmersFlag = cli.StringFlag{ + Name: "input.ommers", + Usage: "`stdin` or file name of where to find the list of ommer header RLPs to use.", + } + InputTxsRlpFlag = cli.StringFlag{ + Name: "input.txs", + Usage: "`stdin` or file name of where to find the transactions list in RLP form.", + Value: "txs.rlp", + } + SealCliqueFlag = cli.StringFlag{ + Name: "seal.clique", + Usage: "Seal block with Clique. `stdin` or file name of where to find the Clique sealing data.", + } + SealEthashFlag = cli.BoolFlag{ + Name: "seal.ethash", + Usage: "Seal block with ethash.", + } + SealEthashDirFlag = cli.StringFlag{ + Name: "seal.ethash.dir", + Usage: "Path to ethash DAG. If none exists, a new DAG will be generated.", + } + SealEthashModeFlag = cli.StringFlag{ + Name: "seal.ethash.mode", + Usage: "Defines the type and amount of PoW verification an ethash engine makes.", + Value: "normal", + } RewardFlag = cli.Int64Flag{ Name: "state.reward", Usage: "Mining reward. Set to -1 to disable", diff --git a/cmd/evm/internal/t8ntool/gen_header.go b/cmd/evm/internal/t8ntool/gen_header.go new file mode 100644 index 000000000..196e49dd7 --- /dev/null +++ b/cmd/evm/internal/t8ntool/gen_header.go @@ -0,0 +1,135 @@ +// Code generated by github.com/fjl/gencodec. DO NOT EDIT. + +package t8ntool + +import ( + "encoding/json" + "errors" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/common/math" + "github.com/ethereum/go-ethereum/core/types" +) + +var _ = (*headerMarshaling)(nil) + +// MarshalJSON marshals as JSON. +func (h header) MarshalJSON() ([]byte, error) { + type header struct { + ParentHash common.Hash `json:"parentHash"` + OmmerHash *common.Hash `json:"sha3Uncles"` + Coinbase *common.Address `json:"miner"` + Root common.Hash `json:"stateRoot" gencodec:"required"` + TxHash *common.Hash `json:"transactionsRoot"` + ReceiptHash *common.Hash `json:"receiptsRoot"` + Bloom types.Bloom `json:"logsBloom"` + Difficulty *math.HexOrDecimal256 `json:"difficulty"` + Number *math.HexOrDecimal256 `json:"number" gencodec:"required"` + GasLimit math.HexOrDecimal64 `json:"gasLimit" gencodec:"required"` + GasUsed math.HexOrDecimal64 `json:"gasUsed"` + Time math.HexOrDecimal64 `json:"timestamp" gencodec:"required"` + Extra hexutil.Bytes `json:"extraData"` + MixDigest common.Hash `json:"mixHash"` + Nonce *types.BlockNonce `json:"nonce"` + BaseFee *math.HexOrDecimal256 `json:"baseFeePerGas" rlp:"optional"` + } + var enc header + enc.ParentHash = h.ParentHash + enc.OmmerHash = h.OmmerHash + enc.Coinbase = h.Coinbase + enc.Root = h.Root + enc.TxHash = h.TxHash + enc.ReceiptHash = h.ReceiptHash + enc.Bloom = h.Bloom + enc.Difficulty = (*math.HexOrDecimal256)(h.Difficulty) + enc.Number = (*math.HexOrDecimal256)(h.Number) + enc.GasLimit = math.HexOrDecimal64(h.GasLimit) + enc.GasUsed = math.HexOrDecimal64(h.GasUsed) + enc.Time = math.HexOrDecimal64(h.Time) + enc.Extra = h.Extra + enc.MixDigest = h.MixDigest + enc.Nonce = h.Nonce + enc.BaseFee = (*math.HexOrDecimal256)(h.BaseFee) + return json.Marshal(&enc) +} + +// UnmarshalJSON unmarshals from JSON. +func (h *header) UnmarshalJSON(input []byte) error { + type header struct { + ParentHash *common.Hash `json:"parentHash"` + OmmerHash *common.Hash `json:"sha3Uncles"` + Coinbase *common.Address `json:"miner"` + Root *common.Hash `json:"stateRoot" gencodec:"required"` + TxHash *common.Hash `json:"transactionsRoot"` + ReceiptHash *common.Hash `json:"receiptsRoot"` + Bloom *types.Bloom `json:"logsBloom"` + Difficulty *math.HexOrDecimal256 `json:"difficulty"` + Number *math.HexOrDecimal256 `json:"number" gencodec:"required"` + GasLimit *math.HexOrDecimal64 `json:"gasLimit" gencodec:"required"` + GasUsed *math.HexOrDecimal64 `json:"gasUsed"` + Time *math.HexOrDecimal64 `json:"timestamp" gencodec:"required"` + Extra *hexutil.Bytes `json:"extraData"` + MixDigest *common.Hash `json:"mixHash"` + Nonce *types.BlockNonce `json:"nonce"` + BaseFee *math.HexOrDecimal256 `json:"baseFeePerGas" rlp:"optional"` + } + var dec header + if err := json.Unmarshal(input, &dec); err != nil { + return err + } + if dec.ParentHash != nil { + h.ParentHash = *dec.ParentHash + } + if dec.OmmerHash != nil { + h.OmmerHash = dec.OmmerHash + } + if dec.Coinbase != nil { + h.Coinbase = dec.Coinbase + } + if dec.Root == nil { + return errors.New("missing required field 'stateRoot' for header") + } + h.Root = *dec.Root + if dec.TxHash != nil { + h.TxHash = dec.TxHash + } + if dec.ReceiptHash != nil { + h.ReceiptHash = dec.ReceiptHash + } + if dec.Bloom != nil { + h.Bloom = *dec.Bloom + } + if dec.Difficulty != nil { + h.Difficulty = (*big.Int)(dec.Difficulty) + } + if dec.Number == nil { + return errors.New("missing required field 'number' for header") + } + h.Number = (*big.Int)(dec.Number) + if dec.GasLimit == nil { + return errors.New("missing required field 'gasLimit' for header") + } + h.GasLimit = uint64(*dec.GasLimit) + if dec.GasUsed != nil { + h.GasUsed = uint64(*dec.GasUsed) + } + if dec.Time == nil { + return errors.New("missing required field 'timestamp' for header") + } + h.Time = uint64(*dec.Time) + if dec.Extra != nil { + h.Extra = *dec.Extra + } + if dec.MixDigest != nil { + h.MixDigest = *dec.MixDigest + } + if dec.Nonce != nil { + h.Nonce = dec.Nonce + } + if dec.BaseFee != nil { + h.BaseFee = (*big.Int)(dec.BaseFee) + } + return nil +} diff --git a/cmd/evm/internal/t8ntool/transaction.go b/cmd/evm/internal/t8ntool/transaction.go index e4fe24108..6f1c964ad 100644 --- a/cmd/evm/internal/t8ntool/transaction.go +++ b/cmd/evm/internal/t8ntool/transaction.go @@ -82,7 +82,7 @@ func Transaction(ctx *cli.Context) error { ) // Construct the chainconfig if cConf, _, err := tests.GetChainConfig(ctx.String(ForknameFlag.Name)); err != nil { - return NewError(ErrorVMConfig, fmt.Errorf("failed constructing chain configuration: %v", err)) + return NewError(ErrorConfig, fmt.Errorf("failed constructing chain configuration: %v", err)) } else { chainConfig = cConf } diff --git a/cmd/evm/internal/t8ntool/transition.go b/cmd/evm/internal/t8ntool/transition.go index 0aff715eb..8f203b0ed 100644 --- a/cmd/evm/internal/t8ntool/transition.go +++ b/cmd/evm/internal/t8ntool/transition.go @@ -43,11 +43,12 @@ import ( const ( ErrorEVM = 2 - ErrorVMConfig = 3 + ErrorConfig = 3 ErrorMissingBlockhash = 4 ErrorJson = 10 ErrorIO = 11 + ErrorRlp = 12 stdinSelector = "stdin" ) @@ -88,21 +89,14 @@ func Transition(ctx *cli.Context) error { log.Root().SetHandler(glogger) var ( - err error - tracer vm.EVMLogger - baseDir = "" + err error + tracer vm.EVMLogger ) var getTracer func(txIndex int, txHash common.Hash) (vm.EVMLogger, error) - // If user specified a basedir, make sure it exists - if ctx.IsSet(OutputBasedir.Name) { - if base := ctx.String(OutputBasedir.Name); len(base) > 0 { - err := os.MkdirAll(base, 0755) // //rw-r--r-- - if err != nil { - return NewError(ErrorIO, fmt.Errorf("failed creating output basedir: %v", err)) - } - baseDir = base - } + baseDir, err := createBasedir(ctx) + if err != nil { + return NewError(ErrorIO, fmt.Errorf("failed creating output basedir: %v", err)) } if ctx.Bool(TraceFlag.Name) { // Configure the EVM logger @@ -155,29 +149,17 @@ func Transition(ctx *cli.Context) error { } } if allocStr != stdinSelector { - inFile, err := os.Open(allocStr) - if err != nil { - return NewError(ErrorIO, fmt.Errorf("failed reading alloc file: %v", err)) - } - defer inFile.Close() - decoder := json.NewDecoder(inFile) - if err := decoder.Decode(&inputData.Alloc); err != nil { - return NewError(ErrorJson, fmt.Errorf("failed unmarshaling alloc-file: %v", err)) + if err := readFile(allocStr, "alloc", &inputData.Alloc); err != nil { + return err } } prestate.Pre = inputData.Alloc // Set the block environment if envStr != stdinSelector { - inFile, err := os.Open(envStr) - if err != nil { - return NewError(ErrorIO, fmt.Errorf("failed reading env file: %v", err)) - } - defer inFile.Close() - decoder := json.NewDecoder(inFile) var env stEnv - if err := decoder.Decode(&env); err != nil { - return NewError(ErrorJson, fmt.Errorf("failed unmarshaling env-file: %v", err)) + if err := readFile(envStr, "env", &env); err != nil { + return err } inputData.Env = &env } @@ -190,7 +172,7 @@ func Transition(ctx *cli.Context) error { // Construct the chainconfig var chainConfig *params.ChainConfig if cConf, extraEips, err := tests.GetChainConfig(ctx.String(ForknameFlag.Name)); err != nil { - return NewError(ErrorVMConfig, fmt.Errorf("failed constructing chain configuration: %v", err)) + return NewError(ErrorConfig, fmt.Errorf("failed constructing chain configuration: %v", err)) } else { chainConfig = cConf vmConfig.ExtraEips = extraEips @@ -254,18 +236,18 @@ func Transition(ctx *cli.Context) error { // Sanity check, to not `panic` in state_transition if chainConfig.IsLondon(big.NewInt(int64(prestate.Env.Number))) { if prestate.Env.BaseFee == nil { - return NewError(ErrorVMConfig, errors.New("EIP-1559 config but missing 'currentBaseFee' in env section")) + return NewError(ErrorConfig, errors.New("EIP-1559 config but missing 'currentBaseFee' in env section")) } } if env := prestate.Env; env.Difficulty == nil { // If difficulty was not provided by caller, we need to calculate it. switch { case env.ParentDifficulty == nil: - return NewError(ErrorVMConfig, errors.New("currentDifficulty was not provided, and cannot be calculated due to missing parentDifficulty")) + return NewError(ErrorConfig, errors.New("currentDifficulty was not provided, and cannot be calculated due to missing parentDifficulty")) case env.Number == 0: - return NewError(ErrorVMConfig, errors.New("currentDifficulty needs to be provided for block number 0")) + return NewError(ErrorConfig, errors.New("currentDifficulty needs to be provided for block number 0")) case env.Timestamp <= env.ParentTimestamp: - return NewError(ErrorVMConfig, fmt.Errorf("currentDifficulty cannot be calculated -- currentTime (%d) needs to be after parent time (%d)", + return NewError(ErrorConfig, fmt.Errorf("currentDifficulty cannot be calculated -- currentTime (%d) needs to be after parent time (%d)", env.Timestamp, env.ParentTimestamp)) } prestate.Env.Difficulty = calcDifficulty(chainConfig, env.Number, env.Timestamp, diff --git a/cmd/evm/internal/t8ntool/utils.go b/cmd/evm/internal/t8ntool/utils.go new file mode 100644 index 000000000..1c54f09bf --- /dev/null +++ b/cmd/evm/internal/t8ntool/utils.go @@ -0,0 +1,54 @@ +// Copyright 2021 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see . + +package t8ntool + +import ( + "encoding/json" + "fmt" + "os" + + "gopkg.in/urfave/cli.v1" +) + +// readFile reads the json-data in the provided path and marshals into dest. +func readFile(path, desc string, dest interface{}) error { + inFile, err := os.Open(path) + if err != nil { + return NewError(ErrorIO, fmt.Errorf("failed reading %s file: %v", desc, err)) + } + defer inFile.Close() + decoder := json.NewDecoder(inFile) + if err := decoder.Decode(dest); err != nil { + return NewError(ErrorJson, fmt.Errorf("failed unmarshaling %s file: %v", desc, err)) + } + return nil +} + +// createBasedir makes sure the basedir exists, if user specified one. +func createBasedir(ctx *cli.Context) (string, error) { + baseDir := "" + if ctx.IsSet(OutputBasedir.Name) { + if base := ctx.String(OutputBasedir.Name); len(base) > 0 { + err := os.MkdirAll(base, 0755) // //rw-r--r-- + if err != nil { + return "", err + } + baseDir = base + } + } + return baseDir, nil +} diff --git a/cmd/evm/main.go b/cmd/evm/main.go index 26064efc3..66d221a70 100644 --- a/cmd/evm/main.go +++ b/cmd/evm/main.go @@ -167,6 +167,25 @@ var transactionCommand = cli.Command{ }, } +var blockBuilderCommand = cli.Command{ + Name: "block-builder", + Aliases: []string{"b11r"}, + Usage: "builds a block", + Action: t8ntool.BuildBlock, + Flags: []cli.Flag{ + t8ntool.OutputBasedir, + t8ntool.OutputBlockFlag, + t8ntool.InputHeaderFlag, + t8ntool.InputOmmersFlag, + t8ntool.InputTxsRlpFlag, + t8ntool.SealCliqueFlag, + t8ntool.SealEthashFlag, + t8ntool.SealEthashDirFlag, + t8ntool.SealEthashModeFlag, + t8ntool.VerbosityFlag, + }, +} + func init() { app.Flags = []cli.Flag{ BenchFlag, @@ -200,6 +219,7 @@ func init() { stateTestCommand, stateTransitionCommand, transactionCommand, + blockBuilderCommand, } cli.CommandHelpTemplate = flags.OriginCommandHelpTemplate } diff --git a/cmd/evm/t8n_test.go b/cmd/evm/t8n_test.go index b4b816f57..3c759570f 100644 --- a/cmd/evm/t8n_test.go +++ b/cmd/evm/t8n_test.go @@ -131,7 +131,7 @@ func TestT8n(t *testing.T) { output: t8nOutput{alloc: true, result: true}, expExitCode: 4, }, - { // Ommer test + { // Uncle test base: "./testdata/5", input: t8nInput{ "alloc.json", "txs.json", "env.json", "Byzantium", "0x80", @@ -171,7 +171,7 @@ func TestT8n(t *testing.T) { output: t8nOutput{result: true}, expOut: "exp2.json", }, - { // Difficulty calculation - with uncles + Berlin + { // Difficulty calculation - with ommers + Berlin base: "./testdata/14", input: t8nInput{ "alloc.json", "txs.json", "env.uncles.json", "Berlin", "", @@ -336,6 +336,126 @@ func TestT9n(t *testing.T) { } } +type b11rInput struct { + inEnv string + inOmmersRlp string + inTxsRlp string + inClique string + ethash bool + ethashMode string + ethashDir string +} + +func (args *b11rInput) get(base string) []string { + var out []string + if opt := args.inEnv; opt != "" { + out = append(out, "--input.header") + out = append(out, fmt.Sprintf("%v/%v", base, opt)) + } + if opt := args.inOmmersRlp; opt != "" { + out = append(out, "--input.ommers") + out = append(out, fmt.Sprintf("%v/%v", base, opt)) + } + if opt := args.inTxsRlp; opt != "" { + out = append(out, "--input.txs") + out = append(out, fmt.Sprintf("%v/%v", base, opt)) + } + if opt := args.inClique; opt != "" { + out = append(out, "--seal.clique") + out = append(out, fmt.Sprintf("%v/%v", base, opt)) + } + if args.ethash { + out = append(out, "--seal.ethash") + } + if opt := args.ethashMode; opt != "" { + out = append(out, "--seal.ethash.mode") + out = append(out, fmt.Sprintf("%v/%v", base, opt)) + } + if opt := args.ethashDir; opt != "" { + out = append(out, "--seal.ethash.dir") + out = append(out, fmt.Sprintf("%v/%v", base, opt)) + } + out = append(out, "--output.block") + out = append(out, "stdout") + return out +} + +func TestB11r(t *testing.T) { + tt := new(testT8n) + tt.TestCmd = cmdtest.NewTestCmd(t, tt) + for i, tc := range []struct { + base string + input b11rInput + expExitCode int + expOut string + }{ + { // unsealed block + base: "./testdata/20", + input: b11rInput{ + inEnv: "header.json", + inOmmersRlp: "ommers.json", + inTxsRlp: "txs.rlp", + }, + expOut: "exp.json", + }, + { // ethash test seal + base: "./testdata/21", + input: b11rInput{ + inEnv: "header.json", + inOmmersRlp: "ommers.json", + inTxsRlp: "txs.rlp", + }, + expOut: "exp.json", + }, + { // clique test seal + base: "./testdata/21", + input: b11rInput{ + inEnv: "header.json", + inOmmersRlp: "ommers.json", + inTxsRlp: "txs.rlp", + inClique: "clique.json", + }, + expOut: "exp-clique.json", + }, + { // block with ommers + base: "./testdata/22", + input: b11rInput{ + inEnv: "header.json", + inOmmersRlp: "ommers.json", + inTxsRlp: "txs.rlp", + }, + expOut: "exp.json", + }, + } { + + args := []string{"b11r"} + args = append(args, tc.input.get(tc.base)...) + + tt.Run("evm-test", args...) + tt.Logf("args:\n go run . %v\n", strings.Join(args, " ")) + // Compare the expected output, if provided + if tc.expOut != "" { + want, err := os.ReadFile(fmt.Sprintf("%v/%v", tc.base, tc.expOut)) + if err != nil { + t.Fatalf("test %d: could not read expected output: %v", i, err) + } + have := tt.Output() + ok, err := cmpJson(have, want) + switch { + case err != nil: + t.Logf(string(have)) + t.Fatalf("test %d, json parsing failed: %v", i, err) + case !ok: + t.Fatalf("test %d: output wrong, have \n%v\nwant\n%v\n", i, string(have), string(want)) + } + } + tt.WaitExit() + if have, want := tt.ExitStatus(), tc.expExitCode; have != want { + t.Fatalf("test %d: wrong exit code, have %d, want %d", i, have, want) + } + } +} + // cmpJson compares the JSON in two byte slices. func cmpJson(a, b []byte) (bool, error) { var j, j2 interface{} diff --git a/cmd/evm/testdata/20/exp.json b/cmd/evm/testdata/20/exp.json new file mode 100644 index 000000000..7bec6cefd --- /dev/null +++ b/cmd/evm/testdata/20/exp.json @@ -0,0 +1,4 @@ +{ + "rlp": "0xf902d9f90211a0d6d785d33cbecf30f30d07e00e226af58f72efdf385d46bc3e6326c23b11e34ea01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d4934794e997a23b159e2e2a5ce72333262972374b15425ca0325aea6db48e9d737cddf59034843e99f05bec269453be83c9b9a981a232cc2ea056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000082100082c3be83050785808455c5277e99476574682f76312e302e312f6c696e75782f676f312e342e32a05865e417635a26db6d1d39ac70d1abf373e5398b3c6fd506acd038fa1334eedf8897435673d874f7c8f8c2f85f8002825208948a8eafb1cf62bfbeb1741769dae1a9dd4799619201801ba09500e8ba27d3c33ca7764e107410f44cbd8c19794bde214d694683a7aa998cdba07235ae07e4bd6e0206d102b1f8979d6adab280466b6a82d2208ee08951f1f600f85f8002825208948a8eafb1cf62bfbeb1741769dae1a9dd4799619201801ba09500e8ba27d3c33ca7764e107410f44cbd8c19794bde214d694683a7aa998cdba07235ae07e4bd6e0206d102b1f8979d6adab280466b6a82d2208ee08951f1f600c0", + "hash": "0xaba9a3b6a4e96e9ecffcadaa5a2ae0589359455617535cd86589fe1dd26fe899" +} diff --git a/cmd/evm/testdata/20/header.json b/cmd/evm/testdata/20/header.json new file mode 100644 index 000000000..fb9b7fc56 --- /dev/null +++ b/cmd/evm/testdata/20/header.json @@ -0,0 +1,14 @@ +{ + "parentHash": "0xd6d785d33cbecf30f30d07e00e226af58f72efdf385d46bc3e6326c23b11e34e", + "miner": "0xe997a23b159e2e2a5ce72333262972374b15425c", + "stateRoot": "0x325aea6db48e9d737cddf59034843e99f05bec269453be83c9b9a981a232cc2e", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "difficulty": "0x1000", + "number": "0xc3be", + "gasLimit": "0x50785", + "gasUsed": "0x0", + "timestamp": "0x55c5277e", + "extraData": "0x476574682f76312e302e312f6c696e75782f676f312e342e32", + "mixHash": "0x5865e417635a26db6d1d39ac70d1abf373e5398b3c6fd506acd038fa1334eedf", + "nonce": "0x97435673d874f7c8" +} diff --git a/cmd/evm/testdata/20/ommers.json b/cmd/evm/testdata/20/ommers.json new file mode 100644 index 000000000..fe51488c7 --- /dev/null +++ b/cmd/evm/testdata/20/ommers.json @@ -0,0 +1 @@ +[] diff --git a/cmd/evm/testdata/20/readme.md b/cmd/evm/testdata/20/readme.md new file mode 100644 index 000000000..2c448a96e --- /dev/null +++ b/cmd/evm/testdata/20/readme.md @@ -0,0 +1,11 @@ +# Block building + +This test shows how `b11r` can be used to assemble an unsealed block. + +```console +$ go run . b11r --input.header=testdata/20/header.json --input.txs=testdata/20/txs.rlp --input.ommers=testdata/20/ommers.json --output.block=stdout +{ + "rlp": "0xf90216f90211a0d6d785d33cbecf30f30d07e00e226af58f72efdf385d46bc3e6326c23b11e34ea01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d4934794e997a23b159e2e2a5ce72333262972374b15425ca0325aea6db48e9d737cddf59034843e99f05bec269453be83c9b9a981a232cc2ea056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000082100082c3be83050785808455c5277e99476574682f76312e302e312f6c696e75782f676f312e342e32a05865e417635a26db6d1d39ac70d1abf373e5398b3c6fd506acd038fa1334eedf8897435673d874f7c8c0c0", + "hash": "0xaba9a3b6a4e96e9ecffcadaa5a2ae0589359455617535cd86589fe1dd26fe899" +} +``` diff --git a/cmd/evm/testdata/20/txs.rlp b/cmd/evm/testdata/20/txs.rlp new file mode 100644 index 000000000..3599ff065 --- /dev/null +++ b/cmd/evm/testdata/20/txs.rlp @@ -0,0 +1 @@ +"0xf8c2f85f8002825208948a8eafb1cf62bfbeb1741769dae1a9dd4799619201801ba09500e8ba27d3c33ca7764e107410f44cbd8c19794bde214d694683a7aa998cdba07235ae07e4bd6e0206d102b1f8979d6adab280466b6a82d2208ee08951f1f600f85f8002825208948a8eafb1cf62bfbeb1741769dae1a9dd4799619201801ba09500e8ba27d3c33ca7764e107410f44cbd8c19794bde214d694683a7aa998cdba07235ae07e4bd6e0206d102b1f8979d6adab280466b6a82d2208ee08951f1f600" \ No newline at end of file diff --git a/cmd/evm/testdata/21/clique.json b/cmd/evm/testdata/21/clique.json new file mode 100644 index 000000000..84fa259a0 --- /dev/null +++ b/cmd/evm/testdata/21/clique.json @@ -0,0 +1,6 @@ +{ + "secretKey": "0x45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "voted": "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", + "authorize": false, + "vanity": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +} diff --git a/cmd/evm/testdata/21/exp-clique.json b/cmd/evm/testdata/21/exp-clique.json new file mode 100644 index 000000000..c990ba8aa --- /dev/null +++ b/cmd/evm/testdata/21/exp-clique.json @@ -0,0 +1,4 @@ +{ + "rlp": "0xf9025ff9025aa0d6d785d33cbecf30f30d07e00e226af58f72efdf385d46bc3e6326c23b11e34ea01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa0325aea6db48e9d737cddf59034843e99f05bec269453be83c9b9a981a232cc2ea056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000082100082c3be83050785808455c5277eb861aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaac540a67aaee364005841da84f488f6b6d0116dfb5103d091402c81a163d5f66666595e37f56f196d8c5c98da714dbfae68d6b7e1790cc734a20ec6ce52213ad800a05865e417635a26db6d1d39ac70d1abf373e5398b3c6fd506acd038fa1334eedf88ffffffffffffffffc0c0", + "hash": "0x71c59102cc805dbe8741e1210ebe229a321eff144ac7276006fefe39e8357dc7" +} diff --git a/cmd/evm/testdata/21/exp.json b/cmd/evm/testdata/21/exp.json new file mode 100644 index 000000000..b3e5e7a83 --- /dev/null +++ b/cmd/evm/testdata/21/exp.json @@ -0,0 +1,4 @@ +{ + "rlp": "0xf901fdf901f8a0d6d785d33cbecf30f30d07e00e226af58f72efdf385d46bc3e6326c23b11e34ea01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347940000000000000000000000000000000000000000a0325aea6db48e9d737cddf59034843e99f05bec269453be83c9b9a981a232cc2ea056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000082100082c3be83050785808455c5277e80a05865e417635a26db6d1d39ac70d1abf373e5398b3c6fd506acd038fa1334eedf880000000000000000c0c0", + "hash": "0x801411e9f6609a659825690d13e4f75a3cfe9143952fa2d9573f3b0a5eb9ebbb" +} diff --git a/cmd/evm/testdata/21/header.json b/cmd/evm/testdata/21/header.json new file mode 100644 index 000000000..62abe3cc2 --- /dev/null +++ b/cmd/evm/testdata/21/header.json @@ -0,0 +1,11 @@ +{ + "parentHash": "0xd6d785d33cbecf30f30d07e00e226af58f72efdf385d46bc3e6326c23b11e34e", + "stateRoot": "0x325aea6db48e9d737cddf59034843e99f05bec269453be83c9b9a981a232cc2e", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "difficulty": "0x1000", + "number": "0xc3be", + "gasLimit": "0x50785", + "gasUsed": "0x0", + "timestamp": "0x55c5277e", + "mixHash": "0x5865e417635a26db6d1d39ac70d1abf373e5398b3c6fd506acd038fa1334eedf" +} diff --git a/cmd/evm/testdata/21/ommers.json b/cmd/evm/testdata/21/ommers.json new file mode 100644 index 000000000..fe51488c7 --- /dev/null +++ b/cmd/evm/testdata/21/ommers.json @@ -0,0 +1 @@ +[] diff --git a/cmd/evm/testdata/21/readme.md b/cmd/evm/testdata/21/readme.md new file mode 100644 index 000000000..b70f106ff --- /dev/null +++ b/cmd/evm/testdata/21/readme.md @@ -0,0 +1,23 @@ +# Sealed block building + +This test shows how `b11r` can be used to assemble a sealed block. + +## Ethash + +```console +$ go run . b11r --input.header=testdata/21/header.json --input.txs=testdata/21/txs.rlp --input.ommers=testdata/21/ommers.json --seal.ethash --seal.ethash.mode=test --output.block=stdout +{ + "rlp": "0xf901fdf901f8a0d6d785d33cbecf30f30d07e00e226af58f72efdf385d46bc3e6326c23b11e34ea01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347940000000000000000000000000000000000000000a0325aea6db48e9d737cddf59034843e99f05bec269453be83c9b9a981a232cc2ea056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000082100082c3be83050785808455c5277e80a05865e417635a26db6d1d39ac70d1abf373e5398b3c6fd506acd038fa1334eedf880000000000000000c0c0", + "hash": "0x801411e9f6609a659825690d13e4f75a3cfe9143952fa2d9573f3b0a5eb9ebbb" +} +``` + +## Clique + +```console +$ go run . b11r --input.header=testdata/21/header.json --input.txs=testdata/21/txs.rlp --input.ommers=testdata/21/ommers.json --seal.clique=testdata/21/clique.json --output.block=stdout +{ + "rlp": "0xf9025ff9025aa0d6d785d33cbecf30f30d07e00e226af58f72efdf385d46bc3e6326c23b11e34ea01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa0325aea6db48e9d737cddf59034843e99f05bec269453be83c9b9a981a232cc2ea056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000082100082c3be83050785808455c5277eb861aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaac540a67aaee364005841da84f488f6b6d0116dfb5103d091402c81a163d5f66666595e37f56f196d8c5c98da714dbfae68d6b7e1790cc734a20ec6ce52213ad800a05865e417635a26db6d1d39ac70d1abf373e5398b3c6fd506acd038fa1334eedf88ffffffffffffffffc0c0", + "hash": "0x71c59102cc805dbe8741e1210ebe229a321eff144ac7276006fefe39e8357dc7" +} +``` diff --git a/cmd/evm/testdata/21/txs.rlp b/cmd/evm/testdata/21/txs.rlp new file mode 100644 index 000000000..e815397b3 --- /dev/null +++ b/cmd/evm/testdata/21/txs.rlp @@ -0,0 +1 @@ +"c0" diff --git a/cmd/evm/testdata/22/exp-clique.json b/cmd/evm/testdata/22/exp-clique.json new file mode 100644 index 000000000..c990ba8aa --- /dev/null +++ b/cmd/evm/testdata/22/exp-clique.json @@ -0,0 +1,4 @@ +{ + "rlp": "0xf9025ff9025aa0d6d785d33cbecf30f30d07e00e226af58f72efdf385d46bc3e6326c23b11e34ea01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa0325aea6db48e9d737cddf59034843e99f05bec269453be83c9b9a981a232cc2ea056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000082100082c3be83050785808455c5277eb861aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaac540a67aaee364005841da84f488f6b6d0116dfb5103d091402c81a163d5f66666595e37f56f196d8c5c98da714dbfae68d6b7e1790cc734a20ec6ce52213ad800a05865e417635a26db6d1d39ac70d1abf373e5398b3c6fd506acd038fa1334eedf88ffffffffffffffffc0c0", + "hash": "0x71c59102cc805dbe8741e1210ebe229a321eff144ac7276006fefe39e8357dc7" +} diff --git a/cmd/evm/testdata/22/exp.json b/cmd/evm/testdata/22/exp.json new file mode 100644 index 000000000..14fd81997 --- /dev/null +++ b/cmd/evm/testdata/22/exp.json @@ -0,0 +1,4 @@ +{ + "rlp": "0xf905f5f901f8a0d6d785d33cbecf30f30d07e00e226af58f72efdf385d46bc3e6326c23b11e34ea06eb9f0c3cd68c9e97134e6725d12b1f1d8f0644458da6870a37ff84c908fb1e7940000000000000000000000000000000000000000a0325aea6db48e9d737cddf59034843e99f05bec269453be83c9b9a981a232cc2ea056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000082100082c3be83050785808455c5277e80a05865e417635a26db6d1d39ac70d1abf373e5398b3c6fd506acd038fa1334eedf880000000000000000c0f903f6f901f8a0d6d785d33cbecf30f30d07e00e226af58f72efdf385d46bc3e6326c23b11e34ea01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347940000000000000000000000000000000000000000a0325aea6db48e9d737cddf59034843e99f05bec269453be83c9b9a981a232cc2ea056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000082100082c3be83050785808455c5277e80a05865e417635a26db6d1d39ac70d1abf373e5398b3c6fd506acd038fa1334eedf880000000000000000f901f8a0d6d785d33cbecf30f30d07e00e226af58f72efdf385d46bc3e6326c23b11e34ea01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347940000000000000000000000000000000000000000a0325aea6db48e9d737cddf59034843e99f05bec269453be83c9b9a981a232cc2ea056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000082100082c3be83050785808455c5277e80a05865e417635a26db6d1d39ac70d1abf373e5398b3c6fd506acd038fa1334eedf880000000000000000", + "hash": "0xd9a81c8fcd57a7f2a0d2c375eff6ad192c30c3729a271303f0a9a7e1b357e755" +} diff --git a/cmd/evm/testdata/22/header.json b/cmd/evm/testdata/22/header.json new file mode 100644 index 000000000..62abe3cc2 --- /dev/null +++ b/cmd/evm/testdata/22/header.json @@ -0,0 +1,11 @@ +{ + "parentHash": "0xd6d785d33cbecf30f30d07e00e226af58f72efdf385d46bc3e6326c23b11e34e", + "stateRoot": "0x325aea6db48e9d737cddf59034843e99f05bec269453be83c9b9a981a232cc2e", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "difficulty": "0x1000", + "number": "0xc3be", + "gasLimit": "0x50785", + "gasUsed": "0x0", + "timestamp": "0x55c5277e", + "mixHash": "0x5865e417635a26db6d1d39ac70d1abf373e5398b3c6fd506acd038fa1334eedf" +} diff --git a/cmd/evm/testdata/22/ommers.json b/cmd/evm/testdata/22/ommers.json new file mode 100644 index 000000000..997015b3c --- /dev/null +++ b/cmd/evm/testdata/22/ommers.json @@ -0,0 +1 @@ +["0xf901fdf901f8a0d6d785d33cbecf30f30d07e00e226af58f72efdf385d46bc3e6326c23b11e34ea01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347940000000000000000000000000000000000000000a0325aea6db48e9d737cddf59034843e99f05bec269453be83c9b9a981a232cc2ea056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000082100082c3be83050785808455c5277e80a05865e417635a26db6d1d39ac70d1abf373e5398b3c6fd506acd038fa1334eedf880000000000000000c0c0","0xf901fdf901f8a0d6d785d33cbecf30f30d07e00e226af58f72efdf385d46bc3e6326c23b11e34ea01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347940000000000000000000000000000000000000000a0325aea6db48e9d737cddf59034843e99f05bec269453be83c9b9a981a232cc2ea056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000082100082c3be83050785808455c5277e80a05865e417635a26db6d1d39ac70d1abf373e5398b3c6fd506acd038fa1334eedf880000000000000000c0c0"] diff --git a/cmd/evm/testdata/22/readme.md b/cmd/evm/testdata/22/readme.md new file mode 100644 index 000000000..2cac8a243 --- /dev/null +++ b/cmd/evm/testdata/22/readme.md @@ -0,0 +1,11 @@ +# Building blocks with ommers + +This test shows how `b11r` can chain together ommer assembles into a canonical block. + +```console +$ echo "{ \"ommers\": [`go run . b11r --input.header=testdata/22/header.json --input.txs=testdata/22/txs.rlp --output.block=stdout | jq '.[\"rlp\"]'`,`go run . b11r --input.header=testdata/22/header.json --input.txs=testdata/22/txs.rlp --output.block=stdout | jq '.[\"rlp\"]'`]}" | go run . b11r --input.header=testdata/22/header.json --input.txs=testdata/22/txs.rlp --input.ommers=stdin --output.block=stdout +{ + "rlp": "0xf905f5f901f8a0d6d785d33cbecf30f30d07e00e226af58f72efdf385d46bc3e6326c23b11e34ea06eb9f0c3cd68c9e97134e6725d12b1f1d8f0644458da6870a37ff84c908fb1e7940000000000000000000000000000000000000000a0325aea6db48e9d737cddf59034843e99f05bec269453be83c9b9a981a232cc2ea056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000082100082c3be83050785808455c5277e80a05865e417635a26db6d1d39ac70d1abf373e5398b3c6fd506acd038fa1334eedf880000000000000000c0f903f6f901f8a0d6d785d33cbecf30f30d07e00e226af58f72efdf385d46bc3e6326c23b11e34ea01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347940000000000000000000000000000000000000000a0325aea6db48e9d737cddf59034843e99f05bec269453be83c9b9a981a232cc2ea056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000082100082c3be83050785808455c5277e80a05865e417635a26db6d1d39ac70d1abf373e5398b3c6fd506acd038fa1334eedf880000000000000000f901f8a0d6d785d33cbecf30f30d07e00e226af58f72efdf385d46bc3e6326c23b11e34ea01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347940000000000000000000000000000000000000000a0325aea6db48e9d737cddf59034843e99f05bec269453be83c9b9a981a232cc2ea056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000082100082c3be83050785808455c5277e80a05865e417635a26db6d1d39ac70d1abf373e5398b3c6fd506acd038fa1334eedf880000000000000000", + "hash": "0xd9a81c8fcd57a7f2a0d2c375eff6ad192c30c3729a271303f0a9a7e1b357e755" +} +``` diff --git a/cmd/evm/testdata/22/txs.rlp b/cmd/evm/testdata/22/txs.rlp new file mode 100644 index 000000000..e815397b3 --- /dev/null +++ b/cmd/evm/testdata/22/txs.rlp @@ -0,0 +1 @@ +"c0" From d9c13d407f737b00fc64eb3f1803856c91dfb531 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Szil=C3=A1gyi?= Date: Mon, 22 Nov 2021 11:11:59 +0200 Subject: [PATCH 24/30] core, eth/downloader: fix resetting below freezer threshold --- core/blockchain.go | 16 ++++++++-------- eth/downloader/downloader.go | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/core/blockchain.go b/core/blockchain.go index ff372870d..6ee58ef4f 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -296,7 +296,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par if diskRoot != (common.Hash{}) { log.Warn("Head state missing, repairing", "number", head.Number(), "hash", head.Hash(), "snaproot", diskRoot) - snapDisk, err := bc.SetHeadBeyondRoot(head.NumberU64(), diskRoot) + snapDisk, err := bc.setHeadBeyondRoot(head.NumberU64(), diskRoot, true) if err != nil { return nil, err } @@ -306,7 +306,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par } } else { log.Warn("Head state missing, repairing", "number", head.Number(), "hash", head.Hash()) - if err := bc.SetHead(head.NumberU64()); err != nil { + if _, err := bc.setHeadBeyondRoot(head.NumberU64(), common.Hash{}, true); err != nil { return nil, err } } @@ -482,11 +482,11 @@ func (bc *BlockChain) loadLastState() error { // was fast synced or full synced and in which state, the method will try to // delete minimal data from disk whilst retaining chain consistency. func (bc *BlockChain) SetHead(head uint64) error { - _, err := bc.SetHeadBeyondRoot(head, common.Hash{}) + _, err := bc.setHeadBeyondRoot(head, common.Hash{}, false) return err } -// SetHeadBeyondRoot rewinds the local chain to a new head with the extra condition +// setHeadBeyondRoot rewinds the local chain to a new head with the extra condition // that the rewind must pass the specified state root. This method is meant to be // used when rewinding with snapshots enabled to ensure that we go back further than // persistent disk layer. Depending on whether the node was fast synced or full, and @@ -494,7 +494,7 @@ func (bc *BlockChain) SetHead(head uint64) error { // retaining chain consistency. // // The method returns the block number where the requested root cap was found. -func (bc *BlockChain) SetHeadBeyondRoot(head uint64, root common.Hash) (uint64, error) { +func (bc *BlockChain) setHeadBeyondRoot(head uint64, root common.Hash, repair bool) (uint64, error) { if !bc.chainmu.TryLock() { return 0, errChainStopped } @@ -509,7 +509,7 @@ func (bc *BlockChain) SetHeadBeyondRoot(head uint64, root common.Hash) (uint64, frozen, _ := bc.db.Ancients() updateFn := func(db ethdb.KeyValueWriter, header *types.Header) (uint64, bool) { - // Rewind the block chain, ensuring we don't end up with a stateless head + // Rewind the blockchain, ensuring we don't end up with a stateless head // block. Note, depth equality is permitted to allow using SetHead as a // chain reparation mechanism without deleting any data! if currentBlock := bc.CurrentBlock(); currentBlock != nil && header.Number.Uint64() <= currentBlock.NumberU64() { @@ -610,8 +610,8 @@ func (bc *BlockChain) SetHeadBeyondRoot(head uint64, root common.Hash) (uint64, } // If SetHead was only called as a chain reparation method, try to skip // touching the header chain altogether, unless the freezer is broken - if block := bc.CurrentBlock(); block.NumberU64() == head { - if target, force := updateFn(bc.db, block.Header()); force { + if repair { + if target, force := updateFn(bc.db, bc.CurrentBlock().Header()); force { bc.hc.SetHead(target, updateFn, delFn) } } else { diff --git a/eth/downloader/downloader.go b/eth/downloader/downloader.go index a6bf87acb..4ca1b55bb 100644 --- a/eth/downloader/downloader.go +++ b/eth/downloader/downloader.go @@ -535,7 +535,7 @@ func (d *Downloader) syncWithPeer(p *peerConnection, hash common.Hash, td *big.I } // Rewind the ancient store and blockchain if reorg happens. if origin+1 < frozen { - if err := d.lightchain.SetHead(origin + 1); err != nil { + if err := d.lightchain.SetHead(origin); err != nil { return err } } From 23f69c6db08c8241f7610756f04938990f74d93e Mon Sep 17 00:00:00 2001 From: lightclient <14004106+lightclient@users.noreply.github.com> Date: Tue, 23 Nov 2021 02:33:15 -0700 Subject: [PATCH 25/30] cmd/evm: add support for signing transactions in the unprotected format (#23937) * cmd/evm: add support for signing transactions in the unprotected format * cmd/evm: simplify signing of unprotected txs --- cmd/evm/internal/t8ntool/transition.go | 35 ++++++++++++++++++-------- cmd/evm/t8n_test.go | 8 ++++++ cmd/evm/testdata/23/alloc.json | 16 ++++++++++++ cmd/evm/testdata/23/env.json | 7 ++++++ cmd/evm/testdata/23/exp.json | 25 ++++++++++++++++++ cmd/evm/testdata/23/readme.md | 1 + cmd/evm/testdata/23/txs.json | 15 +++++++++++ 7 files changed, 97 insertions(+), 10 deletions(-) create mode 100644 cmd/evm/testdata/23/alloc.json create mode 100644 cmd/evm/testdata/23/env.json create mode 100644 cmd/evm/testdata/23/exp.json create mode 100644 cmd/evm/testdata/23/readme.md create mode 100644 cmd/evm/testdata/23/txs.json diff --git a/cmd/evm/internal/t8ntool/transition.go b/cmd/evm/internal/t8ntool/transition.go index 8f203b0ed..e0e67cd7e 100644 --- a/cmd/evm/internal/t8ntool/transition.go +++ b/cmd/evm/internal/t8ntool/transition.go @@ -268,27 +268,34 @@ func Transition(ctx *cli.Context) error { // txWithKey is a helper-struct, to allow us to use the types.Transaction along with // a `secretKey`-field, for input type txWithKey struct { - key *ecdsa.PrivateKey - tx *types.Transaction + key *ecdsa.PrivateKey + tx *types.Transaction + protected bool } func (t *txWithKey) UnmarshalJSON(input []byte) error { - // Read the secretKey, if present - type sKey struct { - Key *common.Hash `json:"secretKey"` + // Read the metadata, if present + type txMetadata struct { + Key *common.Hash `json:"secretKey"` + Protected *bool `json:"protected"` } - var key sKey - if err := json.Unmarshal(input, &key); err != nil { + var data txMetadata + if err := json.Unmarshal(input, &data); err != nil { return err } - if key.Key != nil { - k := key.Key.Hex()[2:] + if data.Key != nil { + k := data.Key.Hex()[2:] if ecdsaKey, err := crypto.HexToECDSA(k); err != nil { return err } else { t.key = ecdsaKey } } + if data.Protected != nil { + t.protected = *data.Protected + } else { + t.protected = true + } // Now, read the transaction itself var tx types.Transaction if err := json.Unmarshal(input, &tx); err != nil { @@ -317,7 +324,15 @@ func signUnsignedTransactions(txs []*txWithKey, signer types.Signer) (types.Tran v, r, s := tx.RawSignatureValues() if key != nil && v.BitLen()+r.BitLen()+s.BitLen() == 0 { // This transaction needs to be signed - signed, err := types.SignTx(tx, signer, key) + var ( + signed *types.Transaction + err error + ) + if txWithKey.protected { + signed, err = types.SignTx(tx, signer, key) + } else { + signed, err = types.SignTx(tx, types.FrontierSigner{}, key) + } if err != nil { return nil, NewError(ErrorJson, fmt.Errorf("tx %d: failed to sign tx: %v", i, err)) } diff --git a/cmd/evm/t8n_test.go b/cmd/evm/t8n_test.go index 3c759570f..3f0bd3185 100644 --- a/cmd/evm/t8n_test.go +++ b/cmd/evm/t8n_test.go @@ -195,6 +195,14 @@ func TestT8n(t *testing.T) { output: t8nOutput{result: true}, expOut: "exp_arrowglacier.json", }, + { // Sign unprotected (pre-EIP155) transaction + base: "./testdata/23", + input: t8nInput{ + "alloc.json", "txs.json", "env.json", "Berlin", "", + }, + output: t8nOutput{result: true}, + expOut: "exp.json", + }, } { args := []string{"t8n"} diff --git a/cmd/evm/testdata/23/alloc.json b/cmd/evm/testdata/23/alloc.json new file mode 100644 index 000000000..239b3553f --- /dev/null +++ b/cmd/evm/testdata/23/alloc.json @@ -0,0 +1,16 @@ +{ + "0x095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "0x0de0b6b3a7640000", + "code" : "0x6001", + "nonce" : "0x00", + "storage" : { + } + }, + "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "0x0de0b6b3a7640000", + "code" : "0x", + "nonce" : "0x00", + "storage" : { + } + } +} diff --git a/cmd/evm/testdata/23/env.json b/cmd/evm/testdata/23/env.json new file mode 100644 index 000000000..1b4632151 --- /dev/null +++ b/cmd/evm/testdata/23/env.json @@ -0,0 +1,7 @@ +{ + "currentCoinbase" : "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", + "currentDifficulty" : "0x020000", + "currentGasLimit" : "0x3b9aca00", + "currentNumber" : "0x05", + "currentTimestamp" : "0x03e8" +} diff --git a/cmd/evm/testdata/23/exp.json b/cmd/evm/testdata/23/exp.json new file mode 100644 index 000000000..e51f37d9c --- /dev/null +++ b/cmd/evm/testdata/23/exp.json @@ -0,0 +1,25 @@ +{ + "result": { + "stateRoot": "0x65334305e4accfa18352deb24f007b837b5036425b0712cf0e65a43bfa95154d", + "txRoot": "0x75e61774a2ff58cbe32653420256c7f44bc715715a423b0b746d5c622979af6b", + "receiptsRoot": "0xf951f9396af203499cc7d379715a9110323de73967c5700e2f424725446a3c76", + "logsHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "receipts": [ + { + "root": "0x", + "status": "0x1", + "cumulativeGasUsed": "0x520b", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "logs": null, + "transactionHash": "0x72fadbef39cd251a437eea619cfeda752271a5faaaa2147df012e112159ffb81", + "contractAddress": "0x0000000000000000000000000000000000000000", + "gasUsed": "0x520b", + "blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "transactionIndex": "0x0" + } + ], + "currentDifficulty": "0x20000", + "gasUsed": "0x520b" + } +} diff --git a/cmd/evm/testdata/23/readme.md b/cmd/evm/testdata/23/readme.md new file mode 100644 index 000000000..85fe8db66 --- /dev/null +++ b/cmd/evm/testdata/23/readme.md @@ -0,0 +1 @@ +These files examplify how to sign a transaction using the pre-EIP155 scheme. diff --git a/cmd/evm/testdata/23/txs.json b/cmd/evm/testdata/23/txs.json new file mode 100644 index 000000000..22f3840f8 --- /dev/null +++ b/cmd/evm/testdata/23/txs.json @@ -0,0 +1,15 @@ +[ + { + "input" : "0x", + "gas" : "0x5f5e100", + "gasPrice" : "0x1", + "nonce" : "0x0", + "to" : "0x095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "0x186a0", + "v" : "0x0", + "r" : "0x0", + "s" : "0x0", + "secretKey" : "0x45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "protected": false + } +] From 347c37b362dbcf1cd986a5b0000a9d67667f6bee Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Tue, 23 Nov 2021 12:37:26 +0100 Subject: [PATCH 26/30] core/rawdb: use AncientRange when initializing leveldb from freezer (#23612) * core/rawdb: utilize AncientRange when initiating from freezer * core/rawdb: remove debug sanity check --- core/rawdb/chain_iterator.go | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/core/rawdb/chain_iterator.go b/core/rawdb/chain_iterator.go index ad222005b..daee72159 100644 --- a/core/rawdb/chain_iterator.go +++ b/core/rawdb/chain_iterator.go @@ -44,24 +44,29 @@ func InitDatabaseFromFreezer(db ethdb.Database) { logged = start.Add(-7 * time.Second) // Unindex during import is fast, don't double log hash common.Hash ) - for i := uint64(0); i < frozen; i++ { - // Since the freezer has all data in sequential order on a file, - // it would be 'neat' to read more data in one go, and let the - // freezerdb return N items (e.g up to 1000 items per go) - // That would require an API change in Ancients though - if h, err := db.Ancient(freezerHashTable, i); err != nil { + for i := uint64(0); i < frozen; { + // We read 100K hashes at a time, for a total of 3.2M + count := uint64(100_000) + if i+count > frozen { + count = frozen - i + } + data, err := db.AncientRange(freezerHashTable, i, count, 32*count) + if err != nil { log.Crit("Failed to init database from freezer", "err", err) - } else { + } + for j, h := range data { + number := i + uint64(j) hash = common.BytesToHash(h) - } - WriteHeaderNumber(batch, hash, i) - // If enough data was accumulated in memory or we're at the last block, dump to disk - if batch.ValueSize() > ethdb.IdealBatchSize { - if err := batch.Write(); err != nil { - log.Crit("Failed to write data to db", "err", err) + WriteHeaderNumber(batch, hash, number) + // If enough data was accumulated in memory or we're at the last block, dump to disk + if batch.ValueSize() > ethdb.IdealBatchSize { + if err := batch.Write(); err != nil { + log.Crit("Failed to write data to db", "err", err) + } + batch.Reset() } - batch.Reset() } + i += uint64(len(data)) // If we've spent too much time already, notify the user of what we're doing if time.Since(logged) > 8*time.Second { log.Info("Initializing database from freezer", "total", frozen, "number", i, "hash", hash, "elapsed", common.PrettyDuration(time.Since(start))) From d15e423562b451a9e059cb1c39608f26a938cf3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Serhat=20=C5=9Eevki=20Din=C3=A7er?= Date: Tue, 23 Nov 2021 17:14:08 +0300 Subject: [PATCH 27/30] p2p/enode: store local port number as uint16 (#23926) --- p2p/enode/localnode.go | 17 ++++++++++------- p2p/simulations/adapters/types.go | 2 +- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/p2p/enode/localnode.go b/p2p/enode/localnode.go index 4827b6c0a..a18204e75 100644 --- a/p2p/enode/localnode.go +++ b/p2p/enode/localnode.go @@ -63,7 +63,7 @@ type LocalNode struct { type lnEndpoint struct { track *netutil.IPTracker staticIP, fallbackIP net.IP - fallbackUDP int + fallbackUDP uint16 // port } // NewLocalNode creates a local node. @@ -208,8 +208,8 @@ func (ln *LocalNode) SetFallbackUDP(port int) { ln.mu.Lock() defer ln.mu.Unlock() - ln.endpoint4.fallbackUDP = port - ln.endpoint6.fallbackUDP = port + ln.endpoint4.fallbackUDP = uint16(port) + ln.endpoint6.fallbackUDP = uint16(port) ln.updateEndpoints() } @@ -261,7 +261,7 @@ func (ln *LocalNode) updateEndpoints() { } // get returns the endpoint with highest precedence. -func (e *lnEndpoint) get() (newIP net.IP, newPort int) { +func (e *lnEndpoint) get() (newIP net.IP, newPort uint16) { newPort = e.fallbackUDP if e.fallbackIP != nil { newIP = e.fallbackIP @@ -277,15 +277,18 @@ func (e *lnEndpoint) get() (newIP net.IP, newPort int) { // predictAddr wraps IPTracker.PredictEndpoint, converting from its string-based // endpoint representation to IP and port types. -func predictAddr(t *netutil.IPTracker) (net.IP, int) { +func predictAddr(t *netutil.IPTracker) (net.IP, uint16) { ep := t.PredictEndpoint() if ep == "" { return nil, 0 } ipString, portString, _ := net.SplitHostPort(ep) ip := net.ParseIP(ipString) - port, _ := strconv.Atoi(portString) - return ip, port + port, err := strconv.ParseUint(portString, 10, 16) + if err != nil { + return nil, 0 + } + return ip, uint16(port) } func (ln *LocalNode) invalidate() { diff --git a/p2p/simulations/adapters/types.go b/p2p/simulations/adapters/types.go index 1da464a10..aeb8ef777 100644 --- a/p2p/simulations/adapters/types.go +++ b/p2p/simulations/adapters/types.go @@ -242,7 +242,7 @@ func assignTCPPort() (uint16, error) { if err != nil { return 0, err } - p, err := strconv.ParseInt(port, 10, 32) + p, err := strconv.ParseUint(port, 10, 16) if err != nil { return 0, err } From 743769f48e2fbfa847e1128b11afc76d7d6aaba0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Szil=C3=A1gyi?= Date: Tue, 23 Nov 2021 19:28:17 +0200 Subject: [PATCH 28/30] trie: reject deletions when verifying range proofs --- trie/proof.go | 7 +++- trie/proof_test.go | 79 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+), 1 deletion(-) diff --git a/trie/proof.go b/trie/proof.go index 08a9e4042..51ecea0c3 100644 --- a/trie/proof.go +++ b/trie/proof.go @@ -472,12 +472,17 @@ func VerifyRangeProof(rootHash common.Hash, firstKey []byte, lastKey []byte, key if len(keys) != len(values) { return false, fmt.Errorf("inconsistent proof data, keys: %d, values: %d", len(keys), len(values)) } - // Ensure the received batch is monotonic increasing. + // Ensure the received batch is monotonic increasing and contains no deletions for i := 0; i < len(keys)-1; i++ { if bytes.Compare(keys[i], keys[i+1]) >= 0 { return false, errors.New("range is not monotonically increasing") } } + for _, value := range values { + if len(value) == 0 { + return false, errors.New("range contains deletion") + } + } // Special case, there is no edge proof at all. The given range is expected // to be the whole leaf-set in the trie. if proof == nil { diff --git a/trie/proof_test.go b/trie/proof_test.go index a35b7144c..95ad6169c 100644 --- a/trie/proof_test.go +++ b/trie/proof_test.go @@ -813,6 +813,85 @@ func TestBloatedProof(t *testing.T) { } } +// TestEmptyValueRangeProof tests normal range proof with both edge proofs +// as the existent proof, but with an extra empty value included, which is a +// noop technically, but practically should be rejected. +func TestEmptyValueRangeProof(t *testing.T) { + trie, values := randomTrie(512) + var entries entrySlice + for _, kv := range values { + entries = append(entries, kv) + } + sort.Sort(entries) + + // Create a new entry with a slightly modified key + mid := len(entries) / 2 + key := common.CopyBytes(entries[mid-1].k) + for n := len(key) - 1; n >= 0; n-- { + if key[n] < 0xff { + key[n]++ + break + } + } + noop := &kv{key, []byte{}, false} + entries = append(append(append([]*kv{}, entries[:mid]...), noop), entries[mid:]...) + + start, end := 1, len(entries)-1 + + proof := memorydb.New() + if err := trie.Prove(entries[start].k, 0, proof); err != nil { + t.Fatalf("Failed to prove the first node %v", err) + } + if err := trie.Prove(entries[end-1].k, 0, proof); err != nil { + t.Fatalf("Failed to prove the last node %v", err) + } + var keys [][]byte + var vals [][]byte + for i := start; i < end; i++ { + keys = append(keys, entries[i].k) + vals = append(vals, entries[i].v) + } + _, err := VerifyRangeProof(trie.Hash(), keys[0], keys[len(keys)-1], keys, vals, proof) + if err == nil { + t.Fatalf("Expected failure on noop entry") + } +} + +// TestAllElementsEmptyValueRangeProof tests the range proof with all elements, +// but with an extra empty value included, which is a noop technically, but +// practically should be rejected. +func TestAllElementsEmptyValueRangeProof(t *testing.T) { + trie, values := randomTrie(512) + var entries entrySlice + for _, kv := range values { + entries = append(entries, kv) + } + sort.Sort(entries) + + // Create a new entry with a slightly modified key + mid := len(entries) / 2 + key := common.CopyBytes(entries[mid-1].k) + for n := len(key) - 1; n >= 0; n-- { + if key[n] < 0xff { + key[n]++ + break + } + } + noop := &kv{key, []byte{}, false} + entries = append(append(append([]*kv{}, entries[:mid]...), noop), entries[mid:]...) + + var keys [][]byte + var vals [][]byte + for i := 0; i < len(entries); i++ { + keys = append(keys, entries[i].k) + vals = append(vals, entries[i].v) + } + _, err := VerifyRangeProof(trie.Hash(), nil, nil, keys, vals, nil) + if err == nil { + t.Fatalf("Expected failure on noop entry") + } +} + // mutateByte changes one byte in b. func mutateByte(b []byte) { for r := mrand.Intn(len(b)); ; { From 0a7672fc9aa816ff0ae385da28f2cbd5b36e3677 Mon Sep 17 00:00:00 2001 From: lightclient <14004106+lightclient@users.noreply.github.com> Date: Wed, 24 Nov 2021 02:15:23 -0700 Subject: [PATCH 29/30] cmd/evm: rename t8n args to improve clarity when tracing (#23934) * cmd/evm: rename t8n args to improve clarity when tracing * cmd/evm: add back removed tracing flags and note that they are deprecated * cmd/evm: add warning when using deprecated flag --- cmd/evm/internal/t8ntool/flags.go | 12 ++++++++++-- cmd/evm/internal/t8ntool/transition.go | 16 ++++++++++++++-- cmd/evm/main.go | 2 ++ 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/cmd/evm/internal/t8ntool/flags.go b/cmd/evm/internal/t8ntool/flags.go index 7db39479c..b6054ea56 100644 --- a/cmd/evm/internal/t8ntool/flags.go +++ b/cmd/evm/internal/t8ntool/flags.go @@ -32,7 +32,11 @@ var ( } TraceDisableMemoryFlag = cli.BoolTFlag{ Name: "trace.nomemory", - Usage: "Disable full memory dump in traces", + Usage: "Disable full memory dump in traces (deprecated)", + } + TraceEnableMemoryFlag = cli.BoolFlag{ + Name: "trace.memory", + Usage: "Enable full memory dump in traces", } TraceDisableStackFlag = cli.BoolFlag{ Name: "trace.nostack", @@ -40,7 +44,11 @@ var ( } TraceDisableReturnDataFlag = cli.BoolTFlag{ Name: "trace.noreturndata", - Usage: "Disable return data output in traces", + Usage: "Disable return data output in traces (deprecated)", + } + TraceEnableReturnDataFlag = cli.BoolFlag{ + Name: "trace.returndata", + Usage: "Enable return data output in traces", } OutputBasedir = cli.StringFlag{ Name: "output.basedir", diff --git a/cmd/evm/internal/t8ntool/transition.go b/cmd/evm/internal/t8ntool/transition.go index e0e67cd7e..edb439425 100644 --- a/cmd/evm/internal/t8ntool/transition.go +++ b/cmd/evm/internal/t8ntool/transition.go @@ -99,11 +99,23 @@ func Transition(ctx *cli.Context) error { return NewError(ErrorIO, fmt.Errorf("failed creating output basedir: %v", err)) } if ctx.Bool(TraceFlag.Name) { + if ctx.IsSet(TraceDisableMemoryFlag.Name) && ctx.IsSet(TraceEnableMemoryFlag.Name) { + return NewError(ErrorConfig, fmt.Errorf("can't use both flags --%s and --%s", TraceDisableMemoryFlag.Name, TraceEnableMemoryFlag.Name)) + } + if ctx.IsSet(TraceDisableReturnDataFlag.Name) && ctx.IsSet(TraceEnableReturnDataFlag.Name) { + return NewError(ErrorConfig, fmt.Errorf("can't use both flags --%s and --%s", TraceDisableReturnDataFlag.Name, TraceEnableReturnDataFlag.Name)) + } + if ctx.IsSet(TraceDisableMemoryFlag.Name) { + log.Warn(fmt.Sprintf("--%s has been deprecated in favour of --%s", TraceDisableMemoryFlag.Name, TraceEnableMemoryFlag.Name)) + } + if ctx.IsSet(TraceDisableReturnDataFlag.Name) { + log.Warn(fmt.Sprintf("--%s has been deprecated in favour of --%s", TraceDisableReturnDataFlag.Name, TraceEnableReturnDataFlag.Name)) + } // Configure the EVM logger logConfig := &vm.LogConfig{ DisableStack: ctx.Bool(TraceDisableStackFlag.Name), - EnableMemory: !ctx.Bool(TraceDisableMemoryFlag.Name), - EnableReturnData: !ctx.Bool(TraceDisableReturnDataFlag.Name), + EnableMemory: !ctx.Bool(TraceDisableMemoryFlag.Name) || ctx.Bool(TraceEnableMemoryFlag.Name), + EnableReturnData: !ctx.Bool(TraceDisableReturnDataFlag.Name) || ctx.Bool(TraceEnableReturnDataFlag.Name), Debug: true, } var prevFile *os.File diff --git a/cmd/evm/main.go b/cmd/evm/main.go index 66d221a70..2f404d48e 100644 --- a/cmd/evm/main.go +++ b/cmd/evm/main.go @@ -139,8 +139,10 @@ var stateTransitionCommand = cli.Command{ Flags: []cli.Flag{ t8ntool.TraceFlag, t8ntool.TraceDisableMemoryFlag, + t8ntool.TraceEnableMemoryFlag, t8ntool.TraceDisableStackFlag, t8ntool.TraceDisableReturnDataFlag, + t8ntool.TraceEnableReturnDataFlag, t8ntool.OutputBasedir, t8ntool.OutputAllocFlag, t8ntool.OutputResultFlag, From 7a0c19f813e285516f4b525305fd73b625d2dec8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Szil=C3=A1gyi?= Date: Wed, 24 Nov 2021 13:44:10 +0200 Subject: [PATCH 30/30] params: release Geth v1.10.13 --- params/version.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/params/version.go b/params/version.go index 9d392e64e..4a5b9835f 100644 --- a/params/version.go +++ b/params/version.go @@ -21,10 +21,10 @@ import ( ) const ( - VersionMajor = 1 // Major version component of the current release - VersionMinor = 10 // Minor version component of the current release - VersionPatch = 13 // Patch version component of the current release - VersionMeta = "unstable" // Version metadata to append to the version string + VersionMajor = 1 // Major version component of the current release + VersionMinor = 10 // Minor version component of the current release + VersionPatch = 13 // Patch version component of the current release + VersionMeta = "stable" // Version metadata to append to the version string ) // Version holds the textual version string.