diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3867e3f400..6ec5e10f6b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -59,7 +59,7 @@ jobs: strategy: matrix: go: ['1.19'] - os: [macos-11.0, ubuntu-20.04] + os: [macos-11.0, ubuntu-20.04, windows-latest] steps: - name: check out if: ${{ github.event_name != 'workflow_dispatch' }} diff --git a/Dockerfile b/Dockerfile index 7c8c4b7dc5..c6120e309a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # ============= Compilation Stage ================ -FROM golang:1.18.5-buster AS builder +FROM golang:1.19.10-buster AS builder RUN apt-get update && apt-get install -y --no-install-recommends bash=5.0-4 make=4.2.1-1.2 gcc=4:8.3.0-1 musl-dev=1.1.21-2 ca-certificates=20200601~deb10u2 linux-headers-amd64 diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go index c49d5d30b3..281e80cfbc 100644 --- a/accounts/abi/bind/backends/simulated.go +++ b/accounts/abi/bind/backends/simulated.go @@ -149,10 +149,10 @@ func NewSimulatedBackendWithChainConfig(alloc core.GenesisAlloc, gasLimit uint64 // and uses a simulated blockchain for testing purposes. // A simulated backend always uses chainID 1337. func NewSimulatedBackendWithDatabase(database ethdb.Database, alloc core.GenesisAlloc, gasLimit uint64, addr common.Address) *SimulatedBackend { - cpcfg := params.TestChainConfig - cpcfg.ChainID = big.NewInt(1337) + copyConfig := *params.TestChainConfig + copyConfig.ChainID = big.NewInt(1337) genesis := core.Genesis{ - Config: cpcfg, + Config: ©Config, GasLimit: gasLimit, Alloc: alloc, InitialAdmin: addr, diff --git a/accounts/abi/bind/backends/simulated_test.go b/accounts/abi/bind/backends/simulated_test.go index 044fc53bc8..3ad2121fe2 100644 --- a/accounts/abi/bind/backends/simulated_test.go +++ b/accounts/abi/bind/backends/simulated_test.go @@ -137,14 +137,6 @@ func TestNewSimulatedBackend(t *testing.T) { sim := simTestBackend(testAddr) defer sim.Close() - if sim.config != params.TestChainConfig { - t.Errorf("expected sim config to equal params.AllEthashProtocolChanges, got %v", sim.config) - } - - if sim.blockchain.Config() != params.TestChainConfig { - t.Errorf("expected sim blockchain config to equal params.AllEthashProtocolChanges, got %v", sim.config) - } - stateDB, _ := sim.blockchain.State() bal := stateDB.GetBalance(testAddr) if bal.Cmp(expectedBal) != 0 { diff --git a/accounts/abi/bind/template.go b/accounts/abi/bind/template.go index dea4920c4a..22dca1e71d 100644 --- a/accounts/abi/bind/template.go +++ b/accounts/abi/bind/template.go @@ -335,7 +335,7 @@ var ( if err != nil { return *outstruct, err } - {{range $i, $t := .Normalized.Outputs}} + {{range $i, $t := .Normalized.Outputs}} outstruct.{{.Name}} = *abi.ConvertType(out[{{$i}}], new({{bindtype .Type $structs}})).(*{{bindtype .Type $structs}}){{end}} return *outstruct, err @@ -345,7 +345,7 @@ var ( } {{range $i, $t := .Normalized.Outputs}} out{{$i}} := *abi.ConvertType(out[{{$i}}], new({{bindtype .Type $structs}})).(*{{bindtype .Type $structs}}){{end}} - + return {{range $i, $t := .Normalized.Outputs}}out{{$i}}, {{end}} err {{end}} } @@ -388,7 +388,7 @@ var ( } {{end}} - {{if .Fallback}} + {{if .Fallback}} // Fallback is a paid mutator transaction binding the contract fallback function. // // Solidity: {{.Fallback.Original.String}} @@ -402,16 +402,16 @@ var ( func (_{{$contract.Type}} *{{$contract.Type}}Session) Fallback(calldata []byte) (*types.Transaction, error) { return _{{$contract.Type}}.Contract.Fallback(&_{{$contract.Type}}.TransactOpts, calldata) } - + // Fallback is a paid mutator transaction binding the contract fallback function. - // + // // Solidity: {{.Fallback.Original.String}} func (_{{$contract.Type}} *{{$contract.Type}}TransactorSession) Fallback(calldata []byte) (*types.Transaction, error) { return _{{$contract.Type}}.Contract.Fallback(&_{{$contract.Type}}.TransactOpts, calldata) } {{end}} - {{if .Receive}} + {{if .Receive}} // Receive is a paid mutator transaction binding the contract receive function. // // Solidity: {{.Receive.Original.String}} @@ -425,9 +425,9 @@ var ( func (_{{$contract.Type}} *{{$contract.Type}}Session) Receive() (*types.Transaction, error) { return _{{$contract.Type}}.Contract.Receive(&_{{$contract.Type}}.TransactOpts) } - + // Receive is a paid mutator transaction binding the contract receive function. - // + // // Solidity: {{.Receive.Original.String}} func (_{{$contract.Type}} *{{$contract.Type}}TransactorSession) Receive() (*types.Transaction, error) { return _{{$contract.Type}}.Contract.Receive(&_{{$contract.Type}}.TransactOpts) @@ -576,6 +576,6 @@ var ( return event, nil } - {{end}} + {{end}} {{end}} ` diff --git a/accounts/abi/error.go b/accounts/abi/error.go index 682b4bd108..60d83ec51f 100644 --- a/accounts/abi/error.go +++ b/accounts/abi/error.go @@ -42,7 +42,7 @@ type Error struct { str string // Sig contains the string signature according to the ABI spec. - // e.g. error foo(uint32 a, int b) = "foo(uint32,int256)" + // e.g. error foo(uint32 a, int b) = "foo(uint32,int256)" // Please note that "int" is substitute for its canonical representation "int256" Sig string diff --git a/core/blockchain_log_test.go b/core/blockchain_log_test.go index 094ed747bd..40cb8515d8 100644 --- a/core/blockchain_log_test.go +++ b/core/blockchain_log_test.go @@ -19,20 +19,21 @@ import ( "github.com/stretchr/testify/require" ) -/* -Example contract to test event emission: - - pragma solidity >=0.7.0 <0.9.0; - contract Callable { - event Called(); - function Call() public { emit Called(); } - } -*/ -const callableAbi = "[{\"anonymous\":false,\"inputs\":[],\"name\":\"Called\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"Call\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]" - -const callableBin = "6080604052348015600f57600080fd5b5060998061001e6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c806334e2292114602d575b600080fd5b60336035565b005b7f81fab7a4a0aa961db47eefc81f143a5220e8c8495260dd65b1356f1d19d3c7b860405160405180910390a156fea2646970667358221220029436d24f3ac598ceca41d4d712e13ced6d70727f4cdc580667de66d2f51d8b64736f6c63430008010033" - -func TestEmitLogsCorrectness(t *testing.T) { +func TestAcceptedLogsSubscription(t *testing.T) { + /* + Example contract to test event emission: + + pragma solidity >=0.7.0 <0.9.0; + contract Callable { + event Called(); + function Call() public { emit Called(); } + } + */ + + const ( + callableABI = "[{\"anonymous\":false,\"inputs\":[],\"name\":\"Called\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"Call\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]" + callableBin = "6080604052348015600f57600080fd5b5060998061001e6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c806334e2292114602d575b600080fd5b60336035565b005b7f81fab7a4a0aa961db47eefc81f143a5220e8c8495260dd65b1356f1d19d3c7b860405160405180910390a156fea2646970667358221220029436d24f3ac598ceca41d4d712e13ced6d70727f4cdc580667de66d2f51d8b64736f6c63430008010033" + ) var ( require = require.New(t) engine = dummy.NewFaker() @@ -48,7 +49,7 @@ func TestEmitLogsCorrectness(t *testing.T) { signer = types.LatestSigner(gspec.Config) ) - parsed, err := abi.JSON(strings.NewReader(callableAbi)) + parsed, err := abi.JSON(strings.NewReader(callableABI)) require.NoError(err) packedFunction, err := parsed.Pack("Call") @@ -74,6 +75,7 @@ func TestEmitLogsCorrectness(t *testing.T) { chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), DefaultCacheConfig, gspec, engine, vm.Config{}, common.Hash{}, false) require.NoError(err) + defer chain.Stop() // Create Log Subscriber logsCh := make(chan []*types.Log, 10) diff --git a/core/blockchain_repair_test.go b/core/blockchain_repair_test.go index b649fee16b..4bd07e83ee 100644 --- a/core/blockchain_repair_test.go +++ b/core/blockchain_repair_test.go @@ -537,6 +537,7 @@ func testRepair(t *testing.T, tt *rewindTest, snapshots bool) { if err != nil { t.Fatalf("Failed to create chain: %v", err) } + defer chain.Stop() lastAcceptedHash := chain.GetBlockByNumber(0).Hash() // If sidechain blocks are needed, make a light chain and import it diff --git a/core/blockchain_test.go b/core/blockchain_test.go index 2cfb9886a8..4b18b2dfa0 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -355,11 +355,14 @@ func TestBlockChainOfflinePruningUngracefulShutdown(t *testing.T) { return blockchain, nil } - tempDir := t.TempDir() if err := blockchain.CleanBlockRootsAboveLastAccepted(); err != nil { return nil, err } + // get the target root to prune to before stopping the blockchain + targetRoot := blockchain.LastAcceptedBlock().Root() + blockchain.Stop() + tempDir := t.TempDir() prunerConfig := pruner.Config{ Datadir: tempDir, BloomSize: 256, @@ -371,7 +374,6 @@ func TestBlockChainOfflinePruningUngracefulShutdown(t *testing.T) { return nil, fmt.Errorf("offline pruning failed (%s, %d): %w", tempDir, 256, err) } - targetRoot := blockchain.LastAcceptedBlock().Root() if err := pruner.Prune(targetRoot); err != nil { return nil, fmt.Errorf("failed to prune blockchain with target root: %s due to: %w", targetRoot, err) } @@ -379,8 +381,8 @@ func TestBlockChainOfflinePruningUngracefulShutdown(t *testing.T) { return createBlockChain(db, pruningConfig, gspec, lastAcceptedHash) } for _, tt := range tests { + tt := tt t.Run(tt.Name, func(t *testing.T) { - tt := tt t.Parallel() tt.testFunc(t, create) }) @@ -465,6 +467,7 @@ func testRepopulateMissingTriesParallel(t *testing.T, parallelism int) { if err != nil { t.Fatal(err) } + defer blockchain.Stop() for _, block := range chain { if !blockchain.HasState(block.Root()) { @@ -951,6 +954,7 @@ func testCreateThenDelete(t *testing.T, config *params.ChainConfig) { if err != nil { t.Fatalf("failed to create tester chain: %v", err) } + defer chain.Stop() // Import the blocks for _, block := range blocks { if _, err := chain.InsertChain([]*types.Block{block}); err != nil { @@ -1037,6 +1041,7 @@ func TestTransientStorageReset(t *testing.T) { if err != nil { t.Fatalf("failed to create tester chain: %v", err) } + defer chain.Stop() // Import the blocks if _, err := chain.InsertChain(blocks); err != nil { t.Fatalf("failed to insert into chain: %v", err) @@ -1125,6 +1130,7 @@ func TestEIP3651(t *testing.T) { if err != nil { t.Fatalf("failed to create tester chain: %v", err) } + defer chain.Stop() if n, err := chain.InsertChain(blocks); err != nil { t.Fatalf("block %d: failed to insert into chain: %v", n, err) } diff --git a/core/headerchain_test.go b/core/headerchain_test.go index e92f45cd1f..3b349be043 100644 --- a/core/headerchain_test.go +++ b/core/headerchain_test.go @@ -84,6 +84,8 @@ func TestHeaderInsertion(t *testing.T) { if err != nil { t.Fatal(err) } + defer chain.Stop() + // chain A: G->A1->A2...A128 chainA, _, _ := GenerateChain(params.TestChainConfig, types.NewBlockWithHeader(genesis.Header()), dummy.NewFaker(), db, 128, 10, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{0: byte(10), 19: byte(i)}) diff --git a/core/main_test.go b/core/main_test.go new file mode 100644 index 0000000000..1d0e299f4a --- /dev/null +++ b/core/main_test.go @@ -0,0 +1,22 @@ +// (c) 2023, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package core + +import ( + "testing" + + "go.uber.org/goleak" +) + +// TestMain uses goleak to verify tests in this package do not leak unexpected +// goroutines. +func TestMain(m *testing.M) { + opts := []goleak.Option{ + // No good way to shut down these goroutines: + goleak.IgnoreTopFunction("github.com/ava-labs/coreth/core/state/snapshot.(*diskLayer).generate"), + goleak.IgnoreTopFunction("github.com/ava-labs/coreth/metrics.(*meterArbiter).tick"), + goleak.IgnoreTopFunction("github.com/syndtr/goleveldb/leveldb.(*DB).mpoolDrain"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/core/state_processor_test.go b/core/state_processor_test.go index 1415d37d41..f95513c7f0 100644 --- a/core/state_processor_test.go +++ b/core/state_processor_test.go @@ -368,7 +368,6 @@ func TestStateProcessorErrors(t *testing.T) { }{ { // ErrMaxInitCodeSizeExceeded txs: []*types.Transaction{ - mkDynamicCreationTx(0, 500000, common.Big0, big.NewInt(params.ApricotPhase3InitialBaseFee), tooBigInitCode[:]), }, want: "could not apply tx 0 [0x18a05f40f29ff16d5287f6f88b21c9f3c7fbc268f707251144996294552c4cd6]: max initcode size exceeded: code size 49153 limit 49152", diff --git a/core/test_blockchain.go b/core/test_blockchain.go index cab51a3609..c82004f76d 100644 --- a/core/test_blockchain.go +++ b/core/test_blockchain.go @@ -128,6 +128,7 @@ func checkBlockChainState( if err != nil { t.Fatalf("Failed to create new blockchain instance: %s", err) } + defer newBlockChain.Stop() for i := uint64(1); i <= lastAcceptedBlock.NumberU64(); i++ { block := bc.GetBlockByNumber(i) diff --git a/core/txpool/txpool_test.go b/core/txpool/txpool_test.go index 00e53b5d6b..bd888f6204 100644 --- a/core/txpool/txpool_test.go +++ b/core/txpool/txpool_test.go @@ -134,7 +134,8 @@ func (bc *testBlockChain) SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) } func (bc *testBlockChain) SenderCacher() *core.TxSenderCacher { - return core.NewTxSenderCacher(1) + // Zero threads avoids starting goroutines. + return core.NewTxSenderCacher(0) } func (bc *testBlockChain) AdminController() admin.AdminController { diff --git a/core/vm/runtime/runtime_test.go b/core/vm/runtime/runtime_test.go index 83809958c5..237cd75b2c 100644 --- a/core/vm/runtime/runtime_test.go +++ b/core/vm/runtime/runtime_test.go @@ -708,30 +708,30 @@ func TestColdAccountAccessCost(t *testing.T) { func TestRuntimeJSTracer(t *testing.T) { jsTracers := []string{ `{enters: 0, exits: 0, enterGas: 0, gasUsed: 0, steps:0, - step: function() { this.steps++}, - fault: function() {}, - result: function() { - return [this.enters, this.exits,this.enterGas,this.gasUsed, this.steps].join(",") - }, - enter: function(frame) { - this.enters++; + step: function() { this.steps++}, + fault: function() {}, + result: function() { + return [this.enters, this.exits,this.enterGas,this.gasUsed, this.steps].join(",") + }, + enter: function(frame) { + this.enters++; this.enterGas = frame.getGas(); - }, - exit: function(res) { - this.exits++; + }, + exit: function(res) { + this.exits++; this.gasUsed = res.getGasUsed(); }}`, `{enters: 0, exits: 0, enterGas: 0, gasUsed: 0, steps:0, - fault: function() {}, - result: function() { - return [this.enters, this.exits,this.enterGas,this.gasUsed, this.steps].join(",") - }, - enter: function(frame) { - this.enters++; + fault: function() {}, + result: function() { + return [this.enters, this.exits,this.enterGas,this.gasUsed, this.steps].join(",") + }, + enter: function(frame) { + this.enters++; this.enterGas = frame.getGas(); - }, - exit: function(res) { - this.exits++; + }, + exit: function(res) { + this.exits++; this.gasUsed = res.getGasUsed(); }}`} tests := []struct { diff --git a/eth/tracers/internal/tracetest/calltrace_test.go b/eth/tracers/internal/tracetest/calltrace_test.go index 1df558f11b..eeee9b119c 100644 --- a/eth/tracers/internal/tracetest/calltrace_test.go +++ b/eth/tracers/internal/tracetest/calltrace_test.go @@ -44,7 +44,6 @@ import ( "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/crypto" "github.com/ethereum/go-ethereum/rlp" ) @@ -270,75 +269,122 @@ func benchTracer(tracerName string, test *callTracerTest, b *testing.B) { } } -// 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: 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), +func TestInternals(t *testing.T) { + var ( + to = common.HexToAddress("0x00000000000000000000000000000000deadbeef") + origin = common.HexToAddress("0x00000000000000000000000000000000feed") + 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: 5, + Difficulty: big.NewInt(0x30000), + GasLimit: uint64(6000000), + BaseFee: common.Big0, + } + ) + mkTracer := func(name string, cfg json.RawMessage) tracers.Tracer { + tr, err := tracers.DefaultDirectory.New(name, nil, cfg) + if err != nil { + t.Fatalf("failed to create call tracer: %v", err) + } + return tr } - var alloc = core.GenesisAlloc{ - to: core.GenesisAccount{ - Nonce: 1, - Code: code, + + for _, tc := range []struct { + name string + code []byte + tracer tracers.Tracer + want string + }{ + { + // 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 + name: "ZeroValueToNotExitCall", + 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), + }, + tracer: mkTracer("callTracer", nil), + want: `{"from":"0x000000000000000000000000000000000000feed","gas":"0x7148","gasUsed":"0x54d8","to":"0x00000000000000000000000000000000deadbeef","input":"0x","calls":[{"from":"0x00000000000000000000000000000000deadbeef","gas":"0x6cbf","gasUsed":"0x0","to":"0x00000000000000000000000000000000000000ff","input":"0x","value":"0x0","type":"CALL"}],"value":"0x0","type":"CALL"}`, }, - origin: core.GenesisAccount{ - Nonce: 0, - Balance: big.NewInt(500000000000000), + { + name: "Stack depletion in LOG0", + code: []byte{byte(vm.LOG3)}, + tracer: mkTracer("callTracer", json.RawMessage(`{ "withLog": true }`)), + want: `{"from":"0x000000000000000000000000000000000000feed","gas":"0x7148","gasUsed":"0xc350","to":"0x00000000000000000000000000000000deadbeef","input":"0x","error":"stack underflow (0 \u003c=\u003e 5)","value":"0x0","type":"CALL"}`, }, - } - _, statedb := tests.MakePreState(rawdb.NewMemoryDatabase(), alloc, false) - // Create the tracer, the EVM environment and run it - tracer, err := tracers.DefaultDirectory.New("callTracer", nil, nil) - if err != nil { - t.Fatalf("failed to create call tracer: %v", err) - } - evm := vm.NewEVM(context, txContext, statedb, params.AvalancheMainnetChainConfig, vm.Config{Debug: true, Tracer: tracer}) - msg, err := core.TransactionToMessage(tx, 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) - } - wantStr := `{"from":"0x682a80a6f560eec50d54e63cbeda1c324c5f8d1b","gas":"0x7148","gasUsed":"0x54d8","to":"0x00000000000000000000000000000000deadbeef","input":"0x","calls":[{"from":"0x00000000000000000000000000000000deadbeef","gas":"0x6cbf","gasUsed":"0x0","to":"0x00000000000000000000000000000000000000ff","input":"0x","value":"0x0","type":"CALL"}],"value":"0x0","type":"CALL"}` - if string(res) != wantStr { - t.Fatalf("trace mismatch\n have: %v\n want: %v\n", string(res), wantStr) + { + name: "Mem expansion in LOG0", + code: []byte{ + byte(vm.PUSH1), 0x1, + byte(vm.PUSH1), 0x0, + byte(vm.MSTORE), + byte(vm.PUSH1), 0xff, + byte(vm.PUSH1), 0x0, + byte(vm.LOG0), + }, + tracer: mkTracer("callTracer", json.RawMessage(`{ "withLog": true }`)), + want: `{"from":"0x000000000000000000000000000000000000feed","gas":"0x7148","gasUsed":"0x5b9e","to":"0x00000000000000000000000000000000deadbeef","input":"0x","logs":[{"address":"0x00000000000000000000000000000000deadbeef","topics":[],"data":"0x000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"}],"value":"0x0","type":"CALL"}`, + }, + { + // Leads to OOM on the prestate tracer + name: "Prestate-tracer - mem expansion in CREATE2", + code: []byte{ + byte(vm.PUSH1), 0x1, + byte(vm.PUSH1), 0x0, + byte(vm.MSTORE), + byte(vm.PUSH1), 0x1, + byte(vm.PUSH5), 0xff, 0xff, 0xff, 0xff, 0xff, + byte(vm.PUSH1), 0x1, + byte(vm.PUSH1), 0x0, + byte(vm.CREATE2), + byte(vm.PUSH1), 0xff, + byte(vm.PUSH1), 0x0, + byte(vm.LOG0), + }, + tracer: mkTracer("prestateTracer", json.RawMessage(`{ "withLog": true }`)), + want: `{"0x0000000000000000000000000000000000000000":{"balance":"0x0"},"0x000000000000000000000000000000000000feed":{"balance":"0x1c6bf52640350"},"0x00000000000000000000000000000000deadbeef":{"balance":"0x0","code":"0x6001600052600164ffffffffff60016000f560ff6000a0"}}`, + }, + } { + _, statedb := tests.MakePreState(rawdb.NewMemoryDatabase(), + core.GenesisAlloc{ + to: core.GenesisAccount{ + Code: tc.code, + }, + origin: core.GenesisAccount{ + Balance: big.NewInt(500000000000000), + }, + }, false) + evm := vm.NewEVM(context, txContext, statedb, params.TestLaunchConfig, vm.Config{Debug: true, Tracer: tc.tracer}) + msg := &core.Message{ + To: &to, + From: origin, + Value: big.NewInt(0), + GasLimit: 50000, + GasPrice: big.NewInt(0), + GasFeeCap: big.NewInt(0), + GasTipCap: big.NewInt(0), + SkipAccountChecks: false, + } + st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(msg.GasLimit)) + if _, err := st.TransitionDb(); err != nil { + t.Fatalf("test %v: failed to execute transaction: %v", tc.name, err) + } + // Retrieve the trace result and compare against the expected + res, err := tc.tracer.GetResult() + if err != nil { + t.Fatalf("test %v: failed to retrieve trace result: %v", tc.name, err) + } + if string(res) != tc.want { + t.Fatalf("test %v: trace mismatch\n have: %v\n want: %v\n", tc.name, string(res), tc.want) + } } } diff --git a/eth/tracers/internal/tracetest/prestate_test.go b/eth/tracers/internal/tracetest/prestate_test.go index 3bcc04d278..d6d70793eb 100644 --- a/eth/tracers/internal/tracetest/prestate_test.go +++ b/eth/tracers/internal/tracetest/prestate_test.go @@ -74,6 +74,10 @@ func TestPrestateWithDiffModeTracer(t *testing.T) { testPrestateDiffTracer("prestateTracer", "prestate_tracer_with_diff_mode", t) } +func TestPrestateWithDiffModeANTTracer(t *testing.T) { + testPrestateDiffTracer("prestateTracer", "prestate_tracer_ant", t) +} + func testPrestateDiffTracer(tracerName string, dirPath string, t *testing.T) { files, err := os.ReadDir(filepath.Join("testdata", dirPath)) if err != nil { @@ -110,14 +114,16 @@ func testPrestateDiffTracer(tracerName string, dirPath string, t *testing.T) { GasPrice: tx.GasPrice(), } context = vm.BlockContext{ - CanTransfer: core.CanTransfer, - Transfer: core.Transfer, - Coinbase: test.Context.Miner, - BlockNumber: blockNumber, - Time: uint64(test.Context.Time), - Difficulty: (*big.Int)(test.Context.Difficulty), - GasLimit: uint64(test.Context.GasLimit), - BaseFee: test.Genesis.BaseFee, + CanTransfer: core.CanTransfer, + CanTransferMC: core.CanTransferMC, + Transfer: core.Transfer, + TransferMultiCoin: core.TransferMultiCoin, + Coinbase: test.Context.Miner, + BlockNumber: blockNumber, + Time: uint64(test.Context.Time), + Difficulty: (*big.Int)(test.Context.Difficulty), + GasLimit: uint64(test.Context.GasLimit), + BaseFee: test.Genesis.BaseFee, } _, statedb = tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc, false) ) diff --git a/eth/tracers/internal/tracetest/testdata/prestate_tracer_ant/sload.json b/eth/tracers/internal/tracetest/testdata/prestate_tracer_ant/sload.json new file mode 100644 index 0000000000..eb91e878f7 --- /dev/null +++ b/eth/tracers/internal/tracetest/testdata/prestate_tracer_ant/sload.json @@ -0,0 +1,75 @@ +{ + "context": { + "difficulty": "3755480783", + "gasLimit": "5401723", + "miner": "0xd049bfd667cb46aa3ef5df0da3e57db3be39e511", + "number": "2294702", + "timestamp": "1513676146" + }, + "genesis": { + "alloc": { + "0xe296F389F90f3cF5EdeFb63690eb7a6257B69203": { + "balance": "0xcf3e0938579f00000", + "code": "0x", + "nonce": "0", + "storage": {} + }, + "0x7dc9c9730689ff0b0fd506c67db815f12d90a448": { + "balance": "0x0", + "code": "0x600154", + "nonce": "0", + "storage": {} + } + }, + "config": { + "chainId": 1, + "homesteadBlock": 0, + "eip150Block": 0, + "eip150Hash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "eip155Block": 0, + "eip158Block": 0, + "byzantiumBlock": 0, + "constantinopleBlock": 0, + "petersburgBlock": 0, + "istanbulBlock": 0, + "muirGlacierBlock": 0, + "apricotPhase1BlockTimestamp": 0, + "apricotPhase2BlockTimestamp": 0 + }, + "difficulty": "3757315409", + "extraData": "0x566961425443", + "gasLimit": "5406414", + "hash": "0xae107f592eebdd9ff8d6ba00363676096e6afb0e1007a7d3d0af88173077378d", + "miner": "0xd049bfd667cb46aa3ef5df0da3e57db3be39e511", + "mixHash": "0xc927aa05a38bc3de864e95c33b3ae559d3f39c4ccd51cef6f113f9c50ba0caf1", + "nonce": "0x93363bbd2c95f410", + "number": "2294701", + "stateRoot": "0x6b6737d5bde8058990483e915866bd1578014baeff57bd5e4ed228a2bfad635c", + "timestamp": "1513676127", + "totalDifficulty": "7160808139332585" + }, + "input": "0xf8ba808534630b8a00834c4b4094010000000000000000000000000000000000000280b8547dc9c9730689ff0b0fd506c67db815f12d90a4480000000000000000000000007dc9c9730689ff0b0fd506c67db815f12d90a448000000000000000000000000000000000000000000000000000000000000000026a0b441b733b17156dee3ff2107978a4238bfcb5874a36d008a1bcc5b22617a3232a04212c6b26069068d604be380ace25133dab677f9d18c2010d6f92cc7491fef86", + "tracerConfig": { + "onlyTopCall": false, + "diffMode": true + }, + "result": { + "post": { + "0xd049bfd667cb46aa3ef5df0da3e57db3be39e511": { + "balance": "0x231b6c08a01600" + }, + "0xe296f389f90f3cf5edefb63690eb7a6257b69203": { + "balance": "0xcf3bd7819714fea00", + "nonce": 1 + } + }, + "pre": { + "0xd049bfd667cb46aa3ef5df0da3e57db3be39e511": { + "balance": "0x0" + }, + "0xe296f389f90f3cf5edefb63690eb7a6257b69203": { + "balance": "0xcf3e0938579f00000" + } + } + } +} diff --git a/eth/tracers/js/goja.go b/eth/tracers/js/goja.go index 3b1cee1898..25892bdb55 100644 --- a/eth/tracers/js/goja.go +++ b/eth/tracers/js/goja.go @@ -42,10 +42,6 @@ import ( "github.com/ethereum/go-ethereum/crypto" ) -const ( - memoryPadLimit = 1024 * 1024 -) - var assetTracers = make(map[string]string) // init retrieves the JavaScript transaction tracers included in go-ethereum. @@ -581,14 +577,10 @@ func (mo *memoryObj) slice(begin, end int64) ([]byte, error) { if end < begin || begin < 0 { return nil, fmt.Errorf("tracer accessed out of bound memory: offset %d, end %d", begin, end) } - mlen := mo.memory.Len() - if end-int64(mlen) > memoryPadLimit { - return nil, fmt.Errorf("tracer reached limit for padding memory slice: end %d, memorySize %d", end, mlen) + slice, err := tracers.GetMemoryCopyPadded(mo.memory, begin, end-begin) + if err != nil { + return nil, err } - slice := make([]byte, end-begin) - end = min(end, int64(mo.memory.Len())) - ptr := mo.memory.GetPtr(begin, end-begin) - copy(slice[:], ptr[:]) return slice, nil } @@ -969,10 +961,3 @@ func (l *steplog) setupObject() *goja.Object { o.Set("contract", l.contract.setupObject()) return o } - -func min(a, b int64) int64 { - if a < b { - return a - } - return b -} diff --git a/eth/tracers/js/tracer_test.go b/eth/tracers/js/tracer_test.go index 9edc5698ab..f91625e8c2 100644 --- a/eth/tracers/js/tracer_test.go +++ b/eth/tracers/js/tracer_test.go @@ -160,12 +160,12 @@ func TestTracer(t *testing.T) { }, { code: "{res: [], step: function(log) { if (log.op.toString() === 'STOP') { this.res.push(log.memory.slice(5, 1025 * 1024)) } }, fault: function() {}, result: function() { return this.res }}", want: "", - fail: "tracer reached limit for padding memory slice: end 1049600, memorySize 32 at step (:1:83(20)) in server-side tracer function 'step'", + fail: "reached limit for padding memory slice: 1049568 at step (:1:83(20)) in server-side tracer function 'step'", contract: []byte{byte(vm.PUSH1), byte(0xff), byte(vm.PUSH1), byte(0x00), byte(vm.MSTORE8), byte(vm.STOP)}, }, } { if have, err := execTracer(tt.code, tt.contract); tt.want != string(have) || tt.fail != err { - t.Errorf("testcase %d: expected return value to be '%s' got '%s', error to be '%s' got '%s'\n\tcode: %v", i, tt.want, string(have), tt.fail, err, tt.code) + t.Errorf("testcase %d: expected return value to be \n'%s'\n\tgot\n'%s'\nerror to be\n'%s'\n\tgot\n'%s'\n\tcode: %v", i, tt.want, string(have), tt.fail, err, tt.code) } } } diff --git a/eth/tracers/native/call.go b/eth/tracers/native/call.go index 92394f1892..e9ff3d1fdd 100644 --- a/eth/tracers/native/call.go +++ b/eth/tracers/native/call.go @@ -159,6 +159,10 @@ func (t *callTracer) CaptureEnd(output []byte, gasUsed uint64, 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) { + // skip if the previous op caused an error + if err != nil { + return + } // Only logs need to be captured via opcode processing if !t.config.WithLog { return @@ -187,7 +191,12 @@ func (t *callTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, sco topics[i] = common.Hash(topic.Bytes32()) } - data := scope.Memory.GetCopy(int64(mStart.Uint64()), int64(mSize.Uint64())) + data, err := tracers.GetMemoryCopyPadded(scope.Memory, int64(mStart.Uint64()), int64(mSize.Uint64())) + if err != nil { + // mSize was unrealistically large + return + } + log := callLog{Address: scope.Contract.Address(), Topics: topics, Data: hexutil.Bytes(data)} t.callstack[len(t.callstack)-1].Logs = append(t.callstack[len(t.callstack)-1].Logs, log) } diff --git a/eth/tracers/native/prestate.go b/eth/tracers/native/prestate.go index 691c2f93fa..23774a9087 100644 --- a/eth/tracers/native/prestate.go +++ b/eth/tracers/native/prestate.go @@ -143,6 +143,9 @@ func (t *prestateTracer) CaptureEnd(output []byte, gasUsed uint64, err error) { // CaptureState implements the EVMLogger interface to trace a single step of VM execution. func (t *prestateTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) { + if err != nil { + return + } stack := scope.Stack stackData := stack.Data() stackLen := len(stackData) @@ -288,6 +291,14 @@ func (t *prestateTracer) lookupAccount(addr common.Address) { // it to the prestate of the given contract. It assumes `lookupAccount` // has been performed on the contract before. func (t *prestateTracer) lookupStorage(addr common.Address, key common.Hash) { + // lookupStorage assumes that lookupAccount has already been called. + // This assumption is violated for some historical blocks by the NativeAssetCall + // precompile. To fix this, we perform an extra call to lookupAccount here to ensure + // that the pre-state account is populated before attempting to read from the Storage + // map. When the invariant is maintained properly (since de-activation of the precompile), + // lookupAccount is a no-op. When the invariant is broken by the precompile, this avoids + // the panic and correctly captures the account prestate before the next opcode is executed. + t.lookupAccount(addr) if _, ok := t.pre[addr].Storage[key]; ok { return } diff --git a/eth/tracers/tracers.go b/eth/tracers/tracers.go index dc1d2456a0..6fccae3c50 100644 --- a/eth/tracers/tracers.go +++ b/eth/tracers/tracers.go @@ -19,6 +19,7 @@ package tracers import ( "encoding/json" + "fmt" "math/big" "github.com/ava-labs/coreth/core/vm" @@ -95,3 +96,27 @@ func (d *directory) IsJS(name string) bool { // JS eval will execute JS code return true } + +const ( + memoryPadLimit = 1024 * 1024 +) + +// GetMemoryCopyPadded returns offset + size as a new slice. +// It zero-pads the slice if it extends beyond memory bounds. +func GetMemoryCopyPadded(m *vm.Memory, offset, size int64) ([]byte, error) { + if offset < 0 || size < 0 { + return nil, fmt.Errorf("offset or size must not be negative") + } + if int(offset+size) < m.Len() { // slice fully inside memory + return m.GetCopy(offset, size), nil + } + paddingNeeded := int(offset+size) - m.Len() + if paddingNeeded > memoryPadLimit { + return nil, fmt.Errorf("reached limit for padding memory slice: %d", paddingNeeded) + } + cpy := make([]byte, size) + if overlap := int64(m.Len()) - offset; overlap > 0 { + copy(cpy, m.GetPtr(offset, overlap)) + } + return cpy, nil +} diff --git a/eth/tracers/tracers_test.go b/eth/tracers/tracers_test.go index a33627e6f0..a3c84d0d25 100644 --- a/eth/tracers/tracers_test.go +++ b/eth/tracers/tracers_test.go @@ -119,3 +119,41 @@ func BenchmarkTransactionTrace(b *testing.B) { tracer.Reset() } } + +func TestMemCopying(t *testing.T) { + for i, tc := range []struct { + memsize int64 + offset int64 + size int64 + wantErr string + wantSize int + }{ + {0, 0, 100, "", 100}, // Should pad up to 100 + {0, 100, 0, "", 0}, // No need to pad (0 size) + {100, 50, 100, "", 100}, // Should pad 100-150 + {100, 50, 5, "", 5}, // Wanted range fully within memory + {100, -50, 0, "offset or size must not be negative", 0}, // Errror + {0, 1, 1024*1024 + 1, "reached limit for padding memory slice: 1048578", 0}, // Errror + {10, 0, 1024*1024 + 100, "reached limit for padding memory slice: 1048666", 0}, // Errror + + } { + mem := vm.NewMemory() + mem.Resize(uint64(tc.memsize)) + cpy, err := GetMemoryCopyPadded(mem, tc.offset, tc.size) + if want := tc.wantErr; want != "" { + if err == nil { + t.Fatalf("test %d: want '%v' have no error", i, want) + } + if have := err.Error(); want != have { + t.Fatalf("test %d: want '%v' have '%v'", i, want, have) + } + continue + } + if err != nil { + t.Fatalf("test %d: unexpected error: %v", i, err) + } + if want, have := tc.wantSize, len(cpy); have != want { + t.Fatalf("test %d: want %v have %v", i, want, have) + } + } +} diff --git a/go.mod b/go.mod index 7bbd0fa002..35f5de179d 100644 --- a/go.mod +++ b/go.mod @@ -4,12 +4,12 @@ go 1.19 require ( github.com/VictoriaMetrics/fastcache v1.10.0 - github.com/ava-labs/avalanchego v1.10.3 + github.com/ava-labs/avalanchego v1.10.5-rc.1 github.com/cespare/cp v0.1.0 github.com/cockroachdb/pebble v0.0.0-20230209160836-829675f94811 github.com/davecgh/go-spew v1.1.1 github.com/deckarep/golang-set/v2 v2.1.0 - github.com/dop251/goja v0.0.0-20230122112309-96b1610dd4f7 + github.com/dop251/goja v0.0.0-20230605162241-28ee0ee714f3 github.com/ethereum/go-ethereum v1.11.4 github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 github.com/fsnotify/fsnotify v1.6.0 @@ -37,7 +37,9 @@ require ( github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a github.com/tyler-smith/go-bip39 v1.1.0 github.com/urfave/cli/v2 v2.17.2-0.20221006022127-8f469abc00aa + go.uber.org/goleak v1.2.1 golang.org/x/crypto v0.12.0 + golang.org/x/exp v0.0.0-20230206171751-46f607a40771 golang.org/x/sync v0.1.0 golang.org/x/sys v0.11.0 golang.org/x/text v0.12.0 @@ -69,8 +71,9 @@ require ( github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt/v4 v4.3.0 // indirect github.com/golang/mock v1.6.0 // indirect - github.com/golang/protobuf v1.5.2 // indirect + github.com/golang/protobuf v1.5.3 // indirect github.com/golang/snappy v0.0.4 // indirect + github.com/google/pprof v0.0.0-20230207041349-798e818bf904 // indirect github.com/graph-gophers/graphql-go v1.3.0 // indirect github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.12.0 // indirect @@ -120,12 +123,11 @@ require ( go.uber.org/atomic v1.10.0 // indirect go.uber.org/multierr v1.8.0 // indirect go.uber.org/zap v1.24.0 // indirect - golang.org/x/exp v0.0.0-20230206171751-46f607a40771 // indirect golang.org/x/net v0.14.0 // indirect golang.org/x/term v0.11.0 // indirect gonum.org/v1/gonum v0.11.0 // indirect - google.golang.org/genproto v0.0.0-20221027153422-115e99e71e1c // indirect - google.golang.org/grpc v1.50.1 // indirect + google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 // indirect + google.golang.org/grpc v1.55.0 // indirect google.golang.org/protobuf v1.30.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect @@ -134,6 +136,6 @@ require ( gopkg.in/yaml.v3 v3.0.1 // indirect ) -replace github.com/ava-labs/avalanchego => github.com/chain4travel/caminogo v1.1.3-rc1 +replace github.com/ava-labs/avalanchego => github.com/chain4travel/caminogo v1.1.5-rc0 replace github.com/ava-labs/avalanche-ledger-go => github.com/chain4travel/camino-ledger-go v0.0.13-c4t diff --git a/go.sum b/go.sum index e952b0d95e..a4a87aad67 100644 --- a/go.sum +++ b/go.sum @@ -108,11 +108,14 @@ github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/chain4travel/caminogo v1.1.3-rc1 h1:gXz7h7Z1VXfiqRKCKpTMU6XV8kj9L2ngJ3q/AMNbe+w= -github.com/chain4travel/caminogo v1.1.3-rc1/go.mod h1:fxqP4eKa1yPtPKV5sPbpRWFJdvRUEzrgwl0El0xM2LI= +github.com/chain4travel/caminogo v1.1.5-rc0 h1:ePRrEdvncMJnkzrRX7SGy+rHBGsw1fLT1K0twCTrXDc= +github.com/chain4travel/caminogo v1.1.5-rc0/go.mod h1:pACeJpTq/XSYDgbViYIeD8uv/6crUVTR5PoBgOuUgDo= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/logex v1.2.0/go.mod h1:9+9sk7u7pGNWYMkh0hdiL++6OeibzJccyQU4p4MedaY= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/readline v1.5.0/go.mod h1:x22KAscuvRqlLoK9CsoYsmxoXZMMFVyOl86cAH8qUic= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/chzyer/test v0.0.0-20210722231415-061457976a23/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= @@ -167,8 +170,8 @@ github.com/dlclark/regexp2 v1.7.0 h1:7lJfhqlPssTb1WQx4yvTHN0uElPEv52sbaECrAQxjAo github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/docker/docker v1.6.2 h1:HlFGsy+9/xrgMmhmN+NGhCc5SHGJ7I+kHosRR1xc/aI= github.com/dop251/goja v0.0.0-20211022113120-dc8c55024d06/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk= -github.com/dop251/goja v0.0.0-20230122112309-96b1610dd4f7 h1:kgvzE5wLsLa7XKfV85VZl40QXaMCaeFtHpPwJ8fhotY= -github.com/dop251/goja v0.0.0-20230122112309-96b1610dd4f7/go.mod h1:yRkwfj0CBpOGre+TwBsqPV0IH0Pk73e4PXJOeNDboGs= +github.com/dop251/goja v0.0.0-20230605162241-28ee0ee714f3 h1:+3HCtB74++ClLy8GgjUQYeC8R4ILzVcIe8+5edAJJnE= +github.com/dop251/goja v0.0.0-20230605162241-28ee0ee714f3/go.mod h1:QMWlm50DNe14hD7t24KEqZuUdC9sOTy8W6XbCU1mlw4= github.com/dop251/goja_nodejs v0.0.0-20210225215109-d91c329300e7/go.mod h1:hn7BA7c8pLvoGndExHudxTDKZ84Pyvv+90pbBjbTz0Y= github.com/dop251/goja_nodejs v0.0.0-20211022123610-8dd9abb0616d/go.mod h1:DngW8aVqWbuLRMHItjPUyqdj+HWPvnQe8V8y1nDpIbM= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= @@ -259,8 +262,8 @@ github.com/golang-jwt/jwt/v4 v4.3.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzw github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/geo v0.0.0-20190916061304-5b978397cfec/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/glog v1.0.0 h1:nfP3RFugxnNRyKgeWd4oI1nYvXpxrx8ck8ZrcizshdQ= github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= +github.com/golang/glog v1.1.0 h1:/d3pCKDPWNnvIWe0vVUpNP32qc8U3PDVxySP/y360qE= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -288,8 +291,9 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= @@ -326,6 +330,8 @@ github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20230207041349-798e818bf904 h1:4/hN5RUoecvl+RmJRE2YxKWtnnQls6rQjjW5oV7qg2U= +github.com/google/pprof v0.0.0-20230207041349-798e818bf904/go.mod h1:uglQLonpP8qtYCYyzA+8c/9qtqgA3qsXGYqCPKARAFg= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= @@ -370,6 +376,7 @@ github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3 github.com/hydrogen18/memlistener v0.0.0-20200120041712-dcc25e7acd91/go.mod h1:qEIFzExnS6016fRpRfxrExeVn2gbClQA99gQhnIcdhE= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w= github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/influxdata/flux v0.65.1/go.mod h1:J754/zds0vvpfwuq7Gc2wRdVwEodfpCFM7mYlOw2LqY= @@ -664,6 +671,7 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg= github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= @@ -693,7 +701,8 @@ go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= -go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= +go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= +go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8= go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= @@ -759,6 +768,7 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -805,6 +815,7 @@ golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT golang.org/x/net v0.0.0-20211008194852-3b03d305991f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14= golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -828,6 +839,7 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -893,9 +905,11 @@ golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220405052023-b1e9470b6e64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= @@ -914,6 +928,7 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc= golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -982,6 +997,7 @@ golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -1063,8 +1079,8 @@ google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20221027153422-115e99e71e1c h1:QgY/XxIAIeccR+Ca/rDdKubLIU9rcJ3xfy1DC/Wd2Oo= -google.golang.org/genproto v0.0.0-20221027153422-115e99e71e1c/go.mod h1:CGI5F/G+E5bKwmfYo09AXuVN4dD894kIKUFmVbP2/Fo= +google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 h1:DdoeryqhaXp1LtT/emMP1BRJPHHKFi5akj/nbx/zNTA= +google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= @@ -1087,8 +1103,8 @@ google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAG google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.50.1 h1:DS/BukOZWp8s6p4Dt/tOaJaTQyPyOoCcrjroHuCeLzY= -google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= +google.golang.org/grpc v1.55.0 h1:3Oj82/tFSCeUrRTg/5E/7d/W5A1tj6Ky1ABAuZuv5ag= +google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= diff --git a/plugin/evm/atomic_tx_repository.go b/plugin/evm/atomic_tx_repository.go index 6bb863d5a6..ef2cf0c601 100644 --- a/plugin/evm/atomic_tx_repository.go +++ b/plugin/evm/atomic_tx_repository.go @@ -7,9 +7,10 @@ import ( "encoding/binary" "errors" "fmt" - "sort" "time" + "golang.org/x/exp/slices" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/log" @@ -272,7 +273,9 @@ func (a *atomicTxRepository) write(height uint64, txs []*Tx, bonus bool) error { // with txs initialized from the txID index. copyTxs := make([]*Tx, len(txs)) copy(copyTxs, txs) - sort.Slice(copyTxs, func(i, j int) bool { return copyTxs[i].ID().Hex() < copyTxs[j].ID().Hex() }) + slices.SortFunc(copyTxs, func(i, j *Tx) bool { + return i.Less(j) + }) txs = copyTxs } heightBytes := make([]byte, wrappers.LongLen) @@ -452,6 +455,6 @@ func getAtomicRepositoryRepairHeights(bonusBlocks map[uint64]ids.ID, canonicalBl repairHeights = append(repairHeights, height) } } - sort.Slice(repairHeights, func(i, j int) bool { return repairHeights[i] < repairHeights[j] }) + slices.Sort(repairHeights) return repairHeights } diff --git a/plugin/evm/atomic_tx_repository_test.go b/plugin/evm/atomic_tx_repository_test.go index 614d9c9e94..b17b93d0ba 100644 --- a/plugin/evm/atomic_tx_repository_test.go +++ b/plugin/evm/atomic_tx_repository_test.go @@ -6,9 +6,10 @@ package evm import ( "encoding/binary" "fmt" - "sort" "testing" + "golang.org/x/exp/slices" + "github.com/ava-labs/avalanchego/chains/atomic" "github.com/ava-labs/avalanchego/database" "github.com/ava-labs/avalanchego/database/prefixdb" @@ -98,17 +99,14 @@ func writeTxs(t testing.TB, repo AtomicTxRepository, fromHeight uint64, toHeight // verifyTxs asserts [repo] can find all txs in [txMap] by height and txID func verifyTxs(t testing.TB, repo AtomicTxRepository, txMap map[uint64][]*Tx) { // We should be able to fetch indexed txs by height: - getComparator := func(txs []*Tx) func(int, int) bool { - return func(i, j int) bool { - return txs[i].ID().Hex() < txs[j].ID().Hex() - } - } for height, expectedTxs := range txMap { txs, err := repo.GetByHeight(height) assert.NoErrorf(t, err, "unexpected error on GetByHeight at height=%d", height) assert.Lenf(t, txs, len(expectedTxs), "wrong len of txs at height=%d", height) // txs should be stored in order of txID - sort.Slice(expectedTxs, getComparator(expectedTxs)) + slices.SortFunc(expectedTxs, func(i, j *Tx) bool { + return i.Less(j) + }) txIDs := set.Set[ids.ID]{} for i := 0; i < len(txs); i++ { diff --git a/plugin/evm/export_tx.go b/plugin/evm/export_tx.go index 9130a9d2d0..bf0a1b2af4 100644 --- a/plugin/evm/export_tx.go +++ b/plugin/evm/export_tx.go @@ -15,6 +15,7 @@ import ( "github.com/ava-labs/avalanchego/chains/atomic" "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/snow" + "github.com/ava-labs/avalanchego/utils" "github.com/ava-labs/avalanchego/utils/constants" "github.com/ava-labs/avalanchego/utils/crypto/secp256k1" "github.com/ava-labs/avalanchego/utils/math" @@ -117,7 +118,7 @@ func (utx *UnsignedExportTx) Verify( if !avax.IsSortedTransferableOutputs(utx.ExportedOutputs, Codec) { return errOutputsNotSorted } - if rules.IsApricotPhase1 && !IsSortedAndUniqueEVMInputs(utx.Ins) { + if rules.IsApricotPhase1 && !utils.IsSortedAndUnique(utx.Ins) { return errInputsNotSortedUnique } diff --git a/plugin/evm/import_tx.go b/plugin/evm/import_tx.go index 05ec8e568e..8122da22bf 100644 --- a/plugin/evm/import_tx.go +++ b/plugin/evm/import_tx.go @@ -9,6 +9,8 @@ import ( "fmt" "math/big" + "golang.org/x/exp/slices" + "github.com/ava-labs/coreth/core" "github.com/ava-labs/coreth/core/state" "github.com/ava-labs/coreth/params" @@ -107,16 +109,18 @@ func (utx *UnsignedImportTx) Verify( return errImportNonAVAXInputBanff } } - if !utils.IsSortedAndUniqueSortable(utx.ImportedInputs) { + if !utils.IsSortedAndUnique(utx.ImportedInputs) { return errInputsNotSortedUnique } if rules.IsApricotPhase2 { - if !IsSortedAndUniqueEVMOutputs(utx.Outs) { + if !utils.IsSortedAndUnique(utx.Outs) { return errOutputsNotSortedUnique } } else if rules.IsApricotPhase1 { - if !IsSortedEVMOutputs(utx.Outs) { + if !slices.IsSortedFunc(utx.Outs, func(i, j EVMOutput) bool { + return i.Less(j) + }) { return errOutputsNotSorted } } @@ -454,7 +458,7 @@ func (vm *VM) newImportTxWithUTXOs( return nil, errNoEVMOutputs } - SortEVMOutputs(outs) + utils.Sort(outs) // Create the transaction utx := &UnsignedImportTx{ diff --git a/plugin/evm/import_tx_test.go b/plugin/evm/import_tx_test.go index cb4ee868ab..91f7fc6be1 100644 --- a/plugin/evm/import_tx_test.go +++ b/plugin/evm/import_tx_test.go @@ -129,7 +129,7 @@ func TestImportTxVerify(t *testing.T) { // Sort the inputs and outputs to ensure the transaction is canonical utils.Sort(importTx.ImportedInputs) - SortEVMOutputs(importTx.Outs) + utils.Sort(importTx.Outs) tests := map[string]atomicTxVerifyTest{ "nil tx": { diff --git a/plugin/evm/mempool_atomic_gossiping_test.go b/plugin/evm/mempool_atomic_gossiping_test.go index 84a9dd10f5..a0f82a8c01 100644 --- a/plugin/evm/mempool_atomic_gossiping_test.go +++ b/plugin/evm/mempool_atomic_gossiping_test.go @@ -168,7 +168,7 @@ func createImportTx(t *testing.T, vm *VM, txID ids.ID, feeAmount uint64) *Tx { // Sort the inputs and outputs to ensure the transaction is canonical utils.Sort(importTx.ImportedInputs) - SortEVMOutputs(importTx.Outs) + utils.Sort(importTx.Outs) tx := &Tx{UnsignedAtomicTx: importTx} // Sign with the correct key diff --git a/plugin/evm/tx.go b/plugin/evm/tx.go index 442ad7b0e6..1f0ba6112b 100644 --- a/plugin/evm/tx.go +++ b/plugin/evm/tx.go @@ -10,6 +10,8 @@ import ( "math/big" "sort" + "golang.org/x/exp/slices" + "github.com/ethereum/go-ethereum/common" "github.com/ava-labs/coreth/core/state" @@ -19,7 +21,6 @@ import ( "github.com/ava-labs/avalanchego/codec" "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/snow" - "github.com/ava-labs/avalanchego/utils" "github.com/ava-labs/avalanchego/utils/crypto/secp256k1" "github.com/ava-labs/avalanchego/utils/hashing" "github.com/ava-labs/avalanchego/utils/set" @@ -55,6 +56,14 @@ type EVMOutput struct { AssetID ids.ID `serialize:"true" json:"assetID"` } +func (o EVMOutput) Less(other EVMOutput) bool { + addrComp := bytes.Compare(o.Address.Bytes(), other.Address.Bytes()) + if addrComp != 0 { + return addrComp < 0 + } + return bytes.Compare(o.AssetID[:], other.AssetID[:]) < 0 +} + // EVMInput defines an input created from the EVM state to fund export transactions type EVMInput struct { Address common.Address `serialize:"true" json:"address"` @@ -63,6 +72,14 @@ type EVMInput struct { Nonce uint64 `serialize:"true" json:"nonce"` } +func (i EVMInput) Less(other EVMInput) bool { + addrComp := bytes.Compare(i.Address.Bytes(), other.Address.Bytes()) + if addrComp != 0 { + return addrComp < 0 + } + return bytes.Compare(i.AssetID[:], other.AssetID[:]) < 0 +} + // Verify ... func (out *EVMOutput) Verify() error { switch { @@ -126,6 +143,10 @@ type Tx struct { Creds []verify.Verifiable `serialize:"true" json:"credentials"` } +func (tx *Tx) Less(other *Tx) bool { + return tx.ID().Hex() < other.ID().Hex() +} + // Sign this transaction with the provided signers func (tx *Tx) Sign(c codec.Manager, signers [][]*secp256k1.PrivateKey) error { unsignedBytes, err := c.Marshal(codecVersion, &tx.UnsignedAtomicTx) @@ -217,49 +238,6 @@ func SortEVMInputsAndSigners(inputs []EVMInput, signers [][]*secp256k1.PrivateKe sort.Sort(&innerSortInputsAndSigners{inputs: inputs, signers: signers}) } -// IsSortedAndUniqueEVMInputs returns true if the EVM Inputs are sorted and unique -// based on the account addresses -func IsSortedAndUniqueEVMInputs(inputs []EVMInput) bool { - return utils.IsSortedAndUnique(&innerSortInputsAndSigners{inputs: inputs}) -} - -// innerSortEVMOutputs implements sort.Interface for EVMOutput -type innerSortEVMOutputs struct { - outputs []EVMOutput -} - -func (outs *innerSortEVMOutputs) Less(i, j int) bool { - addrComp := bytes.Compare(outs.outputs[i].Address.Bytes(), outs.outputs[j].Address.Bytes()) - if addrComp != 0 { - return addrComp < 0 - } - return bytes.Compare(outs.outputs[i].AssetID[:], outs.outputs[j].AssetID[:]) < 0 -} - -func (outs *innerSortEVMOutputs) Len() int { return len(outs.outputs) } - -func (outs *innerSortEVMOutputs) Swap(i, j int) { - outs.outputs[j], outs.outputs[i] = outs.outputs[i], outs.outputs[j] -} - -// SortEVMOutputs sorts the list of EVMOutputs based on the addresses and assetIDs -// of the outputs -func SortEVMOutputs(outputs []EVMOutput) { - sort.Sort(&innerSortEVMOutputs{outputs: outputs}) -} - -// IsSortedEVMOutputs returns true if the EVMOutputs are sorted -// based on the account addresses and assetIDs -func IsSortedEVMOutputs(outputs []EVMOutput) bool { - return sort.IsSorted(&innerSortEVMOutputs{outputs: outputs}) -} - -// IsSortedAndUniqueEVMOutputs returns true if the EVMOutputs are sorted -// and unique based on the account addresses and assetIDs -func IsSortedAndUniqueEVMOutputs(outputs []EVMOutput) bool { - return utils.IsSortedAndUnique(&innerSortEVMOutputs{outputs: outputs}) -} - // calculates the amount of AVAX that must be burned by an atomic transaction // that consumes [cost] at [baseFee]. func CalculateDynamicFee(cost uint64, baseFee *big.Int) (uint64, error) { @@ -289,7 +267,9 @@ func mergeAtomicOps(txs []*Tx) (map[ids.ID]*atomic.Requests, error) { // with txs initialized from the txID index. copyTxs := make([]*Tx, len(txs)) copy(copyTxs, txs) - sort.Slice(copyTxs, func(i, j int) bool { return copyTxs[i].ID().Hex() < copyTxs[j].ID().Hex() }) + slices.SortFunc(copyTxs, func(i, j *Tx) bool { + return i.Less(j) + }) txs = copyTxs } output := make(map[ids.ID]*atomic.Requests) diff --git a/plugin/evm/tx_test.go b/plugin/evm/tx_test.go index abe6486afa..3438f16296 100644 --- a/plugin/evm/tx_test.go +++ b/plugin/evm/tx_test.go @@ -9,10 +9,15 @@ import ( "strings" "testing" + "github.com/stretchr/testify/require" + + "github.com/ethereum/go-ethereum/common" + + "github.com/ava-labs/coreth/params" + "github.com/ava-labs/avalanchego/chains/atomic" + "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/snow" - "github.com/ava-labs/coreth/params" - "github.com/stretchr/testify/require" ) func TestCalculateDynamicFee(t *testing.T) { @@ -180,3 +185,157 @@ func executeTxTest(t *testing.T, test atomicTxTest) { test.checkState(t, vm) } } + +func TestEVMOutputLess(t *testing.T) { + type test struct { + name string + a, b EVMOutput + expected bool + } + + tests := []test{ + { + name: "first address less", + a: EVMOutput{ + Address: common.BytesToAddress([]byte{0x01}), + AssetID: ids.ID{1}, + }, + b: EVMOutput{ + Address: common.BytesToAddress([]byte{0x02}), + AssetID: ids.ID{0}, + }, + expected: true, + }, + { + name: "first address greater", + a: EVMOutput{ + Address: common.BytesToAddress([]byte{0x02}), + AssetID: ids.ID{0}, + }, + b: EVMOutput{ + Address: common.BytesToAddress([]byte{0x01}), + AssetID: ids.ID{1}, + }, + expected: false, + }, + { + name: "first address greater; assetIDs equal", + a: EVMOutput{ + Address: common.BytesToAddress([]byte{0x02}), + AssetID: ids.ID{}, + }, + b: EVMOutput{ + Address: common.BytesToAddress([]byte{0x01}), + AssetID: ids.ID{}, + }, + expected: false, + }, + { + name: "addresses equal; first assetID less", + a: EVMOutput{ + Address: common.BytesToAddress([]byte{0x01}), + AssetID: ids.ID{0}, + }, + b: EVMOutput{ + Address: common.BytesToAddress([]byte{0x01}), + AssetID: ids.ID{1}, + }, + expected: true, + }, + { + name: "equal", + a: EVMOutput{}, + b: EVMOutput{}, + expected: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + require.Equal(t, tt.expected, tt.a.Less(tt.b)) + }) + } +} + +func TestEVMInputLess(t *testing.T) { + type test struct { + name string + a, b EVMInput + expected bool + } + + tests := []test{ + { + name: "first address less", + a: EVMInput{ + Address: common.BytesToAddress([]byte{0x01}), + AssetID: ids.ID{1}, + }, + b: EVMInput{ + Address: common.BytesToAddress([]byte{0x02}), + AssetID: ids.ID{0}, + }, + expected: true, + }, + { + name: "first address greater", + a: EVMInput{ + Address: common.BytesToAddress([]byte{0x02}), + AssetID: ids.ID{0}, + }, + b: EVMInput{ + Address: common.BytesToAddress([]byte{0x01}), + AssetID: ids.ID{1}, + }, + expected: false, + }, + { + name: "first address greater; assetIDs equal", + a: EVMInput{ + Address: common.BytesToAddress([]byte{0x02}), + AssetID: ids.ID{}, + }, + b: EVMInput{ + Address: common.BytesToAddress([]byte{0x01}), + AssetID: ids.ID{}, + }, + expected: false, + }, + { + name: "addresses equal; first assetID less", + a: EVMInput{ + Address: common.BytesToAddress([]byte{0x01}), + AssetID: ids.ID{0}, + }, + b: EVMInput{ + Address: common.BytesToAddress([]byte{0x01}), + AssetID: ids.ID{1}, + }, + expected: true, + }, + { + name: "addresses equal; first assetID greater", + a: EVMInput{ + Address: common.BytesToAddress([]byte{0x01}), + AssetID: ids.ID{1}, + }, + b: EVMInput{ + Address: common.BytesToAddress([]byte{0x01}), + AssetID: ids.ID{0}, + }, + expected: false, + }, + { + name: "equal", + a: EVMInput{}, + b: EVMInput{}, + expected: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + require.Equal(t, tt.expected, tt.a.Less(tt.b)) + }) + } +} diff --git a/plugin/evm/vm_test.go b/plugin/evm/vm_test.go index 2157822752..5c5e53b38a 100644 --- a/plugin/evm/vm_test.go +++ b/plugin/evm/vm_test.go @@ -12,11 +12,12 @@ import ( "math/big" "os" "path/filepath" - "sort" "strings" "testing" "time" + "golang.org/x/exp/slices" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/crypto" @@ -4071,8 +4072,7 @@ func TestExtraStateChangeAtomicGasLimitExceeded(t *testing.T) { func TestGetAtomicRepositoryRepairHeights(t *testing.T) { mainnetHeights := getAtomicRepositoryRepairHeights(bonusBlockMainnetHeights, canonicalBlockMainnetHeights) assert.Len(t, mainnetHeights, 76) - sorted := sort.SliceIsSorted(mainnetHeights, func(i, j int) bool { return mainnetHeights[i] < mainnetHeights[j] }) - assert.True(t, sorted) + assert.True(t, slices.IsSorted(mainnetHeights)) } func TestSkipChainConfigCheckCompatible(t *testing.T) { diff --git a/scripts/versions.sh b/scripts/versions.sh index c4dec11861..2f500443b6 100644 --- a/scripts/versions.sh +++ b/scripts/versions.sh @@ -1,4 +1,4 @@ #!/usr/bin/env bash # Don't export them as they're used in the context of other calls -avalanche_version=${AVALANCHE_VERSION:-'v1.1.3-rc1'} # caminogo version +avalanche_version=${AVALANCHE_VERSION:-'v1.1.5-rc0'} # caminogo version