From d19bf657a790b196742a638b67d8146940363373 Mon Sep 17 00:00:00 2001 From: Quentin McGaw Date: Thu, 15 May 2025 14:36:18 +0200 Subject: [PATCH 01/20] chore(all): use upstream protocol params --- consensus/misc/eip4844/eip4844.go | 18 +-- consensus/misc/eip4844/eip4844_test.go | 20 +-- core/bench_test.go | 11 +- core/block_validator.go | 9 +- core/blockchain_repair_test.go | 5 +- core/blockchain_test.go | 9 +- core/chain_makers_test.go | 7 +- core/genesis.go | 5 +- core/state_processor.go | 9 +- core/state_processor_test.go | 37 ++--- core/state_transition.go | 29 ++-- core/test_blockchain.go | 47 +++--- core/txindexer_test.go | 9 +- core/txpool/blobpool/blobpool.go | 9 +- core/txpool/blobpool/blobpool_test.go | 9 +- core/txpool/blobpool/evictheap_test.go | 6 +- core/txpool/validation.go | 11 +- core/vm/runtime/runtime.go | 3 +- eth/gasestimator/gasestimator.go | 9 +- eth/gasprice/feehistory_test.go | 8 +- eth/gasprice/gasprice_test.go | 11 +- eth/tracers/api_test.go | 17 ++- ethclient/simulated/backend_test.go | 8 +- ethclient/simulated/options_test.go | 4 +- internal/ethapi/api_test.go | 11 +- internal/ethapi/transaction_args.go | 6 +- miner/worker.go | 21 +-- nativeasset/contract_test.go | 15 +- params/hooks_libevm.go | 5 +- params/protocol_params.go | 204 ------------------------- params/protocol_params_ext.go | 15 ++ params/protocol_params_test.go | 142 +++++++++++++++++ plugin/evm/syncervm_test.go | 4 +- plugin/evm/vm_test.go | 3 +- sync/client/client.go | 19 +-- sync/client/client_test.go | 3 +- sync/handlers/code_request_test.go | 9 +- 37 files changed, 370 insertions(+), 397 deletions(-) delete mode 100644 params/protocol_params.go create mode 100644 params/protocol_params_ext.go create mode 100644 params/protocol_params_test.go diff --git a/consensus/misc/eip4844/eip4844.go b/consensus/misc/eip4844/eip4844.go index 5b78fa9e61..f1cff9b941 100644 --- a/consensus/misc/eip4844/eip4844.go +++ b/consensus/misc/eip4844/eip4844.go @@ -31,13 +31,13 @@ import ( "fmt" "math/big" - "github.com/ava-labs/coreth/params" "github.com/ava-labs/libevm/core/types" + ethparams "github.com/ava-labs/libevm/params" ) var ( - minBlobGasPrice = big.NewInt(params.BlobTxMinBlobGasprice) - blobGaspriceUpdateFraction = big.NewInt(params.BlobTxBlobGaspriceUpdateFraction) + minBlobGasPrice = big.NewInt(ethparams.BlobTxMinBlobGasprice) + blobGaspriceUpdateFraction = big.NewInt(ethparams.BlobTxBlobGaspriceUpdateFraction) ) // VerifyEIP4844Header verifies the presence of the excessBlobGas field and that @@ -52,11 +52,11 @@ func VerifyEIP4844Header(parent, header *types.Header) error { return errors.New("header is missing blobGasUsed") } // Verify that the blob gas used remains within reasonable limits. - if *header.BlobGasUsed > params.MaxBlobGasPerBlock { - return fmt.Errorf("blob gas used %d exceeds maximum allowance %d", *header.BlobGasUsed, params.MaxBlobGasPerBlock) + if *header.BlobGasUsed > ethparams.MaxBlobGasPerBlock { + return fmt.Errorf("blob gas used %d exceeds maximum allowance %d", *header.BlobGasUsed, ethparams.MaxBlobGasPerBlock) } - if *header.BlobGasUsed%params.BlobTxBlobGasPerBlob != 0 { - return fmt.Errorf("blob gas used %d not a multiple of blob gas per blob %d", header.BlobGasUsed, params.BlobTxBlobGasPerBlob) + if *header.BlobGasUsed%ethparams.BlobTxBlobGasPerBlob != 0 { + return fmt.Errorf("blob gas used %d not a multiple of blob gas per blob %d", header.BlobGasUsed, ethparams.BlobTxBlobGasPerBlob) } // Verify the excessBlobGas is correct based on the parent header var ( @@ -79,10 +79,10 @@ func VerifyEIP4844Header(parent, header *types.Header) error { // blobs on top of the excess blob gas. func CalcExcessBlobGas(parentExcessBlobGas uint64, parentBlobGasUsed uint64) uint64 { excessBlobGas := parentExcessBlobGas + parentBlobGasUsed - if excessBlobGas < params.BlobTxTargetBlobGasPerBlock { + if excessBlobGas < ethparams.BlobTxTargetBlobGasPerBlock { return 0 } - return excessBlobGas - params.BlobTxTargetBlobGasPerBlock + return excessBlobGas - ethparams.BlobTxTargetBlobGasPerBlock } // CalcBlobFee calculates the blobfee from the header's excess blob gas field. diff --git a/consensus/misc/eip4844/eip4844_test.go b/consensus/misc/eip4844/eip4844_test.go index e66334ed89..304f0611c5 100644 --- a/consensus/misc/eip4844/eip4844_test.go +++ b/consensus/misc/eip4844/eip4844_test.go @@ -21,7 +21,7 @@ import ( "math/big" "testing" - "github.com/ava-labs/coreth/params" + ethparams "github.com/ava-labs/libevm/params" ) func TestCalcExcessBlobGas(t *testing.T) { @@ -34,23 +34,23 @@ func TestCalcExcessBlobGas(t *testing.T) { // slots are below - or equal - to the target. {0, 0, 0}, {0, 1, 0}, - {0, params.BlobTxTargetBlobGasPerBlock / params.BlobTxBlobGasPerBlob, 0}, + {0, ethparams.BlobTxTargetBlobGasPerBlock / ethparams.BlobTxBlobGasPerBlob, 0}, // If the target blob gas is exceeded, the excessBlobGas should increase // by however much it was overshot - {0, (params.BlobTxTargetBlobGasPerBlock / params.BlobTxBlobGasPerBlob) + 1, params.BlobTxBlobGasPerBlob}, - {1, (params.BlobTxTargetBlobGasPerBlock / params.BlobTxBlobGasPerBlob) + 1, params.BlobTxBlobGasPerBlob + 1}, - {1, (params.BlobTxTargetBlobGasPerBlock / params.BlobTxBlobGasPerBlob) + 2, 2*params.BlobTxBlobGasPerBlob + 1}, + {0, (ethparams.BlobTxTargetBlobGasPerBlock / ethparams.BlobTxBlobGasPerBlob) + 1, ethparams.BlobTxBlobGasPerBlob}, + {1, (ethparams.BlobTxTargetBlobGasPerBlock / ethparams.BlobTxBlobGasPerBlob) + 1, ethparams.BlobTxBlobGasPerBlob + 1}, + {1, (ethparams.BlobTxTargetBlobGasPerBlock / ethparams.BlobTxBlobGasPerBlob) + 2, 2*ethparams.BlobTxBlobGasPerBlob + 1}, // The excess blob gas should decrease by however much the target was // under-shot, capped at zero. - {params.BlobTxTargetBlobGasPerBlock, params.BlobTxTargetBlobGasPerBlock / params.BlobTxBlobGasPerBlob, params.BlobTxTargetBlobGasPerBlock}, - {params.BlobTxTargetBlobGasPerBlock, (params.BlobTxTargetBlobGasPerBlock / params.BlobTxBlobGasPerBlob) - 1, params.BlobTxTargetBlobGasPerBlock - params.BlobTxBlobGasPerBlob}, - {params.BlobTxTargetBlobGasPerBlock, (params.BlobTxTargetBlobGasPerBlock / params.BlobTxBlobGasPerBlob) - 2, params.BlobTxTargetBlobGasPerBlock - (2 * params.BlobTxBlobGasPerBlob)}, - {params.BlobTxBlobGasPerBlob - 1, (params.BlobTxTargetBlobGasPerBlock / params.BlobTxBlobGasPerBlob) - 1, 0}, + {ethparams.BlobTxTargetBlobGasPerBlock, ethparams.BlobTxTargetBlobGasPerBlock / ethparams.BlobTxBlobGasPerBlob, ethparams.BlobTxTargetBlobGasPerBlock}, + {ethparams.BlobTxTargetBlobGasPerBlock, (ethparams.BlobTxTargetBlobGasPerBlock / ethparams.BlobTxBlobGasPerBlob) - 1, ethparams.BlobTxTargetBlobGasPerBlock - ethparams.BlobTxBlobGasPerBlob}, + {ethparams.BlobTxTargetBlobGasPerBlock, (ethparams.BlobTxTargetBlobGasPerBlock / ethparams.BlobTxBlobGasPerBlob) - 2, ethparams.BlobTxTargetBlobGasPerBlock - (2 * ethparams.BlobTxBlobGasPerBlob)}, + {ethparams.BlobTxBlobGasPerBlob - 1, (ethparams.BlobTxTargetBlobGasPerBlock / ethparams.BlobTxBlobGasPerBlob) - 1, 0}, } for i, tt := range tests { - result := CalcExcessBlobGas(tt.excess, tt.blobs*params.BlobTxBlobGasPerBlob) + result := CalcExcessBlobGas(tt.excess, tt.blobs*ethparams.BlobTxBlobGasPerBlob) if result != tt.want { t.Errorf("test %d: excess blob gas mismatch: have %v, want %v", i, result, tt.want) } diff --git a/core/bench_test.go b/core/bench_test.go index ba25ae991e..4b291a8c07 100644 --- a/core/bench_test.go +++ b/core/bench_test.go @@ -40,6 +40,7 @@ import ( "github.com/ava-labs/libevm/core/vm" "github.com/ava-labs/libevm/crypto" "github.com/ava-labs/libevm/ethdb" + ethparams "github.com/ava-labs/libevm/params" ) func BenchmarkInsertChain_empty_memdb(b *testing.B) { @@ -125,26 +126,26 @@ func init() { // and fills the blocks with many small transactions. func genTxRing(naccounts int) func(int, *BlockGen) { from := 0 - fee := big.NewInt(0).SetUint64(params.TxGas * 225000000000) + fee := big.NewInt(0).SetUint64(ethparams.TxGas * 225000000000) amount := big.NewInt(0).Set(benchRootFunds) return func(i int, gen *BlockGen) { block := gen.PrevBlock(i - 1) gas := block.GasLimit() signer := gen.Signer() for { - gas -= params.TxGas - if gas < params.TxGas { + gas -= ethparams.TxGas + if gas < ethparams.TxGas { break } to := (from + 1) % naccounts - burn := new(big.Int).SetUint64(params.TxGas) + burn := new(big.Int).SetUint64(ethparams.TxGas) burn.Mul(burn, gen.header.BaseFee) tx, err := types.SignNewTx(ringKeys[from], signer, &types.LegacyTx{ Nonce: gen.TxNonce(ringAddrs[from]), To: &ringAddrs[to], Value: amount.Sub(amount, fee), - Gas: params.TxGas, + Gas: ethparams.TxGas, GasPrice: big.NewInt(225000000000), }) if err != nil { diff --git a/core/block_validator.go b/core/block_validator.go index 66911ce2cc..b1667de620 100644 --- a/core/block_validator.go +++ b/core/block_validator.go @@ -35,6 +35,7 @@ import ( "github.com/ava-labs/coreth/params" "github.com/ava-labs/coreth/plugin/evm/upgrade/ap0" "github.com/ava-labs/libevm/core/types" + ethparams "github.com/ava-labs/libevm/params" "github.com/ava-labs/libevm/trie" ) @@ -97,8 +98,8 @@ func (v *BlockValidator) ValidateBody(block *types.Block) error { // Check blob gas usage. if header.BlobGasUsed != nil { - if want := *header.BlobGasUsed / params.BlobTxBlobGasPerBlob; uint64(blobs) != want { // div because the header is surely good vs the body might be bloated - return fmt.Errorf("blob gas used mismatch (header %v, calculated %v)", *header.BlobGasUsed, blobs*params.BlobTxBlobGasPerBlob) + if want := *header.BlobGasUsed / ethparams.BlobTxBlobGasPerBlob; uint64(blobs) != want { // div because the header is surely good vs the body might be bloated + return fmt.Errorf("blob gas used mismatch (header %v, calculated %v)", *header.BlobGasUsed, blobs*ethparams.BlobTxBlobGasPerBlob) } } else { if blobs > 0 { @@ -148,10 +149,10 @@ func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateD // the gas allowance. func CalcGasLimit(parentGasUsed, parentGasLimit, gasFloor, gasCeil uint64) uint64 { // contrib = (parentGasUsed * 3 / 2) / 1024 - contrib := (parentGasUsed + parentGasUsed/2) / ap0.GasLimitBoundDivisor + contrib := (parentGasUsed + parentGasUsed/2) / ethparams.GasLimitBoundDivisor // decay = parentGasLimit / 1024 -1 - decay := parentGasLimit/ap0.GasLimitBoundDivisor - 1 + decay := parentGasLimit/ethparams.GasLimitBoundDivisor - 1 /* strategy: gasLimit of block-to-mine is set based on parent's diff --git a/core/blockchain_repair_test.go b/core/blockchain_repair_test.go index 202f8e9fc8..753ecf0bc7 100644 --- a/core/blockchain_repair_test.go +++ b/core/blockchain_repair_test.go @@ -42,6 +42,7 @@ import ( "github.com/ava-labs/libevm/core/types" "github.com/ava-labs/libevm/core/vm" "github.com/ava-labs/libevm/crypto" + ethparams "github.com/ava-labs/libevm/params" "github.com/ava-labs/libevm/triedb" "github.com/stretchr/testify/require" ) @@ -566,7 +567,7 @@ func testRepairWithScheme(t *testing.T, tt *rewindTest, snapshots bool, scheme s gspec.MustCommit(genDb, triedb.NewDatabase(genDb, nil)) sideblocks, _, err = GenerateChain(gspec.Config, gspec.ToBlock(), engine, genDb, tt.sidechainBlocks, 10, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{0x01}) - tx, err := types.SignTx(types.NewTransaction(b.TxNonce(addr1), common.Address{0x01}, big.NewInt(10000), params.TxGas, big.NewInt(ap3.InitialBaseFee), nil), signer, key1) + tx, err := types.SignTx(types.NewTransaction(b.TxNonce(addr1), common.Address{0x01}, big.NewInt(10000), ethparams.TxGas, big.NewInt(ap3.InitialBaseFee), nil), signer, key1) require.NoError(err) b.AddTx(tx) }) @@ -580,7 +581,7 @@ func testRepairWithScheme(t *testing.T, tt *rewindTest, snapshots bool, scheme s canonblocks, _, err := GenerateChain(gspec.Config, gspec.ToBlock(), engine, genDb, tt.canonicalBlocks, 10, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{0x02}) b.SetDifficulty(big.NewInt(1000000)) - tx, err := types.SignTx(types.NewTransaction(b.TxNonce(addr1), common.Address{0x02}, big.NewInt(10000), params.TxGas, big.NewInt(ap3.InitialBaseFee), nil), signer, key1) + tx, err := types.SignTx(types.NewTransaction(b.TxNonce(addr1), common.Address{0x02}, big.NewInt(10000), ethparams.TxGas, big.NewInt(ap3.InitialBaseFee), nil), signer, key1) require.NoError(err) b.AddTx(tx) }) diff --git a/core/blockchain_test.go b/core/blockchain_test.go index 6565da75e1..428fb498c9 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -23,6 +23,7 @@ import ( "github.com/ava-labs/libevm/crypto" "github.com/ava-labs/libevm/eth/tracers/logger" "github.com/ava-labs/libevm/ethdb" + ethparams "github.com/ava-labs/libevm/params" "github.com/holiman/uint256" ) @@ -324,7 +325,7 @@ func testRepopulateMissingTriesParallel(t *testing.T, parallelism int) { // This call generates a chain of 3 blocks. signer := types.HomesteadSigner{} _, chain, _, err := GenerateChainWithGenesis(gspec, blockchain.engine, 10, 10, func(i int, gen *BlockGen) { - tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(10000), params.TxGas, nil, nil), signer, key1) + tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(10000), ethparams.TxGas, nil, nil), signer, key1) gen.AddTx(tx) }) if err != nil { @@ -437,7 +438,7 @@ func TestUngracefulAsyncShutdown(t *testing.T) { // This call generates a chain of 10 blocks. signer := types.HomesteadSigner{} _, chain, _, err := GenerateChainWithGenesis(gspec, blockchain.engine, 10, 10, func(i int, gen *BlockGen) { - tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(10000), params.TxGas, nil, nil), signer, key1) + tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(10000), ethparams.TxGas, nil, nil), signer, key1) gen.AddTx(tx) }) if err != nil { @@ -1085,8 +1086,8 @@ func TestEIP3651(t *testing.T) { block := chain.GetBlockByNumber(1) // 1+2: Ensure EIP-1559 access lists are accounted for via gas usage. - innerGas := vm.GasQuickStep*2 + params.ColdSloadCostEIP2929*2 - expectedGas := params.TxGas + 5*vm.GasFastestStep + vm.GasQuickStep + 100 + innerGas // 100 because 0xaaaa is in access list + innerGas := vm.GasQuickStep*2 + ethparams.ColdSloadCostEIP2929*2 + expectedGas := ethparams.TxGas + 5*vm.GasFastestStep + vm.GasQuickStep + 100 + innerGas // 100 because 0xaaaa is in access list if block.GasUsed() != expectedGas { t.Fatalf("incorrect amount of gas spent: expected %d, got %d", expectedGas, block.GasUsed()) } diff --git a/core/chain_makers_test.go b/core/chain_makers_test.go index 695ae96d25..01ec61f5d2 100644 --- a/core/chain_makers_test.go +++ b/core/chain_makers_test.go @@ -37,6 +37,7 @@ import ( "github.com/ava-labs/libevm/core/types" "github.com/ava-labs/libevm/core/vm" "github.com/ava-labs/libevm/crypto" + ethparams "github.com/ava-labs/libevm/params" "github.com/ava-labs/libevm/triedb" ) @@ -67,15 +68,15 @@ func ExampleGenerateChain() { switch i { case 0: // In block 1, addr1 sends addr2 some ether. - tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(10000), params.TxGas, nil, nil), signer, key1) + tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(10000), ethparams.TxGas, nil, nil), signer, key1) gen.AddTx(tx) case 1: // In block 2, addr1 sends some more ether to addr2. // addr2 passes it on to addr3. - tx1, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(1000), params.TxGas, nil, nil), signer, key1) + tx1, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(1000), ethparams.TxGas, nil, nil), signer, key1) gen.AddTx(tx1) case 2: - tx2, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr2), addr3, big.NewInt(1000), params.TxGas, nil, nil), signer, key2) + tx2, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr2), addr3, big.NewInt(1000), ethparams.TxGas, nil, nil), signer, key2) gen.AddTx(tx2) } }) diff --git a/core/genesis.go b/core/genesis.go index 8e862af82e..b580c33265 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -44,6 +44,7 @@ import ( "github.com/ava-labs/libevm/core/types" "github.com/ava-labs/libevm/ethdb" "github.com/ava-labs/libevm/log" + ethparams "github.com/ava-labs/libevm/params" "github.com/ava-labs/libevm/trie" "github.com/ava-labs/libevm/triedb" "github.com/holiman/uint256" @@ -267,10 +268,10 @@ func (g *Genesis) toBlock(db ethdb.Database, triedb *triedb.Database) *types.Blo head.Root = root if g.GasLimit == 0 { - head.GasLimit = params.GenesisGasLimit + head.GasLimit = ethparams.GenesisGasLimit } if g.Difficulty == nil { - head.Difficulty = params.GenesisDifficulty + head.Difficulty = ethparams.GenesisDifficulty } if conf := g.Config; conf != nil { num := new(big.Int).SetUint64(g.Number) diff --git a/core/state_processor.go b/core/state_processor.go index 4b00c141db..c4a0bae4d2 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -38,6 +38,7 @@ import ( "github.com/ava-labs/libevm/core/vm" "github.com/ava-labs/libevm/crypto" "github.com/ava-labs/libevm/log" + ethparams "github.com/ava-labs/libevm/params" ) // StateProcessor is a basic Processor, which takes care of transitioning @@ -148,7 +149,7 @@ func applyTransaction(msg *Message, config *params.ChainConfig, gp *GasPool, sta receipt.GasUsed = result.UsedGas if tx.Type() == types.BlobTxType { - receipt.BlobGasUsed = uint64(len(tx.BlobHashes()) * params.BlobTxBlobGasPerBlob) + receipt.BlobGasUsed = uint64(len(tx.BlobHashes()) * ethparams.BlobTxBlobGasPerBlob) receipt.BlobGasPrice = evm.Context.BlobBaseFee } @@ -187,16 +188,16 @@ func ProcessBeaconBlockRoot(beaconRoot common.Hash, vmenv *vm.EVM, statedb *stat // If EIP-4788 is enabled, we need to invoke the beaconroot storage contract with // the new root msg := &Message{ - From: params.SystemAddress, + From: ethparams.SystemAddress, GasLimit: 30_000_000, GasPrice: common.Big0, GasFeeCap: common.Big0, GasTipCap: common.Big0, - To: ¶ms.BeaconRootsStorageAddress, + To: ðparams.BeaconRootsStorageAddress, Data: beaconRoot[:], } vmenv.Reset(NewEVMTxContext(msg), statedb) - statedb.AddAddressToAccessList(params.BeaconRootsStorageAddress) + statedb.AddAddressToAccessList(ethparams.BeaconRootsStorageAddress) _, _, _ = vmenv.Call(vm.AccountRef(msg.From), *msg.To, msg.Data, 30_000_000, common.U2560) statedb.Finalise(true) } diff --git a/core/state_processor_test.go b/core/state_processor_test.go index 705a6dfe7e..5d3f33b38b 100644 --- a/core/state_processor_test.go +++ b/core/state_processor_test.go @@ -49,6 +49,7 @@ import ( "github.com/ava-labs/libevm/core/types" "github.com/ava-labs/libevm/core/vm" "github.com/ava-labs/libevm/crypto" + ethparams "github.com/ava-labs/libevm/params" "github.com/ava-labs/libevm/trie" "github.com/holiman/uint256" "golang.org/x/crypto/sha3" @@ -129,7 +130,7 @@ func TestStateProcessorErrors(t *testing.T) { } // FullFaker used to skip header verification that enforces no blobs. blockchain, _ = NewBlockChain(db, DefaultCacheConfig, gspec, dummy.NewFullFaker(), vm.Config{}, common.Hash{}, false) - tooBigInitCode = [params.MaxInitCodeSize + 1]byte{} + tooBigInitCode = [ethparams.MaxInitCodeSize + 1]byte{} ) defer blockchain.Stop() @@ -142,14 +143,14 @@ func TestStateProcessorErrors(t *testing.T) { }{ { // ErrNonceTooLow txs: []*types.Transaction{ - makeTx(key1, 0, common.Address{}, big.NewInt(0), params.TxGas, big.NewInt(225000000000), nil), - makeTx(key1, 0, common.Address{}, big.NewInt(0), params.TxGas, big.NewInt(225000000000), nil), + makeTx(key1, 0, common.Address{}, big.NewInt(0), ethparams.TxGas, big.NewInt(225000000000), nil), + makeTx(key1, 0, common.Address{}, big.NewInt(0), ethparams.TxGas, big.NewInt(225000000000), nil), }, want: "could not apply tx 1 [0x734d821c990099c6ae42d78072aadd3931c35328cf03ef4cf5b2a4ac9c398522]: nonce too low: address 0x71562b71999873DB5b286dF957af199Ec94617F7, tx: 0 state: 1", }, { // ErrNonceTooHigh txs: []*types.Transaction{ - makeTx(key1, 100, common.Address{}, big.NewInt(0), params.TxGas, big.NewInt(225000000000), nil), + makeTx(key1, 100, common.Address{}, big.NewInt(0), ethparams.TxGas, big.NewInt(225000000000), nil), }, want: "could not apply tx 0 [0x0df36254cfbef8ed6961b38fc68aecc777177166144c8a56bc8919e23a559bf4]: nonce too high: address 0x71562b71999873DB5b286dF957af199Ec94617F7, tx: 100 state: 0", }, @@ -162,13 +163,13 @@ func TestStateProcessorErrors(t *testing.T) { }, { // ErrInsufficientFundsForTransfer txs: []*types.Transaction{ - makeTx(key1, 0, common.Address{}, big.NewInt(4000000000000000000), params.TxGas, big.NewInt(225000000000), nil), + makeTx(key1, 0, common.Address{}, big.NewInt(4000000000000000000), ethparams.TxGas, big.NewInt(225000000000), nil), }, want: "could not apply tx 0 [0x1632f2bffcce84a5c91dd8ab2016128fccdbcfbe0485d2c67457e1c793c72a4b]: insufficient funds for gas * price + value: address 0x71562b71999873DB5b286dF957af199Ec94617F7 have 4000000000000000000 want 4004725000000000000", }, { // ErrInsufficientFunds txs: []*types.Transaction{ - makeTx(key1, 0, common.Address{}, big.NewInt(0), params.TxGas, big.NewInt(900000000000000000), nil), + makeTx(key1, 0, common.Address{}, big.NewInt(0), ethparams.TxGas, big.NewInt(900000000000000000), nil), }, want: "could not apply tx 0 [0x4a69690c4b0cd85e64d0d9ea06302455b01e10a83db964d60281739752003440]: insufficient funds for gas * price + value: address 0x71562b71999873DB5b286dF957af199Ec94617F7 have 4000000000000000000 want 18900000000000000000000", }, @@ -178,38 +179,38 @@ func TestStateProcessorErrors(t *testing.T) { // multiplication len(data) +gas_per_byte overflows uint64. Not testable at the moment { // ErrIntrinsicGas txs: []*types.Transaction{ - makeTx(key1, 0, common.Address{}, big.NewInt(0), params.TxGas-1000, big.NewInt(225000000000), nil), + makeTx(key1, 0, common.Address{}, big.NewInt(0), ethparams.TxGas-1000, big.NewInt(225000000000), nil), }, want: "could not apply tx 0 [0x2fc3e3b5cc26917d413e26983fe189475f47d4f0757e32aaa5561fcb9c9dc432]: intrinsic gas too low: have 20000, want 21000", }, { // ErrGasLimitReached txs: []*types.Transaction{ // This test was modified to account for ACP-176 gas limits - makeTx(key1, 0, common.Address{}, big.NewInt(0), params.TxGas*953, big.NewInt(acp176.MinGasPrice), nil), + makeTx(key1, 0, common.Address{}, big.NewInt(0), ethparams.TxGas*953, big.NewInt(acp176.MinGasPrice), nil), }, want: "could not apply tx 0 [0xcd46718c1af6fd074deb6b036d34c1cb499517bf90d93df74dcfaba25fdf34af]: gas limit reached", }, { // ErrFeeCapTooLow txs: []*types.Transaction{ - mkDynamicTx(0, common.Address{}, params.TxGas, big.NewInt(0), big.NewInt(0)), + mkDynamicTx(0, common.Address{}, ethparams.TxGas, big.NewInt(0), big.NewInt(0)), }, want: "could not apply tx 0 [0xc4ab868fef0c82ae0387b742aee87907f2d0fc528fc6ea0a021459fb0fc4a4a8]: max fee per gas less than block base fee: address 0x71562b71999873DB5b286dF957af199Ec94617F7, maxFeePerGas: 0, baseFee: 1", }, { // ErrTipVeryHigh txs: []*types.Transaction{ - mkDynamicTx(0, common.Address{}, params.TxGas, tooBigNumber, big.NewInt(1)), + mkDynamicTx(0, common.Address{}, ethparams.TxGas, tooBigNumber, big.NewInt(1)), }, want: "could not apply tx 0 [0x15b8391b9981f266b32f3ab7da564bbeb3d6c21628364ea9b32a21139f89f712]: max priority fee per gas higher than 2^256-1: address 0x71562b71999873DB5b286dF957af199Ec94617F7, maxPriorityFeePerGas bit length: 257", }, { // ErrFeeCapVeryHigh txs: []*types.Transaction{ - mkDynamicTx(0, common.Address{}, params.TxGas, big.NewInt(1), tooBigNumber), + mkDynamicTx(0, common.Address{}, ethparams.TxGas, big.NewInt(1), tooBigNumber), }, want: "could not apply tx 0 [0x48bc299b83fdb345c57478f239e89814bb3063eb4e4b49f3b6057a69255c16bd]: max fee per gas higher than 2^256-1: address 0x71562b71999873DB5b286dF957af199Ec94617F7, maxFeePerGas bit length: 257", }, { // ErrTipAboveFeeCap txs: []*types.Transaction{ - mkDynamicTx(0, common.Address{}, params.TxGas, big.NewInt(2), big.NewInt(1)), + mkDynamicTx(0, common.Address{}, ethparams.TxGas, big.NewInt(2), big.NewInt(1)), }, want: "could not apply tx 0 [0xf987a31ff0c71895780a7612f965a0c8b056deb54e020bb44fa478092f14c9b4]: max priority fee per gas higher than max fee per gas: address 0x71562b71999873DB5b286dF957af199Ec94617F7, maxPriorityFeePerGas: 2, maxFeePerGas: 1", }, @@ -220,13 +221,13 @@ func TestStateProcessorErrors(t *testing.T) { // This test is designed to have the effective cost be covered by the balance, but // the extended requirement on FeeCap*gas < balance to fail txs: []*types.Transaction{ - mkDynamicTx(0, common.Address{}, params.TxGas, big.NewInt(1), big.NewInt(200000000000000)), + mkDynamicTx(0, common.Address{}, ethparams.TxGas, big.NewInt(1), big.NewInt(200000000000000)), }, want: "could not apply tx 0 [0xa3840aa3cad37eec8607b9f4846813d4a80e70b462a793fa21f64138156f849b]: insufficient funds for gas * price + value: address 0x71562b71999873DB5b286dF957af199Ec94617F7 have 4000000000000000000 want 4200000000000000000", }, { // Another ErrInsufficientFunds, this one to ensure that feecap/tip of max u256 is allowed txs: []*types.Transaction{ - mkDynamicTx(0, common.Address{}, params.TxGas, bigNumber, bigNumber), + mkDynamicTx(0, common.Address{}, ethparams.TxGas, bigNumber, bigNumber), }, want: "could not apply tx 0 [0xd82a0c2519acfeac9a948258c47e784acd20651d9d80f9a1c67b4137651c3a24]: insufficient funds for gas * price + value: address 0x71562b71999873DB5b286dF957af199Ec94617F7 required balance exceeds 256 bits", }, @@ -244,7 +245,7 @@ func TestStateProcessorErrors(t *testing.T) { }, { // ErrBlobFeeCapTooLow txs: []*types.Transaction{ - mkBlobTx(0, common.Address{}, params.TxGas, big.NewInt(1), big.NewInt(1), big.NewInt(0), []common.Hash{(common.Hash{1})}), + mkBlobTx(0, common.Address{}, ethparams.TxGas, big.NewInt(1), big.NewInt(1), big.NewInt(0), []common.Hash{(common.Hash{1})}), }, want: "could not apply tx 0 [0x6c11015985ce82db691d7b2d017acda296db88b811c3c60dc71449c76256c716]: max fee per blob gas less than block blob gas fee: address 0x71562b71999873DB5b286dF957af199Ec94617F7 blobGasFeeCap: 0, blobBaseFee: 1", }, @@ -303,7 +304,7 @@ func TestStateProcessorErrors(t *testing.T) { }{ { // ErrTxTypeNotSupported txs: []*types.Transaction{ - mkDynamicTx(0, common.Address{}, params.TxGas-1000, big.NewInt(0), big.NewInt(0)), + mkDynamicTx(0, common.Address{}, ethparams.TxGas-1000, big.NewInt(0), big.NewInt(0)), }, want: "could not apply tx 0 [0x88626ac0d53cb65308f2416103c62bb1f18b805573d4f96a3640bbbfff13c14f]: transaction type not supported", }, @@ -343,7 +344,7 @@ func TestStateProcessorErrors(t *testing.T) { }{ { // ErrSenderNoEOA txs: []*types.Transaction{ - mkDynamicTx(0, common.Address{}, params.TxGas-1000, big.NewInt(0), big.NewInt(0)), + mkDynamicTx(0, common.Address{}, ethparams.TxGas-1000, big.NewInt(0), big.NewInt(0)), }, want: "could not apply tx 0 [0x88626ac0d53cb65308f2416103c62bb1f18b805573d4f96a3640bbbfff13c14f]: sender not an eoa: address 0x71562b71999873DB5b286dF957af199Ec94617F7, codehash: 0x9280914443471259d4570a8661015ae4a5b80186dbc619658fb494bebc3da3d1", }, @@ -416,7 +417,7 @@ func GenerateBadBlock(parent *types.Block, engine consensus.Engine, txs types.Tr pUsed = *parent.BlobGasUsed() } excess := eip4844.CalcExcessBlobGas(pExcess, pUsed) - used := uint64(nBlobs * params.BlobTxBlobGasPerBlob) + used := uint64(nBlobs * ethparams.BlobTxBlobGasPerBlob) header.ExcessBlobGas = &excess header.BlobGasUsed = &used diff --git a/core/state_transition.go b/core/state_transition.go index 31c5758454..2c3eb4aa91 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -38,6 +38,7 @@ import ( "github.com/ava-labs/libevm/core/types" "github.com/ava-labs/libevm/core/vm" "github.com/ava-labs/libevm/crypto/kzg4844" + ethparams "github.com/ava-labs/libevm/params" "github.com/holiman/uint256" ) @@ -82,9 +83,9 @@ func IntrinsicGas(data []byte, accessList types.AccessList, isContractCreation b // Set the starting gas for the raw transaction var gas uint64 if isContractCreation && rules.IsHomestead { - gas = params.TxGasContractCreation + gas = ethparams.TxGasContractCreation } else { - gas = params.TxGas + gas = ethparams.TxGas } dataLen := uint64(len(data)) // Bump the required gas by the amount of transactional data @@ -97,9 +98,9 @@ func IntrinsicGas(data []byte, accessList types.AccessList, isContractCreation b } } // Make sure we don't exceed uint64 for all data combinations - nonZeroGas := params.TxDataNonZeroGasFrontier + nonZeroGas := ethparams.TxDataNonZeroGasFrontier if rules.IsIstanbul { - nonZeroGas = params.TxDataNonZeroGasEIP2028 + nonZeroGas = ethparams.TxDataNonZeroGasEIP2028 } if (math.MaxUint64-gas)/nonZeroGas < nz { return 0, ErrGasUintOverflow @@ -107,17 +108,17 @@ func IntrinsicGas(data []byte, accessList types.AccessList, isContractCreation b gas += nz * nonZeroGas z := dataLen - nz - if (math.MaxUint64-gas)/params.TxDataZeroGas < z { + if (math.MaxUint64-gas)/ethparams.TxDataZeroGas < z { return 0, ErrGasUintOverflow } - gas += z * params.TxDataZeroGas + gas += z * ethparams.TxDataZeroGas if isContractCreation && params.GetRulesExtra(rules).IsDurango { lenWords := toWordSize(dataLen) - if (math.MaxUint64-gas)/params.InitCodeWordGas < lenWords { + if (math.MaxUint64-gas)/ethparams.InitCodeWordGas < lenWords { return 0, ErrGasUintOverflow } - gas += lenWords * params.InitCodeWordGas + gas += lenWords * ethparams.InitCodeWordGas } } if accessList != nil { @@ -139,8 +140,8 @@ func accessListGas(rules params.Rules, accessList types.AccessList) (uint64, err var gas uint64 rulesExtra := params.GetRulesExtra(rules) if !rulesExtra.PredicatersExist() { - gas += uint64(len(accessList)) * params.TxAccessListAddressGas - gas += uint64(accessList.StorageKeys()) * params.TxAccessListStorageKeyGas + gas += uint64(len(accessList)) * ethparams.TxAccessListAddressGas + gas += uint64(accessList.StorageKeys()) * ethparams.TxAccessListStorageKeyGas return gas, nil } @@ -152,7 +153,7 @@ func accessListGas(rules params.Rules, accessList types.AccessList) (uint64, err // the size of access lists that could be included in a block and standard access list gas costs. // Therefore, we only check for overflow when adding to [totalGas], which could include the sum of values // returned by a predicate. - accessTupleGas := params.TxAccessListAddressGas + uint64(len(accessTuple.StorageKeys))*params.TxAccessListStorageKeyGas + accessTupleGas := ethparams.TxAccessListAddressGas + uint64(len(accessTuple.StorageKeys))*ethparams.TxAccessListStorageKeyGas totalGas, overflow := cmath.SafeAdd(gas, accessTupleGas) if overflow { return 0, ErrGasUintOverflow @@ -473,8 +474,8 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { } // Check whether the init code size has been exceeded. - if rulesExtra.IsDurango && contractCreation && len(msg.Data) > params.MaxInitCodeSize { - return nil, fmt.Errorf("%w: code size %v limit %v", vm.ErrMaxInitCodeSizeExceeded, len(msg.Data), params.MaxInitCodeSize) + if rulesExtra.IsDurango && contractCreation && len(msg.Data) > ethparams.MaxInitCodeSize { + return nil, fmt.Errorf("%w: code size %v limit %v", vm.ErrMaxInitCodeSizeExceeded, len(msg.Data), ethparams.MaxInitCodeSize) } // Execute the preparatory steps for state transition which includes: @@ -541,5 +542,5 @@ func (st *StateTransition) gasUsed() uint64 { // blobGasUsed returns the amount of blob gas used by the message. func (st *StateTransition) blobGasUsed() uint64 { - return uint64(len(st.msg.BlobHashes) * params.BlobTxBlobGasPerBlob) + return uint64(len(st.msg.BlobHashes) * ethparams.BlobTxBlobGasPerBlob) } diff --git a/core/test_blockchain.go b/core/test_blockchain.go index a5dc5be6ce..797c14fd63 100644 --- a/core/test_blockchain.go +++ b/core/test_blockchain.go @@ -18,6 +18,7 @@ import ( "github.com/ava-labs/libevm/core/types" "github.com/ava-labs/libevm/crypto" "github.com/ava-labs/libevm/ethdb" + ethparams "github.com/ava-labs/libevm/params" "github.com/holiman/uint256" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -224,7 +225,7 @@ func TestInsertChainAcceptSingleBlock(t *testing.T, create func(db ethdb.Databas // This call generates a chain of 3 blocks. signer := types.HomesteadSigner{} _, chain, _, err := GenerateChainWithGenesis(gspec, blockchain.engine, 3, 10, func(i int, gen *BlockGen) { - tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(10000), params.TxGas, nil, nil), signer, key1) + tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(10000), ethparams.TxGas, nil, nil), signer, key1) gen.AddTx(tx) }) if err != nil { @@ -296,7 +297,7 @@ func TestInsertLongForkedChain(t *testing.T, create func(db ethdb.Database, gspe signer := types.HomesteadSigner{} _, chain1, _, err := GenerateChainWithGenesis(gspec, blockchain.engine, numBlocks, 10, func(i int, gen *BlockGen) { // Generate a transaction to create a unique block - tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(10000), params.TxGas, nil, nil), signer, key1) + tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(10000), ethparams.TxGas, nil, nil), signer, key1) gen.AddTx(tx) }) if err != nil { @@ -306,7 +307,7 @@ func TestInsertLongForkedChain(t *testing.T, create func(db ethdb.Database, gspe // a longer chain can trigger a reorg. _, chain2, _, err := GenerateChainWithGenesis(gspec, blockchain.engine, numBlocks+1, 10, func(i int, gen *BlockGen) { // Generate a transaction with a different amount to ensure [chain2] is different than [chain1]. - tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(5000), params.TxGas, nil, nil), signer, key1) + tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(5000), ethparams.TxGas, nil, nil), signer, key1) gen.AddTx(tx) }) if err != nil { @@ -462,7 +463,7 @@ func TestAcceptNonCanonicalBlock(t *testing.T, create func(db ethdb.Database, gs signer := types.HomesteadSigner{} _, chain1, _, err := GenerateChainWithGenesis(gspec, blockchain.engine, numBlocks, 10, func(i int, gen *BlockGen) { // Generate a transaction to create a unique block - tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(10000), params.TxGas, nil, nil), signer, key1) + tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(10000), ethparams.TxGas, nil, nil), signer, key1) gen.AddTx(tx) }) if err != nil { @@ -470,7 +471,7 @@ func TestAcceptNonCanonicalBlock(t *testing.T, create func(db ethdb.Database, gs } _, chain2, _, err := GenerateChainWithGenesis(gspec, blockchain.engine, numBlocks, 10, func(i int, gen *BlockGen) { // Generate a transaction with a different amount to create a chain of blocks different from [chain1] - tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(5000), params.TxGas, nil, nil), signer, key1) + tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(5000), ethparams.TxGas, nil, nil), signer, key1) gen.AddTx(tx) }) if err != nil { @@ -571,7 +572,7 @@ func TestSetPreferenceRewind(t *testing.T, create func(db ethdb.Database, gspec signer := types.HomesteadSigner{} _, chain, _, err := GenerateChainWithGenesis(gspec, blockchain.engine, numBlocks, 10, func(i int, gen *BlockGen) { // Generate a transaction to create a unique block - tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(10000), params.TxGas, nil, nil), signer, key1) + tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(10000), ethparams.TxGas, nil, nil), signer, key1) gen.AddTx(tx) }) if err != nil { @@ -709,10 +710,10 @@ func TestBuildOnVariousStages(t *testing.T, create func(db ethdb.Database, gspec genDB, chain1, _, err := GenerateChainWithGenesis(gspec, blockchain.engine, 20, 10, func(i int, gen *BlockGen) { // Send all funds back and forth between the two accounts if i%2 == 0 { - tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, genesisBalance, params.TxGas, nil, nil), signer, key1) + tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, genesisBalance, ethparams.TxGas, nil, nil), signer, key1) gen.AddTx(tx) } else { - tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr2), addr1, genesisBalance, params.TxGas, nil, nil), signer, key2) + tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr2), addr1, genesisBalance, ethparams.TxGas, nil, nil), signer, key2) gen.AddTx(tx) } }) @@ -723,10 +724,10 @@ func TestBuildOnVariousStages(t *testing.T, create func(db ethdb.Database, gspec chain2, _, err := GenerateChain(gspec.Config, chain1[9], blockchain.engine, genDB, 10, 10, func(i int, gen *BlockGen) { // Send all funds back and forth between the two accounts if i%2 == 0 { - tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr3), addr2, genesisBalance, params.TxGas, nil, nil), signer, key3) + tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr3), addr2, genesisBalance, ethparams.TxGas, nil, nil), signer, key3) gen.AddTx(tx) } else { - tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr2), addr3, genesisBalance, params.TxGas, nil, nil), signer, key2) + tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr2), addr3, genesisBalance, ethparams.TxGas, nil, nil), signer, key2) gen.AddTx(tx) } }) @@ -739,10 +740,10 @@ func TestBuildOnVariousStages(t *testing.T, create func(db ethdb.Database, gspec chain3, _, err := GenerateChain(gspec.Config, chain1[4], blockchain.engine, genDB, 10, 10, func(i int, gen *BlockGen) { // Send all funds back and forth between accounts 2 and 3. if i%2 == 0 { - tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr2), addr3, genesisBalance, params.TxGas, nil, nil), signer, key2) + tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr2), addr3, genesisBalance, ethparams.TxGas, nil, nil), signer, key2) gen.AddTx(tx) } else { - tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr3), addr2, genesisBalance, params.TxGas, nil, nil), signer, key3) + tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr3), addr2, genesisBalance, ethparams.TxGas, nil, nil), signer, key3) gen.AddTx(tx) } }) @@ -909,7 +910,7 @@ func TestReorgReInsert(t *testing.T, create func(db ethdb.Database, gspec *Genes numBlocks := 3 _, chain, _, err := GenerateChainWithGenesis(gspec, blockchain.engine, numBlocks, 10, func(i int, gen *BlockGen) { // Generate a transaction to create a unique block - tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(10000), params.TxGas, nil, nil), signer, key1) + tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(10000), ethparams.TxGas, nil, nil), signer, key1) gen.AddTx(tx) }) if err != nil { @@ -1015,7 +1016,7 @@ func TestAcceptBlockIdenticalStateRoot(t *testing.T, create func(db ethdb.Databa _, chain1, _, err := GenerateChainWithGenesis(gspec, blockchain.engine, 3, 10, func(i int, gen *BlockGen) { if i < 2 { // Send half the funds from addr1 to addr2 in one transaction per each of the two blocks in [chain1] - tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(500000000), params.TxGas, nil, nil), signer, key1) + tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(500000000), ethparams.TxGas, nil, nil), signer, key1) gen.AddTx(tx) } // Allow the third block to be empty. @@ -1027,10 +1028,10 @@ func TestAcceptBlockIdenticalStateRoot(t *testing.T, create func(db ethdb.Databa // Send 1/4 of the funds from addr1 to addr2 in tx1 and 3/4 of the funds in tx2. This will produce the identical state // root in the second block of [chain2] as is present in the second block of [chain1]. if i == 0 { - tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(250000000), params.TxGas, nil, nil), signer, key1) + tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(250000000), ethparams.TxGas, nil, nil), signer, key1) gen.AddTx(tx) } else { - tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(750000000), params.TxGas, nil, nil), signer, key1) + tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(750000000), ethparams.TxGas, nil, nil), signer, key1) gen.AddTx(tx) } }) @@ -1158,7 +1159,7 @@ func TestReprocessAcceptBlockIdenticalStateRoot(t *testing.T, create func(db eth _, chain1, _, err := GenerateChainWithGenesis(gspec, blockchain.engine, 3, 10, func(i int, gen *BlockGen) { if i < 2 { // Send half the funds from addr1 to addr2 in one transaction per each of the two blocks in [chain1] - tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(500000000), params.TxGas, nil, nil), signer, key1) + tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(500000000), ethparams.TxGas, nil, nil), signer, key1) gen.AddTx(tx) } // Allow the third block to be empty. @@ -1170,10 +1171,10 @@ func TestReprocessAcceptBlockIdenticalStateRoot(t *testing.T, create func(db eth // Send 1/4 of the funds from addr1 to addr2 in tx1 and 3/4 of the funds in tx2. This will produce the identical state // root in the second block of [chain2] as is present in the second block of [chain1]. if i == 0 { - tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(250000000), params.TxGas, nil, nil), signer, key1) + tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(250000000), ethparams.TxGas, nil, nil), signer, key1) gen.AddTx(tx) } else { - tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(750000000), params.TxGas, nil, nil), signer, key1) + tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(750000000), ethparams.TxGas, nil, nil), signer, key1) gen.AddTx(tx) } }) @@ -1315,7 +1316,7 @@ func TestGenerateChainInvalidBlockFee(t *testing.T, create func(db ethdb.Databas ChainID: params.TestChainConfig.ChainID, Nonce: gen.TxNonce(addr1), To: &addr2, - Gas: params.TxGas, + Gas: ethparams.TxGas, GasFeeCap: gen.BaseFee(), GasTipCap: big.NewInt(0), Data: []byte{}, @@ -1357,7 +1358,7 @@ func TestInsertChainInvalidBlockFee(t *testing.T, create func(db ethdb.Database, ChainID: params.TestChainConfig.ChainID, Nonce: gen.TxNonce(addr1), To: &addr2, - Gas: params.TxGas, + Gas: ethparams.TxGas, GasFeeCap: gen.BaseFee(), GasTipCap: big.NewInt(0), Data: []byte{}, @@ -1405,7 +1406,7 @@ func TestInsertChainValidBlockFee(t *testing.T, create func(db ethdb.Database, g ChainID: params.TestChainConfig.ChainID, Nonce: gen.TxNonce(addr1), To: &addr2, - Gas: params.TxGas, + Gas: ethparams.TxGas, Value: transfer, GasFeeCap: feeCap, GasTipCap: tip, @@ -1435,7 +1436,7 @@ func TestInsertChainValidBlockFee(t *testing.T, create func(db ethdb.Database, g genesisBalance := uint256.MustFromBig(genesisBalance) expectedBalance1 := new(uint256.Int).Sub(genesisBalance, transfer) baseFee := chain[0].BaseFee() - feeSpend := new(big.Int).Mul(new(big.Int).Add(baseFee, tip), new(big.Int).SetUint64(params.TxGas)) + feeSpend := new(big.Int).Mul(new(big.Int).Add(baseFee, tip), new(big.Int).SetUint64(ethparams.TxGas)) expectedBalance1.Sub(expectedBalance1, uint256.MustFromBig(feeSpend)) if balance1.Cmp(expectedBalance1) != 0 { return fmt.Errorf("expected addr1 balance: %d, found balance: %d", expectedBalance1, balance1) diff --git a/core/txindexer_test.go b/core/txindexer_test.go index 347bac340a..8b8a6a8604 100644 --- a/core/txindexer_test.go +++ b/core/txindexer_test.go @@ -28,6 +28,7 @@ import ( "github.com/ava-labs/libevm/core/types" "github.com/ava-labs/libevm/crypto" "github.com/ava-labs/libevm/ethdb" + ethparams "github.com/ava-labs/libevm/params" "github.com/stretchr/testify/require" ) @@ -49,14 +50,14 @@ func TestTransactionIndices(t *testing.T) { signer = types.LatestSigner(gspec.Config) ) genDb, blocks, _, err := GenerateChainWithGenesis(gspec, dummy.NewFakerWithCallbacks(TestCallbacks), 128, 10, func(i int, block *BlockGen) { - tx, err := types.SignTx(types.NewTransaction(block.TxNonce(addr1), addr2, big.NewInt(10000), params.TxGas, nil, nil), signer, key1) + tx, err := types.SignTx(types.NewTransaction(block.TxNonce(addr1), addr2, big.NewInt(10000), ethparams.TxGas, nil, nil), signer, key1) require.NoError(err) block.AddTx(tx) }) require.NoError(err) blocks2, _, err := GenerateChain(gspec.Config, blocks[len(blocks)-1], dummy.NewFakerWithCallbacks(TestCallbacks), genDb, 10, 10, func(i int, block *BlockGen) { - tx, err := types.SignTx(types.NewTransaction(block.TxNonce(addr1), addr2, big.NewInt(10000), params.TxGas, nil, nil), signer, key1) + tx, err := types.SignTx(types.NewTransaction(block.TxNonce(addr1), addr2, big.NewInt(10000), ethparams.TxGas, nil, nil), signer, key1) require.NoError(err) block.AddTx(tx) }) @@ -160,14 +161,14 @@ func TestTransactionSkipIndexing(t *testing.T) { signer = types.LatestSigner(gspec.Config) ) genDb, blocks, _, err := GenerateChainWithGenesis(gspec, dummy.NewFakerWithCallbacks(TestCallbacks), 5, 10, func(i int, block *BlockGen) { - tx, err := types.SignTx(types.NewTransaction(block.TxNonce(addr1), addr2, big.NewInt(10000), params.TxGas, nil, nil), signer, key1) + tx, err := types.SignTx(types.NewTransaction(block.TxNonce(addr1), addr2, big.NewInt(10000), ethparams.TxGas, nil, nil), signer, key1) require.NoError(err) block.AddTx(tx) }) require.NoError(err) blocks2, _, err := GenerateChain(gspec.Config, blocks[len(blocks)-1], dummy.NewFakerWithCallbacks(TestCallbacks), genDb, 5, 10, func(i int, block *BlockGen) { - tx, err := types.SignTx(types.NewTransaction(block.TxNonce(addr1), addr2, big.NewInt(10000), params.TxGas, nil, nil), signer, key1) + tx, err := types.SignTx(types.NewTransaction(block.TxNonce(addr1), addr2, big.NewInt(10000), ethparams.TxGas, nil, nil), signer, key1) require.NoError(err) block.AddTx(tx) }) diff --git a/core/txpool/blobpool/blobpool.go b/core/txpool/blobpool/blobpool.go index b947649ccc..9b51f3af35 100644 --- a/core/txpool/blobpool/blobpool.go +++ b/core/txpool/blobpool/blobpool.go @@ -50,6 +50,7 @@ import ( "github.com/ava-labs/libevm/event" "github.com/ava-labs/libevm/log" "github.com/ava-labs/libevm/metrics" + ethparams "github.com/ava-labs/libevm/params" "github.com/ava-labs/libevm/rlp" "github.com/holiman/billy" "github.com/holiman/uint256" @@ -58,12 +59,12 @@ import ( const ( // blobSize is the protocol constrained byte size of a single blob in a // transaction. There can be multiple of these embedded into a single tx. - blobSize = params.BlobTxFieldElementsPerBlob * params.BlobTxBytesPerFieldElement + blobSize = ethparams.BlobTxFieldElementsPerBlob * ethparams.BlobTxBytesPerFieldElement // maxBlobsPerTransaction is the maximum number of blobs a single transaction // is allowed to contain. Whilst the spec states it's unlimited, the block // data slots are protocol bound, which implicitly also limit this. - maxBlobsPerTransaction = params.MaxBlobGasPerBlock / params.BlobTxBlobGasPerBlob + maxBlobsPerTransaction = ethparams.MaxBlobGasPerBlock / ethparams.BlobTxBlobGasPerBlob // txAvgSize is an approximate byte size of a transaction metadata to avoid // tiny overflows causing all txs to move a shelf higher, wasting disk space. @@ -422,7 +423,7 @@ func (p *BlobPool) Init(gasTip uint64, head *types.Header, reserve txpool.Addres var ( // basefee = uint256.MustFromBig(eip1559.CalcBaseFee(p.chain.Config(), p.head)) basefee = uint256.MustFromBig(baseFee) - blobfee = uint256.NewInt(params.BlobTxMinBlobGasprice) + blobfee = uint256.NewInt(ethparams.BlobTxMinBlobGasprice) ) if p.head.ExcessBlobGas != nil { blobfee = uint256.MustFromBig(eip4844.CalcBlobFee(*p.head.ExcessBlobGas)) @@ -853,7 +854,7 @@ func (p *BlobPool) Reset(oldHead, newHead *types.Header) { var ( // basefee = uint256.MustFromBig(eip1559.CalcBaseFee(p.chain.Config(), newHead)) basefee = uint256.MustFromBig(baseFeeBig) - blobfee = uint256.MustFromBig(big.NewInt(params.BlobTxMinBlobGasprice)) + blobfee = uint256.MustFromBig(big.NewInt(ethparams.BlobTxMinBlobGasprice)) ) if newHead.ExcessBlobGas != nil { blobfee = uint256.MustFromBig(eip4844.CalcBlobFee(*newHead.ExcessBlobGas)) diff --git a/core/txpool/blobpool/blobpool_test.go b/core/txpool/blobpool/blobpool_test.go index 3402cdeb8e..23dfdabf27 100644 --- a/core/txpool/blobpool/blobpool_test.go +++ b/core/txpool/blobpool/blobpool_test.go @@ -53,6 +53,7 @@ import ( "github.com/ava-labs/libevm/crypto/kzg4844" "github.com/ava-labs/libevm/ethdb/memorydb" "github.com/ava-labs/libevm/log" + ethparams "github.com/ava-labs/libevm/params" "github.com/ava-labs/libevm/rlp" "github.com/holiman/billy" "github.com/holiman/uint256" @@ -584,7 +585,7 @@ func TestOpenDrops(t *testing.T) { chain := &testBlockChain{ config: testChainConfig, basefee: uint256.NewInt(ap3.MinBaseFee), - blobfee: uint256.NewInt(params.BlobTxMinBlobGasprice), + blobfee: uint256.NewInt(ethparams.BlobTxMinBlobGasprice), statedb: statedb, } pool := New(Config{Datadir: storage}, chain) @@ -703,7 +704,7 @@ func TestOpenIndex(t *testing.T) { chain := &testBlockChain{ config: testChainConfig, basefee: uint256.NewInt(ap3.MinBaseFee), - blobfee: uint256.NewInt(params.BlobTxMinBlobGasprice), + blobfee: uint256.NewInt(ethparams.BlobTxMinBlobGasprice), statedb: statedb, } pool := New(Config{Datadir: storage}, chain) @@ -1262,7 +1263,7 @@ func TestAdd(t *testing.T) { }, { // Same as above but blob fee cap equals minimum, should be accepted from: "alice", - tx: makeUnsignedTx(0, 1, 1, params.BlobTxMinBlobGasprice), + tx: makeUnsignedTx(0, 1, 1, ethparams.BlobTxMinBlobGasprice), err: nil, }, }, @@ -1337,7 +1338,7 @@ func BenchmarkPoolPending10GB(b *testing.B) { benchmarkPoolPending(b, 10_000_00 func benchmarkPoolPending(b *testing.B, datacap uint64) { // Calculate the maximum number of transaction that would fit into the pool // and generate a set of random accounts to seed them with. - capacity := datacap / params.BlobTxBlobGasPerBlob + capacity := datacap / ethparams.BlobTxBlobGasPerBlob var ( basefee = uint64(1050) diff --git a/core/txpool/blobpool/evictheap_test.go b/core/txpool/blobpool/evictheap_test.go index 70bd772b5d..618b1ccc49 100644 --- a/core/txpool/blobpool/evictheap_test.go +++ b/core/txpool/blobpool/evictheap_test.go @@ -31,8 +31,8 @@ import ( mrand "math/rand" "testing" - "github.com/ava-labs/coreth/params" "github.com/ava-labs/libevm/common" + ethparams "github.com/ava-labs/libevm/params" "github.com/holiman/uint256" ) @@ -196,7 +196,7 @@ func BenchmarkPriceHeapReinit100GB(b *testing.B) { benchmarkPriceHeapReinit(b, 1 func benchmarkPriceHeapReinit(b *testing.B, datacap uint64) { // Calculate how many unique transactions we can fit into the provided disk // data cap - blobs := datacap / (params.BlobTxBytesPerFieldElement * params.BlobTxFieldElementsPerBlob) + blobs := datacap / (ethparams.BlobTxBytesPerFieldElement * ethparams.BlobTxFieldElementsPerBlob) // Create a random set of transactions with random fees. Use a separate account // for each transaction to make it worse case. @@ -256,7 +256,7 @@ func BenchmarkPriceHeapOverflow100GB(b *testing.B) { benchmarkPriceHeapOverflow( func benchmarkPriceHeapOverflow(b *testing.B, datacap uint64) { // Calculate how many unique transactions we can fit into the provided disk // data cap - blobs := datacap / (params.BlobTxBytesPerFieldElement * params.BlobTxFieldElementsPerBlob) + blobs := datacap / (ethparams.BlobTxBytesPerFieldElement * ethparams.BlobTxFieldElementsPerBlob) // Create a random set of transactions with random fees. Use a separate account // for each transaction to make it worse case. diff --git a/core/txpool/validation.go b/core/txpool/validation.go index fabcda6bb3..94bd5437b2 100644 --- a/core/txpool/validation.go +++ b/core/txpool/validation.go @@ -39,12 +39,13 @@ import ( "github.com/ava-labs/libevm/core/vm" "github.com/ava-labs/libevm/crypto/kzg4844" "github.com/ava-labs/libevm/log" + ethparams "github.com/ava-labs/libevm/params" ) var ( // blobTxMinBlobGasPrice is the big.Int version of the configured protocol // parameter to avoid constucting a new big integer for every transaction. - blobTxMinBlobGasPrice = big.NewInt(params.BlobTxMinBlobGasprice) + blobTxMinBlobGasPrice = big.NewInt(ethparams.BlobTxMinBlobGasprice) ) // ValidationOptions define certain differences between transaction validation @@ -84,8 +85,8 @@ func ValidateTransaction(tx *types.Transaction, head *types.Header, signer types return fmt.Errorf("%w: type %d rejected, pool not yet in Cancun", core.ErrTxTypeNotSupported, tx.Type()) } // Check whether the init code size has been exceeded - if opts.Config.IsShanghai(head.Number, head.Time) && tx.To() == nil && len(tx.Data()) > params.MaxInitCodeSize { - return fmt.Errorf("%w: code size %v, limit %v", vm.ErrMaxInitCodeSizeExceeded, len(tx.Data()), params.MaxInitCodeSize) + if opts.Config.IsShanghai(head.Number, head.Time) && tx.To() == nil && len(tx.Data()) > ethparams.MaxInitCodeSize { + return fmt.Errorf("%w: code size %v, limit %v", vm.ErrMaxInitCodeSizeExceeded, len(tx.Data()), ethparams.MaxInitCodeSize) } // Transactions can't be negative. This may never happen using RLP decoded // transactions but may occur for transactions created using the RPC. @@ -145,8 +146,8 @@ func ValidateTransaction(tx *types.Transaction, head *types.Header, signer types if len(hashes) == 0 { return fmt.Errorf("blobless blob transaction") } - if len(hashes) > params.MaxBlobGasPerBlock/params.BlobTxBlobGasPerBlob { - return fmt.Errorf("too many blobs in transaction: have %d, permitted %d", len(hashes), params.MaxBlobGasPerBlock/params.BlobTxBlobGasPerBlob) + if len(hashes) > ethparams.MaxBlobGasPerBlock/ethparams.BlobTxBlobGasPerBlob { + return fmt.Errorf("too many blobs in transaction: have %d, permitted %d", len(hashes), ethparams.MaxBlobGasPerBlock/ethparams.BlobTxBlobGasPerBlob) } // Ensure commitments, proofs and hashes are valid if err := validateBlobSidecar(hashes, sidecar); err != nil { diff --git a/core/vm/runtime/runtime.go b/core/vm/runtime/runtime.go index 9f7b4f36f9..7f288aad55 100644 --- a/core/vm/runtime/runtime.go +++ b/core/vm/runtime/runtime.go @@ -39,6 +39,7 @@ import ( "github.com/ava-labs/libevm/core/types" "github.com/ava-labs/libevm/core/vm" "github.com/ava-labs/libevm/crypto" + ethparams "github.com/ava-labs/libevm/params" "github.com/holiman/uint256" ) @@ -121,7 +122,7 @@ func setDefaults(cfg *Config) { cfg.BaseFee = big.NewInt(ap3.InitialBaseFee) } if cfg.BlobBaseFee == nil { - cfg.BlobBaseFee = big.NewInt(params.BlobTxMinBlobGasprice) + cfg.BlobBaseFee = big.NewInt(ethparams.BlobTxMinBlobGasprice) } } diff --git a/eth/gasestimator/gasestimator.go b/eth/gasestimator/gasestimator.go index b97f1b6747..a5e43ee13e 100644 --- a/eth/gasestimator/gasestimator.go +++ b/eth/gasestimator/gasestimator.go @@ -40,6 +40,7 @@ import ( "github.com/ava-labs/libevm/core/types" "github.com/ava-labs/libevm/core/vm" "github.com/ava-labs/libevm/log" + ethparams "github.com/ava-labs/libevm/params" ) // Options are the contextual parameters to execute the requested call. @@ -67,7 +68,7 @@ func Estimate(ctx context.Context, call *core.Message, opts *Options, gasCap uin ) // Determine the highest gas limit can be used during the estimation. hi = opts.Header.GasLimit - if call.GasLimit >= params.TxGas { + if call.GasLimit >= ethparams.TxGas { hi = call.GasLimit } // Normalize the max fee per gas the call is willing to spend. @@ -114,9 +115,9 @@ func Estimate(ctx context.Context, call *core.Message, opts *Options, gasCap uin // unused access list items). Ever so slightly wasteful, but safer overall. if len(call.Data) == 0 { if call.To != nil && opts.State.GetCodeSize(*call.To) == 0 { - failed, _, err := execute(ctx, call, opts, params.TxGas) + failed, _, err := execute(ctx, call, opts, ethparams.TxGas) if !failed && err == nil { - return params.TxGas, nil, nil + return ethparams.TxGas, nil, nil } } } @@ -142,7 +143,7 @@ func Estimate(ctx context.Context, call *core.Message, opts *Options, gasCap uin // There's a fairly high chance for the transaction to execute successfully // with gasLimit set to the first execution's usedGas + gasRefund. Explicitly // check that gas amount and use as a limit for the binary search. - optimisticGasLimit := (result.UsedGas + result.RefundedGas + params.CallStipend) * 64 / 63 + optimisticGasLimit := (result.UsedGas + result.RefundedGas + ethparams.CallStipend) * 64 / 63 if optimisticGasLimit < hi { failed, _, err = execute(ctx, call, opts, optimisticGasLimit) if err != nil { diff --git a/eth/gasprice/feehistory_test.go b/eth/gasprice/feehistory_test.go index 2e2d1cc8c0..5a2627a06c 100644 --- a/eth/gasprice/feehistory_test.go +++ b/eth/gasprice/feehistory_test.go @@ -33,12 +33,12 @@ import ( "testing" "github.com/ava-labs/coreth/core" - "github.com/ava-labs/libevm/core/types" - "github.com/stretchr/testify/require" - "github.com/ava-labs/coreth/params" "github.com/ava-labs/coreth/rpc" "github.com/ava-labs/libevm/common" + "github.com/ava-labs/libevm/core/types" + ethparams "github.com/ava-labs/libevm/params" + "github.com/stretchr/testify/require" ) func TestFeeHistory(t *testing.T) { @@ -94,7 +94,7 @@ func TestFeeHistory(t *testing.T) { ChainID: params.TestChainConfig.ChainID, Nonce: b.TxNonce(addr), To: &common.Address{}, - Gas: params.TxGas, + Gas: ethparams.TxGas, GasFeeCap: feeCap, GasTipCap: tip, Data: []byte{}, diff --git a/eth/gasprice/gasprice_test.go b/eth/gasprice/gasprice_test.go index 5dcc3c02e0..4d0be49a0f 100644 --- a/eth/gasprice/gasprice_test.go +++ b/eth/gasprice/gasprice_test.go @@ -47,6 +47,7 @@ import ( "github.com/ava-labs/libevm/core/vm" "github.com/ava-labs/libevm/crypto" "github.com/ava-labs/libevm/event" + ethparams "github.com/ava-labs/libevm/params" "github.com/stretchr/testify/require" ) @@ -235,7 +236,7 @@ func testGenBlock(t *testing.T, tip int64, numTx int) func(int, *core.BlockGen) ChainID: params.TestChainConfig.ChainID, Nonce: b.TxNonce(addr), To: &common.Address{}, - Gas: params.TxGas, + Gas: ethparams.TxGas, GasFeeCap: feeCap, GasTipCap: txTip, Data: []byte{}, @@ -294,7 +295,7 @@ func TestSuggestTipCapSmallTips(t *testing.T) { ChainID: params.TestChainConfig.ChainID, Nonce: b.TxNonce(addr), To: &common.Address{}, - Gas: params.TxGas, + Gas: ethparams.TxGas, GasFeeCap: feeCap, GasTipCap: tip, Data: []byte{}, @@ -308,7 +309,7 @@ func TestSuggestTipCapSmallTips(t *testing.T) { ChainID: params.TestChainConfig.ChainID, Nonce: b.TxNonce(addr), To: &common.Address{}, - Gas: params.TxGas, + Gas: ethparams.TxGas, GasFeeCap: feeCap, GasTipCap: common.Big1, Data: []byte{}, @@ -361,7 +362,7 @@ func TestSuggestGasPricePreAP3(t *testing.T) { tx := types.NewTx(&types.LegacyTx{ Nonce: b.TxNonce(addr), To: &common.Address{}, - Gas: params.TxGas, + Gas: ethparams.TxGas, GasPrice: gasPrice, Data: []byte{}, }) @@ -403,7 +404,7 @@ func TestSuggestTipCapIncludesExtraDataGas(t *testing.T) { applyGasPriceTest(t, suggestTipCapTest{ chainConfig: params.TestChainConfig, numBlocks: 1000, - extDataGasUsage: big.NewInt(acp176.MinMaxPerSecond - int64(params.TxGas)), + extDataGasUsage: big.NewInt(acp176.MinMaxPerSecond - int64(ethparams.TxGas)), // The tip on the transaction is very large to pay the block gas cost. genBlock: testGenBlock(t, 100_000, 1), // The actual tip doesn't matter, we just want to ensure that the tip is diff --git a/eth/tracers/api_test.go b/eth/tracers/api_test.go index 053953d09e..5c2069f943 100644 --- a/eth/tracers/api_test.go +++ b/eth/tracers/api_test.go @@ -52,6 +52,7 @@ import ( "github.com/ava-labs/libevm/crypto" "github.com/ava-labs/libevm/eth/tracers/logger" "github.com/ava-labs/libevm/ethdb" + ethparams "github.com/ava-labs/libevm/params" "golang.org/x/exp/slices" ) @@ -245,7 +246,7 @@ func TestTraceCall(t *testing.T) { Nonce: nonce, To: &accounts[1].addr, Value: big.NewInt(1000), - Gas: params.TxGas, + Gas: ethparams.TxGas, GasPrice: b.BaseFee(), Data: nil}), signer, accounts[0].key) @@ -258,7 +259,7 @@ func TestTraceCall(t *testing.T) { Nonce: nonce, To: &accounts[2].addr, Value: big.NewInt(1000), - Gas: params.TxGas, + Gas: ethparams.TxGas, GasPrice: b.BaseFee(), Data: nil}), signer, accounts[0].key) @@ -270,7 +271,7 @@ func TestTraceCall(t *testing.T) { Nonce: nonce, To: &accounts[1].addr, Value: big.NewInt(1000), - Gas: params.TxGas, + Gas: ethparams.TxGas, GasPrice: b.BaseFee(), Data: nil}), signer, accounts[0].key) @@ -461,7 +462,7 @@ func TestTraceTransaction(t *testing.T) { Nonce: uint64(i), To: &accounts[1].addr, Value: big.NewInt(1000), - Gas: params.TxGas, + Gas: ethparams.TxGas, GasPrice: new(big.Int).Add(b.BaseFee(), big.NewInt(int64(500*params.GWei))), Data: nil}), signer, accounts[0].key) @@ -479,7 +480,7 @@ func TestTraceTransaction(t *testing.T) { t.Errorf("failed to unmarshal result %v", err) } expected := &logger.ExecutionResult{ - Gas: params.TxGas, + Gas: ethparams.TxGas, Failed: false, ReturnValue: "", StructLogs: []logger.StructLogRes{}, @@ -519,7 +520,7 @@ func TestTraceBlock(t *testing.T) { Nonce: uint64(i), To: &accounts[1].addr, Value: big.NewInt(1000), - Gas: params.TxGas, + Gas: ethparams.TxGas, GasPrice: b.BaseFee(), Data: nil}), signer, accounts[0].key) @@ -616,7 +617,7 @@ func TestTracingWithOverrides(t *testing.T) { Nonce: uint64(i), To: &accounts[1].addr, Value: big.NewInt(1000), - Gas: params.TxGas, + Gas: ethparams.TxGas, GasPrice: b.BaseFee(), Data: nil}), signer, accounts[0].key) @@ -981,7 +982,7 @@ func TestTraceChain(t *testing.T) { // value: 1000 wei // fee: 0 wei for j := 0; j < i+1; j++ { - tx, _ := types.SignTx(types.NewTransaction(nonce, accounts[1].addr, big.NewInt(1000), params.TxGas, b.BaseFee(), nil), signer, accounts[0].key) + tx, _ := types.SignTx(types.NewTransaction(nonce, accounts[1].addr, big.NewInt(1000), ethparams.TxGas, b.BaseFee(), nil), signer, accounts[0].key) b.AddTx(tx) nonce += 1 } diff --git a/ethclient/simulated/backend_test.go b/ethclient/simulated/backend_test.go index 5afb2cb96c..0b64890ed8 100644 --- a/ethclient/simulated/backend_test.go +++ b/ethclient/simulated/backend_test.go @@ -25,11 +25,11 @@ import ( "time" "github.com/ava-labs/coreth/accounts/abi/bind" - "github.com/ava-labs/coreth/params" "github.com/ava-labs/coreth/rpc" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/types" "github.com/ava-labs/libevm/crypto" + ethparams "github.com/ava-labs/libevm/params" "github.com/stretchr/testify/require" ) @@ -53,7 +53,7 @@ func newTx(sim *Backend, key *ecdsa.PrivateKey) (*types.Transaction, error) { // create a signed transaction to send head, _ := client.HeaderByNumber(context.Background(), nil) // Should be child's, good enough - gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(params.GWei)) + gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(ethparams.GWei)) addr := crypto.PubkeyToAddress(key.PublicKey) chainid, _ := client.ChainID(context.Background()) nonce, err := client.NonceAt(context.Background(), addr, nil) @@ -63,7 +63,7 @@ func newTx(sim *Backend, key *ecdsa.PrivateKey) (*types.Transaction, error) { tx := types.NewTx(&types.DynamicFeeTx{ ChainID: chainid, Nonce: nonce, - GasTipCap: big.NewInt(params.GWei), + GasTipCap: big.NewInt(ethparams.GWei), GasFeeCap: gasPrice, Gas: 21000, To: &addr, @@ -266,7 +266,7 @@ func TestCommitReturnValue(t *testing.T) { // Create a block in the original chain (containing a transaction to force different block hashes) head, _ := client.HeaderByNumber(ctx, nil) // Should be child's, good enough gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1)) - _tx := types.NewTransaction(0, testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil) + _tx := types.NewTransaction(0, testAddr, big.NewInt(1000), ethparams.TxGas, gasPrice, nil) tx, _ := types.SignTx(_tx, types.LatestSignerForChainID(chainid), testKey) if err := client.SendTransaction(ctx, tx); err != nil { t.Fatalf("sending transaction: %v", err) diff --git a/ethclient/simulated/options_test.go b/ethclient/simulated/options_test.go index 0b27fbbd48..f2fe28612a 100644 --- a/ethclient/simulated/options_test.go +++ b/ethclient/simulated/options_test.go @@ -23,10 +23,10 @@ import ( "testing" "github.com/ava-labs/coreth/core" - "github.com/ava-labs/coreth/params" "github.com/ava-labs/coreth/plugin/evm/upgrade/acp176" ethereum "github.com/ava-labs/libevm" "github.com/ava-labs/libevm/core/types" + ethparams "github.com/ava-labs/libevm/params" ) // Tests that the simulator starts with the initial gas limit in the genesis block, @@ -60,7 +60,7 @@ func TestWithCallGasLimitOption(t *testing.T) { // Construct a simulator, targeting a different gas limit sim := NewBackend(types.GenesisAlloc{ testAddr: {Balance: big.NewInt(10000000000000000)}, - }, WithCallGasLimit(params.TxGas-1)) + }, WithCallGasLimit(ethparams.TxGas-1)) defer sim.Close() client := sim.Client() diff --git a/internal/ethapi/api_test.go b/internal/ethapi/api_test.go index 3155805247..1121436845 100644 --- a/internal/ethapi/api_test.go +++ b/internal/ethapi/api_test.go @@ -62,6 +62,7 @@ import ( "github.com/ava-labs/libevm/crypto/kzg4844" "github.com/ava-labs/libevm/ethdb" "github.com/ava-labs/libevm/event" + ethparams "github.com/ava-labs/libevm/params" "github.com/holiman/uint256" "github.com/stretchr/testify/require" "golang.org/x/exp/slices" @@ -654,7 +655,7 @@ func TestEstimateGas(t *testing.T) { // Transfer from account[0] to account[1] // value: 1000 wei // fee: 0 wei - tx, _ := types.SignTx(types.NewTx(&types.LegacyTx{Nonce: uint64(i), To: &accounts[1].addr, Value: big.NewInt(1000), Gas: params.TxGas, GasPrice: b.BaseFee(), Data: nil}), signer, accounts[0].key) + tx, _ := types.SignTx(types.NewTx(&types.LegacyTx{Nonce: uint64(i), To: &accounts[1].addr, Value: big.NewInt(1000), Gas: ethparams.TxGas, GasPrice: b.BaseFee(), Data: nil}), signer, accounts[0].key) b.AddTx(tx) // b.SetPoS() })) @@ -815,7 +816,7 @@ func TestCall(t *testing.T) { // Transfer from account[0] to account[1] // value: 1000 wei // fee: 0 wei - tx, _ := types.SignTx(types.NewTx(&types.LegacyTx{Nonce: uint64(i), To: &accounts[1].addr, Value: big.NewInt(1000), Gas: params.TxGas, GasPrice: b.BaseFee(), Data: nil}), signer, accounts[0].key) + tx, _ := types.SignTx(types.NewTx(&types.LegacyTx{Nonce: uint64(i), To: &accounts[1].addr, Value: big.NewInt(1000), Gas: ethparams.TxGas, GasPrice: b.BaseFee(), Data: nil}), signer, accounts[0].key) b.AddTx(tx) // b.SetPoS() })) @@ -1583,7 +1584,7 @@ func TestRPCGetBlockOrHeader(t *testing.T) { // Transfer from account[0] to account[1] // value: 1000 wei // fee: 0 wei - tx, _ := types.SignTx(types.NewTx(&types.LegacyTx{Nonce: uint64(i), To: &acc2Addr, Value: big.NewInt(1000), Gas: params.TxGas, GasPrice: b.BaseFee(), Data: nil}), signer, acc1Key) + tx, _ := types.SignTx(types.NewTx(&types.LegacyTx{Nonce: uint64(i), To: &acc2Addr, Value: big.NewInt(1000), Gas: ethparams.TxGas, GasPrice: b.BaseFee(), Data: nil}), signer, acc1Key) b.AddTx(tx) }) api := NewBlockChainAPI(backend) @@ -1842,7 +1843,7 @@ func setupReceiptBackend(t *testing.T, genBlocks int) (*testBackend, []common.Ha switch i { case 0: // transfer 1000wei - tx, err = types.SignTx(types.NewTx(&types.LegacyTx{Nonce: uint64(i), To: &acc2Addr, Value: big.NewInt(1000), Gas: params.TxGas, GasPrice: b.BaseFee(), Data: nil}), types.HomesteadSigner{}, acc1Key) + tx, err = types.SignTx(types.NewTx(&types.LegacyTx{Nonce: uint64(i), To: &acc2Addr, Value: big.NewInt(1000), Gas: ethparams.TxGas, GasPrice: b.BaseFee(), Data: nil}), types.HomesteadSigner{}, acc1Key) case 1: // create contract tx, err = types.SignTx(types.NewTx(&types.LegacyTx{Nonce: uint64(i), To: nil, Gas: 53100, GasPrice: b.BaseFee(), Data: common.FromHex("0x60806040")}), signer, acc1Key) @@ -1873,7 +1874,7 @@ func setupReceiptBackend(t *testing.T, genBlocks int) (*testBackend, []common.Ha Nonce: uint64(i), GasTipCap: uint256.NewInt(1), GasFeeCap: uint256.MustFromBig(fee), - Gas: params.TxGas, + Gas: ethparams.TxGas, To: acc2Addr, BlobFeeCap: uint256.NewInt(1), BlobHashes: []common.Hash{{1}}, diff --git a/internal/ethapi/transaction_args.go b/internal/ethapi/transaction_args.go index 30e7d25234..c804123eb0 100644 --- a/internal/ethapi/transaction_args.go +++ b/internal/ethapi/transaction_args.go @@ -36,7 +36,6 @@ import ( "github.com/ava-labs/coreth/consensus/misc/eip4844" "github.com/ava-labs/coreth/core" - "github.com/ava-labs/coreth/params" "github.com/ava-labs/coreth/rpc" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/common/hexutil" @@ -44,11 +43,12 @@ import ( "github.com/ava-labs/libevm/core/types" "github.com/ava-labs/libevm/crypto/kzg4844" "github.com/ava-labs/libevm/log" + ethparams "github.com/ava-labs/libevm/params" "github.com/holiman/uint256" ) var ( - maxBlobsPerTransaction = params.MaxBlobGasPerBlock / params.BlobTxBlobGasPerBlob + maxBlobsPerTransaction = ethparams.MaxBlobGasPerBlock / ethparams.BlobTxBlobGasPerBlob ) // TransactionArgs represents the arguments to construct a new transaction @@ -195,7 +195,7 @@ func (args *TransactionArgs) setDefaults(ctx context.Context, b Backend, skipGas type feeBackend interface { SuggestGasTipCap(ctx context.Context) (*big.Int, error) CurrentHeader() *types.Header - ChainConfig() *params.ChainConfig + ChainConfig() *ethparams.ChainConfig } // setFeeDefaults fills in default fee values for unspecified tx fields. diff --git a/miner/worker.go b/miner/worker.go index ab36654927..2460e2a6a9 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -52,6 +52,7 @@ import ( "github.com/ava-labs/libevm/core/vm" "github.com/ava-labs/libevm/event" "github.com/ava-labs/libevm/log" + ethparams "github.com/ava-labs/libevm/params" "github.com/holiman/uint256" ) @@ -76,7 +77,7 @@ type environment struct { blobs int size uint64 - rules params.Rules + rules ethparams.Rules predicateContext *precompileconfig.PredicateContext // predicateResults contains the results of checking the predicates for each transaction in the miner. // The results are accumulated as transactions are executed by the miner and set on the BlockContext. @@ -91,7 +92,7 @@ type environment struct { // and gathering the sealing result. type worker struct { config *Config - chainConfig *params.ChainConfig + chainConfig *ethparams.ChainConfig engine consensus.Engine eth Backend chain *core.BlockChain @@ -108,7 +109,7 @@ type worker struct { beaconRoot *common.Hash // TODO: set to empty hash, retained for upstream compatibility and future use } -func newWorker(config *Config, chainConfig *params.ChainConfig, engine consensus.Engine, eth Backend, mux *event.TypeMux, clock *mockable.Clock) *worker { +func newWorker(config *Config, chainConfig *ethparams.ChainConfig, engine consensus.Engine, eth Backend, mux *event.TypeMux, clock *mockable.Clock) *worker { worker := &worker{ config: config, chainConfig: chainConfig, @@ -306,7 +307,7 @@ func (w *worker) commitBlobTransaction(env *environment, tx *types.Transaction, // isn't really a better place right now. The blob gas limit is checked at block validation time // and not during execution. This means core.ApplyTransaction will not return an error if the // tx has too many blobs. So we have to explicitly check it here. - if (env.blobs+len(sc.Blobs))*params.BlobTxBlobGasPerBlob > params.MaxBlobGasPerBlock { + if (env.blobs+len(sc.Blobs))*ethparams.BlobTxBlobGasPerBlob > ethparams.MaxBlobGasPerBlock { return nil, errors.New("max data blobs reached") } receipt, err := w.applyTransaction(env, tx, coinbase) @@ -359,20 +360,20 @@ func (w *worker) applyTransaction(env *environment, tx *types.Transaction, coinb func (w *worker) commitTransactions(env *environment, plainTxs, blobTxs *transactionsByPriceAndNonce, coinbase common.Address) { for { // If we don't have enough gas for any further transactions then we're done. - if env.gasPool.Gas() < params.TxGas { - log.Trace("Not enough gas for further transactions", "have", env.gasPool, "want", params.TxGas) + if env.gasPool.Gas() < ethparams.TxGas { + log.Trace("Not enough gas for further transactions", "have", env.gasPool, "want", ethparams.TxGas) break } // If we don't have enough blob space for any further blob transactions, // skip that list altogether - if !blobTxs.Empty() && env.blobs*params.BlobTxBlobGasPerBlob >= params.MaxBlobGasPerBlock { + if !blobTxs.Empty() && env.blobs*ethparams.BlobTxBlobGasPerBlob >= ethparams.MaxBlobGasPerBlock { log.Trace("Not enough blob space for further blob transactions") blobTxs.Clear() // Fall though to pick up any plain txs } // If we don't have enough blob space for any further blob transactions, // skip that list altogether - if !blobTxs.Empty() && env.blobs*params.BlobTxBlobGasPerBlob >= params.MaxBlobGasPerBlock { + if !blobTxs.Empty() && env.blobs*ethparams.BlobTxBlobGasPerBlob >= ethparams.MaxBlobGasPerBlock { log.Trace("Not enough blob space for further blob transactions") blobTxs.Clear() // Fall though to pick up any plain txs @@ -406,7 +407,7 @@ func (w *worker) commitTransactions(env *environment, plainTxs, blobTxs *transac txs.Pop() continue } - if left := uint64(params.MaxBlobGasPerBlock - env.blobs*params.BlobTxBlobGasPerBlob); left < ltx.BlobGas { + if left := uint64(ethparams.MaxBlobGasPerBlock - env.blobs*ethparams.BlobTxBlobGasPerBlob); left < ltx.BlobGas { log.Trace("Not enough blob gas left for transaction", "hash", ltx.Hash, "left", left, "needed", ltx.BlobGas) txs.Pop() continue @@ -514,7 +515,7 @@ func (w *worker) handleResult(env *environment, block *types.Block, createdAt ti logs = append(logs, receipt.Logs...) } fees := totalFees(block, receipts) - feesInEther := new(big.Float).Quo(new(big.Float).SetInt(fees), big.NewFloat(params.Ether)) + feesInEther := new(big.Float).Quo(new(big.Float).SetInt(fees), big.NewFloat(ethparams.Ether)) log.Info("Commit new mining work", "number", block.Number(), "hash", hash, "uncles", 0, "txs", env.tcount, "gas", block.GasUsed(), "fees", feesInEther, diff --git a/nativeasset/contract_test.go b/nativeasset/contract_test.go index cfa2b7e268..f7111eb8b2 100644 --- a/nativeasset/contract_test.go +++ b/nativeasset/contract_test.go @@ -14,6 +14,7 @@ import ( "github.com/ava-labs/libevm/core/rawdb" ethtypes "github.com/ava-labs/libevm/core/types" "github.com/ava-labs/libevm/core/vm" + ethparams "github.com/ava-labs/libevm/params" "github.com/holiman/uint256" "github.com/stretchr/testify/assert" @@ -238,7 +239,7 @@ func TestStatefulPrecompile(t *testing.T) { precompileAddr: NativeAssetCallAddr, input: PackNativeAssetCallInput(userAddr2, assetID, big.NewInt(50), nil), value: big0, - gasInput: params.AssetCallApricot + params.CallNewAccountGas + 123, + gasInput: params.AssetCallApricot + ethparams.CallNewAccountGas + 123, expectedGasRemaining: 123, expectedErr: nil, expectedResult: nil, @@ -271,7 +272,7 @@ func TestStatefulPrecompile(t *testing.T) { precompileAddr: NativeAssetCallAddr, input: PackNativeAssetCallInput(userAddr2, assetID, big.NewInt(50), nil), value: uint256.NewInt(49), - gasInput: params.AssetCallApricot + params.CallNewAccountGas, + gasInput: params.AssetCallApricot + ethparams.CallNewAccountGas, expectedGasRemaining: 0, expectedErr: nil, expectedResult: nil, @@ -391,7 +392,7 @@ func TestStatefulPrecompile(t *testing.T) { precompileAddr: NativeAssetCallAddr, input: PackNativeAssetCallInput(userAddr2, assetID, big.NewInt(50), nil), value: uint256.NewInt(50), - gasInput: params.AssetCallApricot + params.CallNewAccountGas - 1, + gasInput: params.AssetCallApricot + ethparams.CallNewAccountGas - 1, expectedGasRemaining: 0, expectedErr: vm.ErrOutOfGas, expectedResult: nil, @@ -423,8 +424,8 @@ func TestStatefulPrecompile(t *testing.T) { precompileAddr: NativeAssetCallAddr, input: make([]byte, 24), value: uint256.NewInt(50), - gasInput: params.AssetCallApricot + params.CallNewAccountGas, - expectedGasRemaining: params.CallNewAccountGas, + gasInput: params.AssetCallApricot + ethparams.CallNewAccountGas, + expectedGasRemaining: ethparams.CallNewAccountGas, expectedErr: vm.ErrExecutionReverted, expectedResult: nil, name: "native asset call: invalid input", @@ -444,8 +445,8 @@ func TestStatefulPrecompile(t *testing.T) { precompileAddr: GenesisContractAddr, input: PackNativeAssetCallInput(userAddr2, assetID, big.NewInt(50), nil), value: big0, - gasInput: params.AssetCallApricot + params.CallNewAccountGas, - expectedGasRemaining: params.AssetCallApricot + params.CallNewAccountGas, + gasInput: params.AssetCallApricot + ethparams.CallNewAccountGas, + expectedGasRemaining: params.AssetCallApricot + ethparams.CallNewAccountGas, expectedErr: vm.ErrExecutionReverted, expectedResult: nil, name: "deprecated contract", diff --git a/params/hooks_libevm.go b/params/hooks_libevm.go index af7e24a25f..54d781216c 100644 --- a/params/hooks_libevm.go +++ b/params/hooks_libevm.go @@ -20,6 +20,7 @@ import ( "github.com/ava-labs/libevm/core/vm" "github.com/ava-labs/libevm/libevm" "github.com/ava-labs/libevm/libevm/legacy" + ethparams "github.com/ava-labs/libevm/params" ) type RulesExtra extras.Rules @@ -40,7 +41,7 @@ func (r RulesExtra) CanExecuteTransaction(_ common.Address, _ *common.Address, _ var PrecompiledContractsApricotPhase2 = map[common.Address]contract.StatefulPrecompiledContract{ nativeasset.GenesisContractAddr: &nativeasset.DeprecatedContract{}, nativeasset.NativeAssetBalanceAddr: &nativeasset.NativeAssetBalance{GasCost: AssetBalanceApricot}, - nativeasset.NativeAssetCallAddr: &nativeasset.NativeAssetCall{GasCost: AssetCallApricot, CallNewAccountGas: CallNewAccountGas}, + nativeasset.NativeAssetCallAddr: &nativeasset.NativeAssetCall{GasCost: AssetCallApricot, CallNewAccountGas: ethparams.CallNewAccountGas}, } var PrecompiledContractsApricotPhasePre6 = map[common.Address]contract.StatefulPrecompiledContract{ @@ -52,7 +53,7 @@ var PrecompiledContractsApricotPhasePre6 = map[common.Address]contract.StatefulP var PrecompiledContractsApricotPhase6 = map[common.Address]contract.StatefulPrecompiledContract{ nativeasset.GenesisContractAddr: &nativeasset.DeprecatedContract{}, nativeasset.NativeAssetBalanceAddr: &nativeasset.NativeAssetBalance{GasCost: AssetBalanceApricot}, - nativeasset.NativeAssetCallAddr: &nativeasset.NativeAssetCall{GasCost: AssetCallApricot, CallNewAccountGas: CallNewAccountGas}, + nativeasset.NativeAssetCallAddr: &nativeasset.NativeAssetCall{GasCost: AssetCallApricot, CallNewAccountGas: ethparams.CallNewAccountGas}, } var PrecompiledContractsBanff = map[common.Address]contract.StatefulPrecompiledContract{ diff --git a/params/protocol_params.go b/params/protocol_params.go deleted file mode 100644 index 1d8ed7f9e1..0000000000 --- a/params/protocol_params.go +++ /dev/null @@ -1,204 +0,0 @@ -// (c) 2019-2020, Ava Labs, Inc. -// -// This file is a derived work, based on the go-ethereum library whose original -// notices appear below. -// -// It is distributed under a license compatible with the licensing terms of the -// original code from which it is derived. -// -// Much love to the original authors for their work. -// ********** -// Copyright 2015 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 params - -import ( - "math/big" - - "github.com/ava-labs/libevm/common" -) - -const ( - GenesisGasLimit uint64 = 4712388 // Gas limit of the Genesis block. - - ExpByteGas uint64 = 10 // Times ceil(log256(exponent)) for the EXP instruction. - SloadGas uint64 = 50 // Multiplied by the number of 32-byte words that are copied (round up) for any *COPY operation and added. - CallValueTransferGas uint64 = 9000 // Paid for CALL when the value transfer is non-zero. - CallNewAccountGas uint64 = 25000 // Paid for CALL when the destination address didn't exist prior. - TxGas uint64 = 21000 // Per transaction not creating a contract. NOTE: Not payable on data of calls between transactions. - TxGasContractCreation uint64 = 53000 // Per transaction that creates a contract. NOTE: Not payable on data of calls between transactions. - TxDataZeroGas uint64 = 4 // Per byte of data attached to a transaction that equals zero. NOTE: Not payable on data of calls between transactions. - QuadCoeffDiv uint64 = 512 // Divisor for the quadratic particle of the memory cost equation. - LogDataGas uint64 = 8 // Per byte in a LOG* operation's data. - CallStipend uint64 = 2300 // Free gas given at beginning of call. - - Keccak256Gas uint64 = 30 // Once per KECCAK256 operation. - Keccak256WordGas uint64 = 6 // Once per word of the KECCAK256 operation's data. - InitCodeWordGas uint64 = 2 // Once per word of the init code when creating a contract. - - SstoreSetGas uint64 = 20000 // Once per SSTORE operation. - SstoreResetGas uint64 = 5000 // Once per SSTORE operation if the zeroness changes from zero. - SstoreClearGas uint64 = 5000 // Once per SSTORE operation if the zeroness doesn't change. - SstoreRefundGas uint64 = 15000 // Once per SSTORE operation if the zeroness changes to zero. - - NetSstoreNoopGas uint64 = 200 // Once per SSTORE operation if the value doesn't change. - NetSstoreInitGas uint64 = 20000 // Once per SSTORE operation from clean zero. - NetSstoreCleanGas uint64 = 5000 // Once per SSTORE operation from clean non-zero. - NetSstoreDirtyGas uint64 = 200 // Once per SSTORE operation from dirty. - - NetSstoreClearRefund uint64 = 15000 // Once per SSTORE operation for clearing an originally existing storage slot - NetSstoreResetRefund uint64 = 4800 // Once per SSTORE operation for resetting to the original non-zero value - NetSstoreResetClearRefund uint64 = 19800 // Once per SSTORE operation for resetting to the original zero value - - SstoreSentryGasEIP2200 uint64 = 2300 // Minimum gas required to be present for an SSTORE call, not consumed - SstoreSetGasEIP2200 uint64 = 20000 // Once per SSTORE operation from clean zero to non-zero - SstoreResetGasEIP2200 uint64 = 5000 // Once per SSTORE operation from clean non-zero to something else - SstoreClearsScheduleRefundEIP2200 uint64 = 15000 // Once per SSTORE operation for clearing an originally existing storage slot - - ColdAccountAccessCostEIP2929 = uint64(2600) // COLD_ACCOUNT_ACCESS_COST - ColdSloadCostEIP2929 = uint64(2100) // COLD_SLOAD_COST - WarmStorageReadCostEIP2929 = uint64(100) // WARM_STORAGE_READ_COST - - // In EIP-2200: SstoreResetGas was 5000. - // In EIP-2929: SstoreResetGas was changed to '5000 - COLD_SLOAD_COST'. - // In EIP-3529: SSTORE_CLEARS_SCHEDULE is defined as SSTORE_RESET_GAS + ACCESS_LIST_STORAGE_KEY_COST - // Which becomes: 5000 - 2100 + 1900 = 4800 - SstoreClearsScheduleRefundEIP3529 uint64 = SstoreResetGasEIP2200 - ColdSloadCostEIP2929 + TxAccessListStorageKeyGas - - JumpdestGas uint64 = 1 // Once per JUMPDEST operation. - EpochDuration uint64 = 30000 // Duration between proof-of-work epochs. - - CreateDataGas uint64 = 200 // - CallCreateDepth uint64 = 1024 // Maximum depth of call/create stack. - ExpGas uint64 = 10 // Once per EXP instruction - LogGas uint64 = 375 // Per LOG* operation. - CopyGas uint64 = 3 // - StackLimit uint64 = 1024 // Maximum size of VM stack allowed. - TierStepGas uint64 = 0 // Once per operation, for a selection of them. - LogTopicGas uint64 = 375 // Multiplied by the * of the LOG*, per LOG transaction. e.g. LOG0 incurs 0 * c_txLogTopicGas, LOG4 incurs 4 * c_txLogTopicGas. - CreateGas uint64 = 32000 // Once per CREATE operation & contract-creation transaction. - Create2Gas uint64 = 32000 // Once per CREATE2 operation - SelfdestructRefundGas uint64 = 24000 // Refunded following a selfdestruct operation. - MemoryGas uint64 = 3 // Times the address of the (highest referenced byte in memory + 1). NOTE: referencing happens on read, write and in instructions such as RETURN and CALL. - - TxDataNonZeroGasFrontier uint64 = 68 // Per byte of data attached to a transaction that is not equal to zero. NOTE: Not payable on data of calls between transactions. - TxDataNonZeroGasEIP2028 uint64 = 16 // Per byte of non zero data attached to a transaction after EIP 2028 (part in Istanbul) - TxAccessListAddressGas uint64 = 2400 // Per address specified in EIP 2930 access list - TxAccessListStorageKeyGas uint64 = 1900 // Per storage key specified in EIP 2930 access list - - // These have been changed during the course of the chain - CallGasFrontier uint64 = 40 // Once per CALL operation & message call transaction. - CallGasEIP150 uint64 = 700 // Static portion of gas for CALL-derivates after EIP 150 (Tangerine) - BalanceGasFrontier uint64 = 20 // The cost of a BALANCE operation - BalanceGasEIP150 uint64 = 400 // The cost of a BALANCE operation after Tangerine - BalanceGasEIP1884 uint64 = 700 // The cost of a BALANCE operation after EIP 1884 (part of Istanbul) - ExtcodeSizeGasFrontier uint64 = 20 // Cost of EXTCODESIZE before EIP 150 (Tangerine) - ExtcodeSizeGasEIP150 uint64 = 700 // Cost of EXTCODESIZE after EIP 150 (Tangerine) - SloadGasFrontier uint64 = 50 - SloadGasEIP150 uint64 = 200 - SloadGasEIP1884 uint64 = 800 // Cost of SLOAD after EIP 1884 (part of Istanbul) - SloadGasEIP2200 uint64 = 800 // Cost of SLOAD after EIP 2200 (part of Istanbul) - ExtcodeHashGasConstantinople uint64 = 400 // Cost of EXTCODEHASH (introduced in Constantinople) - ExtcodeHashGasEIP1884 uint64 = 700 // Cost of EXTCODEHASH after EIP 1884 (part in Istanbul) - SelfdestructGasEIP150 uint64 = 5000 // Cost of SELFDESTRUCT post EIP 150 (Tangerine) - - // EXP has a dynamic portion depending on the size of the exponent - ExpByteFrontier uint64 = 10 // was set to 10 in Frontier - ExpByteEIP158 uint64 = 50 // was raised to 50 during Eip158 (Spurious Dragon) - - // Extcodecopy has a dynamic AND a static cost. This represents only the - // static portion of the gas. It was changed during EIP 150 (Tangerine) - ExtcodeCopyBaseFrontier uint64 = 20 - ExtcodeCopyBaseEIP150 uint64 = 700 - - // CreateBySelfdestructGas is used when the refunded account is one that does - // not exist. This logic is similar to call. - // Introduced in Tangerine Whistle (Eip 150) - CreateBySelfdestructGas uint64 = 25000 - - MaxCodeSize = 24576 // Maximum bytecode to permit for a contract - MaxInitCodeSize = 2 * MaxCodeSize // Maximum initcode to permit in a creation transaction and create instructions - - // Precompiled contract gas prices - - EcrecoverGas uint64 = 3000 // Elliptic curve sender recovery gas price - Sha256BaseGas uint64 = 60 // Base price for a SHA256 operation - Sha256PerWordGas uint64 = 12 // Per-word price for a SHA256 operation - Ripemd160BaseGas uint64 = 600 // Base price for a RIPEMD160 operation - Ripemd160PerWordGas uint64 = 120 // Per-word price for a RIPEMD160 operation - IdentityBaseGas uint64 = 15 // Base price for a data copy operation - IdentityPerWordGas uint64 = 3 // Per-work price for a data copy operation - - Bn256AddGasByzantium uint64 = 500 // Byzantium gas needed for an elliptic curve addition - Bn256AddGasIstanbul uint64 = 150 // Gas needed for an elliptic curve addition - Bn256ScalarMulGasByzantium uint64 = 40000 // Byzantium gas needed for an elliptic curve scalar multiplication - Bn256ScalarMulGasIstanbul uint64 = 6000 // Gas needed for an elliptic curve scalar multiplication - Bn256PairingBaseGasByzantium uint64 = 100000 // Byzantium base price for an elliptic curve pairing check - Bn256PairingBaseGasIstanbul uint64 = 45000 // Base price for an elliptic curve pairing check - Bn256PairingPerPointGasByzantium uint64 = 80000 // Byzantium per-point price for an elliptic curve pairing check - Bn256PairingPerPointGasIstanbul uint64 = 34000 // Per-point price for an elliptic curve pairing check - - Bls12381G1AddGas uint64 = 600 // Price for BLS12-381 elliptic curve G1 point addition - Bls12381G1MulGas uint64 = 12000 // Price for BLS12-381 elliptic curve G1 point scalar multiplication - Bls12381G2AddGas uint64 = 4500 // Price for BLS12-381 elliptic curve G2 point addition - Bls12381G2MulGas uint64 = 55000 // Price for BLS12-381 elliptic curve G2 point scalar multiplication - Bls12381PairingBaseGas uint64 = 115000 // Base gas price for BLS12-381 elliptic curve pairing check - Bls12381PairingPerPairGas uint64 = 23000 // Per-point pair gas price for BLS12-381 elliptic curve pairing check - Bls12381MapG1Gas uint64 = 5500 // Gas price for BLS12-381 mapping field element to G1 operation - Bls12381MapG2Gas uint64 = 110000 // Gas price for BLS12-381 mapping field element to G2 operation - - // The Refund Quotient is the cap on how much of the used gas can be refunded. Before EIP-3529, - // up to half the consumed gas could be refunded. Redefined as 1/5th in EIP-3529 - RefundQuotient uint64 = 2 - RefundQuotientEIP3529 uint64 = 5 - - BlobTxBytesPerFieldElement = 32 // Size in bytes of a field element - BlobTxFieldElementsPerBlob = 4096 // Number of field elements stored in a single data blob - BlobTxBlobGasPerBlob = 1 << 17 // Gas consumption of a single data blob (== blob byte size) - BlobTxMinBlobGasprice = 1 // Minimum gas price for data blobs - BlobTxBlobGaspriceUpdateFraction = 3338477 // Controls the maximum rate of change for blob gas price - BlobTxPointEvaluationPrecompileGas = 50000 // Gas price for the point evaluation precompile. - - BlobTxTargetBlobGasPerBlock = 3 * BlobTxBlobGasPerBlob // Target consumable blob gas for data blobs per block (for 1559-like pricing) - MaxBlobGasPerBlock = 6 * BlobTxBlobGasPerBlob // Maximum consumable blob gas for data blobs per block -) - -const ( - // Avalanche Stateful Precompile Params - // Gas price for native asset balance lookup. Based on the cost of an SLOAD operation since native - // asset balances are kept in state storage. - AssetBalanceApricot uint64 = 2100 - // Gas price for native asset call. This gas price reflects the additional work done for the native - // asset transfer itself, which is a write to state storage. The cost of creating a new account and - // normal value transfer is assessed separately from this cost. - AssetCallApricot uint64 = 20000 -) - -// Gas discount table for BLS12-381 G1 and G2 multi exponentiation operations -var Bls12381MultiExpDiscountTable = [128]uint64{1200, 888, 764, 641, 594, 547, 500, 453, 438, 423, 408, 394, 379, 364, 349, 334, 330, 326, 322, 318, 314, 310, 306, 302, 298, 294, 289, 285, 281, 277, 273, 269, 268, 266, 265, 263, 262, 260, 259, 257, 256, 254, 253, 251, 250, 248, 247, 245, 244, 242, 241, 239, 238, 236, 235, 233, 232, 231, 229, 228, 226, 225, 223, 222, 221, 220, 219, 219, 218, 217, 216, 216, 215, 214, 213, 213, 212, 211, 211, 210, 209, 208, 208, 207, 206, 205, 205, 204, 203, 202, 202, 201, 200, 199, 199, 198, 197, 196, 196, 195, 194, 193, 193, 192, 191, 191, 190, 189, 188, 188, 187, 186, 185, 185, 184, 183, 182, 182, 181, 180, 179, 179, 178, 177, 176, 176, 175, 174} - -var ( - DifficultyBoundDivisor = big.NewInt(2048) // The bound divisor of the difficulty, used in the update calculations. - GenesisDifficulty = big.NewInt(131072) // Difficulty of the Genesis block. - MinimumDifficulty = big.NewInt(131072) // The minimum that the difficulty may ever be. - DurationLimit = big.NewInt(13) // The decision boundary on the blocktime duration used to determine whether difficulty should go up or not. - - // BeaconRootsStorageAddress is the address where historical beacon roots are stored as per EIP-4788 - BeaconRootsStorageAddress = common.HexToAddress("0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02") - // SystemAddress is where the system-transaction is sent from as per EIP-4788 - SystemAddress common.Address = common.HexToAddress("0xfffffffffffffffffffffffffffffffffffffffe") -) diff --git a/params/protocol_params_ext.go b/params/protocol_params_ext.go new file mode 100644 index 0000000000..8fecce540e --- /dev/null +++ b/params/protocol_params_ext.go @@ -0,0 +1,15 @@ +// (c) 2025, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package params + +const ( + // Avalanche Stateful Precompile Params + // Gas price for native asset balance lookup. Based on the cost of an SLOAD operation since native + // asset balances are kept in state storage. + AssetBalanceApricot uint64 = 2100 + // Gas price for native asset call. This gas price reflects the additional work done for the native + // asset transfer itself, which is a write to state storage. The cost of creating a new account and + // normal value transfer is assessed separately from this cost. + AssetCallApricot uint64 = 20000 +) diff --git a/params/protocol_params_test.go b/params/protocol_params_test.go new file mode 100644 index 0000000000..a732b5dd45 --- /dev/null +++ b/params/protocol_params_test.go @@ -0,0 +1,142 @@ +// (c) 2025, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package params + +import ( + "testing" + + "github.com/ava-labs/libevm/common" + ethparams "github.com/ava-labs/libevm/params" + "github.com/stretchr/testify/assert" +) + +// TestUpstreamParamsValues detects when a params value changes upstream to prevent a subtle change +// to one of the values to have an unpredicted impact in the libevm consumer. +// Values should be updated to newer upstream values once the consumer is updated to handle the +// updated value(s). +func TestUpstreamParamsValues(t *testing.T) { + tests := map[string]struct { + param any + want any + }{ + "GasLimitBoundDivisor": {param: ethparams.GasLimitBoundDivisor, want: uint64(1024)}, + "MinGasLimit": {param: ethparams.MinGasLimit, want: uint64(5000)}, + "MaxGasLimit": {param: ethparams.MaxGasLimit, want: uint64(0x7fffffffffffffff)}, + "GenesisGasLimit": {param: ethparams.GenesisGasLimit, want: uint64(4712388)}, + "ExpByteGas": {param: ethparams.ExpByteGas, want: uint64(10)}, + "SloadGas": {param: ethparams.SloadGas, want: uint64(50)}, + "CallValueTransferGas": {param: ethparams.CallValueTransferGas, want: uint64(9000)}, + "CallNewAccountGas": {param: ethparams.CallNewAccountGas, want: uint64(25000)}, + "TxGas": {param: ethparams.TxGas, want: uint64(21000)}, + "TxGasContractCreation": {param: ethparams.TxGasContractCreation, want: uint64(53000)}, + "TxDataZeroGas": {param: ethparams.TxDataZeroGas, want: uint64(4)}, + "QuadCoeffDiv": {param: ethparams.QuadCoeffDiv, want: uint64(512)}, + "LogDataGas": {param: ethparams.LogDataGas, want: uint64(8)}, + "CallStipend": {param: ethparams.CallStipend, want: uint64(2300)}, + "Keccak256Gas": {param: ethparams.Keccak256Gas, want: uint64(30)}, + "Keccak256WordGas": {param: ethparams.Keccak256WordGas, want: uint64(6)}, + "InitCodeWordGas": {param: ethparams.InitCodeWordGas, want: uint64(2)}, + "SstoreSetGas": {param: ethparams.SstoreSetGas, want: uint64(20000)}, + "SstoreResetGas": {param: ethparams.SstoreResetGas, want: uint64(5000)}, + "SstoreClearGas": {param: ethparams.SstoreClearGas, want: uint64(5000)}, + "SstoreRefundGas": {param: ethparams.SstoreRefundGas, want: uint64(15000)}, + "NetSstoreNoopGas": {param: ethparams.NetSstoreNoopGas, want: uint64(200)}, + "NetSstoreInitGas": {param: ethparams.NetSstoreInitGas, want: uint64(20000)}, + "NetSstoreCleanGas": {param: ethparams.NetSstoreCleanGas, want: uint64(5000)}, + "NetSstoreDirtyGas": {param: ethparams.NetSstoreDirtyGas, want: uint64(200)}, + "NetSstoreClearRefund": {param: ethparams.NetSstoreClearRefund, want: uint64(15000)}, + "NetSstoreResetRefund": {param: ethparams.NetSstoreResetRefund, want: uint64(4800)}, + "NetSstoreResetClearRefund": {param: ethparams.NetSstoreResetClearRefund, want: uint64(19800)}, + "SstoreSentryGasEIP2200": {param: ethparams.SstoreSentryGasEIP2200, want: uint64(2300)}, + "SstoreSetGasEIP2200": {param: ethparams.SstoreSetGasEIP2200, want: uint64(20000)}, + "SstoreResetGasEIP2200": {param: ethparams.SstoreResetGasEIP2200, want: uint64(5000)}, + "SstoreClearsScheduleRefundEIP2200": {param: ethparams.SstoreClearsScheduleRefundEIP2200, want: uint64(15000)}, + "ColdAccountAccessCostEIP2929": {param: ethparams.ColdAccountAccessCostEIP2929, want: uint64(2600)}, + "ColdSloadCostEIP2929": {param: ethparams.ColdSloadCostEIP2929, want: uint64(2100)}, + "WarmStorageReadCostEIP2929": {param: ethparams.WarmStorageReadCostEIP2929, want: uint64(100)}, + "SstoreClearsScheduleRefundEIP3529": {param: ethparams.SstoreClearsScheduleRefundEIP3529, want: uint64(5000 - 2100 + 1900)}, + "JumpdestGas": {param: ethparams.JumpdestGas, want: uint64(1)}, + "EpochDuration": {param: ethparams.EpochDuration, want: uint64(30000)}, + "CreateDataGas": {param: ethparams.CreateDataGas, want: uint64(200)}, + "CallCreateDepth": {param: ethparams.CallCreateDepth, want: uint64(1024)}, + "ExpGas": {param: ethparams.ExpGas, want: uint64(10)}, + "LogGas": {param: ethparams.LogGas, want: uint64(375)}, + "CopyGas": {param: ethparams.CopyGas, want: uint64(3)}, + "StackLimit": {param: ethparams.StackLimit, want: uint64(1024)}, + "TierStepGas": {param: ethparams.TierStepGas, want: uint64(0)}, + "LogTopicGas": {param: ethparams.LogTopicGas, want: uint64(375)}, + "CreateGas": {param: ethparams.CreateGas, want: uint64(32000)}, + "Create2Gas": {param: ethparams.Create2Gas, want: uint64(32000)}, + "SelfdestructRefundGas": {param: ethparams.SelfdestructRefundGas, want: uint64(24000)}, + "MemoryGas": {param: ethparams.MemoryGas, want: uint64(3)}, + "TxDataNonZeroGasFrontier": {param: ethparams.TxDataNonZeroGasFrontier, want: uint64(68)}, + "TxDataNonZeroGasEIP2028": {param: ethparams.TxDataNonZeroGasEIP2028, want: uint64(16)}, + "TxAccessListAddressGas": {param: ethparams.TxAccessListAddressGas, want: uint64(2400)}, + "TxAccessListStorageKeyGas": {param: ethparams.TxAccessListStorageKeyGas, want: uint64(1900)}, + "CallGasFrontier": {param: ethparams.CallGasFrontier, want: uint64(40)}, + "CallGasEIP150": {param: ethparams.CallGasEIP150, want: uint64(700)}, + "BalanceGasFrontier": {param: ethparams.BalanceGasFrontier, want: uint64(20)}, + "BalanceGasEIP150": {param: ethparams.BalanceGasEIP150, want: uint64(400)}, + "BalanceGasEIP1884": {param: ethparams.BalanceGasEIP1884, want: uint64(700)}, + "ExtcodeSizeGasFrontier": {param: ethparams.ExtcodeSizeGasFrontier, want: uint64(20)}, + "ExtcodeSizeGasEIP150": {param: ethparams.ExtcodeSizeGasEIP150, want: uint64(700)}, + "SloadGasFrontier": {param: ethparams.SloadGasFrontier, want: uint64(50)}, + "SloadGasEIP150": {param: ethparams.SloadGasEIP150, want: uint64(200)}, + "SloadGasEIP1884": {param: ethparams.SloadGasEIP1884, want: uint64(800)}, + "SloadGasEIP2200": {param: ethparams.SloadGasEIP2200, want: uint64(800)}, + "ExtcodeHashGasConstantinople": {param: ethparams.ExtcodeHashGasConstantinople, want: uint64(400)}, + "ExtcodeHashGasEIP1884": {param: ethparams.ExtcodeHashGasEIP1884, want: uint64(700)}, + "SelfdestructGasEIP150": {param: ethparams.SelfdestructGasEIP150, want: uint64(5000)}, + "ExpByteFrontier": {param: ethparams.ExpByteFrontier, want: uint64(10)}, + "ExpByteEIP158": {param: ethparams.ExpByteEIP158, want: uint64(50)}, + "ExtcodeCopyBaseFrontier": {param: ethparams.ExtcodeCopyBaseFrontier, want: uint64(20)}, + "ExtcodeCopyBaseEIP150": {param: ethparams.ExtcodeCopyBaseEIP150, want: uint64(700)}, + "CreateBySelfdestructGas": {param: ethparams.CreateBySelfdestructGas, want: uint64(25000)}, + "DefaultBaseFeeChangeDenominator": {param: ethparams.DefaultBaseFeeChangeDenominator, want: 8}, + "DefaultElasticityMultiplier": {param: ethparams.DefaultElasticityMultiplier, want: 2}, + "InitialBaseFee": {param: ethparams.InitialBaseFee, want: 1000000000}, + "MaxCodeSize": {param: ethparams.MaxCodeSize, want: 24576}, + "MaxInitCodeSize": {param: ethparams.MaxInitCodeSize, want: 2 * 24576}, + "EcrecoverGas": {param: ethparams.EcrecoverGas, want: uint64(3000)}, + "Sha256BaseGas": {param: ethparams.Sha256BaseGas, want: uint64(60)}, + "Sha256PerWordGas": {param: ethparams.Sha256PerWordGas, want: uint64(12)}, + "Ripemd160BaseGas": {param: ethparams.Ripemd160BaseGas, want: uint64(600)}, + "Ripemd160PerWordGas": {param: ethparams.Ripemd160PerWordGas, want: uint64(120)}, + "IdentityBaseGas": {param: ethparams.IdentityBaseGas, want: uint64(15)}, + "IdentityPerWordGas": {param: ethparams.IdentityPerWordGas, want: uint64(3)}, + "Bn256AddGasByzantium": {param: ethparams.Bn256AddGasByzantium, want: uint64(500)}, + "Bn256AddGasIstanbul": {param: ethparams.Bn256AddGasIstanbul, want: uint64(150)}, + "Bn256ScalarMulGasByzantium": {param: ethparams.Bn256ScalarMulGasByzantium, want: uint64(40000)}, + "Bn256ScalarMulGasIstanbul": {param: ethparams.Bn256ScalarMulGasIstanbul, want: uint64(6000)}, + "Bn256PairingBaseGasByzantium": {param: ethparams.Bn256PairingBaseGasByzantium, want: uint64(100000)}, + "Bn256PairingBaseGasIstanbul": {param: ethparams.Bn256PairingBaseGasIstanbul, want: uint64(45000)}, + "Bn256PairingPerPointGasByzantium": {param: ethparams.Bn256PairingPerPointGasByzantium, want: uint64(80000)}, + "Bn256PairingPerPointGasIstanbul": {param: ethparams.Bn256PairingPerPointGasIstanbul, want: uint64(34000)}, + "Bls12381G1AddGas": {param: ethparams.Bls12381G1AddGas, want: uint64(600)}, + "Bls12381G1MulGas": {param: ethparams.Bls12381G1MulGas, want: uint64(12000)}, + "Bls12381G2AddGas": {param: ethparams.Bls12381G2AddGas, want: uint64(4500)}, + "Bls12381G2MulGas": {param: ethparams.Bls12381G2MulGas, want: uint64(55000)}, + "Bls12381PairingBaseGas": {param: ethparams.Bls12381PairingBaseGas, want: uint64(115000)}, + "Bls12381PairingPerPairGas": {param: ethparams.Bls12381PairingPerPairGas, want: uint64(23000)}, + "Bls12381MapG1Gas": {param: ethparams.Bls12381MapG1Gas, want: uint64(5500)}, + "Bls12381MapG2Gas": {param: ethparams.Bls12381MapG2Gas, want: uint64(110000)}, + "RefundQuotient": {param: ethparams.RefundQuotient, want: uint64(2)}, + "RefundQuotientEIP3529": {param: ethparams.RefundQuotientEIP3529, want: uint64(5)}, + "BlobTxBytesPerFieldElement": {param: ethparams.BlobTxBytesPerFieldElement, want: 32}, + "BlobTxFieldElementsPerBlob": {param: ethparams.BlobTxFieldElementsPerBlob, want: 4096}, + "BlobTxBlobGasPerBlob": {param: ethparams.BlobTxBlobGasPerBlob, want: 1 << 17}, + "BlobTxMinBlobGasprice": {param: ethparams.BlobTxMinBlobGasprice, want: 1}, + "BlobTxBlobGaspriceUpdateFraction": {param: ethparams.BlobTxBlobGaspriceUpdateFraction, want: 3338477}, + "BlobTxPointEvaluationPrecompileGas": {param: ethparams.BlobTxPointEvaluationPrecompileGas, want: 50000}, + "BlobTxTargetBlobGasPerBlock": {param: ethparams.BlobTxTargetBlobGasPerBlock, want: 3 * 131072}, + "MaxBlobGasPerBlock": {param: ethparams.MaxBlobGasPerBlock, want: 6 * 131072}, + "GenesisDifficulty": {param: ethparams.GenesisDifficulty.Int64(), want: int64(131072)}, + "BeaconRootsStorageAddress": {param: ethparams.BeaconRootsStorageAddress, want: common.HexToAddress("0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02")}, + "SystemAddress": {param: ethparams.SystemAddress, want: common.HexToAddress("0xfffffffffffffffffffffffffffffffffffffffe")}, + } + + for name, test := range tests { + assert.Equal(t, test.want, test.param, name) + } +} diff --git a/plugin/evm/syncervm_test.go b/plugin/evm/syncervm_test.go index 8e9c36793b..5952a97132 100644 --- a/plugin/evm/syncervm_test.go +++ b/plugin/evm/syncervm_test.go @@ -32,7 +32,6 @@ import ( "github.com/ava-labs/coreth/consensus/dummy" "github.com/ava-labs/coreth/constants" "github.com/ava-labs/coreth/core" - "github.com/ava-labs/coreth/params" "github.com/ava-labs/coreth/plugin/evm/atomic" "github.com/ava-labs/coreth/plugin/evm/customrawdb" "github.com/ava-labs/coreth/plugin/evm/customtypes" @@ -46,6 +45,7 @@ import ( "github.com/ava-labs/libevm/core/types" "github.com/ava-labs/libevm/ethdb" "github.com/ava-labs/libevm/log" + ethparams "github.com/ava-labs/libevm/params" "github.com/ava-labs/libevm/rlp" "github.com/ava-labs/libevm/trie" "github.com/ava-labs/libevm/triedb" @@ -324,7 +324,7 @@ func createSyncServerAndClientVMs(t *testing.T, test syncTest, numBlocks int) *s require.NoError(server.vm.mempool.AddLocalTx(exportTx)) default: // Generate simple transfer transactions. pk := testKeys[0].ToECDSA() - tx := types.NewTransaction(gen.TxNonce(testEthAddrs[0]), testEthAddrs[1], common.Big1, params.TxGas, initialBaseFee, nil) + tx := types.NewTransaction(gen.TxNonce(testEthAddrs[0]), testEthAddrs[1], common.Big1, ethparams.TxGas, initialBaseFee, nil) signedTx, err := types.SignTx(tx, types.NewEIP155Signer(server.vm.chainID), pk) require.NoError(err) gen.AddTx(signedTx) diff --git a/plugin/evm/vm_test.go b/plugin/evm/vm_test.go index 4d0fcc2e69..dab4633cab 100644 --- a/plugin/evm/vm_test.go +++ b/plugin/evm/vm_test.go @@ -17,6 +17,7 @@ import ( "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/log" + ethparams "github.com/ava-labs/libevm/params" "github.com/ava-labs/libevm/rlp" "github.com/holiman/uint256" @@ -3848,7 +3849,7 @@ func TestNoBlobsAllowed(t *testing.T) { Nonce: 0, GasTipCap: uint256.NewInt(1), GasFeeCap: uint256.MustFromBig(fee), - Gas: params.TxGas, + Gas: ethparams.TxGas, To: testEthAddrs[0], BlobFeeCap: uint256.NewInt(1), BlobHashes: []common.Hash{{1}}, // This blob is expected to cause verification to fail diff --git a/sync/client/client.go b/sync/client/client.go index 351c83ab16..82089dfdcf 100644 --- a/sync/client/client.go +++ b/sync/client/client.go @@ -11,24 +11,21 @@ import ( "sync/atomic" "time" - "github.com/ava-labs/avalanchego/ids" - - "github.com/ava-labs/coreth/params" - "github.com/ava-labs/coreth/sync/client/stats" - "github.com/ava-labs/avalanchego/codec" + "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/version" - - "github.com/ava-labs/libevm/common" - "github.com/ava-labs/libevm/crypto" - "github.com/ava-labs/libevm/log" - "github.com/ava-labs/coreth/peer" "github.com/ava-labs/coreth/plugin/evm/message" + "github.com/ava-labs/coreth/sync/client/stats" + "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/rawdb" "github.com/ava-labs/libevm/core/types" + "github.com/ava-labs/libevm/crypto" "github.com/ava-labs/libevm/ethdb" + "github.com/ava-labs/libevm/log" "github.com/ava-labs/libevm/trie" + + ethparams "github.com/ava-labs/libevm/params" ) const ( @@ -267,7 +264,7 @@ func parseCode(codec codec.Manager, req message.Request, data []byte) (interface totalBytes := 0 for i, code := range response.Data { - if len(code) > params.MaxCodeSize { + if len(code) > ethparams.MaxCodeSize { return nil, 0, fmt.Errorf("%w: (hash %s) (size %d)", errMaxCodeSizeExceeded, codeRequest.Hashes[i], len(code)) } diff --git a/sync/client/client_test.go b/sync/client/client_test.go index 39c554a6bb..03288d7ec2 100644 --- a/sync/client/client_test.go +++ b/sync/client/client_test.go @@ -27,6 +27,7 @@ import ( "github.com/ava-labs/libevm/core/rawdb" "github.com/ava-labs/libevm/core/types" "github.com/ava-labs/libevm/crypto" + ethparams "github.com/ava-labs/libevm/params" "github.com/ava-labs/libevm/triedb" ) @@ -74,7 +75,7 @@ func TestGetCode(t *testing.T) { }, "code size is too large": { setupRequest: func() (requestHashes []common.Hash, mockResponse message.CodeResponse, expectedCode [][]byte) { - oversizedCode := make([]byte, params.MaxCodeSize+1) + oversizedCode := make([]byte, ethparams.MaxCodeSize+1) codeHash := crypto.Keccak256Hash(oversizedCode) return []common.Hash{codeHash}, message.CodeResponse{ Data: [][]byte{oversizedCode}, diff --git a/sync/handlers/code_request_test.go b/sync/handlers/code_request_test.go index 944118b3e4..488c77ad2d 100644 --- a/sync/handlers/code_request_test.go +++ b/sync/handlers/code_request_test.go @@ -8,8 +8,6 @@ import ( "crypto/rand" "testing" - "github.com/ava-labs/coreth/params" - "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/coreth/plugin/evm/message" "github.com/ava-labs/coreth/sync/handlers/stats" @@ -17,6 +15,7 @@ import ( "github.com/ava-labs/libevm/core/rawdb" "github.com/ava-labs/libevm/crypto" "github.com/ava-labs/libevm/ethdb/memorydb" + ethparams "github.com/ava-labs/libevm/params" "github.com/stretchr/testify/assert" ) @@ -27,10 +26,10 @@ func TestCodeRequestHandler(t *testing.T) { codeHash := crypto.Keccak256Hash(codeBytes) rawdb.WriteCode(database, codeHash, codeBytes) - maxSizeCodeBytes := make([]byte, params.MaxCodeSize) + maxSizeCodeBytes := make([]byte, ethparams.MaxCodeSize) n, err := rand.Read(maxSizeCodeBytes) assert.NoError(t, err) - assert.Equal(t, params.MaxCodeSize, n) + assert.Equal(t, ethparams.MaxCodeSize, n) maxSizeCodeHash := crypto.Keccak256Hash(maxSizeCodeBytes) rawdb.WriteCode(database, maxSizeCodeHash, maxSizeCodeBytes) @@ -80,7 +79,7 @@ func TestCodeRequestHandler(t *testing.T) { }, verifyStats: func(t *testing.T, stats *stats.MockHandlerStats) { assert.EqualValues(t, 1, mockHandlerStats.CodeRequestCount) - assert.EqualValues(t, params.MaxCodeSize, mockHandlerStats.CodeBytesReturnedSum) + assert.EqualValues(t, ethparams.MaxCodeSize, mockHandlerStats.CodeBytesReturnedSum) }, }, } From fa27e71de7968e31fa6433c59753bb3cd1110416 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Fri, 23 May 2025 12:26:49 +0200 Subject: [PATCH 02/20] Fix copyright headers --- params/protocol_params_ext.go | 2 +- params/protocol_params_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/params/protocol_params_ext.go b/params/protocol_params_ext.go index 8fecce540e..8612ae0b39 100644 --- a/params/protocol_params_ext.go +++ b/params/protocol_params_ext.go @@ -1,4 +1,4 @@ -// (c) 2025, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2025, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package params diff --git a/params/protocol_params_test.go b/params/protocol_params_test.go index a732b5dd45..f02ce59cca 100644 --- a/params/protocol_params_test.go +++ b/params/protocol_params_test.go @@ -1,4 +1,4 @@ -// (c) 2025, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2025, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package params From b4bbbcaf2d09fcc0b65b67a13452b8c4f729c0a0 Mon Sep 17 00:00:00 2001 From: Jonathan Oppenheimer Date: Tue, 5 Aug 2025 14:33:18 -0400 Subject: [PATCH 03/20] resolve further import errors --- core/blockchain_ext_test.go | 6 +++--- core/blockchain_test.go | 1 - core/state_transition.go | 1 + plugin/evm/syncervm_test.go | 2 -- plugin/evm/vm_test.go | 6 ------ 5 files changed, 4 insertions(+), 12 deletions(-) diff --git a/core/blockchain_ext_test.go b/core/blockchain_ext_test.go index 8d66ff9919..6aa1529e80 100644 --- a/core/blockchain_ext_test.go +++ b/core/blockchain_ext_test.go @@ -952,7 +952,7 @@ func EmptyAndNonEmptyBlocksTest(t *testing.T, create createFunc) { _, chain, _, err := GenerateChainWithGenesis(gspec, blockchain.engine, 5, 10, func(i int, gen *BlockGen) { if i == 3 { // Generate a transaction to create a unique block - tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(10000), params.TxGas, nil, nil), types.HomesteadSigner{}, key1) + tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(10000), ethparams.TxGas, nil, nil), types.HomesteadSigner{}, key1) gen.AddTx(tx) } }) @@ -1597,7 +1597,7 @@ func ReexecBlocksTest(t *testing.T, create ReexecTestFunc) { // This call generates a chain of 10 blocks. signer := types.HomesteadSigner{} _, chain, _, err := GenerateChainWithGenesis(gspec, blockchain.engine, 10, 10, func(i int, gen *BlockGen) { - tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(10000), params.TxGas, nil, nil), signer, key1) + tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(10000), ethparams.TxGas, nil, nil), signer, key1) gen.AddTx(tx) }) if err != nil { @@ -1731,7 +1731,7 @@ func ReexecMaxBlocksTest(t *testing.T, create ReexecTestFunc) { signer := types.HomesteadSigner{} _, chain, _, err := GenerateChainWithGenesis(gspec, blockchain.engine, genNumBlocks, 10, func(i int, gen *BlockGen) { - tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(10000), params.TxGas, nil, nil), signer, key1) + tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(10000), ethparams.TxGas, nil, nil), signer, key1) gen.AddTx(tx) }) if err != nil { diff --git a/core/blockchain_test.go b/core/blockchain_test.go index 61c6c8463d..737e23e17d 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -47,7 +47,6 @@ import ( "github.com/ava-labs/libevm/eth/tracers/logger" "github.com/ava-labs/libevm/ethdb" ethparams "github.com/ava-labs/libevm/params" - "github.com/holiman/uint256" ) var ( diff --git a/core/state_transition.go b/core/state_transition.go index 4a2ea079cd..a06f4049a6 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -39,6 +39,7 @@ import ( "github.com/ava-labs/libevm/core/types" "github.com/ava-labs/libevm/core/vm" "github.com/ava-labs/libevm/crypto/kzg4844" + "github.com/ava-labs/libevm/log" ethparams "github.com/ava-labs/libevm/params" "github.com/holiman/uint256" ) diff --git a/plugin/evm/syncervm_test.go b/plugin/evm/syncervm_test.go index 4d2e784132..de1a1000d9 100644 --- a/plugin/evm/syncervm_test.go +++ b/plugin/evm/syncervm_test.go @@ -37,8 +37,6 @@ import ( "github.com/ava-labs/coreth/constants" "github.com/ava-labs/coreth/core" "github.com/ava-labs/coreth/core/coretest" - "github.com/ava-labs/coreth/plugin/evm/atomic" - "github.com/ava-labs/coreth/plugin/evm/atomic/atomictest" "github.com/ava-labs/coreth/plugin/evm/customrawdb" "github.com/ava-labs/coreth/plugin/evm/customtypes" "github.com/ava-labs/coreth/plugin/evm/database" diff --git a/plugin/evm/vm_test.go b/plugin/evm/vm_test.go index 266386ea4c..e483d22d04 100644 --- a/plugin/evm/vm_test.go +++ b/plugin/evm/vm_test.go @@ -17,14 +17,8 @@ import ( "testing" "time" - "github.com/ava-labs/libevm/common" - "github.com/ava-labs/libevm/log" ethparams "github.com/ava-labs/libevm/params" - "github.com/ava-labs/libevm/rlp" - "github.com/holiman/uint256" - "github.com/ava-labs/coreth/constants" - "github.com/ava-labs/coreth/eth/filters" "github.com/ava-labs/coreth/plugin/evm/atomic" atomictxpool "github.com/ava-labs/coreth/plugin/evm/atomic/txpool" From ab05cef7d0ab172d915a77ebe6099e92349b57c1 Mon Sep 17 00:00:00 2001 From: Jonathan Oppenheimer Date: Tue, 5 Aug 2025 14:54:14 -0400 Subject: [PATCH 04/20] simplify errors further --- plugin/evm/import_tx_test.go | 33 +++++++++++++++--------------- plugin/evm/prestate_tracer_test.go | 3 +-- 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/plugin/evm/import_tx_test.go b/plugin/evm/import_tx_test.go index 68a9e51bde..a197e2af12 100644 --- a/plugin/evm/import_tx_test.go +++ b/plugin/evm/import_tx_test.go @@ -10,7 +10,6 @@ import ( "github.com/ava-labs/coreth/core/extstate" "github.com/ava-labs/coreth/plugin/evm/atomic" "github.com/ava-labs/coreth/plugin/evm/atomic/vm" - atomicvm "github.com/ava-labs/coreth/plugin/evm/atomic/vm" "github.com/ava-labs/coreth/plugin/evm/upgrade/ap0" "github.com/ava-labs/coreth/utils" "github.com/ava-labs/libevm/common" @@ -30,7 +29,7 @@ import ( // createImportTxOptions adds a UTXO to shared memory and generates a list of import transactions sending this UTXO // to each of the three test keys (conflicting transactions) -func createImportTxOptions(t *testing.T, vm *atomicvm.VM, sharedMemory *avalancheatomic.Memory) []*atomic.Tx { +func createImportTxOptions(t *testing.T, vm *vm.VM, sharedMemory *avalancheatomic.Memory) []*atomic.Tx { utxo := &avax.UTXO{ UTXOID: avax.UTXOID{TxID: ids.GenerateTestID()}, Asset: avax.Asset{ID: vm.Ctx.AVAXAssetID}, @@ -433,7 +432,7 @@ func TestNewImportTx(t *testing.T) { importAmount := uint64(5000000) // createNewImportAVAXTx adds a UTXO to shared memory and then constructs a new import transaction // and checks that it has the correct fee for the base fee that has been used - createNewImportAVAXTx := func(t *testing.T, vm *atomicvm.VM, sharedMemory *avalancheatomic.Memory) *atomic.Tx { + createNewImportAVAXTx := func(t *testing.T, vm *vm.VM, sharedMemory *avalancheatomic.Memory) *atomic.Tx { txID := ids.GenerateTestID() _, err := addUTXO(sharedMemory, vm.Ctx, txID, 0, vm.Ctx.AVAXAssetID, importAmount, testShortIDAddrs[0]) if err != nil { @@ -473,7 +472,7 @@ func TestNewImportTx(t *testing.T) { return tx } - checkState := func(t *testing.T, vm *atomicvm.VM) { + checkState := func(t *testing.T, vm *vm.VM) { txs := vm.LastAcceptedExtendedBlock().GetBlockExtension().(atomic.AtomicBlockContext).AtomicTxs() if len(txs) != 1 { t.Fatalf("Expected one import tx to be in the last accepted block, but found %d", len(txs)) @@ -876,7 +875,7 @@ func TestImportTxGasCost(t *testing.T) { func TestImportTxSemanticVerify(t *testing.T) { tests := map[string]atomicTxTest{ "UTXO not present during bootstrapping": { - setup: func(t *testing.T, vm *atomicvm.VM, sharedMemory *avalancheatomic.Memory) *atomic.Tx { + setup: func(t *testing.T, vm *vm.VM, sharedMemory *avalancheatomic.Memory) *atomic.Tx { tx := &atomic.Tx{UnsignedAtomicTx: &atomic.UnsignedImportTx{ NetworkID: vm.Ctx.NetworkID, BlockchainID: vm.Ctx.ChainID, @@ -905,7 +904,7 @@ func TestImportTxSemanticVerify(t *testing.T) { bootstrapping: true, }, "UTXO not present": { - setup: func(t *testing.T, vm *atomicvm.VM, sharedMemory *avalancheatomic.Memory) *atomic.Tx { + setup: func(t *testing.T, vm *vm.VM, sharedMemory *avalancheatomic.Memory) *atomic.Tx { tx := &atomic.Tx{UnsignedAtomicTx: &atomic.UnsignedImportTx{ NetworkID: vm.Ctx.NetworkID, BlockchainID: vm.Ctx.ChainID, @@ -934,7 +933,7 @@ func TestImportTxSemanticVerify(t *testing.T) { semanticVerifyErr: "failed to fetch import UTXOs from", }, "garbage UTXO": { - setup: func(t *testing.T, vm *atomicvm.VM, sharedMemory *avalancheatomic.Memory) *atomic.Tx { + setup: func(t *testing.T, vm *vm.VM, sharedMemory *avalancheatomic.Memory) *atomic.Tx { utxoID := avax.UTXOID{TxID: ids.GenerateTestID()} xChainSharedMemory := sharedMemory.NewSharedMemory(vm.Ctx.XChainID) inputID := utxoID.InputID() @@ -974,7 +973,7 @@ func TestImportTxSemanticVerify(t *testing.T) { semanticVerifyErr: "failed to unmarshal UTXO", }, "UTXO AssetID mismatch": { - setup: func(t *testing.T, vm *atomicvm.VM, sharedMemory *avalancheatomic.Memory) *atomic.Tx { + setup: func(t *testing.T, vm *vm.VM, sharedMemory *avalancheatomic.Memory) *atomic.Tx { txID := ids.GenerateTestID() expectedAssetID := ids.GenerateTestID() utxo, err := addUTXO(sharedMemory, vm.Ctx, txID, 0, expectedAssetID, 1, testShortIDAddrs[0]) @@ -1008,7 +1007,7 @@ func TestImportTxSemanticVerify(t *testing.T) { semanticVerifyErr: vm.ErrAssetIDMismatch.Error(), }, "insufficient AVAX funds": { - setup: func(t *testing.T, vm *atomicvm.VM, sharedMemory *avalancheatomic.Memory) *atomic.Tx { + setup: func(t *testing.T, vm *vm.VM, sharedMemory *avalancheatomic.Memory) *atomic.Tx { txID := ids.GenerateTestID() utxo, err := addUTXO(sharedMemory, vm.Ctx, txID, 0, vm.Ctx.AVAXAssetID, 1, testShortIDAddrs[0]) if err != nil { @@ -1041,7 +1040,7 @@ func TestImportTxSemanticVerify(t *testing.T) { semanticVerifyErr: "import tx flow check failed due to", }, "insufficient non-AVAX funds": { - setup: func(t *testing.T, vm *atomicvm.VM, sharedMemory *avalancheatomic.Memory) *atomic.Tx { + setup: func(t *testing.T, vm *vm.VM, sharedMemory *avalancheatomic.Memory) *atomic.Tx { txID := ids.GenerateTestID() assetID := ids.GenerateTestID() utxo, err := addUTXO(sharedMemory, vm.Ctx, txID, 0, assetID, 1, testShortIDAddrs[0]) @@ -1075,7 +1074,7 @@ func TestImportTxSemanticVerify(t *testing.T) { semanticVerifyErr: "import tx flow check failed due to", }, "no signatures": { - setup: func(t *testing.T, vm *atomicvm.VM, sharedMemory *avalancheatomic.Memory) *atomic.Tx { + setup: func(t *testing.T, vm *vm.VM, sharedMemory *avalancheatomic.Memory) *atomic.Tx { txID := ids.GenerateTestID() utxo, err := addUTXO(sharedMemory, vm.Ctx, txID, 0, vm.Ctx.AVAXAssetID, 1, testShortIDAddrs[0]) if err != nil { @@ -1108,7 +1107,7 @@ func TestImportTxSemanticVerify(t *testing.T) { semanticVerifyErr: "import tx contained mismatched number of inputs/credentials", }, "incorrect signature": { - setup: func(t *testing.T, vm *atomicvm.VM, sharedMemory *avalancheatomic.Memory) *atomic.Tx { + setup: func(t *testing.T, vm *vm.VM, sharedMemory *avalancheatomic.Memory) *atomic.Tx { txID := ids.GenerateTestID() utxo, err := addUTXO(sharedMemory, vm.Ctx, txID, 0, vm.Ctx.AVAXAssetID, 1, testShortIDAddrs[0]) if err != nil { @@ -1142,7 +1141,7 @@ func TestImportTxSemanticVerify(t *testing.T) { semanticVerifyErr: "import tx transfer failed verification", }, "non-unique EVM Outputs": { - setup: func(t *testing.T, vm *atomicvm.VM, sharedMemory *avalancheatomic.Memory) *atomic.Tx { + setup: func(t *testing.T, vm *vm.VM, sharedMemory *avalancheatomic.Memory) *atomic.Tx { txID := ids.GenerateTestID() utxo, err := addUTXO(sharedMemory, vm.Ctx, txID, 0, vm.Ctx.AVAXAssetID, 2, testShortIDAddrs[0]) if err != nil { @@ -1195,7 +1194,7 @@ func TestImportTxEVMStateTransfer(t *testing.T) { assetID := ids.GenerateTestID() tests := map[string]atomicTxTest{ "AVAX UTXO": { - setup: func(t *testing.T, vm *atomicvm.VM, sharedMemory *avalancheatomic.Memory) *atomic.Tx { + setup: func(t *testing.T, vm *vm.VM, sharedMemory *avalancheatomic.Memory) *atomic.Tx { txID := ids.GenerateTestID() utxo, err := addUTXO(sharedMemory, vm.Ctx, txID, 0, vm.Ctx.AVAXAssetID, 1, testShortIDAddrs[0]) if err != nil { @@ -1225,7 +1224,7 @@ func TestImportTxEVMStateTransfer(t *testing.T) { } return tx }, - checkState: func(t *testing.T, vm *atomicvm.VM) { + checkState: func(t *testing.T, vm *vm.VM) { lastAcceptedBlock := vm.LastAcceptedExtendedBlock() sdb, err := vm.Blockchain().StateAt(lastAcceptedBlock.GetEthBlock().Root()) @@ -1240,7 +1239,7 @@ func TestImportTxEVMStateTransfer(t *testing.T) { }, }, "non-AVAX UTXO": { - setup: func(t *testing.T, vm *atomicvm.VM, sharedMemory *avalancheatomic.Memory) *atomic.Tx { + setup: func(t *testing.T, vm *vm.VM, sharedMemory *avalancheatomic.Memory) *atomic.Tx { txID := ids.GenerateTestID() utxo, err := addUTXO(sharedMemory, vm.Ctx, txID, 0, assetID, 1, testShortIDAddrs[0]) if err != nil { @@ -1270,7 +1269,7 @@ func TestImportTxEVMStateTransfer(t *testing.T) { } return tx }, - checkState: func(t *testing.T, vm *atomicvm.VM) { + checkState: func(t *testing.T, vm *vm.VM) { lastAcceptedBlock := vm.LastAcceptedExtendedBlock() statedb, err := vm.Blockchain().StateAt(lastAcceptedBlock.GetEthBlock().Root()) diff --git a/plugin/evm/prestate_tracer_test.go b/plugin/evm/prestate_tracer_test.go index d043d959ba..ee52e5406a 100644 --- a/plugin/evm/prestate_tracer_test.go +++ b/plugin/evm/prestate_tracer_test.go @@ -19,7 +19,6 @@ import ( "github.com/ava-labs/libevm/common/math" "github.com/ava-labs/libevm/core/rawdb" "github.com/ava-labs/libevm/core/types" - ethtypes "github.com/ava-labs/libevm/core/types" "github.com/ava-labs/libevm/core/vm" ) @@ -66,7 +65,7 @@ func testPrestateDiffTracer(tracerName string, dirPath string, t *testing.T) { Difficulty: (*big.Int)(test.Context.Difficulty), GasLimit: uint64(test.Context.GasLimit), BaseFee: test.Genesis.BaseFee, - Header: ðtypes.Header{ + Header: &types.Header{ Number: new(big.Int).SetUint64(uint64(test.Context.Number)), Time: uint64(test.Context.Time), }, From 7782f352c42d7a4995bab6c5091a35b1c37d452d Mon Sep 17 00:00:00 2001 From: Jonathan Oppenheimer Date: Tue, 5 Aug 2025 15:20:22 -0400 Subject: [PATCH 05/20] align evm repo blockchain_test.go configs --- core/blockchain_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/core/blockchain_test.go b/core/blockchain_test.go index 737e23e17d..f0b6c8917c 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -514,6 +514,7 @@ func TestUngracefulAsyncShutdown(t *testing.T) { TriePrefetcherParallelism: 4, Pruning: true, CommitInterval: 4096, + StateHistory: 32, // Align with subnet-evm configuration SnapshotLimit: 256, SnapshotNoBuild: true, // Ensure the test errors if snapshot initialization fails AcceptorQueueLimit: 1000, // ensure channel doesn't block From 1751e2ecd5a6f8b1f1de734e98d4c2d14a963f4e Mon Sep 17 00:00:00 2001 From: Jonathan Oppenheimer Date: Tue, 5 Aug 2025 15:32:58 -0400 Subject: [PATCH 06/20] remove re-duplicated test --- core/blockchain_test.go | 81 ----------------------------------------- 1 file changed, 81 deletions(-) diff --git a/core/blockchain_test.go b/core/blockchain_test.go index f0b6c8917c..d19c02b884 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -505,87 +505,6 @@ func TestRepopulateMissingTries(t *testing.T) { } func TestUngracefulAsyncShutdown(t *testing.T) { - var ( - create = func(db ethdb.Database, gspec *Genesis, lastAcceptedHash common.Hash) (*BlockChain, error) { - blockchain, err := createBlockChain(db, &CacheConfig{ - TrieCleanLimit: 256, - TrieDirtyLimit: 256, - TrieDirtyCommitTarget: 20, - TriePrefetcherParallelism: 4, - Pruning: true, - CommitInterval: 4096, - StateHistory: 32, // Align with subnet-evm configuration - SnapshotLimit: 256, - SnapshotNoBuild: true, // Ensure the test errors if snapshot initialization fails - AcceptorQueueLimit: 1000, // ensure channel doesn't block - }, gspec, lastAcceptedHash) - if err != nil { - return nil, err - } - return blockchain, nil - } - - key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") - key2, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a") - addr1 = crypto.PubkeyToAddress(key1.PublicKey) - addr2 = crypto.PubkeyToAddress(key2.PublicKey) - chainDB = rawdb.NewMemoryDatabase() - ) - - // Ensure that key1 has some funds in the genesis block. - genesisBalance := big.NewInt(1000000) - gspec := &Genesis{ - Config: ¶ms.ChainConfig{HomesteadBlock: new(big.Int)}, - Alloc: types.GenesisAlloc{addr1: {Balance: genesisBalance}}, - } - - blockchain, err := create(chainDB, gspec, common.Hash{}) - if err != nil { - t.Fatal(err) - } - defer blockchain.Stop() - - // This call generates a chain of 10 blocks. - signer := types.HomesteadSigner{} - _, chain, _, err := GenerateChainWithGenesis(gspec, blockchain.engine, 10, 10, func(i int, gen *BlockGen) { - tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(10000), ethparams.TxGas, nil, nil), signer, key1) - gen.AddTx(tx) - }) - if err != nil { - t.Fatal(err) - } - - // Insert three blocks into the chain and accept only the first block. - if _, err := blockchain.InsertChain(chain); err != nil { - t.Fatal(err) - } - - foundTxs := []common.Hash{} - missingTxs := []common.Hash{} - for i, block := range chain { - if err := blockchain.Accept(block); err != nil { - t.Fatal(err) - } - - if i == 3 { - // At height 3, kill the async accepted block processor to force an - // ungraceful recovery - blockchain.stopAcceptor() - blockchain.acceptorQueue = nil - } - - if i <= 3 { - // If <= height 3, all txs should be accessible on lookup - for _, tx := range block.Transactions() { - foundTxs = append(foundTxs, tx.Hash()) - } - } else { - // If > 3, all txs should be accessible on lookup - for _, tx := range block.Transactions() { - missingTxs = append(missingTxs, tx.Hash()) - } - } - } testUngracefulAsyncShutdown(t, rawdb.HashScheme, true) } From 7b8a921509879d8da0fa0250f27eaed26a7b934e Mon Sep 17 00:00:00 2001 From: Jonathan Oppenheimer Date: Tue, 5 Aug 2025 15:48:16 -0400 Subject: [PATCH 07/20] don't use ethparams for types --- internal/ethapi/transaction_args.go | 3 ++- miner/worker.go | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/internal/ethapi/transaction_args.go b/internal/ethapi/transaction_args.go index b486695516..92c1ed66da 100644 --- a/internal/ethapi/transaction_args.go +++ b/internal/ethapi/transaction_args.go @@ -37,6 +37,7 @@ import ( "github.com/ava-labs/coreth/consensus/misc/eip4844" "github.com/ava-labs/coreth/core" + "github.com/ava-labs/coreth/params" "github.com/ava-labs/coreth/rpc" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/common/hexutil" @@ -196,7 +197,7 @@ func (args *TransactionArgs) setDefaults(ctx context.Context, b Backend, skipGas type feeBackend interface { SuggestGasTipCap(ctx context.Context) (*big.Int, error) CurrentHeader() *types.Header - ChainConfig() *ethparams.ChainConfig + ChainConfig() *params.ChainConfig } // setFeeDefaults fills in default fee values for unspecified tx fields. diff --git a/miner/worker.go b/miner/worker.go index 67948569ce..85083ad266 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -83,7 +83,7 @@ type environment struct { blobs int size uint64 - rules ethparams.Rules + rules params.Rules predicateContext *precompileconfig.PredicateContext // predicateResults contains the results of checking the predicates for each transaction in the miner. // The results are accumulated as transactions are executed by the miner and set on the BlockContext. @@ -98,7 +98,7 @@ type environment struct { // and gathering the sealing result. type worker struct { config *Config - chainConfig *ethparams.ChainConfig + chainConfig *params.ChainConfig engine consensus.Engine eth Backend chain *core.BlockChain @@ -115,7 +115,7 @@ type worker struct { beaconRoot *common.Hash // TODO: set to empty hash, retained for upstream compatibility and future use } -func newWorker(config *Config, chainConfig *ethparams.ChainConfig, engine consensus.Engine, eth Backend, mux *event.TypeMux, clock *mockable.Clock) *worker { +func newWorker(config *Config, chainConfig *params.ChainConfig, engine consensus.Engine, eth Backend, mux *event.TypeMux, clock *mockable.Clock) *worker { worker := &worker{ config: config, chainConfig: chainConfig, From 1576b56b6f9d1095aa36349e41b98b055d2a9a8e Mon Sep 17 00:00:00 2001 From: Jonathan Oppenheimer Date: Mon, 11 Aug 2025 15:57:42 -0400 Subject: [PATCH 08/20] fix ethparams --- sync/blocksync/syncer_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sync/blocksync/syncer_test.go b/sync/blocksync/syncer_test.go index 6e2253e402..f8f9d1cc3f 100644 --- a/sync/blocksync/syncer_test.go +++ b/sync/blocksync/syncer_test.go @@ -21,6 +21,7 @@ import ( "github.com/ava-labs/libevm/core/types" "github.com/ava-labs/libevm/crypto" "github.com/ava-labs/libevm/ethdb" + ethparams "github.com/ava-labs/libevm/params" "github.com/stretchr/testify/require" ) @@ -199,7 +200,7 @@ func newTestEnvironment(t *testing.T, numBlocks int) *testEnvironment { _, blocks, _, err := core.GenerateChainWithGenesis(gspec, engine, numBlocks, 0, func(i int, gen *core.BlockGen) { // Generate a transaction to create a unique block - tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr), addr, big.NewInt(10), params.TxGas, nil, nil), signer, key) + tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr), addr, big.NewInt(10), ethparams.TxGas, nil, nil), signer, key) gen.AddTx(tx) }) require.NoError(t, err) From 0595063f5b233a19c54085398d78dc1973c011da Mon Sep 17 00:00:00 2001 From: Stephen Buttolph Date: Wed, 13 Aug 2025 12:16:10 -0400 Subject: [PATCH 09/20] Simplify atomic mempool tx signaling (#1116) --- plugin/evm/atomic/txpool/mempool.go | 11 ++++------- plugin/evm/atomic/txpool/txs.go | 27 +++------------------------ 2 files changed, 7 insertions(+), 31 deletions(-) diff --git a/plugin/evm/atomic/txpool/mempool.go b/plugin/evm/atomic/txpool/mempool.go index aec23919fb..ea5d6b2c0b 100644 --- a/plugin/evm/atomic/txpool/mempool.go +++ b/plugin/evm/atomic/txpool/mempool.go @@ -287,13 +287,10 @@ func (m *Mempool) addTx(tx *atomic.Tx, local bool, force bool) error { // When adding a transaction to the mempool, we make sure that there is an // item in Pending to signal the VM to produce a block. - // - // If the VM's buildStatus has already been set to something other than - // dontBuild, this will be ignored and won't be reset until the engine calls - // BuildBlock. This case is handled in [Txs.IssueCurrentTxs] and - // [Txs.CancelCurrentTxs]. - m.addPending() - + select { + case m.pending <- struct{}{}: + default: + } return nil } diff --git a/plugin/evm/atomic/txpool/txs.go b/plugin/evm/atomic/txpool/txs.go index aefcd40e23..c234a2f3a0 100644 --- a/plugin/evm/atomic/txpool/txs.go +++ b/plugin/evm/atomic/txpool/txs.go @@ -38,8 +38,8 @@ type Txs struct { // maxSize is the maximum number of transactions allowed to be kept in // mempool. maxSize int - // pending is a channel of length one, which the mempool ensures has an item - // on it as long as there is a pending transaction. + // pending is a channel of length one, which the mempool uses to awake the + // block builder when a new transaction is added. pending chan struct{} lock sync.RWMutex @@ -178,12 +178,6 @@ func (t *Txs) IssueCurrentTxs() { } t.metrics.issuedTxs.Update(int64(len(t.issuedTxs))) t.metrics.currentTxs.Update(0) - - // Since this function is called after the block is built, we should make - // sure to signal if we are willing to build another block. - if t.pendingTxs.Len() > 0 { - t.addPending() - } } // CancelCurrentTx attempts to mark the Current transaction as Pending. @@ -212,12 +206,6 @@ func (t *Txs) CancelCurrentTxs() { for _, tx := range t.currentTxs { t.cancelTx(tx) } - - // Since this function is called after block building has failed, we should - // make sure to signal if we are willing to build another block. - if t.pendingTxs.Len() > 0 { - t.addPending() - } } // cancelTx attempts to mark the Current transaction as Pending. If the tx can @@ -331,17 +319,8 @@ func (t *Txs) RemoveTx(tx *atomic.Tx) { t.removeTx(tx, false) } -// addPending makes sure that an item is in the Pending channel. -func (t *Txs) addPending() { - select { - case t.pending <- struct{}{}: - default: - } -} - // SubscribePendingTxs returns a channel that signals when there is a -// transaction added to the mempool or when the mempool is interacted with after -// block building finishes. +// transaction added to the mempool. func (t *Txs) SubscribePendingTxs() <-chan struct{} { return t.pending } From 2c4805d101983e5c5f8f1f3f32d520aaa698d1f9 Mon Sep 17 00:00:00 2001 From: Stephen Buttolph Date: Wed, 13 Aug 2025 13:35:19 -0400 Subject: [PATCH 10/20] Refactor atomic tx gas price calculations (#1118) --- plugin/evm/atomic/tx.go | 32 +++++ plugin/evm/atomic/tx_test.go | 164 +++++++++++++++++++++++ plugin/evm/atomic/txpool/mempool.go | 31 +++-- plugin/evm/atomic/txpool/mempool_test.go | 2 +- plugin/evm/atomic/txpool/tx_heap.go | 13 +- plugin/evm/atomic/txpool/tx_heap_test.go | 27 ++-- plugin/evm/atomic/txpool/txs.go | 23 +--- 7 files changed, 238 insertions(+), 54 deletions(-) create mode 100644 plugin/evm/atomic/tx_test.go diff --git a/plugin/evm/atomic/tx.go b/plugin/evm/atomic/tx.go index bfecba4a21..096161a0e8 100644 --- a/plugin/evm/atomic/tx.go +++ b/plugin/evm/atomic/tx.go @@ -40,6 +40,7 @@ var ( ErrNilTx = errors.New("tx is nil") errNoValueOutput = errors.New("output has no value") ErrNoValueInput = errors.New("input has no value") + ErrNoGasUsed = errors.New("no gas used") errNilOutput = errors.New("nil output") errNilInput = errors.New("nil input") errEmptyAssetID = errors.New("empty asset ID is not valid") @@ -288,6 +289,37 @@ func SortEVMInputsAndSigners(inputs []EVMInput, signers [][]*secp256k1.PrivateKe sort.Sort(&innerSortInputsAndSigners{inputs: inputs, signers: signers}) } +// EffectiveGasPrice returns the price per gas that the transaction is paying +// denominated in aAVAX/gas. +// +// The result is rounded down to the nearest aAVAX/gas. +func EffectiveGasPrice( + tx UnsignedTx, + avaxAssetID ids.ID, + isApricotPhase5 bool, +) (uint256.Int, error) { + gasUsed, err := tx.GasUsed(isApricotPhase5) + if err != nil { + return uint256.Int{}, err + } + if gasUsed == 0 { + return uint256.Int{}, ErrNoGasUsed + } + burned, err := tx.Burned(avaxAssetID) + if err != nil { + return uint256.Int{}, err + } + + var bigGasUsed uint256.Int + bigGasUsed.SetUint64(gasUsed) + + var gasPrice uint256.Int // gasPrice = burned * x2cRate / gasUsed + gasPrice.SetUint64(burned) + gasPrice.Mul(&gasPrice, X2CRate) + gasPrice.Div(&gasPrice, &bigGasUsed) + return gasPrice, nil +} + // 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) { diff --git a/plugin/evm/atomic/tx_test.go b/plugin/evm/atomic/tx_test.go new file mode 100644 index 0000000000..41c2181f91 --- /dev/null +++ b/plugin/evm/atomic/tx_test.go @@ -0,0 +1,164 @@ +// Copyright (C) 2019-2025, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package atomic + +import ( + "testing" + + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/utils/math" + "github.com/ava-labs/avalanchego/utils/units" + "github.com/ava-labs/avalanchego/vms/components/avax" + "github.com/ava-labs/avalanchego/vms/secp256k1fx" + "github.com/ava-labs/coreth/plugin/evm/upgrade/ap5" + "github.com/holiman/uint256" + "github.com/stretchr/testify/require" +) + +func TestEffectiveGasPrice(t *testing.T) { + avaxAssetID := ids.GenerateTestID() + tests := []struct { + name string + tx UnsignedTx + isApricotPhase5 bool + want uint256.Int + wantErr error + }{ + { + name: "no_gas_used", + tx: &UnsignedImportTx{}, + wantErr: ErrNoGasUsed, + }, + { + name: "invalid_amount_burned", + tx: &UnsignedImportTx{ + ImportedInputs: []*avax.TransferableInput{ + { + Asset: avax.Asset{ + ID: ids.GenerateTestID(), + }, + In: &secp256k1fx.TransferInput{ + Amt: 1, + Input: secp256k1fx.Input{ + SigIndices: []uint32{0}, + }, + }, + }, + }, + Outs: []EVMOutput{ + { + Amount: units.NanoAvax, + AssetID: avaxAssetID, + }, + }, + }, + wantErr: math.ErrUnderflow, + }, + { + name: "valid", + tx: &UnsignedImportTx{ + ImportedInputs: []*avax.TransferableInput{ + { + Asset: avax.Asset{ + ID: avaxAssetID, + }, + In: &secp256k1fx.TransferInput{ + Amt: units.NanoAvax, + Input: secp256k1fx.Input{ + SigIndices: []uint32{0}, + }, + }, + }, + }, + }, + want: *uint256.NewInt( + (units.NanoAvax * X2CRateUint64) / secp256k1fx.CostPerSignature, + ), + }, + { + name: "valid_multiple_assets", + tx: &UnsignedImportTx{ + ImportedInputs: []*avax.TransferableInput{ + { + Asset: avax.Asset{ + ID: avaxAssetID, + }, + In: &secp256k1fx.TransferInput{ + Amt: units.NanoAvax, + Input: secp256k1fx.Input{ + SigIndices: []uint32{0}, + }, + }, + }, + { + Asset: avax.Asset{ + ID: ids.GenerateTestID(), + }, + In: &secp256k1fx.TransferInput{ + Amt: 1, + Input: secp256k1fx.Input{ + SigIndices: []uint32{0}, + }, + }, + }, + }, + }, + want: *uint256.NewInt( + (units.NanoAvax * X2CRateUint64) / (2 * secp256k1fx.CostPerSignature), + ), + }, + { + name: "valid_post_AP5", + tx: &UnsignedImportTx{ + ImportedInputs: []*avax.TransferableInput{ + { + Asset: avax.Asset{ + ID: avaxAssetID, + }, + In: &secp256k1fx.TransferInput{ + Amt: units.NanoAvax, + Input: secp256k1fx.Input{ + SigIndices: []uint32{0}, + }, + }, + }, + }, + }, + isApricotPhase5: true, + want: *uint256.NewInt( + (units.NanoAvax * X2CRateUint64) / (ap5.AtomicTxIntrinsicGas + secp256k1fx.CostPerSignature), + ), + }, + { + name: "gas_price_exceeds_uint64", + tx: &UnsignedImportTx{ + ImportedInputs: []*avax.TransferableInput{ + { + Asset: avax.Asset{ + ID: avaxAssetID, + }, + In: &secp256k1fx.TransferInput{ + Amt: units.MegaAvax, + Input: secp256k1fx.Input{ + SigIndices: []uint32{0}, + }, + }, + }, + }, + }, + want: *uint256.MustFromDecimal( + "1000000000000000000000", // (units.MegaAvax * X2CRateUint64) / secp256k1fx.CostPerSignature, + ), + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + require := require.New(t) + + got, err := EffectiveGasPrice(test.tx, avaxAssetID, test.isApricotPhase5) + require.ErrorIs(err, test.wantErr) + require.Equal(test.want, got) + }) + } +} diff --git a/plugin/evm/atomic/txpool/mempool.go b/plugin/evm/atomic/txpool/mempool.go index ea5d6b2c0b..7788ff2ed8 100644 --- a/plugin/evm/atomic/txpool/mempool.go +++ b/plugin/evm/atomic/txpool/mempool.go @@ -12,6 +12,7 @@ import ( "github.com/ava-labs/coreth/plugin/evm/atomic" "github.com/ava-labs/coreth/plugin/evm/config" "github.com/ava-labs/libevm/log" + "github.com/holiman/uint256" "github.com/prometheus/client_golang/prometheus" ) @@ -126,11 +127,11 @@ func (m *Mempool) ForceAddTx(tx *atomic.Tx) error { // same input UTXOs as the provided transaction. If any conflicts are present, // it returns the highest gas price of any conflicting transaction, the ID of // the corresponding tx and the full list of conflicting transactions. -func (m *Mempool) checkConflictTx(tx *atomic.Tx) (uint64, ids.ID, []*atomic.Tx, error) { +func (m *Mempool) checkConflictTx(tx *atomic.Tx) (uint256.Int, ids.ID, []*atomic.Tx, error) { utxoSet := tx.InputUTXOs() var ( - highestGasPrice uint64 + highestGasPrice uint256.Int highestGasPriceConflictTxID ids.ID conflictingTxs []*atomic.Tx ) @@ -140,15 +141,15 @@ func (m *Mempool) checkConflictTx(tx *atomic.Tx) (uint64, ids.ID, []*atomic.Tx, if !ok { continue } - conflictTxID := conflictTx.ID() - conflictTxGasPrice, err := m.atomicTxGasPrice(conflictTx) - // Should never error to calculate the gas price of a transaction already in the mempool + conflictTxGasPrice, err := atomic.EffectiveGasPrice(conflictTx, m.ctx.AVAXAssetID, true) + // Should never error to calculate the gas price of a transaction + // already in the mempool if err != nil { - return 0, ids.ID{}, conflictingTxs, fmt.Errorf("failed to re-calculate gas price for conflict tx due to: %w", err) + return uint256.Int{}, ids.ID{}, conflictingTxs, fmt.Errorf("failed to re-calculate gas price for conflict tx due to: %w", err) } - if highestGasPrice < conflictTxGasPrice { + if highestGasPrice.Lt(&conflictTxGasPrice) { highestGasPrice = conflictTxGasPrice - highestGasPriceConflictTxID = conflictTxID + highestGasPriceConflictTxID = conflictTx.ID() } conflictingTxs = append(conflictingTxs, conflictTx) } @@ -190,7 +191,7 @@ func (m *Mempool) addTx(tx *atomic.Tx, local bool, force bool) error { } } - gasPrice, err := m.atomicTxGasPrice(tx) + gasPrice, err := atomic.EffectiveGasPrice(tx, m.ctx.AVAXAssetID, true) if err != nil { return err } @@ -200,8 +201,9 @@ func (m *Mempool) addTx(tx *atomic.Tx, local bool, force bool) error { } if len(conflictingTxs) != 0 && !force { // If the transaction does not have a higher fee than all of its - // conflicts, we refuse to add it to the mempool. - if highestGasPrice >= gasPrice { + // conflicts, we refuse to add it to the mempool. (Prefer items already + // in the mempool). + if !gasPrice.Gt(&highestGasPrice) { return fmt.Errorf( "%w: issued tx (%s) gas price %d <= conflict tx (%s) gas price %d (%d total conflicts in mempool)", ErrConflict, @@ -227,9 +229,10 @@ func (m *Mempool) addTx(tx *atomic.Tx, local bool, force bool) error { if m.pendingTxs.Len() > 0 { // Get the transaction with the lowest gasPrice minTx, minGasPrice := m.pendingTxs.PeekMin() - // If the lowest gasPrice >= the gasPrice of the new transaction, - // Discard new transaction. (Prefer items already in the mempool). - if minGasPrice >= gasPrice { + // If the new tx doesn't have a higher fee than the transaction it + // would replace, discard new transaction. (Prefer items already + // in the mempool). + if !gasPrice.Gt(&minGasPrice) { return fmt.Errorf( "%w currentMin=%d provided=%d", ErrInsufficientFee, diff --git a/plugin/evm/atomic/txpool/mempool_test.go b/plugin/evm/atomic/txpool/mempool_test.go index c6752a30ae..f98a53d466 100644 --- a/plugin/evm/atomic/txpool/mempool_test.go +++ b/plugin/evm/atomic/txpool/mempool_test.go @@ -71,7 +71,7 @@ func TestMempoolAddNoGas(t *testing.T) { tx := atomictest.GenerateTestImportTxWithGas(0, 1) err = m.Add(tx) - require.ErrorIs(err, ErrNoGasUsed) + require.ErrorIs(err, atomic.ErrNoGasUsed) } // Add should return an error if a tx doesn't consume any gas diff --git a/plugin/evm/atomic/txpool/tx_heap.go b/plugin/evm/atomic/txpool/tx_heap.go index e5c82fc24e..dc3c5f6839 100644 --- a/plugin/evm/atomic/txpool/tx_heap.go +++ b/plugin/evm/atomic/txpool/tx_heap.go @@ -7,13 +7,14 @@ import ( "container/heap" "github.com/ava-labs/coreth/plugin/evm/atomic" + "github.com/holiman/uint256" "github.com/ava-labs/avalanchego/ids" ) type txEntry struct { id ids.ID - gasPrice uint64 + gasPrice uint256.Int tx *atomic.Tx index int } @@ -37,9 +38,9 @@ func (th internalTxHeap) Len() int { return len(th.items) } func (th internalTxHeap) Less(i, j int) bool { if th.isMinHeap { - return th.items[i].gasPrice < th.items[j].gasPrice + return th.items[i].gasPrice.Lt(&th.items[j].gasPrice) } - return th.items[i].gasPrice > th.items[j].gasPrice + return th.items[i].gasPrice.Gt(&th.items[j].gasPrice) } func (th internalTxHeap) Swap(i, j int) { @@ -91,7 +92,7 @@ func newTxHeap(maxSize int) *txHeap { } } -func (th *txHeap) Push(tx *atomic.Tx, gasPrice uint64) { +func (th *txHeap) Push(tx *atomic.Tx, gasPrice uint256.Int) { txID := tx.ID() oldLen := th.Len() heap.Push(th.maxHeap, &txEntry{ @@ -109,13 +110,13 @@ func (th *txHeap) Push(tx *atomic.Tx, gasPrice uint64) { } // Assumes there is non-zero items -func (th *txHeap) PeekMax() (*atomic.Tx, uint64) { +func (th *txHeap) PeekMax() (*atomic.Tx, uint256.Int) { txEntry := th.maxHeap.items[0] return txEntry.tx, txEntry.gasPrice } // Assumes there is non-zero items -func (th *txHeap) PeekMin() (*atomic.Tx, uint64) { +func (th *txHeap) PeekMin() (*atomic.Tx, uint256.Int) { txEntry := th.minHeap.items[0] return txEntry.tx, txEntry.gasPrice } diff --git a/plugin/evm/atomic/txpool/tx_heap_test.go b/plugin/evm/atomic/txpool/tx_heap_test.go index 351b81624f..14fc911c8a 100644 --- a/plugin/evm/atomic/txpool/tx_heap_test.go +++ b/plugin/evm/atomic/txpool/tx_heap_test.go @@ -7,6 +7,7 @@ import ( "testing" "github.com/ava-labs/coreth/plugin/evm/atomic" + "github.com/holiman/uint256" "github.com/stretchr/testify/assert" ) @@ -47,7 +48,7 @@ func TestTxHeap(t *testing.T) { assert.Zero(t, h.Len()) assert := assert.New(t) - h.Push(tx0, 5) + h.Push(tx0, *uint256.NewInt(5)) assert.True(h.Has(id0)) gTx0, gHas0 := h.Get(id0) assert.Equal(tx0, gTx0) @@ -55,7 +56,7 @@ func TestTxHeap(t *testing.T) { h.Remove(id0) assert.False(h.Has(id0)) assert.Zero(h.Len()) - h.Push(tx0, 5) + h.Push(tx0, *uint256.NewInt(5)) assert.True(h.Has(id0)) assert.Equal(1, h.Len()) }) @@ -65,13 +66,13 @@ func TestTxHeap(t *testing.T) { assert.Zero(t, h.Len()) assert := assert.New(t) - h.Push(tx1, 10) + h.Push(tx1, *uint256.NewInt(10)) assert.True(h.Has(id1)) gTx1, gHas1 := h.Get(id1) assert.Equal(tx1, gTx1) assert.True(gHas1) - h.Push(tx2, 2) + h.Push(tx2, *uint256.NewInt(2)) assert.True(h.Has(id2)) gTx2, gHas2 := h.Get(id2) assert.Equal(tx2, gTx2) @@ -118,27 +119,27 @@ func TestTxHeap(t *testing.T) { h := newTxHeap(3) assert.Zero(t, h.Len()) - h.Push(tx0, 5) - h.Push(tx1, 10) - h.Push(tx2, 2) + h.Push(tx0, *uint256.NewInt(5)) + h.Push(tx1, *uint256.NewInt(10)) + h.Push(tx2, *uint256.NewInt(2)) verifyRemovalOrder(t, h) }) t.Run("drop (alt order)", func(t *testing.T) { h := newTxHeap(3) assert.Zero(t, h.Len()) - h.Push(tx0, 5) - h.Push(tx2, 2) - h.Push(tx1, 10) + h.Push(tx0, *uint256.NewInt(5)) + h.Push(tx2, *uint256.NewInt(2)) + h.Push(tx1, *uint256.NewInt(10)) verifyRemovalOrder(t, h) }) t.Run("drop (alt order 2)", func(t *testing.T) { h := newTxHeap(3) assert.Zero(t, h.Len()) - h.Push(tx2, 2) - h.Push(tx0, 5) - h.Push(tx1, 10) + h.Push(tx2, *uint256.NewInt(2)) + h.Push(tx0, *uint256.NewInt(5)) + h.Push(tx1, *uint256.NewInt(10)) verifyRemovalOrder(t, h) }) } diff --git a/plugin/evm/atomic/txpool/txs.go b/plugin/evm/atomic/txpool/txs.go index c234a2f3a0..800f3246e9 100644 --- a/plugin/evm/atomic/txpool/txs.go +++ b/plugin/evm/atomic/txpool/txs.go @@ -4,7 +4,6 @@ package txpool import ( - "errors" "sync" "github.com/ava-labs/avalanchego/cache/lru" @@ -17,8 +16,6 @@ import ( const discardedTxsCacheSize = 50 -var ErrNoGasUsed = errors.New("no gas used") - // Txs stores the transactions inside of the mempool. // // Transactions in the mempool can be in 1 of 4 statuses: @@ -86,22 +83,6 @@ func (t *Txs) PendingLen() int { return t.pendingTxs.Len() } -// atomicTxGasPrice returns the gasPrice of a transaction in nAVAX/gas. -func (t *Txs) atomicTxGasPrice(tx *atomic.Tx) (uint64, error) { - gasUsed, err := tx.GasUsed(true) - if err != nil { - return 0, err - } - if gasUsed == 0 { - return 0, ErrNoGasUsed - } - burned, err := tx.Burned(t.ctx.AVAXAssetID) - if err != nil { - return 0, err - } - return burned / gasUsed, nil -} - // Iterate applies f to all Pending transactions. If f returns false, the // iteration stops early. func (t *Txs) Iterate(f func(tx *atomic.Tx) bool) { @@ -214,7 +195,9 @@ func (t *Txs) CancelCurrentTxs() { // Assumes the lock is held. func (t *Txs) cancelTx(tx *atomic.Tx) { txID := tx.ID() - gasPrice, err := t.atomicTxGasPrice(tx) + gasPrice, err := atomic.EffectiveGasPrice(tx, t.ctx.AVAXAssetID, true) + // Should never error to calculate the gas price of a transaction already in + // the mempool if err != nil { log.Error("failed to calculate atomic tx gas price while canceling current tx", "txID", txID, From 60661566eb882e4ddb257e5f24c1207a5dcf45d8 Mon Sep 17 00:00:00 2001 From: Ceyhun Onur Date: Thu, 14 Aug 2025 21:11:39 +0300 Subject: [PATCH 11/20] Atomic vm tests (#1112) --- plugin/evm/atomic/vm/api.go | 4 +- plugin/evm/{ => atomic/vm}/export_tx_test.go | 421 +- .../vm}/gossiper_atomic_gossiping_test.go | 84 +- plugin/evm/{ => atomic/vm}/import_tx_test.go | 346 +- plugin/evm/atomic/vm/syncervm_test.go | 124 + plugin/evm/atomic/vm/tx_gossip_test.go | 319 ++ plugin/evm/atomic/vm/tx_semantic_verifier.go | 2 +- plugin/evm/{ => atomic/vm}/tx_test.go | 112 +- plugin/evm/atomic/vm/vm.go | 16 +- plugin/evm/atomic/vm/vm_test.go | 2162 +++++++++++ plugin/evm/block_builder.go | 12 +- plugin/evm/extension/config.go | 13 +- plugin/evm/gossiper_eth_gossiping_test.go | 20 +- plugin/evm/mempool_atomic_gossiping_test.go | 96 - plugin/evm/syncervm_test.go | 684 +--- plugin/evm/tx_gossip_test.go | 316 +- plugin/evm/vm_extensible.go | 6 +- plugin/evm/vm_test.go | 3419 ++++------------- plugin/evm/vm_warp_test.go | 237 +- plugin/evm/vmtest/genesis.go | 154 + plugin/evm/vmtest/test_syncervm.go | 684 ++++ plugin/evm/vmtest/test_vm.go | 131 + utils/utilstest/context.go | 45 + 23 files changed, 5065 insertions(+), 4342 deletions(-) rename plugin/evm/{ => atomic/vm}/export_tx_test.go (77%) rename plugin/evm/{ => atomic/vm}/gossiper_atomic_gossiping_test.go (69%) rename plugin/evm/{ => atomic/vm}/import_tx_test.go (75%) create mode 100644 plugin/evm/atomic/vm/syncervm_test.go create mode 100644 plugin/evm/atomic/vm/tx_gossip_test.go rename plugin/evm/{ => atomic/vm}/tx_test.go (57%) create mode 100644 plugin/evm/atomic/vm/vm_test.go delete mode 100644 plugin/evm/mempool_atomic_gossiping_test.go create mode 100644 plugin/evm/vmtest/genesis.go create mode 100644 plugin/evm/vmtest/test_syncervm.go create mode 100644 plugin/evm/vmtest/test_vm.go create mode 100644 utils/utilstest/context.go diff --git a/plugin/evm/atomic/vm/api.go b/plugin/evm/atomic/vm/api.go index 003aa08eb5..640dd7ba37 100644 --- a/plugin/evm/atomic/vm/api.go +++ b/plugin/evm/atomic/vm/api.go @@ -190,7 +190,7 @@ func (service *AvaxAPI) GetAtomicTxStatus(r *http.Request, args *api.JSONTxID, r // Since chain state updates run asynchronously with VM block acceptance, // avoid returning [Accepted] until the chain state reaches the block // containing the atomic tx. - lastAccepted := service.vm.InnerVM.Blockchain().LastAcceptedBlock() + lastAccepted := service.vm.InnerVM.Ethereum().BlockChain().LastAcceptedBlock() if height > lastAccepted.NumberU64() { reply.Status = atomic.Processing return nil @@ -237,7 +237,7 @@ func (service *AvaxAPI) GetAtomicTx(r *http.Request, args *api.GetTxArgs, reply // Since chain state updates run asynchronously with VM block acceptance, // avoid returning [Accepted] until the chain state reaches the block // containing the atomic tx. - lastAccepted := service.vm.InnerVM.Blockchain().LastAcceptedBlock() + lastAccepted := service.vm.InnerVM.Ethereum().BlockChain().LastAcceptedBlock() if height > lastAccepted.NumberU64() { return nil } diff --git a/plugin/evm/export_tx_test.go b/plugin/evm/atomic/vm/export_tx_test.go similarity index 77% rename from plugin/evm/export_tx_test.go rename to plugin/evm/atomic/vm/export_tx_test.go index b41bd1230c..2201c869b0 100644 --- a/plugin/evm/export_tx_test.go +++ b/plugin/evm/atomic/vm/export_tx_test.go @@ -1,7 +1,7 @@ // Copyright (C) 2019-2025, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. -package evm +package vm import ( "bytes" @@ -9,11 +9,6 @@ import ( "math/big" "testing" - "github.com/ava-labs/coreth/plugin/evm/atomic" - atomicvm "github.com/ava-labs/coreth/plugin/evm/atomic/vm" - - "github.com/stretchr/testify/require" - avalancheatomic "github.com/ava-labs/avalanchego/chains/atomic" "github.com/ava-labs/avalanchego/ids" commonEng "github.com/ava-labs/avalanchego/snow/engine/common" @@ -27,14 +22,21 @@ import ( "github.com/ava-labs/avalanchego/vms/secp256k1fx" "github.com/ava-labs/coreth/core/extstate" "github.com/ava-labs/coreth/params/extras" + "github.com/ava-labs/coreth/plugin/evm/atomic" + "github.com/ava-labs/coreth/plugin/evm/vmtest" "github.com/ava-labs/coreth/utils" "github.com/ava-labs/libevm/common" "github.com/holiman/uint256" + "github.com/stretchr/testify/require" ) // createExportTxOptions adds funds to shared memory, imports them, and returns a list of export transactions // that attempt to send the funds to each of the test keys (list of length 3). -func createExportTxOptions(t *testing.T, vm *atomicvm.VM, sharedMemory *avalancheatomic.Memory) []*atomic.Tx { +func createExportTxOptions(t *testing.T, vm *VM, sharedMemory *avalancheatomic.Memory) []*atomic.Tx { + key, err := secp256k1.NewPrivateKey() + require.NoError(t, err) + ethAddr := key.EthAddress() + // Add a UTXO to shared memory utxo := &avax.UTXO{ UTXOID: avax.UTXOID{TxID: ids.GenerateTestID()}, @@ -43,7 +45,7 @@ func createExportTxOptions(t *testing.T, vm *atomicvm.VM, sharedMemory *avalanch Amt: uint64(50000000), OutputOwners: secp256k1fx.OutputOwners{ Threshold: 1, - Addrs: []ids.ShortID{testKeys[0].Address()}, + Addrs: []ids.ShortID{key.Address()}, }, }, } @@ -58,14 +60,14 @@ func createExportTxOptions(t *testing.T, vm *atomicvm.VM, sharedMemory *avalanch Key: inputID[:], Value: utxoBytes, Traits: [][]byte{ - testKeys[0].Address().Bytes(), + key.Address().Bytes(), }, }}}}); err != nil { t.Fatal(err) } // Import the funds - importTx, err := vm.NewImportTx(vm.Ctx.XChainID, testEthAddrs[0], initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) + importTx, err := vm.newImportTx(vm.Ctx.XChainID, ethAddr, vmtest.InitialBaseFee, []*secp256k1.PrivateKey{key}) if err != nil { t.Fatal(err) } @@ -95,7 +97,7 @@ func createExportTxOptions(t *testing.T, vm *atomicvm.VM, sharedMemory *avalanch t.Fatal(err) } - statedb, err := vm.Blockchain().State() + statedb, err := vm.Ethereum().BlockChain().State() if err != nil { t.Fatal(err) } @@ -103,8 +105,8 @@ func createExportTxOptions(t *testing.T, vm *atomicvm.VM, sharedMemory *avalanch // Use the funds to create 3 conflicting export transactions sending the funds to each of the test addresses exportTxs := make([]*atomic.Tx, 0, 3) wrappedStateDB := extstate.New(statedb) - for _, addr := range testShortIDAddrs { - exportTx, err := atomic.NewExportTx(vm.Ctx, vm.CurrentRules(), wrappedStateDB, vm.Ctx.AVAXAssetID, uint64(5000000), vm.Ctx.XChainID, addr, initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) + for _, addr := range vmtest.TestShortIDAddrs { + exportTx, err := atomic.NewExportTx(vm.Ctx, vm.CurrentRules(), wrappedStateDB, vm.Ctx.AVAXAssetID, uint64(5000000), vm.Ctx.XChainID, addr, vmtest.InitialBaseFee, []*secp256k1.PrivateKey{vmtest.TestKeys[0]}) if err != nil { t.Fatal(err) } @@ -115,7 +117,8 @@ func createExportTxOptions(t *testing.T, vm *atomicvm.VM, sharedMemory *avalanch } func TestExportTxEVMStateTransfer(t *testing.T) { - key := testKeys[0] + key, err := secp256k1.NewPrivateKey() + require.NoError(t, err) addr := key.Address() ethAddr := key.EthAddress() @@ -337,18 +340,19 @@ func TestExportTxEVMStateTransfer(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { fork := upgradetest.NoUpgrades - tvm := newVM(t, testVMConfig{ - fork: &fork, + vm := newAtomicTestVM() + tvm := vmtest.SetupTestVM(t, vm, vmtest.TestVMConfig{ + Fork: &fork, }) defer func() { - if err := tvm.vm.Shutdown(context.Background()); err != nil { + if err := vm.Shutdown(context.Background()); err != nil { t.Fatal(err) } }() avaxUTXO := &avax.UTXO{ UTXOID: avaxUTXOID, - Asset: avax.Asset{ID: tvm.vm.ctx.AVAXAssetID}, + Asset: avax.Asset{ID: vm.Ctx.AVAXAssetID}, Out: &secp256k1fx.TransferOutput{ Amt: avaxAmount, OutputOwners: secp256k1fx.OutputOwners{ @@ -368,8 +372,8 @@ func TestExportTxEVMStateTransfer(t *testing.T) { t.Fatal(err) } - xChainSharedMemory := tvm.atomicMemory.NewSharedMemory(tvm.vm.ctx.XChainID) - if err := xChainSharedMemory.Apply(map[ids.ID]*avalancheatomic.Requests{tvm.vm.ctx.ChainID: {PutRequests: []*avalancheatomic.Element{ + xChainSharedMemory := tvm.AtomicMemory.NewSharedMemory(vm.Ctx.XChainID) + if err := xChainSharedMemory.Apply(map[ids.ID]*avalancheatomic.Requests{vm.Ctx.ChainID: {PutRequests: []*avalancheatomic.Element{ { Key: avaxInputID[:], Value: avaxUTXOBytes, @@ -388,18 +392,20 @@ func TestExportTxEVMStateTransfer(t *testing.T) { t.Fatal(err) } - tx, err := tvm.atomicVM.NewImportTx(tvm.vm.ctx.XChainID, testEthAddrs[0], initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) + tx, err := vm.newImportTx(vm.Ctx.XChainID, ethAddr, vmtest.InitialBaseFee, []*secp256k1.PrivateKey{key}) if err != nil { t.Fatal(err) } - if err := tvm.atomicVM.AtomicMempool.AddLocalTx(tx); err != nil { + if err := vm.AtomicMempool.AddLocalTx(tx); err != nil { t.Fatal(err) } - require.Equal(t, commonEng.PendingTxs, tvm.WaitForEvent(context.Background())) + msg, err := tvm.VM.WaitForEvent(context.Background()) + require.NoError(t, err) + require.Equal(t, commonEng.PendingTxs, msg) - blk, err := tvm.vm.BuildBlock(context.Background()) + blk, err := vm.BuildBlock(context.Background()) if err != nil { t.Fatal(err) } @@ -408,7 +414,7 @@ func TestExportTxEVMStateTransfer(t *testing.T) { t.Fatal(err) } - if err := tvm.vm.SetPreference(context.Background(), blk.ID()); err != nil { + if err := vm.SetPreference(context.Background(), blk.ID()); err != nil { t.Fatal(err) } @@ -420,13 +426,13 @@ func TestExportTxEVMStateTransfer(t *testing.T) { Ins: test.tx, } - statedb, err := tvm.vm.blockChain.State() + statedb, err := vm.Ethereum().BlockChain().State() if err != nil { t.Fatal(err) } wrappedStateDB := extstate.New(statedb) - err = newTx.EVMStateTransfer(tvm.vm.ctx, wrappedStateDB) + err = newTx.EVMStateTransfer(vm.Ctx, wrappedStateDB) if test.shouldErr { if err == nil { t.Fatal("expected EVMStateTransfer to fail") @@ -458,10 +464,10 @@ func TestExportTxEVMStateTransfer(t *testing.T) { func TestExportTxSemanticVerify(t *testing.T) { fork := upgradetest.NoUpgrades - tvm := newVM(t, testVMConfig{ - fork: &fork, + vm := newAtomicTestVM() + vmtest.SetupTestVM(t, vm, vmtest.TestVMConfig{ + Fork: &fork, }) - vm := tvm.atomicVM defer func() { if err := vm.Shutdown(context.Background()); err != nil { t.Fatal(err) @@ -470,9 +476,9 @@ func TestExportTxSemanticVerify(t *testing.T) { parent := vm.LastAcceptedExtendedBlock() - key := testKeys[0] + key := vmtest.TestKeys[0] addr := key.Address() - ethAddr := testEthAddrs[0] + ethAddr := vmtest.TestEthAddrs[0] var ( avaxBalance = 10 * units.Avax @@ -552,7 +558,7 @@ func TestExportTxSemanticVerify(t *testing.T) { tx *atomic.Tx signers [][]*secp256k1.PrivateKey baseFee *big.Int - rules extras.Rules + rules *extras.Rules shouldErr bool }{ { @@ -563,8 +569,8 @@ func TestExportTxSemanticVerify(t *testing.T) { {key}, {key}, }, - baseFee: initialBaseFee, - rules: apricotRulesPhase3, + baseFee: vmtest.InitialBaseFee, + rules: vmtest.ForkToRules(upgradetest.ApricotPhase3), shouldErr: false, }, { @@ -577,8 +583,8 @@ func TestExportTxSemanticVerify(t *testing.T) { signers: [][]*secp256k1.PrivateKey{ {key}, }, - baseFee: initialBaseFee, - rules: apricotRulesPhase3, + baseFee: vmtest.InitialBaseFee, + rules: vmtest.ForkToRules(upgradetest.ApricotPhase3), shouldErr: true, }, { @@ -591,8 +597,8 @@ func TestExportTxSemanticVerify(t *testing.T) { signers: [][]*secp256k1.PrivateKey{ {key}, }, - baseFee: initialBaseFee, - rules: apricotRulesPhase5, + baseFee: vmtest.InitialBaseFee, + rules: vmtest.ForkToRules(upgradetest.ApricotPhase5), shouldErr: false, }, { @@ -605,8 +611,8 @@ func TestExportTxSemanticVerify(t *testing.T) { signers: [][]*secp256k1.PrivateKey{ {key}, }, - baseFee: initialBaseFee, - rules: apricotRulesPhase5, + baseFee: vmtest.InitialBaseFee, + rules: vmtest.ForkToRules(upgradetest.ApricotPhase5), shouldErr: true, }, { @@ -621,8 +627,8 @@ func TestExportTxSemanticVerify(t *testing.T) { {key}, {key}, }, - baseFee: initialBaseFee, - rules: apricotRulesPhase3, + baseFee: vmtest.InitialBaseFee, + rules: vmtest.ForkToRules(upgradetest.ApricotPhase3), shouldErr: true, }, { @@ -637,8 +643,8 @@ func TestExportTxSemanticVerify(t *testing.T) { {key}, {key}, }, - baseFee: initialBaseFee, - rules: apricotRulesPhase5, + baseFee: vmtest.InitialBaseFee, + rules: vmtest.ForkToRules(upgradetest.ApricotPhase5), shouldErr: true, }, { @@ -653,8 +659,8 @@ func TestExportTxSemanticVerify(t *testing.T) { {key}, {key}, }, - baseFee: initialBaseFee, - rules: apricotRulesPhase5, + baseFee: vmtest.InitialBaseFee, + rules: vmtest.ForkToRules(upgradetest.ApricotPhase5), shouldErr: true, }, { @@ -669,8 +675,8 @@ func TestExportTxSemanticVerify(t *testing.T) { {key}, {key}, }, - baseFee: initialBaseFee, - rules: apricotRulesPhase3, + baseFee: vmtest.InitialBaseFee, + rules: vmtest.ForkToRules(upgradetest.ApricotPhase3), shouldErr: true, }, { @@ -685,8 +691,8 @@ func TestExportTxSemanticVerify(t *testing.T) { {key}, {key}, }, - baseFee: initialBaseFee, - rules: apricotRulesPhase3, + baseFee: vmtest.InitialBaseFee, + rules: vmtest.ForkToRules(upgradetest.ApricotPhase3), shouldErr: true, }, { @@ -701,8 +707,8 @@ func TestExportTxSemanticVerify(t *testing.T) { {key}, {key}, }, - baseFee: initialBaseFee, - rules: apricotRulesPhase3, + baseFee: vmtest.InitialBaseFee, + rules: vmtest.ForkToRules(upgradetest.ApricotPhase3), shouldErr: true, }, { @@ -718,8 +724,8 @@ func TestExportTxSemanticVerify(t *testing.T) { {key}, {key}, }, - baseFee: initialBaseFee, - rules: apricotRulesPhase3, + baseFee: vmtest.InitialBaseFee, + rules: vmtest.ForkToRules(upgradetest.ApricotPhase3), shouldErr: true, }, { @@ -743,8 +749,8 @@ func TestExportTxSemanticVerify(t *testing.T) { {key}, {key}, }, - baseFee: initialBaseFee, - rules: apricotRulesPhase3, + baseFee: vmtest.InitialBaseFee, + rules: vmtest.ForkToRules(upgradetest.ApricotPhase3), shouldErr: true, }, { @@ -784,8 +790,8 @@ func TestExportTxSemanticVerify(t *testing.T) { {key}, {key}, }, - baseFee: initialBaseFee, - rules: apricotRulesPhase3, + baseFee: vmtest.InitialBaseFee, + rules: vmtest.ForkToRules(upgradetest.ApricotPhase3), shouldErr: true, }, { @@ -801,8 +807,8 @@ func TestExportTxSemanticVerify(t *testing.T) { {key}, {key}, }, - baseFee: initialBaseFee, - rules: apricotRulesPhase3, + baseFee: vmtest.InitialBaseFee, + rules: vmtest.ForkToRules(upgradetest.ApricotPhase3), shouldErr: true, }, { @@ -828,8 +834,8 @@ func TestExportTxSemanticVerify(t *testing.T) { {key}, {key}, }, - baseFee: initialBaseFee, - rules: apricotRulesPhase3, + baseFee: vmtest.InitialBaseFee, + rules: vmtest.ForkToRules(upgradetest.ApricotPhase3), shouldErr: true, }, { @@ -855,8 +861,8 @@ func TestExportTxSemanticVerify(t *testing.T) { {key}, {key}, }, - baseFee: initialBaseFee, - rules: apricotRulesPhase3, + baseFee: vmtest.InitialBaseFee, + rules: vmtest.ForkToRules(upgradetest.ApricotPhase3), shouldErr: true, }, { @@ -868,8 +874,8 @@ func TestExportTxSemanticVerify(t *testing.T) { {key}, {key}, }, - baseFee: initialBaseFee, - rules: apricotRulesPhase3, + baseFee: vmtest.InitialBaseFee, + rules: vmtest.ForkToRules(upgradetest.ApricotPhase3), shouldErr: true, }, { @@ -879,20 +885,20 @@ func TestExportTxSemanticVerify(t *testing.T) { {key}, {key}, }, - baseFee: initialBaseFee, - rules: apricotRulesPhase3, + baseFee: vmtest.InitialBaseFee, + rules: vmtest.ForkToRules(upgradetest.ApricotPhase3), shouldErr: true, }, { name: "too many signatures on credential", tx: &atomic.Tx{UnsignedAtomicTx: validExportTx}, signers: [][]*secp256k1.PrivateKey{ - {key, testKeys[1]}, + {key, vmtest.TestKeys[1]}, {key}, {key}, }, - baseFee: initialBaseFee, - rules: apricotRulesPhase3, + baseFee: vmtest.InitialBaseFee, + rules: vmtest.ForkToRules(upgradetest.ApricotPhase3), shouldErr: true, }, { @@ -903,28 +909,28 @@ func TestExportTxSemanticVerify(t *testing.T) { {key}, {key}, }, - baseFee: initialBaseFee, - rules: apricotRulesPhase3, + baseFee: vmtest.InitialBaseFee, + rules: vmtest.ForkToRules(upgradetest.ApricotPhase3), shouldErr: true, }, { name: "wrong signature on credential", tx: &atomic.Tx{UnsignedAtomicTx: validExportTx}, signers: [][]*secp256k1.PrivateKey{ - {testKeys[1]}, + {vmtest.TestKeys[1]}, {key}, {key}, }, - baseFee: initialBaseFee, - rules: apricotRulesPhase3, + baseFee: vmtest.InitialBaseFee, + rules: vmtest.ForkToRules(upgradetest.ApricotPhase3), shouldErr: true, }, { name: "no signatures", tx: &atomic.Tx{UnsignedAtomicTx: validExportTx}, signers: [][]*secp256k1.PrivateKey{}, - baseFee: initialBaseFee, - rules: apricotRulesPhase3, + baseFee: vmtest.InitialBaseFee, + rules: vmtest.ForkToRules(upgradetest.ApricotPhase3), shouldErr: true, }, } @@ -933,11 +939,10 @@ func TestExportTxSemanticVerify(t *testing.T) { t.Fatal(err) } - backend := atomicvm.NewVerifierBackend(vm, test.rules) + backend := NewVerifierBackend(vm, *test.rules) t.Run(test.name, func(t *testing.T) { tx := test.tx - err := backend.SemanticVerify(tx, parent, test.baseFee) if test.shouldErr && err == nil { t.Fatalf("should have errored but returned valid") @@ -951,20 +956,21 @@ func TestExportTxSemanticVerify(t *testing.T) { func TestExportTxAccept(t *testing.T) { fork := upgradetest.NoUpgrades - tvm := newVM(t, testVMConfig{ - fork: &fork, + vm := newAtomicTestVM() + tvm := vmtest.SetupTestVM(t, vm, vmtest.TestVMConfig{ + Fork: &fork, }) defer func() { - if err := tvm.vm.Shutdown(context.Background()); err != nil { + if err := vm.Shutdown(context.Background()); err != nil { t.Fatal(err) } }() - xChainSharedMemory := tvm.atomicMemory.NewSharedMemory(tvm.vm.ctx.XChainID) + xChainSharedMemory := tvm.AtomicMemory.NewSharedMemory(vm.Ctx.XChainID) - key := testKeys[0] + key := vmtest.TestKeys[0] addr := key.Address() - ethAddr := testEthAddrs[0] + ethAddr := vmtest.TestEthAddrs[0] var ( avaxBalance = 10 * units.Avax @@ -973,14 +979,14 @@ func TestExportTxAccept(t *testing.T) { ) exportTx := &atomic.UnsignedExportTx{ - NetworkID: tvm.vm.ctx.NetworkID, - BlockchainID: tvm.vm.ctx.ChainID, - DestinationChain: tvm.vm.ctx.XChainID, + NetworkID: vm.Ctx.NetworkID, + BlockchainID: vm.Ctx.ChainID, + DestinationChain: vm.Ctx.XChainID, Ins: []atomic.EVMInput{ { Address: ethAddr, Amount: avaxBalance, - AssetID: tvm.vm.ctx.AVAXAssetID, + AssetID: vm.Ctx.AVAXAssetID, Nonce: 0, }, { @@ -992,7 +998,7 @@ func TestExportTxAccept(t *testing.T) { }, ExportedOutputs: []*avax.TransferableOutput{ { - Asset: avax.Asset{ID: tvm.vm.ctx.AVAXAssetID}, + Asset: avax.Asset{ID: vm.Ctx.AVAXAssetID}, Out: &secp256k1fx.TransferOutput{ Amt: avaxBalance, OutputOwners: secp256k1fx.OutputOwners{ @@ -1026,7 +1032,7 @@ func TestExportTxAccept(t *testing.T) { t.Fatal(err) } - commitBatch, err := tvm.vm.versiondb.CommitBatch() + commitBatch, err := vm.VersionDB().CommitBatch() if err != nil { t.Fatalf("Failed to create commit batch for VM due to %s", err) } @@ -1035,10 +1041,10 @@ func TestExportTxAccept(t *testing.T) { t.Fatalf("Failed to accept export transaction due to: %s", err) } - if err := tvm.vm.ctx.SharedMemory.Apply(map[ids.ID]*avalancheatomic.Requests{chainID: atomicRequests}, commitBatch); err != nil { + if err := vm.Ctx.SharedMemory.Apply(map[ids.ID]*avalancheatomic.Requests{chainID: atomicRequests}, commitBatch); err != nil { t.Fatal(err) } - indexedValues, _, _, err := xChainSharedMemory.Indexed(tvm.vm.ctx.ChainID, [][]byte{addr.Bytes()}, nil, nil, 3) + indexedValues, _, _, err := xChainSharedMemory.Indexed(vm.Ctx.ChainID, [][]byte{addr.Bytes()}, nil, nil, 3) if err != nil { t.Fatal(err) } @@ -1059,7 +1065,7 @@ func TestExportTxAccept(t *testing.T) { } customInputID := customUTXOID.InputID() - fetchedValues, err := xChainSharedMemory.Get(tvm.vm.ctx.ChainID, [][]byte{ + fetchedValues, err := xChainSharedMemory.Get(vm.Ctx.ChainID, [][]byte{ customInputID[:], avaxInputID[:], }) @@ -1085,7 +1091,7 @@ func TestExportTxAccept(t *testing.T) { avaxUTXOBytes, err := atomic.Codec.Marshal(atomic.CodecVersion, &avax.UTXO{ UTXOID: avaxUTXOID, - Asset: avax.Asset{ID: tvm.vm.ctx.AVAXAssetID}, + Asset: avax.Asset{ID: vm.Ctx.AVAXAssetID}, Out: exportTx.ExportedOutputs[0].Out, }) if err != nil { @@ -1108,13 +1114,13 @@ func TestExportTxVerify(t *testing.T) { DestinationChain: snowtest.XChainID, Ins: []atomic.EVMInput{ { - Address: testEthAddrs[0], + Address: vmtest.TestEthAddrs[0], Amount: exportAmount, AssetID: snowtest.AVAXAssetID, Nonce: 0, }, { - Address: testEthAddrs[2], + Address: vmtest.TestEthAddrs[2], Amount: exportAmount, AssetID: snowtest.AVAXAssetID, Nonce: 0, @@ -1128,7 +1134,7 @@ func TestExportTxVerify(t *testing.T) { OutputOwners: secp256k1fx.OutputOwners{ Locktime: 0, Threshold: 1, - Addrs: []ids.ShortID{testShortIDAddrs[0]}, + Addrs: []ids.ShortID{vmtest.TestShortIDAddrs[0]}, }, }, }, @@ -1139,7 +1145,7 @@ func TestExportTxVerify(t *testing.T) { OutputOwners: secp256k1fx.OutputOwners{ Locktime: 0, Threshold: 1, - Addrs: []ids.ShortID{testShortIDAddrs[1]}, + Addrs: []ids.ShortID{vmtest.TestShortIDAddrs[1]}, }, }, }, @@ -1161,7 +1167,7 @@ func TestExportTxVerify(t *testing.T) { return (*atomic.UnsignedExportTx)(nil) }, ctx: ctx, - rules: apricotRulesPhase0, + rules: vmtest.ForkToRules(upgradetest.NoUpgrades), expectedErr: atomic.ErrNilTx.Error(), }, "valid export tx": { @@ -1169,7 +1175,7 @@ func TestExportTxVerify(t *testing.T) { return exportTx }, ctx: ctx, - rules: apricotRulesPhase0, + rules: vmtest.ForkToRules(upgradetest.NoUpgrades), expectedErr: "", }, "valid export tx banff": { @@ -1177,7 +1183,7 @@ func TestExportTxVerify(t *testing.T) { return exportTx }, ctx: ctx, - rules: banffRules, + rules: vmtest.ForkToRules(upgradetest.Banff), expectedErr: "", }, "incorrect networkID": { @@ -1187,7 +1193,7 @@ func TestExportTxVerify(t *testing.T) { return &tx }, ctx: ctx, - rules: apricotRulesPhase0, + rules: vmtest.ForkToRules(upgradetest.NoUpgrades), expectedErr: atomic.ErrWrongNetworkID.Error(), }, "incorrect blockchainID": { @@ -1197,7 +1203,7 @@ func TestExportTxVerify(t *testing.T) { return &tx }, ctx: ctx, - rules: apricotRulesPhase0, + rules: vmtest.ForkToRules(upgradetest.NoUpgrades), expectedErr: atomic.ErrWrongChainID.Error(), }, "incorrect destination chain": { @@ -1207,7 +1213,7 @@ func TestExportTxVerify(t *testing.T) { return &tx }, ctx: ctx, - rules: apricotRulesPhase0, + rules: vmtest.ForkToRules(upgradetest.NoUpgrades), expectedErr: atomic.ErrWrongChainID.Error(), // TODO make this error more specific to destination not just chainID }, "no exported outputs": { @@ -1217,7 +1223,7 @@ func TestExportTxVerify(t *testing.T) { return &tx }, ctx: ctx, - rules: apricotRulesPhase0, + rules: vmtest.ForkToRules(upgradetest.NoUpgrades), expectedErr: atomic.ErrNoExportOutputs.Error(), }, "unsorted outputs": { @@ -1230,7 +1236,7 @@ func TestExportTxVerify(t *testing.T) { return &tx }, ctx: ctx, - rules: apricotRulesPhase0, + rules: vmtest.ForkToRules(upgradetest.NoUpgrades), expectedErr: atomic.ErrOutputsNotSorted.Error(), }, "invalid exported output": { @@ -1240,7 +1246,7 @@ func TestExportTxVerify(t *testing.T) { return &tx }, ctx: ctx, - rules: apricotRulesPhase0, + rules: vmtest.ForkToRules(upgradetest.NoUpgrades), expectedErr: "nil transferable output is not valid", }, "unsorted EVM inputs before AP1": { @@ -1253,7 +1259,7 @@ func TestExportTxVerify(t *testing.T) { return &tx }, ctx: ctx, - rules: apricotRulesPhase0, + rules: vmtest.ForkToRules(upgradetest.NoUpgrades), expectedErr: "", }, "unsorted EVM inputs after AP1": { @@ -1266,7 +1272,7 @@ func TestExportTxVerify(t *testing.T) { return &tx }, ctx: ctx, - rules: apricotRulesPhase1, + rules: vmtest.ForkToRules(upgradetest.ApricotPhase1), expectedErr: atomic.ErrInputsNotSortedUnique.Error(), }, "EVM input with amount 0": { @@ -1274,7 +1280,7 @@ func TestExportTxVerify(t *testing.T) { tx := *exportTx tx.Ins = []atomic.EVMInput{ { - Address: testEthAddrs[0], + Address: vmtest.TestEthAddrs[0], Amount: 0, AssetID: snowtest.AVAXAssetID, Nonce: 0, @@ -1283,7 +1289,7 @@ func TestExportTxVerify(t *testing.T) { return &tx }, ctx: ctx, - rules: apricotRulesPhase0, + rules: vmtest.ForkToRules(upgradetest.NoUpgrades), expectedErr: atomic.ErrNoValueInput.Error(), }, "non-unique EVM input before AP1": { @@ -1293,7 +1299,7 @@ func TestExportTxVerify(t *testing.T) { return &tx }, ctx: ctx, - rules: apricotRulesPhase0, + rules: vmtest.ForkToRules(upgradetest.NoUpgrades), expectedErr: "", }, "non-unique EVM input after AP1": { @@ -1303,7 +1309,7 @@ func TestExportTxVerify(t *testing.T) { return &tx }, ctx: ctx, - rules: apricotRulesPhase1, + rules: vmtest.ForkToRules(upgradetest.ApricotPhase1), expectedErr: atomic.ErrInputsNotSortedUnique.Error(), }, "non-AVAX input Apricot Phase 6": { @@ -1311,7 +1317,7 @@ func TestExportTxVerify(t *testing.T) { tx := *exportTx tx.Ins = []atomic.EVMInput{ { - Address: testEthAddrs[0], + Address: vmtest.TestEthAddrs[0], Amount: 1, AssetID: ids.GenerateTestID(), Nonce: 0, @@ -1320,7 +1326,7 @@ func TestExportTxVerify(t *testing.T) { return &tx }, ctx: ctx, - rules: apricotRulesPhase6, + rules: vmtest.ForkToRules(upgradetest.ApricotPhase6), expectedErr: "", }, "non-AVAX output Apricot Phase 6": { @@ -1334,7 +1340,7 @@ func TestExportTxVerify(t *testing.T) { OutputOwners: secp256k1fx.OutputOwners{ Locktime: 0, Threshold: 1, - Addrs: []ids.ShortID{testShortIDAddrs[0]}, + Addrs: []ids.ShortID{vmtest.TestShortIDAddrs[0]}, }, }, }, @@ -1342,7 +1348,7 @@ func TestExportTxVerify(t *testing.T) { return &tx }, ctx: ctx, - rules: apricotRulesPhase6, + rules: vmtest.ForkToRules(upgradetest.ApricotPhase6), expectedErr: "", }, "non-AVAX input Banff": { @@ -1350,7 +1356,7 @@ func TestExportTxVerify(t *testing.T) { tx := *exportTx tx.Ins = []atomic.EVMInput{ { - Address: testEthAddrs[0], + Address: vmtest.TestEthAddrs[0], Amount: 1, AssetID: ids.GenerateTestID(), Nonce: 0, @@ -1359,7 +1365,7 @@ func TestExportTxVerify(t *testing.T) { return &tx }, ctx: ctx, - rules: banffRules, + rules: vmtest.ForkToRules(upgradetest.Banff), expectedErr: atomic.ErrExportNonAVAXInputBanff.Error(), }, "non-AVAX output Banff": { @@ -1373,7 +1379,7 @@ func TestExportTxVerify(t *testing.T) { OutputOwners: secp256k1fx.OutputOwners{ Locktime: 0, Threshold: 1, - Addrs: []ids.ShortID{testShortIDAddrs[0]}, + Addrs: []ids.ShortID{vmtest.TestShortIDAddrs[0]}, }, }, }, @@ -1381,7 +1387,7 @@ func TestExportTxVerify(t *testing.T) { return &tx }, ctx: ctx, - rules: banffRules, + rules: vmtest.ForkToRules(upgradetest.Banff), expectedErr: atomic.ErrExportNonAVAXOutputBanff.Error(), }, } @@ -1418,7 +1424,7 @@ func TestExportTxGasCost(t *testing.T) { DestinationChain: xChainID, Ins: []atomic.EVMInput{ { - Address: testEthAddrs[0], + Address: vmtest.TestEthAddrs[0], Amount: exportAmount, AssetID: avaxAssetID, Nonce: 0, @@ -1432,13 +1438,13 @@ func TestExportTxGasCost(t *testing.T) { OutputOwners: secp256k1fx.OutputOwners{ Locktime: 0, Threshold: 1, - Addrs: []ids.ShortID{testShortIDAddrs[0]}, + Addrs: []ids.ShortID{vmtest.TestShortIDAddrs[0]}, }, }, }, }, }, - Keys: [][]*secp256k1.PrivateKey{{testKeys[0]}}, + Keys: [][]*secp256k1.PrivateKey{{vmtest.TestKeys[0]}}, ExpectedGasUsed: 1230, ExpectedFee: 1, BaseFee: big.NewInt(1), @@ -1450,7 +1456,7 @@ func TestExportTxGasCost(t *testing.T) { DestinationChain: xChainID, Ins: []atomic.EVMInput{ { - Address: testEthAddrs[0], + Address: vmtest.TestEthAddrs[0], Amount: exportAmount, AssetID: avaxAssetID, Nonce: 0, @@ -1464,13 +1470,13 @@ func TestExportTxGasCost(t *testing.T) { OutputOwners: secp256k1fx.OutputOwners{ Locktime: 0, Threshold: 1, - Addrs: []ids.ShortID{testShortIDAddrs[0]}, + Addrs: []ids.ShortID{vmtest.TestShortIDAddrs[0]}, }, }, }, }, }, - Keys: [][]*secp256k1.PrivateKey{{testKeys[0]}}, + Keys: [][]*secp256k1.PrivateKey{{vmtest.TestKeys[0]}}, ExpectedGasUsed: 11230, ExpectedFee: 1, BaseFee: big.NewInt(1), @@ -1483,7 +1489,7 @@ func TestExportTxGasCost(t *testing.T) { DestinationChain: xChainID, Ins: []atomic.EVMInput{ { - Address: testEthAddrs[0], + Address: vmtest.TestEthAddrs[0], Amount: exportAmount, AssetID: avaxAssetID, Nonce: 0, @@ -1497,13 +1503,13 @@ func TestExportTxGasCost(t *testing.T) { OutputOwners: secp256k1fx.OutputOwners{ Locktime: 0, Threshold: 1, - Addrs: []ids.ShortID{testShortIDAddrs[0]}, + Addrs: []ids.ShortID{vmtest.TestShortIDAddrs[0]}, }, }, }, }, }, - Keys: [][]*secp256k1.PrivateKey{{testKeys[0]}}, + Keys: [][]*secp256k1.PrivateKey{{vmtest.TestKeys[0]}}, ExpectedGasUsed: 1230, ExpectedFee: 30750, BaseFee: big.NewInt(25 * utils.GWei), @@ -1515,7 +1521,7 @@ func TestExportTxGasCost(t *testing.T) { DestinationChain: xChainID, Ins: []atomic.EVMInput{ { - Address: testEthAddrs[0], + Address: vmtest.TestEthAddrs[0], Amount: exportAmount, AssetID: avaxAssetID, Nonce: 0, @@ -1529,13 +1535,13 @@ func TestExportTxGasCost(t *testing.T) { OutputOwners: secp256k1fx.OutputOwners{ Locktime: 0, Threshold: 1, - Addrs: []ids.ShortID{testShortIDAddrs[0]}, + Addrs: []ids.ShortID{vmtest.TestShortIDAddrs[0]}, }, }, }, }, }, - Keys: [][]*secp256k1.PrivateKey{{testKeys[0]}}, + Keys: [][]*secp256k1.PrivateKey{{vmtest.TestKeys[0]}}, ExpectedGasUsed: 1230, ExpectedFee: 276750, BaseFee: big.NewInt(225 * utils.GWei), @@ -1547,19 +1553,19 @@ func TestExportTxGasCost(t *testing.T) { DestinationChain: xChainID, Ins: []atomic.EVMInput{ { - Address: testEthAddrs[0], + Address: vmtest.TestEthAddrs[0], Amount: exportAmount, AssetID: avaxAssetID, Nonce: 0, }, { - Address: testEthAddrs[1], + Address: vmtest.TestEthAddrs[1], Amount: exportAmount, AssetID: avaxAssetID, Nonce: 0, }, { - Address: testEthAddrs[2], + Address: vmtest.TestEthAddrs[2], Amount: exportAmount, AssetID: avaxAssetID, Nonce: 0, @@ -1573,13 +1579,13 @@ func TestExportTxGasCost(t *testing.T) { OutputOwners: secp256k1fx.OutputOwners{ Locktime: 0, Threshold: 1, - Addrs: []ids.ShortID{testShortIDAddrs[0]}, + Addrs: []ids.ShortID{vmtest.TestShortIDAddrs[0]}, }, }, }, }, }, - Keys: [][]*secp256k1.PrivateKey{{testKeys[0], testKeys[0], testKeys[0]}}, + Keys: [][]*secp256k1.PrivateKey{{vmtest.TestKeys[0], vmtest.TestKeys[0], vmtest.TestKeys[0]}}, ExpectedGasUsed: 3366, ExpectedFee: 84150, BaseFee: big.NewInt(25 * utils.GWei), @@ -1591,19 +1597,19 @@ func TestExportTxGasCost(t *testing.T) { DestinationChain: xChainID, Ins: []atomic.EVMInput{ { - Address: testEthAddrs[0], + Address: vmtest.TestEthAddrs[0], Amount: exportAmount, AssetID: avaxAssetID, Nonce: 0, }, { - Address: testEthAddrs[1], + Address: vmtest.TestEthAddrs[1], Amount: exportAmount, AssetID: avaxAssetID, Nonce: 0, }, { - Address: testEthAddrs[2], + Address: vmtest.TestEthAddrs[2], Amount: exportAmount, AssetID: avaxAssetID, Nonce: 0, @@ -1617,13 +1623,13 @@ func TestExportTxGasCost(t *testing.T) { OutputOwners: secp256k1fx.OutputOwners{ Locktime: 0, Threshold: 1, - Addrs: []ids.ShortID{testShortIDAddrs[0]}, + Addrs: []ids.ShortID{vmtest.TestShortIDAddrs[0]}, }, }, }, }, }, - Keys: [][]*secp256k1.PrivateKey{{testKeys[0], testKeys[0], testKeys[0]}}, + Keys: [][]*secp256k1.PrivateKey{{vmtest.TestKeys[0], vmtest.TestKeys[0], vmtest.TestKeys[0]}}, ExpectedGasUsed: 3366, ExpectedFee: 757350, BaseFee: big.NewInt(225 * utils.GWei), @@ -1659,6 +1665,9 @@ func TestExportTxGasCost(t *testing.T) { } func TestNewExportTx(t *testing.T) { + key, err := secp256k1.NewPrivateKey() + require.NoError(t, err) + ethAddress := key.PublicKey().EthAddress() tests := []struct { fork upgradetest.Fork bal uint64 @@ -1697,27 +1706,28 @@ func TestNewExportTx(t *testing.T) { } for _, test := range tests { t.Run(test.fork.String(), func(t *testing.T) { - tvm := newVM(t, testVMConfig{ - fork: &test.fork, + vm := newAtomicTestVM() + tvm := vmtest.SetupTestVM(t, vm, vmtest.TestVMConfig{ + Fork: &test.fork, }) defer func() { - if err := tvm.vm.Shutdown(context.Background()); err != nil { + if err := vm.Shutdown(context.Background()); err != nil { t.Fatal(err) } }() - parent := tvm.vm.LastAcceptedExtendedBlock() + parent := vm.LastAcceptedExtendedBlock() importAmount := uint64(50000000) utxoID := avax.UTXOID{TxID: ids.GenerateTestID()} utxo := &avax.UTXO{ UTXOID: utxoID, - Asset: avax.Asset{ID: tvm.vm.ctx.AVAXAssetID}, + Asset: avax.Asset{ID: vm.Ctx.AVAXAssetID}, Out: &secp256k1fx.TransferOutput{ Amt: importAmount, OutputOwners: secp256k1fx.OutputOwners{ Threshold: 1, - Addrs: []ids.ShortID{testKeys[0].Address()}, + Addrs: []ids.ShortID{key.Address()}, }, }, } @@ -1726,30 +1736,32 @@ func TestNewExportTx(t *testing.T) { t.Fatal(err) } - xChainSharedMemory := tvm.atomicMemory.NewSharedMemory(tvm.vm.ctx.XChainID) + xChainSharedMemory := tvm.AtomicMemory.NewSharedMemory(vm.Ctx.XChainID) inputID := utxo.InputID() - if err := xChainSharedMemory.Apply(map[ids.ID]*avalancheatomic.Requests{tvm.vm.ctx.ChainID: {PutRequests: []*avalancheatomic.Element{{ + if err := xChainSharedMemory.Apply(map[ids.ID]*avalancheatomic.Requests{vm.Ctx.ChainID: {PutRequests: []*avalancheatomic.Element{{ Key: inputID[:], Value: utxoBytes, Traits: [][]byte{ - testKeys[0].Address().Bytes(), + key.Address().Bytes(), }, }}}}); err != nil { t.Fatal(err) } - tx, err := tvm.atomicVM.NewImportTx(tvm.vm.ctx.XChainID, testEthAddrs[0], initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) + tx, err := vm.newImportTx(vm.Ctx.XChainID, ethAddress, vmtest.InitialBaseFee, []*secp256k1.PrivateKey{key}) if err != nil { t.Fatal(err) } - if err := tvm.atomicVM.AtomicMempool.AddLocalTx(tx); err != nil { + if err := vm.AtomicMempool.AddLocalTx(tx); err != nil { t.Fatal(err) } - require.Equal(t, commonEng.PendingTxs, tvm.WaitForEvent(context.Background())) + msg, err := tvm.VM.WaitForEvent(context.Background()) + require.NoError(t, err) + require.Equal(t, commonEng.PendingTxs, msg) - blk, err := tvm.vm.BuildBlock(context.Background()) + blk, err := vm.BuildBlock(context.Background()) if err != nil { t.Fatal(err) } @@ -1758,7 +1770,7 @@ func TestNewExportTx(t *testing.T) { t.Fatal(err) } - if err := tvm.vm.SetPreference(context.Background(), blk.ID()); err != nil { + if err := vm.SetPreference(context.Background(), blk.ID()); err != nil { t.Fatal(err) } @@ -1766,29 +1778,28 @@ func TestNewExportTx(t *testing.T) { t.Fatal(err) } - parent = tvm.vm.LastAcceptedExtendedBlock() + parent = vm.LastAcceptedExtendedBlock() exportAmount := uint64(5000000) - statedb, err := tvm.vm.blockChain.State() + statedb, err := vm.Ethereum().BlockChain().State() if err != nil { t.Fatal(err) } wrappedStateDB := extstate.New(statedb) - tx, err = atomic.NewExportTx(tvm.vm.ctx, tvm.vm.currentRules(), wrappedStateDB, tvm.vm.ctx.AVAXAssetID, exportAmount, tvm.vm.ctx.XChainID, testShortIDAddrs[0], initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) + tx, err = atomic.NewExportTx(vm.Ctx, vm.CurrentRules(), wrappedStateDB, vm.Ctx.AVAXAssetID, exportAmount, vm.Ctx.XChainID, key.Address(), vmtest.InitialBaseFee, []*secp256k1.PrivateKey{key}) if err != nil { t.Fatal(err) } exportTx := tx.UnsignedAtomicTx - backend := atomicvm.NewVerifierBackend(tvm.atomicVM, tvm.vm.currentRules()) - + backend := NewVerifierBackend(vm, vm.CurrentRules()) if err := backend.SemanticVerify(tx, parent, parent.GetEthBlock().BaseFee()); err != nil { t.Fatal("newExportTx created an invalid transaction", err) } - burnedAVAX, err := exportTx.Burned(tvm.vm.ctx.AVAXAssetID) + burnedAVAX, err := exportTx.Burned(vm.Ctx.AVAXAssetID) if err != nil { t.Fatal(err) } @@ -1796,7 +1807,7 @@ func TestNewExportTx(t *testing.T) { t.Fatalf("burned wrong amount of AVAX - expected %d burned %d", test.expectedBurnedAVAX, burnedAVAX) } - commitBatch, err := tvm.vm.versiondb.CommitBatch() + commitBatch, err := vm.VersionDB().CommitBatch() if err != nil { t.Fatalf("Failed to create commit batch for VM due to %s", err) } @@ -1805,29 +1816,31 @@ func TestNewExportTx(t *testing.T) { t.Fatalf("Failed to accept export transaction due to: %s", err) } - if err := tvm.vm.ctx.SharedMemory.Apply(map[ids.ID]*avalancheatomic.Requests{chainID: atomicRequests}, commitBatch); err != nil { + if err := vm.Ctx.SharedMemory.Apply(map[ids.ID]*avalancheatomic.Requests{chainID: atomicRequests}, commitBatch); err != nil { t.Fatal(err) } - statedb, err = tvm.vm.blockChain.State() + statedb, err = vm.Ethereum().BlockChain().State() if err != nil { t.Fatal(err) } wrappedStateDB = extstate.New(statedb) - err = exportTx.EVMStateTransfer(tvm.vm.ctx, wrappedStateDB) + err = exportTx.EVMStateTransfer(vm.Ctx, wrappedStateDB) if err != nil { t.Fatal(err) } - addr := testKeys[0].EthAddress() - if wrappedStateDB.GetBalance(addr).Cmp(uint256.NewInt(test.bal*units.Avax)) != 0 { - t.Fatalf("address balance %s equal %s not %s", addr.String(), wrappedStateDB.GetBalance(addr), new(big.Int).SetUint64(test.bal*units.Avax)) + if wrappedStateDB.GetBalance(ethAddress).Cmp(uint256.NewInt(test.bal*units.Avax)) != 0 { + t.Fatalf("address balance %s equal %s not %s", ethAddress.String(), wrappedStateDB.GetBalance(ethAddress), new(big.Int).SetUint64(test.bal*units.Avax)) } }) } } func TestNewExportTxMulticoin(t *testing.T) { + key, err := secp256k1.NewPrivateKey() + require.NoError(t, err) + ethAddress := key.PublicKey().EthAddress() tests := []struct { fork upgradetest.Fork bal uint64 @@ -1856,27 +1869,28 @@ func TestNewExportTxMulticoin(t *testing.T) { } for _, test := range tests { t.Run(test.fork.String(), func(t *testing.T) { - tvm := newVM(t, testVMConfig{ - fork: &test.fork, + vm := newAtomicTestVM() + tvm := vmtest.SetupTestVM(t, vm, vmtest.TestVMConfig{ + Fork: &test.fork, }) defer func() { - if err := tvm.vm.Shutdown(context.Background()); err != nil { + if err := vm.Shutdown(context.Background()); err != nil { t.Fatal(err) } }() - parent := tvm.vm.LastAcceptedExtendedBlock() + parent := vm.LastAcceptedExtendedBlock() importAmount := uint64(50000000) utxoID := avax.UTXOID{TxID: ids.GenerateTestID()} utxo := &avax.UTXO{ UTXOID: utxoID, - Asset: avax.Asset{ID: tvm.vm.ctx.AVAXAssetID}, + Asset: avax.Asset{ID: vm.Ctx.AVAXAssetID}, Out: &secp256k1fx.TransferOutput{ Amt: importAmount, OutputOwners: secp256k1fx.OutputOwners{ Threshold: 1, - Addrs: []ids.ShortID{testKeys[0].Address()}, + Addrs: []ids.ShortID{key.Address()}, }, }, } @@ -1897,7 +1911,7 @@ func TestNewExportTxMulticoin(t *testing.T) { Amt: importAmount2, OutputOwners: secp256k1fx.OutputOwners{ Threshold: 1, - Addrs: []ids.ShortID{testKeys[0].Address()}, + Addrs: []ids.ShortID{key.Address()}, }, }, } @@ -1906,39 +1920,41 @@ func TestNewExportTxMulticoin(t *testing.T) { t.Fatal(err) } - xChainSharedMemory := tvm.atomicMemory.NewSharedMemory(tvm.vm.ctx.XChainID) + xChainSharedMemory := tvm.AtomicMemory.NewSharedMemory(vm.Ctx.XChainID) inputID2 := utxo2.InputID() - if err := xChainSharedMemory.Apply(map[ids.ID]*avalancheatomic.Requests{tvm.vm.ctx.ChainID: {PutRequests: []*avalancheatomic.Element{ + if err := xChainSharedMemory.Apply(map[ids.ID]*avalancheatomic.Requests{vm.Ctx.ChainID: {PutRequests: []*avalancheatomic.Element{ { Key: inputID[:], Value: utxoBytes, Traits: [][]byte{ - testKeys[0].Address().Bytes(), + key.Address().Bytes(), }, }, { Key: inputID2[:], Value: utxoBytes2, Traits: [][]byte{ - testKeys[0].Address().Bytes(), + key.Address().Bytes(), }, }, }}}); err != nil { t.Fatal(err) } - tx, err := tvm.atomicVM.NewImportTx(tvm.vm.ctx.XChainID, testEthAddrs[0], initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) + tx, err := vm.newImportTx(vm.Ctx.XChainID, ethAddress, vmtest.InitialBaseFee, []*secp256k1.PrivateKey{key}) if err != nil { t.Fatal(err) } - if err := tvm.atomicVM.AtomicMempool.AddRemoteTx(tx); err != nil { + if err := vm.AtomicMempool.AddRemoteTx(tx); err != nil { t.Fatal(err) } - require.Equal(t, commonEng.PendingTxs, tvm.WaitForEvent(context.Background())) + msg, err := tvm.VM.WaitForEvent(context.Background()) + require.NoError(t, err) + require.Equal(t, commonEng.PendingTxs, msg) - blk, err := tvm.vm.BuildBlock(context.Background()) + blk, err := vm.BuildBlock(context.Background()) if err != nil { t.Fatal(err) } @@ -1947,7 +1963,7 @@ func TestNewExportTxMulticoin(t *testing.T) { t.Fatal(err) } - if err := tvm.vm.SetPreference(context.Background(), blk.ID()); err != nil { + if err := vm.SetPreference(context.Background(), blk.ID()); err != nil { t.Fatal(err) } @@ -1955,34 +1971,34 @@ func TestNewExportTxMulticoin(t *testing.T) { t.Fatal(err) } - parent = tvm.vm.LastAcceptedExtendedBlock() + parent = vm.LastAcceptedExtendedBlock() exportAmount := uint64(5000000) - testKeys0Addr := testKeys[0].EthAddress() + testKeys0Addr := vmtest.TestKeys[0].EthAddress() exportId, err := ids.ToShortID(testKeys0Addr[:]) if err != nil { t.Fatal(err) } - statedb, err := tvm.vm.blockChain.State() + statedb, err := vm.Ethereum().BlockChain().State() if err != nil { t.Fatal(err) } wrappedStateDB := extstate.New(statedb) - tx, err = atomic.NewExportTx(tvm.vm.ctx, tvm.vm.currentRules(), wrappedStateDB, tid, exportAmount, tvm.vm.ctx.XChainID, exportId, initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) + tx, err = atomic.NewExportTx(vm.Ctx, vm.CurrentRules(), wrappedStateDB, tid, exportAmount, vm.Ctx.XChainID, exportId, vmtest.InitialBaseFee, []*secp256k1.PrivateKey{key}) if err != nil { t.Fatal(err) } exportTx := tx.UnsignedAtomicTx - backend := atomicvm.NewVerifierBackend(tvm.atomicVM, tvm.vm.currentRules()) + backend := NewVerifierBackend(vm, vm.CurrentRules()) if err := backend.SemanticVerify(tx, parent, parent.GetEthBlock().BaseFee()); err != nil { t.Fatal("newExportTx created an invalid transaction", err) } - commitBatch, err := tvm.vm.versiondb.CommitBatch() + commitBatch, err := vm.VersionDB().CommitBatch() if err != nil { t.Fatalf("Failed to create commit batch for VM due to %s", err) } @@ -1991,26 +2007,25 @@ func TestNewExportTxMulticoin(t *testing.T) { t.Fatalf("Failed to accept export transaction due to: %s", err) } - if err := tvm.vm.ctx.SharedMemory.Apply(map[ids.ID]*avalancheatomic.Requests{chainID: atomicRequests}, commitBatch); err != nil { + if err := vm.Ctx.SharedMemory.Apply(map[ids.ID]*avalancheatomic.Requests{chainID: atomicRequests}, commitBatch); err != nil { t.Fatal(err) } - statedb, err = tvm.vm.blockChain.State() + statedb, err = vm.Ethereum().BlockChain().State() if err != nil { t.Fatal(err) } wrappedStateDB = extstate.New(statedb) - err = exportTx.EVMStateTransfer(tvm.vm.ctx, wrappedStateDB) + err = exportTx.EVMStateTransfer(vm.Ctx, wrappedStateDB) if err != nil { t.Fatal(err) } - addr := testKeys[0].EthAddress() - if wrappedStateDB.GetBalance(addr).Cmp(uint256.NewInt(test.bal*units.Avax)) != 0 { - t.Fatalf("address balance %s equal %s not %s", addr.String(), wrappedStateDB.GetBalance(addr), new(big.Int).SetUint64(test.bal*units.Avax)) + if wrappedStateDB.GetBalance(ethAddress).Cmp(uint256.NewInt(test.bal*units.Avax)) != 0 { + t.Fatalf("address balance %s equal %s not %s", ethAddress.String(), wrappedStateDB.GetBalance(ethAddress), new(big.Int).SetUint64(test.bal*units.Avax)) } - if wrappedStateDB.GetBalanceMultiCoin(addr, common.BytesToHash(tid[:])).Cmp(new(big.Int).SetUint64(test.balmc)) != 0 { - t.Fatalf("address balance multicoin %s equal %s not %s", addr.String(), wrappedStateDB.GetBalanceMultiCoin(addr, common.BytesToHash(tid[:])), new(big.Int).SetUint64(test.balmc)) + if wrappedStateDB.GetBalanceMultiCoin(ethAddress, common.BytesToHash(tid[:])).Cmp(new(big.Int).SetUint64(test.balmc)) != 0 { + t.Fatalf("address balance multicoin %s equal %s not %s", ethAddress.String(), wrappedStateDB.GetBalanceMultiCoin(ethAddress, common.BytesToHash(tid[:])), new(big.Int).SetUint64(test.balmc)) } }) } diff --git a/plugin/evm/gossiper_atomic_gossiping_test.go b/plugin/evm/atomic/vm/gossiper_atomic_gossiping_test.go similarity index 69% rename from plugin/evm/gossiper_atomic_gossiping_test.go rename to plugin/evm/atomic/vm/gossiper_atomic_gossiping_test.go index 6a8c4c856a..99aa9375b6 100644 --- a/plugin/evm/gossiper_atomic_gossiping_test.go +++ b/plugin/evm/atomic/vm/gossiper_atomic_gossiping_test.go @@ -1,7 +1,7 @@ // Copyright (C) 2019-2025, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. -package evm +package vm import ( "context" @@ -10,8 +10,6 @@ import ( "testing" "time" - "github.com/ava-labs/coreth/plugin/evm/atomic" - "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/network/p2p" "github.com/ava-labs/avalanchego/proto/pb/sdk" @@ -20,6 +18,9 @@ import ( "google.golang.org/protobuf/proto" commonEng "github.com/ava-labs/avalanchego/snow/engine/common" + + "github.com/ava-labs/coreth/plugin/evm/atomic" + "github.com/ava-labs/coreth/plugin/evm/vmtest" ) // show that a txID discovered from gossip is requested to the same node only if @@ -27,9 +28,10 @@ import ( func TestMempoolAtmTxsAppGossipHandling(t *testing.T) { assert := assert.New(t) - tvm := newVM(t, testVMConfig{}) + vm := newAtomicTestVM() + tvm := vmtest.SetupTestVM(t, vm, vmtest.TestVMConfig{}) defer func() { - assert.NoError(tvm.vm.Shutdown(context.Background())) + assert.NoError(vm.Shutdown(context.Background())) }() nodeID := ids.GenerateTestNodeID() @@ -39,50 +41,50 @@ func TestMempoolAtmTxsAppGossipHandling(t *testing.T) { txGossipedLock sync.Mutex txRequested bool ) - tvm.appSender.CantSendAppGossip = false - tvm.appSender.SendAppGossipF = func(context.Context, commonEng.SendConfig, []byte) error { + tvm.AppSender.CantSendAppGossip = false + tvm.AppSender.SendAppGossipF = func(context.Context, commonEng.SendConfig, []byte) error { txGossipedLock.Lock() defer txGossipedLock.Unlock() txGossiped++ return nil } - tvm.appSender.SendAppRequestF = func(context.Context, set.Set[ids.NodeID], uint32, []byte) error { + tvm.AppSender.SendAppRequestF = func(context.Context, set.Set[ids.NodeID], uint32, []byte) error { txRequested = true return nil } // Create conflicting transactions - importTxs := createImportTxOptions(t, tvm.atomicVM, tvm.atomicMemory) + importTxs := createImportTxOptions(t, vm, tvm.AtomicMemory) tx, conflictingTx := importTxs[0], importTxs[1] // gossip tx and check it is accepted and gossiped marshaller := atomic.TxMarshaller{} txBytes, err := marshaller.MarshalGossip(tx) assert.NoError(err) - tvm.vm.ctx.Lock.Unlock() + vm.Ctx.Lock.Unlock() msgBytes, err := buildAtomicPushGossip(txBytes) assert.NoError(err) // show that no txID is requested - assert.NoError(tvm.vm.AppGossip(context.Background(), nodeID, msgBytes)) + assert.NoError(vm.AppGossip(context.Background(), nodeID, msgBytes)) time.Sleep(500 * time.Millisecond) - tvm.vm.ctx.Lock.Lock() + vm.Ctx.Lock.Lock() assert.False(txRequested, "tx should not have been requested") txGossipedLock.Lock() assert.Equal(0, txGossiped, "tx should not have been gossiped") txGossipedLock.Unlock() - assert.True(tvm.atomicVM.AtomicMempool.Has(tx.ID())) + assert.True(vm.AtomicMempool.Has(tx.ID())) - tvm.vm.ctx.Lock.Unlock() + vm.Ctx.Lock.Unlock() // show that tx is not re-gossiped - assert.NoError(tvm.vm.AppGossip(context.Background(), nodeID, msgBytes)) + assert.NoError(vm.AppGossip(context.Background(), nodeID, msgBytes)) - tvm.vm.ctx.Lock.Lock() + vm.Ctx.Lock.Lock() txGossipedLock.Lock() assert.Equal(0, txGossiped, "tx should not have been gossiped") @@ -93,60 +95,60 @@ func TestMempoolAtmTxsAppGossipHandling(t *testing.T) { txBytes, err = marshaller.MarshalGossip(conflictingTx) assert.NoError(err) - tvm.vm.ctx.Lock.Unlock() + vm.Ctx.Lock.Unlock() msgBytes, err = buildAtomicPushGossip(txBytes) assert.NoError(err) - assert.NoError(tvm.vm.AppGossip(context.Background(), nodeID, msgBytes)) + assert.NoError(vm.AppGossip(context.Background(), nodeID, msgBytes)) - tvm.vm.ctx.Lock.Lock() + vm.Ctx.Lock.Lock() assert.False(txRequested, "tx should not have been requested") txGossipedLock.Lock() assert.Equal(0, txGossiped, "tx should not have been gossiped") txGossipedLock.Unlock() - assert.False(tvm.atomicVM.AtomicMempool.Has(conflictingTx.ID()), "conflicting tx should not be in the atomic mempool") + assert.False(vm.AtomicMempool.Has(conflictingTx.ID()), "conflicting tx should not be in the atomic mempool") } // show that txs already marked as invalid are not re-requested on gossiping func TestMempoolAtmTxsAppGossipHandlingDiscardedTx(t *testing.T) { assert := assert.New(t) - tvm := newVM(t, testVMConfig{}) + vm := newAtomicTestVM() + tvm := vmtest.SetupTestVM(t, vm, vmtest.TestVMConfig{}) defer func() { - assert.NoError(tvm.vm.Shutdown(context.Background())) + assert.NoError(vm.Shutdown(context.Background())) }() - mempool := tvm.atomicVM.AtomicMempool var ( txGossiped int txGossipedLock sync.Mutex txRequested bool ) - tvm.appSender.CantSendAppGossip = false - tvm.appSender.SendAppGossipF = func(context.Context, commonEng.SendConfig, []byte) error { + tvm.AppSender.CantSendAppGossip = false + tvm.AppSender.SendAppGossipF = func(context.Context, commonEng.SendConfig, []byte) error { txGossipedLock.Lock() defer txGossipedLock.Unlock() txGossiped++ return nil } - tvm.appSender.SendAppRequestF = func(context.Context, set.Set[ids.NodeID], uint32, []byte) error { + tvm.AppSender.SendAppRequestF = func(context.Context, set.Set[ids.NodeID], uint32, []byte) error { txRequested = true return nil } // Create a transaction and mark it as invalid by discarding it - importTxs := createImportTxOptions(t, tvm.atomicVM, tvm.atomicMemory) + importTxs := createImportTxOptions(t, vm, tvm.AtomicMemory) tx, conflictingTx := importTxs[0], importTxs[1] txID := tx.ID() - mempool.AddRemoteTx(tx) - mempool.NextTx() - mempool.DiscardCurrentTx(txID) + vm.AtomicMempool.AddRemoteTx(tx) + vm.AtomicMempool.NextTx() + vm.AtomicMempool.DiscardCurrentTx(txID) // Check the mempool does not contain the discarded transaction - assert.False(mempool.Has(txID)) + assert.False(vm.AtomicMempool.Has(txID)) // Gossip the transaction to the VM and ensure that it is not added to the mempool // and is not re-gossipped. @@ -155,39 +157,39 @@ func TestMempoolAtmTxsAppGossipHandlingDiscardedTx(t *testing.T) { txBytes, err := marshaller.MarshalGossip(tx) assert.NoError(err) - tvm.vm.ctx.Lock.Unlock() + vm.Ctx.Lock.Unlock() msgBytes, err := buildAtomicPushGossip(txBytes) assert.NoError(err) - assert.NoError(tvm.vm.AppGossip(context.Background(), nodeID, msgBytes)) + assert.NoError(vm.AppGossip(context.Background(), nodeID, msgBytes)) - tvm.vm.ctx.Lock.Lock() + vm.Ctx.Lock.Lock() assert.False(txRequested, "tx shouldn't be requested") txGossipedLock.Lock() assert.Zero(txGossiped, "tx should not have been gossiped") txGossipedLock.Unlock() - assert.False(mempool.Has(txID)) + assert.False(vm.AtomicMempool.Has(txID)) - tvm.vm.ctx.Lock.Unlock() + vm.Ctx.Lock.Unlock() // Conflicting tx must be submitted over the API to be included in push gossip. // (i.e., txs received via p2p are not included in push gossip) // This test adds it directly to the mempool + gossiper to simulate that. - tvm.atomicVM.AtomicMempool.AddRemoteTx(conflictingTx) - tvm.atomicVM.AtomicTxPushGossiper.Add(conflictingTx) + vm.AtomicMempool.AddRemoteTx(conflictingTx) + vm.AtomicTxPushGossiper.Add(conflictingTx) time.Sleep(500 * time.Millisecond) - tvm.vm.ctx.Lock.Lock() + vm.Ctx.Lock.Lock() assert.False(txRequested, "tx shouldn't be requested") txGossipedLock.Lock() assert.Equal(1, txGossiped, "conflicting tx should have been gossiped") txGossipedLock.Unlock() - assert.False(mempool.Has(txID)) - assert.True(mempool.Has(conflictingTx.ID())) + assert.False(vm.AtomicMempool.Has(txID)) + assert.True(vm.AtomicMempool.Has(conflictingTx.ID())) } func buildAtomicPushGossip(txBytes []byte) ([]byte, error) { diff --git a/plugin/evm/import_tx_test.go b/plugin/evm/atomic/vm/import_tx_test.go similarity index 75% rename from plugin/evm/import_tx_test.go rename to plugin/evm/atomic/vm/import_tx_test.go index a197e2af12..f6ca03d856 100644 --- a/plugin/evm/import_tx_test.go +++ b/plugin/evm/atomic/vm/import_tx_test.go @@ -1,22 +1,26 @@ // Copyright (C) 2019-2025, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. -package evm +package vm import ( + "context" "math/big" + "strings" "testing" "github.com/ava-labs/coreth/core/extstate" "github.com/ava-labs/coreth/plugin/evm/atomic" - "github.com/ava-labs/coreth/plugin/evm/atomic/vm" "github.com/ava-labs/coreth/plugin/evm/upgrade/ap0" + "github.com/ava-labs/coreth/plugin/evm/vmtest" "github.com/ava-labs/coreth/utils" "github.com/ava-labs/libevm/common" "github.com/holiman/uint256" + "github.com/stretchr/testify/require" avalancheatomic "github.com/ava-labs/avalanchego/chains/atomic" "github.com/ava-labs/avalanchego/ids" + commonEng "github.com/ava-labs/avalanchego/snow/engine/common" "github.com/ava-labs/avalanchego/snow/snowtest" "github.com/ava-labs/avalanchego/upgrade/upgradetest" avalancheutils "github.com/ava-labs/avalanchego/utils" @@ -29,7 +33,7 @@ import ( // createImportTxOptions adds a UTXO to shared memory and generates a list of import transactions sending this UTXO // to each of the three test keys (conflicting transactions) -func createImportTxOptions(t *testing.T, vm *vm.VM, sharedMemory *avalancheatomic.Memory) []*atomic.Tx { +func createImportTxOptions(t *testing.T, vm *VM, sharedMemory *avalancheatomic.Memory) []*atomic.Tx { utxo := &avax.UTXO{ UTXOID: avax.UTXOID{TxID: ids.GenerateTestID()}, Asset: avax.Asset{ID: vm.Ctx.AVAXAssetID}, @@ -37,7 +41,7 @@ func createImportTxOptions(t *testing.T, vm *vm.VM, sharedMemory *avalancheatomi Amt: uint64(50000000), OutputOwners: secp256k1fx.OutputOwners{ Threshold: 1, - Addrs: []ids.ShortID{testKeys[0].Address()}, + Addrs: []ids.ShortID{vmtest.TestKeys[0].Address()}, }, }, } @@ -52,15 +56,15 @@ func createImportTxOptions(t *testing.T, vm *vm.VM, sharedMemory *avalancheatomi Key: inputID[:], Value: utxoBytes, Traits: [][]byte{ - testKeys[0].Address().Bytes(), + vmtest.TestKeys[0].Address().Bytes(), }, }}}}); err != nil { t.Fatal(err) } importTxs := make([]*atomic.Tx, 0, 3) - for _, ethAddr := range testEthAddrs { - importTx, err := vm.NewImportTx(vm.Ctx.XChainID, ethAddr, initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) + for _, ethAddr := range vmtest.TestEthAddrs { + importTx, err := vm.newImportTx(vm.Ctx.XChainID, ethAddr, vmtest.InitialBaseFee, []*secp256k1.PrivateKey{vmtest.TestKeys[0]}) if err != nil { t.Fatal(err) } @@ -109,12 +113,12 @@ func TestImportTxVerify(t *testing.T) { }, Outs: []atomic.EVMOutput{ { - Address: testEthAddrs[0], + Address: vmtest.TestEthAddrs[0], Amount: importAmount - ap0.AtomicTxFee, AssetID: ctx.AVAXAssetID, }, { - Address: testEthAddrs[1], + Address: vmtest.TestEthAddrs[1], Amount: importAmount, AssetID: ctx.AVAXAssetID, }, @@ -132,7 +136,7 @@ func TestImportTxVerify(t *testing.T) { return importTx }, ctx: ctx, - rules: apricotRulesPhase0, + rules: vmtest.ForkToRules(upgradetest.NoUpgrades), expectedErr: atomic.ErrNilTx.Error(), }, "valid import tx": { @@ -140,7 +144,7 @@ func TestImportTxVerify(t *testing.T) { return importTx }, ctx: ctx, - rules: apricotRulesPhase0, + rules: vmtest.ForkToRules(upgradetest.NoUpgrades), expectedErr: "", // Expect this transaction to be valid in Apricot Phase 0 }, "valid import tx banff": { @@ -148,7 +152,7 @@ func TestImportTxVerify(t *testing.T) { return importTx }, ctx: ctx, - rules: banffRules, + rules: vmtest.ForkToRules(upgradetest.Banff), expectedErr: "", // Expect this transaction to be valid in Banff }, "invalid network ID": { @@ -158,7 +162,7 @@ func TestImportTxVerify(t *testing.T) { return &tx }, ctx: ctx, - rules: apricotRulesPhase0, + rules: vmtest.ForkToRules(upgradetest.NoUpgrades), expectedErr: atomic.ErrWrongNetworkID.Error(), }, "invalid blockchain ID": { @@ -168,7 +172,7 @@ func TestImportTxVerify(t *testing.T) { return &tx }, ctx: ctx, - rules: apricotRulesPhase0, + rules: vmtest.ForkToRules(upgradetest.NoUpgrades), expectedErr: atomic.ErrWrongChainID.Error(), }, "P-chain source before AP5": { @@ -178,7 +182,7 @@ func TestImportTxVerify(t *testing.T) { return &tx }, ctx: ctx, - rules: apricotRulesPhase0, + rules: vmtest.ForkToRules(upgradetest.NoUpgrades), expectedErr: atomic.ErrWrongChainID.Error(), }, "P-chain source after AP5": { @@ -188,7 +192,7 @@ func TestImportTxVerify(t *testing.T) { return &tx }, ctx: ctx, - rules: apricotRulesPhase5, + rules: vmtest.ForkToRules(upgradetest.ApricotPhase5), }, "invalid source chain ID": { generate: func(t *testing.T) atomic.UnsignedAtomicTx { @@ -197,7 +201,7 @@ func TestImportTxVerify(t *testing.T) { return &tx }, ctx: ctx, - rules: apricotRulesPhase5, + rules: vmtest.ForkToRules(upgradetest.ApricotPhase5), expectedErr: atomic.ErrWrongChainID.Error(), }, "no inputs": { @@ -207,7 +211,7 @@ func TestImportTxVerify(t *testing.T) { return &tx }, ctx: ctx, - rules: apricotRulesPhase0, + rules: vmtest.ForkToRules(upgradetest.NoUpgrades), expectedErr: atomic.ErrNoImportInputs.Error(), }, "inputs sorted incorrectly": { @@ -220,7 +224,7 @@ func TestImportTxVerify(t *testing.T) { return &tx }, ctx: ctx, - rules: apricotRulesPhase0, + rules: vmtest.ForkToRules(upgradetest.NoUpgrades), expectedErr: atomic.ErrInputsNotSortedUnique.Error(), }, "invalid input": { @@ -233,7 +237,7 @@ func TestImportTxVerify(t *testing.T) { return &tx }, ctx: ctx, - rules: apricotRulesPhase0, + rules: vmtest.ForkToRules(upgradetest.NoUpgrades), expectedErr: "atomic input failed verification", }, "unsorted outputs phase 0 passes verification": { @@ -246,7 +250,7 @@ func TestImportTxVerify(t *testing.T) { return &tx }, ctx: ctx, - rules: apricotRulesPhase0, + rules: vmtest.ForkToRules(upgradetest.NoUpgrades), expectedErr: "", }, "non-unique outputs phase 0 passes verification": { @@ -259,7 +263,7 @@ func TestImportTxVerify(t *testing.T) { return &tx }, ctx: ctx, - rules: apricotRulesPhase0, + rules: vmtest.ForkToRules(upgradetest.NoUpgrades), expectedErr: "", }, "unsorted outputs phase 1 fails verification": { @@ -272,7 +276,7 @@ func TestImportTxVerify(t *testing.T) { return &tx }, ctx: ctx, - rules: apricotRulesPhase1, + rules: vmtest.ForkToRules(upgradetest.ApricotPhase1), expectedErr: atomic.ErrOutputsNotSorted.Error(), }, "non-unique outputs phase 1 passes verification": { @@ -285,7 +289,7 @@ func TestImportTxVerify(t *testing.T) { return &tx }, ctx: ctx, - rules: apricotRulesPhase1, + rules: vmtest.ForkToRules(upgradetest.ApricotPhase1), expectedErr: "", }, "outputs not sorted and unique phase 2 fails verification": { @@ -298,7 +302,7 @@ func TestImportTxVerify(t *testing.T) { return &tx }, ctx: ctx, - rules: apricotRulesPhase2, + rules: vmtest.ForkToRules(upgradetest.ApricotPhase2), expectedErr: atomic.ErrOutputsNotSortedUnique.Error(), }, "outputs not sorted phase 2 fails verification": { @@ -311,7 +315,7 @@ func TestImportTxVerify(t *testing.T) { return &tx }, ctx: ctx, - rules: apricotRulesPhase2, + rules: vmtest.ForkToRules(upgradetest.ApricotPhase2), expectedErr: atomic.ErrOutputsNotSortedUnique.Error(), }, "invalid EVMOutput fails verification": { @@ -319,7 +323,7 @@ func TestImportTxVerify(t *testing.T) { tx := *importTx tx.Outs = []atomic.EVMOutput{ { - Address: testEthAddrs[0], + Address: vmtest.TestEthAddrs[0], Amount: 0, AssetID: snowtest.AVAXAssetID, }, @@ -327,7 +331,7 @@ func TestImportTxVerify(t *testing.T) { return &tx }, ctx: ctx, - rules: apricotRulesPhase0, + rules: vmtest.ForkToRules(upgradetest.NoUpgrades), expectedErr: "EVM Output failed verification", }, "no outputs apricot phase 3": { @@ -337,7 +341,7 @@ func TestImportTxVerify(t *testing.T) { return &tx }, ctx: ctx, - rules: apricotRulesPhase3, + rules: vmtest.ForkToRules(upgradetest.ApricotPhase3), expectedErr: atomic.ErrNoEVMOutputs.Error(), }, "non-AVAX input Apricot Phase 6": { @@ -361,7 +365,7 @@ func TestImportTxVerify(t *testing.T) { return &tx }, ctx: ctx, - rules: apricotRulesPhase6, + rules: vmtest.ForkToRules(upgradetest.ApricotPhase6), expectedErr: "", }, "non-AVAX output Apricot Phase 6": { @@ -377,7 +381,7 @@ func TestImportTxVerify(t *testing.T) { return &tx }, ctx: ctx, - rules: apricotRulesPhase6, + rules: vmtest.ForkToRules(upgradetest.ApricotPhase6), expectedErr: "", }, "non-AVAX input Banff": { @@ -401,7 +405,7 @@ func TestImportTxVerify(t *testing.T) { return &tx }, ctx: ctx, - rules: banffRules, + rules: vmtest.ForkToRules(upgradetest.Banff), expectedErr: atomic.ErrImportNonAVAXInputBanff.Error(), }, "non-AVAX output Banff": { @@ -417,7 +421,7 @@ func TestImportTxVerify(t *testing.T) { return &tx }, ctx: ctx, - rules: banffRules, + rules: vmtest.ForkToRules(upgradetest.Banff), expectedErr: atomic.ErrImportNonAVAXOutputBanff.Error(), }, } @@ -430,16 +434,19 @@ func TestImportTxVerify(t *testing.T) { func TestNewImportTx(t *testing.T) { importAmount := uint64(5000000) + key, err := secp256k1.NewPrivateKey() + require.NoError(t, err) + ethAddress := key.EthAddress() // createNewImportAVAXTx adds a UTXO to shared memory and then constructs a new import transaction // and checks that it has the correct fee for the base fee that has been used - createNewImportAVAXTx := func(t *testing.T, vm *vm.VM, sharedMemory *avalancheatomic.Memory) *atomic.Tx { + createNewImportAVAXTx := func(t *testing.T, vm *VM, sharedMemory *avalancheatomic.Memory) *atomic.Tx { txID := ids.GenerateTestID() - _, err := addUTXO(sharedMemory, vm.Ctx, txID, 0, vm.Ctx.AVAXAssetID, importAmount, testShortIDAddrs[0]) + _, err := addUTXO(sharedMemory, vm.Ctx, txID, 0, vm.Ctx.AVAXAssetID, importAmount, key.Address()) if err != nil { t.Fatal(err) } - tx, err := vm.NewImportTx(vm.Ctx.XChainID, testEthAddrs[0], initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) + tx, err := vm.newImportTx(vm.Ctx.XChainID, ethAddress, vmtest.InitialBaseFee, []*secp256k1.PrivateKey{key}) if err != nil { t.Fatal(err) } @@ -456,7 +463,7 @@ func TestNewImportTx(t *testing.T) { if err != nil { t.Fatal(err) } - actualFee, err = atomic.CalculateDynamicFee(actualCost, initialBaseFee) + actualFee, err = atomic.CalculateDynamicFee(actualCost, vmtest.InitialBaseFee) if err != nil { t.Fatal(err) } @@ -472,8 +479,11 @@ func TestNewImportTx(t *testing.T) { return tx } - checkState := func(t *testing.T, vm *vm.VM) { - txs := vm.LastAcceptedExtendedBlock().GetBlockExtension().(atomic.AtomicBlockContext).AtomicTxs() + checkState := func(t *testing.T, vm *VM) { + blk := vm.LastAcceptedExtendedBlock() + blockExtension, ok := blk.GetBlockExtension().(atomic.AtomicBlockContext) + require.True(t, ok) + txs := blockExtension.AtomicTxs() if len(txs) != 1 { t.Fatalf("Expected one import tx to be in the last accepted block, but found %d", len(txs)) } @@ -486,8 +496,8 @@ func TestNewImportTx(t *testing.T) { // Ensure that the UTXO has been removed from shared memory within Accept addrSet := set.Set[ids.ShortID]{} - addrSet.Add(testShortIDAddrs[0]) - utxos, _, _, err := avax.GetAtomicUTXOs(vm.Ctx.SharedMemory, atomic.Codec, vm.Ctx.XChainID, addrSet, ids.ShortEmpty, ids.Empty, 100) + addrSet.Add(key.Address()) + utxos, _, _, err := avax.GetAtomicUTXOs(vm.Ctx.SharedMemory, atomic.Codec, vm.Ctx.XChainID, addrSet, ids.ShortEmpty, ids.Empty, maxUTXOsToFetch) if err != nil { t.Fatal(err) } @@ -496,16 +506,15 @@ func TestNewImportTx(t *testing.T) { } // Ensure that the call to EVMStateTransfer correctly updates the balance of [addr] - sdb, err := vm.Blockchain().State() + sdb, err := vm.Ethereum().BlockChain().State() if err != nil { t.Fatal(err) } expectedRemainingBalance := new(uint256.Int).Mul( uint256.NewInt(importAmount-actualAVAXBurned), atomic.X2CRate) - addr := testKeys[0].EthAddress() - if actualBalance := sdb.GetBalance(addr); actualBalance.Cmp(expectedRemainingBalance) != 0 { - t.Fatalf("address remaining balance %s equal %s not %s", addr.String(), actualBalance, expectedRemainingBalance) + if actualBalance := sdb.GetBalance(ethAddress); actualBalance.Cmp(expectedRemainingBalance) != 0 { + t.Fatalf("address remaining balance %s equal %s not %s", ethAddress.String(), actualBalance, expectedRemainingBalance) } } tests2 := map[string]atomicTxTest{ @@ -571,12 +580,12 @@ func TestImportTxGasCost(t *testing.T) { }, }}, Outs: []atomic.EVMOutput{{ - Address: testEthAddrs[0], + Address: vmtest.TestEthAddrs[0], Amount: importAmount, AssetID: avaxAssetID, }}, }, - Keys: [][]*secp256k1.PrivateKey{{testKeys[0]}}, + Keys: [][]*secp256k1.PrivateKey{{vmtest.TestKeys[0]}}, ExpectedGasUsed: 1230, ExpectedFee: 30750, BaseFee: big.NewInt(25 * utils.GWei), @@ -595,12 +604,12 @@ func TestImportTxGasCost(t *testing.T) { }, }}, Outs: []atomic.EVMOutput{{ - Address: testEthAddrs[0], + Address: vmtest.TestEthAddrs[0], Amount: importAmount, AssetID: avaxAssetID, }}, }, - Keys: [][]*secp256k1.PrivateKey{{testKeys[0]}}, + Keys: [][]*secp256k1.PrivateKey{{vmtest.TestKeys[0]}}, ExpectedGasUsed: 1230, ExpectedFee: 1, BaseFee: big.NewInt(1), @@ -619,12 +628,12 @@ func TestImportTxGasCost(t *testing.T) { }, }}, Outs: []atomic.EVMOutput{{ - Address: testEthAddrs[0], + Address: vmtest.TestEthAddrs[0], Amount: importAmount, AssetID: avaxAssetID, }}, }, - Keys: [][]*secp256k1.PrivateKey{{testKeys[0]}}, + Keys: [][]*secp256k1.PrivateKey{{vmtest.TestKeys[0]}}, ExpectedGasUsed: 11230, ExpectedFee: 1, BaseFee: big.NewInt(1), @@ -655,13 +664,13 @@ func TestImportTxGasCost(t *testing.T) { }, Outs: []atomic.EVMOutput{ { - Address: testEthAddrs[0], + Address: vmtest.TestEthAddrs[0], Amount: importAmount, AssetID: antAssetID, }, }, }, - Keys: [][]*secp256k1.PrivateKey{{testKeys[0]}, {testKeys[0]}}, + Keys: [][]*secp256k1.PrivateKey{{vmtest.TestKeys[0]}, {vmtest.TestKeys[0]}}, ExpectedGasUsed: 2318, ExpectedFee: 57950, BaseFee: big.NewInt(25 * utils.GWei), @@ -691,18 +700,18 @@ func TestImportTxGasCost(t *testing.T) { }, Outs: []atomic.EVMOutput{ { - Address: testEthAddrs[0], + Address: vmtest.TestEthAddrs[0], Amount: importAmount, AssetID: avaxAssetID, }, { - Address: testEthAddrs[0], + Address: vmtest.TestEthAddrs[0], Amount: importAmount, AssetID: antAssetID, }, }, }, - Keys: [][]*secp256k1.PrivateKey{{testKeys[0]}, {testKeys[0]}}, + Keys: [][]*secp256k1.PrivateKey{{vmtest.TestKeys[0]}, {vmtest.TestKeys[0]}}, ExpectedGasUsed: 2378, ExpectedFee: 59450, BaseFee: big.NewInt(25 * utils.GWei), @@ -721,12 +730,12 @@ func TestImportTxGasCost(t *testing.T) { }, }}, Outs: []atomic.EVMOutput{{ - Address: testEthAddrs[0], + Address: vmtest.TestEthAddrs[0], Amount: importAmount, AssetID: avaxAssetID, }}, }, - Keys: [][]*secp256k1.PrivateKey{{testKeys[0], testKeys[1]}}, + Keys: [][]*secp256k1.PrivateKey{{vmtest.TestKeys[0], vmtest.TestKeys[1]}}, ExpectedGasUsed: 2234, ExpectedFee: 55850, BaseFee: big.NewInt(25 * utils.GWei), @@ -820,23 +829,23 @@ func TestImportTxGasCost(t *testing.T) { }, Outs: []atomic.EVMOutput{ { - Address: testEthAddrs[0], + Address: vmtest.TestEthAddrs[0], Amount: importAmount * 10, AssetID: avaxAssetID, }, }, }, Keys: [][]*secp256k1.PrivateKey{ - {testKeys[0]}, - {testKeys[0]}, - {testKeys[0]}, - {testKeys[0]}, - {testKeys[0]}, - {testKeys[0]}, - {testKeys[0]}, - {testKeys[0]}, - {testKeys[0]}, - {testKeys[0]}, + {vmtest.TestKeys[0]}, + {vmtest.TestKeys[0]}, + {vmtest.TestKeys[0]}, + {vmtest.TestKeys[0]}, + {vmtest.TestKeys[0]}, + {vmtest.TestKeys[0]}, + {vmtest.TestKeys[0]}, + {vmtest.TestKeys[0]}, + {vmtest.TestKeys[0]}, + {vmtest.TestKeys[0]}, }, ExpectedGasUsed: 11022, ExpectedFee: 275550, @@ -873,9 +882,12 @@ func TestImportTxGasCost(t *testing.T) { } func TestImportTxSemanticVerify(t *testing.T) { + key, err := secp256k1.NewPrivateKey() + require.NoError(t, err) + ethAddress := key.PublicKey().EthAddress() tests := map[string]atomicTxTest{ "UTXO not present during bootstrapping": { - setup: func(t *testing.T, vm *vm.VM, sharedMemory *avalancheatomic.Memory) *atomic.Tx { + setup: func(t *testing.T, vm *VM, sharedMemory *avalancheatomic.Memory) *atomic.Tx { tx := &atomic.Tx{UnsignedAtomicTx: &atomic.UnsignedImportTx{ NetworkID: vm.Ctx.NetworkID, BlockchainID: vm.Ctx.ChainID, @@ -891,12 +903,12 @@ func TestImportTxSemanticVerify(t *testing.T) { }, }}, Outs: []atomic.EVMOutput{{ - Address: testEthAddrs[0], + Address: ethAddress, Amount: 1, AssetID: vm.Ctx.AVAXAssetID, }}, }} - if err := tx.Sign(atomic.Codec, [][]*secp256k1.PrivateKey{{testKeys[0]}}); err != nil { + if err := tx.Sign(atomic.Codec, [][]*secp256k1.PrivateKey{{key}}); err != nil { t.Fatal(err) } return tx @@ -904,7 +916,7 @@ func TestImportTxSemanticVerify(t *testing.T) { bootstrapping: true, }, "UTXO not present": { - setup: func(t *testing.T, vm *vm.VM, sharedMemory *avalancheatomic.Memory) *atomic.Tx { + setup: func(t *testing.T, vm *VM, sharedMemory *avalancheatomic.Memory) *atomic.Tx { tx := &atomic.Tx{UnsignedAtomicTx: &atomic.UnsignedImportTx{ NetworkID: vm.Ctx.NetworkID, BlockchainID: vm.Ctx.ChainID, @@ -920,12 +932,12 @@ func TestImportTxSemanticVerify(t *testing.T) { }, }}, Outs: []atomic.EVMOutput{{ - Address: testEthAddrs[0], + Address: ethAddress, Amount: 1, AssetID: vm.Ctx.AVAXAssetID, }}, }} - if err := tx.Sign(atomic.Codec, [][]*secp256k1.PrivateKey{{testKeys[0]}}); err != nil { + if err := tx.Sign(atomic.Codec, [][]*secp256k1.PrivateKey{{key}}); err != nil { t.Fatal(err) } return tx @@ -933,7 +945,7 @@ func TestImportTxSemanticVerify(t *testing.T) { semanticVerifyErr: "failed to fetch import UTXOs from", }, "garbage UTXO": { - setup: func(t *testing.T, vm *vm.VM, sharedMemory *avalancheatomic.Memory) *atomic.Tx { + setup: func(t *testing.T, vm *VM, sharedMemory *avalancheatomic.Memory) *atomic.Tx { utxoID := avax.UTXOID{TxID: ids.GenerateTestID()} xChainSharedMemory := sharedMemory.NewSharedMemory(vm.Ctx.XChainID) inputID := utxoID.InputID() @@ -941,7 +953,7 @@ func TestImportTxSemanticVerify(t *testing.T) { Key: inputID[:], Value: []byte("hey there"), Traits: [][]byte{ - testShortIDAddrs[0].Bytes(), + key.Address().Bytes(), }, }}}}); err != nil { t.Fatal(err) @@ -960,12 +972,12 @@ func TestImportTxSemanticVerify(t *testing.T) { }, }}, Outs: []atomic.EVMOutput{{ - Address: testEthAddrs[0], + Address: ethAddress, Amount: 1, AssetID: vm.Ctx.AVAXAssetID, }}, }} - if err := tx.Sign(atomic.Codec, [][]*secp256k1.PrivateKey{{testKeys[0]}}); err != nil { + if err := tx.Sign(atomic.Codec, [][]*secp256k1.PrivateKey{{key}}); err != nil { t.Fatal(err) } return tx @@ -973,10 +985,10 @@ func TestImportTxSemanticVerify(t *testing.T) { semanticVerifyErr: "failed to unmarshal UTXO", }, "UTXO AssetID mismatch": { - setup: func(t *testing.T, vm *vm.VM, sharedMemory *avalancheatomic.Memory) *atomic.Tx { + setup: func(t *testing.T, vm *VM, sharedMemory *avalancheatomic.Memory) *atomic.Tx { txID := ids.GenerateTestID() expectedAssetID := ids.GenerateTestID() - utxo, err := addUTXO(sharedMemory, vm.Ctx, txID, 0, expectedAssetID, 1, testShortIDAddrs[0]) + utxo, err := addUTXO(sharedMemory, vm.Ctx, txID, 0, expectedAssetID, 1, key.Address()) if err != nil { t.Fatal(err) } @@ -994,22 +1006,22 @@ func TestImportTxSemanticVerify(t *testing.T) { }, }}, Outs: []atomic.EVMOutput{{ - Address: testEthAddrs[0], + Address: ethAddress, Amount: 1, AssetID: vm.Ctx.AVAXAssetID, }}, }} - if err := tx.Sign(atomic.Codec, [][]*secp256k1.PrivateKey{{testKeys[0]}}); err != nil { + if err := tx.Sign(atomic.Codec, [][]*secp256k1.PrivateKey{{key}}); err != nil { t.Fatal(err) } return tx }, - semanticVerifyErr: vm.ErrAssetIDMismatch.Error(), + semanticVerifyErr: ErrAssetIDMismatch.Error(), }, "insufficient AVAX funds": { - setup: func(t *testing.T, vm *vm.VM, sharedMemory *avalancheatomic.Memory) *atomic.Tx { + setup: func(t *testing.T, vm *VM, sharedMemory *avalancheatomic.Memory) *atomic.Tx { txID := ids.GenerateTestID() - utxo, err := addUTXO(sharedMemory, vm.Ctx, txID, 0, vm.Ctx.AVAXAssetID, 1, testShortIDAddrs[0]) + utxo, err := addUTXO(sharedMemory, vm.Ctx, txID, 0, vm.Ctx.AVAXAssetID, 1, key.Address()) if err != nil { t.Fatal(err) } @@ -1027,12 +1039,12 @@ func TestImportTxSemanticVerify(t *testing.T) { }, }}, Outs: []atomic.EVMOutput{{ - Address: testEthAddrs[0], + Address: ethAddress, Amount: 2, // Produce more output than is consumed by the transaction AssetID: vm.Ctx.AVAXAssetID, }}, }} - if err := tx.Sign(atomic.Codec, [][]*secp256k1.PrivateKey{{testKeys[0]}}); err != nil { + if err := tx.Sign(atomic.Codec, [][]*secp256k1.PrivateKey{{key}}); err != nil { t.Fatal(err) } return tx @@ -1040,10 +1052,10 @@ func TestImportTxSemanticVerify(t *testing.T) { semanticVerifyErr: "import tx flow check failed due to", }, "insufficient non-AVAX funds": { - setup: func(t *testing.T, vm *vm.VM, sharedMemory *avalancheatomic.Memory) *atomic.Tx { + setup: func(t *testing.T, vm *VM, sharedMemory *avalancheatomic.Memory) *atomic.Tx { txID := ids.GenerateTestID() assetID := ids.GenerateTestID() - utxo, err := addUTXO(sharedMemory, vm.Ctx, txID, 0, assetID, 1, testShortIDAddrs[0]) + utxo, err := addUTXO(sharedMemory, vm.Ctx, txID, 0, assetID, 1, key.Address()) if err != nil { t.Fatal(err) } @@ -1061,12 +1073,12 @@ func TestImportTxSemanticVerify(t *testing.T) { }, }}, Outs: []atomic.EVMOutput{{ - Address: testEthAddrs[0], + Address: ethAddress, Amount: 2, // Produce more output than is consumed by the transaction AssetID: assetID, }}, }} - if err := tx.Sign(atomic.Codec, [][]*secp256k1.PrivateKey{{testKeys[0]}}); err != nil { + if err := tx.Sign(atomic.Codec, [][]*secp256k1.PrivateKey{{key}}); err != nil { t.Fatal(err) } return tx @@ -1074,9 +1086,9 @@ func TestImportTxSemanticVerify(t *testing.T) { semanticVerifyErr: "import tx flow check failed due to", }, "no signatures": { - setup: func(t *testing.T, vm *vm.VM, sharedMemory *avalancheatomic.Memory) *atomic.Tx { + setup: func(t *testing.T, vm *VM, sharedMemory *avalancheatomic.Memory) *atomic.Tx { txID := ids.GenerateTestID() - utxo, err := addUTXO(sharedMemory, vm.Ctx, txID, 0, vm.Ctx.AVAXAssetID, 1, testShortIDAddrs[0]) + utxo, err := addUTXO(sharedMemory, vm.Ctx, txID, 0, vm.Ctx.AVAXAssetID, 1, key.Address()) if err != nil { t.Fatal(err) } @@ -1094,7 +1106,7 @@ func TestImportTxSemanticVerify(t *testing.T) { }, }}, Outs: []atomic.EVMOutput{{ - Address: testEthAddrs[0], + Address: ethAddress, Amount: 1, AssetID: vm.Ctx.AVAXAssetID, }}, @@ -1107,9 +1119,9 @@ func TestImportTxSemanticVerify(t *testing.T) { semanticVerifyErr: "import tx contained mismatched number of inputs/credentials", }, "incorrect signature": { - setup: func(t *testing.T, vm *vm.VM, sharedMemory *avalancheatomic.Memory) *atomic.Tx { + setup: func(t *testing.T, vm *VM, sharedMemory *avalancheatomic.Memory) *atomic.Tx { txID := ids.GenerateTestID() - utxo, err := addUTXO(sharedMemory, vm.Ctx, txID, 0, vm.Ctx.AVAXAssetID, 1, testShortIDAddrs[0]) + utxo, err := addUTXO(sharedMemory, vm.Ctx, txID, 0, vm.Ctx.AVAXAssetID, 1, key.Address()) if err != nil { t.Fatal(err) } @@ -1127,13 +1139,15 @@ func TestImportTxSemanticVerify(t *testing.T) { }, }}, Outs: []atomic.EVMOutput{{ - Address: testEthAddrs[0], + Address: ethAddress, Amount: 1, AssetID: vm.Ctx.AVAXAssetID, }}, }} + incorrectKey, err := secp256k1.NewPrivateKey() + require.NoError(t, err) // Sign the transaction with the incorrect key - if err := tx.Sign(atomic.Codec, [][]*secp256k1.PrivateKey{{testKeys[1]}}); err != nil { + if err := tx.Sign(atomic.Codec, [][]*secp256k1.PrivateKey{{incorrectKey}}); err != nil { t.Fatal(err) } return tx @@ -1141,9 +1155,9 @@ func TestImportTxSemanticVerify(t *testing.T) { semanticVerifyErr: "import tx transfer failed verification", }, "non-unique EVM Outputs": { - setup: func(t *testing.T, vm *vm.VM, sharedMemory *avalancheatomic.Memory) *atomic.Tx { + setup: func(t *testing.T, vm *VM, sharedMemory *avalancheatomic.Memory) *atomic.Tx { txID := ids.GenerateTestID() - utxo, err := addUTXO(sharedMemory, vm.Ctx, txID, 0, vm.Ctx.AVAXAssetID, 2, testShortIDAddrs[0]) + utxo, err := addUTXO(sharedMemory, vm.Ctx, txID, 0, vm.Ctx.AVAXAssetID, 2, key.Address()) if err != nil { t.Fatal(err) } @@ -1162,18 +1176,18 @@ func TestImportTxSemanticVerify(t *testing.T) { }}, Outs: []atomic.EVMOutput{ { - Address: testEthAddrs[0], + Address: ethAddress, Amount: 1, AssetID: vm.Ctx.AVAXAssetID, }, { - Address: testEthAddrs[0], + Address: ethAddress, Amount: 1, AssetID: vm.Ctx.AVAXAssetID, }, }, }} - if err := tx.Sign(atomic.Codec, [][]*secp256k1.PrivateKey{{testKeys[0]}}); err != nil { + if err := tx.Sign(atomic.Codec, [][]*secp256k1.PrivateKey{{key}}); err != nil { t.Fatal(err) } return tx @@ -1192,11 +1206,14 @@ func TestImportTxSemanticVerify(t *testing.T) { func TestImportTxEVMStateTransfer(t *testing.T) { assetID := ids.GenerateTestID() + key, err := secp256k1.NewPrivateKey() + require.NoError(t, err) + ethAddress := key.EthAddress() tests := map[string]atomicTxTest{ "AVAX UTXO": { - setup: func(t *testing.T, vm *vm.VM, sharedMemory *avalancheatomic.Memory) *atomic.Tx { + setup: func(t *testing.T, vm *VM, sharedMemory *avalancheatomic.Memory) *atomic.Tx { txID := ids.GenerateTestID() - utxo, err := addUTXO(sharedMemory, vm.Ctx, txID, 0, vm.Ctx.AVAXAssetID, 1, testShortIDAddrs[0]) + utxo, err := addUTXO(sharedMemory, vm.Ctx, txID, 0, vm.Ctx.AVAXAssetID, 1, key.Address()) if err != nil { t.Fatal(err) } @@ -1214,34 +1231,34 @@ func TestImportTxEVMStateTransfer(t *testing.T) { }, }}, Outs: []atomic.EVMOutput{{ - Address: testEthAddrs[0], + Address: ethAddress, Amount: 1, AssetID: vm.Ctx.AVAXAssetID, }}, }} - if err := tx.Sign(atomic.Codec, [][]*secp256k1.PrivateKey{{testKeys[0]}}); err != nil { + if err := tx.Sign(atomic.Codec, [][]*secp256k1.PrivateKey{{key}}); err != nil { t.Fatal(err) } return tx }, - checkState: func(t *testing.T, vm *vm.VM) { + checkState: func(t *testing.T, vm *VM) { lastAcceptedBlock := vm.LastAcceptedExtendedBlock() - sdb, err := vm.Blockchain().StateAt(lastAcceptedBlock.GetEthBlock().Root()) + sdb, err := vm.Ethereum().BlockChain().StateAt(lastAcceptedBlock.GetEthBlock().Root()) if err != nil { t.Fatal(err) } - avaxBalance := sdb.GetBalance(testEthAddrs[0]) + avaxBalance := sdb.GetBalance(ethAddress) if avaxBalance.Cmp(atomic.X2CRate) != 0 { t.Fatalf("Expected AVAX balance to be %d, found balance: %d", *atomic.X2CRate, avaxBalance) } }, }, "non-AVAX UTXO": { - setup: func(t *testing.T, vm *vm.VM, sharedMemory *avalancheatomic.Memory) *atomic.Tx { + setup: func(t *testing.T, vm *VM, sharedMemory *avalancheatomic.Memory) *atomic.Tx { txID := ids.GenerateTestID() - utxo, err := addUTXO(sharedMemory, vm.Ctx, txID, 0, assetID, 1, testShortIDAddrs[0]) + utxo, err := addUTXO(sharedMemory, vm.Ctx, txID, 0, assetID, 1, key.Address()) if err != nil { t.Fatal(err) } @@ -1259,30 +1276,30 @@ func TestImportTxEVMStateTransfer(t *testing.T) { }, }}, Outs: []atomic.EVMOutput{{ - Address: testEthAddrs[0], + Address: ethAddress, Amount: 1, AssetID: assetID, }}, }} - if err := tx.Sign(atomic.Codec, [][]*secp256k1.PrivateKey{{testKeys[0]}}); err != nil { + if err := tx.Sign(atomic.Codec, [][]*secp256k1.PrivateKey{{key}}); err != nil { t.Fatal(err) } return tx }, - checkState: func(t *testing.T, vm *vm.VM) { + checkState: func(t *testing.T, vm *VM) { lastAcceptedBlock := vm.LastAcceptedExtendedBlock() - statedb, err := vm.Blockchain().StateAt(lastAcceptedBlock.GetEthBlock().Root()) + statedb, err := vm.Ethereum().BlockChain().StateAt(lastAcceptedBlock.GetEthBlock().Root()) if err != nil { t.Fatal(err) } wrappedStateDB := extstate.New(statedb) - assetBalance := wrappedStateDB.GetBalanceMultiCoin(testEthAddrs[0], common.Hash(assetID)) + assetBalance := wrappedStateDB.GetBalanceMultiCoin(ethAddress, common.Hash(assetID)) if assetBalance.Cmp(common.Big1) != 0 { t.Fatalf("Expected asset balance to be %d, found balance: %d", common.Big1, assetBalance) } - avaxBalance := wrappedStateDB.GetBalance(testEthAddrs[0]) + avaxBalance := wrappedStateDB.GetBalance(ethAddress) if avaxBalance.Cmp(common.U2560) != 0 { t.Fatalf("Expected AVAX balance to be 0, found balance: %d", avaxBalance) } @@ -1296,3 +1313,94 @@ func TestImportTxEVMStateTransfer(t *testing.T) { }) } } + +func executeTxTest(t *testing.T, test atomicTxTest) { + vm := newAtomicTestVM() + tvm := vmtest.SetupTestVM(t, vm, vmtest.TestVMConfig{ + IsSyncing: test.bootstrapping, + Fork: &test.fork, + }) + rules := vm.CurrentRules() + + tx := test.setup(t, vm, tvm.AtomicMemory) + + var baseFee *big.Int + // If ApricotPhase3 is active, use the initial base fee for the atomic transaction + switch { + case rules.IsApricotPhase3: + baseFee = vmtest.InitialBaseFee + } + + lastAcceptedBlock := vm.LastAcceptedExtendedBlock() + backend := NewVerifierBackend(vm, rules) + + if err := backend.SemanticVerify(tx, lastAcceptedBlock, baseFee); len(test.semanticVerifyErr) == 0 && err != nil { + t.Fatalf("SemanticVerify failed unexpectedly due to: %s", err) + } else if len(test.semanticVerifyErr) != 0 { + if err == nil { + t.Fatalf("SemanticVerify unexpectedly returned a nil error. Expected err: %s", test.semanticVerifyErr) + } + if !strings.Contains(err.Error(), test.semanticVerifyErr) { + t.Fatalf("Expected SemanticVerify to fail due to %s, but failed with: %s", test.semanticVerifyErr, err) + } + // If SemanticVerify failed for the expected reason, return early + return + } + + // Retrieve dummy state to test that EVMStateTransfer works correctly + statedb, err := vm.Ethereum().BlockChain().StateAt(lastAcceptedBlock.GetEthBlock().Root()) + if err != nil { + t.Fatal(err) + } + wrappedStateDB := extstate.New(statedb) + if err := tx.UnsignedAtomicTx.EVMStateTransfer(vm.Ctx, wrappedStateDB); len(test.evmStateTransferErr) == 0 && err != nil { + t.Fatalf("EVMStateTransfer failed unexpectedly due to: %s", err) + } else if len(test.evmStateTransferErr) != 0 { + if err == nil { + t.Fatalf("EVMStateTransfer unexpectedly returned a nil error. Expected err: %s", test.evmStateTransferErr) + } + if !strings.Contains(err.Error(), test.evmStateTransferErr) { + t.Fatalf("Expected SemanticVerify to fail due to %s, but failed with: %s", test.evmStateTransferErr, err) + } + // If EVMStateTransfer failed for the expected reason, return early + return + } + + if err := vm.AtomicMempool.AddLocalTx(tx); err != nil { + t.Fatal(err) + } + + if test.bootstrapping { + return + } + msg, err := vm.WaitForEvent(context.Background()) + require.NoError(t, err) + require.Equal(t, commonEng.PendingTxs, msg) + + // If we've reached this point, we expect to be able to build and verify the block without any errors + blk, err := vm.BuildBlock(context.Background()) + if err != nil { + t.Fatal(err) + } + + if err := blk.Verify(context.Background()); err != nil { + t.Fatal(err) + } + + if err := blk.Accept(context.Background()); len(test.acceptErr) == 0 && err != nil { + t.Fatalf("Accept failed unexpectedly due to: %s", err) + } else if len(test.acceptErr) != 0 { + if err == nil { + t.Fatalf("Accept unexpectedly returned a nil error. Expected err: %s", test.acceptErr) + } + if !strings.Contains(err.Error(), test.acceptErr) { + t.Fatalf("Expected Accept to fail due to %s, but failed with: %s", test.acceptErr, err) + } + // If Accept failed for the expected reason, return early + return + } + + if test.checkState != nil { + test.checkState(t, vm) + } +} diff --git a/plugin/evm/atomic/vm/syncervm_test.go b/plugin/evm/atomic/vm/syncervm_test.go new file mode 100644 index 0000000000..073ea34d1a --- /dev/null +++ b/plugin/evm/atomic/vm/syncervm_test.go @@ -0,0 +1,124 @@ +// Copyright (C) 2019-2025, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package vm + +import ( + "testing" + + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/utils/hashing" + "github.com/ava-labs/avalanchego/utils/units" + "github.com/ava-labs/coreth/consensus/dummy" + "github.com/ava-labs/coreth/core" + "github.com/ava-labs/coreth/core/extstate" + "github.com/ava-labs/coreth/params" + "github.com/ava-labs/coreth/plugin/evm/atomic" + "github.com/ava-labs/coreth/plugin/evm/atomic/atomictest" + "github.com/ava-labs/coreth/plugin/evm/extension" + "github.com/ava-labs/coreth/plugin/evm/vmtest" + "github.com/ava-labs/coreth/predicate" + + "github.com/ava-labs/libevm/common" + "github.com/ava-labs/libevm/core/types" + + "github.com/stretchr/testify/require" +) + +func TestAtomicSyncerVM(t *testing.T) { + importAmount := 2000000 * units.Avax // 2M avax + for _, test := range vmtest.SyncerVMTests { + includedAtomicTxs := make([]*atomic.Tx, 0) + + t.Run(test.Name, func(t *testing.T) { + genFn := func(i int, vm extension.InnerVM, gen *core.BlockGen) { + atomicVM, ok := vm.(*VM) + require.True(t, ok) + b, err := predicate.NewResults().Bytes() + require.NoError(t, err) + gen.AppendExtra(b) + switch i { + case 0: + // spend the UTXOs from shared memory + importTx, err := atomicVM.newImportTx(atomicVM.Ctx.XChainID, vmtest.TestEthAddrs[0], vmtest.InitialBaseFee, vmtest.TestKeys[0:1]) + require.NoError(t, err) + require.NoError(t, atomicVM.AtomicMempool.AddLocalTx(importTx)) + includedAtomicTxs = append(includedAtomicTxs, importTx) + case 1: + // export some of the imported UTXOs to test exportTx is properly synced + state, err := vm.Ethereum().BlockChain().State() + require.NoError(t, err) + wrappedStateDB := extstate.New(state) + exportTx, err := atomic.NewExportTx( + atomicVM.Ctx, + atomicVM.CurrentRules(), + wrappedStateDB, + atomicVM.Ctx.AVAXAssetID, + importAmount/2, + atomicVM.Ctx.XChainID, + vmtest.TestShortIDAddrs[0], + vmtest.InitialBaseFee, + vmtest.TestKeys[0:1], + ) + require.NoError(t, err) + require.NoError(t, atomicVM.AtomicMempool.AddLocalTx(exportTx)) + includedAtomicTxs = append(includedAtomicTxs, exportTx) + default: // Generate simple transfer transactions. + pk := vmtest.TestKeys[0].ToECDSA() + tx := types.NewTransaction(gen.TxNonce(vmtest.TestEthAddrs[0]), vmtest.TestEthAddrs[1], common.Big1, params.TxGas, vmtest.InitialBaseFee, nil) + signedTx, err := types.SignTx(tx, types.NewEIP155Signer(vm.Ethereum().BlockChain().Config().ChainID), pk) + require.NoError(t, err) + gen.AddTx(signedTx) + } + } + newVMFn := func() (extension.InnerVM, dummy.ConsensusCallbacks) { + vm := newAtomicTestVM() + return vm, vm.createConsensusCallbacks() + } + + afterInit := func(t *testing.T, params vmtest.SyncTestParams, vmSetup vmtest.SyncVMSetup, isServer bool) { + atomicVM, ok := vmSetup.VM.(*VM) + require.True(t, ok) + + alloc := map[ids.ShortID]uint64{ + vmtest.TestShortIDAddrs[0]: importAmount, + } + + for addr, avaxAmount := range alloc { + txID, err := ids.ToID(hashing.ComputeHash256(addr.Bytes())) + require.NoError(t, err, "Failed to generate txID from addr") + _, err = addUTXO(vmSetup.AtomicMemory, vmSetup.SnowCtx, txID, 0, vmSetup.SnowCtx.AVAXAssetID, avaxAmount, addr) + require.NoError(t, err, "Failed to add UTXO to shared memory") + } + if isServer { + serverAtomicTrie := atomicVM.AtomicBackend.AtomicTrie() + // Calling AcceptTrie with SyncableInterval creates a commit for the atomic trie + committed, err := serverAtomicTrie.AcceptTrie(params.SyncableInterval, serverAtomicTrie.LastAcceptedRoot()) + require.NoError(t, err) + require.True(t, committed) + require.NoError(t, atomicVM.VersionDB().Commit()) + } + } + + testSetup := &vmtest.SyncTestSetup{ + NewVM: newVMFn, + GenFn: genFn, + AfterInit: afterInit, + ExtraSyncerVMTest: func(t *testing.T, syncerVMSetup vmtest.SyncVMSetup) { + // check atomic memory was synced properly + syncerVM := syncerVMSetup.VM + atomicVM, ok := syncerVM.(*VM) + require.True(t, ok) + syncerSharedMemories := atomictest.NewSharedMemories(syncerVMSetup.AtomicMemory, atomicVM.Ctx.ChainID, atomicVM.Ctx.XChainID) + + for _, tx := range includedAtomicTxs { + atomicOps, err := atomictest.ConvertToAtomicOps(tx) + require.NoError(t, err) + syncerSharedMemories.AssertOpsApplied(t, atomicOps) + } + }, + } + test.TestFunc(t, testSetup) + }) + } +} diff --git a/plugin/evm/atomic/vm/tx_gossip_test.go b/plugin/evm/atomic/vm/tx_gossip_test.go new file mode 100644 index 0000000000..65c43067f4 --- /dev/null +++ b/plugin/evm/atomic/vm/tx_gossip_test.go @@ -0,0 +1,319 @@ +// Copyright (C) 2019-2025, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package vm + +import ( + "context" + "encoding/binary" + "sync" + "testing" + "time" + + avalancheatomic "github.com/ava-labs/avalanchego/chains/atomic" + "github.com/ava-labs/avalanchego/database/memdb" + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/network/p2p" + "github.com/ava-labs/avalanchego/network/p2p/gossip" + "github.com/ava-labs/avalanchego/proto/pb/sdk" + "github.com/ava-labs/avalanchego/snow" + "github.com/ava-labs/avalanchego/snow/engine/enginetest" + "github.com/ava-labs/avalanchego/snow/snowtest" + "github.com/ava-labs/avalanchego/snow/validators" + agoUtils "github.com/ava-labs/avalanchego/utils" + "github.com/ava-labs/avalanchego/utils/crypto/secp256k1" + "github.com/ava-labs/avalanchego/utils/logging" + "github.com/ava-labs/avalanchego/utils/set" + "github.com/prometheus/client_golang/prometheus" + "github.com/stretchr/testify/require" + + "github.com/ava-labs/avalanchego/vms/components/avax" + "github.com/ava-labs/avalanchego/vms/secp256k1fx" + + "google.golang.org/protobuf/proto" + + "github.com/ava-labs/coreth/plugin/evm/atomic" + "github.com/ava-labs/coreth/plugin/evm/config" + "github.com/ava-labs/coreth/plugin/evm/vmtest" + "github.com/ava-labs/coreth/utils" + "github.com/ava-labs/coreth/utils/utilstest" +) + +func TestAtomicTxGossip(t *testing.T) { + require := require.New(t) + ctx, cancel := utilstest.NewTestContext(t) + defer cancel() + + snowCtx := snowtest.Context(t, snowtest.CChainID) + snowCtx.AVAXAssetID = ids.GenerateTestID() + validatorState := utils.NewTestValidatorState() + snowCtx.ValidatorState = validatorState + memory := avalancheatomic.NewMemory(memdb.New()) + snowCtx.SharedMemory = memory.NewSharedMemory(snowCtx.ChainID) + + pk, err := secp256k1.NewPrivateKey() + require.NoError(err) + address := pk.EthAddress() + genesis := vmtest.NewPrefundedGenesis(100_000_000_000_000_000, address) + genesisBytes, err := genesis.MarshalJSON() + require.NoError(err) + + responseSender := &enginetest.SenderStub{ + SentAppResponse: make(chan []byte, 1), + } + vm := newAtomicTestVM() + + require.NoError(vm.Initialize( + ctx, + snowCtx, + memdb.New(), + genesisBytes, + nil, + nil, + nil, + responseSender, + )) + require.NoError(vm.SetState(ctx, snow.NormalOp)) + + defer func() { + require.NoError(vm.Shutdown(ctx)) + }() + + // sender for the peer requesting gossip from [vm] + peerSender := &enginetest.SenderStub{ + SentAppRequest: make(chan []byte, 1), + } + network, err := p2p.NewNetwork(logging.NoLog{}, peerSender, prometheus.NewRegistry(), "") + require.NoError(err) + client := network.NewClient(p2p.AtomicTxGossipHandlerID) + + // we only accept gossip requests from validators + requestingNodeID := ids.GenerateTestNodeID() + require.NoError(vm.Connected(ctx, requestingNodeID, nil)) + validatorState.GetCurrentHeightF = func(context.Context) (uint64, error) { + return 0, nil + } + validatorState.GetValidatorSetF = func(context.Context, uint64, ids.ID) (map[ids.NodeID]*validators.GetValidatorOutput, error) { + return map[ids.NodeID]*validators.GetValidatorOutput{ + requestingNodeID: { + NodeID: requestingNodeID, + Weight: 1, + }, + }, nil + } + + // Ask the VM for any new transactions. We should get nothing at first. + emptyBloomFilter, err := gossip.NewBloomFilter( + prometheus.NewRegistry(), + "", + config.TxGossipBloomMinTargetElements, + config.TxGossipBloomTargetFalsePositiveRate, + config.TxGossipBloomResetFalsePositiveRate, + ) + require.NoError(err) + emptyBloomFilterBytes, _ := emptyBloomFilter.Marshal() + request := &sdk.PullGossipRequest{ + Filter: emptyBloomFilterBytes, + Salt: agoUtils.RandomBytes(32), + } + + requestBytes, err := proto.Marshal(request) + require.NoError(err) + + wg := &sync.WaitGroup{} + wg.Add(1) + onResponse := func(_ context.Context, nodeID ids.NodeID, responseBytes []byte, err error) { + require.NoError(err) + + response := &sdk.PullGossipResponse{} + require.NoError(proto.Unmarshal(responseBytes, response)) + require.Empty(response.Gossip) + wg.Done() + } + require.NoError(client.AppRequest(ctx, set.Of(vm.Ctx.NodeID), requestBytes, onResponse)) + require.NoError(vm.AppRequest(ctx, requestingNodeID, 1, time.Time{}, <-peerSender.SentAppRequest)) + require.NoError(network.AppResponse(ctx, snowCtx.NodeID, 1, <-responseSender.SentAppResponse)) + utilstest.WaitGroupWithContext(t, ctx, wg) + + // Issue a tx to the VM + utxo, err := addUTXO( + memory, + snowCtx, + ids.GenerateTestID(), + 0, + snowCtx.AVAXAssetID, + 100_000_000_000, + pk.Address(), + ) + require.NoError(err) + tx, err := atomic.NewImportTx(vm.Ctx, vm.CurrentRules(), vm.clock.Unix(), vm.Ctx.XChainID, address, vmtest.InitialBaseFee, secp256k1fx.NewKeychain(pk), []*avax.UTXO{utxo}) + require.NoError(err) + require.NoError(vm.AtomicMempool.AddLocalTx(tx)) + + // wait so we aren't throttled by the vm + utilstest.SleepWithContext(ctx, 5*time.Second) + + // Ask the VM for new transactions. We should get the newly issued tx. + wg.Add(1) + + marshaller := atomic.TxMarshaller{} + onResponse = func(_ context.Context, nodeID ids.NodeID, responseBytes []byte, err error) { + require.NoError(err) + + response := &sdk.PullGossipResponse{} + require.NoError(proto.Unmarshal(responseBytes, response)) + require.Len(response.Gossip, 1) + + gotTx, err := marshaller.UnmarshalGossip(response.Gossip[0]) + require.NoError(err) + require.Equal(tx.ID(), gotTx.GossipID()) + + wg.Done() + } + require.NoError(client.AppRequest(ctx, set.Of(vm.Ctx.NodeID), requestBytes, onResponse)) + require.NoError(vm.AppRequest(ctx, requestingNodeID, 3, time.Time{}, <-peerSender.SentAppRequest)) + require.NoError(network.AppResponse(ctx, snowCtx.NodeID, 3, <-responseSender.SentAppResponse)) + utilstest.WaitGroupWithContext(t, ctx, wg) +} + +// Tests that a tx is gossiped when it is issued +func TestAtomicTxPushGossipOutbound(t *testing.T) { + require := require.New(t) + ctx, cancel := utilstest.NewTestContext(t) + defer cancel() + + snowCtx := snowtest.Context(t, snowtest.CChainID) + snowCtx.AVAXAssetID = ids.GenerateTestID() + validatorState := utils.NewTestValidatorState() + snowCtx.ValidatorState = validatorState + memory := avalancheatomic.NewMemory(memdb.New()) + snowCtx.SharedMemory = memory.NewSharedMemory(snowCtx.ChainID) + + pk, err := secp256k1.NewPrivateKey() + require.NoError(err) + address := pk.EthAddress() + genesis := vmtest.NewPrefundedGenesis(100_000_000_000_000_000, address) + genesisBytes, err := genesis.MarshalJSON() + require.NoError(err) + + sender := &enginetest.SenderStub{ + SentAppGossip: make(chan []byte, 1), + } + vm := newAtomicTestVM() + vm.AtomicTxPullGossiper = gossip.NoOpGossiper{} + + require.NoError(vm.Initialize( + ctx, + snowCtx, + memdb.New(), + genesisBytes, + nil, + nil, + nil, + sender, + )) + require.NoError(vm.SetState(ctx, snow.NormalOp)) + + defer func() { + require.NoError(vm.Shutdown(ctx)) + }() + + // Issue a tx to the VM + utxo, err := addUTXO( + memory, + snowCtx, + ids.GenerateTestID(), + 0, + snowCtx.AVAXAssetID, + 100_000_000_000, + pk.Address(), + ) + require.NoError(err) + tx, err := atomic.NewImportTx(vm.Ctx, vm.CurrentRules(), vm.clock.Unix(), vm.Ctx.XChainID, address, vmtest.InitialBaseFee, secp256k1fx.NewKeychain(pk), []*avax.UTXO{utxo}) + require.NoError(err) + require.NoError(vm.AtomicMempool.AddLocalTx(tx)) + vm.AtomicTxPushGossiper.Add(tx) + + gossipedBytes := <-sender.SentAppGossip + require.Equal(byte(p2p.AtomicTxGossipHandlerID), gossipedBytes[0]) + + outboundGossipMsg := &sdk.PushGossip{} + require.NoError(proto.Unmarshal(gossipedBytes[1:], outboundGossipMsg)) + require.Len(outboundGossipMsg.Gossip, 1) + + marshaller := atomic.TxMarshaller{} + gossipedTx, err := marshaller.UnmarshalGossip(outboundGossipMsg.Gossip[0]) + require.NoError(err) + require.Equal(tx.ID(), gossipedTx.ID()) +} + +// Tests that a tx is gossiped when it is issued +func TestAtomicTxPushGossipInbound(t *testing.T) { + require := require.New(t) + ctx, cancel := utilstest.NewTestContext(t) + defer cancel() + + snowCtx := snowtest.Context(t, snowtest.CChainID) + snowCtx.AVAXAssetID = ids.GenerateTestID() + validatorState := utils.NewTestValidatorState() + snowCtx.ValidatorState = validatorState + memory := avalancheatomic.NewMemory(memdb.New()) + snowCtx.SharedMemory = memory.NewSharedMemory(snowCtx.ChainID) + + pk, err := secp256k1.NewPrivateKey() + require.NoError(err) + address := pk.EthAddress() + genesis := vmtest.NewPrefundedGenesis(100_000_000_000_000_000, address) + genesisBytes, err := genesis.MarshalJSON() + require.NoError(err) + + sender := &enginetest.Sender{} + vm := newAtomicTestVM() + vm.AtomicTxPullGossiper = gossip.NoOpGossiper{} + + require.NoError(vm.Initialize( + ctx, + snowCtx, + memdb.New(), + genesisBytes, + nil, + nil, + nil, + sender, + )) + require.NoError(vm.SetState(ctx, snow.NormalOp)) + + defer func() { + require.NoError(vm.Shutdown(ctx)) + }() + + // issue a tx to the vm + utxo, err := addUTXO( + memory, + snowCtx, + ids.GenerateTestID(), + 0, + snowCtx.AVAXAssetID, + 100_000_000_000, + pk.Address(), + ) + require.NoError(err) + tx, err := atomic.NewImportTx(vm.Ctx, vm.CurrentRules(), vm.clock.Unix(), vm.Ctx.XChainID, address, vmtest.InitialBaseFee, secp256k1fx.NewKeychain(pk), []*avax.UTXO{utxo}) + require.NoError(err) + require.NoError(vm.AtomicMempool.AddLocalTx(tx)) + + marshaller := atomic.TxMarshaller{} + gossipBytes, err := marshaller.MarshalGossip(tx) + require.NoError(err) + + inboundGossip := &sdk.PushGossip{ + Gossip: [][]byte{gossipBytes}, + } + inboundGossipBytes, err := proto.Marshal(inboundGossip) + require.NoError(err) + + inboundGossipMsg := append(binary.AppendUvarint(nil, p2p.AtomicTxGossipHandlerID), inboundGossipBytes...) + + require.NoError(vm.AppGossip(ctx, ids.EmptyNodeID, inboundGossipMsg)) + require.True(vm.AtomicMempool.Has(tx.ID())) +} diff --git a/plugin/evm/atomic/vm/tx_semantic_verifier.go b/plugin/evm/atomic/vm/tx_semantic_verifier.go index c47d20fbd8..7bdb4e2c47 100644 --- a/plugin/evm/atomic/vm/tx_semantic_verifier.go +++ b/plugin/evm/atomic/vm/tx_semantic_verifier.go @@ -32,7 +32,7 @@ var ( ) type BlockFetcher interface { - // GetExtendedBlock returns the VMBlock for the given ID or an error if the block is not found + // GetExtendedBlock returns the ExtendedBlock for the given ID or an error if the block is not found GetExtendedBlock(context.Context, ids.ID) (extension.ExtendedBlock, error) // LastAcceptedExtendedBlock returns the last accepted VM block LastAcceptedExtendedBlock() extension.ExtendedBlock diff --git a/plugin/evm/tx_test.go b/plugin/evm/atomic/vm/tx_test.go similarity index 57% rename from plugin/evm/tx_test.go rename to plugin/evm/atomic/vm/tx_test.go index 5df800d9fa..404edae2da 100644 --- a/plugin/evm/tx_test.go +++ b/plugin/evm/atomic/vm/tx_test.go @@ -1,25 +1,18 @@ // Copyright (C) 2019-2025, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. -package evm +package vm import ( - "context" "math/big" - "strings" "testing" - "github.com/ava-labs/coreth/plugin/evm/atomic" - atomicvm "github.com/ava-labs/coreth/plugin/evm/atomic/vm" - - commonEng "github.com/ava-labs/avalanchego/snow/engine/common" - "github.com/stretchr/testify/require" "github.com/ava-labs/libevm/common" - "github.com/ava-labs/coreth/core/extstate" "github.com/ava-labs/coreth/params/extras" + "github.com/ava-labs/coreth/plugin/evm/atomic" "github.com/ava-labs/coreth/utils" avalancheatomic "github.com/ava-labs/avalanchego/chains/atomic" @@ -68,7 +61,7 @@ func TestCalculateDynamicFee(t *testing.T) { type atomicTxVerifyTest struct { ctx *snow.Context generate func(t *testing.T) atomic.UnsignedAtomicTx - rules extras.Rules + rules *extras.Rules expectedErr string } @@ -76,7 +69,7 @@ type atomicTxVerifyTest struct { func executeTxVerifyTest(t *testing.T, test atomicTxVerifyTest) { require := require.New(t) atomicTx := test.generate(t) - err := atomicTx.Verify(test.ctx, test.rules) + err := atomicTx.Verify(test.ctx, *test.rules) if len(test.expectedErr) == 0 { require.NoError(err) } else { @@ -86,14 +79,14 @@ func executeTxVerifyTest(t *testing.T, test atomicTxVerifyTest) { type atomicTxTest struct { // setup returns the atomic transaction for the test - setup func(t *testing.T, vm *atomicvm.VM, sharedMemory *avalancheatomic.Memory) *atomic.Tx + setup func(t *testing.T, vm *VM, sharedMemory *avalancheatomic.Memory) *atomic.Tx // define a string that should be contained in the error message if the tx fails verification // at some point. If the strings are empty, then the tx should pass verification at the // respective step. semanticVerifyErr, evmStateTransferErr, acceptErr string // checkState is called iff building and verifying a block containing the transaction is successful. Verifies // the state of the VM following the block's acceptance. - checkState func(t *testing.T, vm *atomicvm.VM) + checkState func(t *testing.T, vm *VM) // Whether or not the VM should be considered to still be bootstrapping bootstrapping bool @@ -102,99 +95,6 @@ type atomicTxTest struct { fork upgradetest.Fork } -func executeTxTest(t *testing.T, test atomicTxTest) { - tvm := newVM(t, testVMConfig{ - isSyncing: test.bootstrapping, - fork: &test.fork, - }) - rules := tvm.vm.currentRules() - - tx := test.setup(t, tvm.atomicVM, tvm.atomicMemory) - - var baseFee *big.Int - // If ApricotPhase3 is active, use the initial base fee for the atomic transaction - switch { - case rules.IsApricotPhase3: - baseFee = initialBaseFee - } - - lastAcceptedBlock := tvm.vm.LastAcceptedExtendedBlock() - backend := atomicvm.NewVerifierBackend(tvm.atomicVM, rules) - if err := backend.SemanticVerify(tx, lastAcceptedBlock, baseFee); len(test.semanticVerifyErr) == 0 && err != nil { - t.Fatalf("SemanticVerify failed unexpectedly due to: %s", err) - } else if len(test.semanticVerifyErr) != 0 { - if err == nil { - t.Fatalf("SemanticVerify unexpectedly returned a nil error. Expected err: %s", test.semanticVerifyErr) - } - if !strings.Contains(err.Error(), test.semanticVerifyErr) { - t.Fatalf("Expected SemanticVerify to fail due to %s, but failed with: %s", test.semanticVerifyErr, err) - } - // If SemanticVerify failed for the expected reason, return early - return - } - - // Retrieve dummy state to test that EVMStateTransfer works correctly - statedb, err := tvm.vm.blockChain.StateAt(lastAcceptedBlock.GetEthBlock().Root()) - if err != nil { - t.Fatal(err) - } - wrappedStateDB := extstate.New(statedb) - if err := tx.UnsignedAtomicTx.EVMStateTransfer(tvm.vm.ctx, wrappedStateDB); len(test.evmStateTransferErr) == 0 && err != nil { - t.Fatalf("EVMStateTransfer failed unexpectedly due to: %s", err) - } else if len(test.evmStateTransferErr) != 0 { - if err == nil { - t.Fatalf("EVMStateTransfer unexpectedly returned a nil error. Expected err: %s", test.evmStateTransferErr) - } - if !strings.Contains(err.Error(), test.evmStateTransferErr) { - t.Fatalf("Expected SemanticVerify to fail due to %s, but failed with: %s", test.evmStateTransferErr, err) - } - // If EVMStateTransfer failed for the expected reason, return early - return - } - - if test.bootstrapping { - // If this test simulates processing txs during bootstrapping (where some verification is skipped), - // initialize the block building goroutines normally initialized in SetState(snow.NormalOps). - // This ensures that the VM can build a block correctly during the test. - if err := tvm.vm.initBlockBuilding(); err != nil { - t.Fatal(err) - } - } - - if err := tvm.atomicVM.AtomicMempool.AddLocalTx(tx); err != nil { - t.Fatal(err) - } - - require.Equal(t, commonEng.PendingTxs, tvm.WaitForEvent(context.Background())) - - // If we've reached this point, we expect to be able to build and verify the block without any errors - blk, err := tvm.vm.BuildBlock(context.Background()) - if err != nil { - t.Fatal(err) - } - - if err := blk.Verify(context.Background()); err != nil { - t.Fatal(err) - } - - if err := blk.Accept(context.Background()); len(test.acceptErr) == 0 && err != nil { - t.Fatalf("Accept failed unexpectedly due to: %s", err) - } else if len(test.acceptErr) != 0 { - if err == nil { - t.Fatalf("Accept unexpectedly returned a nil error. Expected err: %s", test.acceptErr) - } - if !strings.Contains(err.Error(), test.acceptErr) { - t.Fatalf("Expected Accept to fail due to %s, but failed with: %s", test.acceptErr, err) - } - // If Accept failed for the expected reason, return early - return - } - - if test.checkState != nil { - test.checkState(t, tvm.atomicVM) - } -} - func TestEVMOutputCompare(t *testing.T) { type test struct { name string diff --git a/plugin/evm/atomic/vm/vm.go b/plugin/evm/atomic/vm/vm.go index efbfb8313c..f836a01ef8 100644 --- a/plugin/evm/atomic/vm/vm.go +++ b/plugin/evm/atomic/vm/vm.go @@ -87,19 +87,19 @@ type VM struct { baseCodec codec.Registry AtomicMempool *txpool.Mempool - // [atomicTxRepository] maintains two indexes on accepted atomic txs. + // AtomicTxRepository maintains two indexes on accepted atomic txs. // - txID to accepted atomic tx // - block height to list of atomic txs accepted on block at that height // TODO: unexport these fields AtomicTxRepository *atomicstate.AtomicRepository - // [atomicBackend] abstracts verification and processing of atomic transactions + // AtomicBackend abstracts verification and processing of atomic transactions AtomicBackend *atomicstate.AtomicBackend atomicTxGossipHandler p2p.Handler AtomicTxPushGossiper *avalanchegossip.PushGossiper[*atomic.Tx] AtomicTxPullGossiper avalanchegossip.Gossiper - // [cancel] may be nil until [snow.NormalOp] starts + // cancel may be nil until [snow.NormalOp] starts cancel context.CancelFunc shutdownWg sync.WaitGroup @@ -381,7 +381,7 @@ func (vm *VM) verifyTxAtTip(tx *atomic.Tx) error { if gasUsed > maxAtomicTxMempoolGas { return fmt.Errorf("tx gas usage (%d) exceeds maximum allowed mempool gas usage (%d)", gasUsed, maxAtomicTxMempoolGas) } - blockchain := vm.InnerVM.Blockchain() + blockchain := vm.InnerVM.Ethereum().BlockChain() // Note: we fetch the current block and then the state at that block instead of the current state directly // since we need the header of the current block below. preferredBlock := blockchain.CurrentBlock() @@ -403,7 +403,7 @@ func (vm *VM) verifyTxAtTip(tx *atomic.Tx) error { } // We don’t need to revert the state here in case verifyTx errors, because - // [preferredState] is thrown away either way. + // preferredState is thrown away either way. return vm.verifyTx(tx, parentHeader.Hash(), nextBaseFee, preferredState, *extraRules) } @@ -430,7 +430,7 @@ func (vm *VM) verifyTx(tx *atomic.Tx, parentHash common.Hash, baseFee *big.Int, // using [rules] as the current rule set. func (vm *VM) verifyTxs(txs []*atomic.Tx, parentHash common.Hash, baseFee *big.Int, height uint64, rules extras.Rules) error { // Ensure that the parent was verified and inserted correctly. - if !vm.InnerVM.Blockchain().HasBlock(parentHash, height-1) { + if !vm.InnerVM.Ethereum().BlockChain().HasBlock(parentHash, height-1) { return errRejectedParent } @@ -756,7 +756,7 @@ func (vm *VM) rules(number *big.Int, time uint64) extras.Rules { // CurrentRules returns the chain rules for the current block. func (vm *VM) CurrentRules() extras.Rules { - header := vm.InnerVM.Blockchain().CurrentHeader() + header := vm.InnerVM.Ethereum().BlockChain().CurrentHeader() return vm.rules(header.Number, header.Time) } @@ -809,7 +809,7 @@ func (vm *VM) NewExportTx( baseFee *big.Int, // fee to use post-AP3 keys []*secp256k1.PrivateKey, // Pay the fee and provide the tokens ) (*atomic.Tx, error) { - statedb, err := vm.InnerVM.Blockchain().State() + statedb, err := vm.Ethereum().BlockChain().State() if err != nil { return nil, err } diff --git a/plugin/evm/atomic/vm/vm_test.go b/plugin/evm/atomic/vm/vm_test.go new file mode 100644 index 0000000000..d9fa556e7b --- /dev/null +++ b/plugin/evm/atomic/vm/vm_test.go @@ -0,0 +1,2162 @@ +// Copyright (C) 2019-2025, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package vm + +import ( + "context" + "crypto/ecdsa" + "errors" + "fmt" + "math/big" + "strings" + "sync" + "testing" + "time" + + avalancheatomic "github.com/ava-labs/avalanchego/chains/atomic" + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/snow" + commonEng "github.com/ava-labs/avalanchego/snow/engine/common" + "github.com/ava-labs/avalanchego/upgrade/upgradetest" + "github.com/ava-labs/avalanchego/utils/crypto/secp256k1" + "github.com/ava-labs/avalanchego/utils/hashing" + "github.com/ava-labs/avalanchego/utils/set" + "github.com/ava-labs/avalanchego/utils/units" + "github.com/ava-labs/avalanchego/vms/components/avax" + "github.com/ava-labs/avalanchego/vms/components/chain" + "github.com/ava-labs/avalanchego/vms/secp256k1fx" + + "github.com/ava-labs/coreth/core" + "github.com/ava-labs/coreth/core/extstate" + "github.com/ava-labs/coreth/params" + "github.com/ava-labs/coreth/plugin/evm" + "github.com/ava-labs/coreth/plugin/evm/atomic" + "github.com/ava-labs/coreth/plugin/evm/atomic/txpool" + "github.com/ava-labs/coreth/plugin/evm/customtypes" + "github.com/ava-labs/coreth/plugin/evm/extension" + "github.com/ava-labs/coreth/plugin/evm/header" + "github.com/ava-labs/coreth/plugin/evm/upgrade/ap0" + "github.com/ava-labs/coreth/plugin/evm/upgrade/ap1" + "github.com/ava-labs/coreth/plugin/evm/vmtest" + "github.com/ava-labs/coreth/utils/utilstest" + + "github.com/ava-labs/libevm/common" + "github.com/ava-labs/libevm/core/types" + "github.com/ava-labs/libevm/rlp" + "github.com/ava-labs/libevm/trie" + + "github.com/stretchr/testify/require" +) + +func newAtomicTestVM() *VM { + return WrapVM(&evm.VM{}) +} + +func (vm *VM) newImportTx( + chainID ids.ID, // chain to import from + to common.Address, // Address of recipient + baseFee *big.Int, // fee to use post-AP3 + keys []*secp256k1.PrivateKey, // Keys to import the funds +) (*atomic.Tx, error) { + kc := secp256k1fx.NewKeychain() + for _, key := range keys { + kc.Add(key) + } + + atomicUTXOs, _, _, err := avax.GetAtomicUTXOs(vm.Ctx.SharedMemory, atomic.Codec, chainID, kc.Addresses(), ids.ShortEmpty, ids.Empty, maxUTXOsToFetch) + if err != nil { + return nil, fmt.Errorf("problem retrieving atomic UTXOs: %w", err) + } + + return atomic.NewImportTx(vm.Ctx, vm.CurrentRules(), vm.clock.Unix(), chainID, to, baseFee, kc, atomicUTXOs) +} + +func addUTXO(sharedMemory *avalancheatomic.Memory, ctx *snow.Context, txID ids.ID, index uint32, assetID ids.ID, amount uint64, addr ids.ShortID) (*avax.UTXO, error) { + utxo := &avax.UTXO{ + UTXOID: avax.UTXOID{ + TxID: txID, + OutputIndex: index, + }, + Asset: avax.Asset{ID: assetID}, + Out: &secp256k1fx.TransferOutput{ + Amt: amount, + OutputOwners: secp256k1fx.OutputOwners{ + Threshold: 1, + Addrs: []ids.ShortID{addr}, + }, + }, + } + utxoBytes, err := atomic.Codec.Marshal(atomic.CodecVersion, utxo) + if err != nil { + return nil, err + } + + xChainSharedMemory := sharedMemory.NewSharedMemory(ctx.XChainID) + inputID := utxo.InputID() + if err := xChainSharedMemory.Apply(map[ids.ID]*avalancheatomic.Requests{ctx.ChainID: {PutRequests: []*avalancheatomic.Element{{ + Key: inputID[:], + Value: utxoBytes, + Traits: [][]byte{ + addr.Bytes(), + }, + }}}}); err != nil { + return nil, err + } + + return utxo, nil +} + +func addUTXOs(sharedMemory *avalancheatomic.Memory, ctx *snow.Context, utxos map[ids.ShortID]uint64) error { + for addr, avaxAmount := range utxos { + txID, err := ids.ToID(hashing.ComputeHash256(addr.Bytes())) + if err != nil { + return fmt.Errorf("Failed to generate txID from addr: %s", err) + } + if _, err := addUTXO(sharedMemory, ctx, txID, 0, ctx.AVAXAssetID, avaxAmount, addr); err != nil { + return fmt.Errorf("Failed to add UTXO to shared memory: %s", err) + } + } + return nil +} + +func TestImportMissingUTXOs(t *testing.T) { + for _, scheme := range vmtest.Schemes { + t.Run(scheme, func(t *testing.T) { + testImportMissingUTXOs(t, scheme) + }) + } +} + +func testImportMissingUTXOs(t *testing.T, scheme string) { + // make a VM with a shared memory that has an importable UTXO to build a block + importAmount := uint64(50000000) + fork := upgradetest.ApricotPhase2 + vm1 := newAtomicTestVM() + tvm := vmtest.SetupTestVM(t, vm1, vmtest.TestVMConfig{ + Fork: &fork, + Scheme: scheme, + }) + require.NoError(t, addUTXOs(tvm.AtomicMemory, vm1.Ctx, map[ids.ShortID]uint64{ + vmtest.TestShortIDAddrs[0]: importAmount, + })) + defer func() { + require.NoError(t, vm1.Shutdown(context.Background())) + }() + + importTx, err := vm1.newImportTx(vm1.Ctx.XChainID, vmtest.TestEthAddrs[0], vmtest.InitialBaseFee, vmtest.TestKeys[0:1]) + require.NoError(t, err) + require.NoError(t, vm1.AtomicMempool.AddLocalTx(importTx)) + msg, err := vm1.WaitForEvent(context.Background()) + require.NoError(t, err) + require.Equal(t, commonEng.PendingTxs, msg) + blk, err := vm1.BuildBlock(context.Background()) + require.NoError(t, err) + + // make another VM which is missing the UTXO in shared memory + vm2 := newAtomicTestVM() + tvm = vmtest.SetupTestVM(t, vm2, vmtest.TestVMConfig{ + Fork: &fork, + Scheme: scheme, + }) + defer func() { + require.NoError(t, vm2.Shutdown(context.Background())) + }() + + vm2Blk, err := vm2.ParseBlock(context.Background(), blk.Bytes()) + require.NoError(t, err) + err = vm2Blk.Verify(context.Background()) + require.ErrorIs(t, err, ErrMissingUTXOs) + + // This should not result in a bad block since the missing UTXO should + // prevent InsertBlockManual from being called. + badBlocks, _ := vm2.Ethereum().BlockChain().BadBlocks() + require.Len(t, badBlocks, 0) +} + +// Simple test to ensure we can issue an import transaction followed by an export transaction +// and they will be indexed correctly when accepted. +func TestIssueAtomicTxs(t *testing.T) { + for _, scheme := range vmtest.Schemes { + t.Run(scheme, func(t *testing.T) { + testIssueAtomicTxs(t, scheme) + }) + } +} + +func testIssueAtomicTxs(t *testing.T, scheme string) { + importAmount := uint64(50000000) + vm := newAtomicTestVM() + fork := upgradetest.ApricotPhase2 + tvm := vmtest.SetupTestVM(t, vm, vmtest.TestVMConfig{ + Fork: &fork, + Scheme: scheme, + }) + utxos := map[ids.ShortID]uint64{ + vmtest.TestShortIDAddrs[0]: importAmount, + } + addUTXOs(tvm.AtomicMemory, vm.Ctx, utxos) + defer func() { + if err := vm.Shutdown(context.Background()); err != nil { + t.Fatal(err) + } + }() + + importTx, err := vm.newImportTx(vm.Ctx.XChainID, vmtest.TestEthAddrs[0], vmtest.InitialBaseFee, vmtest.TestKeys[0:1]) + if err != nil { + t.Fatal(err) + } + + if err := vm.AtomicMempool.AddLocalTx(importTx); err != nil { + t.Fatal(err) + } + + msg, err := vm.WaitForEvent(context.Background()) + require.NoError(t, err) + require.Equal(t, commonEng.PendingTxs, msg) + + blk, err := vm.BuildBlock(context.Background()) + if err != nil { + t.Fatal(err) + } + + if err := blk.Verify(context.Background()); err != nil { + t.Fatal(err) + } + + if err := vm.SetPreference(context.Background(), blk.ID()); err != nil { + t.Fatal(err) + } + + if err := blk.Accept(context.Background()); err != nil { + t.Fatal(err) + } + + if lastAcceptedID, err := vm.LastAccepted(context.Background()); err != nil { + t.Fatal(err) + } else if lastAcceptedID != blk.ID() { + t.Fatalf("Expected last accepted blockID to be the accepted block: %s, but found %s", blk.ID(), lastAcceptedID) + } + vm.Ethereum().BlockChain().DrainAcceptorQueue() + + state, err := vm.Ethereum().BlockChain().State() + if err != nil { + t.Fatal(err) + } + + wrappedState := extstate.New(state) + exportTx, err := atomic.NewExportTx(vm.Ctx, vm.CurrentRules(), wrappedState, vm.Ctx.AVAXAssetID, importAmount-(2*ap0.AtomicTxFee), vm.Ctx.XChainID, vmtest.TestShortIDAddrs[0], vmtest.InitialBaseFee, vmtest.TestKeys[0:1]) + if err != nil { + t.Fatal(err) + } + + if err := vm.AtomicMempool.AddLocalTx(exportTx); err != nil { + t.Fatal(err) + } + + msg, err = vm.WaitForEvent(context.Background()) + require.NoError(t, err) + require.Equal(t, commonEng.PendingTxs, msg) + + blk2, err := vm.BuildBlock(context.Background()) + if err != nil { + t.Fatal(err) + } + + if err := blk2.Verify(context.Background()); err != nil { + t.Fatal(err) + } + + if err := blk2.Accept(context.Background()); err != nil { + t.Fatal(err) + } + + if lastAcceptedID, err := vm.LastAccepted(context.Background()); err != nil { + t.Fatal(err) + } else if lastAcceptedID != blk2.ID() { + t.Fatalf("Expected last accepted blockID to be the accepted block: %s, but found %s", blk2.ID(), lastAcceptedID) + } + + // Check that both atomic transactions were indexed as expected. + indexedImportTx, status, height, err := vm.GetAtomicTx(importTx.ID()) + require.NoError(t, err) + require.Equal(t, atomic.Accepted, status) + require.Equal(t, uint64(1), height, "expected height of indexed import tx to be 1") + require.Equal(t, indexedImportTx.ID(), importTx.ID(), "expected ID of indexed import tx to match original txID") + + indexedExportTx, status, height, err := vm.GetAtomicTx(exportTx.ID()) + require.NoError(t, err) + require.Equal(t, atomic.Accepted, status) + require.Equal(t, uint64(2), height, "expected height of indexed export tx to be 2") + require.Equal(t, indexedExportTx.ID(), exportTx.ID(), "expected ID of indexed import tx to match original txID") +} + +func testConflictingImportTxs(t *testing.T, fork upgradetest.Fork, scheme string) { + importAmount := uint64(10000000) + vm := newAtomicTestVM() + tvm := vmtest.SetupTestVM(t, vm, vmtest.TestVMConfig{ + Fork: &fork, + Scheme: scheme, + }) + require.NoError(t, addUTXOs(tvm.AtomicMemory, vm.Ctx, map[ids.ShortID]uint64{ + vmtest.TestShortIDAddrs[0]: importAmount, + vmtest.TestShortIDAddrs[1]: importAmount, + vmtest.TestShortIDAddrs[2]: importAmount, + })) + + defer func() { + if err := vm.Shutdown(context.Background()); err != nil { + t.Fatal(err) + } + }() + + importTxs := make([]*atomic.Tx, 0, 3) + conflictTxs := make([]*atomic.Tx, 0, 3) + for i, key := range vmtest.TestKeys { + importTx, err := vm.newImportTx(vm.Ctx.XChainID, vmtest.TestEthAddrs[i], vmtest.InitialBaseFee, []*secp256k1.PrivateKey{key}) + if err != nil { + t.Fatal(err) + } + importTxs = append(importTxs, importTx) + + conflictAddr := vmtest.TestEthAddrs[(i+1)%len(vmtest.TestEthAddrs)] + conflictTx, err := vm.newImportTx(vm.Ctx.XChainID, conflictAddr, vmtest.InitialBaseFee, []*secp256k1.PrivateKey{key}) + if err != nil { + t.Fatal(err) + } + conflictTxs = append(conflictTxs, conflictTx) + } + + expectedParentBlkID, err := vm.LastAccepted(context.Background()) + if err != nil { + t.Fatal(err) + } + for _, tx := range importTxs[:2] { + if err := vm.AtomicMempool.AddLocalTx(tx); err != nil { + t.Fatal(err) + } + + msg, err := vm.WaitForEvent(context.Background()) + require.NoError(t, err) + require.Equal(t, commonEng.PendingTxs, msg) + + vm.clock.Set(vm.clock.Time().Add(2 * time.Second)) + blk, err := vm.BuildBlock(context.Background()) + if err != nil { + t.Fatal(err) + } + + if err := blk.Verify(context.Background()); err != nil { + t.Fatal(err) + } + + if parentID := blk.Parent(); parentID != expectedParentBlkID { + t.Fatalf("Expected parent to have blockID %s, but found %s", expectedParentBlkID, parentID) + } + + expectedParentBlkID = blk.ID() + if err := vm.SetPreference(context.Background(), blk.ID()); err != nil { + t.Fatal(err) + } + } + + // Check that for each conflict tx (whose conflict is in the chain ancestry) + // the VM returns an error when it attempts to issue the conflict into the mempool + // and when it attempts to build a block with the conflict force added to the mempool. + for i, tx := range conflictTxs[:2] { + if err := vm.AtomicMempool.AddLocalTx(tx); err == nil { + t.Fatal("Expected issueTx to fail due to conflicting transaction") + } + // Force issue transaction directly to the mempool + if err := vm.AtomicMempool.ForceAddTx(tx); err != nil { + t.Fatal(err) + } + msg, err := vm.WaitForEvent(context.Background()) + require.NoError(t, err) + require.Equal(t, commonEng.PendingTxs, msg) + + vm.clock.Set(vm.clock.Time().Add(2 * time.Second)) + _, err = vm.BuildBlock(context.Background()) + // The new block is verified in BuildBlock, so + // BuildBlock should fail due to an attempt to + // double spend an atomic UTXO. + if err == nil { + t.Fatalf("Block verification should have failed in BuildBlock %d due to double spending atomic UTXO", i) + } + } + + // Generate one more valid block so that we can copy the header to create an invalid block + // with modified extra data. This new block will be invalid for more than one reason (invalid merkle root) + // so we check to make sure that the expected error is returned from block verification. + if err := vm.AtomicMempool.AddLocalTx(importTxs[2]); err != nil { + t.Fatal(err) + } + msg, err := vm.WaitForEvent(context.Background()) + require.NoError(t, err) + require.Equal(t, commonEng.PendingTxs, msg) + vm.clock.Set(vm.clock.Time().Add(2 * time.Second)) + + validBlock, err := vm.BuildBlock(context.Background()) + if err != nil { + t.Fatal(err) + } + + if err := validBlock.Verify(context.Background()); err != nil { + t.Fatal(err) + } + + validEthBlock := validBlock.(*chain.BlockWrapper).Block.(extension.ExtendedBlock).GetEthBlock() + + rules := vm.CurrentRules() + var extraData []byte + switch { + case rules.IsApricotPhase5: + extraData, err = atomic.Codec.Marshal(atomic.CodecVersion, []*atomic.Tx{conflictTxs[1]}) + default: + extraData, err = atomic.Codec.Marshal(atomic.CodecVersion, conflictTxs[1]) + } + if err != nil { + t.Fatal(err) + } + + conflictingAtomicTxBlock := customtypes.NewBlockWithExtData( + types.CopyHeader(validEthBlock.Header()), + nil, + nil, + nil, + new(trie.Trie), + extraData, + true, + ) + + blockBytes, err := rlp.EncodeToBytes(conflictingAtomicTxBlock) + if err != nil { + t.Fatal(err) + } + + parsedBlock, err := vm.ParseBlock(context.Background(), blockBytes) + if err != nil { + t.Fatal(err) + } + + if err := parsedBlock.Verify(context.Background()); !errors.Is(err, ErrConflictingAtomicInputs) { + t.Fatalf("Expected to fail with err: %s, but found err: %s", ErrConflictingAtomicInputs, err) + } + + if !rules.IsApricotPhase5 { + return + } + + extraData, err = atomic.Codec.Marshal(atomic.CodecVersion, []*atomic.Tx{importTxs[2], conflictTxs[2]}) + if err != nil { + t.Fatal(err) + } + + header := types.CopyHeader(validEthBlock.Header()) + headerExtra := customtypes.GetHeaderExtra(header) + headerExtra.ExtDataGasUsed.Mul(common.Big2, headerExtra.ExtDataGasUsed) + + internalConflictBlock := customtypes.NewBlockWithExtData( + header, + nil, + nil, + nil, + new(trie.Trie), + extraData, + true, + ) + + blockBytes, err = rlp.EncodeToBytes(internalConflictBlock) + if err != nil { + t.Fatal(err) + } + + parsedBlock, err = vm.ParseBlock(context.Background(), blockBytes) + if err != nil { + t.Fatal(err) + } + + if err := parsedBlock.Verify(context.Background()); !errors.Is(err, ErrConflictingAtomicInputs) { + t.Fatalf("Expected to fail with err: %s, but found err: %s", ErrConflictingAtomicInputs, err) + } +} + +func TestReissueAtomicTxHigherGasPrice(t *testing.T) { + for _, scheme := range vmtest.Schemes { + t.Run(scheme, func(t *testing.T) { + testReissueAtomicTxHigherGasPrice(t, scheme) + }) + } +} + +func testReissueAtomicTxHigherGasPrice(t *testing.T, scheme string) { + kc := secp256k1fx.NewKeychain(vmtest.TestKeys...) + + tests := map[string]func(t *testing.T, vm *VM, sharedMemory *avalancheatomic.Memory) (issued []*atomic.Tx, discarded []*atomic.Tx){ + "single UTXO override": func(t *testing.T, vm *VM, sharedMemory *avalancheatomic.Memory) (issued []*atomic.Tx, evicted []*atomic.Tx) { + utxo, err := addUTXO(sharedMemory, vm.Ctx, ids.GenerateTestID(), 0, vm.Ctx.AVAXAssetID, units.Avax, vmtest.TestShortIDAddrs[0]) + if err != nil { + t.Fatal(err) + } + tx1, err := atomic.NewImportTx(vm.Ctx, vm.CurrentRules(), vm.clock.Unix(), vm.Ctx.XChainID, vmtest.TestEthAddrs[0], vmtest.InitialBaseFee, kc, []*avax.UTXO{utxo}) + if err != nil { + t.Fatal(err) + } + tx2, err := atomic.NewImportTx(vm.Ctx, vm.CurrentRules(), vm.clock.Unix(), vm.Ctx.XChainID, vmtest.TestEthAddrs[0], new(big.Int).Mul(common.Big2, vmtest.InitialBaseFee), kc, []*avax.UTXO{utxo}) + if err != nil { + t.Fatal(err) + } + + if err := vm.AtomicMempool.AddLocalTx(tx1); err != nil { + t.Fatal(err) + } + if err := vm.AtomicMempool.AddLocalTx(tx2); err != nil { + t.Fatal(err) + } + + return []*atomic.Tx{tx2}, []*atomic.Tx{tx1} + }, + "one of two UTXOs overrides": func(t *testing.T, vm *VM, sharedMemory *avalancheatomic.Memory) (issued []*atomic.Tx, evicted []*atomic.Tx) { + utxo1, err := addUTXO(sharedMemory, vm.Ctx, ids.GenerateTestID(), 0, vm.Ctx.AVAXAssetID, units.Avax, vmtest.TestShortIDAddrs[0]) + if err != nil { + t.Fatal(err) + } + utxo2, err := addUTXO(sharedMemory, vm.Ctx, ids.GenerateTestID(), 0, vm.Ctx.AVAXAssetID, units.Avax, vmtest.TestShortIDAddrs[0]) + if err != nil { + t.Fatal(err) + } + tx1, err := atomic.NewImportTx(vm.Ctx, vm.CurrentRules(), vm.clock.Unix(), vm.Ctx.XChainID, vmtest.TestEthAddrs[0], vmtest.InitialBaseFee, kc, []*avax.UTXO{utxo1, utxo2}) + if err != nil { + t.Fatal(err) + } + tx2, err := atomic.NewImportTx(vm.Ctx, vm.CurrentRules(), vm.clock.Unix(), vm.Ctx.XChainID, vmtest.TestEthAddrs[0], new(big.Int).Mul(common.Big2, vmtest.InitialBaseFee), kc, []*avax.UTXO{utxo1}) + if err != nil { + t.Fatal(err) + } + + if err := vm.AtomicMempool.AddLocalTx(tx1); err != nil { + t.Fatal(err) + } + if err := vm.AtomicMempool.AddLocalTx(tx2); err != nil { + t.Fatal(err) + } + + return []*atomic.Tx{tx2}, []*atomic.Tx{tx1} + }, + "hola": func(t *testing.T, vm *VM, sharedMemory *avalancheatomic.Memory) (issued []*atomic.Tx, evicted []*atomic.Tx) { + utxo1, err := addUTXO(sharedMemory, vm.Ctx, ids.GenerateTestID(), 0, vm.Ctx.AVAXAssetID, units.Avax, vmtest.TestShortIDAddrs[0]) + if err != nil { + t.Fatal(err) + } + utxo2, err := addUTXO(sharedMemory, vm.Ctx, ids.GenerateTestID(), 0, vm.Ctx.AVAXAssetID, units.Avax, vmtest.TestShortIDAddrs[0]) + if err != nil { + t.Fatal(err) + } + + importTx1, err := atomic.NewImportTx(vm.Ctx, vm.CurrentRules(), vm.clock.Unix(), vm.Ctx.XChainID, vmtest.TestEthAddrs[0], vmtest.InitialBaseFee, kc, []*avax.UTXO{utxo1}) + if err != nil { + t.Fatal(err) + } + + importTx2, err := atomic.NewImportTx(vm.Ctx, vm.CurrentRules(), vm.clock.Unix(), vm.Ctx.XChainID, vmtest.TestEthAddrs[0], new(big.Int).Mul(big.NewInt(3), vmtest.InitialBaseFee), kc, []*avax.UTXO{utxo2}) + if err != nil { + t.Fatal(err) + } + + reissuanceTx1, err := atomic.NewImportTx(vm.Ctx, vm.CurrentRules(), vm.clock.Unix(), vm.Ctx.XChainID, vmtest.TestEthAddrs[0], new(big.Int).Mul(big.NewInt(2), vmtest.InitialBaseFee), kc, []*avax.UTXO{utxo1, utxo2}) + if err != nil { + t.Fatal(err) + } + if err := vm.AtomicMempool.AddLocalTx(importTx1); err != nil { + t.Fatal(err) + } + + if err := vm.AtomicMempool.AddLocalTx(importTx2); err != nil { + t.Fatal(err) + } + + if err := vm.AtomicMempool.AddLocalTx(reissuanceTx1); !errors.Is(err, txpool.ErrConflict) { + t.Fatalf("Expected to fail with err: %s, but found err: %s", txpool.ErrConflict, err) + } + + require.True(t, vm.AtomicMempool.Has(importTx1.ID())) + require.True(t, vm.AtomicMempool.Has(importTx2.ID())) + require.False(t, vm.AtomicMempool.Has(reissuanceTx1.ID())) + + reissuanceTx2, err := atomic.NewImportTx(vm.Ctx, vm.CurrentRules(), vm.clock.Unix(), vm.Ctx.XChainID, vmtest.TestEthAddrs[0], new(big.Int).Mul(big.NewInt(4), vmtest.InitialBaseFee), kc, []*avax.UTXO{utxo1, utxo2}) + if err != nil { + t.Fatal(err) + } + if err := vm.AtomicMempool.AddLocalTx(reissuanceTx2); err != nil { + t.Fatal(err) + } + + return []*atomic.Tx{reissuanceTx2}, []*atomic.Tx{importTx1, importTx2} + }, + } + for name, issueTxs := range tests { + t.Run(name, func(t *testing.T) { + fork := upgradetest.ApricotPhase5 + vm := newAtomicTestVM() + tvm := vmtest.SetupTestVM(t, vm, vmtest.TestVMConfig{ + Fork: &fork, + Scheme: scheme, + ConfigJSON: `{"pruning-enabled":true}`, + }) + issuedTxs, evictedTxs := issueTxs(t, vm, tvm.AtomicMemory) + + for i, tx := range issuedTxs { + _, issued := vm.AtomicMempool.GetPendingTx(tx.ID()) + require.True(t, issued, "expected issued tx at index %d to be issued", i) + } + + for i, tx := range evictedTxs { + _, discarded, _ := vm.AtomicMempool.GetTx(tx.ID()) + require.True(t, discarded, "expected discarded tx at index %d to be discarded", i) + } + }) + } +} + +func TestConflictingImportTxsAcrossBlocks(t *testing.T) { + for _, fork := range []upgradetest.Fork{ + upgradetest.ApricotPhase1, + upgradetest.ApricotPhase2, + upgradetest.ApricotPhase3, + upgradetest.ApricotPhase4, + upgradetest.ApricotPhase5, + } { + t.Run(fork.String(), func(t *testing.T) { + for _, scheme := range vmtest.Schemes { + t.Run(scheme, func(t *testing.T) { + testConflictingImportTxs(t, fork, scheme) + }) + } + }) + } +} + +func TestConflictingTransitiveAncestryWithGap(t *testing.T) { + for _, scheme := range vmtest.Schemes { + t.Run(scheme, func(t *testing.T) { + testConflictingTransitiveAncestryWithGap(t, scheme) + }) + } +} + +func testConflictingTransitiveAncestryWithGap(t *testing.T, scheme string) { + key := utilstest.NewKey(t) + + key0 := vmtest.TestKeys[0] + addr0 := key0.Address() + + key1 := vmtest.TestKeys[1] + addr1 := key1.Address() + + importAmount := uint64(1000000000) + fork := upgradetest.NoUpgrades + vm := newAtomicTestVM() + tvm := vmtest.SetupTestVM(t, vm, vmtest.TestVMConfig{ + Fork: &fork, + Scheme: scheme, + }) + require.NoError(t, addUTXOs(tvm.AtomicMemory, vm.Ctx, map[ids.ShortID]uint64{ + addr0: importAmount, + addr1: importAmount, + })) + + defer func() { + if err := vm.Shutdown(context.Background()); err != nil { + t.Fatal(err) + } + }() + + newTxPoolHeadChan := make(chan core.NewTxPoolReorgEvent, 1) + vm.Ethereum().TxPool().SubscribeNewReorgEvent(newTxPoolHeadChan) + + importTx0A, err := vm.newImportTx(vm.Ctx.XChainID, key.Address, vmtest.InitialBaseFee, []*secp256k1.PrivateKey{key0}) + if err != nil { + t.Fatal(err) + } + // Create a conflicting transaction + importTx0B, err := vm.newImportTx(vm.Ctx.XChainID, vmtest.TestEthAddrs[2], vmtest.InitialBaseFee, []*secp256k1.PrivateKey{key0}) + if err != nil { + t.Fatal(err) + } + + if err := vm.AtomicMempool.AddLocalTx(importTx0A); err != nil { + t.Fatalf("Failed to issue importTx0A: %s", err) + } + + msg, err := vm.WaitForEvent(context.Background()) + require.NoError(t, err) + require.Equal(t, commonEng.PendingTxs, msg) + + blk0, err := vm.BuildBlock(context.Background()) + if err != nil { + t.Fatalf("Failed to build block with import transaction: %s", err) + } + + if err := blk0.Verify(context.Background()); err != nil { + t.Fatalf("Block failed verification: %s", err) + } + + if err := vm.SetPreference(context.Background(), blk0.ID()); err != nil { + t.Fatal(err) + } + + newHead := <-newTxPoolHeadChan + if newHead.Head.Hash() != common.Hash(blk0.ID()) { + t.Fatalf("Expected new block to match") + } + + tx := types.NewTransaction(0, key.Address, big.NewInt(10), 21000, big.NewInt(ap0.MinGasPrice), nil) + signedTx, err := types.SignTx(tx, types.NewEIP155Signer(vm.Ethereum().BlockChain().Config().ChainID), key.PrivateKey) + if err != nil { + t.Fatal(err) + } + + // Add the remote transactions, build the block, and set VM1's preference for block A + errs := vm.Ethereum().TxPool().AddRemotesSync([]*types.Transaction{signedTx}) + for i, err := range errs { + if err != nil { + t.Fatalf("Failed to add transaction to VM1 at index %d: %s", i, err) + } + } + + msg, err = vm.WaitForEvent(context.Background()) + require.NoError(t, err) + require.Equal(t, commonEng.PendingTxs, msg) + + blk1, err := vm.BuildBlock(context.Background()) + if err != nil { + t.Fatalf("Failed to build blk1: %s", err) + } + + if err := blk1.Verify(context.Background()); err != nil { + t.Fatalf("blk1 failed verification due to %s", err) + } + + if err := vm.SetPreference(context.Background(), blk1.ID()); err != nil { + t.Fatal(err) + } + + importTx1, err := vm.newImportTx(vm.Ctx.XChainID, key.Address, vmtest.InitialBaseFee, []*secp256k1.PrivateKey{key1}) + if err != nil { + t.Fatalf("Failed to issue importTx1 due to: %s", err) + } + + if err := vm.AtomicMempool.AddLocalTx(importTx1); err != nil { + t.Fatal(err) + } + + msg, err = vm.WaitForEvent(context.Background()) + require.NoError(t, err) + require.Equal(t, commonEng.PendingTxs, msg) + + blk2, err := vm.BuildBlock(context.Background()) + if err != nil { + t.Fatalf("Failed to build block with import transaction: %s", err) + } + + if err := blk2.Verify(context.Background()); err != nil { + t.Fatalf("Block failed verification: %s", err) + } + + if err := vm.SetPreference(context.Background(), blk2.ID()); err != nil { + t.Fatal(err) + } + + if err := vm.AtomicMempool.AddLocalTx(importTx0B); err == nil { + t.Fatalf("Should not have been able to issue import tx with conflict") + } + // Force issue transaction directly into the mempool + if err := vm.AtomicMempool.ForceAddTx(importTx0B); err != nil { + t.Fatal(err) + } + msg, err = vm.WaitForEvent(context.Background()) + require.NoError(t, err) + require.Equal(t, commonEng.PendingTxs, msg) + + _, err = vm.BuildBlock(context.Background()) + if err == nil { + t.Fatal("Shouldn't have been able to build an invalid block") + } +} + +func TestBonusBlocksTxs(t *testing.T) { + for _, scheme := range vmtest.Schemes { + t.Run(scheme, func(t *testing.T) { + testBonusBlocksTxs(t, scheme) + }) + } +} + +func testBonusBlocksTxs(t *testing.T, scheme string) { + fork := upgradetest.NoUpgrades + vm := newAtomicTestVM() + tvm := vmtest.SetupTestVM(t, vm, vmtest.TestVMConfig{ + Fork: &fork, + Scheme: scheme, + }) + defer func() { + if err := vm.Shutdown(context.Background()); err != nil { + t.Fatal(err) + } + }() + + importAmount := uint64(10000000) + utxoID := avax.UTXOID{TxID: ids.GenerateTestID()} + + utxo := &avax.UTXO{ + UTXOID: utxoID, + Asset: avax.Asset{ID: vm.Ctx.AVAXAssetID}, + Out: &secp256k1fx.TransferOutput{ + Amt: importAmount, + OutputOwners: secp256k1fx.OutputOwners{ + Threshold: 1, + Addrs: []ids.ShortID{vmtest.TestKeys[0].Address()}, + }, + }, + } + utxoBytes, err := atomic.Codec.Marshal(atomic.CodecVersion, utxo) + if err != nil { + t.Fatal(err) + } + + xChainSharedMemory := tvm.AtomicMemory.NewSharedMemory(vm.Ctx.XChainID) + inputID := utxo.InputID() + if err := xChainSharedMemory.Apply(map[ids.ID]*avalancheatomic.Requests{vm.Ctx.ChainID: {PutRequests: []*avalancheatomic.Element{{ + Key: inputID[:], + Value: utxoBytes, + Traits: [][]byte{ + vmtest.TestKeys[0].Address().Bytes(), + }, + }}}}); err != nil { + t.Fatal(err) + } + + importTx, err := vm.newImportTx(vm.Ctx.XChainID, vmtest.TestEthAddrs[0], vmtest.InitialBaseFee, []*secp256k1.PrivateKey{vmtest.TestKeys[0]}) + if err != nil { + t.Fatal(err) + } + + if err := vm.AtomicMempool.AddLocalTx(importTx); err != nil { + t.Fatal(err) + } + + msg, err := vm.WaitForEvent(context.Background()) + require.NoError(t, err) + require.Equal(t, commonEng.PendingTxs, msg) + + blk, err := vm.BuildBlock(context.Background()) + if err != nil { + t.Fatal(err) + } + + // Make [blk] a bonus block. + vm.AtomicBackend.AddBonusBlock(blk.Height(), blk.ID()) + + // Remove the UTXOs from shared memory, so that non-bonus blocks will fail verification + if err := vm.Ctx.SharedMemory.Apply(map[ids.ID]*avalancheatomic.Requests{vm.Ctx.XChainID: {RemoveRequests: [][]byte{inputID[:]}}}); err != nil { + t.Fatal(err) + } + + if err := blk.Verify(context.Background()); err != nil { + t.Fatal(err) + } + + if err := vm.SetPreference(context.Background(), blk.ID()); err != nil { + t.Fatal(err) + } + + if err := blk.Accept(context.Background()); err != nil { + t.Fatal(err) + } + + lastAcceptedID, err := vm.LastAccepted(context.Background()) + if err != nil { + t.Fatal(err) + } + if lastAcceptedID != blk.ID() { + t.Fatalf("Expected last accepted blockID to be the accepted block: %s, but found %s", blk.ID(), lastAcceptedID) + } +} + +// Builds [blkA] with a virtuous import transaction and [blkB] with a separate import transaction +// that does not conflict. Accepts [blkB] and rejects [blkA], then requires that the virtuous atomic +// transaction in [blkA] is correctly re-issued into the atomic transaction mempool. +func TestReissueAtomicTx(t *testing.T) { + for _, scheme := range vmtest.Schemes { + t.Run(scheme, func(t *testing.T) { + testReissueAtomicTx(t, scheme) + }) + } +} + +func testReissueAtomicTx(t *testing.T, scheme string) { + fork := upgradetest.ApricotPhase1 + vm := newAtomicTestVM() + tvm := vmtest.SetupTestVM(t, vm, vmtest.TestVMConfig{ + Fork: &fork, + }) + require.NoError(t, addUTXOs(tvm.AtomicMemory, vm.Ctx, map[ids.ShortID]uint64{ + vmtest.TestShortIDAddrs[0]: 10000000, + vmtest.TestShortIDAddrs[1]: 10000000, + })) + + defer func() { + if err := vm.Shutdown(context.Background()); err != nil { + t.Fatal(err) + } + }() + + genesisBlkID, err := vm.LastAccepted(context.Background()) + if err != nil { + t.Fatal(err) + } + + importTx, err := vm.newImportTx(vm.Ctx.XChainID, vmtest.TestEthAddrs[0], vmtest.InitialBaseFee, []*secp256k1.PrivateKey{vmtest.TestKeys[0]}) + if err != nil { + t.Fatal(err) + } + + if err := vm.AtomicMempool.AddLocalTx(importTx); err != nil { + t.Fatal(err) + } + + msg, err := vm.WaitForEvent(context.Background()) + require.NoError(t, err) + require.Equal(t, commonEng.PendingTxs, msg) + + blkA, err := vm.BuildBlock(context.Background()) + if err != nil { + t.Fatal(err) + } + + if err := blkA.Verify(context.Background()); err != nil { + t.Fatal(err) + } + + if err := vm.SetPreference(context.Background(), blkA.ID()); err != nil { + t.Fatal(err) + } + + // SetPreference to parent before rejecting (will rollback state to genesis + // so that atomic transaction can be reissued, otherwise current block will + // conflict with UTXO to be reissued) + if err := vm.SetPreference(context.Background(), genesisBlkID); err != nil { + t.Fatal(err) + } + + // Rejecting [blkA] should cause [importTx] to be re-issued into the mempool. + if err := blkA.Reject(context.Background()); err != nil { + t.Fatal(err) + } + + // Sleep for a minimum of two seconds to ensure that [blkB] will have a different timestamp + // than [blkA] so that the block will be unique. This is necessary since we have marked [blkA] + // as Rejected. + time.Sleep(2 * time.Second) + msg, err = vm.WaitForEvent(context.Background()) + require.NoError(t, err) + require.Equal(t, commonEng.PendingTxs, msg) + blkB, err := vm.BuildBlock(context.Background()) + if err != nil { + t.Fatal(err) + } + + if blkB.Height() != blkA.Height() { + t.Fatalf("Expected blkB (%d) to have the same height as blkA (%d)", blkB.Height(), blkA.Height()) + } + + if err := blkB.Verify(context.Background()); err != nil { + t.Fatal(err) + } + + if err := vm.SetPreference(context.Background(), blkB.ID()); err != nil { + t.Fatal(err) + } + + if err := blkB.Accept(context.Background()); err != nil { + t.Fatal(err) + } + + if lastAcceptedID, err := vm.LastAccepted(context.Background()); err != nil { + t.Fatal(err) + } else if lastAcceptedID != blkB.ID() { + t.Fatalf("Expected last accepted blockID to be the accepted block: %s, but found %s", blkB.ID(), lastAcceptedID) + } + + // Check that [importTx] has been indexed correctly after [blkB] is accepted. + _, height, err := vm.AtomicTxRepository.GetByTxID(importTx.ID()) + if err != nil { + t.Fatal(err) + } else if height != blkB.Height() { + t.Fatalf("Expected indexed height of import tx to be %d, but found %d", blkB.Height(), height) + } +} + +func TestAtomicTxFailsEVMStateTransferBuildBlock(t *testing.T) { + for _, scheme := range vmtest.Schemes { + t.Run(scheme, func(t *testing.T) { + testAtomicTxFailsEVMStateTransferBuildBlock(t, scheme) + }) + } +} + +func testAtomicTxFailsEVMStateTransferBuildBlock(t *testing.T, scheme string) { + fork := upgradetest.ApricotPhase1 + vm := newAtomicTestVM() + tvm := vmtest.SetupTestVM(t, vm, vmtest.TestVMConfig{ + Fork: &fork, + Scheme: scheme, + }) + require.NoError(t, addUTXOs(tvm.AtomicMemory, vm.Ctx, map[ids.ShortID]uint64{ + vmtest.TestShortIDAddrs[0]: 10000000, + vmtest.TestShortIDAddrs[1]: 10000000, + })) + + defer func() { + if err := vm.Shutdown(context.Background()); err != nil { + t.Fatal(err) + } + }() + + exportTxs := createExportTxOptions(t, vm, tvm.AtomicMemory) + exportTx1, exportTx2 := exportTxs[0], exportTxs[1] + + if err := vm.AtomicMempool.AddLocalTx(exportTx1); err != nil { + t.Fatal(err) + } + msg, err := vm.WaitForEvent(context.Background()) + require.NoError(t, err) + require.Equal(t, commonEng.PendingTxs, msg) + exportBlk1, err := vm.BuildBlock(context.Background()) + if err != nil { + t.Fatal(err) + } + if err := exportBlk1.Verify(context.Background()); err != nil { + t.Fatal(err) + } + + if err := vm.SetPreference(context.Background(), exportBlk1.ID()); err != nil { + t.Fatal(err) + } + + if err := vm.AtomicMempool.AddLocalTx(exportTx2); err == nil { + t.Fatal("Should have failed to issue due to an invalid export tx") + } + + if err := vm.AtomicMempool.AddRemoteTx(exportTx2); err == nil { + t.Fatal("Should have failed to add because conflicting") + } + + // Manually add transaction to mempool to bypass validation + if err := vm.AtomicMempool.ForceAddTx(exportTx2); err != nil { + t.Fatal(err) + } + msg, err = vm.WaitForEvent(context.Background()) + require.NoError(t, err) + require.Equal(t, commonEng.PendingTxs, msg) + + _, err = vm.BuildBlock(context.Background()) + if err == nil { + t.Fatal("BuildBlock should have returned an error due to invalid export transaction") + } +} + +// This is a regression test to ensure that if two consecutive atomic transactions fail verification +// in onFinalizeAndAssemble it will not cause a panic due to calling RevertToSnapshot(revID) on the +// same revision ID twice. +func TestConsecutiveAtomicTransactionsRevertSnapshot(t *testing.T) { + fork := upgradetest.ApricotPhase1 + vm := newAtomicTestVM() + tvm := vmtest.SetupTestVM(t, vm, vmtest.TestVMConfig{ + Fork: &fork, + }) + require.NoError(t, addUTXOs(tvm.AtomicMemory, vm.Ctx, map[ids.ShortID]uint64{ + vmtest.TestShortIDAddrs[0]: 10000000, + vmtest.TestShortIDAddrs[1]: 10000000, + })) + + defer func() { + if err := vm.Shutdown(context.Background()); err != nil { + t.Fatal(err) + } + }() + + newTxPoolHeadChan := make(chan core.NewTxPoolReorgEvent, 1) + vm.Ethereum().TxPool().SubscribeNewReorgEvent(newTxPoolHeadChan) + + // Create three conflicting import transactions + importTxs := createImportTxOptions(t, vm, tvm.AtomicMemory) + + // Issue the first import transaction, build, and accept the block. + if err := vm.AtomicMempool.AddLocalTx(importTxs[0]); err != nil { + t.Fatal(err) + } + + msg, err := vm.WaitForEvent(context.Background()) + require.NoError(t, err) + require.Equal(t, commonEng.PendingTxs, msg) + + blk, err := vm.BuildBlock(context.Background()) + if err != nil { + t.Fatal(err) + } + + if err := blk.Verify(context.Background()); err != nil { + t.Fatal(err) + } + + if err := vm.SetPreference(context.Background(), blk.ID()); err != nil { + t.Fatal(err) + } + + if err := blk.Accept(context.Background()); err != nil { + t.Fatal(err) + } + + newHead := <-newTxPoolHeadChan + if newHead.Head.Hash() != common.Hash(blk.ID()) { + t.Fatalf("Expected new block to match") + } + + // Add the two conflicting transactions directly to the mempool, so that two consecutive transactions + // will fail verification when build block is called. + vm.AtomicMempool.AddRemoteTx(importTxs[1]) + vm.AtomicMempool.AddRemoteTx(importTxs[2]) + + if _, err := vm.BuildBlock(context.Background()); err == nil { + t.Fatal("Expected build block to fail due to empty block") + } +} + +func TestAtomicTxBuildBlockDropsConflicts(t *testing.T) { + importAmount := uint64(10000000) + fork := upgradetest.ApricotPhase5 + vm := newAtomicTestVM() + tvm := vmtest.SetupTestVM(t, vm, vmtest.TestVMConfig{ + Fork: &fork, + }) + require.NoError(t, addUTXOs(tvm.AtomicMemory, vm.Ctx, map[ids.ShortID]uint64{ + vmtest.TestShortIDAddrs[0]: importAmount, + vmtest.TestShortIDAddrs[1]: importAmount, + vmtest.TestShortIDAddrs[2]: importAmount, + })) + conflictKey := utilstest.NewKey(t) + + defer func() { + if err := vm.Shutdown(context.Background()); err != nil { + t.Fatal(err) + } + }() + + // Create a conflict set for each pair of transactions + conflictSets := make([]set.Set[ids.ID], len(vmtest.TestKeys)) + for index, key := range vmtest.TestKeys { + importTx, err := vm.newImportTx(vm.Ctx.XChainID, vmtest.TestEthAddrs[index], vmtest.InitialBaseFee, []*secp256k1.PrivateKey{key}) + if err != nil { + t.Fatal(err) + } + if err := vm.AtomicMempool.AddLocalTx(importTx); err != nil { + t.Fatal(err) + } + conflictSets[index].Add(importTx.ID()) + conflictTx, err := vm.newImportTx(vm.Ctx.XChainID, conflictKey.Address, vmtest.InitialBaseFee, []*secp256k1.PrivateKey{key}) + if err != nil { + t.Fatal(err) + } + if err := vm.AtomicMempool.AddLocalTx(conflictTx); err == nil { + t.Fatal("should conflict with the utxoSet in the mempool") + } + // force add the tx + vm.AtomicMempool.ForceAddTx(conflictTx) + conflictSets[index].Add(conflictTx.ID()) + } + msg, err := vm.WaitForEvent(context.Background()) + require.NoError(t, err) + require.Equal(t, commonEng.PendingTxs, msg) + // Note: this only checks the path through OnFinalizeAndAssemble, we should make sure to add a test + // that verifies blocks received from the network will also fail verification + blk, err := vm.BuildBlock(context.Background()) + if err != nil { + t.Fatal(err) + } + wrappedBlk, ok := blk.(*chain.BlockWrapper).Block.(extension.ExtendedBlock) + require.True(t, ok, "expected block to be a ExtendedBlock") + blockExtension, ok := wrappedBlk.GetBlockExtension().(*blockExtension) + require.True(t, ok, "expected block to be a blockExtension") + atomicTxs := blockExtension.atomicTxs + require.True(t, len(atomicTxs) == len(vmtest.TestKeys), "Conflict transactions should be out of the batch") + atomicTxIDs := set.Set[ids.ID]{} + for _, tx := range atomicTxs { + atomicTxIDs.Add(tx.ID()) + } + + // Check that removing the txIDs actually included in the block from each conflict set + // leaves one item remaining for each conflict set ie. only one tx from each conflict set + // has been included in the block. + for _, conflictSet := range conflictSets { + conflictSet.Difference(atomicTxIDs) + require.Equal(t, 1, conflictSet.Len()) + } + + if err := blk.Verify(context.Background()); err != nil { + t.Fatal(err) + } + if err := blk.Accept(context.Background()); err != nil { + t.Fatal(err) + } +} + +func TestBuildBlockDoesNotExceedAtomicGasLimit(t *testing.T) { + importAmount := uint64(10000000) + fork := upgradetest.ApricotPhase5 + vm := newAtomicTestVM() + tvm := vmtest.SetupTestVM(t, vm, vmtest.TestVMConfig{ + Fork: &fork, + }) + + defer func() { + if err := vm.Shutdown(context.Background()); err != nil { + t.Fatal(err) + } + }() + + kc := secp256k1fx.NewKeychain() + kc.Add(vmtest.TestKeys[0]) + txID, err := ids.ToID(hashing.ComputeHash256(vmtest.TestShortIDAddrs[0][:])) + require.NoError(t, err) + + mempoolTxs := 200 + for i := 0; i < mempoolTxs; i++ { + utxo, err := addUTXO(tvm.AtomicMemory, vm.Ctx, txID, uint32(i), vm.Ctx.AVAXAssetID, importAmount, vmtest.TestShortIDAddrs[0]) + require.NoError(t, err) + + importTx, err := atomic.NewImportTx(vm.Ctx, vm.CurrentRules(), vm.clock.Unix(), vm.Ctx.XChainID, vmtest.TestEthAddrs[0], vmtest.InitialBaseFee, kc, []*avax.UTXO{utxo}) + if err != nil { + t.Fatal(err) + } + if err := vm.AtomicMempool.AddLocalTx(importTx); err != nil { + t.Fatal(err) + } + } + + msg, err := vm.WaitForEvent(context.Background()) + require.NoError(t, err) + require.Equal(t, commonEng.PendingTxs, msg) + blk, err := vm.BuildBlock(context.Background()) + if err != nil { + t.Fatal(err) + } + + wrappedBlk, ok := blk.(*chain.BlockWrapper).Block.(extension.ExtendedBlock) + require.True(t, ok, "expected block to be a ExtendedBlock") + blockExtension, ok := wrappedBlk.GetBlockExtension().(*blockExtension) + require.True(t, ok, "expected block to be a blockExtension") + // Need to ensure that not all of the transactions in the mempool are included in the block. + // This ensures that we hit the atomic gas limit while building the block before we hit the + // upper limit on the size of the codec for marshalling the atomic transactions. + atomicTxs := blockExtension.atomicTxs + if len(atomicTxs) >= mempoolTxs { + t.Fatalf("Expected number of atomic transactions included in the block (%d) to be less than the number of transactions added to the mempool (%d)", len(atomicTxs), mempoolTxs) + } +} + +func TestExtraStateChangeAtomicGasLimitExceeded(t *testing.T) { + importAmount := uint64(10000000) + // We create two VMs one in ApriotPhase4 and one in ApricotPhase5, so that we can construct a block + // containing a large enough atomic transaction that it will exceed the atomic gas limit in + // ApricotPhase5. + fork1 := upgradetest.ApricotPhase4 + vm1 := newAtomicTestVM() + tvm1 := vmtest.SetupTestVM(t, vm1, vmtest.TestVMConfig{ + Fork: &fork1, + }) + fork2 := upgradetest.ApricotPhase5 + vm2 := newAtomicTestVM() + tvm2 := vmtest.SetupTestVM(t, vm2, vmtest.TestVMConfig{ + Fork: &fork2, + }) + defer func() { + if err := vm1.Shutdown(context.Background()); err != nil { + t.Fatal(err) + } + if err := vm2.Shutdown(context.Background()); err != nil { + t.Fatal(err) + } + }() + + kc := secp256k1fx.NewKeychain() + kc.Add(vmtest.TestKeys[0]) + txID, err := ids.ToID(hashing.ComputeHash256(vmtest.TestShortIDAddrs[0][:])) + require.NoError(t, err) + + // Add enough UTXOs, such that the created import transaction will attempt to consume more gas than allowed + // in ApricotPhase5. + for i := 0; i < 100; i++ { + _, err := addUTXO(tvm1.AtomicMemory, vm1.Ctx, txID, uint32(i), vm1.Ctx.AVAXAssetID, importAmount, vmtest.TestShortIDAddrs[0]) + require.NoError(t, err) + + _, err = addUTXO(tvm2.AtomicMemory, vm2.Ctx, txID, uint32(i), vm2.Ctx.AVAXAssetID, importAmount, vmtest.TestShortIDAddrs[0]) + require.NoError(t, err) + } + + // Double the initial base fee used when estimating the cost of this transaction to ensure that when it is + // used in ApricotPhase5 it still pays a sufficient fee with the fixed fee per atomic transaction. + importTx, err := vm1.newImportTx(vm1.Ctx.XChainID, vmtest.TestEthAddrs[0], new(big.Int).Mul(common.Big2, vmtest.InitialBaseFee), []*secp256k1.PrivateKey{vmtest.TestKeys[0]}) + if err != nil { + t.Fatal(err) + } + if err := vm1.AtomicMempool.ForceAddTx(importTx); err != nil { + t.Fatal(err) + } + + msg, err := vm1.WaitForEvent(context.Background()) + require.NoError(t, err) + require.Equal(t, commonEng.PendingTxs, msg) + blk1, err := vm1.BuildBlock(context.Background()) + if err != nil { + t.Fatal(err) + } + if err := blk1.Verify(context.Background()); err != nil { + t.Fatal(err) + } + + wrappedBlk, ok := blk1.(*chain.BlockWrapper).Block.(extension.ExtendedBlock) + require.True(t, ok, "expected block to be a ExtendedBlock") + validEthBlock := wrappedBlk.GetEthBlock() + extraData, err := atomic.Codec.Marshal(atomic.CodecVersion, []*atomic.Tx{importTx}) + if err != nil { + t.Fatal(err) + } + + // Construct the new block with the extra data in the new format (slice of atomic transactions). + ethBlk2 := customtypes.NewBlockWithExtData( + types.CopyHeader(validEthBlock.Header()), + nil, + nil, + nil, + new(trie.Trie), + extraData, + true, + ) + + state, err := vm2.Ethereum().BlockChain().State() + if err != nil { + t.Fatal(err) + } + + // Hack: test [onExtraStateChange] directly to ensure it catches the atomic gas limit error correctly. + if _, _, err := vm2.onExtraStateChange(ethBlk2, nil, state); err == nil || !strings.Contains(err.Error(), "exceeds atomic gas limit") { + t.Fatalf("Expected block to fail verification due to exceeded atomic gas limit, but found error: %v", err) + } +} + +// Regression test to ensure that a VM that is not able to parse a block that +// contains no transactions. +func TestEmptyBlock(t *testing.T) { + for _, scheme := range vmtest.Schemes { + t.Run(scheme, func(t *testing.T) { + testEmptyBlock(t, scheme) + }) + } +} + +func testEmptyBlock(t *testing.T, scheme string) { + importAmount := uint64(1000000000) + fork := upgradetest.NoUpgrades + vm := newAtomicTestVM() + tvm := vmtest.SetupTestVM(t, vm, vmtest.TestVMConfig{ + Fork: &fork, + Scheme: scheme, + }) + require.NoError(t, addUTXOs(tvm.AtomicMemory, vm.Ctx, map[ids.ShortID]uint64{ + vmtest.TestShortIDAddrs[0]: importAmount, + })) + + defer func() { + if err := vm.Shutdown(context.Background()); err != nil { + t.Fatal(err) + } + }() + + importTx, err := vm.newImportTx(vm.Ctx.XChainID, vmtest.TestEthAddrs[0], vmtest.InitialBaseFee, []*secp256k1.PrivateKey{vmtest.TestKeys[0]}) + if err != nil { + t.Fatal(err) + } + + if err := vm.AtomicMempool.AddLocalTx(importTx); err != nil { + t.Fatal(err) + } + + msg, err := vm.WaitForEvent(context.Background()) + require.NoError(t, err) + require.Equal(t, commonEng.PendingTxs, msg) + + blk, err := vm.BuildBlock(context.Background()) + if err != nil { + t.Fatalf("Failed to build block with import transaction: %s", err) + } + + // Create empty block from blkA + wrappedBlk, ok := blk.(*chain.BlockWrapper).Block.(extension.ExtendedBlock) + require.True(t, ok, "expected block to be a ExtendedBlock") + ethBlock := wrappedBlk.GetEthBlock() + + emptyEthBlock := customtypes.NewBlockWithExtData( + types.CopyHeader(ethBlock.Header()), + nil, + nil, + nil, + new(trie.Trie), + nil, + false, + ) + + if len(customtypes.BlockExtData(emptyEthBlock)) != 0 || customtypes.GetHeaderExtra(emptyEthBlock.Header()).ExtDataHash != (common.Hash{}) { + t.Fatalf("emptyEthBlock should not have any extra data") + } + + emptyBlockBytes, err := rlp.EncodeToBytes(emptyEthBlock) + require.NoError(t, err) + + if _, err := vm.ParseBlock(context.Background(), emptyBlockBytes); !errors.Is(err, ErrEmptyBlock) { + t.Fatalf("VM should have failed with errEmptyBlock but got %s", err.Error()) + } +} + +// Regression test to ensure we can build blocks if we are starting with the +// Apricot Phase 5 ruleset in genesis. +func TestBuildApricotPhase5Block(t *testing.T) { + for _, scheme := range vmtest.Schemes { + t.Run(scheme, func(t *testing.T) { + testBuildApricotPhase5Block(t, scheme) + }) + } +} + +func testBuildApricotPhase5Block(t *testing.T, scheme string) { + fork := upgradetest.ApricotPhase5 + vm := newAtomicTestVM() + tvm := vmtest.SetupTestVM(t, vm, vmtest.TestVMConfig{ + Fork: &fork, + Scheme: scheme, + }) + + defer func() { + if err := vm.Shutdown(context.Background()); err != nil { + t.Fatal(err) + } + }() + + newTxPoolHeadChan := make(chan core.NewTxPoolReorgEvent, 1) + vm.Ethereum().TxPool().SubscribeNewReorgEvent(newTxPoolHeadChan) + + key := vmtest.TestKeys[0].ToECDSA() + address := vmtest.TestEthAddrs[0] + + importAmount := uint64(1000000000) + utxoID := avax.UTXOID{TxID: ids.GenerateTestID()} + + utxo := &avax.UTXO{ + UTXOID: utxoID, + Asset: avax.Asset{ID: vm.Ctx.AVAXAssetID}, + Out: &secp256k1fx.TransferOutput{ + Amt: importAmount, + OutputOwners: secp256k1fx.OutputOwners{ + Threshold: 1, + Addrs: []ids.ShortID{vmtest.TestKeys[0].Address()}, + }, + }, + } + utxoBytes, err := atomic.Codec.Marshal(atomic.CodecVersion, utxo) + if err != nil { + t.Fatal(err) + } + + xChainSharedMemory := tvm.AtomicMemory.NewSharedMemory(vm.Ctx.XChainID) + inputID := utxo.InputID() + if err := xChainSharedMemory.Apply(map[ids.ID]*avalancheatomic.Requests{vm.Ctx.ChainID: {PutRequests: []*avalancheatomic.Element{{ + Key: inputID[:], + Value: utxoBytes, + Traits: [][]byte{ + vmtest.TestKeys[0].Address().Bytes(), + }, + }}}}); err != nil { + t.Fatal(err) + } + + importTx, err := vm.newImportTx(vm.Ctx.XChainID, address, vmtest.InitialBaseFee, []*secp256k1.PrivateKey{vmtest.TestKeys[0]}) + if err != nil { + t.Fatal(err) + } + + if err := vm.AtomicMempool.AddLocalTx(importTx); err != nil { + t.Fatal(err) + } + + msg, err := vm.WaitForEvent(context.Background()) + require.NoError(t, err) + require.Equal(t, commonEng.PendingTxs, msg) + + blk, err := vm.BuildBlock(context.Background()) + if err != nil { + t.Fatal(err) + } + + if err := blk.Verify(context.Background()); err != nil { + t.Fatal(err) + } + + if err := vm.SetPreference(context.Background(), blk.ID()); err != nil { + t.Fatal(err) + } + + if err := blk.Accept(context.Background()); err != nil { + t.Fatal(err) + } + + wrappedBlk, ok := blk.(*chain.BlockWrapper).Block.(extension.ExtendedBlock) + require.True(t, ok, "expected block to be a ExtendedBlock") + ethBlk := wrappedBlk.GetEthBlock() + if eBlockGasCost := customtypes.BlockGasCost(ethBlk); eBlockGasCost == nil || eBlockGasCost.Cmp(common.Big0) != 0 { + t.Fatalf("expected blockGasCost to be 0 but got %d", eBlockGasCost) + } + if eExtDataGasUsed := customtypes.BlockExtDataGasUsed(ethBlk); eExtDataGasUsed == nil || eExtDataGasUsed.Cmp(big.NewInt(11230)) != 0 { + t.Fatalf("expected extDataGasUsed to be 11230 but got %d", eExtDataGasUsed) + } + minRequiredTip, err := header.EstimateRequiredTip(vm.chainConfigExtra(), ethBlk.Header()) + if err != nil { + t.Fatal(err) + } + if minRequiredTip == nil || minRequiredTip.Cmp(common.Big0) != 0 { + t.Fatalf("expected minRequiredTip to be 0 but got %d", minRequiredTip) + } + + newHead := <-newTxPoolHeadChan + if newHead.Head.Hash() != common.Hash(blk.ID()) { + t.Fatalf("Expected new block to match") + } + + txs := make([]*types.Transaction, 10) + for i := 0; i < 10; i++ { + tx := types.NewTransaction(uint64(i), address, big.NewInt(10), 21000, big.NewInt(ap0.MinGasPrice*3), nil) + signedTx, err := types.SignTx(tx, types.NewEIP155Signer(vm.Ethereum().BlockChain().Config().ChainID), key) + if err != nil { + t.Fatal(err) + } + txs[i] = signedTx + } + errs := vm.Ethereum().TxPool().Add(txs, false, false) + for i, err := range errs { + if err != nil { + t.Fatalf("Failed to add tx at index %d: %s", i, err) + } + } + + msg, err = vm.WaitForEvent(context.Background()) + require.NoError(t, err) + require.Equal(t, commonEng.PendingTxs, msg) + + blk, err = vm.BuildBlock(context.Background()) + if err != nil { + t.Fatal(err) + } + + if err := blk.Verify(context.Background()); err != nil { + t.Fatal(err) + } + + if err := blk.Accept(context.Background()); err != nil { + t.Fatal(err) + } + + wrappedBlk, ok = blk.(*chain.BlockWrapper).Block.(extension.ExtendedBlock) + require.True(t, ok, "expected block to be a ExtendedBlock") + ethBlk = wrappedBlk.GetEthBlock() + if customtypes.BlockGasCost(ethBlk) == nil || customtypes.BlockGasCost(ethBlk).Cmp(big.NewInt(100)) < 0 { + t.Fatalf("expected blockGasCost to be at least 100 but got %d", customtypes.BlockGasCost(ethBlk)) + } + if customtypes.BlockExtDataGasUsed(ethBlk) == nil || customtypes.BlockExtDataGasUsed(ethBlk).Cmp(common.Big0) != 0 { + t.Fatalf("expected extDataGasUsed to be 0 but got %d", customtypes.BlockExtDataGasUsed(ethBlk)) + } + minRequiredTip, err = header.EstimateRequiredTip(vm.chainConfigExtra(), ethBlk.Header()) + if err != nil { + t.Fatal(err) + } + if minRequiredTip == nil || minRequiredTip.Cmp(big.NewInt(0.05*params.GWei)) < 0 { + t.Fatalf("expected minRequiredTip to be at least 0.05 gwei but got %d", minRequiredTip) + } + + lastAcceptedID, err := vm.LastAccepted(context.Background()) + if err != nil { + t.Fatal(err) + } + if lastAcceptedID != blk.ID() { + t.Fatalf("Expected last accepted blockID to be the accepted block: %s, but found %s", blk.ID(), lastAcceptedID) + } + + // Confirm all txs are present + ethBlkTxs := vm.Ethereum().BlockChain().GetBlockByNumber(2).Transactions() + for i, tx := range txs { + if len(ethBlkTxs) <= i { + t.Fatalf("missing transactions expected: %d but found: %d", len(txs), len(ethBlkTxs)) + } + if ethBlkTxs[i].Hash() != tx.Hash() { + t.Fatalf("expected tx at index %d to have hash: %x but has: %x", i, txs[i].Hash(), tx.Hash()) + } + } +} + +// Regression test to ensure we can build blocks if we are starting with the +// Apricot Phase 4 ruleset in genesis. +func TestBuildApricotPhase4Block(t *testing.T) { + for _, scheme := range vmtest.Schemes { + t.Run(scheme, func(t *testing.T) { + testBuildApricotPhase4Block(t, scheme) + }) + } +} + +func testBuildApricotPhase4Block(t *testing.T, scheme string) { + fork := upgradetest.ApricotPhase4 + vm := newAtomicTestVM() + tvm := vmtest.SetupTestVM(t, vm, vmtest.TestVMConfig{ + Fork: &fork, + }) + + defer func() { + if err := vm.Shutdown(context.Background()); err != nil { + t.Fatal(err) + } + }() + + newTxPoolHeadChan := make(chan core.NewTxPoolReorgEvent, 1) + vm.Ethereum().TxPool().SubscribeNewReorgEvent(newTxPoolHeadChan) + + key := vmtest.TestKeys[0].ToECDSA() + address := vmtest.TestEthAddrs[0] + + importAmount := uint64(1000000000) + utxoID := avax.UTXOID{TxID: ids.GenerateTestID()} + + utxo := &avax.UTXO{ + UTXOID: utxoID, + Asset: avax.Asset{ID: vm.Ctx.AVAXAssetID}, + Out: &secp256k1fx.TransferOutput{ + Amt: importAmount, + OutputOwners: secp256k1fx.OutputOwners{ + Threshold: 1, + Addrs: []ids.ShortID{vmtest.TestKeys[0].Address()}, + }, + }, + } + utxoBytes, err := atomic.Codec.Marshal(atomic.CodecVersion, utxo) + if err != nil { + t.Fatal(err) + } + + xChainSharedMemory := tvm.AtomicMemory.NewSharedMemory(vm.Ctx.XChainID) + inputID := utxo.InputID() + if err := xChainSharedMemory.Apply(map[ids.ID]*avalancheatomic.Requests{vm.Ctx.ChainID: {PutRequests: []*avalancheatomic.Element{{ + Key: inputID[:], + Value: utxoBytes, + Traits: [][]byte{ + vmtest.TestKeys[0].Address().Bytes(), + }, + }}}}); err != nil { + t.Fatal(err) + } + + importTx, err := vm.newImportTx(vm.Ctx.XChainID, address, vmtest.InitialBaseFee, []*secp256k1.PrivateKey{vmtest.TestKeys[0]}) + if err != nil { + t.Fatal(err) + } + + if err := vm.AtomicMempool.AddLocalTx(importTx); err != nil { + t.Fatal(err) + } + + msg, err := vm.WaitForEvent(context.Background()) + require.NoError(t, err) + require.Equal(t, commonEng.PendingTxs, msg) + + blk, err := vm.BuildBlock(context.Background()) + if err != nil { + t.Fatal(err) + } + + if err := blk.Verify(context.Background()); err != nil { + t.Fatal(err) + } + + if err := vm.SetPreference(context.Background(), blk.ID()); err != nil { + t.Fatal(err) + } + + if err := blk.Accept(context.Background()); err != nil { + t.Fatal(err) + } + + wrappedBlk, ok := blk.(*chain.BlockWrapper).Block.(extension.ExtendedBlock) + require.True(t, ok, "expected block to be a ExtendedBlock") + ethBlk := wrappedBlk.GetEthBlock() + if eBlockGasCost := customtypes.BlockGasCost(ethBlk); eBlockGasCost == nil || eBlockGasCost.Cmp(common.Big0) != 0 { + t.Fatalf("expected blockGasCost to be 0 but got %d", eBlockGasCost) + } + if eExtDataGasUsed := customtypes.BlockExtDataGasUsed(ethBlk); eExtDataGasUsed == nil || eExtDataGasUsed.Cmp(big.NewInt(1230)) != 0 { + t.Fatalf("expected extDataGasUsed to be 1000 but got %d", eExtDataGasUsed) + } + minRequiredTip, err := header.EstimateRequiredTip(vm.chainConfigExtra(), ethBlk.Header()) + if err != nil { + t.Fatal(err) + } + if minRequiredTip == nil || minRequiredTip.Cmp(common.Big0) != 0 { + t.Fatalf("expected minRequiredTip to be 0 but got %d", minRequiredTip) + } + + newHead := <-newTxPoolHeadChan + if newHead.Head.Hash() != common.Hash(blk.ID()) { + t.Fatalf("Expected new block to match") + } + + txs := make([]*types.Transaction, 10) + chainID := vm.Ethereum().BlockChain().Config().ChainID + for i := 0; i < 5; i++ { + tx := types.NewTransaction(uint64(i), address, big.NewInt(10), 21000, big.NewInt(ap0.MinGasPrice), nil) + signedTx, err := types.SignTx(tx, types.NewEIP155Signer(chainID), key) + if err != nil { + t.Fatal(err) + } + txs[i] = signedTx + } + for i := 5; i < 10; i++ { + tx := types.NewTransaction(uint64(i), address, big.NewInt(10), 21000, big.NewInt(ap1.MinGasPrice), nil) + signedTx, err := types.SignTx(tx, types.NewEIP155Signer(chainID), key) + if err != nil { + t.Fatal(err) + } + txs[i] = signedTx + } + errs := vm.Ethereum().TxPool().AddRemotesSync(txs) + for i, err := range errs { + if err != nil { + t.Fatalf("Failed to add tx at index %d: %s", i, err) + } + } + + msg, err = vm.WaitForEvent(context.Background()) + require.NoError(t, err) + require.Equal(t, commonEng.PendingTxs, msg) + + blk, err = vm.BuildBlock(context.Background()) + if err != nil { + t.Fatal(err) + } + + if err := blk.Verify(context.Background()); err != nil { + t.Fatal(err) + } + + if err := blk.Accept(context.Background()); err != nil { + t.Fatal(err) + } + + wrappedBlk, ok = blk.(*chain.BlockWrapper).Block.(extension.ExtendedBlock) + require.True(t, ok, "expected block to be a ExtendedBlock") + ethBlk = wrappedBlk.GetEthBlock() + if customtypes.BlockGasCost(ethBlk) == nil || customtypes.BlockGasCost(ethBlk).Cmp(big.NewInt(100)) < 0 { + t.Fatalf("expected blockGasCost to be at least 100 but got %d", customtypes.BlockGasCost(ethBlk)) + } + if customtypes.BlockExtDataGasUsed(ethBlk) == nil || customtypes.BlockExtDataGasUsed(ethBlk).Cmp(common.Big0) != 0 { + t.Fatalf("expected extDataGasUsed to be 0 but got %d", customtypes.BlockExtDataGasUsed(ethBlk)) + } + minRequiredTip, err = header.EstimateRequiredTip(vm.chainConfigExtra(), ethBlk.Header()) + if err != nil { + t.Fatal(err) + } + if minRequiredTip == nil || minRequiredTip.Cmp(big.NewInt(0.05*params.GWei)) < 0 { + t.Fatalf("expected minRequiredTip to be at least 0.05 gwei but got %d", minRequiredTip) + } + + lastAcceptedID, err := vm.LastAccepted(context.Background()) + if err != nil { + t.Fatal(err) + } + if lastAcceptedID != blk.ID() { + t.Fatalf("Expected last accepted blockID to be the accepted block: %s, but found %s", blk.ID(), lastAcceptedID) + } + + // Confirm all txs are present + ethBlkTxs := vm.Ethereum().BlockChain().GetBlockByNumber(2).Transactions() + for i, tx := range txs { + if len(ethBlkTxs) <= i { + t.Fatalf("missing transactions expected: %d but found: %d", len(txs), len(ethBlkTxs)) + } + if ethBlkTxs[i].Hash() != tx.Hash() { + t.Fatalf("expected tx at index %d to have hash: %x but has: %x", i, txs[i].Hash(), tx.Hash()) + } + } +} + +func TestBuildInvalidBlockHead(t *testing.T) { + for _, scheme := range vmtest.Schemes { + t.Run(scheme, func(t *testing.T) { + testBuildInvalidBlockHead(t, scheme) + }) + } +} + +func testBuildInvalidBlockHead(t *testing.T, scheme string) { + fork := upgradetest.NoUpgrades + vm := newAtomicTestVM() + vmtest.SetupTestVM(t, vm, vmtest.TestVMConfig{ + Fork: &fork, + Scheme: scheme, + }) + + defer func() { + if err := vm.Shutdown(context.Background()); err != nil { + t.Fatal(err) + } + }() + + key0 := vmtest.TestKeys[0] + addr0 := key0.Address() + + // Create the transaction + utx := &atomic.UnsignedImportTx{ + NetworkID: vm.Ctx.NetworkID, + BlockchainID: vm.Ctx.ChainID, + Outs: []atomic.EVMOutput{{ + Address: common.Address(addr0), + Amount: 1 * units.Avax, + AssetID: vm.Ctx.AVAXAssetID, + }}, + ImportedInputs: []*avax.TransferableInput{ + { + Asset: avax.Asset{ID: vm.Ctx.AVAXAssetID}, + In: &secp256k1fx.TransferInput{ + Amt: 1 * units.Avax, + Input: secp256k1fx.Input{ + SigIndices: []uint32{0}, + }, + }, + }, + }, + SourceChain: vm.Ctx.XChainID, + } + tx := &atomic.Tx{UnsignedAtomicTx: utx} + if err := tx.Sign(atomic.Codec, [][]*secp256k1.PrivateKey{{key0}}); err != nil { + t.Fatal(err) + } + + currentBlock := vm.Ethereum().BlockChain().CurrentBlock() + + // Verify that the transaction fails verification when attempting to issue + // it into the atomic mempool. + if err := vm.AtomicMempool.AddLocalTx(tx); err == nil { + t.Fatal("Should have failed to issue invalid transaction") + } + // Force issue the transaction directly to the mempool + if err := vm.AtomicMempool.ForceAddTx(tx); err != nil { + t.Fatal(err) + } + + msg, err := vm.WaitForEvent(context.Background()) + require.NoError(t, err) + require.Equal(t, commonEng.PendingTxs, msg) + + if _, err := vm.BuildBlock(context.Background()); err == nil { + t.Fatalf("Unexpectedly created a block") + } + + newCurrentBlock := vm.Ethereum().BlockChain().CurrentBlock() + + if currentBlock.Hash() != newCurrentBlock.Hash() { + t.Fatal("current block changed") + } +} + +// shows that a locally generated AtomicTx can be added to mempool and then +// removed by inclusion in a block +func TestMempoolAddLocallyCreateAtomicTx(t *testing.T) { + for _, name := range []string{"import", "export"} { + t.Run(name, func(t *testing.T) { + require := require.New(t) + + // we use AP3 here to not trip any block fees + fork := upgradetest.ApricotPhase3 + vm := newAtomicTestVM() + tvm := vmtest.SetupTestVM(t, vm, vmtest.TestVMConfig{ + Fork: &fork, + }) + defer func() { + require.NoError(vm.Shutdown(context.Background())) + }() + mempool := vm.AtomicMempool + + // generate a valid and conflicting tx + var ( + tx, conflictingTx *atomic.Tx + ) + if name == "import" { + importTxs := createImportTxOptions(t, vm, tvm.AtomicMemory) + tx, conflictingTx = importTxs[0], importTxs[1] + } else { + exportTxs := createExportTxOptions(t, vm, tvm.AtomicMemory) + tx, conflictingTx = exportTxs[0], exportTxs[1] + } + txID := tx.ID() + conflictingTxID := conflictingTx.ID() + + // add a tx to the mempool + require.NoError(vm.AtomicMempool.AddLocalTx(tx)) + has := mempool.Has(txID) + require.True(has, "valid tx not recorded into mempool") + + // try to add a conflicting tx + err := vm.AtomicMempool.AddLocalTx(conflictingTx) + require.ErrorIs(err, txpool.ErrConflict) + has = mempool.Has(conflictingTxID) + require.False(has, "conflicting tx in mempool") + + msg, err := vm.WaitForEvent(context.Background()) + require.NoError(err) + require.Equal(commonEng.PendingTxs, msg) + + has = mempool.Has(txID) + require.True(has, "valid tx not recorded into mempool") + + // Show that BuildBlock generates a block containing [txID] and that it is + // still present in the mempool. + blk, err := vm.BuildBlock(context.Background()) + require.NoError(err, "could not build block out of mempool") + + wrappedBlock, ok := blk.(*chain.BlockWrapper).Block.(extension.ExtendedBlock) + require.True(ok, "unknown block type") + + blockExtension, ok := wrappedBlock.GetBlockExtension().(*blockExtension) + require.True(ok, "unknown block extension type") + + atomicTxs := blockExtension.atomicTxs + require.Equal(txID, atomicTxs[0].ID(), "block does not include expected transaction") + + has = mempool.Has(txID) + require.True(has, "tx should stay in mempool until block is accepted") + + require.NoError(blk.Verify(context.Background())) + require.NoError(blk.Accept(context.Background())) + + has = mempool.Has(txID) + require.False(has, "tx shouldn't be in mempool after block is accepted") + }) + } +} + +func TestWaitForEvent(t *testing.T) { + for _, testCase := range []struct { + name string + testCase func(*testing.T, *VM, common.Address, *ecdsa.PrivateKey) + }{ + { + name: "WaitForEvent with context cancelled returns 0", + testCase: func(t *testing.T, vm *VM, address common.Address, key *ecdsa.PrivateKey) { + ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*100) + defer cancel() + + var wg sync.WaitGroup + wg.Add(1) + + // We run WaitForEvent in a goroutine to ensure it can be safely called concurrently. + go func() { + defer wg.Done() + msg, err := vm.WaitForEvent(ctx) + require.ErrorIs(t, err, context.DeadlineExceeded) + require.Zero(t, msg) + }() + + wg.Wait() + }, + }, + { + name: "WaitForEvent returns when a transaction is added to the mempool", + testCase: func(t *testing.T, vm *VM, address common.Address, key *ecdsa.PrivateKey) { + importTx, err := vm.NewImportTx(vm.Ctx.XChainID, address, vmtest.InitialBaseFee, []*secp256k1.PrivateKey{vmtest.TestKeys[0]}) + require.NoError(t, err) + + var wg sync.WaitGroup + wg.Add(1) + + go func() { + defer wg.Done() + msg, err := vm.WaitForEvent(context.Background()) + require.NoError(t, err) + require.Equal(t, commonEng.PendingTxs, msg) + }() + + require.NoError(t, vm.AtomicMempool.AddLocalTx(importTx)) + + wg.Wait() + }, + }, + { + name: "WaitForEvent doesn't return once a block is built and accepted", + testCase: func(t *testing.T, vm *VM, address common.Address, key *ecdsa.PrivateKey) { + importTx, err := vm.NewImportTx(vm.Ctx.XChainID, address, vmtest.InitialBaseFee, []*secp256k1.PrivateKey{vmtest.TestKeys[0]}) + require.NoError(t, err) + + require.NoError(t, vm.AtomicMempool.AddLocalTx(importTx)) + + blk, err := vm.BuildBlock(context.Background()) + require.NoError(t, err) + + require.NoError(t, blk.Verify(context.Background())) + + require.NoError(t, vm.SetPreference(context.Background(), blk.ID())) + + require.NoError(t, blk.Accept(context.Background())) + + ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*100) + defer cancel() + + var wg sync.WaitGroup + wg.Add(1) + + // We run WaitForEvent in a goroutine to ensure it can be safely called concurrently. + go func() { + defer wg.Done() + msg, err := vm.WaitForEvent(ctx) + require.ErrorIs(t, err, context.DeadlineExceeded) + require.Zero(t, msg) + }() + + wg.Wait() + + t.Log("WaitForEvent returns when regular transactions are added to the mempool") + + txs := make([]*types.Transaction, 10) + for i := 0; i < 10; i++ { + tx := types.NewTransaction(uint64(i), address, big.NewInt(10), 21000, big.NewInt(3*ap0.MinGasPrice), nil) + signedTx, err := types.SignTx(tx, types.NewEIP155Signer(vm.Ethereum().BlockChain().Config().ChainID), key) + require.NoError(t, err) + + txs[i] = signedTx + } + errs := vm.Ethereum().TxPool().Add(txs, false, false) + for _, err := range errs { + require.NoError(t, err) + } + + wg.Add(1) + + go func() { + defer wg.Done() + msg, err := vm.WaitForEvent(context.Background()) + require.NoError(t, err) + require.Equal(t, commonEng.PendingTxs, msg) + }() + + wg.Wait() + + // Build a block again to wipe out the subscription + blk, err = vm.BuildBlock(context.Background()) + require.NoError(t, err) + + require.NoError(t, blk.Verify(context.Background())) + + require.NoError(t, vm.SetPreference(context.Background(), blk.ID())) + + require.NoError(t, blk.Accept(context.Background())) + }, + }, + { + name: "WaitForEvent waits some time after a block is built", + testCase: func(t *testing.T, vm *VM, address common.Address, key *ecdsa.PrivateKey) { + importTx, err := vm.NewImportTx(vm.Ctx.XChainID, address, vmtest.InitialBaseFee, []*secp256k1.PrivateKey{vmtest.TestKeys[0]}) + require.NoError(t, err) + + require.NoError(t, vm.AtomicMempool.AddLocalTx(importTx)) + + lastBuildBlockTime := time.Now() + + blk, err := vm.BuildBlock(context.Background()) + require.NoError(t, err) + + require.NoError(t, blk.Verify(context.Background())) + + require.NoError(t, vm.SetPreference(context.Background(), blk.ID())) + + require.NoError(t, blk.Accept(context.Background())) + + txs := make([]*types.Transaction, 10) + for i := 0; i < 10; i++ { + tx := types.NewTransaction(uint64(i), address, big.NewInt(10), 21000, big.NewInt(3*ap0.MinGasPrice), nil) + signedTx, err := types.SignTx(tx, types.NewEIP155Signer(vm.Ethereum().BlockChain().Config().ChainID), key) + require.NoError(t, err) + + txs[i] = signedTx + } + errs := vm.Ethereum().TxPool().Add(txs, false, false) + for _, err := range errs { + require.NoError(t, err) + } + + var wg sync.WaitGroup + wg.Add(1) + + go func() { + defer wg.Done() + msg, err := vm.WaitForEvent(context.Background()) + require.NoError(t, err) + require.Equal(t, commonEng.PendingTxs, msg) + require.GreaterOrEqual(t, time.Since(lastBuildBlockTime), evm.MinBlockBuildingRetryDelay) + }() + + wg.Wait() + }, + }, + } { + t.Run(testCase.name, func(t *testing.T) { + fork := upgradetest.Latest + vm := newAtomicTestVM() + tvm := vmtest.SetupTestVM(t, vm, vmtest.TestVMConfig{ + Fork: &fork, + }) + key := vmtest.TestKeys[0].ToECDSA() + address := vmtest.TestEthAddrs[0] + + importAmount := uint64(1000000000) + utxoID := avax.UTXOID{TxID: ids.GenerateTestID()} + + utxo := &avax.UTXO{ + UTXOID: utxoID, + Asset: avax.Asset{ID: vm.Ctx.AVAXAssetID}, + Out: &secp256k1fx.TransferOutput{ + Amt: importAmount, + OutputOwners: secp256k1fx.OutputOwners{ + Threshold: 1, + Addrs: []ids.ShortID{vmtest.TestKeys[0].Address()}, + }, + }, + } + utxoBytes, err := atomic.Codec.Marshal(atomic.CodecVersion, utxo) + require.NoError(t, err) + + xChainSharedMemory := tvm.AtomicMemory.NewSharedMemory(vm.Ctx.XChainID) + inputID := utxo.InputID() + require.NoError(t, xChainSharedMemory.Apply(map[ids.ID]*avalancheatomic.Requests{vm.Ctx.ChainID: {PutRequests: []*avalancheatomic.Element{{ + Key: inputID[:], + Value: utxoBytes, + Traits: [][]byte{ + vmtest.TestKeys[0].Address().Bytes(), + }, + }}}})) + testCase.testCase(t, vm, address, key) + vm.Shutdown(context.Background()) + }) + } +} diff --git a/plugin/evm/block_builder.go b/plugin/evm/block_builder.go index f7c4a2858e..d281ddb4fa 100644 --- a/plugin/evm/block_builder.go +++ b/plugin/evm/block_builder.go @@ -20,11 +20,9 @@ import ( "github.com/ava-labs/libevm/log" ) -const ( - // Minimum amount of time to wait after building a block before attempting to build a block - // a second time without changing the contents of the mempool. - minBlockBuildingRetryDelay = 500 * time.Millisecond -) +// Minimum amount of time to wait after building a block before attempting to build a block +// a second time without changing the contents of the mempool. +const MinBlockBuildingRetryDelay = 500 * time.Millisecond type blockBuilder struct { ctx *snow.Context @@ -122,13 +120,13 @@ func (b *blockBuilder) waitForEvent(ctx context.Context) (commonEng.Message, err return 0, err } timeSinceLastBuildTime := time.Since(lastBuildTime) - if b.lastBuildTime.IsZero() || timeSinceLastBuildTime >= minBlockBuildingRetryDelay { + if b.lastBuildTime.IsZero() || timeSinceLastBuildTime >= MinBlockBuildingRetryDelay { b.ctx.Log.Debug("Last time we built a block was long enough ago, no need to wait", zap.Duration("timeSinceLastBuildTime", timeSinceLastBuildTime), ) return commonEng.PendingTxs, nil } - timeUntilNextBuild := minBlockBuildingRetryDelay - timeSinceLastBuildTime + timeUntilNextBuild := MinBlockBuildingRetryDelay - timeSinceLastBuildTime b.ctx.Log.Debug("Last time we built a block was too recent, waiting", zap.Duration("timeUntilNextBuild", timeUntilNextBuild), ) diff --git a/plugin/evm/extension/config.go b/plugin/evm/extension/config.go index 2e905fcfa1..833707a448 100644 --- a/plugin/evm/extension/config.go +++ b/plugin/evm/extension/config.go @@ -19,13 +19,14 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/ava-labs/coreth/consensus/dummy" - "github.com/ava-labs/coreth/core" + "github.com/ava-labs/coreth/eth" "github.com/ava-labs/coreth/params" "github.com/ava-labs/coreth/params/extras" "github.com/ava-labs/coreth/plugin/evm/config" "github.com/ava-labs/coreth/plugin/evm/message" synccommon "github.com/ava-labs/coreth/sync" "github.com/ava-labs/coreth/sync/handlers" + vmsync "github.com/ava-labs/coreth/sync/vm" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/types" @@ -46,7 +47,9 @@ type ExtensibleVM interface { NewClient(protocol uint64, options ...p2p.ClientOption) *p2p.Client // AddHandler registers a server handler for an application protocol AddHandler(protocol uint64, handler p2p.Handler) error - // GetExtendedBlock returns the VMBlock for the given ID or an error if the block is not found + // SetLastAcceptedBlock sets the last accepted block + SetLastAcceptedBlock(lastAcceptedBlock snowman.Block) error + // GetExtendedBlock returns the ExtendedBlock for the given ID or an error if the block is not found GetExtendedBlock(context.Context, ids.ID) (ExtendedBlock, error) // LastAcceptedExtendedBlock returns the last accepted VM block LastAcceptedExtendedBlock() ExtendedBlock @@ -54,8 +57,8 @@ type ExtensibleVM interface { ChainConfig() *params.ChainConfig // P2PValidators returns the validators for the network P2PValidators() *p2p.Validators - // Blockchain returns the blockchain client - Blockchain() *core.BlockChain + // Ethereum returns the Ethereum service + Ethereum() *eth.Ethereum // Config returns the configuration for the VM Config() config.Config // MetricRegistry returns the metric registry for the VM @@ -64,6 +67,8 @@ type ExtensibleVM interface { ReadLastAccepted() (common.Hash, uint64, error) // VersionDB returns the versioned database for the VM VersionDB() *versiondb.Database + // SyncerClient returns the syncer client for the VM + SyncerClient() vmsync.Client } // InnerVM is the interface that must be implemented by the VM diff --git a/plugin/evm/gossiper_eth_gossiping_test.go b/plugin/evm/gossiper_eth_gossiping_test.go index fefe1f3a10..883d5e67fb 100644 --- a/plugin/evm/gossiper_eth_gossiping_test.go +++ b/plugin/evm/gossiper_eth_gossiping_test.go @@ -25,6 +25,7 @@ import ( "github.com/ava-labs/coreth/core" "github.com/ava-labs/coreth/params" + "github.com/ava-labs/coreth/plugin/evm/vmtest" "github.com/ava-labs/libevm/core/types" ) @@ -83,27 +84,28 @@ func TestMempoolEthTxsAppGossipHandling(t *testing.T) { genesisJSON, err := fundAddressByGenesis([]common.Address{addr}) assert.NoError(err) - tvm := newVM(t, testVMConfig{ - genesisJSON: genesisJSON, + vm := newDefaultTestVM() + tvm := vmtest.SetupTestVM(t, vm, vmtest.TestVMConfig{ + GenesisJSON: genesisJSON, }) defer func() { - err := tvm.vm.Shutdown(context.Background()) + err := vm.Shutdown(context.Background()) assert.NoError(err) }() - tvm.vm.txPool.SetGasTip(common.Big1) - tvm.vm.txPool.SetMinFee(common.Big0) + vm.txPool.SetGasTip(common.Big1) + vm.txPool.SetMinFee(common.Big0) var ( wg sync.WaitGroup txRequested bool ) - tvm.appSender.CantSendAppGossip = false - tvm.appSender.SendAppRequestF = func(context.Context, set.Set[ids.NodeID], uint32, []byte) error { + tvm.AppSender.CantSendAppGossip = false + tvm.AppSender.SendAppRequestF = func(context.Context, set.Set[ids.NodeID], uint32, []byte) error { txRequested = true return nil } wg.Add(1) - tvm.appSender.SendAppGossipF = func(context.Context, commonEng.SendConfig, []byte) error { + tvm.AppSender.SendAppGossipF = func(context.Context, commonEng.SendConfig, []byte) error { wg.Done() return nil } @@ -113,7 +115,7 @@ func TestMempoolEthTxsAppGossipHandling(t *testing.T) { // Txs must be submitted over the API to be included in push gossip. // (i.e., txs received via p2p are not included in push gossip) - err = tvm.vm.eth.APIBackend.SendTx(context.Background(), tx) + err = vm.eth.APIBackend.SendTx(context.Background(), tx) assert.NoError(err) assert.False(txRequested, "tx should not be requested") diff --git a/plugin/evm/mempool_atomic_gossiping_test.go b/plugin/evm/mempool_atomic_gossiping_test.go deleted file mode 100644 index 6965d2a00e..0000000000 --- a/plugin/evm/mempool_atomic_gossiping_test.go +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright (C) 2019-2025, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package evm - -import ( - "context" - "testing" - - "github.com/ava-labs/coreth/plugin/evm/atomic" - atomictxpool "github.com/ava-labs/coreth/plugin/evm/atomic/txpool" - - "github.com/ava-labs/avalanchego/snow/engine/common" - "github.com/ava-labs/avalanchego/upgrade/upgradetest" - "github.com/ava-labs/avalanchego/vms/components/chain" - "github.com/ava-labs/coreth/plugin/evm/extension" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -// shows that a locally generated AtomicTx can be added to mempool and then -// removed by inclusion in a block -func TestMempoolAddLocallyCreateAtomicTx(t *testing.T) { - for _, name := range []string{"import", "export"} { - t.Run(name, func(t *testing.T) { - assert := assert.New(t) - - // we use AP3 here to not trip any block fees - fork := upgradetest.ApricotPhase3 - tvm := newVM(t, testVMConfig{ - fork: &fork, - }) - defer func() { - err := tvm.vm.Shutdown(context.Background()) - assert.NoError(err) - }() - mempool := tvm.atomicVM.AtomicMempool - - // generate a valid and conflicting tx - var ( - tx, conflictingTx *atomic.Tx - ) - if name == "import" { - importTxs := createImportTxOptions(t, tvm.atomicVM, tvm.atomicMemory) - tx, conflictingTx = importTxs[0], importTxs[1] - } else { - exportTxs := createExportTxOptions(t, tvm.atomicVM, tvm.atomicMemory) - tx, conflictingTx = exportTxs[0], exportTxs[1] - } - txID := tx.ID() - conflictingTxID := conflictingTx.ID() - - // add a tx to the mempool - err := tvm.atomicVM.AtomicMempool.AddLocalTx(tx) - assert.NoError(err) - has := mempool.Has(txID) - assert.True(has, "valid tx not recorded into mempool") - - // try to add a conflicting tx - err = tvm.atomicVM.AtomicMempool.AddLocalTx(conflictingTx) - assert.ErrorIs(err, atomictxpool.ErrConflict) - has = mempool.Has(conflictingTxID) - assert.False(has, "conflicting tx in mempool") - - require.Equal(t, common.PendingTxs, tvm.WaitForEvent(context.Background())) - - has = mempool.Has(txID) - assert.True(has, "valid tx not recorded into mempool") - - // Show that BuildBlock generates a block containing [txID] and that it is - // still present in the mempool. - blk, err := tvm.vm.BuildBlock(context.Background()) - assert.NoError(err, "could not build block out of mempool") - - wrappedBlock, ok := blk.(*chain.BlockWrapper).Block.(extension.ExtendedBlock) - assert.True(ok, "unknown block type") - - blockExtension, ok := wrappedBlock.GetBlockExtension().(atomic.AtomicBlockContext) - assert.True(ok, "unknown block extension type") - - assert.Equal(txID, blockExtension.AtomicTxs()[0].ID(), "block does not include expected transaction") - - has = mempool.Has(txID) - assert.True(has, "tx should stay in mempool until block is accepted") - - err = blk.Verify(context.Background()) - assert.NoError(err) - - err = blk.Accept(context.Background()) - assert.NoError(err) - - has = mempool.Has(txID) - assert.False(has, "tx shouldn't be in mempool after block is accepted") - }) - } -} diff --git a/plugin/evm/syncervm_test.go b/plugin/evm/syncervm_test.go index 88975d4daa..8b90ef8e5c 100644 --- a/plugin/evm/syncervm_test.go +++ b/plugin/evm/syncervm_test.go @@ -4,675 +4,45 @@ package evm import ( - "context" - "fmt" - "math/big" - "math/rand" - "sync" "testing" - "time" - - "github.com/ava-labs/coreth/plugin/evm/atomic" - "github.com/ava-labs/coreth/plugin/evm/atomic/atomictest" - atomicvm "github.com/ava-labs/coreth/plugin/evm/atomic/vm" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/ava-labs/avalanchego/api/metrics" - avalancheatomic "github.com/ava-labs/avalanchego/chains/atomic" - avalanchedatabase "github.com/ava-labs/avalanchego/database" - "github.com/ava-labs/avalanchego/database/prefixdb" - "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/avalanchego/snow" - commonEng "github.com/ava-labs/avalanchego/snow/engine/common" - "github.com/ava-labs/avalanchego/snow/engine/enginetest" - "github.com/ava-labs/avalanchego/snow/engine/snowman/block" - "github.com/ava-labs/avalanchego/upgrade/upgradetest" - "github.com/ava-labs/avalanchego/utils/crypto/secp256k1" - "github.com/ava-labs/avalanchego/utils/set" - "github.com/ava-labs/avalanchego/utils/units" "github.com/ava-labs/coreth/consensus/dummy" - "github.com/ava-labs/coreth/constants" "github.com/ava-labs/coreth/core" - "github.com/ava-labs/coreth/core/coretest" - "github.com/ava-labs/coreth/plugin/evm/customrawdb" - "github.com/ava-labs/coreth/plugin/evm/customtypes" - "github.com/ava-labs/coreth/plugin/evm/database" + "github.com/ava-labs/coreth/params" + "github.com/ava-labs/coreth/plugin/evm/extension" + "github.com/ava-labs/coreth/plugin/evm/vmtest" "github.com/ava-labs/coreth/predicate" - statesyncclient "github.com/ava-labs/coreth/sync/client" - "github.com/ava-labs/coreth/sync/statesync/statesynctest" - syncervm "github.com/ava-labs/coreth/sync/vm" - "github.com/ava-labs/coreth/utils/utilstest" + "github.com/ava-labs/libevm/common" - "github.com/ava-labs/libevm/core/rawdb" "github.com/ava-labs/libevm/core/types" - "github.com/ava-labs/libevm/ethdb" - "github.com/ava-labs/libevm/log" - ethparams "github.com/ava-labs/libevm/params" - "github.com/ava-labs/libevm/rlp" - "github.com/ava-labs/libevm/trie" - "github.com/ava-labs/libevm/triedb" -) - -func TestSkipStateSync(t *testing.T) { - rand.Seed(1) - test := syncTest{ - syncableInterval: 256, - stateSyncMinBlocks: 300, // must be greater than [syncableInterval] to skip sync - syncMode: block.StateSyncSkipped, - } - vmSetup := createSyncServerAndClientVMs(t, test, syncervm.BlocksToFetch) - - testSyncerVM(t, vmSetup, test) -} - -func TestStateSyncFromScratch(t *testing.T) { - rand.Seed(1) - test := syncTest{ - syncableInterval: 256, - stateSyncMinBlocks: 50, // must be less than [syncableInterval] to perform sync - syncMode: block.StateSyncStatic, - } - vmSetup := createSyncServerAndClientVMs(t, test, syncervm.BlocksToFetch) - - testSyncerVM(t, vmSetup, test) -} - -func TestStateSyncFromScratchExceedParent(t *testing.T) { - rand.Seed(1) - numToGen := syncervm.BlocksToFetch + uint64(32) - test := syncTest{ - syncableInterval: numToGen, - stateSyncMinBlocks: 50, // must be less than [syncableInterval] to perform sync - syncMode: block.StateSyncStatic, - } - vmSetup := createSyncServerAndClientVMs(t, test, int(numToGen)) - - testSyncerVM(t, vmSetup, test) -} - -func TestStateSyncToggleEnabledToDisabled(t *testing.T) { - rand.Seed(1) - - var lock sync.Mutex - reqCount := 0 - test := syncTest{ - syncableInterval: 256, - stateSyncMinBlocks: 50, // must be less than [syncableInterval] to perform sync - syncMode: block.StateSyncStatic, - responseIntercept: func(syncerVM *VM, nodeID ids.NodeID, requestID uint32, response []byte) { - lock.Lock() - defer lock.Unlock() - - reqCount++ - // Fail all requests after number 50 to interrupt the sync - if reqCount > 50 { - if err := syncerVM.AppRequestFailed(context.Background(), nodeID, requestID, commonEng.ErrTimeout); err != nil { - panic(err) - } - if err := syncerVM.Client.Shutdown(); err != nil { - panic(err) - } - } else { - syncerVM.AppResponse(context.Background(), nodeID, requestID, response) - } - }, - expectedErr: context.Canceled, - } - vmSetup := createSyncServerAndClientVMs(t, test, syncervm.BlocksToFetch) - - // Perform sync resulting in early termination. - testSyncerVM(t, vmSetup, test) - - test.syncMode = block.StateSyncStatic - test.responseIntercept = nil - test.expectedErr = nil - - syncDisabledVM := atomicvm.WrapVM(&VM{}) - appSender := &enginetest.Sender{T: t} - appSender.SendAppGossipF = func(context.Context, commonEng.SendConfig, []byte) error { return nil } - appSender.SendAppRequestF = func(ctx context.Context, nodeSet set.Set[ids.NodeID], requestID uint32, request []byte) error { - nodeID, hasItem := nodeSet.Pop() - if !hasItem { - t.Fatal("expected nodeSet to contain at least 1 nodeID") - } - go vmSetup.serverVM.AppRequest(ctx, nodeID, requestID, time.Now().Add(1*time.Second), request) - return nil - } - // Reset metrics to allow re-initialization - vmSetup.syncerVM.ctx.Metrics = metrics.NewPrefixGatherer() - stateSyncDisabledConfigJSON := `{"state-sync-enabled":false}` - genesisJSON := []byte(genesisJSON(forkToChainConfig[upgradetest.Latest])) - if err := syncDisabledVM.Initialize( - context.Background(), - vmSetup.syncerVM.ctx, - vmSetup.syncerDB, - genesisJSON, - nil, - []byte(stateSyncDisabledConfigJSON), - []*commonEng.Fx{}, - appSender, - ); err != nil { - t.Fatal(err) - } - - defer func() { - if err := syncDisabledVM.Shutdown(context.Background()); err != nil { - t.Fatal(err) - } - }() - - if height := syncDisabledVM.LastAcceptedExtendedBlock().Height(); height != 0 { - t.Fatalf("Unexpected last accepted height: %d", height) - } - - enabled, err := syncDisabledVM.StateSyncEnabled(context.Background()) - assert.NoError(t, err) - assert.False(t, enabled, "sync should be disabled") - - // Process the first 10 blocks from the serverVM - for i := uint64(1); i < 10; i++ { - ethBlock := vmSetup.serverVM.blockChain.GetBlockByNumber(i) - if ethBlock == nil { - t.Fatalf("VM Server did not have a block available at height %d", i) - } - b, err := rlp.EncodeToBytes(ethBlock) - if err != nil { - t.Fatal(err) - } - blk, err := syncDisabledVM.ParseBlock(context.Background(), b) - if err != nil { - t.Fatal(err) - } - if err := blk.Verify(context.Background()); err != nil { - t.Fatal(err) - } - if err := blk.Accept(context.Background()); err != nil { - t.Fatal(err) - } - } - // Verify the snapshot disk layer matches the last block root - lastRoot := syncDisabledVM.Blockchain().CurrentBlock().Root - if err := syncDisabledVM.Blockchain().Snapshots().Verify(lastRoot); err != nil { - t.Fatal(err) - } - syncDisabledVM.Blockchain().DrainAcceptorQueue() - - // Create a new VM from the same database with state sync enabled. - syncReEnabledInnerVM := &VM{} - syncReEnabledVM := atomicvm.WrapVM(syncReEnabledInnerVM) - // Enable state sync in configJSON - configJSON := fmt.Sprintf( - `{"state-sync-enabled":true, "state-sync-min-blocks":%d}`, - test.stateSyncMinBlocks, - ) - // Reset metrics to allow re-initialization - vmSetup.syncerVM.ctx.Metrics = metrics.NewPrefixGatherer() - if err := syncReEnabledVM.Initialize( - context.Background(), - vmSetup.syncerVM.ctx, - vmSetup.syncerDB, - genesisJSON, - nil, - []byte(configJSON), - []*commonEng.Fx{}, - appSender, - ); err != nil { - t.Fatal(err) - } - - // override [serverVM]'s SendAppResponse function to trigger AppResponse on [syncerVM] - vmSetup.serverAppSender.SendAppResponseF = func(ctx context.Context, nodeID ids.NodeID, requestID uint32, response []byte) error { - if test.responseIntercept == nil { - go syncReEnabledVM.AppResponse(ctx, nodeID, requestID, response) - } else { - go test.responseIntercept(syncReEnabledInnerVM, nodeID, requestID, response) - } - - return nil - } - - // connect peer to [syncerVM] - assert.NoError(t, syncReEnabledVM.Connected( - context.Background(), - vmSetup.serverVM.ctx.NodeID, - statesyncclient.StateSyncVersion, - )) - - enabled, err = syncReEnabledVM.StateSyncEnabled(context.Background()) - assert.NoError(t, err) - assert.True(t, enabled, "sync should be enabled") - - vmSetup.syncerVM = syncReEnabledInnerVM - testSyncerVM(t, vmSetup, test) -} - -func TestVMShutdownWhileSyncing(t *testing.T) { - var ( - lock sync.Mutex - vmSetup *syncVMSetup - ) - reqCount := 0 - test := syncTest{ - syncableInterval: 256, - stateSyncMinBlocks: 50, // must be less than [syncableInterval] to perform sync - syncMode: block.StateSyncStatic, - responseIntercept: func(syncerVM *VM, nodeID ids.NodeID, requestID uint32, response []byte) { - lock.Lock() - defer lock.Unlock() - - reqCount++ - // Shutdown the VM after 50 requests to interrupt the sync - if reqCount == 50 { - // Note this verifies the VM shutdown does not time out while syncing. - require.NoError(t, vmSetup.shutdownOnceSyncerVM.Shutdown(context.Background())) - } else if reqCount < 50 { - require.NoError(t, syncerVM.AppResponse(context.Background(), nodeID, requestID, response)) - } - }, - expectedErr: context.Canceled, - } - vmSetup = createSyncServerAndClientVMs(t, test, syncervm.BlocksToFetch) - // Perform sync resulting in early termination. - testSyncerVM(t, vmSetup, test) -} - -func createSyncServerAndClientVMs(t *testing.T, test syncTest, numBlocks int) *syncVMSetup { - var ( - require = require.New(t) - importAmount = 2000000 * units.Avax // 2M avax - alloc = map[ids.ShortID]uint64{ - testShortIDAddrs[0]: importAmount, - } - ) - configJSON := fmt.Sprintf(`{"commit-interval": %d, "state-sync-commit-interval": %d}`, test.syncableInterval, test.syncableInterval) - server := newVM(t, testVMConfig{ - utxos: alloc, - configJSON: configJSON, - }) - t.Cleanup(func() { - log.Info("Shutting down server VM") - require.NoError(server.vm.Shutdown(context.Background())) - }) - var ( - importTx, exportTx *atomic.Tx - err error - ) - generateAndAcceptBlocks(t, server.vm, numBlocks, func(i int, gen *core.BlockGen) { - b, err := predicate.NewResults().Bytes() - if err != nil { - t.Fatal(err) - } - gen.AppendExtra(b) - switch i { - case 0: - // spend the UTXOs from shared memory - importTx, err = server.atomicVM.NewImportTx(server.vm.ctx.XChainID, testEthAddrs[0], initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) - require.NoError(err) - require.NoError(server.atomicVM.AtomicMempool.AddLocalTx(importTx)) - case 1: - // export some of the imported UTXOs to test exportTx is properly synced - exportTx, err = server.atomicVM.NewExportTx( - server.vm.ctx.AVAXAssetID, - importAmount/2, - server.vm.ctx.XChainID, - testShortIDAddrs[0], - initialBaseFee, - []*secp256k1.PrivateKey{testKeys[0]}, - ) - require.NoError(err) - require.NoError(server.atomicVM.AtomicMempool.AddLocalTx(exportTx)) - default: // Generate simple transfer transactions. - pk := testKeys[0].ToECDSA() - tx := types.NewTransaction(gen.TxNonce(testEthAddrs[0]), testEthAddrs[1], common.Big1, ethparams.TxGas, initialBaseFee, nil) - signedTx, err := types.SignTx(tx, types.NewEIP155Signer(server.vm.chainID), pk) - require.NoError(err) - gen.AddTx(signedTx) - } - }, nil) - - // override serverAtomicTrie's commitInterval so the call to [serverAtomicTrie.Index] - // creates a commit at the height [syncableInterval]. This is necessary to support - // fetching a state summary. - serverAtomicTrie := server.atomicVM.AtomicBackend.AtomicTrie() - require.NoError(serverAtomicTrie.Commit(test.syncableInterval, serverAtomicTrie.LastAcceptedRoot())) - require.NoError(server.vm.versiondb.Commit()) - - serverSharedMemories := atomictest.NewSharedMemories(server.atomicMemory, server.vm.ctx.ChainID, server.vm.ctx.XChainID) - importOps, err := atomictest.ConvertToAtomicOps(importTx) - require.NoError(err) - exportOps, err := atomictest.ConvertToAtomicOps(exportTx) - require.NoError(err) - serverSharedMemories.AssertOpsApplied(t, importOps) - serverSharedMemories.AssertOpsApplied(t, exportOps) - // make some accounts - trieDB := triedb.NewDatabase(server.vm.chaindb, nil) - root, accounts := statesynctest.FillAccountsWithOverlappingStorage(t, trieDB, types.EmptyRootHash, 1000, 16) - - // patch serverVM's lastAcceptedBlock to have the new root - // and update the vm's state so the trie with accounts will - // be returned by StateSyncGetLastSummary - lastAccepted := server.vm.blockChain.LastAcceptedBlock() - patchedBlock := patchBlock(lastAccepted, root, server.vm.chaindb) - blockBytes, err := rlp.EncodeToBytes(patchedBlock) - require.NoError(err) - internalBlock, err := server.vm.parseBlock(context.Background(), blockBytes) - require.NoError(err) - require.NoError(server.vm.State.SetLastAcceptedBlock(internalBlock)) - - // initialise [syncerVM] with blank genesis state - stateSyncEnabledJSON := fmt.Sprintf(`{"state-sync-enabled":true, "state-sync-min-blocks": %d, "tx-lookup-limit": %d, "commit-interval": %d}`, test.stateSyncMinBlocks, 4, test.syncableInterval) - syncer := newVM(t, testVMConfig{ - isSyncing: true, - configJSON: stateSyncEnabledJSON, - utxos: alloc, - }) - shutdownOnceSyncerVM := &shutdownOnceVM{VM: syncer.vm} - t.Cleanup(func() { - require.NoError(shutdownOnceSyncerVM.Shutdown(context.Background())) - }) - require.NoError(syncer.vm.SetState(context.Background(), snow.StateSyncing)) - enabled, err := syncer.vm.StateSyncEnabled(context.Background()) - require.NoError(err) - require.True(enabled) - - // override [serverVM]'s SendAppResponse function to trigger AppResponse on [syncerVM] - server.appSender.SendAppResponseF = func(ctx context.Context, nodeID ids.NodeID, requestID uint32, response []byte) error { - if test.responseIntercept == nil { - go syncer.vm.AppResponse(ctx, nodeID, requestID, response) - } else { - go test.responseIntercept(syncer.vm, nodeID, requestID, response) - } - - return nil - } - - // connect peer to [syncerVM] - require.NoError( - syncer.vm.Connected( - context.Background(), - server.vm.ctx.NodeID, - statesyncclient.StateSyncVersion, - ), - ) - - // override [syncerVM]'s SendAppRequest function to trigger AppRequest on [serverVM] - syncer.appSender.SendAppRequestF = func(ctx context.Context, nodeSet set.Set[ids.NodeID], requestID uint32, request []byte) error { - nodeID, hasItem := nodeSet.Pop() - require.True(hasItem, "expected nodeSet to contain at least 1 nodeID") - require.NoError(server.vm.AppRequest(ctx, nodeID, requestID, time.Now().Add(1*time.Second), request)) - return nil - } - - return &syncVMSetup{ - serverVM: server.vm, - serverAppSender: server.appSender, - includedAtomicTxs: []*atomic.Tx{ - importTx, - exportTx, - }, - fundedAccounts: accounts, - syncerVM: syncer.vm, - syncerDB: syncer.db, - syncerAtomicMemory: syncer.atomicMemory, - shutdownOnceSyncerVM: shutdownOnceSyncerVM, - } -} - -// syncVMSetup contains the required set up for a client VM to perform state sync -// off of a server VM. -type syncVMSetup struct { - serverVM *VM - serverAppSender *enginetest.Sender - - includedAtomicTxs []*atomic.Tx - fundedAccounts map[*utilstest.Key]*types.StateAccount - - syncerVM *VM - syncerDB avalanchedatabase.Database - syncerAtomicMemory *avalancheatomic.Memory - shutdownOnceSyncerVM *shutdownOnceVM -} - -type shutdownOnceVM struct { - *VM - shutdownOnce sync.Once -} - -func (vm *shutdownOnceVM) Shutdown(ctx context.Context) error { - var err error - vm.shutdownOnce.Do(func() { err = vm.VM.Shutdown(ctx) }) - return err -} - -// syncTest contains both the actual VMs as well as the parameters with the expected output. -type syncTest struct { - responseIntercept func(vm *VM, nodeID ids.NodeID, requestID uint32, response []byte) - stateSyncMinBlocks uint64 - syncableInterval uint64 - syncMode block.StateSyncMode - expectedErr error -} - -func testSyncerVM(t *testing.T, vmSetup *syncVMSetup, test syncTest) { - t.Helper() - var ( - require = require.New(t) - serverVM = vmSetup.serverVM - includedAtomicTxs = vmSetup.includedAtomicTxs - fundedAccounts = vmSetup.fundedAccounts - syncerVM = vmSetup.syncerVM - syncerAtomicMemory = vmSetup.syncerAtomicMemory - ) - // get last summary and test related methods - summary, err := serverVM.GetLastStateSummary(context.Background()) - require.NoError(err, "error getting state sync last summary") - parsedSummary, err := syncerVM.ParseStateSummary(context.Background(), summary.Bytes()) - require.NoError(err, "error parsing state summary") - retrievedSummary, err := serverVM.GetStateSummary(context.Background(), parsedSummary.Height()) - require.NoError(err, "error getting state sync summary at height") - require.Equal(summary, retrievedSummary) - - syncMode, err := parsedSummary.Accept(context.Background()) - require.NoError(err, "error accepting state summary") - require.Equal(test.syncMode, syncMode) - if syncMode == block.StateSyncSkipped { - return - } - - msg, err := syncerVM.WaitForEvent(context.Background()) - require.Equal(commonEng.StateSyncDone, msg) - require.NoError(err) - - // If the test is expected to error, assert the correct error is returned and finish the test. - err = syncerVM.Client.Error() - if test.expectedErr != nil { - require.ErrorIs(err, test.expectedErr) - // Note we re-open the database here to avoid a closed error when the test is for a shutdown VM. - chaindb := database.WrapDatabase(prefixdb.NewNested(ethDBPrefix, syncerVM.db)) - assertSyncPerformedHeights(t, chaindb, map[uint64]struct{}{}) - return - } - require.NoError(err, "state sync failed") - - // set [syncerVM] to bootstrapping and verify the last accepted block has been updated correctly - // and that we can bootstrap and process some blocks. - require.NoError(syncerVM.SetState(context.Background(), snow.Bootstrapping)) - require.Equal(serverVM.LastAcceptedBlock().Height(), syncerVM.LastAcceptedBlock().Height(), "block height mismatch between syncer and server") - require.Equal(serverVM.LastAcceptedBlock().ID(), syncerVM.LastAcceptedBlock().ID(), "blockID mismatch between syncer and server") - require.True(syncerVM.blockChain.HasState(syncerVM.blockChain.LastAcceptedBlock().Root()), "unavailable state for last accepted block") - assertSyncPerformedHeights(t, syncerVM.chaindb, map[uint64]struct{}{retrievedSummary.Height(): {}}) - - lastNumber := syncerVM.blockChain.LastAcceptedBlock().NumberU64() - // check the last block is indexed - lastSyncedBlock := rawdb.ReadBlock(syncerVM.chaindb, rawdb.ReadCanonicalHash(syncerVM.chaindb, lastNumber), lastNumber) - for _, tx := range lastSyncedBlock.Transactions() { - index := rawdb.ReadTxLookupEntry(syncerVM.chaindb, tx.Hash()) - require.NotNilf(index, "Miss transaction indices, number %d hash %s", lastNumber, tx.Hash().Hex()) - } - - // tail should be the last block synced - if syncerVM.ethConfig.TransactionHistory != 0 { - tail := lastSyncedBlock.NumberU64() - - coretest.CheckTxIndices(t, &tail, tail, tail, tail, syncerVM.chaindb, true) - } + "github.com/stretchr/testify/require" +) - blocksToBuild := 10 - txsPerBlock := 10 - toAddress := testEthAddrs[1] // arbitrary choice - generateAndAcceptBlocks(t, syncerVM, blocksToBuild, func(_ int, gen *core.BlockGen) { - b, err := predicate.NewResults().Bytes() - if err != nil { - t.Fatal(err) - } - gen.AppendExtra(b) - i := 0 - for k := range fundedAccounts { - tx := types.NewTransaction(gen.TxNonce(k.Address), toAddress, big.NewInt(1), 21000, initialBaseFee, nil) - signedTx, err := types.SignTx(tx, types.NewEIP155Signer(serverVM.chainConfig.ChainID), k.PrivateKey) - require.NoError(err) - gen.AddTx(signedTx) - i++ - if i >= txsPerBlock { - break +func TestEVMSyncerVM(t *testing.T) { + for _, test := range vmtest.SyncerVMTests { + t.Run(test.Name, func(t *testing.T) { + genFn := func(i int, vm extension.InnerVM, gen *core.BlockGen) { + b, err := predicate.NewResults().Bytes() + require.NoError(t, err) + gen.AppendExtra(b) + + tx := types.NewTransaction(gen.TxNonce(vmtest.TestEthAddrs[0]), vmtest.TestEthAddrs[1], common.Big1, params.TxGas, vmtest.InitialBaseFee, nil) + signedTx, err := types.SignTx(tx, types.NewEIP155Signer(vm.Ethereum().BlockChain().Config().ChainID), vmtest.TestKeys[0].ToECDSA()) + require.NoError(t, err) + gen.AddTx(signedTx) } - } - }, - func(block *types.Block) { - if syncerVM.ethConfig.TransactionHistory != 0 { - tail := block.NumberU64() - syncerVM.ethConfig.TransactionHistory + 1 - // tail should be the minimum last synced block, since we skipped it to the last block - if tail < lastSyncedBlock.NumberU64() { - tail = lastSyncedBlock.NumberU64() - } - coretest.CheckTxIndices(t, &tail, tail, block.NumberU64(), block.NumberU64(), syncerVM.chaindb, true) + newVMFn := func() (extension.InnerVM, dummy.ConsensusCallbacks) { + vm := newDefaultTestVM() + return vm, vm.extensionConfig.ConsensusCallbacks } - }, - ) - - // check we can transition to [NormalOp] state and continue to process blocks. - require.NoError(syncerVM.SetState(context.Background(), snow.NormalOp)) - require.True(syncerVM.bootstrapped.Get()) - - // check atomic memory was synced properly - syncerSharedMemories := atomictest.NewSharedMemories(syncerAtomicMemory, syncerVM.ctx.ChainID, syncerVM.ctx.XChainID) - - for _, tx := range includedAtomicTxs { - atomicOps, err := atomictest.ConvertToAtomicOps(tx) - require.NoError(err) - syncerSharedMemories.AssertOpsApplied(t, atomicOps) - } - // Generate blocks after we have entered normal consensus as well - generateAndAcceptBlocks(t, syncerVM, blocksToBuild, func(_ int, gen *core.BlockGen) { - b, err := predicate.NewResults().Bytes() - if err != nil { - t.Fatal(err) - } - gen.AppendExtra(b) - i := 0 - for k := range fundedAccounts { - tx := types.NewTransaction(gen.TxNonce(k.Address), toAddress, big.NewInt(1), 21000, initialBaseFee, nil) - signedTx, err := types.SignTx(tx, types.NewEIP155Signer(serverVM.chainConfig.ChainID), k.PrivateKey) - require.NoError(err) - gen.AddTx(signedTx) - i++ - if i >= txsPerBlock { - break + testSetup := &vmtest.SyncTestSetup{ + NewVM: newVMFn, + GenFn: genFn, + ExtraSyncerVMTest: nil, } - } - }, - func(block *types.Block) { - if syncerVM.ethConfig.TransactionHistory != 0 { - tail := block.NumberU64() - syncerVM.ethConfig.TransactionHistory + 1 - // tail should be the minimum last synced block, since we skipped it to the last block - if tail < lastSyncedBlock.NumberU64() { - tail = lastSyncedBlock.NumberU64() - } - coretest.CheckTxIndices(t, &tail, tail, block.NumberU64(), block.NumberU64(), syncerVM.chaindb, true) - } - }, - ) -} - -// patchBlock returns a copy of [blk] with [root] and updates [db] to -// include the new block as canonical for [blk]'s height. -// This breaks the digestibility of the chain since after this call -// [blk] does not necessarily define a state transition from its parent -// state to the new state root. -func patchBlock(blk *types.Block, root common.Hash, db ethdb.Database) *types.Block { - header := blk.Header() - header.Root = root - receipts := rawdb.ReadRawReceipts(db, blk.Hash(), blk.NumberU64()) - newBlk := customtypes.NewBlockWithExtData( - header, blk.Transactions(), blk.Uncles(), receipts, trie.NewStackTrie(nil), customtypes.BlockExtData(blk), true, - ) - rawdb.WriteBlock(db, newBlk) - rawdb.WriteCanonicalHash(db, newBlk.Hash(), newBlk.NumberU64()) - return newBlk -} - -// generateAndAcceptBlocks uses [core.GenerateChain] to generate blocks, then -// calls Verify and Accept on each generated block -// TODO: consider using this helper function in vm_test.go and elsewhere in this package to clean up tests -func generateAndAcceptBlocks(t *testing.T, vm *VM, numBlocks int, gen func(int, *core.BlockGen), accepted func(*types.Block)) { - t.Helper() - - // acceptExternalBlock defines a function to parse, verify, and accept a block once it has been - // generated by GenerateChain - acceptExternalBlock := func(block *types.Block) { - bytes, err := rlp.EncodeToBytes(block) - if err != nil { - t.Fatal(err) - } - vmBlock, err := vm.ParseBlock(context.Background(), bytes) - if err != nil { - t.Fatal(err) - } - if err := vmBlock.Verify(context.Background()); err != nil { - t.Fatal(err) - } - if err := vmBlock.Accept(context.Background()); err != nil { - t.Fatal(err) - } - - if accepted != nil { - accepted(block) - } - } - _, _, err := core.GenerateChain( - vm.chainConfig, - vm.blockChain.LastAcceptedBlock(), - dummy.NewFakerWithCallbacks(vm.extensionConfig.ConsensusCallbacks), - vm.chaindb, - numBlocks, - 10, - func(i int, g *core.BlockGen) { - g.SetOnBlockGenerated(acceptExternalBlock) - g.SetCoinbase(constants.BlackholeAddr) // necessary for syntactic validation of the block - gen(i, g) - }, - ) - if err != nil { - t.Fatal(err) - } - vm.blockChain.DrainAcceptorQueue() -} - -// assertSyncPerformedHeights iterates over all heights the VM has synced to and -// verifies it matches [expected]. -func assertSyncPerformedHeights(t *testing.T, db ethdb.Iteratee, expected map[uint64]struct{}) { - it := customrawdb.NewSyncPerformedIterator(db) - defer it.Release() - - found := make(map[uint64]struct{}, len(expected)) - for it.Next() { - found[customrawdb.UnpackSyncPerformedKey(it.Key())] = struct{}{} + test.TestFunc(t, testSetup) + }) } - require.NoError(t, it.Error()) - require.Equal(t, expected, found) } diff --git a/plugin/evm/tx_gossip_test.go b/plugin/evm/tx_gossip_test.go index 490d4af017..b6a831e4b3 100644 --- a/plugin/evm/tx_gossip_test.go +++ b/plugin/evm/tx_gossip_test.go @@ -11,10 +11,6 @@ import ( "testing" "time" - "github.com/ava-labs/coreth/plugin/evm/atomic" - atomicvm "github.com/ava-labs/coreth/plugin/evm/atomic/vm" - - avalancheatomic "github.com/ava-labs/avalanchego/chains/atomic" "github.com/ava-labs/avalanchego/database/memdb" "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/network/p2p" @@ -31,13 +27,11 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/stretchr/testify/require" - "github.com/ava-labs/avalanchego/vms/components/avax" - "github.com/ava-labs/avalanchego/vms/secp256k1fx" - "google.golang.org/protobuf/proto" "github.com/ava-labs/coreth/plugin/evm/config" "github.com/ava-labs/coreth/plugin/evm/upgrade/ap0" + "github.com/ava-labs/coreth/plugin/evm/vmtest" "github.com/ava-labs/coreth/utils" "github.com/ava-labs/libevm/core/types" ) @@ -52,15 +46,14 @@ func TestEthTxGossip(t *testing.T) { pk, err := secp256k1.NewPrivateKey() require.NoError(err) address := pk.EthAddress() - genesis := newPrefundedGenesis(100_000_000_000_000_000, address) + genesis := vmtest.NewPrefundedGenesis(100_000_000_000_000_000, address) genesisBytes, err := genesis.MarshalJSON() require.NoError(err) responseSender := &enginetest.SenderStub{ SentAppResponse: make(chan []byte, 1), } - innerVM := &VM{} - vm := atomicvm.WrapVM(innerVM) + vm := newDefaultTestVM() require.NoError(vm.Initialize( ctx, @@ -130,17 +123,17 @@ func TestEthTxGossip(t *testing.T) { require.Empty(response.Gossip) wg.Done() } - require.NoError(client.AppRequest(ctx, set.Of(vm.Ctx.NodeID), requestBytes, onResponse)) + require.NoError(client.AppRequest(ctx, set.Of(vm.ctx.NodeID), requestBytes, onResponse)) require.NoError(vm.AppRequest(ctx, requestingNodeID, 1, time.Time{}, <-peerSender.SentAppRequest)) require.NoError(network.AppResponse(ctx, snowCtx.NodeID, 1, <-responseSender.SentAppResponse)) wg.Wait() // Issue a tx to the VM tx := types.NewTransaction(0, address, big.NewInt(10), 100_000, big.NewInt(ap0.MinGasPrice), nil) - signedTx, err := types.SignTx(tx, types.NewEIP155Signer(innerVM.chainID), pk.ToECDSA()) + signedTx, err := types.SignTx(tx, types.NewEIP155Signer(vm.chainID), pk.ToECDSA()) require.NoError(err) - errs := innerVM.txPool.Add([]*types.Transaction{signedTx}, true, true) + errs := vm.txPool.Add([]*types.Transaction{signedTx}, true, true) require.Len(errs, 1) require.Nil(errs[0]) @@ -163,143 +156,7 @@ func TestEthTxGossip(t *testing.T) { wg.Done() } - require.NoError(client.AppRequest(ctx, set.Of(vm.Ctx.NodeID), requestBytes, onResponse)) - require.NoError(vm.AppRequest(ctx, requestingNodeID, 3, time.Time{}, <-peerSender.SentAppRequest)) - require.NoError(network.AppResponse(ctx, snowCtx.NodeID, 3, <-responseSender.SentAppResponse)) - wg.Wait() -} - -func TestAtomicTxGossip(t *testing.T) { - require := require.New(t) - ctx := context.Background() - snowCtx := snowtest.Context(t, snowtest.CChainID) - snowCtx.AVAXAssetID = ids.GenerateTestID() - validatorState := utils.NewTestValidatorState() - snowCtx.ValidatorState = validatorState - memory := avalancheatomic.NewMemory(memdb.New()) - snowCtx.SharedMemory = memory.NewSharedMemory(snowCtx.ChainID) - - pk, err := secp256k1.NewPrivateKey() - require.NoError(err) - address := pk.EthAddress() - genesis := newPrefundedGenesis(100_000_000_000_000_000, address) - genesisBytes, err := genesis.MarshalJSON() - require.NoError(err) - - responseSender := &enginetest.SenderStub{ - SentAppResponse: make(chan []byte, 1), - } - innerVM := &VM{} - vm := atomicvm.WrapVM(innerVM) - - require.NoError(vm.Initialize( - ctx, - snowCtx, - memdb.New(), - genesisBytes, - nil, - nil, - nil, - responseSender, - )) - require.NoError(vm.SetState(ctx, snow.NormalOp)) - - defer func() { - require.NoError(vm.Shutdown(ctx)) - }() - - // sender for the peer requesting gossip from [vm] - peerSender := &enginetest.SenderStub{ - SentAppRequest: make(chan []byte, 1), - } - network, err := p2p.NewNetwork(logging.NoLog{}, peerSender, prometheus.NewRegistry(), "") - require.NoError(err) - client := network.NewClient(p2p.AtomicTxGossipHandlerID) - - // we only accept gossip requests from validators - requestingNodeID := ids.GenerateTestNodeID() - require.NoError(vm.Connected(ctx, requestingNodeID, nil)) - validatorState.GetCurrentHeightF = func(context.Context) (uint64, error) { - return 0, nil - } - validatorState.GetValidatorSetF = func(context.Context, uint64, ids.ID) (map[ids.NodeID]*validators.GetValidatorOutput, error) { - return map[ids.NodeID]*validators.GetValidatorOutput{ - requestingNodeID: { - NodeID: requestingNodeID, - Weight: 1, - }, - }, nil - } - - // Ask the VM for any new transactions. We should get nothing at first. - emptyBloomFilter, err := gossip.NewBloomFilter( - prometheus.NewRegistry(), - "", - config.TxGossipBloomMinTargetElements, - config.TxGossipBloomTargetFalsePositiveRate, - config.TxGossipBloomResetFalsePositiveRate, - ) - require.NoError(err) - emptyBloomFilterBytes, _ := emptyBloomFilter.Marshal() - request := &sdk.PullGossipRequest{ - Filter: emptyBloomFilterBytes, - Salt: agoUtils.RandomBytes(32), - } - - requestBytes, err := proto.Marshal(request) - require.NoError(err) - - wg := &sync.WaitGroup{} - wg.Add(1) - onResponse := func(_ context.Context, nodeID ids.NodeID, responseBytes []byte, err error) { - require.NoError(err) - - response := &sdk.PullGossipResponse{} - require.NoError(proto.Unmarshal(responseBytes, response)) - require.Empty(response.Gossip) - wg.Done() - } - require.NoError(client.AppRequest(ctx, set.Of(vm.Ctx.NodeID), requestBytes, onResponse)) - require.NoError(vm.AppRequest(ctx, requestingNodeID, 1, time.Time{}, <-peerSender.SentAppRequest)) - require.NoError(network.AppResponse(ctx, snowCtx.NodeID, 1, <-responseSender.SentAppResponse)) - wg.Wait() - - // Issue a tx to the VM - utxo, err := addUTXO( - memory, - snowCtx, - ids.GenerateTestID(), - 0, - snowCtx.AVAXAssetID, - 100_000_000_000, - pk.Address(), - ) - require.NoError(err) - tx, err := atomic.NewImportTx(vm.Ctx, vm.CurrentRules(), vm.Clock().Unix(), vm.Ctx.XChainID, address, initialBaseFee, secp256k1fx.NewKeychain(pk), []*avax.UTXO{utxo}) - require.NoError(err) - require.NoError(vm.AtomicMempool.AddLocalTx(tx)) - - // wait so we aren't throttled by the vm - time.Sleep(5 * time.Second) - - // Ask the VM for new transactions. We should get the newly issued tx. - wg.Add(1) - - marshaller := atomic.TxMarshaller{} - onResponse = func(_ context.Context, nodeID ids.NodeID, responseBytes []byte, err error) { - require.NoError(err) - - response := &sdk.PullGossipResponse{} - require.NoError(proto.Unmarshal(responseBytes, response)) - require.Len(response.Gossip, 1) - - gotTx, err := marshaller.UnmarshalGossip(response.Gossip[0]) - require.NoError(err) - require.Equal(tx.ID(), gotTx.GossipID()) - - wg.Done() - } - require.NoError(client.AppRequest(ctx, set.Of(vm.Ctx.NodeID), requestBytes, onResponse)) + require.NoError(client.AppRequest(ctx, set.Of(vm.ctx.NodeID), requestBytes, onResponse)) require.NoError(vm.AppRequest(ctx, requestingNodeID, 3, time.Time{}, <-peerSender.SentAppRequest)) require.NoError(network.AppResponse(ctx, snowCtx.NodeID, 3, <-responseSender.SentAppResponse)) wg.Wait() @@ -314,13 +171,12 @@ func TestEthTxPushGossipOutbound(t *testing.T) { SentAppGossip: make(chan []byte, 1), } - innerVM := &VM{} - vm := atomicvm.WrapVM(innerVM) + vm := newDefaultTestVM() pk, err := secp256k1.NewPrivateKey() require.NoError(err) address := pk.EthAddress() - genesis := newPrefundedGenesis(100_000_000_000_000_000, address) + genesis := vmtest.NewPrefundedGenesis(100_000_000_000_000_000, address) genesisBytes, err := genesis.MarshalJSON() require.NoError(err) @@ -341,12 +197,12 @@ func TestEthTxPushGossipOutbound(t *testing.T) { }() tx := types.NewTransaction(0, address, big.NewInt(10), 100_000, big.NewInt(ap0.MinGasPrice), nil) - signedTx, err := types.SignTx(tx, types.NewEIP155Signer(innerVM.chainID), pk.ToECDSA()) + signedTx, err := types.SignTx(tx, types.NewEIP155Signer(vm.chainID), pk.ToECDSA()) require.NoError(err) // issue a tx - require.NoError(innerVM.txPool.Add([]*types.Transaction{signedTx}, true, true)[0]) - innerVM.ethTxPushGossiper.Get().Add(&GossipEthTx{signedTx}) + require.NoError(vm.txPool.Add([]*types.Transaction{signedTx}, true, true)[0]) + vm.ethTxPushGossiper.Get().Add(&GossipEthTx{signedTx}) sent := <-sender.SentAppGossip got := &sdk.PushGossip{} @@ -370,13 +226,13 @@ func TestEthTxPushGossipInbound(t *testing.T) { snowCtx := snowtest.Context(t, snowtest.CChainID) sender := &enginetest.Sender{} - innerVM := &VM{} - vm := atomicvm.WrapVM(innerVM) + vm := newDefaultTestVM() + vm.ethTxPullGossiper = gossip.NoOpGossiper{} pk, err := secp256k1.NewPrivateKey() require.NoError(err) address := pk.EthAddress() - genesis := newPrefundedGenesis(100_000_000_000_000_000, address) + genesis := vmtest.NewPrefundedGenesis(100_000_000_000_000_000, address) genesisBytes, err := genesis.MarshalJSON() require.NoError(err) @@ -397,7 +253,7 @@ func TestEthTxPushGossipInbound(t *testing.T) { }() tx := types.NewTransaction(0, address, big.NewInt(10), 100_000, big.NewInt(ap0.MinGasPrice), nil) - signedTx, err := types.SignTx(tx, types.NewEIP155Signer(innerVM.chainID), pk.ToECDSA()) + signedTx, err := types.SignTx(tx, types.NewEIP155Signer(vm.chainID), pk.ToECDSA()) require.NoError(err) marshaller := GossipEthTxMarshaller{} @@ -417,143 +273,5 @@ func TestEthTxPushGossipInbound(t *testing.T) { inboundGossipMsg := append(binary.AppendUvarint(nil, p2p.TxGossipHandlerID), inboundGossipBytes...) require.NoError(vm.AppGossip(ctx, ids.EmptyNodeID, inboundGossipMsg)) - require.True(innerVM.txPool.Has(signedTx.Hash())) -} - -// Tests that a tx is gossiped when it is issued -func TestAtomicTxPushGossipOutbound(t *testing.T) { - require := require.New(t) - ctx := context.Background() - snowCtx := snowtest.Context(t, snowtest.CChainID) - snowCtx.AVAXAssetID = ids.GenerateTestID() - validatorState := utils.NewTestValidatorState() - snowCtx.ValidatorState = validatorState - memory := avalancheatomic.NewMemory(memdb.New()) - snowCtx.SharedMemory = memory.NewSharedMemory(snowCtx.ChainID) - - pk, err := secp256k1.NewPrivateKey() - require.NoError(err) - address := pk.EthAddress() - genesis := newPrefundedGenesis(100_000_000_000_000_000, address) - genesisBytes, err := genesis.MarshalJSON() - require.NoError(err) - - sender := &enginetest.SenderStub{ - SentAppGossip: make(chan []byte, 1), - } - innerVM := &VM{} - vm := atomicvm.WrapVM(innerVM) - - require.NoError(vm.Initialize( - ctx, - snowCtx, - memdb.New(), - genesisBytes, - nil, - nil, - nil, - sender, - )) - require.NoError(vm.SetState(ctx, snow.NormalOp)) - - defer func() { - require.NoError(vm.Shutdown(ctx)) - }() - - // Issue a tx to the VM - utxo, err := addUTXO( - memory, - snowCtx, - ids.GenerateTestID(), - 0, - snowCtx.AVAXAssetID, - 100_000_000_000, - pk.Address(), - ) - require.NoError(err) - tx, err := atomic.NewImportTx(vm.Ctx, vm.CurrentRules(), vm.Clock().Unix(), vm.Ctx.XChainID, address, initialBaseFee, secp256k1fx.NewKeychain(pk), []*avax.UTXO{utxo}) - require.NoError(err) - require.NoError(vm.AtomicMempool.AddLocalTx(tx)) - vm.AtomicTxPushGossiper.Add(tx) - - gossipedBytes := <-sender.SentAppGossip - require.Equal(byte(p2p.AtomicTxGossipHandlerID), gossipedBytes[0]) - - outboundGossipMsg := &sdk.PushGossip{} - require.NoError(proto.Unmarshal(gossipedBytes[1:], outboundGossipMsg)) - require.Len(outboundGossipMsg.Gossip, 1) - - marshaller := atomic.TxMarshaller{} - gossipedTx, err := marshaller.UnmarshalGossip(outboundGossipMsg.Gossip[0]) - require.NoError(err) - require.Equal(tx.ID(), gossipedTx.GossipID()) -} - -// Tests that a tx is gossiped when it is issued -func TestAtomicTxPushGossipInbound(t *testing.T) { - require := require.New(t) - ctx := context.Background() - snowCtx := snowtest.Context(t, snowtest.CChainID) - snowCtx.AVAXAssetID = ids.GenerateTestID() - validatorState := utils.NewTestValidatorState() - snowCtx.ValidatorState = validatorState - memory := avalancheatomic.NewMemory(memdb.New()) - snowCtx.SharedMemory = memory.NewSharedMemory(snowCtx.ChainID) - - pk, err := secp256k1.NewPrivateKey() - require.NoError(err) - address := pk.EthAddress() - genesis := newPrefundedGenesis(100_000_000_000_000_000, address) - genesisBytes, err := genesis.MarshalJSON() - require.NoError(err) - - sender := &enginetest.Sender{} - innerVM := &VM{} - vm := atomicvm.WrapVM(innerVM) - - require.NoError(vm.Initialize( - ctx, - snowCtx, - memdb.New(), - genesisBytes, - nil, - nil, - nil, - sender, - )) - require.NoError(vm.SetState(ctx, snow.NormalOp)) - - defer func() { - require.NoError(vm.Shutdown(ctx)) - }() - - // issue a tx to the vm - utxo, err := addUTXO( - memory, - snowCtx, - ids.GenerateTestID(), - 0, - snowCtx.AVAXAssetID, - 100_000_000_000, - pk.Address(), - ) - require.NoError(err) - tx, err := atomic.NewImportTx(vm.Ctx, vm.CurrentRules(), vm.Clock().Unix(), vm.Ctx.XChainID, address, initialBaseFee, secp256k1fx.NewKeychain(pk), []*avax.UTXO{utxo}) - require.NoError(err) - require.NoError(vm.AtomicMempool.AddLocalTx(tx)) - - marshaller := atomic.TxMarshaller{} - gossipBytes, err := marshaller.MarshalGossip(tx) - require.NoError(err) - - inboundGossip := &sdk.PushGossip{ - Gossip: [][]byte{gossipBytes}, - } - inboundGossipBytes, err := proto.Marshal(inboundGossip) - require.NoError(err) - - inboundGossipMsg := append(binary.AppendUvarint(nil, p2p.AtomicTxGossipHandlerID), inboundGossipBytes...) - - require.NoError(vm.AppGossip(ctx, ids.EmptyNodeID, inboundGossipMsg)) - require.True(vm.AtomicMempool.Has(tx.ID())) + require.True(vm.txPool.Has(signedTx.Hash())) } diff --git a/plugin/evm/vm_extensible.go b/plugin/evm/vm_extensible.go index 4b91da9ef3..caf90d330e 100644 --- a/plugin/evm/vm_extensible.go +++ b/plugin/evm/vm_extensible.go @@ -10,7 +10,7 @@ import ( "github.com/ava-labs/avalanchego/database/versiondb" "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/network/p2p" - "github.com/ava-labs/coreth/core" + "github.com/ava-labs/coreth/eth" "github.com/ava-labs/coreth/params" "github.com/ava-labs/coreth/plugin/evm/config" "github.com/ava-labs/coreth/plugin/evm/extension" @@ -65,8 +65,8 @@ func (vm *VM) ChainConfig() *params.ChainConfig { return vm.chainConfig } -func (vm *VM) Blockchain() *core.BlockChain { - return vm.blockChain +func (vm *VM) Ethereum() *eth.Ethereum { + return vm.eth } func (vm *VM) Config() config.Config { diff --git a/plugin/evm/vm_test.go b/plugin/evm/vm_test.go index 6513e18925..f483a71a9f 100644 --- a/plugin/evm/vm_test.go +++ b/plugin/evm/vm_test.go @@ -5,7 +5,6 @@ package evm import ( "context" - "crypto/ecdsa" "encoding/json" "errors" "fmt" @@ -17,96 +16,41 @@ import ( "testing" "time" - ethparams "github.com/ava-labs/libevm/params" - - "github.com/ava-labs/coreth/plugin/evm/atomic" - - atomictxpool "github.com/ava-labs/coreth/plugin/evm/atomic/txpool" - atomicvm "github.com/ava-labs/coreth/plugin/evm/atomic/vm" - - "github.com/ava-labs/avalanchego/api/metrics" - avalancheatomic "github.com/ava-labs/avalanchego/chains/atomic" "github.com/ava-labs/avalanchego/database" - "github.com/ava-labs/avalanchego/database/memdb" - "github.com/ava-labs/avalanchego/database/prefixdb" "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/avalanchego/snow" commonEng "github.com/ava-labs/avalanchego/snow/engine/common" - "github.com/ava-labs/avalanchego/snow/engine/enginetest" "github.com/ava-labs/avalanchego/snow/snowtest" "github.com/ava-labs/avalanchego/upgrade" "github.com/ava-labs/avalanchego/upgrade/upgradetest" - "github.com/ava-labs/avalanchego/utils/crypto/secp256k1" - "github.com/ava-labs/avalanchego/utils/hashing" - "github.com/ava-labs/avalanchego/utils/set" - "github.com/ava-labs/avalanchego/utils/units" - "github.com/ava-labs/avalanchego/vms/components/avax" + "github.com/ava-labs/avalanchego/utils/timer/mockable" "github.com/ava-labs/avalanchego/vms/components/chain" - "github.com/ava-labs/avalanchego/vms/secp256k1fx" "github.com/ava-labs/coreth/consensus/dummy" "github.com/ava-labs/coreth/constants" "github.com/ava-labs/coreth/core" "github.com/ava-labs/coreth/eth" - "github.com/ava-labs/coreth/eth/filters" "github.com/ava-labs/coreth/miner" "github.com/ava-labs/coreth/params" - "github.com/ava-labs/coreth/plugin/evm/customrawdb" "github.com/ava-labs/coreth/plugin/evm/customtypes" "github.com/ava-labs/coreth/plugin/evm/extension" - "github.com/ava-labs/coreth/plugin/evm/header" + "github.com/ava-labs/coreth/plugin/evm/message" "github.com/ava-labs/coreth/plugin/evm/upgrade/acp176" "github.com/ava-labs/coreth/plugin/evm/upgrade/ap0" "github.com/ava-labs/coreth/plugin/evm/upgrade/ap1" - "github.com/ava-labs/coreth/plugin/evm/upgrade/ap3" + "github.com/ava-labs/coreth/plugin/evm/vmtest" "github.com/ava-labs/coreth/rpc" "github.com/ava-labs/coreth/utils" - "github.com/ava-labs/coreth/utils/utilstest" "github.com/ava-labs/libevm/common" - "github.com/ava-labs/libevm/core/rawdb" "github.com/ava-labs/libevm/core/types" "github.com/ava-labs/libevm/log" - "github.com/ava-labs/libevm/rlp" "github.com/ava-labs/libevm/trie" "github.com/holiman/uint256" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + + ethparams "github.com/ava-labs/libevm/params" ) var ( - schemes = []string{rawdb.HashScheme, customrawdb.FirewoodScheme} - initialBaseFee = big.NewInt(ap3.InitialBaseFee) - testKeys = secp256k1.TestKeys()[:3] - testEthAddrs []common.Address // testEthAddrs[i] corresponds to testKeys[i] - testShortIDAddrs []ids.ShortID - - genesisJSON = func(cfg *params.ChainConfig) string { - g := new(core.Genesis) - g.Difficulty = big.NewInt(0) - g.GasLimit = 0x5f5e100 - g.Timestamp = uint64(upgrade.InitiallyActiveTime.Unix()) - - // Use chainId: 43111, so that it does not overlap with any Avalanche ChainIDs, which may have their - // config overridden in vm.Initialize. - cpy := *cfg - cpy.ChainID = big.NewInt(43111) - g.Config = &cpy - - allocStr := `{"0100000000000000000000000000000000000000":{"code":"0x7300000000000000000000000000000000000000003014608060405260043610603d5760003560e01c80631e010439146042578063b6510bb314606e575b600080fd5b605c60048036036020811015605657600080fd5b503560b1565b60408051918252519081900360200190f35b818015607957600080fd5b5060af60048036036080811015608e57600080fd5b506001600160a01b03813516906020810135906040810135906060013560b6565b005b30cd90565b836001600160a01b031681836108fc8690811502906040516000604051808303818888878c8acf9550505050505015801560f4573d6000803e3d6000fd5b505050505056fea26469706673582212201eebce970fe3f5cb96bf8ac6ba5f5c133fc2908ae3dcd51082cfee8f583429d064736f6c634300060a0033","balance":"0x0"}}` - json.Unmarshal([]byte(allocStr), &g.Alloc) - // After Durango, an additional account is funded in tests to use - // with warp messages. - if params.GetExtra(cfg).IsDurango(0) { - addr := common.HexToAddress("0x99b9DEA54C48Dfea6aA9A4Ca4623633EE04ddbB5") - balance := new(big.Int).Mul(big.NewInt(params.Ether), big.NewInt(10)) - g.Alloc[addr] = types.Account{Balance: balance} - } - - b, err := json.Marshal(g) - if err != nil { - panic(err) - } - return string(b) - } + genesisJSONCancun = vmtest.GenesisJSON(activateCancun(params.TestChainConfig)) activateCancun = func(cfg *params.ChainConfig) *params.ChainConfig { cpy := *cfg @@ -114,238 +58,41 @@ var ( cpy.CancunTime = utils.NewUint64(0) return &cpy } - - forkToChainConfig = map[upgradetest.Fork]*params.ChainConfig{ - upgradetest.NoUpgrades: params.TestLaunchConfig, - upgradetest.ApricotPhase1: params.TestApricotPhase1Config, - upgradetest.ApricotPhase2: params.TestApricotPhase2Config, - upgradetest.ApricotPhase3: params.TestApricotPhase3Config, - upgradetest.ApricotPhase4: params.TestApricotPhase4Config, - upgradetest.ApricotPhase5: params.TestApricotPhase5Config, - upgradetest.ApricotPhasePre6: params.TestApricotPhasePre6Config, - upgradetest.ApricotPhase6: params.TestApricotPhase6Config, - upgradetest.ApricotPhasePost6: params.TestApricotPhasePost6Config, - upgradetest.Banff: params.TestBanffChainConfig, - upgradetest.Cortina: params.TestCortinaChainConfig, - upgradetest.Durango: params.TestDurangoChainConfig, - upgradetest.Etna: params.TestEtnaChainConfig, - upgradetest.Fortuna: params.TestFortunaChainConfig, - upgradetest.Granite: params.TestGraniteChainConfig, - } - - genesisJSONCancun = genesisJSON(activateCancun(params.TestChainConfig)) - - apricotRulesPhase0 = *params.GetRulesExtra(params.TestLaunchConfig.Rules(common.Big0, params.IsMergeTODO, 0)) - apricotRulesPhase1 = *params.GetRulesExtra(params.TestApricotPhase1Config.Rules(common.Big0, params.IsMergeTODO, 0)) - apricotRulesPhase2 = *params.GetRulesExtra(params.TestApricotPhase2Config.Rules(common.Big0, params.IsMergeTODO, 0)) - apricotRulesPhase3 = *params.GetRulesExtra(params.TestApricotPhase3Config.Rules(common.Big0, params.IsMergeTODO, 0)) - apricotRulesPhase5 = *params.GetRulesExtra(params.TestApricotPhase5Config.Rules(common.Big0, params.IsMergeTODO, 0)) - apricotRulesPhase6 = *params.GetRulesExtra(params.TestApricotPhase6Config.Rules(common.Big0, params.IsMergeTODO, 0)) - banffRules = *params.GetRulesExtra(params.TestBanffChainConfig.Rules(common.Big0, params.IsMergeTODO, 0)) ) -func init() { - for _, key := range testKeys { - testEthAddrs = append(testEthAddrs, key.EthAddress()) - testShortIDAddrs = append(testShortIDAddrs, key.Address()) - } -} - -func newPrefundedGenesis( - balance int, - addresses ...common.Address, -) *core.Genesis { - alloc := types.GenesisAlloc{} - for _, address := range addresses { - alloc[address] = types.Account{ - Balance: big.NewInt(int64(balance)), - } - } - - return &core.Genesis{ - Config: params.TestChainConfig, - Difficulty: big.NewInt(0), - Alloc: alloc, - } -} - -type testVMConfig struct { - isSyncing bool - fork *upgradetest.Fork - // If genesisJSON is empty, defaults to the genesis corresponding to the - // fork. - genesisJSON string - configJSON string - // the VM will start with UTXOs in the X-Chain Shared Memory containing - // AVAX based on the map - // The UTXOIDs are generated by using a hash of the address in the map such - // that the UTXOs will be generated deterministically. - utxos map[ids.ShortID]uint64 -} - -type testVM struct { - t *testing.T - atomicVM *atomicvm.VM - vm *VM - db *prefixdb.Database - atomicMemory *avalancheatomic.Memory - appSender *enginetest.Sender -} - -func newVM(t *testing.T, config testVMConfig) *testVM { - ctx := snowtest.Context(t, snowtest.CChainID) - fork := upgradetest.Latest - if config.fork != nil { - fork = *config.fork - } - ctx.NetworkUpgrades = upgradetest.GetConfig(fork) - - if len(config.genesisJSON) == 0 { - config.genesisJSON = genesisJSON(forkToChainConfig[fork]) - } - - baseDB := memdb.New() - - // initialize the atomic memory - atomicMemory := avalancheatomic.NewMemory(prefixdb.New([]byte{0}, baseDB)) - ctx.SharedMemory = atomicMemory.NewSharedMemory(ctx.ChainID) - - // NB: this lock is intentionally left locked when this function returns. - // The caller of this function is responsible for unlocking. - ctx.Lock.Lock() - - prefixedDB := prefixdb.New([]byte{1}, baseDB) - - innerVM := &VM{} - atomicVM := atomicvm.WrapVM(innerVM) - appSender := &enginetest.Sender{ - T: t, - CantSendAppGossip: true, - SendAppGossipF: func(context.Context, commonEng.SendConfig, []byte) error { return nil }, - } - require.NoError(t, atomicVM.Initialize( - context.Background(), - ctx, - prefixedDB, - []byte(config.genesisJSON), - nil, - []byte(config.configJSON), - nil, - appSender, - ), "error initializing vm") - - if !config.isSyncing { - require.NoError(t, atomicVM.SetState(context.Background(), snow.Bootstrapping)) - require.NoError(t, atomicVM.SetState(context.Background(), snow.NormalOp)) - } - - for addr, avaxAmount := range config.utxos { - txID, err := ids.ToID(hashing.ComputeHash256(addr.Bytes())) - if err != nil { - t.Fatalf("Failed to generate txID from addr: %s", err) - } - if _, err := addUTXO(atomicMemory, innerVM.ctx, txID, 0, innerVM.ctx.AVAXAssetID, avaxAmount, addr); err != nil { - t.Fatalf("Failed to add UTXO to shared memory: %s", err) - } - } - - return &testVM{ - t: t, - atomicVM: atomicVM, - vm: innerVM, - db: prefixedDB, - atomicMemory: atomicMemory, - appSender: appSender, - } +func defaultExtensions() (*extension.Config, error) { + return &extension.Config{ + SyncSummaryProvider: &message.BlockSyncSummaryProvider{}, + SyncableParser: &message.BlockSyncSummaryParser{}, + Clock: &mockable.Clock{}, + }, nil } -// Firewood cannot yet be run with an empty config. -func getConfig(scheme, otherConfig string) string { - innerConfig := otherConfig - if scheme == customrawdb.FirewoodScheme { - if len(innerConfig) > 0 { - innerConfig += ", " - } - innerConfig += fmt.Sprintf(`"state-scheme": "%s", "snapshot-cache": 0, "pruning-enabled": true, "state-sync-enabled": false, "metrics-expensive-enabled": false`, customrawdb.FirewoodScheme) - } - - return fmt.Sprintf(`{%s}`, innerConfig) -} - -func (vm *testVM) WaitForEvent(ctx context.Context) commonEng.Message { - msg, err := vm.vm.WaitForEvent(ctx) - require.NoError(vm.t, err) - return msg -} - -// setupGenesis sets up the genesis -func setupGenesis( - t *testing.T, - fork upgradetest.Fork, -) (*snow.Context, - *prefixdb.Database, - []byte, - *avalancheatomic.Memory, -) { - ctx := snowtest.Context(t, snowtest.CChainID) - ctx.NetworkUpgrades = upgradetest.GetConfig(fork) - - baseDB := memdb.New() - - // initialize the atomic memory - atomicMemory := avalancheatomic.NewMemory(prefixdb.New([]byte{0}, baseDB)) - ctx.SharedMemory = atomicMemory.NewSharedMemory(ctx.ChainID) - - // NB: this lock is intentionally left locked when this function returns. - // The caller of this function is responsible for unlocking. - ctx.Lock.Lock() - - prefixedDB := prefixdb.New([]byte{1}, baseDB) - genesisJSON := genesisJSON(forkToChainConfig[fork]) - return ctx, prefixedDB, []byte(genesisJSON), atomicMemory -} - -func addUTXO(sharedMemory *avalancheatomic.Memory, ctx *snow.Context, txID ids.ID, index uint32, assetID ids.ID, amount uint64, addr ids.ShortID) (*avax.UTXO, error) { - utxo := &avax.UTXO{ - UTXOID: avax.UTXOID{ - TxID: txID, - OutputIndex: index, - }, - Asset: avax.Asset{ID: assetID}, - Out: &secp256k1fx.TransferOutput{ - Amt: amount, - OutputOwners: secp256k1fx.OutputOwners{ - Threshold: 1, - Addrs: []ids.ShortID{addr}, - }, - }, - } - utxoBytes, err := atomic.Codec.Marshal(atomic.CodecVersion, utxo) +// newDefaultTestVM returns a new instance of the VM with default extensions +// This should not be called if the VM is being extended +func newDefaultTestVM() *VM { + vm := &VM{} + exts, err := defaultExtensions() if err != nil { - return nil, err + panic(err) } - xChainSharedMemory := sharedMemory.NewSharedMemory(ctx.XChainID) - inputID := utxo.InputID() - if err := xChainSharedMemory.Apply(map[ids.ID]*avalancheatomic.Requests{ctx.ChainID: {PutRequests: []*avalancheatomic.Element{{ - Key: inputID[:], - Value: utxoBytes, - Traits: [][]byte{ - addr.Bytes(), - }, - }}}}); err != nil { - return nil, err + if err := vm.SetExtensionConfig(exts); err != nil { + panic(err) } - - return utxo, nil + return vm } func TestVMContinuousProfiler(t *testing.T) { profilerDir := t.TempDir() profilerFrequency := 500 * time.Millisecond - vm := newVM(t, testVMConfig{ - configJSON: fmt.Sprintf(`{"continuous-profiler-dir": %q,"continuous-profiler-frequency": "500ms"}`, profilerDir), - }).vm + configJSON := fmt.Sprintf(`{"continuous-profiler-dir": %q,"continuous-profiler-frequency": "500ms"}`, profilerDir) + fork := upgradetest.Latest + vm := newDefaultTestVM() + vmtest.SetupTestVM(t, vm, vmtest.TestVMConfig{ + Fork: &fork, + ConfigJSON: configJSON, + }) require.Equal(t, vm.config.ContinuousProfilerDir, profilerDir, "profiler dir should be set") require.Equal(t, vm.config.ContinuousProfilerFrequency.Duration, profilerFrequency, "profiler frequency should be set") @@ -361,7 +108,7 @@ func TestVMContinuousProfiler(t *testing.T) { } func TestVMUpgrades(t *testing.T) { - for _, scheme := range schemes { + for _, scheme := range vmtest.Schemes { t.Run(scheme, func(t *testing.T) { testVMUpgrades(t, scheme) }) @@ -414,11 +161,12 @@ func testVMUpgrades(t *testing.T, scheme string) { for _, test := range genesisTests { t.Run(test.fork.String(), func(t *testing.T) { require := require.New(t) + vm := newDefaultTestVM() + vmtest.SetupTestVM(t, vm, vmtest.TestVMConfig{ + Fork: &test.fork, + Scheme: scheme, + }) - vm := newVM(t, testVMConfig{ - fork: &test.fork, - configJSON: getConfig(scheme, ""), - }).vm defer func() { require.NoError(vm.Shutdown(context.Background())) }() @@ -440,179 +188,8 @@ func testVMUpgrades(t *testing.T, scheme string) { } } -func TestImportMissingUTXOs(t *testing.T) { - for _, scheme := range schemes { - t.Run(scheme, func(t *testing.T) { - testImportMissingUTXOs(t, scheme) - }) - } -} - -func testImportMissingUTXOs(t *testing.T, scheme string) { - // make a VM with a shared memory that has an importable UTXO to build a block - importAmount := uint64(50000000) - fork := upgradetest.ApricotPhase2 - tvm1 := newVM(t, testVMConfig{ - fork: &fork, - utxos: map[ids.ShortID]uint64{ - testShortIDAddrs[0]: importAmount, - }, - configJSON: getConfig(scheme, ""), - }) - defer func() { - require.NoError(t, tvm1.vm.Shutdown(context.Background())) - }() - - importTx, err := tvm1.atomicVM.NewImportTx(tvm1.vm.ctx.XChainID, testEthAddrs[0], initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) - require.NoError(t, err) - require.NoError(t, tvm1.atomicVM.AtomicMempool.AddLocalTx(importTx)) - require.Equal(t, commonEng.PendingTxs, tvm1.WaitForEvent(context.Background())) - blk, err := tvm1.vm.BuildBlock(context.Background()) - require.NoError(t, err) - - // make another VM which is missing the UTXO in shared memory - vm2 := newVM(t, testVMConfig{ - fork: &fork, - configJSON: getConfig(scheme, ""), - }).vm - defer func() { - require.NoError(t, vm2.Shutdown(context.Background())) - }() - - vm2Blk, err := vm2.ParseBlock(context.Background(), blk.Bytes()) - require.NoError(t, err) - err = vm2Blk.Verify(context.Background()) - require.ErrorIs(t, err, atomicvm.ErrMissingUTXOs) - - // This should not result in a bad block since the missing UTXO should - // prevent InsertBlockManual from being called. - badBlocks, _ := vm2.blockChain.BadBlocks() - require.Len(t, badBlocks, 0) -} - -// Simple test to ensure we can issue an import transaction followed by an export transaction -// and they will be indexed correctly when accepted. -func TestIssueAtomicTxs(t *testing.T) { - for _, scheme := range schemes { - t.Run(scheme, func(t *testing.T) { - testIssueAtomicTxs(t, scheme) - }) - } -} - -func testIssueAtomicTxs(t *testing.T, scheme string) { - importAmount := uint64(50000000) - fork := upgradetest.ApricotPhase2 - tvm := newVM(t, testVMConfig{ - fork: &fork, - utxos: map[ids.ShortID]uint64{ - testShortIDAddrs[0]: importAmount, - }, - configJSON: getConfig(scheme, ""), - }) - defer func() { - if err := tvm.vm.Shutdown(context.Background()); err != nil { - t.Fatal(err) - } - }() - - importTx, err := tvm.atomicVM.NewImportTx(tvm.vm.ctx.XChainID, testEthAddrs[0], initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) - if err != nil { - t.Fatal(err) - } - - if err := tvm.atomicVM.AtomicMempool.AddLocalTx(importTx); err != nil { - t.Fatal(err) - } - - require.Equal(t, commonEng.PendingTxs, tvm.WaitForEvent(context.Background())) - - blk, err := tvm.vm.BuildBlock(context.Background()) - if err != nil { - t.Fatal(err) - } - - if err := blk.Verify(context.Background()); err != nil { - t.Fatal(err) - } - - if err := tvm.vm.SetPreference(context.Background(), blk.ID()); err != nil { - t.Fatal(err) - } - - if err := blk.Accept(context.Background()); err != nil { - t.Fatal(err) - } - - if lastAcceptedID, err := tvm.vm.LastAccepted(context.Background()); err != nil { - t.Fatal(err) - } else if lastAcceptedID != blk.ID() { - t.Fatalf("Expected last accepted blockID to be the accepted block: %s, but found %s", blk.ID(), lastAcceptedID) - } - tvm.vm.blockChain.DrainAcceptorQueue() - filterAPI := filters.NewFilterAPI(filters.NewFilterSystem(tvm.vm.eth.APIBackend, filters.Config{ - Timeout: 5 * time.Minute, - })) - blockHash := common.Hash(blk.ID()) - logs, err := filterAPI.GetLogs(context.Background(), filters.FilterCriteria{ - BlockHash: &blockHash, - }) - if err != nil { - t.Fatal(err) - } - if len(logs) != 0 { - t.Fatalf("Expected log length to be 0, but found %d", len(logs)) - } - if logs == nil { - t.Fatal("Expected logs to be non-nil") - } - - exportTx, err := tvm.atomicVM.NewExportTx(tvm.vm.ctx.AVAXAssetID, importAmount-(2*ap0.AtomicTxFee), tvm.vm.ctx.XChainID, testShortIDAddrs[0], initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) - if err != nil { - t.Fatal(err) - } - - if err := tvm.atomicVM.AtomicMempool.AddLocalTx(exportTx); err != nil { - t.Fatal(err) - } - - require.Equal(t, commonEng.PendingTxs, tvm.WaitForEvent(context.Background())) - - blk2, err := tvm.vm.BuildBlock(context.Background()) - if err != nil { - t.Fatal(err) - } - - if err := blk2.Verify(context.Background()); err != nil { - t.Fatal(err) - } - - if err := blk2.Accept(context.Background()); err != nil { - t.Fatal(err) - } - - if lastAcceptedID, err := tvm.vm.LastAccepted(context.Background()); err != nil { - t.Fatal(err) - } else if lastAcceptedID != blk2.ID() { - t.Fatalf("Expected last accepted blockID to be the accepted block: %s, but found %s", blk2.ID(), lastAcceptedID) - } - - // Check that both atomic transactions were indexed as expected. - indexedImportTx, status, height, err := tvm.atomicVM.GetAtomicTx(importTx.ID()) - assert.NoError(t, err) - assert.Equal(t, atomic.Accepted, status) - assert.Equal(t, uint64(1), height, "expected height of indexed import tx to be 1") - assert.Equal(t, indexedImportTx.ID(), importTx.ID(), "expected ID of indexed import tx to match original txID") - - indexedExportTx, status, height, err := tvm.atomicVM.GetAtomicTx(exportTx.ID()) - assert.NoError(t, err) - assert.Equal(t, atomic.Accepted, status) - assert.Equal(t, uint64(2), height, "expected height of indexed export tx to be 2") - assert.Equal(t, indexedExportTx.ID(), exportTx.ID(), "expected ID of indexed import tx to match original txID") -} - func TestBuildEthTxBlock(t *testing.T) { - for _, scheme := range schemes { + for _, scheme := range vmtest.Schemes { t.Run(scheme, func(t *testing.T) { testBuildEthTxBlock(t, scheme) }) @@ -620,36 +197,39 @@ func TestBuildEthTxBlock(t *testing.T) { } func testBuildEthTxBlock(t *testing.T, scheme string) { - importAmount := uint64(20000000) fork := upgradetest.ApricotPhase2 - tvm := newVM(t, testVMConfig{ - fork: &fork, - configJSON: getConfig(scheme, `"pruning-enabled":true`), - utxos: map[ids.ShortID]uint64{ - testShortIDAddrs[0]: importAmount, - }, + vm := newDefaultTestVM() + tvm := vmtest.SetupTestVM(t, vm, vmtest.TestVMConfig{ + Fork: &fork, + Scheme: scheme, }) + defer func() { - if err := tvm.vm.Shutdown(context.Background()); err != nil { + if err := vm.Shutdown(context.Background()); err != nil { t.Fatal(err) } }() newTxPoolHeadChan := make(chan core.NewTxPoolReorgEvent, 1) - tvm.vm.txPool.SubscribeNewReorgEvent(newTxPoolHeadChan) + vm.txPool.SubscribeNewReorgEvent(newTxPoolHeadChan) - importTx, err := tvm.atomicVM.NewImportTx(tvm.vm.ctx.XChainID, testEthAddrs[0], initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) + tx := types.NewTransaction(uint64(0), vmtest.TestEthAddrs[1], big.NewInt(1), 21000, vmtest.InitialBaseFee, nil) + signedTx, err := types.SignTx(tx, types.NewEIP155Signer(vm.chainConfig.ChainID), vmtest.TestKeys[0].ToECDSA()) if err != nil { t.Fatal(err) } - - if err := tvm.atomicVM.AtomicMempool.AddLocalTx(importTx); err != nil { - t.Fatal(err) + errs := vm.txPool.AddRemotesSync([]*types.Transaction{signedTx}) + for i, err := range errs { + if err != nil { + t.Fatalf("Failed to add tx at index %d: %s", i, err) + } } - require.Equal(t, commonEng.PendingTxs, tvm.WaitForEvent(context.Background())) + msg, err := vm.WaitForEvent(context.Background()) + require.NoError(t, err) + require.Equal(t, commonEng.PendingTxs, msg) - blk1, err := tvm.vm.BuildBlock(context.Background()) + blk1, err := vm.BuildBlock(context.Background()) if err != nil { t.Fatal(err) } @@ -658,7 +238,7 @@ func testBuildEthTxBlock(t *testing.T, scheme string) { t.Fatal(err) } - if err := tvm.vm.SetPreference(context.Background(), blk1.ID()); err != nil { + if err := vm.SetPreference(context.Background(), blk1.ID()); err != nil { t.Fatal(err) } @@ -673,23 +253,25 @@ func testBuildEthTxBlock(t *testing.T, scheme string) { txs := make([]*types.Transaction, 10) for i := 0; i < 10; i++ { - tx := types.NewTransaction(uint64(i), testEthAddrs[0], big.NewInt(10), 21000, big.NewInt(ap0.MinGasPrice), nil) - signedTx, err := types.SignTx(tx, types.NewEIP155Signer(tvm.vm.chainID), testKeys[0].ToECDSA()) + tx := types.NewTransaction(uint64(i), vmtest.TestEthAddrs[0], big.NewInt(10), 21000, big.NewInt(ap0.MinGasPrice), nil) + signedTx, err := types.SignTx(tx, types.NewEIP155Signer(vm.chainID), vmtest.TestKeys[1].ToECDSA()) if err != nil { t.Fatal(err) } txs[i] = signedTx } - errs := tvm.vm.txPool.AddRemotesSync(txs) + errs = vm.txPool.AddRemotesSync(txs) for i, err := range errs { if err != nil { t.Fatalf("Failed to add tx at index %d: %s", i, err) } } - require.Equal(t, commonEng.PendingTxs, tvm.WaitForEvent(context.Background())) + msg, err = vm.WaitForEvent(context.Background()) + require.NoError(t, err) + require.Equal(t, commonEng.PendingTxs, msg) - blk2, err := tvm.vm.BuildBlock(context.Background()) + blk2, err := vm.BuildBlock(context.Background()) if err != nil { t.Fatal(err) } @@ -707,7 +289,7 @@ func testBuildEthTxBlock(t *testing.T, scheme string) { t.Fatalf("Expected new block to match") } - lastAcceptedID, err := tvm.vm.LastAccepted(context.Background()) + lastAcceptedID, err := vm.LastAccepted(context.Background()) if err != nil { t.Fatal(err) } @@ -716,19 +298,19 @@ func testBuildEthTxBlock(t *testing.T, scheme string) { } ethBlk1 := blk1.(*chain.BlockWrapper).Block.(*wrappedBlock).ethBlock - if ethBlk1Root := ethBlk1.Root(); !tvm.vm.blockChain.HasState(ethBlk1Root) { + if ethBlk1Root := ethBlk1.Root(); !vm.blockChain.HasState(ethBlk1Root) { t.Fatalf("Expected blk1 state root to not yet be pruned after blk2 was accepted because of tip buffer") } // Clear the cache and ensure that GetBlock returns internal blocks with the correct status - tvm.vm.State.Flush() - blk2Refreshed, err := tvm.vm.GetBlockInternal(context.Background(), blk2.ID()) + vm.State.Flush() + blk2Refreshed, err := vm.GetBlockInternal(context.Background(), blk2.ID()) if err != nil { t.Fatal(err) } blk1RefreshedID := blk2Refreshed.Parent() - blk1Refreshed, err := tvm.vm.GetBlockInternal(context.Background(), blk1RefreshedID) + blk1Refreshed, err := vm.GetBlockInternal(context.Background(), blk1RefreshedID) if err != nil { t.Fatal(err) } @@ -737,17 +319,19 @@ func testBuildEthTxBlock(t *testing.T, scheme string) { t.Fatalf("Found unexpected blkID for parent of blk2") } - restartedVM := atomicvm.WrapVM(&VM{}) + restartedVM := newDefaultTestVM() newCTX := snowtest.Context(t, snowtest.CChainID) newCTX.NetworkUpgrades = upgradetest.GetConfig(fork) - newCTX.ChainDataDir = tvm.vm.ctx.ChainDataDir + newCTX.ChainDataDir = tvm.Ctx.ChainDataDir + conf, err := vmtest.OverrideSchemeConfig(scheme, `{"pruning-enabled":true}`) + require.NoError(t, err) if err := restartedVM.Initialize( context.Background(), newCTX, - tvm.db, - []byte(genesisJSON(forkToChainConfig[fork])), + tvm.DB, + []byte(vmtest.GenesisJSON(vmtest.ForkToChainConfig[fork])), []byte(""), - []byte(getConfig(scheme, `"pruning-enabled":true`)), + []byte(conf), []*commonEng.Fx{}, nil, ); err != nil { @@ -755,439 +339,108 @@ func testBuildEthTxBlock(t *testing.T, scheme string) { } // State root should not have been committed and discarded on restart - if ethBlk1Root := ethBlk1.Root(); restartedVM.Blockchain().HasState(ethBlk1Root) { + if ethBlk1Root := ethBlk1.Root(); restartedVM.Ethereum().BlockChain().HasState(ethBlk1Root) { t.Fatalf("Expected blk1 state root to be pruned after blk2 was accepted on top of it in pruning mode") } // State root should be committed when accepted tip on shutdown ethBlk2 := blk2.(*chain.BlockWrapper).Block.(*wrappedBlock).ethBlock - if ethBlk2Root := ethBlk2.Root(); !restartedVM.Blockchain().HasState(ethBlk2Root) { + if ethBlk2Root := ethBlk2.Root(); !restartedVM.Ethereum().BlockChain().HasState(ethBlk2Root) { t.Fatalf("Expected blk2 state root to not be pruned after shutdown (last accepted tip should be committed)") } } -func testConflictingImportTxs(t *testing.T, fork upgradetest.Fork, scheme string) { - importAmount := uint64(10000000) - tvm := newVM(t, testVMConfig{ - fork: &fork, - utxos: map[ids.ShortID]uint64{ - testShortIDAddrs[0]: importAmount, - testShortIDAddrs[1]: importAmount, - testShortIDAddrs[2]: importAmount, - }, - configJSON: getConfig(scheme, ""), - }) - defer func() { - if err := tvm.vm.Shutdown(context.Background()); err != nil { - t.Fatal(err) - } - }() - - importTxs := make([]*atomic.Tx, 0, 3) - conflictTxs := make([]*atomic.Tx, 0, 3) - for i, key := range testKeys { - importTx, err := tvm.atomicVM.NewImportTx(tvm.vm.ctx.XChainID, testEthAddrs[i], initialBaseFee, []*secp256k1.PrivateKey{key}) - if err != nil { - t.Fatal(err) - } - importTxs = append(importTxs, importTx) - - conflictAddr := testEthAddrs[(i+1)%len(testEthAddrs)] - conflictTx, err := tvm.atomicVM.NewImportTx(tvm.vm.ctx.XChainID, conflictAddr, initialBaseFee, []*secp256k1.PrivateKey{key}) - if err != nil { - t.Fatal(err) - } - conflictTxs = append(conflictTxs, conflictTx) +// Regression test to ensure that after accepting block A +// then calling SetPreference on block B (when it becomes preferred) +// and the head of a longer chain (block D) does not corrupt the +// canonical chain. +// +// A +// / \ +// B C +// | +// D +func TestSetPreferenceRace(t *testing.T) { + for _, scheme := range vmtest.Schemes { + t.Run(scheme, func(t *testing.T) { + testSetPreferenceRace(t, scheme) + }) } +} - expectedParentBlkID, err := tvm.vm.LastAccepted(context.Background()) - if err != nil { - t.Fatal(err) +func testSetPreferenceRace(t *testing.T, scheme string) { + // Create two VMs which will agree on block A and then + // build the two distinct preferred chains above + fork := upgradetest.NoUpgrades + conf := vmtest.TestVMConfig{ + Fork: &fork, + Scheme: scheme, } - for _, tx := range importTxs[:2] { - if err := tvm.atomicVM.AtomicMempool.AddLocalTx(tx); err != nil { - t.Fatal(err) - } + vm1 := newDefaultTestVM() + vm2 := newDefaultTestVM() + vmtest.SetupTestVM(t, vm1, conf) + vmtest.SetupTestVM(t, vm2, conf) - require.Equal(t, commonEng.PendingTxs, tvm.WaitForEvent(context.Background())) - - tvm.vm.clock.Set(tvm.vm.clock.Time().Add(2 * time.Second)) - blk, err := tvm.vm.BuildBlock(context.Background()) - if err != nil { + defer func() { + if err := vm1.Shutdown(context.Background()); err != nil { t.Fatal(err) } - if err := blk.Verify(context.Background()); err != nil { + if err := vm2.Shutdown(context.Background()); err != nil { t.Fatal(err) } + }() - if parentID := blk.Parent(); parentID != expectedParentBlkID { - t.Fatalf("Expected parent to have blockID %s, but found %s", expectedParentBlkID, parentID) - } + newTxPoolHeadChan1 := make(chan core.NewTxPoolReorgEvent, 1) + vm1.txPool.SubscribeNewReorgEvent(newTxPoolHeadChan1) + newTxPoolHeadChan2 := make(chan core.NewTxPoolReorgEvent, 1) + vm2.txPool.SubscribeNewReorgEvent(newTxPoolHeadChan2) - expectedParentBlkID = blk.ID() - if err := tvm.vm.SetPreference(context.Background(), blk.ID()); err != nil { - t.Fatal(err) - } + tx := types.NewTransaction(uint64(0), vmtest.TestEthAddrs[1], big.NewInt(1), 21000, big.NewInt(ap0.MinGasPrice), nil) + signedTx, err := types.SignTx(tx, types.NewEIP155Signer(vm1.chainConfig.ChainID), vmtest.TestKeys[0].ToECDSA()) + if err != nil { + t.Fatal(err) } - - // Check that for each conflict tx (whose conflict is in the chain ancestry) - // the VM returns an error when it attempts to issue the conflict into the mempool - // and when it attempts to build a block with the conflict force added to the mempool. - for i, tx := range conflictTxs[:2] { - if err := tvm.atomicVM.AtomicMempool.AddLocalTx(tx); err == nil { - t.Fatal("Expected issueTx to fail due to conflicting transaction") - } - // Force issue transaction directly to the mempool - if err := tvm.atomicVM.AtomicMempool.ForceAddTx(tx); err != nil { - t.Fatal(err) - } - require.Equal(t, commonEng.PendingTxs, tvm.WaitForEvent(context.Background())) - - tvm.vm.clock.Set(tvm.vm.clock.Time().Add(2 * time.Second)) - _, err = tvm.vm.BuildBlock(context.Background()) - // The new block is verified in BuildBlock, so - // BuildBlock should fail due to an attempt to - // double spend an atomic UTXO. - if err == nil { - t.Fatalf("Block verification should have failed in BuildBlock %d due to double spending atomic UTXO", i) + errs := vm1.txPool.AddRemotesSync([]*types.Transaction{signedTx}) + for i, err := range errs { + if err != nil { + t.Fatalf("Failed to add tx at index %d: %s", i, err) } } - // Generate one more valid block so that we can copy the header to create an invalid block - // with modified extra data. This new block will be invalid for more than one reason (invalid merkle root) - // so we check to make sure that the expected error is returned from block verification. - if err := tvm.atomicVM.AtomicMempool.AddLocalTx(importTxs[2]); err != nil { - t.Fatal(err) - } - require.Equal(t, commonEng.PendingTxs, tvm.WaitForEvent(context.Background())) - tvm.vm.clock.Set(tvm.vm.clock.Time().Add(2 * time.Second)) + msg, err := vm1.WaitForEvent(context.Background()) + require.NoError(t, err) + require.Equal(t, commonEng.PendingTxs, msg) - validBlock, err := tvm.vm.BuildBlock(context.Background()) + vm1BlkA, err := vm1.BuildBlock(context.Background()) if err != nil { - t.Fatal(err) + t.Fatalf("Failed to build block with transaction: %s", err) } - if err := validBlock.Verify(context.Background()); err != nil { - t.Fatal(err) + if err := vm1BlkA.Verify(context.Background()); err != nil { + t.Fatalf("Block failed verification on VM1: %s", err) } - validEthBlock := validBlock.(*chain.BlockWrapper).Block.(extension.ExtendedBlock).GetEthBlock() - - rules := tvm.vm.currentRules() - var extraData []byte - switch { - case rules.IsApricotPhase5: - extraData, err = atomic.Codec.Marshal(atomic.CodecVersion, []*atomic.Tx{conflictTxs[1]}) - default: - extraData, err = atomic.Codec.Marshal(atomic.CodecVersion, conflictTxs[1]) - } - if err != nil { + if err := vm1.SetPreference(context.Background(), vm1BlkA.ID()); err != nil { t.Fatal(err) } - conflictingAtomicTxBlock := customtypes.NewBlockWithExtData( - types.CopyHeader(validEthBlock.Header()), - nil, - nil, - nil, - new(trie.Trie), - extraData, - true, - ) - - blockBytes, err := rlp.EncodeToBytes(conflictingAtomicTxBlock) + vm2BlkA, err := vm2.ParseBlock(context.Background(), vm1BlkA.Bytes()) if err != nil { - t.Fatal(err) + t.Fatalf("Unexpected error parsing block from vm2: %s", err) } - - parsedBlock, err := tvm.vm.ParseBlock(context.Background(), blockBytes) - if err != nil { + if err := vm2BlkA.Verify(context.Background()); err != nil { + t.Fatalf("Block failed verification on VM2: %s", err) + } + if err := vm2.SetPreference(context.Background(), vm2BlkA.ID()); err != nil { t.Fatal(err) } - if err := parsedBlock.Verify(context.Background()); !errors.Is(err, atomicvm.ErrConflictingAtomicInputs) { - t.Fatalf("Expected to fail with err: %s, but found err: %s", atomicvm.ErrConflictingAtomicInputs, err) + if err := vm1BlkA.Accept(context.Background()); err != nil { + t.Fatalf("VM1 failed to accept block: %s", err) } - - if !rules.IsApricotPhase5 { - return - } - - extraData, err = atomic.Codec.Marshal(atomic.CodecVersion, []*atomic.Tx{importTxs[2], conflictTxs[2]}) - if err != nil { - t.Fatal(err) - } - - header := types.CopyHeader(validEthBlock.Header()) - headerExtra := customtypes.GetHeaderExtra(header) - headerExtra.ExtDataGasUsed.Mul(common.Big2, headerExtra.ExtDataGasUsed) - - internalConflictBlock := customtypes.NewBlockWithExtData( - header, - nil, - nil, - nil, - new(trie.Trie), - extraData, - true, - ) - - blockBytes, err = rlp.EncodeToBytes(internalConflictBlock) - if err != nil { - t.Fatal(err) - } - - parsedBlock, err = tvm.vm.ParseBlock(context.Background(), blockBytes) - if err != nil { - t.Fatal(err) - } - - if err := parsedBlock.Verify(context.Background()); !errors.Is(err, atomicvm.ErrConflictingAtomicInputs) { - t.Fatalf("Expected to fail with err: %s, but found err: %s", atomicvm.ErrConflictingAtomicInputs, err) - } -} - -func TestReissueAtomicTxHigherGasPrice(t *testing.T) { - for _, scheme := range schemes { - t.Run(scheme, func(t *testing.T) { - testReissueAtomicTxHigherGasPrice(t, scheme) - }) - } -} - -func testReissueAtomicTxHigherGasPrice(t *testing.T, scheme string) { - kc := secp256k1fx.NewKeychain(testKeys...) - tests := map[string]func(t *testing.T, vm *atomicvm.VM, sharedMemory *avalancheatomic.Memory) (issued []*atomic.Tx, discarded []*atomic.Tx){ - "single UTXO override": func(t *testing.T, vm *atomicvm.VM, sharedMemory *avalancheatomic.Memory) (issued []*atomic.Tx, evicted []*atomic.Tx) { - utxo, err := addUTXO(sharedMemory, vm.Ctx, ids.GenerateTestID(), 0, vm.Ctx.AVAXAssetID, units.Avax, testShortIDAddrs[0]) - if err != nil { - t.Fatal(err) - } - tx1, err := atomic.NewImportTx(vm.Ctx, vm.CurrentRules(), vm.Clock().Unix(), vm.Ctx.XChainID, testEthAddrs[0], initialBaseFee, kc, []*avax.UTXO{utxo}) - if err != nil { - t.Fatal(err) - } - tx2, err := atomic.NewImportTx(vm.Ctx, vm.CurrentRules(), vm.Clock().Unix(), vm.Ctx.XChainID, testEthAddrs[0], new(big.Int).Mul(common.Big2, initialBaseFee), kc, []*avax.UTXO{utxo}) - if err != nil { - t.Fatal(err) - } - - if err := vm.AtomicMempool.AddLocalTx(tx1); err != nil { - t.Fatal(err) - } - if err := vm.AtomicMempool.AddLocalTx(tx2); err != nil { - t.Fatal(err) - } - - return []*atomic.Tx{tx2}, []*atomic.Tx{tx1} - }, - "one of two UTXOs overrides": func(t *testing.T, vm *atomicvm.VM, sharedMemory *avalancheatomic.Memory) (issued []*atomic.Tx, evicted []*atomic.Tx) { - utxo1, err := addUTXO(sharedMemory, vm.Ctx, ids.GenerateTestID(), 0, vm.Ctx.AVAXAssetID, units.Avax, testShortIDAddrs[0]) - if err != nil { - t.Fatal(err) - } - utxo2, err := addUTXO(sharedMemory, vm.Ctx, ids.GenerateTestID(), 0, vm.Ctx.AVAXAssetID, units.Avax, testShortIDAddrs[0]) - if err != nil { - t.Fatal(err) - } - tx1, err := atomic.NewImportTx(vm.Ctx, vm.CurrentRules(), vm.Clock().Unix(), vm.Ctx.XChainID, testEthAddrs[0], initialBaseFee, kc, []*avax.UTXO{utxo1, utxo2}) - if err != nil { - t.Fatal(err) - } - tx2, err := atomic.NewImportTx(vm.Ctx, vm.CurrentRules(), vm.Clock().Unix(), vm.Ctx.XChainID, testEthAddrs[0], new(big.Int).Mul(common.Big2, initialBaseFee), kc, []*avax.UTXO{utxo1}) - if err != nil { - t.Fatal(err) - } - - if err := vm.AtomicMempool.AddLocalTx(tx1); err != nil { - t.Fatal(err) - } - if err := vm.AtomicMempool.AddLocalTx(tx2); err != nil { - t.Fatal(err) - } - - return []*atomic.Tx{tx2}, []*atomic.Tx{tx1} - }, - "hola": func(t *testing.T, vm *atomicvm.VM, sharedMemory *avalancheatomic.Memory) (issued []*atomic.Tx, evicted []*atomic.Tx) { - utxo1, err := addUTXO(sharedMemory, vm.Ctx, ids.GenerateTestID(), 0, vm.Ctx.AVAXAssetID, units.Avax, testShortIDAddrs[0]) - if err != nil { - t.Fatal(err) - } - utxo2, err := addUTXO(sharedMemory, vm.Ctx, ids.GenerateTestID(), 0, vm.Ctx.AVAXAssetID, units.Avax, testShortIDAddrs[0]) - if err != nil { - t.Fatal(err) - } - - importTx1, err := atomic.NewImportTx(vm.Ctx, vm.CurrentRules(), vm.Clock().Unix(), vm.Ctx.XChainID, testEthAddrs[0], initialBaseFee, kc, []*avax.UTXO{utxo1}) - if err != nil { - t.Fatal(err) - } - - importTx2, err := atomic.NewImportTx(vm.Ctx, vm.CurrentRules(), vm.Clock().Unix(), vm.Ctx.XChainID, testEthAddrs[0], new(big.Int).Mul(big.NewInt(3), initialBaseFee), kc, []*avax.UTXO{utxo2}) - if err != nil { - t.Fatal(err) - } - - reissuanceTx1, err := atomic.NewImportTx(vm.Ctx, vm.CurrentRules(), vm.Clock().Unix(), vm.Ctx.XChainID, testEthAddrs[0], new(big.Int).Mul(big.NewInt(2), initialBaseFee), kc, []*avax.UTXO{utxo1, utxo2}) - if err != nil { - t.Fatal(err) - } - if err := vm.AtomicMempool.AddLocalTx(importTx1); err != nil { - t.Fatal(err) - } - - if err := vm.AtomicMempool.AddLocalTx(importTx2); err != nil { - t.Fatal(err) - } - - if err := vm.AtomicMempool.AddLocalTx(reissuanceTx1); !errors.Is(err, atomictxpool.ErrConflict) { - t.Fatalf("Expected to fail with err: %s, but found err: %s", atomictxpool.ErrConflict, err) - } - - assert.True(t, vm.AtomicMempool.Has(importTx1.ID())) - assert.True(t, vm.AtomicMempool.Has(importTx2.ID())) - assert.False(t, vm.AtomicMempool.Has(reissuanceTx1.ID())) - - reissuanceTx2, err := atomic.NewImportTx(vm.Ctx, vm.CurrentRules(), vm.Clock().Unix(), vm.Ctx.XChainID, testEthAddrs[0], new(big.Int).Mul(big.NewInt(4), initialBaseFee), kc, []*avax.UTXO{utxo1, utxo2}) - if err != nil { - t.Fatal(err) - } - if err := vm.AtomicMempool.AddLocalTx(reissuanceTx2); err != nil { - t.Fatal(err) - } - - return []*atomic.Tx{reissuanceTx2}, []*atomic.Tx{importTx1, importTx2} - }, - } - for name, issueTxs := range tests { - t.Run(name, func(t *testing.T) { - fork := upgradetest.ApricotPhase5 - tvm := newVM(t, testVMConfig{ - fork: &fork, - configJSON: getConfig(scheme, `"pruning-enabled":true`), - }) - issuedTxs, evictedTxs := issueTxs(t, tvm.atomicVM, tvm.atomicMemory) - - for i, tx := range issuedTxs { - _, issued := tvm.atomicVM.AtomicMempool.GetPendingTx(tx.ID()) - assert.True(t, issued, "expected issued tx at index %d to be issued", i) - } - - for i, tx := range evictedTxs { - _, discarded, _ := tvm.atomicVM.AtomicMempool.GetTx(tx.ID()) - assert.True(t, discarded, "expected discarded tx at index %d to be discarded", i) - } - }) - } -} - -func TestConflictingImportTxsAcrossBlocks(t *testing.T) { - for _, fork := range []upgradetest.Fork{ - upgradetest.ApricotPhase1, - upgradetest.ApricotPhase2, - upgradetest.ApricotPhase3, - upgradetest.ApricotPhase4, - upgradetest.ApricotPhase5, - } { - t.Run(fork.String(), func(t *testing.T) { - for _, scheme := range schemes { - t.Run(scheme, func(t *testing.T) { - testConflictingImportTxs(t, fork, scheme) - }) - } - }) - } -} - -// Regression test to ensure that after accepting block A -// then calling SetPreference on block B (when it becomes preferred) -// and the head of a longer chain (block D) does not corrupt the -// canonical chain. -// -// A -// / \ -// B C -// | -// D -func TestSetPreferenceRace(t *testing.T) { - for _, scheme := range schemes { - t.Run(scheme, func(t *testing.T) { - testSetPreferenceRace(t, scheme) - }) - } -} - -func testSetPreferenceRace(t *testing.T, scheme string) { - // Create two VMs which will agree on block A and then - // build the two distinct preferred chains above - importAmount := uint64(1000000000) - fork := upgradetest.NoUpgrades - tvmConfig := testVMConfig{ - fork: &fork, - configJSON: getConfig(scheme, `"pruning-enabled":true`), - utxos: map[ids.ShortID]uint64{ - testShortIDAddrs[0]: importAmount, - }, - } - tvm1 := newVM(t, tvmConfig) - tvm2 := newVM(t, tvmConfig) - - defer func() { - if err := tvm1.vm.Shutdown(context.Background()); err != nil { - t.Fatal(err) - } - - if err := tvm2.vm.Shutdown(context.Background()); err != nil { - t.Fatal(err) - } - }() - - newTxPoolHeadChan1 := make(chan core.NewTxPoolReorgEvent, 1) - tvm1.vm.txPool.SubscribeNewReorgEvent(newTxPoolHeadChan1) - newTxPoolHeadChan2 := make(chan core.NewTxPoolReorgEvent, 1) - tvm2.vm.txPool.SubscribeNewReorgEvent(newTxPoolHeadChan2) - - importTx, err := tvm1.atomicVM.NewImportTx(tvm1.vm.ctx.XChainID, testEthAddrs[1], initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) - if err != nil { - t.Fatal(err) - } - - if err := tvm1.atomicVM.AtomicMempool.AddLocalTx(importTx); err != nil { - t.Fatal(err) - } - - require.Equal(t, commonEng.PendingTxs, tvm1.WaitForEvent(context.Background())) - - vm1BlkA, err := tvm1.vm.BuildBlock(context.Background()) - if err != nil { - t.Fatalf("Failed to build block with import transaction: %s", err) - } - - if err := vm1BlkA.Verify(context.Background()); err != nil { - t.Fatalf("Block failed verification on VM1: %s", err) - } - - if err := tvm1.vm.SetPreference(context.Background(), vm1BlkA.ID()); err != nil { - t.Fatal(err) - } - - vm2BlkA, err := tvm2.vm.ParseBlock(context.Background(), vm1BlkA.Bytes()) - if err != nil { - t.Fatalf("Unexpected error parsing block from vm2: %s", err) - } - if err := vm2BlkA.Verify(context.Background()); err != nil { - t.Fatalf("Block failed verification on VM2: %s", err) - } - if err := tvm2.vm.SetPreference(context.Background(), vm2BlkA.ID()); err != nil { - t.Fatal(err) - } - - if err := vm1BlkA.Accept(context.Background()); err != nil { - t.Fatalf("VM1 failed to accept block: %s", err) - } - if err := vm2BlkA.Accept(context.Background()); err != nil { - t.Fatalf("VM2 failed to accept block: %s", err) + if err := vm2BlkA.Accept(context.Background()); err != nil { + t.Fatalf("VM2 failed to accept block: %s", err) } newHead := <-newTxPoolHeadChan1 @@ -1203,27 +456,27 @@ func testSetPreferenceRace(t *testing.T, scheme string) { // and to be split into two separate blocks on VM2 txs := make([]*types.Transaction, 10) for i := 0; i < 10; i++ { - tx := types.NewTransaction(uint64(i), testEthAddrs[1], big.NewInt(10), 21000, big.NewInt(ap0.MinGasPrice), nil) - signedTx, err := types.SignTx(tx, types.NewEIP155Signer(tvm1.vm.chainID), testKeys[1].ToECDSA()) + tx := types.NewTransaction(uint64(i), vmtest.TestEthAddrs[1], big.NewInt(10), 21000, big.NewInt(ap0.MinGasPrice), nil) + signedTx, err := types.SignTx(tx, types.NewEIP155Signer(vm1.chainID), vmtest.TestKeys[1].ToECDSA()) if err != nil { t.Fatal(err) } txs[i] = signedTx } - var errs []error - // Add the remote transactions, build the block, and set VM1's preference for block A - errs = tvm1.vm.txPool.AddRemotesSync(txs) + errs = vm1.txPool.AddRemotesSync(txs) for i, err := range errs { if err != nil { t.Fatalf("Failed to add transaction to VM1 at index %d: %s", i, err) } } - require.Equal(t, commonEng.PendingTxs, tvm1.WaitForEvent(context.Background())) + msg, err = vm1.WaitForEvent(context.Background()) + require.NoError(t, err) + require.Equal(t, commonEng.PendingTxs, msg) - vm1BlkB, err := tvm1.vm.BuildBlock(context.Background()) + vm1BlkB, err := vm1.BuildBlock(context.Background()) if err != nil { t.Fatal(err) } @@ -1232,22 +485,25 @@ func testSetPreferenceRace(t *testing.T, scheme string) { t.Fatal(err) } - if err := tvm1.vm.SetPreference(context.Background(), vm1BlkB.ID()); err != nil { + if err := vm1.SetPreference(context.Background(), vm1BlkB.ID()); err != nil { t.Fatal(err) } // Split the transactions over two blocks, and set VM2's preference to them in sequence // after building each block // Block C - errs = tvm2.vm.txPool.AddRemotesSync(txs[0:5]) + errs = vm2.txPool.AddRemotesSync(txs[0:5]) for i, err := range errs { if err != nil { t.Fatalf("Failed to add transaction to VM2 at index %d: %s", i, err) } } - require.Equal(t, commonEng.PendingTxs, tvm2.WaitForEvent(context.Background())) - vm2BlkC, err := tvm2.vm.BuildBlock(context.Background()) + msg, err = vm2.WaitForEvent(context.Background()) + require.NoError(t, err) + require.Equal(t, commonEng.PendingTxs, msg) + + vm2BlkC, err := vm2.BuildBlock(context.Background()) if err != nil { t.Fatalf("Failed to build BlkC on VM2: %s", err) } @@ -1256,7 +512,7 @@ func testSetPreferenceRace(t *testing.T, scheme string) { t.Fatalf("BlkC failed verification on VM2: %s", err) } - if err := tvm2.vm.SetPreference(context.Background(), vm2BlkC.ID()); err != nil { + if err := vm2.SetPreference(context.Background(), vm2BlkC.ID()); err != nil { t.Fatal(err) } @@ -1266,15 +522,18 @@ func testSetPreferenceRace(t *testing.T, scheme string) { } // Block D - errs = tvm2.vm.txPool.AddRemotesSync(txs[5:10]) + errs = vm2.txPool.AddRemotesSync(txs[5:10]) for i, err := range errs { if err != nil { t.Fatalf("Failed to add transaction to VM2 at index %d: %s", i, err) } } - require.Equal(t, commonEng.PendingTxs, tvm2.WaitForEvent(context.Background())) - vm2BlkD, err := tvm2.vm.BuildBlock(context.Background()) + msg, err = vm2.WaitForEvent(context.Background()) + require.NoError(t, err) + require.Equal(t, commonEng.PendingTxs, msg) + + vm2BlkD, err := vm2.BuildBlock(context.Background()) if err != nil { t.Fatalf("Failed to build BlkD on VM2: %s", err) } @@ -1283,7 +542,7 @@ func testSetPreferenceRace(t *testing.T, scheme string) { t.Fatalf("BlkD failed verification on VM2: %s", err) } - if err := tvm2.vm.SetPreference(context.Background(), vm2BlkD.ID()); err != nil { + if err := vm2.SetPreference(context.Background(), vm2BlkD.ID()); err != nil { t.Fatal(err) } @@ -1293,11 +552,11 @@ func testSetPreferenceRace(t *testing.T, scheme string) { // Here we parse them in reverse order to simulate receiving a chain from the tip // back to the last accepted block as would typically be the case in the consensus // engine - vm1BlkD, err := tvm1.vm.ParseBlock(context.Background(), vm2BlkD.Bytes()) + vm1BlkD, err := vm1.ParseBlock(context.Background(), vm2BlkD.Bytes()) if err != nil { t.Fatalf("VM1 errored parsing blkD: %s", err) } - vm1BlkC, err := tvm1.vm.ParseBlock(context.Background(), vm2BlkC.Bytes()) + vm1BlkC, err := vm1.ParseBlock(context.Background(), vm2BlkC.Bytes()) if err != nil { t.Fatalf("VM1 errored parsing blkC: %s", err) } @@ -1312,7 +571,7 @@ func testSetPreferenceRace(t *testing.T, scheme string) { } // Set VM1's preference to blockD, skipping blockC - if err := tvm1.vm.SetPreference(context.Background(), vm1BlkD.ID()); err != nil { + if err := vm1.SetPreference(context.Background(), vm1BlkD.ID()); err != nil { t.Fatal(err) } @@ -1335,341 +594,112 @@ func testSetPreferenceRace(t *testing.T, scheme string) { log.Info("Validating canonical chain") // Verify the Canonical Chain for Both VMs - if err := tvm2.vm.blockChain.ValidateCanonicalChain(); err != nil { + if err := vm2.blockChain.ValidateCanonicalChain(); err != nil { t.Fatalf("VM2 failed canonical chain verification due to: %s", err) } - if err := tvm1.vm.blockChain.ValidateCanonicalChain(); err != nil { + if err := vm1.blockChain.ValidateCanonicalChain(); err != nil { t.Fatalf("VM1 failed canonical chain verification due to: %s", err) } } -func TestConflictingTransitiveAncestryWithGap(t *testing.T) { - for _, scheme := range schemes { +// Regression test to ensure that a VM that accepts block A and B +// will not attempt to orphan either when verifying blocks C and D +// from another VM (which have a common ancestor under the finalized +// frontier). +// +// A +// / \ +// B C +// +// verifies block B and C, then Accepts block B. Then we test to ensure +// that the VM defends against any attempt to set the preference or to +// accept block C, which should be an orphaned block at this point and +// get rejected. +func TestReorgProtection(t *testing.T) { + for _, scheme := range vmtest.Schemes { t.Run(scheme, func(t *testing.T) { - testConflictingTransitiveAncestryWithGap(t, scheme) + testReorgProtection(t, scheme) }) } } -func testConflictingTransitiveAncestryWithGap(t *testing.T, scheme string) { - key := utilstest.NewKey(t) - - key0 := testKeys[0] - addr0 := key0.Address() - - key1 := testKeys[1] - addr1 := key1.Address() - - importAmount := uint64(1000000000) - +func testReorgProtection(t *testing.T, scheme string) { fork := upgradetest.NoUpgrades - tvm := newVM(t, testVMConfig{ - fork: &fork, - utxos: map[ids.ShortID]uint64{ - addr0: importAmount, - addr1: importAmount, - }, - configJSON: getConfig(scheme, ""), + vm1 := newDefaultTestVM() + vmtest.SetupTestVM(t, vm1, vmtest.TestVMConfig{ + Fork: &fork, + Scheme: scheme, }) + vm2 := newDefaultTestVM() + vmtest.SetupTestVM(t, vm2, vmtest.TestVMConfig{ + Fork: &fork, + Scheme: scheme, + }) + defer func() { - if err := tvm.vm.Shutdown(context.Background()); err != nil { + if err := vm1.Shutdown(context.Background()); err != nil { t.Fatal(err) } - }() - - newTxPoolHeadChan := make(chan core.NewTxPoolReorgEvent, 1) - tvm.vm.txPool.SubscribeNewReorgEvent(newTxPoolHeadChan) - - importTx0A, err := tvm.atomicVM.NewImportTx(tvm.vm.ctx.XChainID, key.Address, initialBaseFee, []*secp256k1.PrivateKey{key0}) - if err != nil { - t.Fatal(err) - } - // Create a conflicting transaction - importTx0B, err := tvm.atomicVM.NewImportTx(tvm.vm.ctx.XChainID, testEthAddrs[2], initialBaseFee, []*secp256k1.PrivateKey{key0}) - if err != nil { - t.Fatal(err) - } - - if err := tvm.atomicVM.AtomicMempool.AddLocalTx(importTx0A); err != nil { - t.Fatalf("Failed to issue importTx0A: %s", err) - } - - require.Equal(t, commonEng.PendingTxs, tvm.WaitForEvent(context.Background())) - - blk0, err := tvm.vm.BuildBlock(context.Background()) - if err != nil { - t.Fatalf("Failed to build block with import transaction: %s", err) - } - if err := blk0.Verify(context.Background()); err != nil { - t.Fatalf("Block failed verification: %s", err) - } + if err := vm2.Shutdown(context.Background()); err != nil { + t.Fatal(err) + } + }() - if err := tvm.vm.SetPreference(context.Background(), blk0.ID()); err != nil { - t.Fatal(err) - } + newTxPoolHeadChan1 := make(chan core.NewTxPoolReorgEvent, 1) + vm1.txPool.SubscribeNewReorgEvent(newTxPoolHeadChan1) + newTxPoolHeadChan2 := make(chan core.NewTxPoolReorgEvent, 1) + vm2.txPool.SubscribeNewReorgEvent(newTxPoolHeadChan2) - newHead := <-newTxPoolHeadChan - if newHead.Head.Hash() != common.Hash(blk0.ID()) { - t.Fatalf("Expected new block to match") - } + key := vmtest.TestKeys[1].ToECDSA() + address := vmtest.TestEthAddrs[1] - tx := types.NewTransaction(0, key.Address, big.NewInt(10), 21000, big.NewInt(ap0.MinGasPrice), nil) - signedTx, err := types.SignTx(tx, types.NewEIP155Signer(tvm.vm.chainID), key.PrivateKey) + tx := types.NewTransaction(uint64(0), vmtest.TestEthAddrs[1], big.NewInt(1), 21000, big.NewInt(ap0.MinGasPrice), nil) + signedTx, err := types.SignTx(tx, types.NewEIP155Signer(vm1.chainConfig.ChainID), vmtest.TestKeys[0].ToECDSA()) if err != nil { t.Fatal(err) } - - // Add the remote transactions, build the block, and set VM1's preference for block A - errs := tvm.vm.txPool.AddRemotesSync([]*types.Transaction{signedTx}) + errs := vm1.txPool.AddRemotesSync([]*types.Transaction{signedTx}) for i, err := range errs { if err != nil { - t.Fatalf("Failed to add transaction to VM1 at index %d: %s", i, err) + t.Fatalf("Failed to add tx at index %d: %s", i, err) } } - require.Equal(t, commonEng.PendingTxs, tvm.WaitForEvent(context.Background())) + msg, err := vm1.WaitForEvent(context.Background()) + require.NoError(t, err) + require.Equal(t, commonEng.PendingTxs, msg) - blk1, err := tvm.vm.BuildBlock(context.Background()) + vm1BlkA, err := vm1.BuildBlock(context.Background()) if err != nil { - t.Fatalf("Failed to build blk1: %s", err) + t.Fatalf("Failed to build block with transaction: %s", err) } - if err := blk1.Verify(context.Background()); err != nil { - t.Fatalf("blk1 failed verification due to %s", err) + if err := vm1BlkA.Verify(context.Background()); err != nil { + t.Fatalf("Block failed verification on VM1: %s", err) } - if err := tvm.vm.SetPreference(context.Background(), blk1.ID()); err != nil { + if err := vm1.SetPreference(context.Background(), vm1BlkA.ID()); err != nil { t.Fatal(err) } - importTx1, err := tvm.atomicVM.NewImportTx(tvm.vm.ctx.XChainID, key.Address, initialBaseFee, []*secp256k1.PrivateKey{key1}) + vm2BlkA, err := vm2.ParseBlock(context.Background(), vm1BlkA.Bytes()) if err != nil { - t.Fatalf("Failed to issue importTx1 due to: %s", err) + t.Fatalf("Unexpected error parsing block from vm2: %s", err) } - - if err := tvm.atomicVM.AtomicMempool.AddLocalTx(importTx1); err != nil { + if err := vm2BlkA.Verify(context.Background()); err != nil { + t.Fatalf("Block failed verification on VM2: %s", err) + } + if err := vm2.SetPreference(context.Background(), vm2BlkA.ID()); err != nil { t.Fatal(err) } - require.Equal(t, commonEng.PendingTxs, tvm.WaitForEvent(context.Background())) - - blk2, err := tvm.vm.BuildBlock(context.Background()) - if err != nil { - t.Fatalf("Failed to build block with import transaction: %s", err) + if err := vm1BlkA.Accept(context.Background()); err != nil { + t.Fatalf("VM1 failed to accept block: %s", err) } - - if err := blk2.Verify(context.Background()); err != nil { - t.Fatalf("Block failed verification: %s", err) - } - - if err := tvm.vm.SetPreference(context.Background(), blk2.ID()); err != nil { - t.Fatal(err) - } - - if err := tvm.atomicVM.AtomicMempool.AddLocalTx(importTx0B); err == nil { - t.Fatalf("Should not have been able to issue import tx with conflict") - } - // Force issue transaction directly into the mempool - if err := tvm.atomicVM.AtomicMempool.ForceAddTx(importTx0B); err != nil { - t.Fatal(err) - } - require.Equal(t, commonEng.PendingTxs, tvm.WaitForEvent(context.Background())) - - _, err = tvm.vm.BuildBlock(context.Background()) - if err == nil { - t.Fatal("Shouldn't have been able to build an invalid block") - } -} - -func TestBonusBlocksTxs(t *testing.T) { - for _, scheme := range schemes { - t.Run(scheme, func(t *testing.T) { - testBonusBlocksTxs(t, scheme) - }) - } -} - -func testBonusBlocksTxs(t *testing.T, scheme string) { - fork := upgradetest.NoUpgrades - tvm := newVM(t, testVMConfig{ - fork: &fork, - configJSON: getConfig(scheme, ""), - }) - defer func() { - if err := tvm.vm.Shutdown(context.Background()); err != nil { - t.Fatal(err) - } - }() - - importAmount := uint64(10000000) - utxoID := avax.UTXOID{TxID: ids.GenerateTestID()} - - utxo := &avax.UTXO{ - UTXOID: utxoID, - Asset: avax.Asset{ID: tvm.vm.ctx.AVAXAssetID}, - Out: &secp256k1fx.TransferOutput{ - Amt: importAmount, - OutputOwners: secp256k1fx.OutputOwners{ - Threshold: 1, - Addrs: []ids.ShortID{testKeys[0].Address()}, - }, - }, - } - utxoBytes, err := atomic.Codec.Marshal(atomic.CodecVersion, utxo) - if err != nil { - t.Fatal(err) - } - - xChainSharedMemory := tvm.atomicMemory.NewSharedMemory(tvm.vm.ctx.XChainID) - inputID := utxo.InputID() - if err := xChainSharedMemory.Apply(map[ids.ID]*avalancheatomic.Requests{tvm.vm.ctx.ChainID: {PutRequests: []*avalancheatomic.Element{{ - Key: inputID[:], - Value: utxoBytes, - Traits: [][]byte{ - testKeys[0].Address().Bytes(), - }, - }}}}); err != nil { - t.Fatal(err) - } - - importTx, err := tvm.atomicVM.NewImportTx(tvm.vm.ctx.XChainID, testEthAddrs[0], initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) - if err != nil { - t.Fatal(err) - } - - if err := tvm.atomicVM.AtomicMempool.AddLocalTx(importTx); err != nil { - t.Fatal(err) - } - - require.Equal(t, commonEng.PendingTxs, tvm.WaitForEvent(context.Background())) - - blk, err := tvm.vm.BuildBlock(context.Background()) - if err != nil { - t.Fatal(err) - } - - // Make [blk] a bonus block. - tvm.atomicVM.AtomicBackend.AddBonusBlock(blk.Height(), blk.ID()) - - // Remove the UTXOs from shared memory, so that non-bonus blocks will fail verification - if err := tvm.vm.ctx.SharedMemory.Apply(map[ids.ID]*avalancheatomic.Requests{tvm.vm.ctx.XChainID: {RemoveRequests: [][]byte{inputID[:]}}}); err != nil { - t.Fatal(err) - } - - if err := blk.Verify(context.Background()); err != nil { - t.Fatal(err) - } - - if err := tvm.vm.SetPreference(context.Background(), blk.ID()); err != nil { - t.Fatal(err) - } - - if err := blk.Accept(context.Background()); err != nil { - t.Fatal(err) - } - - lastAcceptedID, err := tvm.vm.LastAccepted(context.Background()) - if err != nil { - t.Fatal(err) - } - if lastAcceptedID != blk.ID() { - t.Fatalf("Expected last accepted blockID to be the accepted block: %s, but found %s", blk.ID(), lastAcceptedID) - } -} - -// Regression test to ensure that a VM that accepts block A and B -// will not attempt to orphan either when verifying blocks C and D -// from another VM (which have a common ancestor under the finalized -// frontier). -// -// A -// / \ -// B C -// -// verifies block B and C, then Accepts block B. Then we test to ensure -// that the VM defends against any attempt to set the preference or to -// accept block C, which should be an orphaned block at this point and -// get rejected. -func TestReorgProtection(t *testing.T) { - for _, scheme := range schemes { - t.Run(scheme, func(t *testing.T) { - testReorgProtection(t, scheme) - }) - } -} - -func testReorgProtection(t *testing.T, scheme string) { - importAmount := uint64(1000000000) - fork := upgradetest.NoUpgrades - tvmConfig := testVMConfig{ - fork: &fork, - configJSON: getConfig(scheme, `"pruning-enabled":false`), - utxos: map[ids.ShortID]uint64{ - testShortIDAddrs[0]: importAmount, - }, - } - tvm1 := newVM(t, tvmConfig) - tvm2 := newVM(t, tvmConfig) - defer func() { - if err := tvm1.vm.Shutdown(context.Background()); err != nil { - t.Fatal(err) - } - - if err := tvm2.vm.Shutdown(context.Background()); err != nil { - t.Fatal(err) - } - }() - - newTxPoolHeadChan1 := make(chan core.NewTxPoolReorgEvent, 1) - tvm1.vm.txPool.SubscribeNewReorgEvent(newTxPoolHeadChan1) - newTxPoolHeadChan2 := make(chan core.NewTxPoolReorgEvent, 1) - tvm2.vm.txPool.SubscribeNewReorgEvent(newTxPoolHeadChan2) - - key := testKeys[0].ToECDSA() - address := testEthAddrs[0] - - importTx, err := tvm1.atomicVM.NewImportTx(tvm1.vm.ctx.XChainID, address, initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) - if err != nil { - t.Fatal(err) - } - - if err := tvm1.atomicVM.AtomicMempool.AddLocalTx(importTx); err != nil { - t.Fatal(err) - } - - require.Equal(t, commonEng.PendingTxs, tvm1.WaitForEvent(context.Background())) - - vm1BlkA, err := tvm1.vm.BuildBlock(context.Background()) - if err != nil { - t.Fatalf("Failed to build block with import transaction: %s", err) - } - - if err := vm1BlkA.Verify(context.Background()); err != nil { - t.Fatalf("Block failed verification on VM1: %s", err) - } - - if err := tvm1.vm.SetPreference(context.Background(), vm1BlkA.ID()); err != nil { - t.Fatal(err) - } - - vm2BlkA, err := tvm2.vm.ParseBlock(context.Background(), vm1BlkA.Bytes()) - if err != nil { - t.Fatalf("Unexpected error parsing block from vm2: %s", err) - } - if err := vm2BlkA.Verify(context.Background()); err != nil { - t.Fatalf("Block failed verification on VM2: %s", err) - } - if err := tvm2.vm.SetPreference(context.Background(), vm2BlkA.ID()); err != nil { - t.Fatal(err) - } - - if err := vm1BlkA.Accept(context.Background()); err != nil { - t.Fatalf("VM1 failed to accept block: %s", err) - } - if err := vm2BlkA.Accept(context.Background()); err != nil { - t.Fatalf("VM2 failed to accept block: %s", err) + if err := vm2BlkA.Accept(context.Background()); err != nil { + t.Fatalf("VM2 failed to accept block: %s", err) } newHead := <-newTxPoolHeadChan1 @@ -1686,26 +716,26 @@ func testReorgProtection(t *testing.T, scheme string) { txs := make([]*types.Transaction, 10) for i := 0; i < 10; i++ { tx := types.NewTransaction(uint64(i), address, big.NewInt(10), 21000, big.NewInt(ap0.MinGasPrice), nil) - signedTx, err := types.SignTx(tx, types.NewEIP155Signer(tvm1.vm.chainID), key) + signedTx, err := types.SignTx(tx, types.NewEIP155Signer(vm1.chainID), key) if err != nil { t.Fatal(err) } txs[i] = signedTx } - var errs []error - // Add the remote transactions, build the block, and set VM1's preference for block A - errs = tvm1.vm.txPool.AddRemotesSync(txs) + errs = vm1.txPool.AddRemotesSync(txs) for i, err := range errs { if err != nil { t.Fatalf("Failed to add transaction to VM1 at index %d: %s", i, err) } } - require.Equal(t, commonEng.PendingTxs, tvm1.WaitForEvent(context.Background())) + msg, err = vm1.WaitForEvent(context.Background()) + require.NoError(t, err) + require.Equal(t, commonEng.PendingTxs, msg) - vm1BlkB, err := tvm1.vm.BuildBlock(context.Background()) + vm1BlkB, err := vm1.BuildBlock(context.Background()) if err != nil { t.Fatal(err) } @@ -1714,22 +744,25 @@ func testReorgProtection(t *testing.T, scheme string) { t.Fatal(err) } - if err := tvm1.vm.SetPreference(context.Background(), vm1BlkB.ID()); err != nil { + if err := vm1.SetPreference(context.Background(), vm1BlkB.ID()); err != nil { t.Fatal(err) } // Split the transactions over two blocks, and set VM2's preference to them in sequence // after building each block // Block C - errs = tvm2.vm.txPool.AddRemotesSync(txs[0:5]) + errs = vm2.txPool.AddRemotesSync(txs[0:5]) for i, err := range errs { if err != nil { t.Fatalf("Failed to add transaction to VM2 at index %d: %s", i, err) } } - require.Equal(t, commonEng.PendingTxs, tvm2.WaitForEvent(context.Background())) - vm2BlkC, err := tvm2.vm.BuildBlock(context.Background()) + msg, err = vm2.WaitForEvent(context.Background()) + require.NoError(t, err) + require.Equal(t, commonEng.PendingTxs, msg) + + vm2BlkC, err := vm2.BuildBlock(context.Background()) if err != nil { t.Fatalf("Failed to build BlkC on VM2: %s", err) } @@ -1738,7 +771,7 @@ func testReorgProtection(t *testing.T, scheme string) { t.Fatalf("Block failed verification on VM2: %s", err) } - vm1BlkC, err := tvm1.vm.ParseBlock(context.Background(), vm2BlkC.Bytes()) + vm1BlkC, err := vm1.ParseBlock(context.Background(), vm2BlkC.Bytes()) if err != nil { t.Fatalf("Unexpected error parsing block from vm2: %s", err) } @@ -1756,7 +789,7 @@ func testReorgProtection(t *testing.T, scheme string) { // with the preferred chain lower than the last finalized block) // should NEVER happen. However, the VM defends against this // just in case. - if err := tvm1.vm.SetPreference(context.Background(), vm1BlkC.ID()); !strings.Contains(err.Error(), "cannot orphan finalized block") { + if err := vm1.SetPreference(context.Background(), vm1BlkC.ID()); !strings.Contains(err.Error(), "cannot orphan finalized block") { t.Fatalf("Unexpected error when setting preference that would trigger reorg: %s", err) } @@ -1772,7 +805,7 @@ func testReorgProtection(t *testing.T, scheme string) { // / \ // B C func TestNonCanonicalAccept(t *testing.T) { - for _, scheme := range schemes { + for _, scheme := range vmtest.Schemes { t.Run(scheme, func(t *testing.T) { testNonCanonicalAccept(t, scheme) }) @@ -1780,81 +813,85 @@ func TestNonCanonicalAccept(t *testing.T) { } func testNonCanonicalAccept(t *testing.T, scheme string) { - importAmount := uint64(1000000000) fork := upgradetest.NoUpgrades - tvmConfig := testVMConfig{ - fork: &fork, - utxos: map[ids.ShortID]uint64{ - testShortIDAddrs[0]: importAmount, - }, - configJSON: getConfig(scheme, ""), + tvmConfig := vmtest.TestVMConfig{ + Fork: &fork, + Scheme: scheme, } - tvm1 := newVM(t, tvmConfig) - tvm2 := newVM(t, tvmConfig) + vm1 := newDefaultTestVM() + vm2 := newDefaultTestVM() + vmtest.SetupTestVM(t, vm1, tvmConfig) + vmtest.SetupTestVM(t, vm2, tvmConfig) + defer func() { - if err := tvm1.vm.Shutdown(context.Background()); err != nil { + if err := vm1.Shutdown(context.Background()); err != nil { t.Fatal(err) } - if err := tvm2.vm.Shutdown(context.Background()); err != nil { + if err := vm2.Shutdown(context.Background()); err != nil { t.Fatal(err) } }() newTxPoolHeadChan1 := make(chan core.NewTxPoolReorgEvent, 1) - tvm1.vm.txPool.SubscribeNewReorgEvent(newTxPoolHeadChan1) + vm1.txPool.SubscribeNewReorgEvent(newTxPoolHeadChan1) newTxPoolHeadChan2 := make(chan core.NewTxPoolReorgEvent, 1) - tvm2.vm.txPool.SubscribeNewReorgEvent(newTxPoolHeadChan2) + vm2.txPool.SubscribeNewReorgEvent(newTxPoolHeadChan2) - key := testKeys[0].ToECDSA() - address := testEthAddrs[0] + key := vmtest.TestKeys[1].ToECDSA() + address := vmtest.TestEthAddrs[1] - importTx, err := tvm1.atomicVM.NewImportTx(tvm1.vm.ctx.XChainID, address, initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) + tx := types.NewTransaction(uint64(0), vmtest.TestEthAddrs[1], big.NewInt(1), 21000, big.NewInt(ap0.MinGasPrice), nil) + signedTx, err := types.SignTx(tx, types.NewEIP155Signer(vm1.chainConfig.ChainID), vmtest.TestKeys[0].ToECDSA()) if err != nil { t.Fatal(err) } - - if err := tvm1.atomicVM.AtomicMempool.AddLocalTx(importTx); err != nil { - t.Fatal(err) + errs := vm1.txPool.AddRemotesSync([]*types.Transaction{signedTx}) + for i, err := range errs { + if err != nil { + t.Fatalf("Failed to add tx at index %d: %s", i, err) + } } - require.Equal(t, commonEng.PendingTxs, tvm1.WaitForEvent(context.Background())) + msg, err := vm1.WaitForEvent(context.Background()) + require.NoError(t, err) + require.Equal(t, commonEng.PendingTxs, msg) - vm1BlkA, err := tvm1.vm.BuildBlock(context.Background()) + vm1BlkA, err := vm1.BuildBlock(context.Background()) if err != nil { - t.Fatalf("Failed to build block with import transaction: %s", err) + t.Fatalf("Failed to build block with transaction: %s", err) } if err := vm1BlkA.Verify(context.Background()); err != nil { t.Fatalf("Block failed verification on VM1: %s", err) } - if _, err := tvm1.vm.GetBlockIDAtHeight(context.Background(), vm1BlkA.Height()); err != database.ErrNotFound { + if _, err := vm1.GetBlockIDAtHeight(context.Background(), vm1BlkA.Height()); err != database.ErrNotFound { t.Fatalf("Expected unaccepted block not to be indexed by height, but found %s", err) } - if err := tvm1.vm.SetPreference(context.Background(), vm1BlkA.ID()); err != nil { + if err := vm1.SetPreference(context.Background(), vm1BlkA.ID()); err != nil { t.Fatal(err) } - vm2BlkA, err := tvm2.vm.ParseBlock(context.Background(), vm1BlkA.Bytes()) + vm2BlkA, err := vm2.ParseBlock(context.Background(), vm1BlkA.Bytes()) if err != nil { t.Fatalf("Unexpected error parsing block from vm2: %s", err) } if err := vm2BlkA.Verify(context.Background()); err != nil { t.Fatalf("Block failed verification on VM2: %s", err) } - if _, err := tvm2.vm.GetBlockIDAtHeight(context.Background(), vm2BlkA.Height()); err != database.ErrNotFound { + if _, err := vm2.GetBlockIDAtHeight(context.Background(), vm2BlkA.Height()); err != database.ErrNotFound { t.Fatalf("Expected unaccepted block not to be indexed by height, but found %s", err) } - if err := tvm2.vm.SetPreference(context.Background(), vm2BlkA.ID()); err != nil { + if err := vm2.SetPreference(context.Background(), vm2BlkA.ID()); err != nil { t.Fatal(err) } if err := vm1BlkA.Accept(context.Background()); err != nil { t.Fatalf("VM1 failed to accept block: %s", err) } - if blkID, err := tvm1.vm.GetBlockIDAtHeight(context.Background(), vm1BlkA.Height()); err != nil { + if blkID, err := vm1.GetBlockIDAtHeight(context.Background(), vm1BlkA.Height()); err != nil { t.Fatalf("Height lookuped failed on accepted block: %s", err) } else if blkID != vm1BlkA.ID() { t.Fatalf("Expected accepted block to be indexed by height, but found %s", blkID) @@ -1862,7 +899,7 @@ func testNonCanonicalAccept(t *testing.T, scheme string) { if err := vm2BlkA.Accept(context.Background()); err != nil { t.Fatalf("VM2 failed to accept block: %s", err) } - if blkID, err := tvm2.vm.GetBlockIDAtHeight(context.Background(), vm2BlkA.Height()); err != nil { + if blkID, err := vm2.GetBlockIDAtHeight(context.Background(), vm2BlkA.Height()); err != nil { t.Fatalf("Height lookuped failed on accepted block: %s", err) } else if blkID != vm2BlkA.ID() { t.Fatalf("Expected accepted block to be indexed by height, but found %s", blkID) @@ -1882,26 +919,26 @@ func testNonCanonicalAccept(t *testing.T, scheme string) { txs := make([]*types.Transaction, 10) for i := 0; i < 10; i++ { tx := types.NewTransaction(uint64(i), address, big.NewInt(10), 21000, big.NewInt(ap0.MinGasPrice), nil) - signedTx, err := types.SignTx(tx, types.NewEIP155Signer(tvm1.vm.chainID), key) + signedTx, err := types.SignTx(tx, types.NewEIP155Signer(vm1.chainID), key) if err != nil { t.Fatal(err) } txs[i] = signedTx } - var errs []error - // Add the remote transactions, build the block, and set VM1's preference for block A - errs = tvm1.vm.txPool.AddRemotesSync(txs) + errs = vm1.txPool.AddRemotesSync(txs) for i, err := range errs { if err != nil { t.Fatalf("Failed to add transaction to VM1 at index %d: %s", i, err) } } - require.Equal(t, commonEng.PendingTxs, tvm1.WaitForEvent(context.Background())) + msg, err = vm1.WaitForEvent(context.Background()) + require.NoError(t, err) + require.Equal(t, commonEng.PendingTxs, msg) - vm1BlkB, err := tvm1.vm.BuildBlock(context.Background()) + vm1BlkB, err := vm1.BuildBlock(context.Background()) if err != nil { t.Fatal(err) } @@ -1910,36 +947,39 @@ func testNonCanonicalAccept(t *testing.T, scheme string) { t.Fatal(err) } - if _, err := tvm1.vm.GetBlockIDAtHeight(context.Background(), vm1BlkB.Height()); err != database.ErrNotFound { + if _, err := vm1.GetBlockIDAtHeight(context.Background(), vm1BlkB.Height()); err != database.ErrNotFound { t.Fatalf("Expected unaccepted block not to be indexed by height, but found %s", err) } - if err := tvm1.vm.SetPreference(context.Background(), vm1BlkB.ID()); err != nil { + if err := vm1.SetPreference(context.Background(), vm1BlkB.ID()); err != nil { t.Fatal(err) } - tvm1.vm.eth.APIBackend.SetAllowUnfinalizedQueries(true) + vm1.eth.APIBackend.SetAllowUnfinalizedQueries(true) blkBHeight := vm1BlkB.Height() - blkBHash := vm1BlkB.(*chain.BlockWrapper).Block.(extension.ExtendedBlock).GetEthBlock().Hash() - if b := tvm1.vm.blockChain.GetBlockByNumber(blkBHeight); b.Hash() != blkBHash { + blkBHash := vm1BlkB.(*chain.BlockWrapper).Block.(*wrappedBlock).ethBlock.Hash() + if b := vm1.blockChain.GetBlockByNumber(blkBHeight); b.Hash() != blkBHash { t.Fatalf("expected block at %d to have hash %s but got %s", blkBHeight, blkBHash.Hex(), b.Hash().Hex()) } - errs = tvm2.vm.txPool.AddRemotesSync(txs[0:5]) + errs = vm2.txPool.AddRemotesSync(txs[0:5]) for i, err := range errs { if err != nil { t.Fatalf("Failed to add transaction to VM2 at index %d: %s", i, err) } } - require.Equal(t, commonEng.PendingTxs, tvm2.WaitForEvent(context.Background())) - vm2BlkC, err := tvm2.vm.BuildBlock(context.Background()) + msg, err = vm2.WaitForEvent(context.Background()) + require.NoError(t, err) + require.Equal(t, commonEng.PendingTxs, msg) + + vm2BlkC, err := vm2.BuildBlock(context.Background()) if err != nil { t.Fatalf("Failed to build BlkC on VM2: %s", err) } - vm1BlkC, err := tvm1.vm.ParseBlock(context.Background(), vm2BlkC.Bytes()) + vm1BlkC, err := vm1.ParseBlock(context.Background(), vm2BlkC.Bytes()) if err != nil { t.Fatalf("Unexpected error parsing block from vm2: %s", err) } @@ -1948,7 +988,7 @@ func testNonCanonicalAccept(t *testing.T, scheme string) { t.Fatalf("Block failed verification on VM1: %s", err) } - if _, err := tvm1.vm.GetBlockIDAtHeight(context.Background(), vm1BlkC.Height()); err != database.ErrNotFound { + if _, err := vm1.GetBlockIDAtHeight(context.Background(), vm1BlkC.Height()); err != database.ErrNotFound { t.Fatalf("Expected unaccepted block not to be indexed by height, but found %s", err) } @@ -1956,14 +996,14 @@ func testNonCanonicalAccept(t *testing.T, scheme string) { t.Fatalf("VM1 failed to accept block: %s", err) } - if blkID, err := tvm1.vm.GetBlockIDAtHeight(context.Background(), vm1BlkC.Height()); err != nil { + if blkID, err := vm1.GetBlockIDAtHeight(context.Background(), vm1BlkC.Height()); err != nil { t.Fatalf("Height lookuped failed on accepted block: %s", err) } else if blkID != vm1BlkC.ID() { t.Fatalf("Expected accepted block to be indexed by height, but found %s", blkID) } - blkCHash := vm1BlkC.(*chain.BlockWrapper).Block.(extension.ExtendedBlock).GetEthBlock().Hash() - if b := tvm1.vm.blockChain.GetBlockByNumber(blkBHeight); b.Hash() != blkCHash { + blkCHash := vm1BlkC.(*chain.BlockWrapper).Block.(*wrappedBlock).ethBlock.Hash() + if b := vm1.blockChain.GetBlockByNumber(blkBHeight); b.Hash() != blkCHash { t.Fatalf("expected block at %d to have hash %s but got %s", blkBHeight, blkCHash.Hex(), b.Hash().Hex()) } } @@ -1978,7 +1018,7 @@ func testNonCanonicalAccept(t *testing.T, scheme string) { // | // D func TestStickyPreference(t *testing.T) { - for _, scheme := range schemes { + for _, scheme := range vmtest.Schemes { t.Run(scheme, func(t *testing.T) { testStickyPreference(t, scheme) }) @@ -1986,67 +1026,71 @@ func TestStickyPreference(t *testing.T) { } func testStickyPreference(t *testing.T, scheme string) { - importAmount := uint64(1000000000) fork := upgradetest.NoUpgrades - tvmConfig := testVMConfig{ - fork: &fork, - utxos: map[ids.ShortID]uint64{ - testShortIDAddrs[0]: importAmount, - }, - configJSON: getConfig(scheme, ""), + tvmConfig := vmtest.TestVMConfig{ + Fork: &fork, + Scheme: scheme, } - tvm1 := newVM(t, tvmConfig) - tvm2 := newVM(t, tvmConfig) + vm1 := newDefaultTestVM() + vm2 := newDefaultTestVM() + vmtest.SetupTestVM(t, vm1, tvmConfig) + vmtest.SetupTestVM(t, vm2, tvmConfig) + defer func() { - if err := tvm1.vm.Shutdown(context.Background()); err != nil { + if err := vm1.Shutdown(context.Background()); err != nil { t.Fatal(err) } - if err := tvm2.vm.Shutdown(context.Background()); err != nil { + if err := vm2.Shutdown(context.Background()); err != nil { t.Fatal(err) } }() newTxPoolHeadChan1 := make(chan core.NewTxPoolReorgEvent, 1) - tvm1.vm.txPool.SubscribeNewReorgEvent(newTxPoolHeadChan1) + vm1.txPool.SubscribeNewReorgEvent(newTxPoolHeadChan1) newTxPoolHeadChan2 := make(chan core.NewTxPoolReorgEvent, 1) - tvm2.vm.txPool.SubscribeNewReorgEvent(newTxPoolHeadChan2) + vm2.txPool.SubscribeNewReorgEvent(newTxPoolHeadChan2) - key := testKeys[0].ToECDSA() - address := testEthAddrs[0] + key := vmtest.TestKeys[1].ToECDSA() + address := vmtest.TestEthAddrs[1] - importTx, err := tvm1.atomicVM.NewImportTx(tvm1.vm.ctx.XChainID, address, initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) + tx := types.NewTransaction(uint64(0), vmtest.TestEthAddrs[1], big.NewInt(1), 21000, big.NewInt(ap0.MinGasPrice), nil) + signedTx, err := types.SignTx(tx, types.NewEIP155Signer(vm1.chainConfig.ChainID), vmtest.TestKeys[0].ToECDSA()) if err != nil { t.Fatal(err) } - - if err := tvm1.atomicVM.AtomicMempool.AddLocalTx(importTx); err != nil { - t.Fatal(err) + errs := vm1.txPool.AddRemotesSync([]*types.Transaction{signedTx}) + for i, err := range errs { + if err != nil { + t.Fatalf("Failed to add tx at index %d: %s", i, err) + } } - require.Equal(t, commonEng.PendingTxs, tvm1.WaitForEvent(context.Background())) + msg, err := vm1.WaitForEvent(context.Background()) + require.NoError(t, err) + require.Equal(t, commonEng.PendingTxs, msg) - vm1BlkA, err := tvm1.vm.BuildBlock(context.Background()) + vm1BlkA, err := vm1.BuildBlock(context.Background()) if err != nil { - t.Fatalf("Failed to build block with import transaction: %s", err) + t.Fatalf("Failed to build block with transaction: %s", err) } if err := vm1BlkA.Verify(context.Background()); err != nil { t.Fatalf("Block failed verification on VM1: %s", err) } - if err := tvm1.vm.SetPreference(context.Background(), vm1BlkA.ID()); err != nil { + if err := vm1.SetPreference(context.Background(), vm1BlkA.ID()); err != nil { t.Fatal(err) } - vm2BlkA, err := tvm2.vm.ParseBlock(context.Background(), vm1BlkA.Bytes()) + vm2BlkA, err := vm2.ParseBlock(context.Background(), vm1BlkA.Bytes()) if err != nil { t.Fatalf("Unexpected error parsing block from vm2: %s", err) } if err := vm2BlkA.Verify(context.Background()); err != nil { t.Fatalf("Block failed verification on VM2: %s", err) } - if err := tvm2.vm.SetPreference(context.Background(), vm2BlkA.ID()); err != nil { + if err := vm2.SetPreference(context.Background(), vm2BlkA.ID()); err != nil { t.Fatal(err) } @@ -2071,26 +1115,26 @@ func testStickyPreference(t *testing.T, scheme string) { txs := make([]*types.Transaction, 10) for i := 0; i < 10; i++ { tx := types.NewTransaction(uint64(i), address, big.NewInt(10), 21000, big.NewInt(ap0.MinGasPrice), nil) - signedTx, err := types.SignTx(tx, types.NewEIP155Signer(tvm1.vm.chainID), key) + signedTx, err := types.SignTx(tx, types.NewEIP155Signer(vm1.chainID), key) if err != nil { t.Fatal(err) } txs[i] = signedTx } - var errs []error - // Add the remote transactions, build the block, and set VM1's preference for block A - errs = tvm1.vm.txPool.AddRemotesSync(txs) + errs = vm1.txPool.AddRemotesSync(txs) for i, err := range errs { if err != nil { t.Fatalf("Failed to add transaction to VM1 at index %d: %s", i, err) } } - require.Equal(t, commonEng.PendingTxs, tvm1.WaitForEvent(context.Background())) + msg, err = vm1.WaitForEvent(context.Background()) + require.NoError(t, err) + require.Equal(t, commonEng.PendingTxs, msg) - vm1BlkB, err := tvm1.vm.BuildBlock(context.Background()) + vm1BlkB, err := vm1.BuildBlock(context.Background()) if err != nil { t.Fatal(err) } @@ -2099,27 +1143,30 @@ func testStickyPreference(t *testing.T, scheme string) { t.Fatal(err) } - if err := tvm1.vm.SetPreference(context.Background(), vm1BlkB.ID()); err != nil { + if err := vm1.SetPreference(context.Background(), vm1BlkB.ID()); err != nil { t.Fatal(err) } - tvm1.vm.eth.APIBackend.SetAllowUnfinalizedQueries(true) + vm1.eth.APIBackend.SetAllowUnfinalizedQueries(true) blkBHeight := vm1BlkB.Height() - blkBHash := vm1BlkB.(*chain.BlockWrapper).Block.(extension.ExtendedBlock).GetEthBlock().Hash() - if b := tvm1.vm.blockChain.GetBlockByNumber(blkBHeight); b.Hash() != blkBHash { + blkBHash := vm1BlkB.(*chain.BlockWrapper).Block.(*wrappedBlock).ethBlock.Hash() + if b := vm1.blockChain.GetBlockByNumber(blkBHeight); b.Hash() != blkBHash { t.Fatalf("expected block at %d to have hash %s but got %s", blkBHeight, blkBHash.Hex(), b.Hash().Hex()) } - errs = tvm2.vm.txPool.AddRemotesSync(txs[0:5]) + errs = vm2.txPool.AddRemotesSync(txs[0:5]) for i, err := range errs { if err != nil { t.Fatalf("Failed to add transaction to VM2 at index %d: %s", i, err) } } - require.Equal(t, commonEng.PendingTxs, tvm2.WaitForEvent(context.Background())) - vm2BlkC, err := tvm2.vm.BuildBlock(context.Background()) + msg, err = vm2.WaitForEvent(context.Background()) + require.NoError(t, err) + require.Equal(t, commonEng.PendingTxs, msg) + + vm2BlkC, err := vm2.BuildBlock(context.Background()) if err != nil { t.Fatalf("Failed to build BlkC on VM2: %s", err) } @@ -2128,7 +1175,7 @@ func testStickyPreference(t *testing.T, scheme string) { t.Fatalf("BlkC failed verification on VM2: %s", err) } - if err := tvm2.vm.SetPreference(context.Background(), vm2BlkC.ID()); err != nil { + if err := vm2.SetPreference(context.Background(), vm2BlkC.ID()); err != nil { t.Fatal(err) } @@ -2137,32 +1184,35 @@ func testStickyPreference(t *testing.T, scheme string) { t.Fatalf("Expected new block to match") } - errs = tvm2.vm.txPool.AddRemotesSync(txs[5:]) + errs = vm2.txPool.AddRemotesSync(txs[5:]) for i, err := range errs { if err != nil { t.Fatalf("Failed to add transaction to VM2 at index %d: %s", i, err) } } - require.Equal(t, commonEng.PendingTxs, tvm2.WaitForEvent(context.Background())) - vm2BlkD, err := tvm2.vm.BuildBlock(context.Background()) + msg, err = vm2.WaitForEvent(context.Background()) + require.NoError(t, err) + require.Equal(t, commonEng.PendingTxs, msg) + + vm2BlkD, err := vm2.BuildBlock(context.Background()) if err != nil { t.Fatalf("Failed to build BlkD on VM2: %s", err) } // Parse blocks produced in vm2 - vm1BlkC, err := tvm1.vm.ParseBlock(context.Background(), vm2BlkC.Bytes()) + vm1BlkC, err := vm1.ParseBlock(context.Background(), vm2BlkC.Bytes()) if err != nil { t.Fatalf("Unexpected error parsing block from vm2: %s", err) } - blkCHash := vm1BlkC.(*chain.BlockWrapper).Block.(extension.ExtendedBlock).GetEthBlock().Hash() + blkCHash := vm1BlkC.(*chain.BlockWrapper).Block.(*wrappedBlock).ethBlock.Hash() - vm1BlkD, err := tvm1.vm.ParseBlock(context.Background(), vm2BlkD.Bytes()) + vm1BlkD, err := vm1.ParseBlock(context.Background(), vm2BlkD.Bytes()) if err != nil { t.Fatalf("Unexpected error parsing block from vm2: %s", err) } blkDHeight := vm1BlkD.Height() - blkDHash := vm1BlkD.(*chain.BlockWrapper).Block.(extension.ExtendedBlock).GetEthBlock().Hash() + blkDHash := vm1BlkD.(*chain.BlockWrapper).Block.(*wrappedBlock).ethBlock.Hash() // Should be no-ops if err := vm1BlkC.Verify(context.Background()); err != nil { @@ -2171,13 +1221,13 @@ func testStickyPreference(t *testing.T, scheme string) { if err := vm1BlkD.Verify(context.Background()); err != nil { t.Fatalf("Block failed verification on VM1: %s", err) } - if b := tvm1.vm.blockChain.GetBlockByNumber(blkBHeight); b.Hash() != blkBHash { + if b := vm1.blockChain.GetBlockByNumber(blkBHeight); b.Hash() != blkBHash { t.Fatalf("expected block at %d to have hash %s but got %s", blkBHeight, blkBHash.Hex(), b.Hash().Hex()) } - if b := tvm1.vm.blockChain.GetBlockByNumber(blkDHeight); b != nil { + if b := vm1.blockChain.GetBlockByNumber(blkDHeight); b != nil { t.Fatalf("expected block at %d to be nil but got %s", blkDHeight, b.Hash().Hex()) } - if b := tvm1.vm.blockChain.CurrentBlock(); b.Hash() != blkBHash { + if b := vm1.blockChain.CurrentBlock(); b.Hash() != blkBHash { t.Fatalf("expected current block to have hash %s but got %s", blkBHash.Hex(), b.Hash().Hex()) } @@ -2188,35 +1238,34 @@ func testStickyPreference(t *testing.T, scheme string) { if err := vm1BlkD.Verify(context.Background()); err != nil { t.Fatalf("Block failed verification on VM1: %s", err) } - if b := tvm1.vm.blockChain.GetBlockByNumber(blkBHeight); b.Hash() != blkBHash { + if b := vm1.blockChain.GetBlockByNumber(blkBHeight); b.Hash() != blkBHash { t.Fatalf("expected block at %d to have hash %s but got %s", blkBHeight, blkBHash.Hex(), b.Hash().Hex()) } - if b := tvm1.vm.blockChain.GetBlockByNumber(blkDHeight); b != nil { + if b := vm1.blockChain.GetBlockByNumber(blkDHeight); b != nil { t.Fatalf("expected block at %d to be nil but got %s", blkDHeight, b.Hash().Hex()) } - if b := tvm1.vm.blockChain.CurrentBlock(); b.Hash() != blkBHash { + if b := vm1.blockChain.CurrentBlock(); b.Hash() != blkBHash { t.Fatalf("expected current block to have hash %s but got %s", blkBHash.Hex(), b.Hash().Hex()) } // Should be queryable after setting preference to side chain - if err := tvm1.vm.SetPreference(context.Background(), vm1BlkD.ID()); err != nil { + if err := vm1.SetPreference(context.Background(), vm1BlkD.ID()); err != nil { t.Fatal(err) } - if b := tvm1.vm.blockChain.GetBlockByNumber(blkBHeight); b.Hash() != blkCHash { + if b := vm1.blockChain.GetBlockByNumber(blkBHeight); b.Hash() != blkCHash { t.Fatalf("expected block at %d to have hash %s but got %s", blkBHeight, blkCHash.Hex(), b.Hash().Hex()) } - if b := tvm1.vm.blockChain.GetBlockByNumber(blkDHeight); b.Hash() != blkDHash { + if b := vm1.blockChain.GetBlockByNumber(blkDHeight); b.Hash() != blkDHash { t.Fatalf("expected block at %d to have hash %s but got %s", blkDHeight, blkDHash.Hex(), b.Hash().Hex()) } - if b := tvm1.vm.blockChain.CurrentBlock(); b.Hash() != blkDHash { + if b := vm1.blockChain.CurrentBlock(); b.Hash() != blkDHash { t.Fatalf("expected current block to have hash %s but got %s", blkDHash.Hex(), b.Hash().Hex()) } // Attempt to accept out of order - if err := vm1BlkD.Accept(context.Background()); !strings.Contains(err.Error(), "expected accepted block to have parent") { - t.Fatalf("unexpected error when accepting out of order block: %s", err) - } + err = vm1BlkD.Accept(context.Background()) + require.ErrorContains(t, err, "expected accepted block to have parent") // Accept in order if err := vm1BlkC.Accept(context.Background()); err != nil { @@ -2227,13 +1276,13 @@ func testStickyPreference(t *testing.T, scheme string) { } // Ensure queryable after accepting - if b := tvm1.vm.blockChain.GetBlockByNumber(blkBHeight); b.Hash() != blkCHash { + if b := vm1.blockChain.GetBlockByNumber(blkBHeight); b.Hash() != blkCHash { t.Fatalf("expected block at %d to have hash %s but got %s", blkBHeight, blkCHash.Hex(), b.Hash().Hex()) } - if b := tvm1.vm.blockChain.GetBlockByNumber(blkDHeight); b.Hash() != blkDHash { + if b := vm1.blockChain.GetBlockByNumber(blkDHeight); b.Hash() != blkDHash { t.Fatalf("expected block at %d to have hash %s but got %s", blkDHeight, blkDHash.Hex(), b.Hash().Hex()) } - if b := tvm1.vm.blockChain.CurrentBlock(); b.Hash() != blkDHash { + if b := vm1.blockChain.CurrentBlock(); b.Hash() != blkDHash { t.Fatalf("expected current block to have hash %s but got %s", blkDHash.Hex(), b.Hash().Hex()) } } @@ -2248,7 +1297,7 @@ func testStickyPreference(t *testing.T, scheme string) { // | // D func TestUncleBlock(t *testing.T) { - for _, scheme := range schemes { + for _, scheme := range vmtest.Schemes { t.Run(scheme, func(t *testing.T) { testUncleBlock(t, scheme) }) @@ -2256,66 +1305,70 @@ func TestUncleBlock(t *testing.T) { } func testUncleBlock(t *testing.T, scheme string) { - importAmount := uint64(1000000000) fork := upgradetest.NoUpgrades - tvmConfig := testVMConfig{ - fork: &fork, - utxos: map[ids.ShortID]uint64{ - testShortIDAddrs[0]: importAmount, - }, - configJSON: getConfig(scheme, ""), + tvmConfig := vmtest.TestVMConfig{ + Fork: &fork, + Scheme: scheme, } - tvm1 := newVM(t, tvmConfig) - tvm2 := newVM(t, tvmConfig) + vm1 := newDefaultTestVM() + vm2 := newDefaultTestVM() + vmtest.SetupTestVM(t, vm1, tvmConfig) + vmtest.SetupTestVM(t, vm2, tvmConfig) + defer func() { - if err := tvm1.vm.Shutdown(context.Background()); err != nil { + if err := vm1.Shutdown(context.Background()); err != nil { t.Fatal(err) } - if err := tvm2.vm.Shutdown(context.Background()); err != nil { + if err := vm2.Shutdown(context.Background()); err != nil { t.Fatal(err) } }() newTxPoolHeadChan1 := make(chan core.NewTxPoolReorgEvent, 1) - tvm1.vm.txPool.SubscribeNewReorgEvent(newTxPoolHeadChan1) + vm1.txPool.SubscribeNewReorgEvent(newTxPoolHeadChan1) newTxPoolHeadChan2 := make(chan core.NewTxPoolReorgEvent, 1) - tvm2.vm.txPool.SubscribeNewReorgEvent(newTxPoolHeadChan2) + vm2.txPool.SubscribeNewReorgEvent(newTxPoolHeadChan2) - key := testKeys[0].ToECDSA() - address := testEthAddrs[0] + key := vmtest.TestKeys[1].ToECDSA() + address := vmtest.TestEthAddrs[1] - importTx, err := tvm1.atomicVM.NewImportTx(tvm1.vm.ctx.XChainID, address, initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) + tx := types.NewTransaction(uint64(0), vmtest.TestEthAddrs[1], big.NewInt(1), 21000, big.NewInt(ap0.MinGasPrice), nil) + signedTx, err := types.SignTx(tx, types.NewEIP155Signer(vm1.chainConfig.ChainID), vmtest.TestKeys[0].ToECDSA()) if err != nil { t.Fatal(err) } - - if err := tvm1.atomicVM.AtomicMempool.AddLocalTx(importTx); err != nil { - t.Fatal(err) + errs := vm1.txPool.AddRemotesSync([]*types.Transaction{signedTx}) + for i, err := range errs { + if err != nil { + t.Fatalf("Failed to add tx at index %d: %s", i, err) + } } - require.Equal(t, commonEng.PendingTxs, tvm1.WaitForEvent(context.Background())) + msg, err := vm1.WaitForEvent(context.Background()) + require.NoError(t, err) + require.Equal(t, commonEng.PendingTxs, msg) - vm1BlkA, err := tvm1.vm.BuildBlock(context.Background()) + vm1BlkA, err := vm1.BuildBlock(context.Background()) if err != nil { - t.Fatalf("Failed to build block with import transaction: %s", err) + t.Fatalf("Failed to build block with transaction: %s", err) } if err := vm1BlkA.Verify(context.Background()); err != nil { t.Fatalf("Block failed verification on VM1: %s", err) } - if err := tvm1.vm.SetPreference(context.Background(), vm1BlkA.ID()); err != nil { + if err := vm1.SetPreference(context.Background(), vm1BlkA.ID()); err != nil { t.Fatal(err) } - vm2BlkA, err := tvm2.vm.ParseBlock(context.Background(), vm1BlkA.Bytes()) + vm2BlkA, err := vm2.ParseBlock(context.Background(), vm1BlkA.Bytes()) if err != nil { t.Fatalf("Unexpected error parsing block from vm2: %s", err) } if err := vm2BlkA.Verify(context.Background()); err != nil { t.Fatalf("Block failed verification on VM2: %s", err) } - if err := tvm2.vm.SetPreference(context.Background(), vm2BlkA.ID()); err != nil { + if err := vm2.SetPreference(context.Background(), vm2BlkA.ID()); err != nil { t.Fatal(err) } @@ -2338,25 +1391,25 @@ func testUncleBlock(t *testing.T, scheme string) { txs := make([]*types.Transaction, 10) for i := 0; i < 10; i++ { tx := types.NewTransaction(uint64(i), address, big.NewInt(10), 21000, big.NewInt(ap0.MinGasPrice), nil) - signedTx, err := types.SignTx(tx, types.NewEIP155Signer(tvm1.vm.chainID), key) + signedTx, err := types.SignTx(tx, types.NewEIP155Signer(vm1.chainID), key) if err != nil { t.Fatal(err) } txs[i] = signedTx } - var errs []error - - errs = tvm1.vm.txPool.AddRemotesSync(txs) + errs = vm1.txPool.AddRemotesSync(txs) for i, err := range errs { if err != nil { t.Fatalf("Failed to add transaction to VM1 at index %d: %s", i, err) } } - require.Equal(t, commonEng.PendingTxs, tvm1.WaitForEvent(context.Background())) + msg, err = vm1.WaitForEvent(context.Background()) + require.NoError(t, err) + require.Equal(t, commonEng.PendingTxs, msg) - vm1BlkB, err := tvm1.vm.BuildBlock(context.Background()) + vm1BlkB, err := vm1.BuildBlock(context.Background()) if err != nil { t.Fatal(err) } @@ -2365,19 +1418,22 @@ func testUncleBlock(t *testing.T, scheme string) { t.Fatal(err) } - if err := tvm1.vm.SetPreference(context.Background(), vm1BlkB.ID()); err != nil { + if err := vm1.SetPreference(context.Background(), vm1BlkB.ID()); err != nil { t.Fatal(err) } - errs = tvm2.vm.txPool.AddRemotesSync(txs[0:5]) + errs = vm2.txPool.AddRemotesSync(txs[0:5]) for i, err := range errs { if err != nil { t.Fatalf("Failed to add transaction to VM2 at index %d: %s", i, err) } } - require.Equal(t, commonEng.PendingTxs, tvm2.WaitForEvent(context.Background())) - vm2BlkC, err := tvm2.vm.BuildBlock(context.Background()) + msg, err = vm2.WaitForEvent(context.Background()) + require.NoError(t, err) + require.Equal(t, commonEng.PendingTxs, msg) + + vm2BlkC, err := vm2.BuildBlock(context.Background()) if err != nil { t.Fatalf("Failed to build BlkC on VM2: %s", err) } @@ -2386,7 +1442,7 @@ func testUncleBlock(t *testing.T, scheme string) { t.Fatalf("BlkC failed verification on VM2: %s", err) } - if err := tvm2.vm.SetPreference(context.Background(), vm2BlkC.ID()); err != nil { + if err := vm2.SetPreference(context.Background(), vm2BlkC.ID()); err != nil { t.Fatal(err) } @@ -2395,22 +1451,25 @@ func testUncleBlock(t *testing.T, scheme string) { t.Fatalf("Expected new block to match") } - errs = tvm2.vm.txPool.AddRemotesSync(txs[5:10]) + errs = vm2.txPool.AddRemotesSync(txs[5:10]) for i, err := range errs { if err != nil { t.Fatalf("Failed to add transaction to VM2 at index %d: %s", i, err) } } - require.Equal(t, commonEng.PendingTxs, tvm2.WaitForEvent(context.Background())) - vm2BlkD, err := tvm2.vm.BuildBlock(context.Background()) + msg, err = vm2.WaitForEvent(context.Background()) + require.NoError(t, err) + require.Equal(t, commonEng.PendingTxs, msg) + + vm2BlkD, err := vm2.BuildBlock(context.Background()) if err != nil { t.Fatalf("Failed to build BlkD on VM2: %s", err) } // Create uncle block from blkD - blkDEthBlock := vm2BlkD.(*chain.BlockWrapper).Block.(extension.ExtendedBlock).GetEthBlock() - uncles := []*types.Header{vm1BlkB.(*chain.BlockWrapper).Block.(extension.ExtendedBlock).GetEthBlock().Header()} + blkDEthBlock := vm2BlkD.(*chain.BlockWrapper).Block.(*wrappedBlock).ethBlock + uncles := []*types.Header{vm1BlkB.(*chain.BlockWrapper).Block.(*wrappedBlock).ethBlock.Header()} uncleBlockHeader := types.CopyHeader(blkDEthBlock.Header()) uncleBlockHeader.UncleHash = types.CalcUncleHash(uncles) @@ -2423,93 +1482,19 @@ func testUncleBlock(t *testing.T, scheme string) { customtypes.BlockExtData(blkDEthBlock), false, ) - uncleBlock, err := wrapBlock(uncleEthBlock, tvm2.vm) - if err != nil { - t.Fatal(err) - } + uncleBlock, err := wrapBlock(uncleEthBlock, vm2) + require.NoError(t, err) if err := uncleBlock.Verify(context.Background()); !errors.Is(err, errUnclesUnsupported) { t.Fatalf("VM2 should have failed with %q but got %q", errUnclesUnsupported, err.Error()) } - if _, err := tvm1.vm.ParseBlock(context.Background(), vm2BlkC.Bytes()); err != nil { + if _, err := vm1.ParseBlock(context.Background(), vm2BlkC.Bytes()); err != nil { t.Fatalf("VM1 errored parsing blkC: %s", err) } - if _, err := tvm1.vm.ParseBlock(context.Background(), uncleBlock.Bytes()); !errors.Is(err, errUnclesUnsupported) { + if _, err := vm1.ParseBlock(context.Background(), uncleBlock.Bytes()); !errors.Is(err, errUnclesUnsupported) { t.Fatalf("VM1 should have failed with %q but got %q", errUnclesUnsupported, err.Error()) } } -// Regression test to ensure that a VM that is not able to parse a block that -// contains no transactions. -func TestEmptyBlock(t *testing.T) { - for _, scheme := range schemes { - t.Run(scheme, func(t *testing.T) { - testEmptyBlock(t, scheme) - }) - } -} - -func testEmptyBlock(t *testing.T, scheme string) { - importAmount := uint64(1000000000) - fork := upgradetest.NoUpgrades - tvm := newVM(t, testVMConfig{ - fork: &fork, - utxos: map[ids.ShortID]uint64{ - testShortIDAddrs[0]: importAmount, - }, - configJSON: getConfig(scheme, ""), - }) - defer func() { - if err := tvm.vm.Shutdown(context.Background()); err != nil { - t.Fatal(err) - } - }() - - importTx, err := tvm.atomicVM.NewImportTx(tvm.vm.ctx.XChainID, testEthAddrs[0], initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) - if err != nil { - t.Fatal(err) - } - - if err := tvm.atomicVM.AtomicMempool.AddLocalTx(importTx); err != nil { - t.Fatal(err) - } - - require.Equal(t, commonEng.PendingTxs, tvm.WaitForEvent(context.Background())) - - blk, err := tvm.vm.BuildBlock(context.Background()) - if err != nil { - t.Fatalf("Failed to build block with import transaction: %s", err) - } - - // Create empty block from blkA - ethBlock := blk.(*chain.BlockWrapper).Block.(extension.ExtendedBlock).GetEthBlock() - - emptyEthBlock := customtypes.NewBlockWithExtData( - types.CopyHeader(ethBlock.Header()), - nil, - nil, - nil, - new(trie.Trie), - nil, - false, - ) - - if len(customtypes.BlockExtData(emptyEthBlock)) != 0 || customtypes.GetHeaderExtra(emptyEthBlock.Header()).ExtDataHash != (common.Hash{}) { - t.Fatalf("emptyEthBlock should not have any extra data") - } - - emptyBlock, err := wrapBlock(emptyEthBlock, tvm.vm) - if err != nil { - t.Fatal(err) - } - - if _, err := tvm.vm.ParseBlock(context.Background(), emptyBlock.Bytes()); !errors.Is(err, atomicvm.ErrEmptyBlock) { - t.Fatalf("VM should have failed with errEmptyBlock but got %s", err.Error()) - } - if err := emptyBlock.Verify(context.Background()); !errors.Is(err, atomicvm.ErrEmptyBlock) { - t.Fatalf("block should have failed verification with errEmptyBlock but got %s", err.Error()) - } -} - // Regression test to ensure that a VM that verifies block B, C, then // D (preferring block B) reorgs when C and then D are accepted. // @@ -2519,7 +1504,7 @@ func testEmptyBlock(t *testing.T, scheme string) { // | // D func TestAcceptReorg(t *testing.T) { - for _, scheme := range schemes { + for _, scheme := range vmtest.Schemes { t.Run(scheme, func(t *testing.T) { testAcceptReorg(t, scheme) }) @@ -2527,67 +1512,71 @@ func TestAcceptReorg(t *testing.T) { } func testAcceptReorg(t *testing.T, scheme string) { - importAmount := uint64(1000000000) fork := upgradetest.NoUpgrades - tvmConfig := testVMConfig{ - fork: &fork, - utxos: map[ids.ShortID]uint64{ - testShortIDAddrs[0]: importAmount, - }, - configJSON: getConfig(scheme, ""), + tvmConfig := vmtest.TestVMConfig{ + Fork: &fork, + Scheme: scheme, } - tvm1 := newVM(t, tvmConfig) - tvm2 := newVM(t, tvmConfig) + vm1 := newDefaultTestVM() + vm2 := newDefaultTestVM() + vmtest.SetupTestVM(t, vm1, tvmConfig) + vmtest.SetupTestVM(t, vm2, tvmConfig) + defer func() { - if err := tvm1.vm.Shutdown(context.Background()); err != nil { + if err := vm1.Shutdown(context.Background()); err != nil { t.Fatal(err) } - if err := tvm2.vm.Shutdown(context.Background()); err != nil { + if err := vm2.Shutdown(context.Background()); err != nil { t.Fatal(err) } }() newTxPoolHeadChan1 := make(chan core.NewTxPoolReorgEvent, 1) - tvm1.vm.txPool.SubscribeNewReorgEvent(newTxPoolHeadChan1) + vm1.txPool.SubscribeNewReorgEvent(newTxPoolHeadChan1) newTxPoolHeadChan2 := make(chan core.NewTxPoolReorgEvent, 1) - tvm2.vm.txPool.SubscribeNewReorgEvent(newTxPoolHeadChan2) + vm2.txPool.SubscribeNewReorgEvent(newTxPoolHeadChan2) - key := testKeys[0].ToECDSA() - address := testEthAddrs[0] + key := vmtest.TestKeys[1].ToECDSA() + address := vmtest.TestEthAddrs[1] - importTx, err := tvm1.atomicVM.NewImportTx(tvm1.vm.ctx.XChainID, address, initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) + tx := types.NewTransaction(uint64(0), vmtest.TestEthAddrs[1], big.NewInt(1), 21000, big.NewInt(ap0.MinGasPrice), nil) + signedTx, err := types.SignTx(tx, types.NewEIP155Signer(vm1.chainConfig.ChainID), vmtest.TestKeys[0].ToECDSA()) if err != nil { t.Fatal(err) } - - if err := tvm1.atomicVM.AtomicMempool.AddLocalTx(importTx); err != nil { - t.Fatal(err) + errs := vm1.txPool.AddRemotesSync([]*types.Transaction{signedTx}) + for i, err := range errs { + if err != nil { + t.Fatalf("Failed to add tx at index %d: %s", i, err) + } } - require.Equal(t, commonEng.PendingTxs, tvm1.WaitForEvent(context.Background())) + msg, err := vm1.WaitForEvent(context.Background()) + require.NoError(t, err) + require.Equal(t, commonEng.PendingTxs, msg) - vm1BlkA, err := tvm1.vm.BuildBlock(context.Background()) + vm1BlkA, err := vm1.BuildBlock(context.Background()) if err != nil { - t.Fatalf("Failed to build block with import transaction: %s", err) + t.Fatalf("Failed to build block with transaction: %s", err) } if err := vm1BlkA.Verify(context.Background()); err != nil { t.Fatalf("Block failed verification on VM1: %s", err) } - if err := tvm1.vm.SetPreference(context.Background(), vm1BlkA.ID()); err != nil { + if err := vm1.SetPreference(context.Background(), vm1BlkA.ID()); err != nil { t.Fatal(err) } - vm2BlkA, err := tvm2.vm.ParseBlock(context.Background(), vm1BlkA.Bytes()) + vm2BlkA, err := vm2.ParseBlock(context.Background(), vm1BlkA.Bytes()) if err != nil { t.Fatalf("Unexpected error parsing block from vm2: %s", err) } if err := vm2BlkA.Verify(context.Background()); err != nil { t.Fatalf("Block failed verification on VM2: %s", err) } - if err := tvm2.vm.SetPreference(context.Background(), vm2BlkA.ID()); err != nil { + if err := vm2.SetPreference(context.Background(), vm2BlkA.ID()); err != nil { t.Fatal(err) } @@ -2612,7 +1601,7 @@ func testAcceptReorg(t *testing.T, scheme string) { txs := make([]*types.Transaction, 10) for i := 0; i < 10; i++ { tx := types.NewTransaction(uint64(i), address, big.NewInt(10), 21000, big.NewInt(ap0.MinGasPrice), nil) - signedTx, err := types.SignTx(tx, types.NewEIP155Signer(tvm1.vm.chainID), key) + signedTx, err := types.SignTx(tx, types.NewEIP155Signer(vm1.chainID), key) if err != nil { t.Fatal(err) } @@ -2621,16 +1610,18 @@ func testAcceptReorg(t *testing.T, scheme string) { // Add the remote transactions, build the block, and set VM1's preference // for block B - errs := tvm1.vm.txPool.AddRemotesSync(txs) + errs = vm1.txPool.AddRemotesSync(txs) for i, err := range errs { if err != nil { t.Fatalf("Failed to add transaction to VM1 at index %d: %s", i, err) } } - require.Equal(t, commonEng.PendingTxs, tvm1.WaitForEvent(context.Background())) + msg, err = vm1.WaitForEvent(context.Background()) + require.NoError(t, err) + require.Equal(t, commonEng.PendingTxs, msg) - vm1BlkB, err := tvm1.vm.BuildBlock(context.Background()) + vm1BlkB, err := vm1.BuildBlock(context.Background()) if err != nil { t.Fatal(err) } @@ -2639,20 +1630,22 @@ func testAcceptReorg(t *testing.T, scheme string) { t.Fatal(err) } - if err := tvm1.vm.SetPreference(context.Background(), vm1BlkB.ID()); err != nil { + if err := vm1.SetPreference(context.Background(), vm1BlkB.ID()); err != nil { t.Fatal(err) } - errs = tvm2.vm.txPool.AddRemotesSync(txs[0:5]) + errs = vm2.txPool.AddRemotesSync(txs[0:5]) for i, err := range errs { if err != nil { t.Fatalf("Failed to add transaction to VM2 at index %d: %s", i, err) } } - require.Equal(t, commonEng.PendingTxs, tvm2.WaitForEvent(context.Background())) + msg, err = vm2.WaitForEvent(context.Background()) + require.NoError(t, err) + require.Equal(t, commonEng.PendingTxs, msg) - vm2BlkC, err := tvm2.vm.BuildBlock(context.Background()) + vm2BlkC, err := vm2.BuildBlock(context.Background()) if err != nil { t.Fatalf("Failed to build BlkC on VM2: %s", err) } @@ -2661,7 +1654,7 @@ func testAcceptReorg(t *testing.T, scheme string) { t.Fatalf("BlkC failed verification on VM2: %s", err) } - if err := tvm2.vm.SetPreference(context.Background(), vm2BlkC.ID()); err != nil { + if err := vm2.SetPreference(context.Background(), vm2BlkC.ID()); err != nil { t.Fatal(err) } @@ -2670,27 +1663,29 @@ func testAcceptReorg(t *testing.T, scheme string) { t.Fatalf("Expected new block to match") } - errs = tvm2.vm.txPool.AddRemotesSync(txs[5:]) + errs = vm2.txPool.AddRemotesSync(txs[5:]) for i, err := range errs { if err != nil { t.Fatalf("Failed to add transaction to VM2 at index %d: %s", i, err) } } - require.Equal(t, commonEng.PendingTxs, tvm2.WaitForEvent(context.Background())) + msg, err = vm2.WaitForEvent(context.Background()) + require.NoError(t, err) + require.Equal(t, commonEng.PendingTxs, msg) - vm2BlkD, err := tvm2.vm.BuildBlock(context.Background()) + vm2BlkD, err := vm2.BuildBlock(context.Background()) if err != nil { t.Fatalf("Failed to build BlkD on VM2: %s", err) } // Parse blocks produced in vm2 - vm1BlkC, err := tvm1.vm.ParseBlock(context.Background(), vm2BlkC.Bytes()) + vm1BlkC, err := vm1.ParseBlock(context.Background(), vm2BlkC.Bytes()) if err != nil { t.Fatalf("Unexpected error parsing block from vm2: %s", err) } - vm1BlkD, err := tvm1.vm.ParseBlock(context.Background(), vm2BlkD.Bytes()) + vm1BlkD, err := vm1.ParseBlock(context.Background(), vm2BlkD.Bytes()) if err != nil { t.Fatalf("Unexpected error parsing block from vm2: %s", err) } @@ -2702,8 +1697,8 @@ func testAcceptReorg(t *testing.T, scheme string) { t.Fatalf("Block failed verification on VM1: %s", err) } - blkBHash := vm1BlkB.(*chain.BlockWrapper).Block.(extension.ExtendedBlock).GetEthBlock().Hash() - if b := tvm1.vm.blockChain.CurrentBlock(); b.Hash() != blkBHash { + blkBHash := vm1BlkB.(*chain.BlockWrapper).Block.(*wrappedBlock).ethBlock.Hash() + if b := vm1.blockChain.CurrentBlock(); b.Hash() != blkBHash { t.Fatalf("expected current block to have hash %s but got %s", blkBHash.Hex(), b.Hash().Hex()) } @@ -2711,8 +1706,8 @@ func testAcceptReorg(t *testing.T, scheme string) { t.Fatal(err) } - blkCHash := vm1BlkC.(*chain.BlockWrapper).Block.(extension.ExtendedBlock).GetEthBlock().Hash() - if b := tvm1.vm.blockChain.CurrentBlock(); b.Hash() != blkCHash { + blkCHash := vm1BlkC.(*chain.BlockWrapper).Block.(*wrappedBlock).ethBlock.Hash() + if b := vm1.blockChain.CurrentBlock(); b.Hash() != blkCHash { t.Fatalf("expected current block to have hash %s but got %s", blkCHash.Hex(), b.Hash().Hex()) } if err := vm1BlkB.Reject(context.Background()); err != nil { @@ -2722,14 +1717,14 @@ func testAcceptReorg(t *testing.T, scheme string) { if err := vm1BlkD.Accept(context.Background()); err != nil { t.Fatal(err) } - blkDHash := vm1BlkD.(*chain.BlockWrapper).Block.(extension.ExtendedBlock).GetEthBlock().Hash() - if b := tvm1.vm.blockChain.CurrentBlock(); b.Hash() != blkDHash { + blkDHash := vm1BlkD.(*chain.BlockWrapper).Block.(*wrappedBlock).ethBlock.Hash() + if b := vm1.blockChain.CurrentBlock(); b.Hash() != blkDHash { t.Fatalf("expected current block to have hash %s but got %s", blkDHash.Hex(), b.Hash().Hex()) } } func TestFutureBlock(t *testing.T) { - for _, scheme := range schemes { + for _, scheme := range vmtest.Schemes { t.Run(scheme, func(t *testing.T) { testFutureBlock(t, scheme) }) @@ -2737,764 +1732,112 @@ func TestFutureBlock(t *testing.T) { } func testFutureBlock(t *testing.T, scheme string) { - importAmount := uint64(1000000000) fork := upgradetest.NoUpgrades - tvm := newVM(t, testVMConfig{ - fork: &fork, - utxos: map[ids.ShortID]uint64{ - testShortIDAddrs[0]: importAmount, - }, - configJSON: getConfig(scheme, ""), + vm := newDefaultTestVM() + vmtest.SetupTestVM(t, vm, vmtest.TestVMConfig{ + Fork: &fork, + Scheme: scheme, }) + defer func() { - if err := tvm.vm.Shutdown(context.Background()); err != nil { + if err := vm.Shutdown(context.Background()); err != nil { t.Fatal(err) } }() - importTx, err := tvm.atomicVM.NewImportTx(tvm.vm.ctx.XChainID, testEthAddrs[0], initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) + tx := types.NewTransaction(uint64(0), vmtest.TestEthAddrs[1], big.NewInt(1), 21000, big.NewInt(ap0.MinGasPrice), nil) + signedTx, err := types.SignTx(tx, types.NewEIP155Signer(vm.chainConfig.ChainID), vmtest.TestKeys[0].ToECDSA()) if err != nil { t.Fatal(err) } - - if err := tvm.atomicVM.AtomicMempool.AddLocalTx(importTx); err != nil { - t.Fatal(err) + errs := vm.txPool.AddRemotesSync([]*types.Transaction{signedTx}) + for i, err := range errs { + if err != nil { + t.Fatalf("Failed to add tx at index %d: %s", i, err) + } } + msg, err := vm.WaitForEvent(context.Background()) + require.NoError(t, err) + require.Equal(t, commonEng.PendingTxs, msg) - require.Equal(t, commonEng.PendingTxs, tvm.WaitForEvent(context.Background())) - - blkA, err := tvm.vm.BuildBlock(context.Background()) + blkA, err := vm.BuildBlock(context.Background()) if err != nil { - t.Fatalf("Failed to build block with import transaction: %s", err) + t.Fatalf("Failed to build block with transaction: %s", err) } // Create empty block from blkA - internalBlkA := blkA.(*chain.BlockWrapper).Block.(extension.ExtendedBlock) - modifiedHeader := types.CopyHeader(internalBlkA.GetEthBlock().Header()) + internalBlkA := blkA.(*chain.BlockWrapper).Block.(*wrappedBlock) + modifiedHeader := types.CopyHeader(internalBlkA.ethBlock.Header()) // Set the VM's clock to the time of the produced block - tvm.vm.clock.Set(time.Unix(int64(modifiedHeader.Time), 0)) + vm.clock.Set(time.Unix(int64(modifiedHeader.Time), 0)) // Set the modified time to exceed the allowed future time modifiedTime := modifiedHeader.Time + uint64(maxFutureBlockTime.Seconds()+1) modifiedHeader.Time = modifiedTime modifiedBlock := customtypes.NewBlockWithExtData( modifiedHeader, nil, - nil, - nil, - new(trie.Trie), - customtypes.BlockExtData(internalBlkA.GetEthBlock()), - false, - ) - - futureBlock, err := wrapBlock(modifiedBlock, tvm.vm) - if err != nil { - t.Fatal(err) - } - - if err := futureBlock.Verify(context.Background()); err == nil { - t.Fatal("Future block should have failed verification due to block timestamp too far in the future") - } else if !strings.Contains(err.Error(), "block timestamp is too far in the future") { - t.Fatalf("Expected error to be block timestamp too far in the future but found %s", err) - } -} - -// Regression test to ensure we can build blocks if we are starting with the -// Apricot Phase 1 ruleset in genesis. -func TestBuildApricotPhase1Block(t *testing.T) { - for _, scheme := range schemes { - t.Run(scheme, func(t *testing.T) { - testBuildApricotPhase1Block(t, scheme) - }) - } -} - -func testBuildApricotPhase1Block(t *testing.T, scheme string) { - importAmount := uint64(1000000000) - fork := upgradetest.ApricotPhase1 - tvm := newVM(t, testVMConfig{ - fork: &fork, - utxos: map[ids.ShortID]uint64{ - testShortIDAddrs[0]: importAmount, - }, - configJSON: getConfig(scheme, ""), - }) - defer func() { - if err := tvm.vm.Shutdown(context.Background()); err != nil { - t.Fatal(err) - } - }() - - newTxPoolHeadChan := make(chan core.NewTxPoolReorgEvent, 1) - tvm.vm.txPool.SubscribeNewReorgEvent(newTxPoolHeadChan) - - key := testKeys[0].ToECDSA() - address := testEthAddrs[0] - - importTx, err := tvm.atomicVM.NewImportTx(tvm.vm.ctx.XChainID, address, initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) - if err != nil { - t.Fatal(err) - } - - if err := tvm.atomicVM.AtomicMempool.AddLocalTx(importTx); err != nil { - t.Fatal(err) - } - - require.Equal(t, commonEng.PendingTxs, tvm.WaitForEvent(context.Background())) - - blk, err := tvm.vm.BuildBlock(context.Background()) - if err != nil { - t.Fatal(err) - } - - if err := blk.Verify(context.Background()); err != nil { - t.Fatal(err) - } - - if err := tvm.vm.SetPreference(context.Background(), blk.ID()); err != nil { - t.Fatal(err) - } - - if err := blk.Accept(context.Background()); err != nil { - t.Fatal(err) - } - - newHead := <-newTxPoolHeadChan - if newHead.Head.Hash() != common.Hash(blk.ID()) { - t.Fatalf("Expected new block to match") - } - - txs := make([]*types.Transaction, 10) - for i := 0; i < 5; i++ { - tx := types.NewTransaction(uint64(i), address, big.NewInt(10), 21000, big.NewInt(ap0.MinGasPrice), nil) - signedTx, err := types.SignTx(tx, types.NewEIP155Signer(tvm.vm.chainID), key) - if err != nil { - t.Fatal(err) - } - txs[i] = signedTx - } - for i := 5; i < 10; i++ { - tx := types.NewTransaction(uint64(i), address, big.NewInt(10), 21000, big.NewInt(ap1.MinGasPrice), nil) - signedTx, err := types.SignTx(tx, types.NewEIP155Signer(tvm.vm.chainID), key) - if err != nil { - t.Fatal(err) - } - txs[i] = signedTx - } - errs := tvm.vm.txPool.AddRemotesSync(txs) - for i, err := range errs { - if err != nil { - t.Fatalf("Failed to add tx at index %d: %s", i, err) - } - } - - require.Equal(t, commonEng.PendingTxs, tvm.WaitForEvent(context.Background())) - - blk, err = tvm.vm.BuildBlock(context.Background()) - if err != nil { - t.Fatal(err) - } - - if err := blk.Verify(context.Background()); err != nil { - t.Fatal(err) - } - - if err := blk.Accept(context.Background()); err != nil { - t.Fatal(err) - } - - lastAcceptedID, err := tvm.vm.LastAccepted(context.Background()) - if err != nil { - t.Fatal(err) - } - if lastAcceptedID != blk.ID() { - t.Fatalf("Expected last accepted blockID to be the accepted block: %s, but found %s", blk.ID(), lastAcceptedID) - } - - // Confirm all txs are present - ethBlkTxs := tvm.vm.blockChain.GetBlockByNumber(2).Transactions() - for i, tx := range txs { - if len(ethBlkTxs) <= i { - t.Fatalf("missing transactions expected: %d but found: %d", len(txs), len(ethBlkTxs)) - } - if ethBlkTxs[i].Hash() != tx.Hash() { - t.Fatalf("expected tx at index %d to have hash: %x but has: %x", i, txs[i].Hash(), tx.Hash()) - } - } -} - -func TestLastAcceptedBlockNumberAllow(t *testing.T) { - for _, scheme := range schemes { - t.Run(scheme, func(t *testing.T) { - testLastAcceptedBlockNumberAllow(t, scheme) - }) - } -} - -func testLastAcceptedBlockNumberAllow(t *testing.T, scheme string) { - importAmount := uint64(1000000000) - fork := upgradetest.NoUpgrades - tvm := newVM(t, testVMConfig{ - fork: &fork, - utxos: map[ids.ShortID]uint64{ - testShortIDAddrs[0]: importAmount, - }, - configJSON: getConfig(scheme, ""), - }) - defer func() { - if err := tvm.vm.Shutdown(context.Background()); err != nil { - t.Fatal(err) - } - }() - - importTx, err := tvm.atomicVM.NewImportTx(tvm.vm.ctx.XChainID, testEthAddrs[0], initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) - if err != nil { - t.Fatal(err) - } - - if err := tvm.atomicVM.AtomicMempool.AddLocalTx(importTx); err != nil { - t.Fatal(err) - } - - require.Equal(t, commonEng.PendingTxs, tvm.WaitForEvent(context.Background())) - - blk, err := tvm.vm.BuildBlock(context.Background()) - if err != nil { - t.Fatalf("Failed to build block with import transaction: %s", err) - } - - if err := blk.Verify(context.Background()); err != nil { - t.Fatalf("Block failed verification on VM: %s", err) - } - - if err := tvm.vm.SetPreference(context.Background(), blk.ID()); err != nil { - t.Fatal(err) - } - - blkHeight := blk.Height() - blkHash := blk.(*chain.BlockWrapper).Block.(extension.ExtendedBlock).GetEthBlock().Hash() - - tvm.vm.eth.APIBackend.SetAllowUnfinalizedQueries(true) - - ctx := context.Background() - b, err := tvm.vm.eth.APIBackend.BlockByNumber(ctx, rpc.BlockNumber(blkHeight)) - if err != nil { - t.Fatal(err) - } - if b.Hash() != blkHash { - t.Fatalf("expected block at %d to have hash %s but got %s", blkHeight, blkHash.Hex(), b.Hash().Hex()) - } - - tvm.vm.eth.APIBackend.SetAllowUnfinalizedQueries(false) - - _, err = tvm.vm.eth.APIBackend.BlockByNumber(ctx, rpc.BlockNumber(blkHeight)) - if !errors.Is(err, eth.ErrUnfinalizedData) { - t.Fatalf("expected ErrUnfinalizedData but got %s", err.Error()) - } - - if err := blk.Accept(context.Background()); err != nil { - t.Fatalf("VM failed to accept block: %s", err) - } - - if b := tvm.vm.blockChain.GetBlockByNumber(blkHeight); b.Hash() != blkHash { - t.Fatalf("expected block at %d to have hash %s but got %s", blkHeight, blkHash.Hex(), b.Hash().Hex()) - } -} - -// Builds [blkA] with a virtuous import transaction and [blkB] with a separate import transaction -// that does not conflict. Accepts [blkB] and rejects [blkA], then asserts that the virtuous atomic -// transaction in [blkA] is correctly re-issued into the atomic transaction mempool. -func TestReissueAtomicTx(t *testing.T) { - for _, scheme := range schemes { - t.Run(scheme, func(t *testing.T) { - testReissueAtomicTx(t, scheme) - }) - } -} - -func testReissueAtomicTx(t *testing.T, scheme string) { - fork := upgradetest.ApricotPhase1 - tvm := newVM(t, testVMConfig{ - fork: &fork, - utxos: map[ids.ShortID]uint64{ - testShortIDAddrs[0]: 10000000, - testShortIDAddrs[1]: 10000000, - }, - configJSON: getConfig(scheme, ""), - }) - defer func() { - if err := tvm.vm.Shutdown(context.Background()); err != nil { - t.Fatal(err) - } - }() - - genesisBlkID, err := tvm.vm.LastAccepted(context.Background()) - if err != nil { - t.Fatal(err) - } - - importTx, err := tvm.atomicVM.NewImportTx(tvm.vm.ctx.XChainID, testEthAddrs[0], initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) - if err != nil { - t.Fatal(err) - } - - if err := tvm.atomicVM.AtomicMempool.AddLocalTx(importTx); err != nil { - t.Fatal(err) - } - - require.Equal(t, commonEng.PendingTxs, tvm.WaitForEvent(context.Background())) - - blkA, err := tvm.vm.BuildBlock(context.Background()) - if err != nil { - t.Fatal(err) - } - - if err := blkA.Verify(context.Background()); err != nil { - t.Fatal(err) - } - - if err := tvm.vm.SetPreference(context.Background(), blkA.ID()); err != nil { - t.Fatal(err) - } - - // SetPreference to parent before rejecting (will rollback state to genesis - // so that atomic transaction can be reissued, otherwise current block will - // conflict with UTXO to be reissued) - if err := tvm.vm.SetPreference(context.Background(), genesisBlkID); err != nil { - t.Fatal(err) - } - - // Rejecting [blkA] should cause [importTx] to be re-issued into the mempool. - if err := blkA.Reject(context.Background()); err != nil { - t.Fatal(err) - } - - // Sleep for a minimum of two seconds to ensure that [blkB] will have a different timestamp - // than [blkA] so that the block will be unique. This is necessary since we have marked [blkA] - // as Rejected. - time.Sleep(2 * time.Second) - require.Equal(t, commonEng.PendingTxs, tvm.WaitForEvent(context.Background())) - blkB, err := tvm.vm.BuildBlock(context.Background()) - if err != nil { - t.Fatal(err) - } - - if blkB.Height() != blkA.Height() { - t.Fatalf("Expected blkB (%d) to have the same height as blkA (%d)", blkB.Height(), blkA.Height()) - } - - if err := blkB.Verify(context.Background()); err != nil { - t.Fatal(err) - } - - if err := tvm.vm.SetPreference(context.Background(), blkB.ID()); err != nil { - t.Fatal(err) - } - - if err := blkB.Accept(context.Background()); err != nil { - t.Fatal(err) - } - - if lastAcceptedID, err := tvm.vm.LastAccepted(context.Background()); err != nil { - t.Fatal(err) - } else if lastAcceptedID != blkB.ID() { - t.Fatalf("Expected last accepted blockID to be the accepted block: %s, but found %s", blkB.ID(), lastAcceptedID) - } - - // Check that [importTx] has been indexed correctly after [blkB] is accepted. - _, height, err := tvm.atomicVM.AtomicTxRepository.GetByTxID(importTx.ID()) - if err != nil { - t.Fatal(err) - } else if height != blkB.Height() { - t.Fatalf("Expected indexed height of import tx to be %d, but found %d", blkB.Height(), height) - } -} - -func TestAtomicTxFailsEVMStateTransferBuildBlock(t *testing.T) { - for _, scheme := range schemes { - t.Run(scheme, func(t *testing.T) { - testAtomicTxFailsEVMStateTransferBuildBlock(t, scheme) - }) - } -} - -func testAtomicTxFailsEVMStateTransferBuildBlock(t *testing.T, scheme string) { - fork := upgradetest.ApricotPhase1 - tvm := newVM(t, testVMConfig{ - fork: &fork, - configJSON: getConfig(scheme, ""), - }) - defer func() { - if err := tvm.vm.Shutdown(context.Background()); err != nil { - t.Fatal(err) - } - }() - - exportTxs := createExportTxOptions(t, tvm.atomicVM, tvm.atomicMemory) - exportTx1, exportTx2 := exportTxs[0], exportTxs[1] - - if err := tvm.atomicVM.AtomicMempool.AddLocalTx(exportTx1); err != nil { - t.Fatal(err) - } - require.Equal(t, commonEng.PendingTxs, tvm.WaitForEvent(context.Background())) - exportBlk1, err := tvm.vm.BuildBlock(context.Background()) - if err != nil { - t.Fatal(err) - } - if err := exportBlk1.Verify(context.Background()); err != nil { - t.Fatal(err) - } - - if err := tvm.vm.SetPreference(context.Background(), exportBlk1.ID()); err != nil { - t.Fatal(err) - } - - if err := tvm.atomicVM.AtomicMempool.AddLocalTx(exportTx2); err == nil { - t.Fatal("Should have failed to issue due to an invalid export tx") - } - - if err := tvm.atomicVM.AtomicMempool.AddRemoteTx(exportTx2); err == nil { - t.Fatal("Should have failed to add because conflicting") - } - - // Manually add transaction to mempool to bypass validation - if err := tvm.atomicVM.AtomicMempool.ForceAddTx(exportTx2); err != nil { - t.Fatal(err) - } - require.Equal(t, commonEng.PendingTxs, tvm.WaitForEvent(context.Background())) - - _, err = tvm.vm.BuildBlock(context.Background()) - if err == nil { - t.Fatal("BuildBlock should have returned an error due to invalid export transaction") - } -} - -func TestBuildInvalidBlockHead(t *testing.T) { - for _, scheme := range schemes { - t.Run(scheme, func(t *testing.T) { - testBuildInvalidBlockHead(t, scheme) - }) - } -} - -func testBuildInvalidBlockHead(t *testing.T, scheme string) { - fork := upgradetest.ApricotPhase1 - tvm := newVM(t, testVMConfig{ - fork: &fork, - configJSON: getConfig(scheme, ""), - }) - defer func() { - if err := tvm.vm.Shutdown(context.Background()); err != nil { - t.Fatal(err) - } - }() - - key0 := testKeys[0] - addr0 := key0.Address() - - // Create the transaction - utx := &atomic.UnsignedImportTx{ - NetworkID: tvm.vm.ctx.NetworkID, - BlockchainID: tvm.vm.ctx.ChainID, - Outs: []atomic.EVMOutput{{ - Address: common.Address(addr0), - Amount: 1 * units.Avax, - AssetID: tvm.vm.ctx.AVAXAssetID, - }}, - ImportedInputs: []*avax.TransferableInput{ - { - Asset: avax.Asset{ID: tvm.vm.ctx.AVAXAssetID}, - In: &secp256k1fx.TransferInput{ - Amt: 1 * units.Avax, - Input: secp256k1fx.Input{ - SigIndices: []uint32{0}, - }, - }, - }, - }, - SourceChain: tvm.vm.ctx.XChainID, - } - tx := &atomic.Tx{UnsignedAtomicTx: utx} - if err := tx.Sign(atomic.Codec, [][]*secp256k1.PrivateKey{{key0}}); err != nil { - t.Fatal(err) - } - - currentBlock := tvm.vm.blockChain.CurrentBlock() - - // Verify that the transaction fails verification when attempting to issue - // it into the atomic mempool. - if err := tvm.atomicVM.AtomicMempool.AddLocalTx(tx); err == nil { - t.Fatal("Should have failed to issue invalid transaction") - } - // Force issue the transaction directly to the mempool - if err := tvm.atomicVM.AtomicMempool.ForceAddTx(tx); err != nil { - t.Fatal(err) - } - - require.Equal(t, commonEng.PendingTxs, tvm.WaitForEvent(context.Background())) - - if _, err := tvm.vm.BuildBlock(context.Background()); err == nil { - t.Fatalf("Unexpectedly created a block") - } - - newCurrentBlock := tvm.vm.blockChain.CurrentBlock() - - if currentBlock.Hash() != newCurrentBlock.Hash() { - t.Fatal("current block changed") - } -} - -// Regression test to ensure we can build blocks if we are starting with the -// Apricot Phase 4 ruleset in genesis. -func TestBuildApricotPhase4Block(t *testing.T) { - for _, scheme := range schemes { - t.Run(scheme, func(t *testing.T) { - testBuildApricotPhase4Block(t, scheme) - }) - } -} - -func testBuildApricotPhase4Block(t *testing.T, scheme string) { - fork := upgradetest.ApricotPhase4 - tvm := newVM(t, testVMConfig{ - fork: &fork, - configJSON: getConfig(scheme, ""), - }) - defer func() { - if err := tvm.vm.Shutdown(context.Background()); err != nil { - t.Fatal(err) - } - }() - - newTxPoolHeadChan := make(chan core.NewTxPoolReorgEvent, 1) - tvm.vm.txPool.SubscribeNewReorgEvent(newTxPoolHeadChan) - - key := testKeys[0].ToECDSA() - address := testEthAddrs[0] - - importAmount := uint64(1000000000) - utxoID := avax.UTXOID{TxID: ids.GenerateTestID()} - - utxo := &avax.UTXO{ - UTXOID: utxoID, - Asset: avax.Asset{ID: tvm.vm.ctx.AVAXAssetID}, - Out: &secp256k1fx.TransferOutput{ - Amt: importAmount, - OutputOwners: secp256k1fx.OutputOwners{ - Threshold: 1, - Addrs: []ids.ShortID{testKeys[0].Address()}, - }, - }, - } - utxoBytes, err := atomic.Codec.Marshal(atomic.CodecVersion, utxo) - if err != nil { - t.Fatal(err) - } - - xChainSharedMemory := tvm.atomicMemory.NewSharedMemory(tvm.vm.ctx.XChainID) - inputID := utxo.InputID() - if err := xChainSharedMemory.Apply(map[ids.ID]*avalancheatomic.Requests{tvm.vm.ctx.ChainID: {PutRequests: []*avalancheatomic.Element{{ - Key: inputID[:], - Value: utxoBytes, - Traits: [][]byte{ - testKeys[0].Address().Bytes(), - }, - }}}}); err != nil { - t.Fatal(err) - } - - importTx, err := tvm.atomicVM.NewImportTx(tvm.vm.ctx.XChainID, address, initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) - if err != nil { - t.Fatal(err) - } - - if err := tvm.atomicVM.AtomicMempool.AddLocalTx(importTx); err != nil { - t.Fatal(err) - } - - require.Equal(t, commonEng.PendingTxs, tvm.WaitForEvent(context.Background())) - - blk, err := tvm.vm.BuildBlock(context.Background()) - if err != nil { - t.Fatal(err) - } - - if err := blk.Verify(context.Background()); err != nil { - t.Fatal(err) - } - - if err := tvm.vm.SetPreference(context.Background(), blk.ID()); err != nil { - t.Fatal(err) - } - - if err := blk.Accept(context.Background()); err != nil { - t.Fatal(err) - } - - ethBlk := blk.(*chain.BlockWrapper).Block.(extension.ExtendedBlock).GetEthBlock() - if eBlockGasCost := customtypes.BlockGasCost(ethBlk); eBlockGasCost == nil || eBlockGasCost.Cmp(common.Big0) != 0 { - t.Fatalf("expected blockGasCost to be 0 but got %d", eBlockGasCost) - } - if eExtDataGasUsed := customtypes.BlockExtDataGasUsed(ethBlk); eExtDataGasUsed == nil || eExtDataGasUsed.Cmp(big.NewInt(1230)) != 0 { - t.Fatalf("expected extDataGasUsed to be 1000 but got %d", eExtDataGasUsed) - } - minRequiredTip, err := header.EstimateRequiredTip(tvm.vm.chainConfigExtra(), ethBlk.Header()) - if err != nil { - t.Fatal(err) - } - if minRequiredTip == nil || minRequiredTip.Cmp(common.Big0) != 0 { - t.Fatalf("expected minRequiredTip to be 0 but got %d", minRequiredTip) - } - - newHead := <-newTxPoolHeadChan - if newHead.Head.Hash() != common.Hash(blk.ID()) { - t.Fatalf("Expected new block to match") - } - - txs := make([]*types.Transaction, 10) - for i := 0; i < 5; i++ { - tx := types.NewTransaction(uint64(i), address, big.NewInt(10), 21000, big.NewInt(ap0.MinGasPrice), nil) - signedTx, err := types.SignTx(tx, types.NewEIP155Signer(tvm.vm.chainID), key) - if err != nil { - t.Fatal(err) - } - txs[i] = signedTx - } - for i := 5; i < 10; i++ { - tx := types.NewTransaction(uint64(i), address, big.NewInt(10), 21000, big.NewInt(ap1.MinGasPrice), nil) - signedTx, err := types.SignTx(tx, types.NewEIP155Signer(tvm.vm.chainID), key) - if err != nil { - t.Fatal(err) - } - txs[i] = signedTx - } - errs := tvm.vm.txPool.AddRemotesSync(txs) - for i, err := range errs { - if err != nil { - t.Fatalf("Failed to add tx at index %d: %s", i, err) - } - } - - require.Equal(t, commonEng.PendingTxs, tvm.WaitForEvent(context.Background())) - - blk, err = tvm.vm.BuildBlock(context.Background()) - if err != nil { - t.Fatal(err) - } - - if err := blk.Verify(context.Background()); err != nil { - t.Fatal(err) - } - - if err := blk.Accept(context.Background()); err != nil { - t.Fatal(err) - } - - ethBlk = blk.(*chain.BlockWrapper).Block.(extension.ExtendedBlock).GetEthBlock() - if customtypes.BlockGasCost(ethBlk) == nil || customtypes.BlockGasCost(ethBlk).Cmp(big.NewInt(100)) < 0 { - t.Fatalf("expected blockGasCost to be at least 100 but got %d", customtypes.BlockGasCost(ethBlk)) - } - if customtypes.BlockExtDataGasUsed(ethBlk) == nil || customtypes.BlockExtDataGasUsed(ethBlk).Cmp(common.Big0) != 0 { - t.Fatalf("expected extDataGasUsed to be 0 but got %d", customtypes.BlockExtDataGasUsed(ethBlk)) - } - minRequiredTip, err = header.EstimateRequiredTip(tvm.vm.chainConfigExtra(), ethBlk.Header()) - if err != nil { - t.Fatal(err) - } - if minRequiredTip == nil || minRequiredTip.Cmp(big.NewInt(0.05*utils.GWei)) < 0 { - t.Fatalf("expected minRequiredTip to be at least 0.05 gwei but got %d", minRequiredTip) - } - - lastAcceptedID, err := tvm.vm.LastAccepted(context.Background()) - if err != nil { - t.Fatal(err) - } - if lastAcceptedID != blk.ID() { - t.Fatalf("Expected last accepted blockID to be the accepted block: %s, but found %s", blk.ID(), lastAcceptedID) - } - - // Confirm all txs are present - ethBlkTxs := tvm.vm.blockChain.GetBlockByNumber(2).Transactions() - for i, tx := range txs { - if len(ethBlkTxs) <= i { - t.Fatalf("missing transactions expected: %d but found: %d", len(txs), len(ethBlkTxs)) - } - if ethBlkTxs[i].Hash() != tx.Hash() { - t.Fatalf("expected tx at index %d to have hash: %x but has: %x", i, txs[i].Hash(), tx.Hash()) - } + nil, + nil, + new(trie.Trie), + customtypes.BlockExtData(internalBlkA.ethBlock), + false, + ) + + futureBlock, err := wrapBlock(modifiedBlock, vm) + require.NoError(t, err) + if err := futureBlock.Verify(context.Background()); err == nil { + t.Fatal("Future block should have failed verification due to block timestamp too far in the future") + } else if !strings.Contains(err.Error(), "block timestamp is too far in the future") { + t.Fatalf("Expected error to be block timestamp too far in the future but found %s", err) } } // Regression test to ensure we can build blocks if we are starting with the -// Apricot Phase 5 ruleset in genesis. -func TestBuildApricotPhase5Block(t *testing.T) { - for _, scheme := range schemes { +// Apricot Phase 1 ruleset in genesis. +func TestBuildApricotPhase1Block(t *testing.T) { + for _, scheme := range vmtest.Schemes { t.Run(scheme, func(t *testing.T) { - testBuildApricotPhase5Block(t, scheme) + testBuildApricotPhase1Block(t, scheme) }) } } -func testBuildApricotPhase5Block(t *testing.T, scheme string) { - fork := upgradetest.ApricotPhase5 - tvm := newVM(t, testVMConfig{ - fork: &fork, - configJSON: getConfig(scheme, ""), +func testBuildApricotPhase1Block(t *testing.T, scheme string) { + fork := upgradetest.ApricotPhase1 + vm := newDefaultTestVM() + vmtest.SetupTestVM(t, vm, vmtest.TestVMConfig{ + Fork: &fork, + Scheme: scheme, }) defer func() { - if err := tvm.vm.Shutdown(context.Background()); err != nil { + if err := vm.Shutdown(context.Background()); err != nil { t.Fatal(err) } }() newTxPoolHeadChan := make(chan core.NewTxPoolReorgEvent, 1) - tvm.vm.txPool.SubscribeNewReorgEvent(newTxPoolHeadChan) - - key := testKeys[0].ToECDSA() - address := testEthAddrs[0] - - importAmount := uint64(1000000000) - utxoID := avax.UTXOID{TxID: ids.GenerateTestID()} - - utxo := &avax.UTXO{ - UTXOID: utxoID, - Asset: avax.Asset{ID: tvm.vm.ctx.AVAXAssetID}, - Out: &secp256k1fx.TransferOutput{ - Amt: importAmount, - OutputOwners: secp256k1fx.OutputOwners{ - Threshold: 1, - Addrs: []ids.ShortID{testKeys[0].Address()}, - }, - }, - } - utxoBytes, err := atomic.Codec.Marshal(atomic.CodecVersion, utxo) - if err != nil { - t.Fatal(err) - } + vm.txPool.SubscribeNewReorgEvent(newTxPoolHeadChan) - xChainSharedMemory := tvm.atomicMemory.NewSharedMemory(tvm.vm.ctx.XChainID) - inputID := utxo.InputID() - if err := xChainSharedMemory.Apply(map[ids.ID]*avalancheatomic.Requests{tvm.vm.ctx.ChainID: {PutRequests: []*avalancheatomic.Element{{ - Key: inputID[:], - Value: utxoBytes, - Traits: [][]byte{ - testKeys[0].Address().Bytes(), - }, - }}}}); err != nil { - t.Fatal(err) - } + key := vmtest.TestKeys[1].ToECDSA() + address := vmtest.TestEthAddrs[1] - importTx, err := tvm.atomicVM.NewImportTx(tvm.vm.ctx.XChainID, address, initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) + tx := types.NewTransaction(uint64(0), vmtest.TestEthAddrs[1], big.NewInt(1), 21000, vmtest.InitialBaseFee, nil) + signedTx, err := types.SignTx(tx, types.NewEIP155Signer(vm.chainConfig.ChainID), vmtest.TestKeys[0].ToECDSA()) if err != nil { t.Fatal(err) } - - if err := tvm.atomicVM.AtomicMempool.AddLocalTx(importTx); err != nil { - t.Fatal(err) + errs := vm.txPool.AddRemotesSync([]*types.Transaction{signedTx}) + for i, err := range errs { + if err != nil { + t.Fatalf("Failed to add tx at index %d: %s", i, err) + } } - require.Equal(t, commonEng.PendingTxs, tvm.WaitForEvent(context.Background())) + msg, err := vm.WaitForEvent(context.Background()) + require.NoError(t, err) + require.Equal(t, commonEng.PendingTxs, msg) - blk, err := tvm.vm.BuildBlock(context.Background()) + blk, err := vm.BuildBlock(context.Background()) if err != nil { t.Fatal(err) } @@ -3503,7 +1846,7 @@ func testBuildApricotPhase5Block(t *testing.T, scheme string) { t.Fatal(err) } - if err := tvm.vm.SetPreference(context.Background(), blk.ID()); err != nil { + if err := vm.SetPreference(context.Background(), blk.ID()); err != nil { t.Fatal(err) } @@ -3511,45 +1854,40 @@ func testBuildApricotPhase5Block(t *testing.T, scheme string) { t.Fatal(err) } - ethBlk := blk.(*chain.BlockWrapper).Block.(extension.ExtendedBlock).GetEthBlock() - if eBlockGasCost := customtypes.BlockGasCost(ethBlk); eBlockGasCost == nil || eBlockGasCost.Cmp(common.Big0) != 0 { - t.Fatalf("expected blockGasCost to be 0 but got %d", eBlockGasCost) - } - if eExtDataGasUsed := customtypes.BlockExtDataGasUsed(ethBlk); eExtDataGasUsed == nil || eExtDataGasUsed.Cmp(big.NewInt(11230)) != 0 { - t.Fatalf("expected extDataGasUsed to be 11230 but got %d", eExtDataGasUsed) - } - minRequiredTip, err := header.EstimateRequiredTip(tvm.vm.chainConfigExtra(), ethBlk.Header()) - if err != nil { - t.Fatal(err) - } - if minRequiredTip == nil || minRequiredTip.Cmp(common.Big0) != 0 { - t.Fatalf("expected minRequiredTip to be 0 but got %d", minRequiredTip) - } - newHead := <-newTxPoolHeadChan if newHead.Head.Hash() != common.Hash(blk.ID()) { t.Fatalf("Expected new block to match") } txs := make([]*types.Transaction, 10) - for i := 0; i < 10; i++ { - tx := types.NewTransaction(uint64(i), address, big.NewInt(10), 21000, big.NewInt(3*ap0.MinGasPrice), nil) - signedTx, err := types.SignTx(tx, types.NewEIP155Signer(tvm.vm.chainID), key) + for i := 0; i < 5; i++ { + tx := types.NewTransaction(uint64(i), address, big.NewInt(10), 21000, big.NewInt(ap0.MinGasPrice), nil) + signedTx, err := types.SignTx(tx, types.NewEIP155Signer(vm.chainID), key) + if err != nil { + t.Fatal(err) + } + txs[i] = signedTx + } + for i := 5; i < 10; i++ { + tx := types.NewTransaction(uint64(i), address, big.NewInt(10), 21000, big.NewInt(ap1.MinGasPrice), nil) + signedTx, err := types.SignTx(tx, types.NewEIP155Signer(vm.chainID), key) if err != nil { t.Fatal(err) } txs[i] = signedTx } - errs := tvm.vm.txPool.Add(txs, false, false) + errs = vm.txPool.AddRemotesSync(txs) for i, err := range errs { if err != nil { t.Fatalf("Failed to add tx at index %d: %s", i, err) } } - require.Equal(t, commonEng.PendingTxs, tvm.WaitForEvent(context.Background())) + msg, err = vm.WaitForEvent(context.Background()) + require.NoError(t, err) + require.Equal(t, commonEng.PendingTxs, msg) - blk, err = tvm.vm.BuildBlock(context.Background()) + blk, err = vm.BuildBlock(context.Background()) if err != nil { t.Fatal(err) } @@ -3562,22 +1900,7 @@ func testBuildApricotPhase5Block(t *testing.T, scheme string) { t.Fatal(err) } - ethBlk = blk.(*chain.BlockWrapper).Block.(extension.ExtendedBlock).GetEthBlock() - if customtypes.BlockGasCost(ethBlk) == nil || customtypes.BlockGasCost(ethBlk).Cmp(big.NewInt(100)) < 0 { - t.Fatalf("expected blockGasCost to be at least 100 but got %d", customtypes.BlockGasCost(ethBlk)) - } - if customtypes.BlockExtDataGasUsed(ethBlk) == nil || customtypes.BlockExtDataGasUsed(ethBlk).Cmp(common.Big0) != 0 { - t.Fatalf("expected extDataGasUsed to be 0 but got %d", customtypes.BlockExtDataGasUsed(ethBlk)) - } - minRequiredTip, err = header.EstimateRequiredTip(tvm.vm.chainConfigExtra(), ethBlk.Header()) - if err != nil { - t.Fatal(err) - } - if minRequiredTip == nil || minRequiredTip.Cmp(big.NewInt(0.05*utils.GWei)) < 0 { - t.Fatalf("expected minRequiredTip to be at least 0.05 gwei but got %d", minRequiredTip) - } - - lastAcceptedID, err := tvm.vm.LastAccepted(context.Background()) + lastAcceptedID, err := vm.LastAccepted(context.Background()) if err != nil { t.Fatal(err) } @@ -3586,7 +1909,7 @@ func testBuildApricotPhase5Block(t *testing.T, scheme string) { } // Confirm all txs are present - ethBlkTxs := tvm.vm.blockChain.GetBlockByNumber(2).Transactions() + ethBlkTxs := vm.blockChain.GetBlockByNumber(2).Transactions() for i, tx := range txs { if len(ethBlkTxs) <= i { t.Fatalf("missing transactions expected: %d but found: %d", len(txs), len(ethBlkTxs)) @@ -3597,317 +1920,135 @@ func testBuildApricotPhase5Block(t *testing.T, scheme string) { } } -// This is a regression test to ensure that if two consecutive atomic transactions fail verification -// in onFinalizeAndAssemble it will not cause a panic due to calling RevertToSnapshot(revID) on the -// same revision ID twice. -func TestConsecutiveAtomicTransactionsRevertSnapshot(t *testing.T) { - fork := upgradetest.ApricotPhase1 - tvm := newVM(t, testVMConfig{ - fork: &fork, - }) - defer func() { - if err := tvm.vm.Shutdown(context.Background()); err != nil { - t.Fatal(err) - } - }() - - newTxPoolHeadChan := make(chan core.NewTxPoolReorgEvent, 1) - tvm.vm.txPool.SubscribeNewReorgEvent(newTxPoolHeadChan) - - // Create three conflicting import transactions - importTxs := createImportTxOptions(t, tvm.atomicVM, tvm.atomicMemory) - - // Issue the first import transaction, build, and accept the block. - if err := tvm.atomicVM.AtomicMempool.AddLocalTx(importTxs[0]); err != nil { - t.Fatal(err) - } - - require.Equal(t, commonEng.PendingTxs, tvm.WaitForEvent(context.Background())) - - blk, err := tvm.vm.BuildBlock(context.Background()) - if err != nil { - t.Fatal(err) - } - - if err := blk.Verify(context.Background()); err != nil { - t.Fatal(err) - } - - if err := tvm.vm.SetPreference(context.Background(), blk.ID()); err != nil { - t.Fatal(err) - } - - if err := blk.Accept(context.Background()); err != nil { - t.Fatal(err) - } - - newHead := <-newTxPoolHeadChan - if newHead.Head.Hash() != common.Hash(blk.ID()) { - t.Fatalf("Expected new block to match") - } - - // Add the two conflicting transactions directly to the mempool, so that two consecutive transactions - // will fail verification when build block is called. - tvm.atomicVM.AtomicMempool.AddRemoteTx(importTxs[1]) - tvm.atomicVM.AtomicMempool.AddRemoteTx(importTxs[2]) - - if _, err := tvm.vm.BuildBlock(context.Background()); err == nil { - t.Fatal("Expected build block to fail due to empty block") +func TestLastAcceptedBlockNumberAllow(t *testing.T) { + for _, scheme := range vmtest.Schemes { + t.Run(scheme, func(t *testing.T) { + testLastAcceptedBlockNumberAllow(t, scheme) + }) } } -func TestAtomicTxBuildBlockDropsConflicts(t *testing.T) { - importAmount := uint64(10000000) - fork := upgradetest.ApricotPhase5 - tvm := newVM(t, testVMConfig{ - fork: &fork, - utxos: map[ids.ShortID]uint64{ - testShortIDAddrs[0]: importAmount, - testShortIDAddrs[1]: importAmount, - testShortIDAddrs[2]: importAmount, - }, +func testLastAcceptedBlockNumberAllow(t *testing.T, scheme string) { + fork := upgradetest.NoUpgrades + vm := newDefaultTestVM() + vmtest.SetupTestVM(t, vm, vmtest.TestVMConfig{ + Fork: &fork, + Scheme: scheme, }) - conflictKey := utilstest.NewKey(t) + defer func() { - if err := tvm.vm.Shutdown(context.Background()); err != nil { + if err := vm.Shutdown(context.Background()); err != nil { t.Fatal(err) } }() - // Create a conflict set for each pair of transactions - conflictSets := make([]set.Set[ids.ID], len(testKeys)) - for index, key := range testKeys { - importTx, err := tvm.atomicVM.NewImportTx(tvm.vm.ctx.XChainID, testEthAddrs[index], initialBaseFee, []*secp256k1.PrivateKey{key}) - if err != nil { - t.Fatal(err) - } - if err := tvm.atomicVM.AtomicMempool.AddLocalTx(importTx); err != nil { - t.Fatal(err) - } - conflictSets[index].Add(importTx.ID()) - conflictTx, err := tvm.atomicVM.NewImportTx(tvm.vm.ctx.XChainID, conflictKey.Address, initialBaseFee, []*secp256k1.PrivateKey{key}) - if err != nil { - t.Fatal(err) - } - if err := tvm.atomicVM.AtomicMempool.AddLocalTx(conflictTx); err == nil { - t.Fatal("should conflict with the utxoSet in the mempool") - } - // force add the tx - tvm.atomicVM.AtomicMempool.ForceAddTx(conflictTx) - conflictSets[index].Add(conflictTx.ID()) - } - require.Equal(t, commonEng.PendingTxs, tvm.WaitForEvent(context.Background())) - // Note: this only checks the path through OnFinalizeAndAssemble, we should make sure to add a test - // that verifies blocks received from the network will also fail verification - blk, err := tvm.vm.BuildBlock(context.Background()) + tx := types.NewTransaction(uint64(0), vmtest.TestEthAddrs[1], big.NewInt(1), 21000, big.NewInt(ap0.MinGasPrice), nil) + signedTx, err := types.SignTx(tx, types.NewEIP155Signer(vm.chainConfig.ChainID), vmtest.TestKeys[0].ToECDSA()) if err != nil { t.Fatal(err) } - atomicTxs := blk.(*chain.BlockWrapper).Block.(extension.ExtendedBlock).GetBlockExtension().(atomic.AtomicBlockContext).AtomicTxs() - assert.True(t, len(atomicTxs) == len(testKeys), "Conflict transactions should be out of the batch") - atomicTxIDs := set.Set[ids.ID]{} - for _, tx := range atomicTxs { - atomicTxIDs.Add(tx.ID()) - } - - // Check that removing the txIDs actually included in the block from each conflict set - // leaves one item remaining for each conflict set ie. only one tx from each conflict set - // has been included in the block. - for _, conflictSet := range conflictSets { - conflictSet.Difference(atomicTxIDs) - assert.Equal(t, 1, conflictSet.Len()) - } - - if err := blk.Verify(context.Background()); err != nil { - t.Fatal(err) - } - if err := blk.Accept(context.Background()); err != nil { - t.Fatal(err) - } -} - -func TestBuildBlockDoesNotExceedAtomicGasLimit(t *testing.T) { - importAmount := uint64(10000000) - fork := upgradetest.ApricotPhase5 - tvm := newVM(t, testVMConfig{ - fork: &fork, - }) - defer func() { - if err := tvm.vm.Shutdown(context.Background()); err != nil { - t.Fatal(err) - } - }() - - kc := secp256k1fx.NewKeychain() - kc.Add(testKeys[0]) - txID, err := ids.ToID(hashing.ComputeHash256(testShortIDAddrs[0][:])) - assert.NoError(t, err) - - mempoolTxs := 200 - for i := 0; i < mempoolTxs; i++ { - utxo, err := addUTXO(tvm.atomicMemory, tvm.vm.ctx, txID, uint32(i), tvm.vm.ctx.AVAXAssetID, importAmount, testShortIDAddrs[0]) - assert.NoError(t, err) - - importTx, err := atomic.NewImportTx(tvm.vm.ctx, tvm.vm.currentRules(), tvm.vm.clock.Unix(), tvm.vm.ctx.XChainID, testEthAddrs[0], initialBaseFee, kc, []*avax.UTXO{utxo}) + errs := vm.txPool.AddRemotesSync([]*types.Transaction{signedTx}) + for i, err := range errs { if err != nil { - t.Fatal(err) - } - if err := tvm.atomicVM.AtomicMempool.AddLocalTx(importTx); err != nil { - t.Fatal(err) + t.Fatalf("Failed to add tx at index %d: %s", i, err) } } - require.Equal(t, commonEng.PendingTxs, tvm.WaitForEvent(context.Background())) - blk, err := tvm.vm.BuildBlock(context.Background()) - if err != nil { - t.Fatal(err) - } - - atomicTxs := blk.(*chain.BlockWrapper).Block.(extension.ExtendedBlock).GetBlockExtension().(atomic.AtomicBlockContext).AtomicTxs() + msg, err := vm.WaitForEvent(context.Background()) + require.NoError(t, err) + require.Equal(t, commonEng.PendingTxs, msg) - // Need to ensure that not all of the transactions in the mempool are included in the block. - // This ensures that we hit the atomic gas limit while building the block before we hit the - // upper limit on the size of the codec for marshalling the atomic transactions. - if len(atomicTxs) >= mempoolTxs { - t.Fatalf("Expected number of atomic transactions included in the block (%d) to be less than the number of transactions added to the mempool (%d)", len(atomicTxs), mempoolTxs) + blk, err := vm.BuildBlock(context.Background()) + if err != nil { + t.Fatalf("Failed to build block with transaction: %s", err) } -} - -func TestExtraStateChangeAtomicGasLimitExceeded(t *testing.T) { - importAmount := uint64(10000000) - // We create two VMs one in ApriotPhase4 and one in ApricotPhase5, so that we can construct a block - // containing a large enough atomic transaction that it will exceed the atomic gas limit in - // ApricotPhase5. - ap4 := upgradetest.ApricotPhase4 - tvm1 := newVM(t, testVMConfig{ - fork: &ap4, - }) - ap5 := upgradetest.ApricotPhase5 - tvm2 := newVM(t, testVMConfig{ - fork: &ap5, - }) - defer func() { - if err := tvm1.vm.Shutdown(context.Background()); err != nil { - t.Fatal(err) - } - if err := tvm2.vm.Shutdown(context.Background()); err != nil { - t.Fatal(err) - } - }() - - kc := secp256k1fx.NewKeychain() - kc.Add(testKeys[0]) - txID, err := ids.ToID(hashing.ComputeHash256(testShortIDAddrs[0][:])) - assert.NoError(t, err) - - // Add enough UTXOs, such that the created import transaction will attempt to consume more gas than allowed - // in ApricotPhase5. - for i := 0; i < 100; i++ { - _, err := addUTXO(tvm1.atomicMemory, tvm1.vm.ctx, txID, uint32(i), tvm1.vm.ctx.AVAXAssetID, importAmount, testShortIDAddrs[0]) - assert.NoError(t, err) - _, err = addUTXO(tvm2.atomicMemory, tvm2.vm.ctx, txID, uint32(i), tvm2.vm.ctx.AVAXAssetID, importAmount, testShortIDAddrs[0]) - assert.NoError(t, err) + if err := blk.Verify(context.Background()); err != nil { + t.Fatalf("Block failed verification on VM: %s", err) } - // Double the initial base fee used when estimating the cost of this transaction to ensure that when it is - // used in ApricotPhase5 it still pays a sufficient fee with the fixed fee per atomic transaction. - importTx, err := tvm1.atomicVM.NewImportTx(tvm1.vm.ctx.XChainID, testEthAddrs[0], new(big.Int).Mul(common.Big2, initialBaseFee), []*secp256k1.PrivateKey{testKeys[0]}) - if err != nil { - t.Fatal(err) - } - if err := tvm1.atomicVM.AtomicMempool.ForceAddTx(importTx); err != nil { + if err := vm.SetPreference(context.Background(), blk.ID()); err != nil { t.Fatal(err) } - require.Equal(t, commonEng.PendingTxs, tvm1.WaitForEvent(context.Background())) - blk1, err := tvm1.vm.BuildBlock(context.Background()) + blkHeight := blk.Height() + blkHash := blk.(*chain.BlockWrapper).Block.(*wrappedBlock).ethBlock.Hash() + + vm.eth.APIBackend.SetAllowUnfinalizedQueries(true) + + ctx := context.Background() + b, err := vm.eth.APIBackend.BlockByNumber(ctx, rpc.BlockNumber(blkHeight)) if err != nil { t.Fatal(err) } - if err := blk1.Verify(context.Background()); err != nil { - t.Fatal(err) + if b.Hash() != blkHash { + t.Fatalf("expected block at %d to have hash %s but got %s", blkHeight, blkHash.Hex(), b.Hash().Hex()) } - validEthBlock := blk1.(*chain.BlockWrapper).Block.(extension.ExtendedBlock).GetEthBlock() + vm.eth.APIBackend.SetAllowUnfinalizedQueries(false) - extraData, err := atomic.Codec.Marshal(atomic.CodecVersion, []*atomic.Tx{importTx}) - if err != nil { - t.Fatal(err) + _, err = vm.eth.APIBackend.BlockByNumber(ctx, rpc.BlockNumber(blkHeight)) + if !errors.Is(err, eth.ErrUnfinalizedData) { + t.Fatalf("expected ErrUnfinalizedData but got %s", err.Error()) } - // Construct the new block with the extra data in the new format (slice of atomic transactions). - ethBlk2 := customtypes.NewBlockWithExtData( - types.CopyHeader(validEthBlock.Header()), - nil, - nil, - nil, - new(trie.Trie), - extraData, - true, - ) - - state, err := tvm2.vm.blockChain.State() - if err != nil { - t.Fatal(err) + if err := blk.Accept(context.Background()); err != nil { + t.Fatalf("VM failed to accept block: %s", err) } - // Hack: test [onExtraStateChange] directly to ensure it catches the atomic gas limit error correctly. - onExtraStateChangeFn := tvm2.vm.extensionConfig.ConsensusCallbacks.OnExtraStateChange - if _, _, err := onExtraStateChangeFn(ethBlk2, nil, state); err == nil || !strings.Contains(err.Error(), "exceeds atomic gas limit") { - t.Fatalf("Expected block to fail verification due to exceeded atomic gas limit, but found error: %v", err) + if b := vm.blockChain.GetBlockByNumber(blkHeight); b.Hash() != blkHash { + t.Fatalf("expected block at %d to have hash %s but got %s", blkHeight, blkHash.Hex(), b.Hash().Hex()) } } func TestSkipChainConfigCheckCompatible(t *testing.T) { - importAmount := uint64(50000000) fork := upgradetest.Durango - tvm := newVM(t, testVMConfig{ - fork: &fork, - utxos: map[ids.ShortID]uint64{ - testShortIDAddrs[0]: importAmount, - }, + vm := newDefaultTestVM() + tvm := vmtest.SetupTestVM(t, vm, vmtest.TestVMConfig{ + Fork: &fork, }) - defer func() { require.NoError(t, tvm.vm.Shutdown(context.Background())) }() // Since rewinding is permitted for last accepted height of 0, we must // accept one block to test the SkipUpgradeCheck functionality. - importTx, err := tvm.atomicVM.NewImportTx(tvm.vm.ctx.XChainID, testEthAddrs[0], initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) + tx := types.NewTransaction(uint64(0), vmtest.TestEthAddrs[1], big.NewInt(1), 21000, vmtest.InitialBaseFee, nil) + signedTx, err := types.SignTx(tx, types.NewEIP155Signer(vm.chainConfig.ChainID), vmtest.TestKeys[0].ToECDSA()) + if err != nil { + t.Fatal(err) + } + errs := vm.txPool.AddRemotesSync([]*types.Transaction{signedTx}) + for i, err := range errs { + if err != nil { + t.Fatalf("Failed to add tx at index %d: %s", i, err) + } + } + msg, err := vm.WaitForEvent(context.Background()) require.NoError(t, err) - require.NoError(t, tvm.atomicVM.AtomicMempool.AddLocalTx(importTx)) - require.Equal(t, commonEng.PendingTxs, tvm.WaitForEvent(context.Background())) + require.Equal(t, commonEng.PendingTxs, msg) - blk, err := tvm.vm.BuildBlock(context.Background()) + blk, err := vm.BuildBlock(context.Background()) require.NoError(t, err) require.NoError(t, blk.Verify(context.Background())) - require.NoError(t, tvm.vm.SetPreference(context.Background(), blk.ID())) + require.NoError(t, vm.SetPreference(context.Background(), blk.ID())) require.NoError(t, blk.Accept(context.Background())) - reinitVM := atomicvm.WrapVM(&VM{}) + require.NoError(t, vm.Shutdown(context.Background())) + + reinitVM := newDefaultTestVM() // use the block's timestamp instead of 0 since rewind to genesis // is hardcoded to be allowed in core/genesis.go. - newCTX := snowtest.Context(t, tvm.vm.ctx.ChainID) + newCTX := snowtest.Context(t, vm.ctx.ChainID) upgradetest.SetTimesTo(&newCTX.NetworkUpgrades, upgradetest.Latest, upgrade.UnscheduledActivationTime) upgradetest.SetTimesTo(&newCTX.NetworkUpgrades, fork+1, blk.Timestamp()) upgradetest.SetTimesTo(&newCTX.NetworkUpgrades, fork, upgrade.InitiallyActiveTime) - genesis := []byte(genesisJSON(forkToChainConfig[fork])) - err = reinitVM.Initialize(context.Background(), newCTX, tvm.db, genesis, []byte{}, []byte{}, []*commonEng.Fx{}, tvm.appSender) + genesis := []byte(vmtest.GenesisJSON(vmtest.ForkToChainConfig[fork])) + err = reinitVM.Initialize(context.Background(), newCTX, tvm.DB, genesis, []byte{}, []byte{}, []*commonEng.Fx{}, tvm.AppSender) require.ErrorContains(t, err, "mismatching Cancun fork timestamp in database") - reinitVM = atomicvm.WrapVM(&VM{}) - newCTX.Metrics = metrics.NewPrefixGatherer() - // try again with skip-upgrade-check + reinitVM = newDefaultTestVM() + vmtest.ResetMetrics(newCTX) config := []byte(`{"skip-upgrade-check": true}`) - require.NoError(t, reinitVM.Initialize( - context.Background(), - newCTX, - tvm.db, - genesis, - []byte{}, - config, - []*commonEng.Fx{}, - tvm.appSender)) + require.NoError(t, reinitVM.Initialize(context.Background(), newCTX, tvm.DB, genesis, []byte{}, config, []*commonEng.Fx{}, tvm.AppSender)) require.NoError(t, reinitVM.Shutdown(context.Background())) } @@ -3962,53 +2103,47 @@ func TestParentBeaconRootBlock(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { - importAmount := uint64(1000000000) - tvm := newVM(t, testVMConfig{ - fork: &test.fork, - utxos: map[ids.ShortID]uint64{ - testShortIDAddrs[0]: importAmount, - }, + fork := test.fork + vm := newDefaultTestVM() + vmtest.SetupTestVM(t, vm, vmtest.TestVMConfig{ + Fork: &fork, }) + defer func() { - if err := tvm.vm.Shutdown(context.Background()); err != nil { + if err := vm.Shutdown(context.Background()); err != nil { t.Fatal(err) } }() - importTx, err := tvm.atomicVM.NewImportTx(tvm.vm.ctx.XChainID, testEthAddrs[0], initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) + tx := types.NewTransaction(uint64(0), vmtest.TestEthAddrs[1], big.NewInt(1), 21000, vmtest.InitialBaseFee, nil) + signedTx, err := types.SignTx(tx, types.NewEIP155Signer(vm.chainConfig.ChainID), vmtest.TestKeys[0].ToECDSA()) if err != nil { t.Fatal(err) } - - if err := tvm.atomicVM.AtomicMempool.AddLocalTx(importTx); err != nil { - t.Fatal(err) + errs := vm.txPool.AddRemotesSync([]*types.Transaction{signedTx}) + for i, err := range errs { + if err != nil { + t.Fatalf("Failed to add tx at index %d: %s", i, err) + } } - require.Equal(t, commonEng.PendingTxs, tvm.WaitForEvent(context.Background())) + msg, err := vm.WaitForEvent(context.Background()) + require.NoError(t, err) + require.Equal(t, commonEng.PendingTxs, msg) - blk, err := tvm.vm.BuildBlock(context.Background()) + blk, err := vm.BuildBlock(context.Background()) if err != nil { - t.Fatalf("Failed to build block with import transaction: %s", err) + t.Fatalf("Failed to build block with transaction: %s", err) } // Modify the block to have a parent beacon root - ethBlock := blk.(*chain.BlockWrapper).Block.(extension.ExtendedBlock).GetEthBlock() + ethBlock := blk.(*chain.BlockWrapper).Block.(*wrappedBlock).ethBlock header := types.CopyHeader(ethBlock.Header()) header.ParentBeaconRoot = test.beaconRoot - parentBeaconEthBlock := customtypes.NewBlockWithExtData( - header, - nil, - nil, - nil, - new(trie.Trie), - customtypes.BlockExtData(ethBlock), - false, - ) - - parentBeaconBlock, err := wrapBlock(parentBeaconEthBlock, tvm.vm) - if err != nil { - t.Fatal(err) - } + parentBeaconEthBlock := ethBlock.WithSeal(header) + + parentBeaconBlock, err := wrapBlock(parentBeaconEthBlock, vm) + require.NoError(t, err) errCheck := func(err error) { if test.expectedError { @@ -4022,7 +2157,7 @@ func TestParentBeaconRootBlock(t *testing.T) { } } - _, err = tvm.vm.ParseBlock(context.Background(), parentBeaconBlock.Bytes()) + _, err = vm.ParseBlock(context.Background(), parentBeaconBlock.Bytes()) errCheck(err) err = parentBeaconBlock.Verify(context.Background()) errCheck(err) @@ -4048,11 +2183,11 @@ func TestNoBlobsAllowed(t *testing.T) { GasTipCap: uint256.NewInt(1), GasFeeCap: uint256.MustFromBig(fee), Gas: ethparams.TxGas, - To: testEthAddrs[0], + To: vmtest.TestEthAddrs[0], BlobFeeCap: uint256.NewInt(1), BlobHashes: []common.Hash{{1}}, // This blob is expected to cause verification to fail Value: new(uint256.Int), - }), signer, testKeys[0].ToECDSA()) + }), signer, vmtest.TestKeys[0].ToECDSA()) require.NoError(err) b.AddTx(tx) } @@ -4061,17 +2196,18 @@ func TestNoBlobsAllowed(t *testing.T) { require.NoError(err) // Create a VM with the genesis (will use header verification) - vm := newVM(t, testVMConfig{ - genesisJSON: genesisJSONCancun, - }).vm + vm := newDefaultTestVM() + vmtest.SetupTestVM(t, vm, vmtest.TestVMConfig{ + GenesisJSON: genesisJSONCancun, + }) defer func() { require.NoError(vm.Shutdown(ctx)) }() // Verification should fail - vmBlock, err := wrapBlock(blocks[0], vm) + extendedBlock, err := wrapBlock(blocks[0], vm) require.NoError(err) - _, err = vm.ParseBlock(ctx, vmBlock.Bytes()) + _, err = vm.ParseBlock(ctx, extendedBlock.Bytes()) require.ErrorContains(err, "blobs not enabled on avalanche networks") - err = vmBlock.Verify(ctx) + err = extendedBlock.Verify(ctx) require.ErrorContains(err, "blobs not enabled on avalanche networks") } @@ -4079,20 +2215,17 @@ func TestBuildBlockWithInsufficientCapacity(t *testing.T) { ctx := context.Background() require := require.New(t) - importAmount := uint64(2_000_000_000_000_000) // 2M AVAX fork := upgradetest.Fortuna - tvm := newVM(t, testVMConfig{ - fork: &fork, - utxos: map[ids.ShortID]uint64{ - testShortIDAddrs[0]: importAmount, - }, + vm := newDefaultTestVM() + vmtest.SetupTestVM(t, vm, vmtest.TestVMConfig{ + Fork: &fork, }) defer func() { - require.NoError(tvm.vm.Shutdown(ctx)) + require.NoError(vm.Shutdown(ctx)) }() newTxPoolHeadChan := make(chan core.NewTxPoolReorgEvent, 1) - tvm.vm.txPool.SubscribeNewReorgEvent(newTxPoolHeadChan) + vm.txPool.SubscribeNewReorgEvent(newTxPoolHeadChan) // Build a block consuming all of the available gas var ( @@ -4107,36 +2240,45 @@ func TestBuildBlockWithInsufficientCapacity(t *testing.T) { big.NewInt(ap0.MinGasPrice), []byte{0xfe}, // invalid opcode consumes all gas ) - txs[i], err = types.SignTx(tx, types.NewEIP155Signer(tvm.vm.chainID), testKeys[0].ToECDSA()) + txs[i], err = types.SignTx(tx, types.NewEIP155Signer(vm.chainConfig.ChainID), vmtest.TestKeys[0].ToECDSA()) require.NoError(err) } - errs := tvm.vm.txPool.AddRemotesSync([]*types.Transaction{txs[0]}) + errs := vm.txPool.AddRemotesSync([]*types.Transaction{txs[0]}) require.Len(errs, 1) require.NoError(errs[0]) - require.Equal(commonEng.PendingTxs, tvm.WaitForEvent(context.Background())) - blk2, err := tvm.vm.BuildBlock(ctx) + msg, err := vm.WaitForEvent(context.Background()) + require.NoError(err) + require.Equal(commonEng.PendingTxs, msg) + + blk2, err := vm.BuildBlock(ctx) require.NoError(err) require.NoError(blk2.Verify(ctx)) require.NoError(blk2.Accept(ctx)) // Attempt to build a block consuming more than the current gas capacity - errs = tvm.vm.txPool.AddRemotesSync([]*types.Transaction{txs[1]}) + errs = vm.txPool.AddRemotesSync([]*types.Transaction{txs[1]}) require.Len(errs, 1) require.NoError(errs[0]) - require.Equal(commonEng.PendingTxs, tvm.WaitForEvent(context.Background())) + msg, err = vm.WaitForEvent(context.Background()) + require.NoError(err) + require.Equal(commonEng.PendingTxs, msg) + // Expect block building to fail due to insufficient gas capacity - _, err = tvm.vm.BuildBlock(ctx) + _, err = vm.BuildBlock(ctx) require.ErrorIs(err, miner.ErrInsufficientGasCapacityToBuild) // Wait to fill block capacity and retry block builiding - tvm.vm.clock.Set(tvm.vm.clock.Time().Add(acp176.TimeToFillCapacity * time.Second)) + vm.clock.Set(vm.clock.Time().Add(acp176.TimeToFillCapacity * time.Second)) + + msg, err = vm.WaitForEvent(context.Background()) + require.NoError(err) + require.Equal(commonEng.PendingTxs, msg) - require.Equal(commonEng.PendingTxs, tvm.WaitForEvent(context.Background())) - blk3, err := tvm.vm.BuildBlock(ctx) + blk3, err := vm.BuildBlock(ctx) require.NoError(err) require.NoError(blk3.Verify(ctx)) @@ -4147,49 +2289,32 @@ func TestBuildBlockLargeTxStarvation(t *testing.T) { ctx := context.Background() require := require.New(t) - importAmount := uint64(2_000_000_000_000_000) // 2M AVAX fork := upgradetest.Fortuna - tvm := newVM(t, testVMConfig{ - fork: &fork, - utxos: map[ids.ShortID]uint64{ - testShortIDAddrs[0]: importAmount, - testShortIDAddrs[1]: importAmount, - }, + amount := new(big.Int).Mul(big.NewInt(ethparams.Ether), big.NewInt(4000)) + genesis := vmtest.NewTestGenesis(vmtest.ForkToChainConfig[fork]) + for _, addr := range vmtest.TestEthAddrs { + genesis.Alloc[addr] = types.Account{Balance: amount} + } + genesisBytes, err := json.Marshal(genesis) + require.NoError(err) + + vm := newDefaultTestVM() + vmtest.SetupTestVM(t, vm, vmtest.TestVMConfig{ + Fork: &fork, + GenesisJSON: string(genesisBytes), }) defer func() { - require.NoError(tvm.vm.Shutdown(ctx)) + require.NoError(vm.Shutdown(ctx)) }() - newTxPoolHeadChan := make(chan core.NewTxPoolReorgEvent, 1) - tvm.vm.txPool.SubscribeNewReorgEvent(newTxPoolHeadChan) - - importTx1, err := tvm.atomicVM.NewImportTx(tvm.vm.ctx.XChainID, testEthAddrs[0], initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) - require.NoError(err) - require.NoError(tvm.atomicVM.AtomicMempool.AddLocalTx(importTx1)) - - importTx2, err := tvm.atomicVM.NewImportTx(tvm.vm.ctx.XChainID, testEthAddrs[1], initialBaseFee, []*secp256k1.PrivateKey{testKeys[1]}) - require.NoError(err) - require.NoError(tvm.atomicVM.AtomicMempool.AddLocalTx(importTx2)) - - require.Equal(commonEng.PendingTxs, tvm.WaitForEvent(context.Background())) - blk1, err := tvm.vm.BuildBlock(ctx) - require.NoError(err) - - require.NoError(blk1.Verify(ctx)) - require.NoError(tvm.vm.SetPreference(ctx, blk1.ID())) - require.NoError(blk1.Accept(ctx)) - - newHead := <-newTxPoolHeadChan - require.Equal(common.Hash(blk1.ID()), newHead.Head.Hash()) - // Build a block consuming all of the available gas var ( highGasPrice = big.NewInt(2 * ap0.MinGasPrice) lowGasPrice = big.NewInt(ap0.MinGasPrice) ) - // Refill capacity after distributing funds with import transactions - tvm.vm.clock.Set(tvm.vm.clock.Time().Add(acp176.TimeToFillCapacity * time.Second)) + // Refill capacity + vm.clock.Set(vm.clock.Time().Add(acp176.TimeToFillCapacity * time.Second)) maxSizeTxs := make([]*types.Transaction, 2) for i := uint64(0); i < 2; i++ { tx := types.NewContractCreation( @@ -4199,43 +2324,54 @@ func TestBuildBlockLargeTxStarvation(t *testing.T) { highGasPrice, []byte{0xfe}, // invalid opcode consumes all gas ) - maxSizeTxs[i], err = types.SignTx(tx, types.NewEIP155Signer(tvm.vm.chainID), testKeys[0].ToECDSA()) + var err error + maxSizeTxs[i], err = types.SignTx(tx, types.NewEIP155Signer(vm.chainConfig.ChainID), vmtest.TestKeys[0].ToECDSA()) require.NoError(err) } - errs := tvm.vm.txPool.AddRemotesSync([]*types.Transaction{maxSizeTxs[0]}) + errs := vm.txPool.AddRemotesSync([]*types.Transaction{maxSizeTxs[0]}) require.Len(errs, 1) require.NoError(errs[0]) - require.Equal(commonEng.PendingTxs, tvm.WaitForEvent(context.Background())) - blk2, err := tvm.vm.BuildBlock(ctx) + msg, err := vm.WaitForEvent(context.Background()) + require.NoError(err) + require.Equal(commonEng.PendingTxs, msg) + + blk2, err := vm.BuildBlock(ctx) require.NoError(err) require.NoError(blk2.Verify(ctx)) require.NoError(blk2.Accept(ctx)) // Add a second transaction trying to consume the max guaranteed gas capacity at a higher gas price - errs = tvm.vm.txPool.AddRemotesSync([]*types.Transaction{maxSizeTxs[1]}) + errs = vm.txPool.AddRemotesSync([]*types.Transaction{maxSizeTxs[1]}) require.Len(errs, 1) require.NoError(errs[0]) // Build a smaller transaction that consumes less gas at a lower price. Block building should // fail and enforce waiting for more capacity to avoid starving the larger transaction. tx := types.NewContractCreation(0, big.NewInt(0), 2_000_000, lowGasPrice, []byte{0xfe}) - signedTx, err := types.SignTx(tx, types.NewEIP155Signer(tvm.vm.chainID), testKeys[1].ToECDSA()) + signedTx, err := types.SignTx(tx, types.NewEIP155Signer(vm.chainConfig.ChainID), vmtest.TestKeys[1].ToECDSA()) require.NoError(err) - errs = tvm.vm.txPool.AddRemotesSync([]*types.Transaction{signedTx}) + errs = vm.txPool.AddRemotesSync([]*types.Transaction{signedTx}) require.Len(errs, 1) require.NoError(errs[0]) - require.Equal(commonEng.PendingTxs, tvm.WaitForEvent(context.Background())) - _, err = tvm.vm.BuildBlock(ctx) + msg, err = vm.WaitForEvent(context.Background()) + require.NoError(err) + require.Equal(commonEng.PendingTxs, msg) + + _, err = vm.BuildBlock(ctx) require.ErrorIs(err, miner.ErrInsufficientGasCapacityToBuild) // Wait to fill block capacity and retry block building - tvm.vm.clock.Set(tvm.vm.clock.Time().Add(acp176.TimeToFillCapacity * time.Second)) - require.Equal(commonEng.PendingTxs, tvm.WaitForEvent(context.Background())) - blk4, err := tvm.vm.BuildBlock(ctx) + vm.clock.Set(vm.clock.Time().Add(acp176.TimeToFillCapacity * time.Second)) + + msg, err = vm.WaitForEvent(context.Background()) + require.NoError(err) + require.Equal(commonEng.PendingTxs, msg) + + blk4, err := vm.BuildBlock(ctx) require.NoError(err) ethBlk4 := blk4.(*chain.BlockWrapper).Block.(*wrappedBlock).ethBlock actualTxs := ethBlk4.Transactions() @@ -4247,47 +2383,13 @@ func TestBuildBlockLargeTxStarvation(t *testing.T) { } func TestWaitForEvent(t *testing.T) { - populateAtomicMemory := func(t *testing.T, tvm *testVM) (common.Address, *ecdsa.PrivateKey) { - key := testKeys[0].ToECDSA() - address := testEthAddrs[0] - - importAmount := uint64(1000000000) - utxoID := avax.UTXOID{TxID: ids.GenerateTestID()} - - utxo := &avax.UTXO{ - UTXOID: utxoID, - Asset: avax.Asset{ID: tvm.vm.ctx.AVAXAssetID}, - Out: &secp256k1fx.TransferOutput{ - Amt: importAmount, - OutputOwners: secp256k1fx.OutputOwners{ - Threshold: 1, - Addrs: []ids.ShortID{testKeys[0].Address()}, - }, - }, - } - utxoBytes, err := atomic.Codec.Marshal(atomic.CodecVersion, utxo) - require.NoError(t, err) - - xChainSharedMemory := tvm.atomicMemory.NewSharedMemory(tvm.vm.ctx.XChainID) - inputID := utxo.InputID() - require.NoError(t, xChainSharedMemory.Apply(map[ids.ID]*avalancheatomic.Requests{tvm.vm.ctx.ChainID: {PutRequests: []*avalancheatomic.Element{{ - Key: inputID[:], - Value: utxoBytes, - Traits: [][]byte{ - testKeys[0].Address().Bytes(), - }, - }}}})) - - return address, key - } - for _, testCase := range []struct { name string - testCase func(*testing.T, *VM, *atomicvm.VM, common.Address, *ecdsa.PrivateKey) + testCase func(*testing.T, *VM) }{ { name: "WaitForEvent with context cancelled returns 0", - testCase: func(t *testing.T, vm *VM, _ *atomicvm.VM, address common.Address, key *ecdsa.PrivateKey) { + testCase: func(t *testing.T, vm *VM) { ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*100) defer cancel() @@ -4307,10 +2409,7 @@ func TestWaitForEvent(t *testing.T) { }, { name: "WaitForEvent returns when a transaction is added to the mempool", - testCase: func(t *testing.T, vm *VM, atomicVM *atomicvm.VM, address common.Address, key *ecdsa.PrivateKey) { - importTx, err := atomicVM.NewImportTx(vm.ctx.XChainID, address, initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) - require.NoError(t, err) - + testCase: func(t *testing.T, vm *VM) { var wg sync.WaitGroup wg.Add(1) @@ -4321,18 +2420,27 @@ func TestWaitForEvent(t *testing.T) { require.Equal(t, commonEng.PendingTxs, msg) }() - require.NoError(t, atomicVM.AtomicMempool.AddLocalTx(importTx)) + tx := types.NewTransaction(uint64(0), vmtest.TestEthAddrs[1], big.NewInt(1), 21000, vmtest.InitialBaseFee, nil) + signedTx, err := types.SignTx(tx, types.NewEIP155Signer(vm.chainConfig.ChainID), vmtest.TestKeys[0].ToECDSA()) + require.NoError(t, err) + + for _, err := range vm.txPool.AddRemotesSync([]*types.Transaction{signedTx}) { + require.NoError(t, err) + } wg.Wait() }, }, { name: "WaitForEvent doesn't return once a block is built and accepted", - testCase: func(t *testing.T, vm *VM, atomicVM *atomicvm.VM, address common.Address, key *ecdsa.PrivateKey) { - importTx, err := atomicVM.NewImportTx(vm.ctx.XChainID, address, initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) + testCase: func(t *testing.T, vm *VM) { + tx := types.NewTransaction(uint64(0), vmtest.TestEthAddrs[1], big.NewInt(1), 21000, vmtest.InitialBaseFee, nil) + signedTx, err := types.SignTx(tx, types.NewEIP155Signer(vm.chainConfig.ChainID), vmtest.TestKeys[0].ToECDSA()) require.NoError(t, err) - require.NoError(t, atomicVM.AtomicMempool.AddLocalTx(importTx)) + for _, err := range vm.txPool.AddRemotesSync([]*types.Transaction{signedTx}) { + require.NoError(t, err) + } blk, err := vm.BuildBlock(context.Background()) require.NoError(t, err) @@ -4361,16 +2469,13 @@ func TestWaitForEvent(t *testing.T) { t.Log("WaitForEvent returns when regular transactions are added to the mempool") - txs := make([]*types.Transaction, 10) - for i := 0; i < 10; i++ { - tx := types.NewTransaction(uint64(i), address, big.NewInt(10), 21000, big.NewInt(3*ap0.MinGasPrice), nil) - signedTx, err := types.SignTx(tx, types.NewEIP155Signer(vm.chainID), key) - require.NoError(t, err) + time.Sleep(time.Second * 2) // sleep some time to let the gas capacity to refill - txs[i] = signedTx - } - errs := vm.txPool.Add(txs, false, false) - for _, err := range errs { + tx = types.NewTransaction(uint64(1), vmtest.TestEthAddrs[1], big.NewInt(1), 21000, vmtest.InitialBaseFee, nil) + signedTx, err = types.SignTx(tx, types.NewEIP155Signer(vm.chainConfig.ChainID), vmtest.TestKeys[0].ToECDSA()) + require.NoError(t, err) + + for _, err := range vm.txPool.AddRemotesSync([]*types.Transaction{signedTx}) { require.NoError(t, err) } @@ -4398,11 +2503,14 @@ func TestWaitForEvent(t *testing.T) { }, { name: "WaitForEvent waits some time after a block is built", - testCase: func(t *testing.T, vm *VM, atomicVM *atomicvm.VM, address common.Address, key *ecdsa.PrivateKey) { - importTx, err := atomicVM.NewImportTx(vm.ctx.XChainID, address, initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) + testCase: func(t *testing.T, vm *VM) { + tx := types.NewTransaction(uint64(0), vmtest.TestEthAddrs[1], big.NewInt(1), 21000, vmtest.InitialBaseFee, nil) + signedTx, err := types.SignTx(tx, types.NewEIP155Signer(vm.chainConfig.ChainID), vmtest.TestKeys[0].ToECDSA()) require.NoError(t, err) - require.NoError(t, atomicVM.AtomicMempool.AddLocalTx(importTx)) + for _, err := range vm.txPool.AddRemotesSync([]*types.Transaction{signedTx}) { + require.NoError(t, err) + } lastBuildBlockTime := time.Now() @@ -4415,16 +2523,11 @@ func TestWaitForEvent(t *testing.T) { require.NoError(t, blk.Accept(context.Background())) - txs := make([]*types.Transaction, 10) - for i := 0; i < 10; i++ { - tx := types.NewTransaction(uint64(i), address, big.NewInt(10), 21000, big.NewInt(3*ap0.MinGasPrice), nil) - signedTx, err := types.SignTx(tx, types.NewEIP155Signer(vm.chainID), key) - require.NoError(t, err) + tx = types.NewTransaction(uint64(1), vmtest.TestEthAddrs[1], big.NewInt(1), 21000, vmtest.InitialBaseFee, nil) + signedTx, err = types.SignTx(tx, types.NewEIP155Signer(vm.chainConfig.ChainID), vmtest.TestKeys[0].ToECDSA()) + require.NoError(t, err) - txs[i] = signedTx - } - errs := vm.txPool.Add(txs, false, false) - for _, err := range errs { + for _, err := range vm.txPool.AddRemotesSync([]*types.Transaction{signedTx}) { require.NoError(t, err) } @@ -4436,7 +2539,7 @@ func TestWaitForEvent(t *testing.T) { msg, err := vm.WaitForEvent(context.Background()) require.NoError(t, err) require.Equal(t, commonEng.PendingTxs, msg) - require.GreaterOrEqual(t, time.Since(lastBuildBlockTime), minBlockBuildingRetryDelay) + require.GreaterOrEqual(t, time.Since(lastBuildBlockTime), MinBlockBuildingRetryDelay) }() wg.Wait() @@ -4445,12 +2548,12 @@ func TestWaitForEvent(t *testing.T) { } { t.Run(testCase.name, func(t *testing.T) { fork := upgradetest.Latest - tvm := newVM(t, testVMConfig{ - fork: &fork, + vm := newDefaultTestVM() + vmtest.SetupTestVM(t, vm, vmtest.TestVMConfig{ + Fork: &fork, }) - address, key := populateAtomicMemory(t, tvm) - testCase.testCase(t, tvm.vm, tvm.atomicVM, address, key) - tvm.vm.Shutdown(context.Background()) + testCase.testCase(t, vm) + vm.Shutdown(context.Background()) }) } } diff --git a/plugin/evm/vm_warp_test.go b/plugin/evm/vm_warp_test.go index 845d2dadd5..ddecd09ca9 100644 --- a/plugin/evm/vm_warp_test.go +++ b/plugin/evm/vm_warp_test.go @@ -11,8 +11,6 @@ import ( "testing" "time" - atomicvm "github.com/ava-labs/coreth/plugin/evm/atomic/vm" - _ "embed" "github.com/ava-labs/avalanchego/ids" @@ -37,9 +35,9 @@ import ( "github.com/ava-labs/coreth/eth/tracers" "github.com/ava-labs/coreth/params" "github.com/ava-labs/coreth/params/extras" - "github.com/ava-labs/coreth/plugin/evm/extension" customheader "github.com/ava-labs/coreth/plugin/evm/header" "github.com/ava-labs/coreth/plugin/evm/upgrade/ap0" + "github.com/ava-labs/coreth/plugin/evm/vmtest" "github.com/ava-labs/coreth/precompile/contract" warpcontract "github.com/ava-labs/coreth/precompile/contracts/warp" "github.com/ava-labs/coreth/predicate" @@ -75,7 +73,7 @@ const ( ) func TestSendWarpMessage(t *testing.T) { - for _, scheme := range schemes { + for _, scheme := range vmtest.Schemes { t.Run(scheme, func(t *testing.T) { testSendWarpMessage(t, scheme) }) @@ -85,16 +83,17 @@ func TestSendWarpMessage(t *testing.T) { func testSendWarpMessage(t *testing.T, scheme string) { require := require.New(t) fork := upgradetest.Durango - tvm := newVM(t, testVMConfig{ - fork: &fork, - configJSON: getConfig(scheme, ""), + vm := newDefaultTestVM() + vmtest.SetupTestVM(t, vm, vmtest.TestVMConfig{ + Fork: &fork, + Scheme: scheme, }) defer func() { - require.NoError(tvm.vm.Shutdown(context.Background())) + require.NoError(vm.Shutdown(context.Background())) }() acceptedLogsChan := make(chan []*types.Log, 10) - logsSub := tvm.vm.eth.APIBackend.SubscribeAcceptedLogsEvent(acceptedLogsChan) + logsSub := vm.eth.APIBackend.SubscribeAcceptedLogsEvent(acceptedLogsChan) defer logsSub.Unsubscribe() payloadData := avagoUtils.RandomBytes(100) @@ -102,42 +101,44 @@ func testSendWarpMessage(t *testing.T, scheme string) { warpSendMessageInput, err := warpcontract.PackSendWarpMessage(payloadData) require.NoError(err) addressedPayload, err := payload.NewAddressedCall( - testEthAddrs[0].Bytes(), + vmtest.TestEthAddrs[0].Bytes(), payloadData, ) require.NoError(err) expectedUnsignedMessage, err := avalancheWarp.NewUnsignedMessage( - tvm.vm.ctx.NetworkID, - tvm.vm.ctx.ChainID, + vm.ctx.NetworkID, + vm.ctx.ChainID, addressedPayload.Bytes(), ) require.NoError(err) // Submit a transaction to trigger sending a warp message tx0 := types.NewTransaction(uint64(0), warpcontract.ContractAddress, big.NewInt(1), 100_000, big.NewInt(ap0.MinGasPrice), warpSendMessageInput) - signedTx0, err := types.SignTx(tx0, types.LatestSignerForChainID(tvm.vm.chainConfig.ChainID), testKeys[0].ToECDSA()) + signedTx0, err := types.SignTx(tx0, types.LatestSignerForChainID(vm.chainConfig.ChainID), vmtest.TestKeys[0].ToECDSA()) require.NoError(err) - errs := tvm.vm.txPool.AddRemotesSync([]*types.Transaction{signedTx0}) + errs := vm.txPool.AddRemotesSync([]*types.Transaction{signedTx0}) require.NoError(errs[0]) - require.Equal(commonEng.PendingTxs, tvm.WaitForEvent(context.Background())) + msg, err := vm.WaitForEvent(context.Background()) + require.NoError(err) + require.Equal(commonEng.PendingTxs, msg) - blk, err := tvm.vm.BuildBlock(context.Background()) + blk, err := vm.BuildBlock(context.Background()) require.NoError(err) require.NoError(blk.Verify(context.Background())) // Verify that the constructed block contains the expected log with an unsigned warp message in the log data - ethBlock1 := blk.(*chain.BlockWrapper).Block.(extension.ExtendedBlock).GetEthBlock() + ethBlock1 := blk.(*chain.BlockWrapper).Block.(*wrappedBlock).ethBlock require.Len(ethBlock1.Transactions(), 1) - receipts := rawdb.ReadReceipts(tvm.vm.chaindb, ethBlock1.Hash(), ethBlock1.NumberU64(), ethBlock1.Time(), tvm.vm.chainConfig) + receipts := rawdb.ReadReceipts(vm.chaindb, ethBlock1.Hash(), ethBlock1.NumberU64(), ethBlock1.Time(), vm.chainConfig) require.Len(receipts, 1) require.Len(receipts[0].Logs, 1) expectedTopics := []common.Hash{ warpcontract.WarpABI.Events["SendWarpMessage"].ID, - common.BytesToHash(testEthAddrs[0].Bytes()), + common.BytesToHash(vmtest.TestEthAddrs[0].Bytes()), common.Hash(expectedUnsignedMessage.ID()), } require.Equal(expectedTopics, receipts[0].Logs[0].Topics) @@ -146,17 +147,17 @@ func testSendWarpMessage(t *testing.T, scheme string) { require.NoError(err) // Verify the signature cannot be fetched before the block is accepted - _, err = tvm.vm.warpBackend.GetMessageSignature(context.TODO(), unsignedMessage) + _, err = vm.warpBackend.GetMessageSignature(context.TODO(), unsignedMessage) require.Error(err) - _, err = tvm.vm.warpBackend.GetBlockSignature(context.TODO(), blk.ID()) + _, err = vm.warpBackend.GetBlockSignature(context.TODO(), blk.ID()) require.Error(err) - require.NoError(tvm.vm.SetPreference(context.Background(), blk.ID())) + require.NoError(vm.SetPreference(context.Background(), blk.ID())) require.NoError(blk.Accept(context.Background())) - tvm.vm.blockChain.DrainAcceptorQueue() + vm.blockChain.DrainAcceptorQueue() // Verify the message signature after accepting the block. - rawSignatureBytes, err := tvm.vm.warpBackend.GetMessageSignature(context.TODO(), unsignedMessage) + rawSignatureBytes, err := vm.warpBackend.GetMessageSignature(context.TODO(), unsignedMessage) require.NoError(err) blsSignature, err := bls.SignatureFromBytes(rawSignatureBytes[:]) require.NoError(err) @@ -170,25 +171,25 @@ func testSendWarpMessage(t *testing.T, scheme string) { } // Verify the produced message signature is valid - require.True(bls.Verify(tvm.vm.ctx.PublicKey, blsSignature, unsignedMessage.Bytes())) + require.True(bls.Verify(vm.ctx.PublicKey, blsSignature, unsignedMessage.Bytes())) // Verify the blockID will now be signed by the backend and produces a valid signature. - rawSignatureBytes, err = tvm.vm.warpBackend.GetBlockSignature(context.TODO(), blk.ID()) + rawSignatureBytes, err = vm.warpBackend.GetBlockSignature(context.TODO(), blk.ID()) require.NoError(err) blsSignature, err = bls.SignatureFromBytes(rawSignatureBytes[:]) require.NoError(err) blockHashPayload, err := payload.NewHash(blk.ID()) require.NoError(err) - unsignedMessage, err = avalancheWarp.NewUnsignedMessage(tvm.vm.ctx.NetworkID, tvm.vm.ctx.ChainID, blockHashPayload.Bytes()) + unsignedMessage, err = avalancheWarp.NewUnsignedMessage(vm.ctx.NetworkID, vm.ctx.ChainID, blockHashPayload.Bytes()) require.NoError(err) // Verify the produced message signature is valid - require.True(bls.Verify(tvm.vm.ctx.PublicKey, blsSignature, unsignedMessage.Bytes())) + require.True(bls.Verify(vm.ctx.PublicKey, blsSignature, unsignedMessage.Bytes())) } func TestValidateWarpMessage(t *testing.T) { - for _, scheme := range schemes { + for _, scheme := range vmtest.Schemes { t.Run(scheme, func(t *testing.T) { testValidateWarpMessage(t, scheme) }) @@ -222,7 +223,7 @@ func testValidateWarpMessage(t *testing.T, scheme string) { } func TestValidateInvalidWarpMessage(t *testing.T) { - for _, scheme := range schemes { + for _, scheme := range vmtest.Schemes { t.Run(scheme, func(t *testing.T) { testValidateInvalidWarpMessage(t, scheme) }) @@ -253,7 +254,7 @@ func testValidateInvalidWarpMessage(t *testing.T, scheme string) { } func TestValidateWarpBlockHash(t *testing.T) { - for _, scheme := range schemes { + for _, scheme := range vmtest.Schemes { t.Run(scheme, func(t *testing.T) { testValidateWarpBlockHash(t, scheme) }) @@ -282,7 +283,7 @@ func testValidateWarpBlockHash(t *testing.T, scheme string) { } func TestValidateInvalidWarpBlockHash(t *testing.T) { - for _, scheme := range schemes { + for _, scheme := range vmtest.Schemes { t.Run(scheme, func(t *testing.T) { testValidateInvalidWarpBlockHash(t, scheme) }) @@ -311,16 +312,17 @@ func testValidateInvalidWarpBlockHash(t *testing.T, scheme string) { func testWarpVMTransaction(t *testing.T, scheme string, unsignedMessage *avalancheWarp.UnsignedMessage, validSignature bool, txPayload []byte) { require := require.New(t) fork := upgradetest.Durango - tvm := newVM(t, testVMConfig{ - fork: &fork, - configJSON: getConfig(scheme, ""), + vm := newDefaultTestVM() + vmtest.SetupTestVM(t, vm, vmtest.TestVMConfig{ + Fork: &fork, + Scheme: scheme, }) defer func() { - require.NoError(tvm.vm.Shutdown(context.Background())) + require.NoError(vm.Shutdown(context.Background())) }() acceptedLogsChan := make(chan []*types.Log, 10) - logsSub := tvm.vm.eth.APIBackend.SubscribeAcceptedLogsEvent(acceptedLogsChan) + logsSub := vm.eth.APIBackend.SubscribeAcceptedLogsEvent(acceptedLogsChan) defer logsSub.Unsubscribe() nodeID1 := ids.GenerateTestNodeID() @@ -343,7 +345,7 @@ func testWarpVMTransaction(t *testing.T, scheme string, unsignedMessage *avalanc minimumValidPChainHeight := uint64(10) getValidatorSetTestErr := errors.New("can't get validator set test error") - tvm.vm.ctx.ValidatorState = &validatorstest.State{ + vm.ctx.ValidatorState = &validatorstest.State{ // TODO: test both Primary Network / C-Chain and non-Primary Network GetSubnetIDF: func(ctx context.Context, chainID ids.ID) (ids.ID, error) { return ids.Empty, nil @@ -386,15 +388,15 @@ func testWarpVMTransaction(t *testing.T, scheme string, unsignedMessage *avalanc createTx, err := types.SignTx( types.NewContractCreation(0, common.Big0, 7_000_000, big.NewInt(225*utils.GWei), common.Hex2Bytes(exampleWarpBin)), - types.LatestSignerForChainID(tvm.vm.chainConfig.ChainID), - testKeys[0].ToECDSA(), + types.LatestSignerForChainID(vm.chainConfig.ChainID), + vmtest.TestKeys[0].ToECDSA(), ) require.NoError(err) - exampleWarpAddress := crypto.CreateAddress(testEthAddrs[0], 0) + exampleWarpAddress := crypto.CreateAddress(vmtest.TestEthAddrs[0], 0) tx, err := types.SignTx( predicate.NewPredicateTx( - tvm.vm.chainConfig.ChainID, + vm.chainConfig.ChainID, 1, &exampleWarpAddress, 1_000_000, @@ -406,11 +408,11 @@ func testWarpVMTransaction(t *testing.T, scheme string, unsignedMessage *avalanc warpcontract.ContractAddress, signedMessage.Bytes(), ), - types.LatestSignerForChainID(tvm.vm.chainConfig.ChainID), - testKeys[0].ToECDSA(), + types.LatestSignerForChainID(vm.chainConfig.ChainID), + vmtest.TestKeys[0].ToECDSA(), ) require.NoError(err) - errs := tvm.vm.txPool.AddRemotesSync([]*types.Transaction{createTx, tx}) + errs := vm.txPool.AddRemotesSync([]*types.Transaction{createTx, tx}) for i, err := range errs { require.NoError(err, "failed to add tx at index %d", i) } @@ -422,10 +424,12 @@ func testWarpVMTransaction(t *testing.T, scheme string, unsignedMessage *avalanc if validSignature { blockCtx.PChainHeight = minimumValidPChainHeight } - tvm.vm.clock.Set(tvm.vm.clock.Time().Add(2 * time.Second)) - require.Equal(commonEng.PendingTxs, tvm.WaitForEvent(context.Background())) + vm.clock.Set(vm.clock.Time().Add(2 * time.Second)) + msg, err := vm.WaitForEvent(context.Background()) + require.NoError(err) + require.Equal(commonEng.PendingTxs, msg) - warpBlock, err := tvm.vm.BuildBlockWithContext(context.Background(), blockCtx) + warpBlock, err := vm.BuildBlockWithContext(context.Background(), blockCtx) require.NoError(err) warpBlockVerifyWithCtx, ok := warpBlock.(block.WithVerifyContext) @@ -434,18 +438,18 @@ func testWarpVMTransaction(t *testing.T, scheme string, unsignedMessage *avalanc require.NoError(err) require.True(shouldVerifyWithCtx) require.NoError(warpBlockVerifyWithCtx.VerifyWithContext(context.Background(), blockCtx)) - require.NoError(tvm.vm.SetPreference(context.Background(), warpBlock.ID())) + require.NoError(vm.SetPreference(context.Background(), warpBlock.ID())) require.NoError(warpBlock.Accept(context.Background())) - tvm.vm.blockChain.DrainAcceptorQueue() + vm.blockChain.DrainAcceptorQueue() - ethBlock := warpBlock.(*chain.BlockWrapper).Block.(extension.ExtendedBlock).GetEthBlock() - verifiedMessageReceipts := tvm.vm.blockChain.GetReceiptsByHash(ethBlock.Hash()) + ethBlock := warpBlock.(*chain.BlockWrapper).Block.(*wrappedBlock).ethBlock + verifiedMessageReceipts := vm.blockChain.GetReceiptsByHash(ethBlock.Hash()) require.Len(verifiedMessageReceipts, 2) for i, receipt := range verifiedMessageReceipts { require.Equal(types.ReceiptStatusSuccessful, receipt.Status, "index: %d", i) } - tracerAPI := tracers.NewAPI(tvm.vm.eth.APIBackend) + tracerAPI := tracers.NewAPI(vm.eth.APIBackend) txTraceResults, err := tracerAPI.TraceBlockByHash(context.Background(), ethBlock.Hash(), nil) require.NoError(err) require.Len(txTraceResults, 2) @@ -463,7 +467,7 @@ func testWarpVMTransaction(t *testing.T, scheme string, unsignedMessage *avalanc } func TestReceiveWarpMessage(t *testing.T) { - for _, scheme := range schemes { + for _, scheme := range vmtest.Schemes { t.Run(scheme, func(t *testing.T) { testReceiveWarpMessageWithScheme(t, scheme) }) @@ -473,12 +477,13 @@ func TestReceiveWarpMessage(t *testing.T) { func testReceiveWarpMessageWithScheme(t *testing.T, scheme string) { require := require.New(t) fork := upgradetest.Durango - tvm := newVM(t, testVMConfig{ - fork: &fork, - configJSON: getConfig(scheme, ""), + vm := newDefaultTestVM() + vmtest.SetupTestVM(t, vm, vmtest.TestVMConfig{ + Fork: &fork, + Scheme: scheme, }) defer func() { - require.NoError(tvm.vm.Shutdown(context.Background())) + require.NoError(vm.Shutdown(context.Background())) }() // enable warp at the default genesis time @@ -497,7 +502,7 @@ func testReceiveWarpMessageWithScheme(t *testing.T, scheme string) { true, // RequirePrimaryNetworkSigners ) - tvm.vm.chainConfigExtra().UpgradeConfig = extras.UpgradeConfig{ + vm.chainConfigExtra().UpgradeConfig = extras.UpgradeConfig{ PrecompileUpgrades: []extras.PrecompileUpgrade{ {Config: enableConfig}, {Config: disableConfig}, @@ -517,7 +522,7 @@ func testReceiveWarpMessageWithScheme(t *testing.T, scheme string) { tests := []test{ { name: "subnet message should be signed by subnet without RequirePrimaryNetworkSigners", - sourceChainID: tvm.vm.ctx.ChainID, + sourceChainID: vm.ctx.ChainID, msgFrom: fromSubnet, useSigners: signersSubnet, blockTime: upgrade.InitiallyActiveTime, @@ -531,7 +536,7 @@ func testReceiveWarpMessageWithScheme(t *testing.T, scheme string) { }, { name: "C-Chain message should be signed by subnet without RequirePrimaryNetworkSigners", - sourceChainID: tvm.vm.ctx.CChainID, + sourceChainID: vm.ctx.CChainID, msgFrom: fromPrimary, useSigners: signersSubnet, blockTime: upgrade.InitiallyActiveTime.Add(2 * blockGap), @@ -540,7 +545,7 @@ func testReceiveWarpMessageWithScheme(t *testing.T, scheme string) { // by using reEnableTime. { name: "subnet message should be signed by subnet with RequirePrimaryNetworkSigners (unimpacted)", - sourceChainID: tvm.vm.ctx.ChainID, + sourceChainID: vm.ctx.ChainID, msgFrom: fromSubnet, useSigners: signersSubnet, blockTime: reEnableTime, @@ -554,7 +559,7 @@ func testReceiveWarpMessageWithScheme(t *testing.T, scheme string) { }, { name: "C-Chain message should be signed by primary with RequirePrimaryNetworkSigners (impacted)", - sourceChainID: tvm.vm.ctx.CChainID, + sourceChainID: vm.ctx.CChainID, msgFrom: fromPrimary, useSigners: signersPrimary, blockTime: reEnableTime.Add(2 * blockGap), @@ -565,7 +570,7 @@ func testReceiveWarpMessageWithScheme(t *testing.T, scheme string) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { testReceiveWarpMessage( - t, tvm.vm, test.sourceChainID, test.msgFrom, test.useSigners, test.blockTime, + t, vm, test.sourceChainID, test.msgFrom, test.useSigners, test.blockTime, ) }) } @@ -580,7 +585,7 @@ func testReceiveWarpMessage( require := require.New(t) payloadData := avagoUtils.RandomBytes(100) addressedPayload, err := payload.NewAddressedCall( - testEthAddrs[0].Bytes(), + vmtest.TestEthAddrs[0].Bytes(), payloadData, ) require.NoError(err) @@ -688,7 +693,7 @@ func testReceiveWarpMessage( getVerifiedWarpMessageTx, err := types.SignTx( predicate.NewPredicateTx( vm.chainConfig.ChainID, - vm.txPool.Nonce(testEthAddrs[0]), + vm.txPool.Nonce(vmtest.TestEthAddrs[0]), &warpcontract.Module.Address, 1_000_000, big.NewInt(225*utils.GWei), @@ -700,7 +705,7 @@ func testReceiveWarpMessage( signedMessage.Bytes(), ), types.LatestSignerForChainID(vm.chainConfig.ChainID), - testKeys[0].ToECDSA(), + vmtest.TestKeys[0].ToECDSA(), ) require.NoError(err) errs := vm.txPool.AddRemotesSync([]*types.Transaction{getVerifiedWarpMessageTx}) @@ -721,7 +726,7 @@ func testReceiveWarpMessage( require.NoError(err) // Require the block was built with a successful predicate result - ethBlock := block2.(*chain.BlockWrapper).Block.(extension.ExtendedBlock).GetEthBlock() + ethBlock := block2.(*chain.BlockWrapper).Block.(*wrappedBlock).ethBlock rules := params.GetExtra(vm.chainConfig).GetAvalancheRules(ethBlock.Time()) headerPredicateResultsBytes := customheader.PredicateBytesFromExtra(rules, ethBlock.Extra()) results, err := predicate.ParseResults(headerPredicateResultsBytes) @@ -767,7 +772,7 @@ func testReceiveWarpMessage( expectedOutput, err := warpcontract.PackGetVerifiedWarpMessageOutput(warpcontract.GetVerifiedWarpMessageOutput{ Message: warpcontract.WarpMessage{ SourceChainID: common.Hash(sourceChainID), - OriginSenderAddress: testEthAddrs[0], + OriginSenderAddress: vmtest.TestEthAddrs[0], Payload: payloadData, }, Valid: true, @@ -792,7 +797,7 @@ func testReceiveWarpMessage( } func TestSignatureRequestsToVM(t *testing.T) { - for _, scheme := range schemes { + for _, scheme := range vmtest.Schemes { t.Run(scheme, func(t *testing.T) { testSignatureRequestsToVM(t, scheme) }) @@ -801,29 +806,30 @@ func TestSignatureRequestsToVM(t *testing.T) { func testSignatureRequestsToVM(t *testing.T, scheme string) { fork := upgradetest.Durango - tvm := newVM(t, testVMConfig{ - fork: &fork, - configJSON: getConfig(scheme, ""), + vm := newDefaultTestVM() + tvm := vmtest.SetupTestVM(t, vm, vmtest.TestVMConfig{ + Fork: &fork, + Scheme: scheme, }) defer func() { - require.NoError(t, tvm.vm.Shutdown(context.Background())) + require.NoError(t, vm.Shutdown(context.Background())) }() // Setup known message knownPayload, err := payload.NewAddressedCall([]byte{0, 0, 0}, []byte("test")) require.NoError(t, err) - knownWarpMessage, err := avalancheWarp.NewUnsignedMessage(tvm.vm.ctx.NetworkID, tvm.vm.ctx.ChainID, knownPayload.Bytes()) + knownWarpMessage, err := avalancheWarp.NewUnsignedMessage(vm.ctx.NetworkID, vm.ctx.ChainID, knownPayload.Bytes()) require.NoError(t, err) // Add the known message and get its signature to confirm - require.NoError(t, tvm.vm.warpBackend.AddMessage(knownWarpMessage)) - knownMessageSignature, err := tvm.vm.warpBackend.GetMessageSignature(context.TODO(), knownWarpMessage) + require.NoError(t, vm.warpBackend.AddMessage(knownWarpMessage)) + knownMessageSignature, err := vm.warpBackend.GetMessageSignature(context.TODO(), knownWarpMessage) require.NoError(t, err) // Setup known block - lastAcceptedID, err := tvm.vm.LastAccepted(context.Background()) + lastAcceptedID, err := vm.LastAccepted(context.Background()) require.NoError(t, err) - knownBlockSignature, err := tvm.vm.warpBackend.GetBlockSignature(context.TODO(), lastAcceptedID) + knownBlockSignature, err := vm.warpBackend.GetBlockSignature(context.TODO(), lastAcceptedID) require.NoError(t, err) type testCase struct { @@ -844,7 +850,7 @@ func testSignatureRequestsToVM(t *testing.T, scheme string) { message: func() *avalancheWarp.UnsignedMessage { unknownPayload, err := payload.NewAddressedCall([]byte{1, 1, 1}, []byte("unknown")) require.NoError(t, err) - msg, err := avalancheWarp.NewUnsignedMessage(tvm.vm.ctx.NetworkID, tvm.vm.ctx.ChainID, unknownPayload.Bytes()) + msg, err := avalancheWarp.NewUnsignedMessage(vm.ctx.NetworkID, vm.ctx.ChainID, unknownPayload.Bytes()) require.NoError(t, err) return msg }(), @@ -855,7 +861,7 @@ func testSignatureRequestsToVM(t *testing.T, scheme string) { message: func() *avalancheWarp.UnsignedMessage { payload, err := payload.NewHash(lastAcceptedID) require.NoError(t, err) - msg, err := avalancheWarp.NewUnsignedMessage(tvm.vm.ctx.NetworkID, tvm.vm.ctx.ChainID, payload.Bytes()) + msg, err := avalancheWarp.NewUnsignedMessage(vm.ctx.NetworkID, vm.ctx.ChainID, payload.Bytes()) require.NoError(t, err) return msg }(), @@ -866,7 +872,7 @@ func testSignatureRequestsToVM(t *testing.T, scheme string) { message: func() *avalancheWarp.UnsignedMessage { payload, err := payload.NewHash(ids.GenerateTestID()) require.NoError(t, err) - msg, err := avalancheWarp.NewUnsignedMessage(tvm.vm.ctx.NetworkID, tvm.vm.ctx.ChainID, payload.Bytes()) + msg, err := avalancheWarp.NewUnsignedMessage(vm.ctx.NetworkID, vm.ctx.ChainID, payload.Bytes()) require.NoError(t, err) return msg }(), @@ -879,7 +885,7 @@ func testSignatureRequestsToVM(t *testing.T, scheme string) { calledSendAppResponseFn := false calledSendAppErrorFn := false - tvm.appSender.SendAppResponseF = func(ctx context.Context, nodeID ids.NodeID, requestID uint32, responseBytes []byte) error { + tvm.AppSender.SendAppResponseF = func(ctx context.Context, nodeID ids.NodeID, requestID uint32, responseBytes []byte) error { calledSendAppResponseFn = true var response sdk.SignatureResponse if err := proto.Unmarshal(responseBytes, &response); err != nil { @@ -889,7 +895,7 @@ func testSignatureRequestsToVM(t *testing.T, scheme string) { return nil } - tvm.appSender.SendAppErrorF = func(ctx context.Context, nodeID ids.NodeID, requestID uint32, errCode int32, errString string) error { + tvm.AppSender.SendAppErrorF = func(ctx context.Context, nodeID ids.NodeID, requestID uint32, errCode int32, errString string) error { calledSendAppErrorFn = true require.ErrorIs(t, test.err, test.err) return nil @@ -902,7 +908,7 @@ func testSignatureRequestsToVM(t *testing.T, scheme string) { // Send the app request and verify the response deadline := time.Now().Add(60 * time.Second) - appErr := tvm.vm.Network.AppRequest(context.Background(), ids.GenerateTestNodeID(), 1, deadline, msg) + appErr := vm.Network.AppRequest(context.Background(), ids.GenerateTestNodeID(), 1, deadline, msg) require.Nil(t, appErr) if test.err != nil { @@ -915,18 +921,9 @@ func testSignatureRequestsToVM(t *testing.T, scheme string) { } func TestClearWarpDB(t *testing.T) { - ctx, db, genesisBytes, _ := setupGenesis(t, upgradetest.Latest) - innerVM := &VM{} - vm := atomicvm.WrapVM(innerVM) - require.NoError(t, vm.Initialize( - context.Background(), - ctx, - db, - genesisBytes, - []byte{}, - []byte{}, - []*commonEng.Fx{}, - &enginetest.Sender{})) + ctx, db, genesisBytes, _ := vmtest.SetupGenesis(t, upgradetest.Latest) + vm := newDefaultTestVM() + require.NoError(t, vm.Initialize(context.Background(), ctx, db, genesisBytes, []byte{}, []byte{}, []*commonEng.Fx{}, &enginetest.Sender{})) // use multiple messages to test that all messages get cleared payloads := [][]byte{[]byte("test1"), []byte("test2"), []byte("test3"), []byte("test4"), []byte("test5")} @@ -934,11 +931,11 @@ func TestClearWarpDB(t *testing.T) { // add all messages for _, payload := range payloads { - unsignedMsg, err := avalancheWarp.NewUnsignedMessage(vm.Ctx.NetworkID, vm.Ctx.ChainID, payload) + unsignedMsg, err := avalancheWarp.NewUnsignedMessage(vm.ctx.NetworkID, vm.ctx.ChainID, payload) require.NoError(t, err) - require.NoError(t, innerVM.warpBackend.AddMessage(unsignedMsg)) + require.NoError(t, vm.warpBackend.AddMessage(unsignedMsg)) // ensure that the message was added - _, err = innerVM.warpBackend.GetMessageSignature(context.TODO(), unsignedMsg) + _, err = vm.warpBackend.GetMessageSignature(context.TODO(), unsignedMsg) require.NoError(t, err) messages = append(messages, unsignedMsg) } @@ -946,23 +943,14 @@ func TestClearWarpDB(t *testing.T) { require.NoError(t, vm.Shutdown(context.Background())) // Restart VM with the same database default should not prune the warp db - innerVM = &VM{} - vm = atomicvm.WrapVM(innerVM) - // we need new context since the previous one has registered metrics. - ctx, _, _, _ = setupGenesis(t, upgradetest.Latest) - require.NoError(t, vm.Initialize( - context.Background(), - ctx, - db, - genesisBytes, - []byte{}, - []byte{}, - []*commonEng.Fx{}, - &enginetest.Sender{})) + vm = newDefaultTestVM() + // we need to reset context since the previous one has registered metrics. + vmtest.ResetMetrics(ctx) + require.NoError(t, vm.Initialize(context.Background(), ctx, db, genesisBytes, []byte{}, []byte{}, []*commonEng.Fx{}, &enginetest.Sender{})) // check messages are still present for _, message := range messages { - bytes, err := innerVM.warpBackend.GetMessageSignature(context.TODO(), message) + bytes, err := vm.warpBackend.GetMessageSignature(context.TODO(), message) require.NoError(t, err) require.NotEmpty(t, bytes) } @@ -970,27 +958,18 @@ func TestClearWarpDB(t *testing.T) { require.NoError(t, vm.Shutdown(context.Background())) // restart the VM with pruning enabled - innerVM = &VM{} - vm = atomicvm.WrapVM(innerVM) + vm = newDefaultTestVM() config := `{"prune-warp-db-enabled": true}` - ctx, _, _, _ = setupGenesis(t, upgradetest.Latest) - require.NoError(t, vm.Initialize( - context.Background(), - ctx, - db, - genesisBytes, - []byte{}, - []byte(config), - []*commonEng.Fx{}, - &enginetest.Sender{})) - - it := innerVM.warpDB.NewIterator() + vmtest.ResetMetrics(ctx) + require.NoError(t, vm.Initialize(context.Background(), ctx, db, genesisBytes, []byte{}, []byte(config), []*commonEng.Fx{}, &enginetest.Sender{})) + + it := vm.warpDB.NewIterator() require.False(t, it.Next()) it.Release() // ensure all messages have been deleted for _, message := range messages { - _, err := innerVM.warpBackend.GetMessageSignature(context.TODO(), message) + _, err := vm.warpBackend.GetMessageSignature(context.TODO(), message) require.ErrorIs(t, err, &commonEng.AppError{Code: warp.ParseErrCode}) } } diff --git a/plugin/evm/vmtest/genesis.go b/plugin/evm/vmtest/genesis.go new file mode 100644 index 0000000000..2da9eb591f --- /dev/null +++ b/plugin/evm/vmtest/genesis.go @@ -0,0 +1,154 @@ +// Copyright (C) 2019-2025, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. +package vmtest + +import ( + "encoding/json" + "fmt" + "math/big" + "testing" + + avalancheatomic "github.com/ava-labs/avalanchego/chains/atomic" + "github.com/ava-labs/avalanchego/database/memdb" + "github.com/ava-labs/avalanchego/database/prefixdb" + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/snow" + "github.com/ava-labs/avalanchego/snow/snowtest" + "github.com/ava-labs/avalanchego/upgrade" + "github.com/ava-labs/avalanchego/upgrade/upgradetest" + "github.com/ava-labs/avalanchego/utils/crypto/secp256k1" + + "github.com/ava-labs/coreth/core" + "github.com/ava-labs/coreth/params" + "github.com/ava-labs/coreth/params/extras" + "github.com/ava-labs/coreth/plugin/evm/upgrade/ap3" + + "github.com/ava-labs/libevm/common" + "github.com/ava-labs/libevm/core/types" +) + +var ( + TestKeys = secp256k1.TestKeys()[:3] + TestEthAddrs []common.Address // testEthAddrs[i] corresponds to testKeys[i] + TestShortIDAddrs []ids.ShortID + InitialBaseFee = big.NewInt(ap3.InitialBaseFee) + InitialFund = new(big.Int).Mul(big.NewInt(params.Ether), big.NewInt(10)) + + ForkToChainConfig = map[upgradetest.Fork]*params.ChainConfig{ + upgradetest.NoUpgrades: params.TestLaunchConfig, + upgradetest.ApricotPhase1: params.TestApricotPhase1Config, + upgradetest.ApricotPhase2: params.TestApricotPhase2Config, + upgradetest.ApricotPhase3: params.TestApricotPhase3Config, + upgradetest.ApricotPhase4: params.TestApricotPhase4Config, + upgradetest.ApricotPhase5: params.TestApricotPhase5Config, + upgradetest.ApricotPhasePre6: params.TestApricotPhasePre6Config, + upgradetest.ApricotPhase6: params.TestApricotPhase6Config, + upgradetest.ApricotPhasePost6: params.TestApricotPhasePost6Config, + upgradetest.Banff: params.TestBanffChainConfig, + upgradetest.Cortina: params.TestCortinaChainConfig, + upgradetest.Durango: params.TestDurangoChainConfig, + upgradetest.Etna: params.TestEtnaChainConfig, + upgradetest.Fortuna: params.TestFortunaChainConfig, + upgradetest.Granite: params.TestGraniteChainConfig, + } +) + +func init() { + for _, pk := range TestKeys { + TestEthAddrs = append(TestEthAddrs, pk.EthAddress()) + TestShortIDAddrs = append(TestShortIDAddrs, pk.Address()) + } +} + +// GenesisJSON returns the JSON representation of the genesis block +// for the given chain configuration, with pre-funded accounts. +func GenesisJSON(cfg *params.ChainConfig) string { + g := NewTestGenesis(cfg) + b, err := json.Marshal(g) + if err != nil { + panic(err) + } + return string(b) +} + +func NewTestGenesis(cfg *params.ChainConfig) *core.Genesis { + g := new(core.Genesis) + g.Difficulty = big.NewInt(0) + g.GasLimit = 0x5f5e100 + g.Timestamp = uint64(upgrade.InitiallyActiveTime.Unix()) + + // Use chainId: 43111, so that it does not overlap with any Avalanche ChainIDs, which may have their + // config overridden in vm.Initialize. + cpy := *cfg + cpy.ChainID = big.NewInt(43111) + g.Config = &cpy + + allocStr := `{"0100000000000000000000000000000000000000":{"code":"0x7300000000000000000000000000000000000000003014608060405260043610603d5760003560e01c80631e010439146042578063b6510bb314606e575b600080fd5b605c60048036036020811015605657600080fd5b503560b1565b60408051918252519081900360200190f35b818015607957600080fd5b5060af60048036036080811015608e57600080fd5b506001600160a01b03813516906020810135906040810135906060013560b6565b005b30cd90565b836001600160a01b031681836108fc8690811502906040516000604051808303818888878c8acf9550505050505015801560f4573d6000803e3d6000fd5b505050505056fea26469706673582212201eebce970fe3f5cb96bf8ac6ba5f5c133fc2908ae3dcd51082cfee8f583429d064736f6c634300060a0033","balance":"0x0"}}` + json.Unmarshal([]byte(allocStr), &g.Alloc) + // After Durango, an additional account is funded in tests to use + // with warp messages. + if params.GetExtra(cfg).IsDurango(0) { + addr := common.HexToAddress("0x99b9DEA54C48Dfea6aA9A4Ca4623633EE04ddbB5") + g.Alloc[addr] = types.Account{Balance: InitialFund} + } + + // Fund the test keys + for _, ethAddr := range TestEthAddrs { + g.Alloc[ethAddr] = types.Account{Balance: InitialFund} + } + + return g +} + +func NewPrefundedGenesis( + balance int, + addresses ...common.Address, +) *core.Genesis { + alloc := types.GenesisAlloc{} + for _, address := range addresses { + alloc[address] = types.Account{ + Balance: big.NewInt(int64(balance)), + } + } + + return &core.Genesis{ + Config: params.TestChainConfig, + Difficulty: big.NewInt(0), + Alloc: alloc, + } +} + +// SetupGenesis sets up the genesis +func SetupGenesis( + t *testing.T, + fork upgradetest.Fork, +) (*snow.Context, + *prefixdb.Database, + []byte, + *avalancheatomic.Memory, +) { + ctx := snowtest.Context(t, snowtest.CChainID) + ctx.NetworkUpgrades = upgradetest.GetConfig(fork) + + baseDB := memdb.New() + + // initialize the atomic memory + atomicMemory := avalancheatomic.NewMemory(prefixdb.New([]byte{0}, baseDB)) + ctx.SharedMemory = atomicMemory.NewSharedMemory(ctx.ChainID) + + // NB: this lock is intentionally left locked when this function returns. + // The caller of this function is responsible for unlocking. + ctx.Lock.Lock() + + prefixedDB := prefixdb.New([]byte{1}, baseDB) + genesisJSON := GenesisJSON(ForkToChainConfig[fork]) + return ctx, prefixedDB, []byte(genesisJSON), atomicMemory +} + +func ForkToRules(fork upgradetest.Fork) *extras.Rules { + chainConfig, ok := ForkToChainConfig[fork] + if !ok { + panic(fmt.Sprintf("unknown fork: %s", fork)) + } + return params.GetRulesExtra(chainConfig.Rules(common.Big0, params.IsMergeTODO, 0)) +} diff --git a/plugin/evm/vmtest/test_syncervm.go b/plugin/evm/vmtest/test_syncervm.go new file mode 100644 index 0000000000..7d5fd46637 --- /dev/null +++ b/plugin/evm/vmtest/test_syncervm.go @@ -0,0 +1,684 @@ +// Copyright (C) 2019-2025, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. +package vmtest + +import ( + "context" + "fmt" + "math/big" + "math/rand" + "sync" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + avalancheatomic "github.com/ava-labs/avalanchego/chains/atomic" + avalanchedatabase "github.com/ava-labs/avalanchego/database" + "github.com/ava-labs/avalanchego/database/prefixdb" + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/snow" + commonEng "github.com/ava-labs/avalanchego/snow/engine/common" + "github.com/ava-labs/avalanchego/snow/engine/enginetest" + "github.com/ava-labs/avalanchego/snow/engine/snowman/block" + "github.com/ava-labs/avalanchego/upgrade/upgradetest" + "github.com/ava-labs/avalanchego/utils/set" + "github.com/ava-labs/avalanchego/vms/components/chain" + + "github.com/ava-labs/coreth/consensus/dummy" + "github.com/ava-labs/coreth/constants" + "github.com/ava-labs/coreth/core" + "github.com/ava-labs/coreth/core/coretest" + "github.com/ava-labs/coreth/plugin/evm/customrawdb" + "github.com/ava-labs/coreth/plugin/evm/customtypes" + "github.com/ava-labs/coreth/plugin/evm/database" + "github.com/ava-labs/coreth/plugin/evm/extension" + "github.com/ava-labs/coreth/predicate" + statesyncclient "github.com/ava-labs/coreth/sync/client" + "github.com/ava-labs/coreth/sync/statesync/statesynctest" + vmsync "github.com/ava-labs/coreth/sync/vm" + "github.com/ava-labs/coreth/utils/utilstest" + + "github.com/ava-labs/libevm/common" + "github.com/ava-labs/libevm/core/rawdb" + "github.com/ava-labs/libevm/core/types" + "github.com/ava-labs/libevm/ethdb" + "github.com/ava-labs/libevm/log" + "github.com/ava-labs/libevm/rlp" + "github.com/ava-labs/libevm/trie" +) + +type SyncerVMTest struct { + Name string + TestFunc func( + t *testing.T, + testSetup *SyncTestSetup, + ) +} + +var SyncerVMTests = []SyncerVMTest{ + { + Name: "SkipStateSyncTest", + TestFunc: SkipStateSyncTest, + }, + { + Name: "StateSyncFromScratchTest", + TestFunc: StateSyncFromScratchTest, + }, + { + Name: "StateSyncFromScratchExceedParentTest", + TestFunc: StateSyncFromScratchExceedParentTest, + }, + { + Name: "StateSyncToggleEnabledToDisabledTest", + TestFunc: StateSyncToggleEnabledToDisabledTest, + }, + { + Name: "VMShutdownWhileSyncingTest", + TestFunc: VMShutdownWhileSyncingTest, + }, +} + +func SkipStateSyncTest(t *testing.T, testSetup *SyncTestSetup) { + rand.Seed(1) + test := SyncTestParams{ + SyncableInterval: 256, + StateSyncMinBlocks: 300, // must be greater than [syncableInterval] to skip sync + SyncMode: block.StateSyncSkipped, + } + testSyncVMSetup := initSyncServerAndClientVMs(t, test, vmsync.BlocksToFetch, testSetup) + + testSyncerVM(t, testSyncVMSetup, test, testSetup.ExtraSyncerVMTest) +} + +func StateSyncFromScratchTest(t *testing.T, testSetup *SyncTestSetup) { + rand.Seed(1) + test := SyncTestParams{ + SyncableInterval: 256, + StateSyncMinBlocks: 50, // must be less than [syncableInterval] to perform sync + SyncMode: block.StateSyncStatic, + } + testSyncVMSetup := initSyncServerAndClientVMs(t, test, vmsync.BlocksToFetch, testSetup) + + testSyncerVM(t, testSyncVMSetup, test, testSetup.ExtraSyncerVMTest) +} + +func StateSyncFromScratchExceedParentTest(t *testing.T, testSetup *SyncTestSetup) { + rand.Seed(1) + numToGen := vmsync.BlocksToFetch + uint64(32) + test := SyncTestParams{ + SyncableInterval: numToGen, + StateSyncMinBlocks: 50, // must be less than [syncableInterval] to perform sync + SyncMode: block.StateSyncStatic, + } + testSyncVMSetup := initSyncServerAndClientVMs(t, test, int(numToGen), testSetup) + + testSyncerVM(t, testSyncVMSetup, test, testSetup.ExtraSyncerVMTest) +} + +func StateSyncToggleEnabledToDisabledTest(t *testing.T, testSetup *SyncTestSetup) { + rand.Seed(1) + var lock sync.Mutex + reqCount := 0 + test := SyncTestParams{ + SyncableInterval: 256, + StateSyncMinBlocks: 50, // must be less than [syncableInterval] to perform sync + SyncMode: block.StateSyncStatic, + responseIntercept: func(syncerVM extension.InnerVM, nodeID ids.NodeID, requestID uint32, response []byte) { + lock.Lock() + defer lock.Unlock() + + reqCount++ + // Fail all requests after number 50 to interrupt the sync + if reqCount > 50 { + if err := syncerVM.AppRequestFailed(context.Background(), nodeID, requestID, commonEng.ErrTimeout); err != nil { + panic(err) + } + if err := syncerVM.SyncerClient().Shutdown(); err != nil { + panic(err) + } + } else { + require.NoError(t, syncerVM.AppResponse(context.Background(), nodeID, requestID, response)) + } + }, + expectedErr: context.Canceled, + } + testSyncVMSetup := initSyncServerAndClientVMs(t, test, vmsync.BlocksToFetch, testSetup) + + // Perform sync resulting in early termination. + testSyncerVM(t, testSyncVMSetup, test, testSetup.ExtraSyncerVMTest) + + test.SyncMode = block.StateSyncStatic + test.responseIntercept = nil + test.expectedErr = nil + + syncDisabledVM, _ := testSetup.NewVM() + appSender := &enginetest.Sender{T: t} + appSender.SendAppGossipF = func(context.Context, commonEng.SendConfig, []byte) error { return nil } + appSender.SendAppRequestF = func(ctx context.Context, nodeSet set.Set[ids.NodeID], requestID uint32, request []byte) error { + nodeID, hasItem := nodeSet.Pop() + if !hasItem { + t.Fatal("expected nodeSet to contain at least 1 nodeID") + } + go testSyncVMSetup.serverVM.VM.AppRequest(ctx, nodeID, requestID, time.Now().Add(1*time.Second), request) + return nil + } + ResetMetrics(testSyncVMSetup.syncerVM.SnowCtx) + stateSyncDisabledConfigJSON := `{"state-sync-enabled":false}` + genesisJSON := []byte(GenesisJSON(ForkToChainConfig[upgradetest.Latest])) + if err := syncDisabledVM.Initialize( + context.Background(), + testSyncVMSetup.syncerVM.SnowCtx, + testSyncVMSetup.syncerVM.DB, + genesisJSON, + nil, + []byte(stateSyncDisabledConfigJSON), + []*commonEng.Fx{}, + appSender, + ); err != nil { + t.Fatal(err) + } + + defer func() { + if err := syncDisabledVM.Shutdown(context.Background()); err != nil { + t.Fatal(err) + } + }() + + if height := syncDisabledVM.LastAcceptedExtendedBlock().Height(); height != 0 { + t.Fatalf("Unexpected last accepted height: %d", height) + } + + enabled, err := syncDisabledVM.StateSyncEnabled(context.Background()) + assert.NoError(t, err) + assert.False(t, enabled, "sync should be disabled") + + // Process the first 10 blocks from the serverVM + for i := uint64(1); i < 10; i++ { + ethBlock := testSyncVMSetup.serverVM.VM.Ethereum().BlockChain().GetBlockByNumber(i) + if ethBlock == nil { + t.Fatalf("VM Server did not have a block available at height %d", i) + } + b, err := rlp.EncodeToBytes(ethBlock) + if err != nil { + t.Fatal(err) + } + blk, err := syncDisabledVM.ParseBlock(context.Background(), b) + if err != nil { + t.Fatal(err) + } + if err := blk.Verify(context.Background()); err != nil { + t.Fatal(err) + } + if err := blk.Accept(context.Background()); err != nil { + t.Fatal(err) + } + } + // Verify the snapshot disk layer matches the last block root + lastRoot := syncDisabledVM.Ethereum().BlockChain().CurrentBlock().Root + if err := syncDisabledVM.Ethereum().BlockChain().Snapshots().Verify(lastRoot); err != nil { + t.Fatal(err) + } + syncDisabledVM.Ethereum().BlockChain().DrainAcceptorQueue() + + // Create a new VM from the same database with state sync enabled. + syncReEnabledVM, _ := testSetup.NewVM() + // Enable state sync in configJSON + configJSON := fmt.Sprintf( + `{"state-sync-enabled":true, "state-sync-min-blocks":%d}`, + test.StateSyncMinBlocks, + ) + ResetMetrics(testSyncVMSetup.syncerVM.SnowCtx) + if err := syncReEnabledVM.Initialize( + context.Background(), + testSyncVMSetup.syncerVM.SnowCtx, + testSyncVMSetup.syncerVM.DB, + genesisJSON, + nil, + []byte(configJSON), + []*commonEng.Fx{}, + appSender, + ); err != nil { + t.Fatal(err) + } + + // override [serverVM]'s SendAppResponse function to trigger AppResponse on [syncerVM] + testSyncVMSetup.serverVM.AppSender.SendAppResponseF = func(ctx context.Context, nodeID ids.NodeID, requestID uint32, response []byte) error { + if test.responseIntercept == nil { + go syncReEnabledVM.AppResponse(ctx, nodeID, requestID, response) + } else { + go test.responseIntercept(syncReEnabledVM, nodeID, requestID, response) + } + + return nil + } + + // connect peer to [syncerVM] + assert.NoError(t, syncReEnabledVM.Connected( + context.Background(), + testSyncVMSetup.serverVM.SnowCtx.NodeID, + statesyncclient.StateSyncVersion, + )) + + enabled, err = syncReEnabledVM.StateSyncEnabled(context.Background()) + assert.NoError(t, err) + assert.True(t, enabled, "sync should be enabled") + + testSyncVMSetup.syncerVM.VM = syncReEnabledVM + testSyncerVM(t, testSyncVMSetup, test, testSetup.ExtraSyncerVMTest) +} + +func VMShutdownWhileSyncingTest(t *testing.T, testSetup *SyncTestSetup) { + var ( + lock sync.Mutex + testSyncVMSetup *testSyncVMSetup + ) + reqCount := 0 + test := SyncTestParams{ + SyncableInterval: 256, + StateSyncMinBlocks: 50, // must be less than [syncableInterval] to perform sync + SyncMode: block.StateSyncStatic, + responseIntercept: func(syncerVM extension.InnerVM, nodeID ids.NodeID, requestID uint32, response []byte) { + lock.Lock() + defer lock.Unlock() + + reqCount++ + // Shutdown the VM after 50 requests to interrupt the sync + if reqCount == 50 { + // Note this verifies the VM shutdown does not time out while syncing. + require.NoError(t, testSyncVMSetup.syncerVM.shutdownOnceSyncerVM.Shutdown(context.Background())) + } else if reqCount < 50 { + require.NoError(t, syncerVM.AppResponse(context.Background(), nodeID, requestID, response)) + } + }, + expectedErr: context.Canceled, + } + testSyncVMSetup = initSyncServerAndClientVMs(t, test, vmsync.BlocksToFetch, testSetup) + // Perform sync resulting in early termination. + testSyncerVM(t, testSyncVMSetup, test, testSetup.ExtraSyncerVMTest) +} + +type SyncTestSetup struct { + NewVM func() (extension.InnerVM, dummy.ConsensusCallbacks) // should not be initialized + AfterInit func(t *testing.T, testParams SyncTestParams, vmSetup SyncVMSetup, isServer bool) + GenFn func(i int, vm extension.InnerVM, gen *core.BlockGen) + ExtraSyncerVMTest func(t *testing.T, syncerVM SyncVMSetup) +} + +func initSyncServerAndClientVMs(t *testing.T, test SyncTestParams, numBlocks int, testSetup *SyncTestSetup) *testSyncVMSetup { + require := require.New(t) + + // override commitInterval so the call to trie creates a commit at the height [syncableInterval]. + // This is necessary to support fetching a state summary. + config := fmt.Sprintf(`{"commit-interval": %d, "state-sync-commit-interval": %d}`, test.SyncableInterval, test.SyncableInterval) + serverVM, cb := testSetup.NewVM() + fork := upgradetest.Latest + serverTest := SetupTestVM(t, serverVM, TestVMConfig{ + Fork: &fork, + ConfigJSON: config, + }) + t.Cleanup(func() { + log.Info("Shutting down server VM") + require.NoError(serverVM.Shutdown(context.Background())) + }) + serverVmSetup := SyncVMSetup{ + VM: serverVM, + AppSender: serverTest.AppSender, + SnowCtx: serverTest.Ctx, + ConsensusCallbacks: cb, + DB: serverTest.DB, + AtomicMemory: serverTest.AtomicMemory, + } + var err error + if testSetup.AfterInit != nil { + testSetup.AfterInit(t, test, serverVmSetup, true) + } + generateAndAcceptBlocks(t, serverVM, numBlocks, testSetup.GenFn, nil, cb) + + // make some accounts + root, accounts := statesynctest.FillAccountsWithOverlappingStorage(t, serverVM.Ethereum().BlockChain().TrieDB(), types.EmptyRootHash, 1000, 16) + + // patch serverVM's lastAcceptedBlock to have the new root + // and update the vm's state so the trie with accounts will + // be returned by StateSyncGetLastSummary + lastAccepted := serverVM.Ethereum().BlockChain().LastAcceptedBlock() + patchedBlock := patchBlock(lastAccepted, root, serverVM.Ethereum().ChainDb()) + blockBytes, err := rlp.EncodeToBytes(patchedBlock) + require.NoError(err) + internalWrappedBlock, err := serverVM.ParseBlock(context.Background(), blockBytes) + require.NoError(err) + internalBlock, ok := internalWrappedBlock.(*chain.BlockWrapper) + require.True(ok) + require.NoError(serverVM.SetLastAcceptedBlock(internalBlock.Block)) + + // initialise [syncerVM] with blank genesis state + // we also override [syncerVM]'s commit interval so the atomic trie works correctly. + stateSyncEnabledJSON := fmt.Sprintf(`{"state-sync-enabled":true, "state-sync-min-blocks": %d, "tx-lookup-limit": %d, "commit-interval": %d}`, test.StateSyncMinBlocks, 4, test.SyncableInterval) + + syncerVM, syncerCB := testSetup.NewVM() + syncerTest := SetupTestVM(t, syncerVM, TestVMConfig{ + Fork: &fork, + ConfigJSON: stateSyncEnabledJSON, + IsSyncing: true, + }) + shutdownOnceSyncerVM := &shutdownOnceVM{InnerVM: syncerVM} + t.Cleanup(func() { + require.NoError(shutdownOnceSyncerVM.Shutdown(context.Background())) + }) + syncerVmSetup := syncerVMSetup{ + SyncVMSetup: SyncVMSetup{ + VM: syncerVM, + ConsensusCallbacks: syncerCB, + SnowCtx: syncerTest.Ctx, + DB: syncerTest.DB, + AtomicMemory: syncerTest.AtomicMemory, + }, + shutdownOnceSyncerVM: shutdownOnceSyncerVM, + } + if testSetup.AfterInit != nil { + testSetup.AfterInit(t, test, syncerVmSetup.SyncVMSetup, false) + } + require.NoError(syncerVM.SetState(context.Background(), snow.StateSyncing)) + enabled, err := syncerVM.StateSyncEnabled(context.Background()) + require.NoError(err) + require.True(enabled) + + // override [serverVM]'s SendAppResponse function to trigger AppResponse on [syncerVM] + serverTest.AppSender.SendAppResponseF = func(ctx context.Context, nodeID ids.NodeID, requestID uint32, response []byte) error { + if test.responseIntercept == nil { + go syncerVM.AppResponse(ctx, nodeID, requestID, response) + } else { + go test.responseIntercept(syncerVM, nodeID, requestID, response) + } + + return nil + } + + // connect peer to [syncerVM] + require.NoError( + syncerVM.Connected( + context.Background(), + serverTest.Ctx.NodeID, + statesyncclient.StateSyncVersion, + ), + ) + + // override [syncerVM]'s SendAppRequest function to trigger AppRequest on [serverVM] + syncerTest.AppSender.SendAppRequestF = func(ctx context.Context, nodeSet set.Set[ids.NodeID], requestID uint32, request []byte) error { + nodeID, hasItem := nodeSet.Pop() + require.True(hasItem, "expected nodeSet to contain at least 1 nodeID") + require.NoError(serverVM.AppRequest(ctx, nodeID, requestID, time.Now().Add(1*time.Second), request)) + return nil + } + + return &testSyncVMSetup{ + serverVM: SyncVMSetup{ + VM: serverVM, + AppSender: serverTest.AppSender, + SnowCtx: serverTest.Ctx, + }, + fundedAccounts: accounts, + syncerVM: syncerVmSetup, + } +} + +// testSyncVMSetup contains the required set up for a client VM to perform state sync +// off of a server VM. +type testSyncVMSetup struct { + serverVM SyncVMSetup + syncerVM syncerVMSetup + + fundedAccounts map[*utilstest.Key]*types.StateAccount +} + +type SyncVMSetup struct { + VM extension.InnerVM + SnowCtx *snow.Context + ConsensusCallbacks dummy.ConsensusCallbacks + DB avalanchedatabase.Database + AtomicMemory *avalancheatomic.Memory + AppSender *enginetest.Sender +} + +type syncerVMSetup struct { + SyncVMSetup + shutdownOnceSyncerVM *shutdownOnceVM +} + +type shutdownOnceVM struct { + extension.InnerVM + shutdownOnce sync.Once +} + +func (vm *shutdownOnceVM) Shutdown(ctx context.Context) error { + var err error + vm.shutdownOnce.Do(func() { err = vm.InnerVM.Shutdown(ctx) }) + return err +} + +// SyncTestParams contains both the actual VMs as well as the parameters with the expected output. +type SyncTestParams struct { + responseIntercept func(vm extension.InnerVM, nodeID ids.NodeID, requestID uint32, response []byte) + StateSyncMinBlocks uint64 + SyncableInterval uint64 + SyncMode block.StateSyncMode + expectedErr error +} + +func testSyncerVM(t *testing.T, testSyncVMSetup *testSyncVMSetup, test SyncTestParams, extraSyncerVMTest func(t *testing.T, syncerVMSetup SyncVMSetup)) { + t.Helper() + var ( + require = require.New(t) + serverVM = testSyncVMSetup.serverVM.VM + fundedAccounts = testSyncVMSetup.fundedAccounts + syncerVM = testSyncVMSetup.syncerVM.VM + ) + // get last summary and test related methods + summary, err := serverVM.GetLastStateSummary(context.Background()) + require.NoError(err, "error getting state sync last summary") + parsedSummary, err := syncerVM.ParseStateSummary(context.Background(), summary.Bytes()) + require.NoError(err, "error parsing state summary") + retrievedSummary, err := serverVM.GetStateSummary(context.Background(), parsedSummary.Height()) + require.NoError(err, "error getting state sync summary at height") + require.Equal(summary, retrievedSummary) + + syncMode, err := parsedSummary.Accept(context.Background()) + require.NoError(err, "error accepting state summary") + require.Equal(test.SyncMode, syncMode) + if syncMode == block.StateSyncSkipped { + return + } + + msg, err := syncerVM.WaitForEvent(context.Background()) + require.NoError(err) + require.Equal(commonEng.StateSyncDone, msg) + + // If the test is expected to error, assert the correct error is returned and finish the test. + err = syncerVM.SyncerClient().Error() + if test.expectedErr != nil { + require.ErrorIs(err, test.expectedErr) + // Note we re-open the database here to avoid a closed error when the test is for a shutdown VM. + // TODO: this avoids circular dependencies but is not ideal. + ethDBPrefix := []byte("ethdb") + chaindb := database.WrapDatabase(prefixdb.NewNested(ethDBPrefix, testSyncVMSetup.syncerVM.DB)) + assertSyncPerformedHeights(t, chaindb, map[uint64]struct{}{}) + return + } + require.NoError(err, "state sync failed") + + // set [syncerVM] to bootstrapping and verify the last accepted block has been updated correctly + // and that we can bootstrap and process some blocks. + require.NoError(syncerVM.SetState(context.Background(), snow.Bootstrapping)) + require.Equal(serverVM.LastAcceptedExtendedBlock().Height(), syncerVM.LastAcceptedExtendedBlock().Height(), "block height mismatch between syncer and server") + require.Equal(serverVM.LastAcceptedExtendedBlock().ID(), syncerVM.LastAcceptedExtendedBlock().ID(), "blockID mismatch between syncer and server") + require.True(syncerVM.Ethereum().BlockChain().HasState(syncerVM.Ethereum().BlockChain().LastAcceptedBlock().Root()), "unavailable state for last accepted block") + assertSyncPerformedHeights(t, syncerVM.Ethereum().ChainDb(), map[uint64]struct{}{retrievedSummary.Height(): {}}) + + lastNumber := syncerVM.Ethereum().BlockChain().LastAcceptedBlock().NumberU64() + // check the last block is indexed + lastSyncedBlock := rawdb.ReadBlock(syncerVM.Ethereum().ChainDb(), rawdb.ReadCanonicalHash(syncerVM.Ethereum().ChainDb(), lastNumber), lastNumber) + require.NotNil(lastSyncedBlock, "last synced block not found") + for _, tx := range lastSyncedBlock.Transactions() { + index := rawdb.ReadTxLookupEntry(syncerVM.Ethereum().ChainDb(), tx.Hash()) + require.NotNilf(index, "Miss transaction indices, number %d hash %s", lastNumber, tx.Hash().Hex()) + } + + // tail should be the last block synced + if syncerVM.Ethereum().BlockChain().CacheConfig().TransactionHistory != 0 { + tail := lastSyncedBlock.NumberU64() + + coretest.CheckTxIndices(t, &tail, tail, tail, tail, syncerVM.Ethereum().ChainDb(), true) + } + + blocksToBuild := 10 + txsPerBlock := 10 + toAddress := TestEthAddrs[1] // arbitrary choice + generateAndAcceptBlocks(t, syncerVM, blocksToBuild, func(_ int, vm extension.InnerVM, gen *core.BlockGen) { + b, err := predicate.NewResults().Bytes() + if err != nil { + t.Fatal(err) + } + gen.AppendExtra(b) + i := 0 + for k := range fundedAccounts { + tx := types.NewTransaction(gen.TxNonce(k.Address), toAddress, big.NewInt(1), 21000, InitialBaseFee, nil) + signedTx, err := types.SignTx(tx, types.NewEIP155Signer(vm.Ethereum().BlockChain().Config().ChainID), k.PrivateKey) + require.NoError(err) + gen.AddTx(signedTx) + i++ + if i >= txsPerBlock { + break + } + } + }, + func(block *types.Block) { + if syncerVM.Ethereum().BlockChain().CacheConfig().TransactionHistory != 0 { + tail := block.NumberU64() - syncerVM.Ethereum().BlockChain().CacheConfig().TransactionHistory + 1 + // tail should be the minimum last synced block, since we skipped it to the last block + if tail < lastSyncedBlock.NumberU64() { + tail = lastSyncedBlock.NumberU64() + } + coretest.CheckTxIndices(t, &tail, tail, block.NumberU64(), block.NumberU64(), syncerVM.Ethereum().ChainDb(), true) + } + }, + testSyncVMSetup.syncerVM.ConsensusCallbacks, + ) + + // check we can transition to [NormalOp] state and continue to process blocks. + require.NoError(syncerVM.SetState(context.Background(), snow.NormalOp)) + + // Generate blocks after we have entered normal consensus as well + generateAndAcceptBlocks(t, syncerVM, blocksToBuild, func(_ int, vm extension.InnerVM, gen *core.BlockGen) { + b, err := predicate.NewResults().Bytes() + if err != nil { + t.Fatal(err) + } + gen.AppendExtra(b) + i := 0 + for k := range fundedAccounts { + tx := types.NewTransaction(gen.TxNonce(k.Address), toAddress, big.NewInt(1), 21000, InitialBaseFee, nil) + signedTx, err := types.SignTx(tx, types.NewEIP155Signer(vm.Ethereum().BlockChain().Config().ChainID), k.PrivateKey) + require.NoError(err) + gen.AddTx(signedTx) + i++ + if i >= txsPerBlock { + break + } + } + }, + func(block *types.Block) { + if syncerVM.Ethereum().BlockChain().CacheConfig().TransactionHistory != 0 { + tail := block.NumberU64() - syncerVM.Ethereum().BlockChain().CacheConfig().TransactionHistory + 1 + // tail should be the minimum last synced block, since we skipped it to the last block + if tail < lastSyncedBlock.NumberU64() { + tail = lastSyncedBlock.NumberU64() + } + coretest.CheckTxIndices(t, &tail, tail, block.NumberU64(), block.NumberU64(), syncerVM.Ethereum().ChainDb(), true) + } + }, + testSyncVMSetup.syncerVM.ConsensusCallbacks, + ) + + if extraSyncerVMTest != nil { + extraSyncerVMTest(t, testSyncVMSetup.syncerVM.SyncVMSetup) + } +} + +// patchBlock returns a copy of [blk] with [root] and updates [db] to +// include the new block as canonical for [blk]'s height. +// This breaks the digestibility of the chain since after this call +// [blk] does not necessarily define a state transition from its parent +// state to the new state root. +func patchBlock(blk *types.Block, root common.Hash, db ethdb.Database) *types.Block { + header := blk.Header() + header.Root = root + receipts := rawdb.ReadRawReceipts(db, blk.Hash(), blk.NumberU64()) + newBlk := customtypes.NewBlockWithExtData( + header, blk.Transactions(), blk.Uncles(), receipts, trie.NewStackTrie(nil), customtypes.BlockExtData(blk), true, + ) + rawdb.WriteBlock(db, newBlk) + rawdb.WriteCanonicalHash(db, newBlk.Hash(), newBlk.NumberU64()) + return newBlk +} + +// generateAndAcceptBlocks uses [core.GenerateChain] to generate blocks, then +// calls Verify and Accept on each generated block +// TODO: consider using this helper function in vm_test.go and elsewhere in this package to clean up tests +func generateAndAcceptBlocks(t *testing.T, vm extension.InnerVM, numBlocks int, gen func(int, extension.InnerVM, *core.BlockGen), accepted func(*types.Block), cb dummy.ConsensusCallbacks) { + t.Helper() + + // acceptExternalBlock defines a function to parse, verify, and accept a block once it has been + // generated by GenerateChain + acceptExternalBlock := func(block *types.Block) { + bytes, err := rlp.EncodeToBytes(block) + if err != nil { + t.Fatal(err) + } + extendedBlock, err := vm.ParseBlock(context.Background(), bytes) + if err != nil { + t.Fatal(err) + } + if err := extendedBlock.Verify(context.Background()); err != nil { + t.Fatal(err) + } + if err := extendedBlock.Accept(context.Background()); err != nil { + t.Fatal(err) + } + + if accepted != nil { + accepted(block) + } + } + _, _, err := core.GenerateChain( + vm.Ethereum().BlockChain().Config(), + vm.Ethereum().BlockChain().LastAcceptedBlock(), + dummy.NewFakerWithCallbacks(cb), + vm.Ethereum().ChainDb(), + numBlocks, + 10, + func(i int, g *core.BlockGen) { + g.SetOnBlockGenerated(acceptExternalBlock) + g.SetCoinbase(constants.BlackholeAddr) // necessary for syntactic validation of the block + gen(i, vm, g) + }, + ) + if err != nil { + t.Fatal(err) + } + vm.Ethereum().BlockChain().DrainAcceptorQueue() +} + +// assertSyncPerformedHeights iterates over all heights the VM has synced to and +// verifies they all match the heights present in `expected`. +func assertSyncPerformedHeights(t *testing.T, db ethdb.Iteratee, expected map[uint64]struct{}) { + it := customrawdb.NewSyncPerformedIterator(db) + defer it.Release() + + found := make(map[uint64]struct{}, len(expected)) + for it.Next() { + found[customrawdb.UnpackSyncPerformedKey(it.Key())] = struct{}{} + } + require.NoError(t, it.Error()) + require.Equal(t, expected, found) +} diff --git a/plugin/evm/vmtest/test_vm.go b/plugin/evm/vmtest/test_vm.go new file mode 100644 index 0000000000..00cc1bd138 --- /dev/null +++ b/plugin/evm/vmtest/test_vm.go @@ -0,0 +1,131 @@ +// Copyright (C) 2019-2025, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package vmtest + +import ( + "context" + "encoding/json" + "testing" + + "github.com/ava-labs/avalanchego/api/metrics" + avalancheatomic "github.com/ava-labs/avalanchego/chains/atomic" + "github.com/ava-labs/avalanchego/database/prefixdb" + "github.com/ava-labs/avalanchego/snow" + commonEng "github.com/ava-labs/avalanchego/snow/engine/common" + commoneng "github.com/ava-labs/avalanchego/snow/engine/common" + "github.com/ava-labs/avalanchego/snow/engine/enginetest" + "github.com/ava-labs/avalanchego/upgrade/upgradetest" + "github.com/ava-labs/coreth/plugin/evm/customrawdb" + "github.com/ava-labs/coreth/utils/utilstest" + "github.com/ava-labs/libevm/core/rawdb" + "github.com/stretchr/testify/require" +) + +var Schemes = []string{rawdb.HashScheme, customrawdb.FirewoodScheme} + +type TestVMConfig struct { + IsSyncing bool + Fork *upgradetest.Fork + // If genesisJSON is empty, defaults to the genesis corresponding to the + // fork. + GenesisJSON string + ConfigJSON string + // DB scheme, defaults to HashScheme + Scheme string +} + +type TestVMSuite struct { + VM commoneng.VM + DB *prefixdb.Database + AtomicMemory *avalancheatomic.Memory + AppSender *enginetest.Sender + Ctx *snow.Context +} + +// SetupTestVM initializes a VM for testing. It sets up the genesis and returns the +// issuer channel, database, atomic memory, app sender, and context. +// Expects the passed VM to be a uninitialized VM. +func SetupTestVM(t *testing.T, vm commoneng.VM, config TestVMConfig) *TestVMSuite { + fork := upgradetest.Latest + if config.Fork != nil { + fork = *config.Fork + } + snowtCtx, dbManager, genesisBytes, m := SetupGenesis(t, fork) + if len(config.GenesisJSON) != 0 { + genesisBytes = []byte(config.GenesisJSON) + } + appSender := &enginetest.Sender{ + T: t, + CantSendAppGossip: true, + SendAppGossipF: func(context.Context, commonEng.SendConfig, []byte) error { return nil }, + } + + scheme := config.Scheme + if len(scheme) == 0 { + scheme = rawdb.HashScheme + } + + configJSON, err := OverrideSchemeConfig(scheme, config.ConfigJSON) + require.NoError(t, err) + + ctx, cancel := utilstest.NewTestContext(t) + defer cancel() + + err = vm.Initialize( + ctx, + snowtCtx, + dbManager, + genesisBytes, + nil, + []byte(configJSON), + nil, + appSender, + ) + require.NoError(t, err, "error initializing GenesisVM") + + if !config.IsSyncing { + require.NoError(t, vm.SetState(ctx, snow.Bootstrapping)) + require.NoError(t, vm.SetState(ctx, snow.NormalOp)) + } + + return &TestVMSuite{ + VM: vm, + DB: dbManager, + AtomicMemory: m, + AppSender: appSender, + Ctx: snowtCtx, + } +} + +// ResetMetrics resets the vm avalanchego metrics, and allows +// for the VM to be re-initialized in tests. +func ResetMetrics(snowCtx *snow.Context) { + snowCtx.Metrics = metrics.NewPrefixGatherer() +} + +func OverrideSchemeConfig(scheme string, configJSON string) (string, error) { + // If the scheme is not Firewood, return the configJSON as is + if scheme != customrawdb.FirewoodScheme { + return configJSON, nil + } + + // Parse existing config into a map to preserve only non-zero values + configMap := make(map[string]interface{}) + if len(configJSON) > 0 { + if err := json.Unmarshal([]byte(configJSON), &configMap); err != nil { + return "", err + } + } + + // Set Firewood-specific configuration flags (these will override any existing values) + configMap["state-scheme"] = customrawdb.FirewoodScheme + configMap["snapshot-cache"] = 0 + configMap["pruning-enabled"] = true + configMap["state-sync-enabled"] = false + configMap["metrics-expensive-enabled"] = false + + // Marshal back to JSON + result, err := json.Marshal(configMap) + return string(result), err +} diff --git a/utils/utilstest/context.go b/utils/utilstest/context.go new file mode 100644 index 0000000000..b28de6e212 --- /dev/null +++ b/utils/utilstest/context.go @@ -0,0 +1,45 @@ +// Copyright (C) 2019-2025, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package utilstest + +import ( + "context" + "sync" + "testing" + "time" +) + +func NewTestContext(t *testing.T) (context.Context, context.CancelFunc) { + t.Helper() + if d, ok := t.Deadline(); ok { + return context.WithDeadline(context.Background(), d) + } + return context.WithTimeout(context.Background(), 30*time.Second) +} + +func WaitGroupWithContext(t *testing.T, ctx context.Context, wg *sync.WaitGroup) { + t.Helper() + done := make(chan struct{}) + go func() { + wg.Wait() + close(done) + }() + select { + case <-ctx.Done(): + // include context error for easier debugging + t.Fatalf("timeout waiting for response: %v", ctx.Err()) + case <-done: + } +} + +func SleepWithContext(ctx context.Context, d time.Duration) { + timer := time.NewTimer(d) + defer timer.Stop() + select { + case <-ctx.Done(): + return + case <-timer.C: + return + } +} From 359dc449dcb355898e4e39ae4199e629cdd75af4 Mon Sep 17 00:00:00 2001 From: Austin Larson <78000745+alarso16@users.noreply.github.com> Date: Thu, 14 Aug 2025 12:30:05 -0700 Subject: [PATCH 12/20] fix: license header file (#1122) --- consensus/misc/eip4844/eip4844_test.go | 10 +++++++++- log/handler.go | 9 +++++++++ log/logger.go | 9 +++++++++ scripts/upstream_files.txt | 24 +++++++++++++++++------- 4 files changed, 44 insertions(+), 8 deletions(-) diff --git a/consensus/misc/eip4844/eip4844_test.go b/consensus/misc/eip4844/eip4844_test.go index 2f72a6d943..2c4fd87063 100644 --- a/consensus/misc/eip4844/eip4844_test.go +++ b/consensus/misc/eip4844/eip4844_test.go @@ -1,6 +1,14 @@ // Copyright (C) 2019-2025, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. - +// +// This file is a derived work, based on the go-ethereum library whose original +// notices appear below. +// +// It is distributed under a license compatible with the licensing terms of the +// original code from which it is derived. +// +// Much love to the original authors for their work. +// ********** // Copyright 2023 The go-ethereum Authors // This file is part of the go-ethereum library. // diff --git a/log/handler.go b/log/handler.go index dcf3d90c15..518cd6b557 100644 --- a/log/handler.go +++ b/log/handler.go @@ -1,5 +1,14 @@ // Copyright (C) 2019-2025, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. +// +// This file is a derived work, based on the go-ethereum library whose original +// notices appear below. +// +// It is distributed under a license compatible with the licensing terms of the +// original code from which it is derived. +// +// Much love to the original authors for their work. +// ********** package log diff --git a/log/logger.go b/log/logger.go index f9079be2d4..2a28811438 100644 --- a/log/logger.go +++ b/log/logger.go @@ -1,5 +1,14 @@ // Copyright (C) 2019-2025, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. +// +// This file is a derived work, based on the go-ethereum library whose original +// notices appear below. +// +// It is distributed under a license compatible with the licensing terms of the +// original code from which it is derived. +// +// Much love to the original authors for their work. +// ********** package log diff --git a/scripts/upstream_files.txt b/scripts/upstream_files.txt index 565331c561..8f58fc78d2 100644 --- a/scripts/upstream_files.txt +++ b/scripts/upstream_files.txt @@ -1,12 +1,21 @@ +accounts/* +consensus/* core/* eth/* -node/* internal/* +log/* +miner/* +node/* +signer/* +tests/* +triedb/* -!internal/ethapi/api_extra_test.go +!accounts/abi/abi_extra_test.go +!accounts/abi/bind/bind_extra_test.go +!consensus/dummy/* !core/blockchain_ext.go -!core/blockchain_log_test.go !core/blockchain_ext_test.go +!core/blockchain_log_test.go !core/bounded_buffer.go !core/coretest/* !core/extstate/* @@ -14,14 +23,15 @@ internal/* !core/main_test.go !core/predicate_check.go !core/predicate_check_test.go -!core/state/firewood_database.go -!core/state/database_test.go !core/state/snapshot/snapshot_ext.go -!core/state/statedb_multicoin_test.go !core/state_manager_test.go !core/state_processor_ext.go !eth/chain_with_final_block.go !eth/gasprice/fee_info_provider_test.go !internal/ethapi/api.coreth.go !internal/ethapi/api.coreth_test.go -!internal/ethapi/api_extra.go \ No newline at end of file +!internal/ethapi/api_extra.go +!internal/ethapi/api_extra_test.go +!tests/utils/* +!tests/warp/* +!triedb/firewood/* \ No newline at end of file From d0929a8cb48027814089e36a54c4b0559d68a54d Mon Sep 17 00:00:00 2001 From: Austin Larson <78000745+alarso16@users.noreply.github.com> Date: Fri, 15 Aug 2025 13:17:11 -0700 Subject: [PATCH 13/20] chore: more license fixes (#1123) --- cmd/abigen/namefilter.go | 2 ++ cmd/abigen/namefilter_test.go | 2 ++ cmd/utils/cmd.go | 10 +++++++++- cmd/utils/flags.go | 10 +++++++++- ethclient/simulated/backend.go | 10 +++++++++- ethclient/simulated/backend_test.go | 10 +++++++++- ethclient/simulated/options.go | 10 +++++++++- ethclient/simulated/options_test.go | 10 +++++++++- plugin/evm/customtypes/rlp_fuzzer_test.go | 10 +++++++++- rpc/client_opt_test.go | 2 ++ scripts/upstream_files.txt | 15 +++++++++++++++ 11 files changed, 84 insertions(+), 7 deletions(-) diff --git a/cmd/abigen/namefilter.go b/cmd/abigen/namefilter.go index 0dbafbc53f..1785c5b76b 100644 --- a/cmd/abigen/namefilter.go +++ b/cmd/abigen/namefilter.go @@ -8,6 +8,8 @@ // original code from which it is derived. // // Much love to the original authors for their work. +// ********** + package main import ( diff --git a/cmd/abigen/namefilter_test.go b/cmd/abigen/namefilter_test.go index 53a4868994..1cb691c0f9 100644 --- a/cmd/abigen/namefilter_test.go +++ b/cmd/abigen/namefilter_test.go @@ -8,6 +8,8 @@ // original code from which it is derived. // // Much love to the original authors for their work. +// ********** + package main import ( diff --git a/cmd/utils/cmd.go b/cmd/utils/cmd.go index b29876da4d..5d1857d477 100644 --- a/cmd/utils/cmd.go +++ b/cmd/utils/cmd.go @@ -1,6 +1,14 @@ // Copyright (C) 2019-2025, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. - +// +// This file is a derived work, based on the go-ethereum library whose original +// notices appear below. +// +// It is distributed under a license compatible with the licensing terms of the +// original code from which it is derived. +// +// Much love to the original authors for their work. +// ********** // Copyright 2014 The go-ethereum Authors // This file is part of go-ethereum. // diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index cf80445589..6903ad53de 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -1,6 +1,14 @@ // Copyright (C) 2019-2025, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. - +// +// This file is a derived work, based on the go-ethereum library whose original +// notices appear below. +// +// It is distributed under a license compatible with the licensing terms of the +// original code from which it is derived. +// +// Much love to the original authors for their work. +// ********** // Copyright 2015 The go-ethereum Authors // This file is part of go-ethereum. // diff --git a/ethclient/simulated/backend.go b/ethclient/simulated/backend.go index b9b40c5166..5b0a5ecd94 100644 --- a/ethclient/simulated/backend.go +++ b/ethclient/simulated/backend.go @@ -1,6 +1,14 @@ // Copyright (C) 2019-2025, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. - +// +// This file is a derived work, based on the go-ethereum library whose original +// notices appear below. +// +// It is distributed under a license compatible with the licensing terms of the +// original code from which it is derived. +// +// Much love to the original authors for their work. +// ********** // Copyright 2023 The go-ethereum Authors // This file is part of the go-ethereum library. // diff --git a/ethclient/simulated/backend_test.go b/ethclient/simulated/backend_test.go index c7a90065a8..5504315f29 100644 --- a/ethclient/simulated/backend_test.go +++ b/ethclient/simulated/backend_test.go @@ -1,6 +1,14 @@ // Copyright (C) 2019-2025, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. - +// +// This file is a derived work, based on the go-ethereum library whose original +// notices appear below. +// +// It is distributed under a license compatible with the licensing terms of the +// original code from which it is derived. +// +// Much love to the original authors for their work. +// ********** // Copyright 2019 The go-ethereum Authors // This file is part of the go-ethereum library. // diff --git a/ethclient/simulated/options.go b/ethclient/simulated/options.go index b2025c86d4..e8a2744c0b 100644 --- a/ethclient/simulated/options.go +++ b/ethclient/simulated/options.go @@ -1,6 +1,14 @@ // Copyright (C) 2019-2025, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. - +// +// This file is a derived work, based on the go-ethereum library whose original +// notices appear below. +// +// It is distributed under a license compatible with the licensing terms of the +// original code from which it is derived. +// +// Much love to the original authors for their work. +// ********** // Copyright 2024 The go-ethereum Authors // This file is part of the go-ethereum library. // diff --git a/ethclient/simulated/options_test.go b/ethclient/simulated/options_test.go index fd9c5eee73..ac856e221d 100644 --- a/ethclient/simulated/options_test.go +++ b/ethclient/simulated/options_test.go @@ -1,6 +1,14 @@ // Copyright (C) 2019-2025, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. - +// +// This file is a derived work, based on the go-ethereum library whose original +// notices appear below. +// +// It is distributed under a license compatible with the licensing terms of the +// original code from which it is derived. +// +// Much love to the original authors for their work. +// ********** // Copyright 2024 The go-ethereum Authors // This file is part of the go-ethereum library. // diff --git a/plugin/evm/customtypes/rlp_fuzzer_test.go b/plugin/evm/customtypes/rlp_fuzzer_test.go index b7d89eb121..18f7871c20 100644 --- a/plugin/evm/customtypes/rlp_fuzzer_test.go +++ b/plugin/evm/customtypes/rlp_fuzzer_test.go @@ -1,6 +1,14 @@ // Copyright (C) 2019-2025, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. - +// +// This file is a derived work, based on the go-ethereum library whose original +// notices appear below. +// +// It is distributed under a license compatible with the licensing terms of the +// original code from which it is derived. +// +// Much love to the original authors for their work. +// ********** // Copyright 2019 The go-ethereum Authors // This file is part of the go-ethereum library. // diff --git a/rpc/client_opt_test.go b/rpc/client_opt_test.go index f6e0d39a43..51e12e86f4 100644 --- a/rpc/client_opt_test.go +++ b/rpc/client_opt_test.go @@ -8,6 +8,8 @@ // original code from which it is derived. // // Much love to the original authors for their work. +// ********** + package rpc_test import ( diff --git a/scripts/upstream_files.txt b/scripts/upstream_files.txt index 8f58fc78d2..295a3fadfa 100644 --- a/scripts/upstream_files.txt +++ b/scripts/upstream_files.txt @@ -1,11 +1,24 @@ accounts/* consensus/* +cmd/* core/* eth/* +ethclient/* internal/* log/* miner/* node/* +params/config_test.go +params/config.go +params/denomination.go +params/network_params.go +params/protocol_params.go +params/version.go +plugin/evm/customtypes/block_test.go +plugin/evm/customtypes/hashing_test.go +plugin/evm/customtypes/rlp_fuzzer_test.go +plugin/evm/customtypes/types_test.go +rpc/* signer/* tests/* triedb/* @@ -13,6 +26,7 @@ triedb/* !accounts/abi/abi_extra_test.go !accounts/abi/bind/bind_extra_test.go !consensus/dummy/* +!cmd/simulator/* !core/blockchain_ext.go !core/blockchain_ext_test.go !core/blockchain_log_test.go @@ -28,6 +42,7 @@ triedb/* !core/state_processor_ext.go !eth/chain_with_final_block.go !eth/gasprice/fee_info_provider_test.go +!ethclient/ethclient_ext.go !internal/ethapi/api.coreth.go !internal/ethapi/api.coreth_test.go !internal/ethapi/api_extra.go From 63bf72b1189dbf52c09719dfdd4e64b504bd1f80 Mon Sep 17 00:00:00 2001 From: Ceyhun Onur Date: Mon, 18 Aug 2025 18:52:17 +0300 Subject: [PATCH 14/20] prepare for v0.15.4 (#1124) --- RELEASES.md | 5 +++-- plugin/evm/version.go | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/RELEASES.md b/RELEASES.md index df8e4ac3da..69db07d29f 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -2,6 +2,8 @@ ## Pending Release +- Removed deprecated flags `coreth-admin-api-enabled`, `coreth-admin-api-dir`, `tx-regossip-frequency`, `tx-lookup-limit`. Use `admin-api-enabled`, `admin-api-dir`, `regossip-frequency`, `transaction-history` instead. + ## [v0.15.3](https://github.com/ava-labs/coreth/releases/tag/v0.15.3) - Removed legacy warp message handlers in favor of ACP-118 SDK handlers. @@ -10,10 +12,9 @@ - Moves atomic operations from plugin/evm to plugin/evm/atomic and wraps the plugin/evm/VM in `atomicvm` to separate the atomic operations from the EVM execution. - Demoted unnecessary error log in `core/txpool/legacypool.go` to warning, displaying unexpected but valid behavior. - Removed the `snowman-api-enabled` flag and the corresponding API implementation. -- Enable expermiental `state-scheme` flag to specify Firewood as a state database. +- Enable experimental `state-scheme` flag to specify Firewood as a state database. - Added prometheus metrics for Firewood if it is enabled and expensive metrics are being used. - Disable incompatible APIs for Firewood. -- Removed deprecated flags `coreth-admin-api-enabled`, `coreth-admin-api-dir`, `tx-regossip-frequency`, `tx-lookup-limit`. Use `admin-api-enabled`, `admin-api-dir`, `regossip-frequency`, `transaction-history` instead. ## [v0.15.2](https://github.com/ava-labs/coreth/releases/tag/v0.15.2) diff --git a/plugin/evm/version.go b/plugin/evm/version.go index f8a0b32f8e..759b63b7ec 100644 --- a/plugin/evm/version.go +++ b/plugin/evm/version.go @@ -9,7 +9,7 @@ var ( // GitCommit is set by the build script GitCommit string // Version is the version of Coreth - Version string = "v0.15.3" + Version string = "v0.15.4" ) func init() { From 65fbc572f7293580934f350a8ccfdfd1ae0f794b Mon Sep 17 00:00:00 2001 From: Ceyhun Onur Date: Mon, 18 Aug 2025 18:53:05 +0300 Subject: [PATCH 15/20] Vm test helpers (#1125) --- plugin/evm/atomic/vm/vm_test.go | 40 +-- plugin/evm/vm_test.go | 608 +++----------------------------- plugin/evm/vm_warp_test.go | 11 +- plugin/evm/vmtest/test_vm.go | 45 +++ 4 files changed, 89 insertions(+), 615 deletions(-) diff --git a/plugin/evm/atomic/vm/vm_test.go b/plugin/evm/atomic/vm/vm_test.go index d9fa556e7b..0605540475 100644 --- a/plugin/evm/atomic/vm/vm_test.go +++ b/plugin/evm/atomic/vm/vm_test.go @@ -717,28 +717,9 @@ func testConflictingTransitiveAncestryWithGap(t *testing.T, scheme string) { } // Add the remote transactions, build the block, and set VM1's preference for block A - errs := vm.Ethereum().TxPool().AddRemotesSync([]*types.Transaction{signedTx}) - for i, err := range errs { - if err != nil { - t.Fatalf("Failed to add transaction to VM1 at index %d: %s", i, err) - } - } - - msg, err = vm.WaitForEvent(context.Background()) - require.NoError(t, err) - require.Equal(t, commonEng.PendingTxs, msg) - - blk1, err := vm.BuildBlock(context.Background()) + _, err = vmtest.IssueTxsAndSetPreference([]*types.Transaction{signedTx}, vm) if err != nil { - t.Fatalf("Failed to build blk1: %s", err) - } - - if err := blk1.Verify(context.Background()); err != nil { - t.Fatalf("blk1 failed verification due to %s", err) - } - - if err := vm.SetPreference(context.Background(), blk1.ID()); err != nil { - t.Fatal(err) + t.Fatalf("Failed to issue txs and build blk1: %s", err) } importTx1, err := vm.newImportTx(vm.Ctx.XChainID, key.Address, vmtest.InitialBaseFee, []*secp256k1.PrivateKey{key1}) @@ -1741,26 +1722,11 @@ func testBuildApricotPhase4Block(t *testing.T, scheme string) { } txs[i] = signedTx } - errs := vm.Ethereum().TxPool().AddRemotesSync(txs) - for i, err := range errs { - if err != nil { - t.Fatalf("Failed to add tx at index %d: %s", i, err) - } - } - - msg, err = vm.WaitForEvent(context.Background()) - require.NoError(t, err) - require.Equal(t, commonEng.PendingTxs, msg) - - blk, err = vm.BuildBlock(context.Background()) + blk, err = vmtest.IssueTxsAndBuild(txs, vm) if err != nil { t.Fatal(err) } - if err := blk.Verify(context.Background()); err != nil { - t.Fatal(err) - } - if err := blk.Accept(context.Background()); err != nil { t.Fatal(err) } diff --git a/plugin/evm/vm_test.go b/plugin/evm/vm_test.go index f483a71a9f..500ad7b455 100644 --- a/plugin/evm/vm_test.go +++ b/plugin/evm/vm_test.go @@ -218,28 +218,9 @@ func testBuildEthTxBlock(t *testing.T, scheme string) { if err != nil { t.Fatal(err) } - errs := vm.txPool.AddRemotesSync([]*types.Transaction{signedTx}) - for i, err := range errs { - if err != nil { - t.Fatalf("Failed to add tx at index %d: %s", i, err) - } - } - - msg, err := vm.WaitForEvent(context.Background()) - require.NoError(t, err) - require.Equal(t, commonEng.PendingTxs, msg) - - blk1, err := vm.BuildBlock(context.Background()) + blk1, err := vmtest.IssueTxsAndSetPreference([]*types.Transaction{signedTx}, vm) if err != nil { - t.Fatal(err) - } - - if err := blk1.Verify(context.Background()); err != nil { - t.Fatal(err) - } - - if err := vm.SetPreference(context.Background(), blk1.ID()); err != nil { - t.Fatal(err) + t.Fatalf("Failed to issue txs and build block: %s", err) } if err := blk1.Accept(context.Background()); err != nil { @@ -260,24 +241,9 @@ func testBuildEthTxBlock(t *testing.T, scheme string) { } txs[i] = signedTx } - errs = vm.txPool.AddRemotesSync(txs) - for i, err := range errs { - if err != nil { - t.Fatalf("Failed to add tx at index %d: %s", i, err) - } - } - - msg, err = vm.WaitForEvent(context.Background()) - require.NoError(t, err) - require.Equal(t, commonEng.PendingTxs, msg) - - blk2, err := vm.BuildBlock(context.Background()) + blk2, err := vmtest.IssueTxsAndSetPreference(txs, vm) if err != nil { - t.Fatal(err) - } - - if err := blk2.Verify(context.Background()); err != nil { - t.Fatal(err) + t.Fatalf("Failed to issue txs and build block: %s", err) } if err := blk2.Accept(context.Background()); err != nil { @@ -401,30 +367,11 @@ func testSetPreferenceRace(t *testing.T, scheme string) { if err != nil { t.Fatal(err) } - errs := vm1.txPool.AddRemotesSync([]*types.Transaction{signedTx}) - for i, err := range errs { - if err != nil { - t.Fatalf("Failed to add tx at index %d: %s", i, err) - } - } - - msg, err := vm1.WaitForEvent(context.Background()) - require.NoError(t, err) - require.Equal(t, commonEng.PendingTxs, msg) - - vm1BlkA, err := vm1.BuildBlock(context.Background()) + vm1BlkA, err := vmtest.IssueTxsAndSetPreference([]*types.Transaction{signedTx}, vm1) if err != nil { t.Fatalf("Failed to build block with transaction: %s", err) } - if err := vm1BlkA.Verify(context.Background()); err != nil { - t.Fatalf("Block failed verification on VM1: %s", err) - } - - if err := vm1.SetPreference(context.Background(), vm1BlkA.ID()); err != nil { - t.Fatal(err) - } - vm2BlkA, err := vm2.ParseBlock(context.Background(), vm1BlkA.Bytes()) if err != nil { t.Fatalf("Unexpected error parsing block from vm2: %s", err) @@ -465,87 +412,30 @@ func testSetPreferenceRace(t *testing.T, scheme string) { } // Add the remote transactions, build the block, and set VM1's preference for block A - errs = vm1.txPool.AddRemotesSync(txs) - for i, err := range errs { - if err != nil { - t.Fatalf("Failed to add transaction to VM1 at index %d: %s", i, err) - } - } - - msg, err = vm1.WaitForEvent(context.Background()) - require.NoError(t, err) - require.Equal(t, commonEng.PendingTxs, msg) - - vm1BlkB, err := vm1.BuildBlock(context.Background()) + _, err = vmtest.IssueTxsAndSetPreference(txs, vm1) if err != nil { t.Fatal(err) } - if err := vm1BlkB.Verify(context.Background()); err != nil { - t.Fatal(err) - } - - if err := vm1.SetPreference(context.Background(), vm1BlkB.ID()); err != nil { - t.Fatal(err) - } - // Split the transactions over two blocks, and set VM2's preference to them in sequence // after building each block // Block C - errs = vm2.txPool.AddRemotesSync(txs[0:5]) - for i, err := range errs { - if err != nil { - t.Fatalf("Failed to add transaction to VM2 at index %d: %s", i, err) - } - } - - msg, err = vm2.WaitForEvent(context.Background()) - require.NoError(t, err) - require.Equal(t, commonEng.PendingTxs, msg) - - vm2BlkC, err := vm2.BuildBlock(context.Background()) + vm2BlkC, err := vmtest.IssueTxsAndSetPreference(txs[0:5], vm2) if err != nil { t.Fatalf("Failed to build BlkC on VM2: %s", err) } - if err := vm2BlkC.Verify(context.Background()); err != nil { - t.Fatalf("BlkC failed verification on VM2: %s", err) - } - - if err := vm2.SetPreference(context.Background(), vm2BlkC.ID()); err != nil { - t.Fatal(err) - } - newHead = <-newTxPoolHeadChan2 if newHead.Head.Hash() != common.Hash(vm2BlkC.ID()) { t.Fatalf("Expected new block to match") } // Block D - errs = vm2.txPool.AddRemotesSync(txs[5:10]) - for i, err := range errs { - if err != nil { - t.Fatalf("Failed to add transaction to VM2 at index %d: %s", i, err) - } - } - - msg, err = vm2.WaitForEvent(context.Background()) - require.NoError(t, err) - require.Equal(t, commonEng.PendingTxs, msg) - - vm2BlkD, err := vm2.BuildBlock(context.Background()) + vm2BlkD, err := vmtest.IssueTxsAndSetPreference(txs[5:10], vm2) if err != nil { t.Fatalf("Failed to build BlkD on VM2: %s", err) } - if err := vm2BlkD.Verify(context.Background()); err != nil { - t.Fatalf("BlkD failed verification on VM2: %s", err) - } - - if err := vm2.SetPreference(context.Background(), vm2BlkD.ID()); err != nil { - t.Fatal(err) - } - // VM1 receives blkC and blkD from VM1 // and happens to call SetPreference on blkD without ever calling SetPreference // on blkC @@ -660,18 +550,7 @@ func testReorgProtection(t *testing.T, scheme string) { if err != nil { t.Fatal(err) } - errs := vm1.txPool.AddRemotesSync([]*types.Transaction{signedTx}) - for i, err := range errs { - if err != nil { - t.Fatalf("Failed to add tx at index %d: %s", i, err) - } - } - - msg, err := vm1.WaitForEvent(context.Background()) - require.NoError(t, err) - require.Equal(t, commonEng.PendingTxs, msg) - - vm1BlkA, err := vm1.BuildBlock(context.Background()) + vm1BlkA, err := vmtest.IssueTxsAndSetPreference([]*types.Transaction{signedTx}, vm1) if err != nil { t.Fatalf("Failed to build block with transaction: %s", err) } @@ -724,53 +603,19 @@ func testReorgProtection(t *testing.T, scheme string) { } // Add the remote transactions, build the block, and set VM1's preference for block A - errs = vm1.txPool.AddRemotesSync(txs) - for i, err := range errs { - if err != nil { - t.Fatalf("Failed to add transaction to VM1 at index %d: %s", i, err) - } - } - - msg, err = vm1.WaitForEvent(context.Background()) - require.NoError(t, err) - require.Equal(t, commonEng.PendingTxs, msg) - - vm1BlkB, err := vm1.BuildBlock(context.Background()) + vm1BlkB, err := vmtest.IssueTxsAndSetPreference(txs, vm1) if err != nil { t.Fatal(err) } - if err := vm1BlkB.Verify(context.Background()); err != nil { - t.Fatal(err) - } - - if err := vm1.SetPreference(context.Background(), vm1BlkB.ID()); err != nil { - t.Fatal(err) - } - // Split the transactions over two blocks, and set VM2's preference to them in sequence // after building each block // Block C - errs = vm2.txPool.AddRemotesSync(txs[0:5]) - for i, err := range errs { - if err != nil { - t.Fatalf("Failed to add transaction to VM2 at index %d: %s", i, err) - } - } - - msg, err = vm2.WaitForEvent(context.Background()) - require.NoError(t, err) - require.Equal(t, commonEng.PendingTxs, msg) - - vm2BlkC, err := vm2.BuildBlock(context.Background()) + vm2BlkC, err := vmtest.IssueTxsAndBuild(txs[0:5], vm2) if err != nil { t.Fatalf("Failed to build BlkC on VM2: %s", err) } - if err := vm2BlkC.Verify(context.Background()); err != nil { - t.Fatalf("Block failed verification on VM2: %s", err) - } - vm1BlkC, err := vm1.ParseBlock(context.Background(), vm2BlkC.Bytes()) if err != nil { t.Fatalf("Unexpected error parsing block from vm2: %s", err) @@ -846,26 +691,11 @@ func testNonCanonicalAccept(t *testing.T, scheme string) { if err != nil { t.Fatal(err) } - errs := vm1.txPool.AddRemotesSync([]*types.Transaction{signedTx}) - for i, err := range errs { - if err != nil { - t.Fatalf("Failed to add tx at index %d: %s", i, err) - } - } - - msg, err := vm1.WaitForEvent(context.Background()) - require.NoError(t, err) - require.Equal(t, commonEng.PendingTxs, msg) - - vm1BlkA, err := vm1.BuildBlock(context.Background()) + vm1BlkA, err := vmtest.IssueTxsAndBuild([]*types.Transaction{signedTx}, vm1) if err != nil { t.Fatalf("Failed to build block with transaction: %s", err) } - if err := vm1BlkA.Verify(context.Background()); err != nil { - t.Fatalf("Block failed verification on VM1: %s", err) - } - if _, err := vm1.GetBlockIDAtHeight(context.Background(), vm1BlkA.Height()); err != database.ErrNotFound { t.Fatalf("Expected unaccepted block not to be indexed by height, but found %s", err) } @@ -927,26 +757,11 @@ func testNonCanonicalAccept(t *testing.T, scheme string) { } // Add the remote transactions, build the block, and set VM1's preference for block A - errs = vm1.txPool.AddRemotesSync(txs) - for i, err := range errs { - if err != nil { - t.Fatalf("Failed to add transaction to VM1 at index %d: %s", i, err) - } - } - - msg, err = vm1.WaitForEvent(context.Background()) - require.NoError(t, err) - require.Equal(t, commonEng.PendingTxs, msg) - - vm1BlkB, err := vm1.BuildBlock(context.Background()) + vm1BlkB, err := vmtest.IssueTxsAndBuild(txs, vm1) if err != nil { t.Fatal(err) } - if err := vm1BlkB.Verify(context.Background()); err != nil { - t.Fatal(err) - } - if _, err := vm1.GetBlockIDAtHeight(context.Background(), vm1BlkB.Height()); err != database.ErrNotFound { t.Fatalf("Expected unaccepted block not to be indexed by height, but found %s", err) } @@ -963,18 +778,7 @@ func testNonCanonicalAccept(t *testing.T, scheme string) { t.Fatalf("expected block at %d to have hash %s but got %s", blkBHeight, blkBHash.Hex(), b.Hash().Hex()) } - errs = vm2.txPool.AddRemotesSync(txs[0:5]) - for i, err := range errs { - if err != nil { - t.Fatalf("Failed to add transaction to VM2 at index %d: %s", i, err) - } - } - - msg, err = vm2.WaitForEvent(context.Background()) - require.NoError(t, err) - require.Equal(t, commonEng.PendingTxs, msg) - - vm2BlkC, err := vm2.BuildBlock(context.Background()) + vm2BlkC, err := vmtest.IssueTxsAndBuild(txs[0:5], vm2) if err != nil { t.Fatalf("Failed to build BlkC on VM2: %s", err) } @@ -1059,30 +863,11 @@ func testStickyPreference(t *testing.T, scheme string) { if err != nil { t.Fatal(err) } - errs := vm1.txPool.AddRemotesSync([]*types.Transaction{signedTx}) - for i, err := range errs { - if err != nil { - t.Fatalf("Failed to add tx at index %d: %s", i, err) - } - } - - msg, err := vm1.WaitForEvent(context.Background()) - require.NoError(t, err) - require.Equal(t, commonEng.PendingTxs, msg) - - vm1BlkA, err := vm1.BuildBlock(context.Background()) + vm1BlkA, err := vmtest.IssueTxsAndSetPreference([]*types.Transaction{signedTx}, vm1) if err != nil { t.Fatalf("Failed to build block with transaction: %s", err) } - if err := vm1BlkA.Verify(context.Background()); err != nil { - t.Fatalf("Block failed verification on VM1: %s", err) - } - - if err := vm1.SetPreference(context.Background(), vm1BlkA.ID()); err != nil { - t.Fatal(err) - } - vm2BlkA, err := vm2.ParseBlock(context.Background(), vm1BlkA.Bytes()) if err != nil { t.Fatalf("Unexpected error parsing block from vm2: %s", err) @@ -1123,30 +908,11 @@ func testStickyPreference(t *testing.T, scheme string) { } // Add the remote transactions, build the block, and set VM1's preference for block A - errs = vm1.txPool.AddRemotesSync(txs) - for i, err := range errs { - if err != nil { - t.Fatalf("Failed to add transaction to VM1 at index %d: %s", i, err) - } - } - - msg, err = vm1.WaitForEvent(context.Background()) - require.NoError(t, err) - require.Equal(t, commonEng.PendingTxs, msg) - - vm1BlkB, err := vm1.BuildBlock(context.Background()) + vm1BlkB, err := vmtest.IssueTxsAndSetPreference(txs, vm1) if err != nil { t.Fatal(err) } - if err := vm1BlkB.Verify(context.Background()); err != nil { - t.Fatal(err) - } - - if err := vm1.SetPreference(context.Background(), vm1BlkB.ID()); err != nil { - t.Fatal(err) - } - vm1.eth.APIBackend.SetAllowUnfinalizedQueries(true) blkBHeight := vm1BlkB.Height() @@ -1155,47 +921,17 @@ func testStickyPreference(t *testing.T, scheme string) { t.Fatalf("expected block at %d to have hash %s but got %s", blkBHeight, blkBHash.Hex(), b.Hash().Hex()) } - errs = vm2.txPool.AddRemotesSync(txs[0:5]) - for i, err := range errs { - if err != nil { - t.Fatalf("Failed to add transaction to VM2 at index %d: %s", i, err) - } - } - - msg, err = vm2.WaitForEvent(context.Background()) - require.NoError(t, err) - require.Equal(t, commonEng.PendingTxs, msg) - - vm2BlkC, err := vm2.BuildBlock(context.Background()) + vm2BlkC, err := vmtest.IssueTxsAndSetPreference(txs[0:5], vm2) if err != nil { t.Fatalf("Failed to build BlkC on VM2: %s", err) } - if err := vm2BlkC.Verify(context.Background()); err != nil { - t.Fatalf("BlkC failed verification on VM2: %s", err) - } - - if err := vm2.SetPreference(context.Background(), vm2BlkC.ID()); err != nil { - t.Fatal(err) - } - newHead = <-newTxPoolHeadChan2 if newHead.Head.Hash() != common.Hash(vm2BlkC.ID()) { t.Fatalf("Expected new block to match") } - errs = vm2.txPool.AddRemotesSync(txs[5:]) - for i, err := range errs { - if err != nil { - t.Fatalf("Failed to add transaction to VM2 at index %d: %s", i, err) - } - } - - msg, err = vm2.WaitForEvent(context.Background()) - require.NoError(t, err) - require.Equal(t, commonEng.PendingTxs, msg) - - vm2BlkD, err := vm2.BuildBlock(context.Background()) + vm2BlkD, err := vmtest.IssueTxsAndBuild(txs[5:], vm2) if err != nil { t.Fatalf("Failed to build BlkD on VM2: %s", err) } @@ -1337,30 +1073,11 @@ func testUncleBlock(t *testing.T, scheme string) { if err != nil { t.Fatal(err) } - errs := vm1.txPool.AddRemotesSync([]*types.Transaction{signedTx}) - for i, err := range errs { - if err != nil { - t.Fatalf("Failed to add tx at index %d: %s", i, err) - } - } - - msg, err := vm1.WaitForEvent(context.Background()) - require.NoError(t, err) - require.Equal(t, commonEng.PendingTxs, msg) - - vm1BlkA, err := vm1.BuildBlock(context.Background()) + vm1BlkA, err := vmtest.IssueTxsAndSetPreference([]*types.Transaction{signedTx}, vm1) if err != nil { t.Fatalf("Failed to build block with transaction: %s", err) } - if err := vm1BlkA.Verify(context.Background()); err != nil { - t.Fatalf("Block failed verification on VM1: %s", err) - } - - if err := vm1.SetPreference(context.Background(), vm1BlkA.ID()); err != nil { - t.Fatal(err) - } - vm2BlkA, err := vm2.ParseBlock(context.Background(), vm1BlkA.Bytes()) if err != nil { t.Fatalf("Unexpected error parsing block from vm2: %s", err) @@ -1398,71 +1115,22 @@ func testUncleBlock(t *testing.T, scheme string) { txs[i] = signedTx } - errs = vm1.txPool.AddRemotesSync(txs) - for i, err := range errs { - if err != nil { - t.Fatalf("Failed to add transaction to VM1 at index %d: %s", i, err) - } - } - - msg, err = vm1.WaitForEvent(context.Background()) - require.NoError(t, err) - require.Equal(t, commonEng.PendingTxs, msg) - - vm1BlkB, err := vm1.BuildBlock(context.Background()) + vm1BlkB, err := vmtest.IssueTxsAndSetPreference(txs, vm1) if err != nil { t.Fatal(err) } - if err := vm1BlkB.Verify(context.Background()); err != nil { - t.Fatal(err) - } - - if err := vm1.SetPreference(context.Background(), vm1BlkB.ID()); err != nil { - t.Fatal(err) - } - - errs = vm2.txPool.AddRemotesSync(txs[0:5]) - for i, err := range errs { - if err != nil { - t.Fatalf("Failed to add transaction to VM2 at index %d: %s", i, err) - } - } - - msg, err = vm2.WaitForEvent(context.Background()) - require.NoError(t, err) - require.Equal(t, commonEng.PendingTxs, msg) - - vm2BlkC, err := vm2.BuildBlock(context.Background()) + vm2BlkC, err := vmtest.IssueTxsAndSetPreference(txs[0:5], vm2) if err != nil { t.Fatalf("Failed to build BlkC on VM2: %s", err) } - if err := vm2BlkC.Verify(context.Background()); err != nil { - t.Fatalf("BlkC failed verification on VM2: %s", err) - } - - if err := vm2.SetPreference(context.Background(), vm2BlkC.ID()); err != nil { - t.Fatal(err) - } - newHead = <-newTxPoolHeadChan2 if newHead.Head.Hash() != common.Hash(vm2BlkC.ID()) { t.Fatalf("Expected new block to match") } - errs = vm2.txPool.AddRemotesSync(txs[5:10]) - for i, err := range errs { - if err != nil { - t.Fatalf("Failed to add transaction to VM2 at index %d: %s", i, err) - } - } - - msg, err = vm2.WaitForEvent(context.Background()) - require.NoError(t, err) - require.Equal(t, commonEng.PendingTxs, msg) - - vm2BlkD, err := vm2.BuildBlock(context.Background()) + vm2BlkD, err := vmtest.IssueTxsAndBuild(txs[5:10], vm2) if err != nil { t.Fatalf("Failed to build BlkD on VM2: %s", err) } @@ -1545,30 +1213,11 @@ func testAcceptReorg(t *testing.T, scheme string) { if err != nil { t.Fatal(err) } - errs := vm1.txPool.AddRemotesSync([]*types.Transaction{signedTx}) - for i, err := range errs { - if err != nil { - t.Fatalf("Failed to add tx at index %d: %s", i, err) - } - } - - msg, err := vm1.WaitForEvent(context.Background()) - require.NoError(t, err) - require.Equal(t, commonEng.PendingTxs, msg) - - vm1BlkA, err := vm1.BuildBlock(context.Background()) + vm1BlkA, err := vmtest.IssueTxsAndSetPreference([]*types.Transaction{signedTx}, vm1) if err != nil { t.Fatalf("Failed to build block with transaction: %s", err) } - if err := vm1BlkA.Verify(context.Background()); err != nil { - t.Fatalf("Block failed verification on VM1: %s", err) - } - - if err := vm1.SetPreference(context.Background(), vm1BlkA.ID()); err != nil { - t.Fatal(err) - } - vm2BlkA, err := vm2.ParseBlock(context.Background(), vm1BlkA.Bytes()) if err != nil { t.Fatalf("Unexpected error parsing block from vm2: %s", err) @@ -1610,71 +1259,22 @@ func testAcceptReorg(t *testing.T, scheme string) { // Add the remote transactions, build the block, and set VM1's preference // for block B - errs = vm1.txPool.AddRemotesSync(txs) - for i, err := range errs { - if err != nil { - t.Fatalf("Failed to add transaction to VM1 at index %d: %s", i, err) - } - } - - msg, err = vm1.WaitForEvent(context.Background()) - require.NoError(t, err) - require.Equal(t, commonEng.PendingTxs, msg) - - vm1BlkB, err := vm1.BuildBlock(context.Background()) + vm1BlkB, err := vmtest.IssueTxsAndSetPreference(txs, vm1) if err != nil { t.Fatal(err) } - if err := vm1BlkB.Verify(context.Background()); err != nil { - t.Fatal(err) - } - - if err := vm1.SetPreference(context.Background(), vm1BlkB.ID()); err != nil { - t.Fatal(err) - } - - errs = vm2.txPool.AddRemotesSync(txs[0:5]) - for i, err := range errs { - if err != nil { - t.Fatalf("Failed to add transaction to VM2 at index %d: %s", i, err) - } - } - - msg, err = vm2.WaitForEvent(context.Background()) - require.NoError(t, err) - require.Equal(t, commonEng.PendingTxs, msg) - - vm2BlkC, err := vm2.BuildBlock(context.Background()) + vm2BlkC, err := vmtest.IssueTxsAndSetPreference(txs[0:5], vm2) if err != nil { t.Fatalf("Failed to build BlkC on VM2: %s", err) } - if err := vm2BlkC.Verify(context.Background()); err != nil { - t.Fatalf("BlkC failed verification on VM2: %s", err) - } - - if err := vm2.SetPreference(context.Background(), vm2BlkC.ID()); err != nil { - t.Fatal(err) - } - newHead = <-newTxPoolHeadChan2 if newHead.Head.Hash() != common.Hash(vm2BlkC.ID()) { t.Fatalf("Expected new block to match") } - errs = vm2.txPool.AddRemotesSync(txs[5:]) - for i, err := range errs { - if err != nil { - t.Fatalf("Failed to add transaction to VM2 at index %d: %s", i, err) - } - } - - msg, err = vm2.WaitForEvent(context.Background()) - require.NoError(t, err) - require.Equal(t, commonEng.PendingTxs, msg) - - vm2BlkD, err := vm2.BuildBlock(context.Background()) + vm2BlkD, err := vmtest.IssueTxsAndBuild(txs[5:], vm2) if err != nil { t.Fatalf("Failed to build BlkD on VM2: %s", err) } @@ -1750,17 +1350,7 @@ func testFutureBlock(t *testing.T, scheme string) { if err != nil { t.Fatal(err) } - errs := vm.txPool.AddRemotesSync([]*types.Transaction{signedTx}) - for i, err := range errs { - if err != nil { - t.Fatalf("Failed to add tx at index %d: %s", i, err) - } - } - msg, err := vm.WaitForEvent(context.Background()) - require.NoError(t, err) - require.Equal(t, commonEng.PendingTxs, msg) - - blkA, err := vm.BuildBlock(context.Background()) + blkA, err := vmtest.IssueTxsAndBuild([]*types.Transaction{signedTx}, vm) if err != nil { t.Fatalf("Failed to build block with transaction: %s", err) } @@ -1826,30 +1416,11 @@ func testBuildApricotPhase1Block(t *testing.T, scheme string) { if err != nil { t.Fatal(err) } - errs := vm.txPool.AddRemotesSync([]*types.Transaction{signedTx}) - for i, err := range errs { - if err != nil { - t.Fatalf("Failed to add tx at index %d: %s", i, err) - } - } - - msg, err := vm.WaitForEvent(context.Background()) - require.NoError(t, err) - require.Equal(t, commonEng.PendingTxs, msg) - - blk, err := vm.BuildBlock(context.Background()) + blk, err := vmtest.IssueTxsAndSetPreference([]*types.Transaction{signedTx}, vm) if err != nil { t.Fatal(err) } - if err := blk.Verify(context.Background()); err != nil { - t.Fatal(err) - } - - if err := vm.SetPreference(context.Background(), blk.ID()); err != nil { - t.Fatal(err) - } - if err := blk.Accept(context.Background()); err != nil { t.Fatal(err) } @@ -1876,26 +1447,11 @@ func testBuildApricotPhase1Block(t *testing.T, scheme string) { } txs[i] = signedTx } - errs = vm.txPool.AddRemotesSync(txs) - for i, err := range errs { - if err != nil { - t.Fatalf("Failed to add tx at index %d: %s", i, err) - } - } - - msg, err = vm.WaitForEvent(context.Background()) - require.NoError(t, err) - require.Equal(t, commonEng.PendingTxs, msg) - - blk, err = vm.BuildBlock(context.Background()) + blk, err = vmtest.IssueTxsAndBuild(txs, vm) if err != nil { t.Fatal(err) } - if err := blk.Verify(context.Background()); err != nil { - t.Fatal(err) - } - if err := blk.Accept(context.Background()); err != nil { t.Fatal(err) } @@ -1947,30 +1503,11 @@ func testLastAcceptedBlockNumberAllow(t *testing.T, scheme string) { if err != nil { t.Fatal(err) } - errs := vm.txPool.AddRemotesSync([]*types.Transaction{signedTx}) - for i, err := range errs { - if err != nil { - t.Fatalf("Failed to add tx at index %d: %s", i, err) - } - } - - msg, err := vm.WaitForEvent(context.Background()) - require.NoError(t, err) - require.Equal(t, commonEng.PendingTxs, msg) - - blk, err := vm.BuildBlock(context.Background()) + blk, err := vmtest.IssueTxsAndSetPreference([]*types.Transaction{signedTx}, vm) if err != nil { t.Fatalf("Failed to build block with transaction: %s", err) } - if err := blk.Verify(context.Background()); err != nil { - t.Fatalf("Block failed verification on VM: %s", err) - } - - if err := vm.SetPreference(context.Background(), blk.ID()); err != nil { - t.Fatal(err) - } - blkHeight := blk.Height() blkHash := blk.(*chain.BlockWrapper).Block.(*wrappedBlock).ethBlock.Hash() @@ -2015,20 +1552,8 @@ func TestSkipChainConfigCheckCompatible(t *testing.T) { if err != nil { t.Fatal(err) } - errs := vm.txPool.AddRemotesSync([]*types.Transaction{signedTx}) - for i, err := range errs { - if err != nil { - t.Fatalf("Failed to add tx at index %d: %s", i, err) - } - } - msg, err := vm.WaitForEvent(context.Background()) - require.NoError(t, err) - require.Equal(t, commonEng.PendingTxs, msg) - - blk, err := vm.BuildBlock(context.Background()) + blk, err := vmtest.IssueTxsAndSetPreference([]*types.Transaction{signedTx}, vm) require.NoError(t, err) - require.NoError(t, blk.Verify(context.Background())) - require.NoError(t, vm.SetPreference(context.Background(), blk.ID())) require.NoError(t, blk.Accept(context.Background())) require.NoError(t, vm.Shutdown(context.Background())) @@ -2120,18 +1645,7 @@ func TestParentBeaconRootBlock(t *testing.T) { if err != nil { t.Fatal(err) } - errs := vm.txPool.AddRemotesSync([]*types.Transaction{signedTx}) - for i, err := range errs { - if err != nil { - t.Fatalf("Failed to add tx at index %d: %s", i, err) - } - } - - msg, err := vm.WaitForEvent(context.Background()) - require.NoError(t, err) - require.Equal(t, commonEng.PendingTxs, msg) - - blk, err := vm.BuildBlock(context.Background()) + blk, err := vmtest.IssueTxsAndBuild([]*types.Transaction{signedTx}, vm) if err != nil { t.Fatalf("Failed to build block with transaction: %s", err) } @@ -2244,37 +1758,20 @@ func TestBuildBlockWithInsufficientCapacity(t *testing.T) { require.NoError(err) } - errs := vm.txPool.AddRemotesSync([]*types.Transaction{txs[0]}) - require.Len(errs, 1) - require.NoError(errs[0]) - - msg, err := vm.WaitForEvent(context.Background()) - require.NoError(err) - require.Equal(commonEng.PendingTxs, msg) - - blk2, err := vm.BuildBlock(ctx) + blk2, err := vmtest.IssueTxsAndBuild([]*types.Transaction{txs[0]}, vm) require.NoError(err) - require.NoError(blk2.Verify(ctx)) require.NoError(blk2.Accept(ctx)) // Attempt to build a block consuming more than the current gas capacity - errs = vm.txPool.AddRemotesSync([]*types.Transaction{txs[1]}) - require.Len(errs, 1) - require.NoError(errs[0]) - - msg, err = vm.WaitForEvent(context.Background()) - require.NoError(err) - require.Equal(commonEng.PendingTxs, msg) - + _, err = vmtest.IssueTxsAndBuild([]*types.Transaction{txs[1]}, vm) // Expect block building to fail due to insufficient gas capacity - _, err = vm.BuildBlock(ctx) require.ErrorIs(err, miner.ErrInsufficientGasCapacityToBuild) // Wait to fill block capacity and retry block builiding vm.clock.Set(vm.clock.Time().Add(acp176.TimeToFillCapacity * time.Second)) - msg, err = vm.WaitForEvent(context.Background()) + msg, err := vm.WaitForEvent(context.Background()) require.NoError(err) require.Equal(commonEng.PendingTxs, msg) @@ -2329,22 +1826,13 @@ func TestBuildBlockLargeTxStarvation(t *testing.T) { require.NoError(err) } - errs := vm.txPool.AddRemotesSync([]*types.Transaction{maxSizeTxs[0]}) - require.Len(errs, 1) - require.NoError(errs[0]) - - msg, err := vm.WaitForEvent(context.Background()) - require.NoError(err) - require.Equal(commonEng.PendingTxs, msg) - - blk2, err := vm.BuildBlock(ctx) + blk2, err := vmtest.IssueTxsAndBuild([]*types.Transaction{maxSizeTxs[0]}, vm) require.NoError(err) - require.NoError(blk2.Verify(ctx)) require.NoError(blk2.Accept(ctx)) // Add a second transaction trying to consume the max guaranteed gas capacity at a higher gas price - errs = vm.txPool.AddRemotesSync([]*types.Transaction{maxSizeTxs[1]}) + errs := vm.txPool.AddRemotesSync([]*types.Transaction{maxSizeTxs[1]}) require.Len(errs, 1) require.NoError(errs[0]) @@ -2353,21 +1841,13 @@ func TestBuildBlockLargeTxStarvation(t *testing.T) { tx := types.NewContractCreation(0, big.NewInt(0), 2_000_000, lowGasPrice, []byte{0xfe}) signedTx, err := types.SignTx(tx, types.NewEIP155Signer(vm.chainConfig.ChainID), vmtest.TestKeys[1].ToECDSA()) require.NoError(err) - errs = vm.txPool.AddRemotesSync([]*types.Transaction{signedTx}) - require.Len(errs, 1) - require.NoError(errs[0]) - - msg, err = vm.WaitForEvent(context.Background()) - require.NoError(err) - require.Equal(commonEng.PendingTxs, msg) - - _, err = vm.BuildBlock(ctx) + _, err = vmtest.IssueTxsAndBuild([]*types.Transaction{signedTx}, vm) require.ErrorIs(err, miner.ErrInsufficientGasCapacityToBuild) // Wait to fill block capacity and retry block building vm.clock.Set(vm.clock.Time().Add(acp176.TimeToFillCapacity * time.Second)) - msg, err = vm.WaitForEvent(context.Background()) + msg, err := vm.WaitForEvent(context.Background()) require.NoError(err) require.Equal(commonEng.PendingTxs, msg) @@ -2508,19 +1988,11 @@ func TestWaitForEvent(t *testing.T) { signedTx, err := types.SignTx(tx, types.NewEIP155Signer(vm.chainConfig.ChainID), vmtest.TestKeys[0].ToECDSA()) require.NoError(t, err) - for _, err := range vm.txPool.AddRemotesSync([]*types.Transaction{signedTx}) { - require.NoError(t, err) - } - lastBuildBlockTime := time.Now() - blk, err := vm.BuildBlock(context.Background()) + blk, err := vmtest.IssueTxsAndBuild([]*types.Transaction{signedTx}, vm) require.NoError(t, err) - require.NoError(t, blk.Verify(context.Background())) - - require.NoError(t, vm.SetPreference(context.Background(), blk.ID())) - require.NoError(t, blk.Accept(context.Background())) tx = types.NewTransaction(uint64(1), vmtest.TestEthAddrs[1], big.NewInt(1), 21000, vmtest.InitialBaseFee, nil) diff --git a/plugin/evm/vm_warp_test.go b/plugin/evm/vm_warp_test.go index ddecd09ca9..df177e14a0 100644 --- a/plugin/evm/vm_warp_test.go +++ b/plugin/evm/vm_warp_test.go @@ -117,18 +117,9 @@ func testSendWarpMessage(t *testing.T, scheme string) { signedTx0, err := types.SignTx(tx0, types.LatestSignerForChainID(vm.chainConfig.ChainID), vmtest.TestKeys[0].ToECDSA()) require.NoError(err) - errs := vm.txPool.AddRemotesSync([]*types.Transaction{signedTx0}) - require.NoError(errs[0]) - - msg, err := vm.WaitForEvent(context.Background()) - require.NoError(err) - require.Equal(commonEng.PendingTxs, msg) - - blk, err := vm.BuildBlock(context.Background()) + blk, err := vmtest.IssueTxsAndBuild([]*types.Transaction{signedTx0}, vm) require.NoError(err) - require.NoError(blk.Verify(context.Background())) - // Verify that the constructed block contains the expected log with an unsigned warp message in the log data ethBlock1 := blk.(*chain.BlockWrapper).Block.(*wrappedBlock).ethBlock require.Len(ethBlock1.Transactions(), 1) diff --git a/plugin/evm/vmtest/test_vm.go b/plugin/evm/vmtest/test_vm.go index 00cc1bd138..edc4ef3b19 100644 --- a/plugin/evm/vmtest/test_vm.go +++ b/plugin/evm/vmtest/test_vm.go @@ -6,19 +6,23 @@ package vmtest import ( "context" "encoding/json" + "fmt" "testing" "github.com/ava-labs/avalanchego/api/metrics" avalancheatomic "github.com/ava-labs/avalanchego/chains/atomic" "github.com/ava-labs/avalanchego/database/prefixdb" "github.com/ava-labs/avalanchego/snow" + "github.com/ava-labs/avalanchego/snow/consensus/snowman" commonEng "github.com/ava-labs/avalanchego/snow/engine/common" commoneng "github.com/ava-labs/avalanchego/snow/engine/common" "github.com/ava-labs/avalanchego/snow/engine/enginetest" "github.com/ava-labs/avalanchego/upgrade/upgradetest" "github.com/ava-labs/coreth/plugin/evm/customrawdb" + "github.com/ava-labs/coreth/plugin/evm/extension" "github.com/ava-labs/coreth/utils/utilstest" "github.com/ava-labs/libevm/core/rawdb" + "github.com/ava-labs/libevm/core/types" "github.com/stretchr/testify/require" ) @@ -129,3 +133,44 @@ func OverrideSchemeConfig(scheme string, configJSON string) (string, error) { result, err := json.Marshal(configMap) return string(result), err } + +func IssueTxsAndBuild(txs []*types.Transaction, vm extension.InnerVM) (snowman.Block, error) { + errs := vm.Ethereum().TxPool().AddRemotesSync(txs) + for i, err := range errs { + if err != nil { + return nil, fmt.Errorf("failed to add tx at index %d: %w", i, err) + } + } + + msg, err := vm.WaitForEvent(context.Background()) + if err != nil { + return nil, fmt.Errorf("failed to wait for event: %w", err) + } + if msg != commonEng.PendingTxs { + return nil, fmt.Errorf("expected pending txs, got %v", msg) + } + + block, err := vm.BuildBlock(context.Background()) + if err != nil { + return nil, fmt.Errorf("failed to build block with transaction: %w", err) + } + + if err := block.Verify(context.Background()); err != nil { + return nil, fmt.Errorf("block verification failed: %w", err) + } + + return block, nil +} + +func IssueTxsAndSetPreference(txs []*types.Transaction, vm extension.InnerVM) (snowman.Block, error) { + block, err := IssueTxsAndBuild(txs, vm) + if err != nil { + return nil, err + } + + if err := vm.SetPreference(context.Background(), block.ID()); err != nil { + return nil, fmt.Errorf("failed to set preference: %w", err) + } + + return block, nil +} From 7410bfe01fc88499579aafc87b30ec6eb74c36dd Mon Sep 17 00:00:00 2001 From: Austin Larson <78000745+alarso16@users.noreply.github.com> Date: Tue, 19 Aug 2025 09:11:28 -0400 Subject: [PATCH 16/20] ci: Enable extra linters (#1117) --- .avalanche-golangci.yml | 234 ++++++++++++++++++ accounts/abi/bind/bind_extra_test.go | 9 +- cmd/simulator/key/key.go | 1 + cmd/simulator/load/funder.go | 8 +- cmd/simulator/load/loader.go | 10 +- cmd/simulator/load/worker.go | 6 +- cmd/simulator/main/main.go | 4 +- cmd/simulator/metrics/metrics.go | 2 +- cmd/simulator/txs/agent.go | 3 +- cmd/simulator/txs/tx_generator.go | 4 +- consensus/dummy/consensus.go | 9 +- consensus/dummy/consensus_test.go | 5 +- core/blockchain_ext_test.go | 27 +- core/blockchain_log_test.go | 9 +- core/extstate/database.go | 3 +- core/extstate/database_test.go | 29 ++- core/extstate/firewood_database.go | 3 +- core/extstate/options.go | 3 +- core/extstate/statedb.go | 7 +- core/extstate/statedb_multicoin_test.go | 5 +- core/predicate_check.go | 7 +- core/predicate_check_test.go | 5 +- core/state/snapshot/snapshot_ext.go | 3 +- core/state_manager_test.go | 6 +- core/state_processor_ext.go | 5 +- eth/chain_with_final_block.go | 3 +- eth/gasprice/fee_info_provider_test.go | 5 +- ethclient/ethclient_ext.go | 10 +- interfaces/interfaces.go | 3 +- internal/ethapi/api.coreth_test.go | 5 +- internal/ethapi/api_extra.go | 9 +- internal/ethapi/api_extra_test.go | 7 +- nativeasset/contract.go | 3 +- nativeasset/contract_test.go | 11 +- network/network.go | 8 +- network/network_test.go | 10 +- network/peer_tracker.go | 23 +- params/config_extra.go | 1 + params/config_extra_test.go | 3 +- params/config_libevm.go | 4 +- params/extras/config.go | 6 +- params/extras/config_extra_test.go | 3 +- params/extras/network_upgrades.go | 5 +- params/extras/precompile_upgrade.go | 4 +- params/extras/rules.go | 3 +- params/hooks_libevm.go | 12 +- plugin/evm/admin.go | 3 +- plugin/evm/atomic/atomictest/ops.go | 4 +- plugin/evm/atomic/atomictest/tx.go | 7 +- plugin/evm/atomic/export_tx.go | 13 +- plugin/evm/atomic/import_tx.go | 10 +- plugin/evm/atomic/state/atomic_backend.go | 7 +- plugin/evm/atomic/state/atomic_repository.go | 9 +- .../atomic/state/atomic_repository_test.go | 12 +- plugin/evm/atomic/state/atomic_state.go | 7 +- plugin/evm/atomic/state/atomic_trie.go | 14 +- .../evm/atomic/state/atomic_trie_iterator.go | 1 - .../atomic/state/atomic_trie_iterator_test.go | 7 +- plugin/evm/atomic/state/atomic_trie_test.go | 17 +- plugin/evm/atomic/sync/extender.go | 4 +- plugin/evm/atomic/sync/leaf_handler.go | 6 +- plugin/evm/atomic/sync/summary.go | 5 +- plugin/evm/atomic/sync/summary_parser.go | 3 +- plugin/evm/atomic/sync/summary_provider.go | 8 +- plugin/evm/atomic/sync/summary_test.go | 3 +- plugin/evm/atomic/sync/syncer.go | 8 +- plugin/evm/atomic/sync/syncer_test.go | 21 +- plugin/evm/atomic/tx.go | 9 +- plugin/evm/atomic/tx_test.go | 3 +- plugin/evm/atomic/txpool/mempool.go | 5 +- plugin/evm/atomic/txpool/mempool_test.go | 5 +- plugin/evm/atomic/txpool/tx_heap.go | 4 +- plugin/evm/atomic/txpool/tx_heap_test.go | 4 +- plugin/evm/atomic/txpool/txs.go | 2 +- plugin/evm/atomic/vm/api.go | 3 +- plugin/evm/atomic/vm/block_extension.go | 11 +- plugin/evm/atomic/vm/export_tx_test.go | 14 +- plugin/evm/atomic/vm/ext_data_hashes.go | 3 +- .../vm/gossiper_atomic_gossiping_test.go | 4 +- plugin/evm/atomic/vm/import_tx_test.go | 23 +- plugin/evm/atomic/vm/syncervm_test.go | 9 +- plugin/evm/atomic/vm/tx_gossip_test.go | 11 +- plugin/evm/atomic/vm/tx_semantic_verifier.go | 1 + plugin/evm/atomic/vm/tx_test.go | 9 +- plugin/evm/atomic/vm/vm.go | 29 ++- plugin/evm/atomic/vm/vm_test.go | 19 +- plugin/evm/block_builder.go | 9 +- plugin/evm/client/client.go | 7 +- plugin/evm/config/config.go | 7 +- plugin/evm/config/default_config.go | 3 +- plugin/evm/customtypes/block_ext.go | 3 +- plugin/evm/customtypes/header_ext.go | 5 +- plugin/evm/eth_gossiper.go | 10 +- plugin/evm/extension/config.go | 13 +- plugin/evm/gossip_test.go | 13 +- plugin/evm/gossiper_eth_gossiping_test.go | 8 +- plugin/evm/header/base_fee.go | 3 +- plugin/evm/header/base_fee_test.go | 7 +- plugin/evm/header/block_gas_cost.go | 5 +- plugin/evm/header/block_gas_cost_test.go | 7 +- plugin/evm/header/dynamic_fee_state.go | 5 +- plugin/evm/header/dynamic_fee_windower.go | 7 +- plugin/evm/header/extra.go | 3 +- plugin/evm/header/extra_test.go | 5 +- plugin/evm/header/gas_limit.go | 3 +- plugin/evm/header/gas_limit_test.go | 7 +- plugin/evm/imports_test.go | 2 +- plugin/evm/log/log.go | 6 +- plugin/evm/message/block_request.go | 1 - plugin/evm/message/block_sync_summary.go | 1 - .../message/block_sync_summary_provider.go | 1 - plugin/evm/message/syncable.go | 3 +- plugin/evm/network_handler.go | 10 +- plugin/evm/prestate_tracer_test.go | 9 +- plugin/evm/syncervm_test.go | 9 +- plugin/evm/tx_gossip_test.go | 6 +- plugin/evm/upgrade/acp176/acp176.go | 3 +- plugin/evm/upgrade/ap0/params.go | 1 + plugin/evm/upgrade/ap3/window.go | 2 + plugin/evm/upgrade/ap4/cost.go | 3 +- plugin/evm/vm.go | 96 ++++--- plugin/evm/vm_database.go | 6 +- plugin/evm/vm_extensible.go | 4 +- plugin/evm/vm_test.go | 15 +- plugin/evm/vm_warp_test.go | 28 ++- plugin/evm/vmtest/genesis.go | 6 +- plugin/evm/vmtest/test_syncervm.go | 29 ++- plugin/evm/vmtest/test_vm.go | 14 +- plugin/evm/wrapped_block.go | 23 +- plugin/factory/factory.go | 3 +- precompile/contract/interfaces.go | 6 +- precompile/contract/utils.go | 3 +- precompile/contracts/warp/config.go | 16 +- precompile/contracts/warp/config_test.go | 3 +- precompile/contracts/warp/contract.go | 12 +- precompile/contracts/warp/contract_test.go | 12 +- .../contracts/warp/contract_warp_handler.go | 15 +- precompile/contracts/warp/module.go | 4 +- precompile/contracts/warp/predicate_test.go | 8 +- .../warp/signature_verification_test.go | 3 +- precompile/modules/module.go | 3 +- precompile/modules/registerer.go | 3 +- precompile/modules/registerer_test.go | 3 +- precompile/precompiletest/test_config.go | 3 +- precompile/precompiletest/test_precompile.go | 9 +- precompile/precompiletest/test_predicate.go | 3 +- predicate/predicate_bytes.go | 9 +- predicate/predicate_slots.go | 3 +- predicate/predicate_tx.go | 3 +- scripts/lint.sh | 30 ++- sync/blocksync/syncer.go | 5 +- sync/blocksync/syncer_test.go | 16 +- sync/client/client.go | 17 +- sync/client/client_test.go | 20 +- sync/client/leaf_syncer.go | 7 +- sync/client/stats/stats.go | 15 +- sync/client/test_client.go | 7 +- sync/client/test_network.go | 4 +- sync/handlers/block_request.go | 4 +- sync/handlers/block_request_test.go | 13 +- sync/handlers/code_request.go | 6 +- sync/handlers/code_request_test.go | 8 +- sync/handlers/handler.go | 3 +- sync/handlers/leafs_request.go | 11 +- sync/handlers/leafs_request_test.go | 9 +- sync/handlers/test_providers.go | 3 +- sync/statesync/code_syncer.go | 10 +- sync/statesync/code_syncer_test.go | 15 +- sync/statesync/state_syncer.go | 8 +- sync/statesync/statesynctest/test_sync.go | 3 +- sync/statesync/statesynctest/test_trie.go | 10 +- sync/statesync/sync_test.go | 16 +- sync/statesync/trie_queue.go | 3 +- sync/statesync/trie_segments.go | 10 +- sync/statesync/trie_sync_stats.go | 7 +- sync/statesync/trie_sync_tasks.go | 3 +- sync/syncutils/iterators.go | 3 +- sync/types.go | 4 +- sync/vm/client.go | 19 +- sync/vm/registry.go | 3 +- sync/vm/server.go | 7 +- tests/utils/proposervm.go | 8 +- tests/warp/aggregator/aggregator.go | 7 +- tests/warp/aggregator/aggregator_test.go | 7 +- tests/warp/aggregator/signature_getter.go | 6 +- tests/warp/fetcher.go | 3 +- tests/warp/warp_test.go | 16 +- triedb/firewood/database.go | 9 +- utils/metered_cache.go | 15 +- warp/backend.go | 3 +- warp/backend_test.go | 6 +- warp/client.go | 3 +- warp/service.go | 5 +- warp/verifier_backend.go | 3 +- warp/verifier_backend_test.go | 6 +- 195 files changed, 1106 insertions(+), 704 deletions(-) create mode 100644 .avalanche-golangci.yml diff --git a/.avalanche-golangci.yml b/.avalanche-golangci.yml new file mode 100644 index 0000000000..71063d3833 --- /dev/null +++ b/.avalanche-golangci.yml @@ -0,0 +1,234 @@ +# https://golangci-lint.run/usage/configuration/ +version: "2" +run: + # If set we pass it to "go list -mod={option}". From "go help modules": + # If invoked with -mod=readonly, the go command is disallowed from the implicit + # automatic updating of go.mod described above. Instead, it fails when any changes + # to go.mod are needed. This setting is most useful to check that go.mod does + # not need updates, such as in a continuous integration and testing system. + # If invoked with -mod=vendor, the go command assumes that the vendor + # directory holds the correct copies of dependencies and ignores + # the dependency descriptions in go.mod. + # + # Allowed values: readonly|vendor|mod + # By default, it isn't set. + modules-download-mode: readonly + + # Include non-test files tagged as test-only. + # Context: https://github.com/ava-labs/avalanchego/pull/3173 + build-tags: + - test + +issues: + # Make issues output unique by line. + uniq-by-line: false + + # Maximum issues count per one linter. + max-issues-per-linter: 0 + + # Maximum count of issues with the same text. + max-same-issues: 0 + +formatters: + enable: + - gci + - gofmt + - gofumpt + settings: + gci: + sections: + - standard + - default + - blank + - prefix(github.com/ava-labs/coreth) + - alias + - dot + custom-order: true + exclusions: + generated: lax + +# Avalanche linters should be added incrementally +linters: + default: none + enable: + - asciicheck + # - bodyclose + - copyloopvar + # - depguard + # - errcheck + - errorlint + # - forbidigo + # - goconst + # - gocritic + - goprintffuncname + # - gosec + - govet + - importas + - ineffassign + - misspell + - nakedret + # - nilerr + # - noctx + - nolintlint + - perfsprint + # - prealloc + # - predeclared + # - revive + - spancheck + # - staticcheck + - tagalign + # - testifylint + - unconvert + # - unparam + # - unused + - usestdlibvars + - whitespace + settings: + depguard: + rules: + packages: + deny: + - pkg: container/list + desc: github.com/ava-labs/avalanchego/utils/linked should be used instead. + - pkg: github.com/golang/mock/gomock + desc: go.uber.org/mock/gomock should be used instead. + - pkg: github.com/stretchr/testify/assert + desc: github.com/stretchr/testify/require should be used instead. + - pkg: io/ioutil + desc: io/ioutil is deprecated. Use package io or os instead. + errorlint: + # Check for plain type assertions and type switches. + asserts: false + # Check for plain error comparisons. + comparison: false + forbidigo: + # Forbid the following identifiers (list of regexp). + forbid: + - pattern: require\.Error$(# ErrorIs should be used instead)? + - pattern: require\.ErrorContains$(# ErrorIs should be used instead)? + - pattern: require\.EqualValues$(# Equal should be used instead)? + - pattern: require\.NotEqualValues$(# NotEqual should be used instead)? + - pattern: ^(t|b|tb|f)\.(Fatal|Fatalf|Error|Errorf)$(# the require library should be used instead)? + - pattern: ^sort\.(Slice|Strings)$(# the slices package should be used instead)? + # Exclude godoc examples from forbidigo checks. + exclude-godoc-examples: false + gosec: + excludes: + - G107 # Url provided to HTTP request as taint input https://securego.io/docs/rules/g107 + - G115 # TODO(marun) Enable this ruleset in a follow-up PR + importas: + # Do not allow unaliased imports of aliased packages. + no-unaliased: false + # Do not allow non-required aliases. + no-extra-aliases: false + # List of aliases + alias: + - pkg: github.com/ava-labs/avalanchego/utils/math + alias: safemath + - pkg: github.com/ava-labs/avalanchego/utils/json + alias: avajson + revive: + rules: + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#bool-literal-in-expr + - name: bool-literal-in-expr + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#early-return + - name: early-return + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#empty-lines + - name: empty-lines + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#string-format + - name: string-format + disabled: false + arguments: + - - b.Logf[0] + - /.*%.*/ + - no format directive, use b.Log instead + - - fmt.Errorf[0] + - /.*%.*/ + - no format directive, use errors.New instead + - - fmt.Fprintf[1] + - /.*%.*/ + - no format directive, use fmt.Fprint instead + - - fmt.Printf[0] + - /.*%.*/ + - no format directive, use fmt.Print instead + - - fmt.Sprintf[0] + - /.*%.*/ + - no format directive, use fmt.Sprint instead + - - log.Fatalf[0] + - /.*%.*/ + - no format directive, use log.Fatal instead + - - log.Printf[0] + - /.*%.*/ + - no format directive, use log.Print instead + - - t.Logf[0] + - /.*%.*/ + - no format directive, use t.Log instead + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#struct-tag + - name: struct-tag + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#unexported-naming + - name: unexported-naming + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#unhandled-error + - name: unhandled-error + # prefer the errcheck linter since it can be disabled directly with nolint directive + # but revive's disable directive (e.g. //revive:disable:unhandled-error) is not + # supported when run under golangci_lint + disabled: true + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#unused-parameter + - name: unused-parameter + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#unused-receiver + - name: unused-receiver + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#useless-break + - name: useless-break + disabled: false + spancheck: + # https://github.com/jjti/go-spancheck#checks + checks: + - end + staticcheck: + # https://staticcheck.io/docs/options#checks + checks: + - all + - -SA6002A # Storing non-pointer values in sync.Pool allocates memory + - -SA1019 # Using a deprecated function, variable, constant or field + - -QF1008 # Unnecessary embedded expressions + tagalign: + align: true + sort: true + order: + - serialize + strict: true + testifylint: + # Enable all checkers (https://github.com/Antonboom/testifylint#checkers). + # Default: false + enable-all: true + # Disable checkers by name + # (in addition to default + # suite-thelper + # ). + disable: + - go-require + - float-compare + unused: + # Mark all struct fields that have been written to as used. + # Default: true + field-writes-are-uses: false + # Treat IncDec statement (e.g. `i++` or `i--`) as both read and write operation instead of just write. + # Default: false + post-statements-are-reads: true + # Mark all local variables as used. + # default: true + local-variables-are-used: false + exclusions: + generated: lax + presets: + - comments + - common-false-positives + - legacy + - std-error-handling diff --git a/accounts/abi/bind/bind_extra_test.go b/accounts/abi/bind/bind_extra_test.go index 2bf24d9097..0c541a3ae1 100644 --- a/accounts/abi/bind/bind_extra_test.go +++ b/accounts/abi/bind/bind_extra_test.go @@ -8,6 +8,11 @@ import ( "math/big" "testing" + "github.com/ava-labs/libevm/common" + "github.com/ava-labs/libevm/core/types" + "github.com/ava-labs/libevm/crypto" + "github.com/stretchr/testify/require" + "github.com/ava-labs/coreth/accounts/abi" "github.com/ava-labs/coreth/accounts/abi/bind" "github.com/ava-labs/coreth/accounts/abi/bind/backends" @@ -15,10 +20,6 @@ import ( "github.com/ava-labs/coreth/ethclient/simulated" "github.com/ava-labs/coreth/node" "github.com/ava-labs/coreth/params" - "github.com/ava-labs/libevm/common" - "github.com/ava-labs/libevm/core/types" - "github.com/ava-labs/libevm/crypto" - "github.com/stretchr/testify/require" ) // TestGetSenderNativeAssetCall checks that the NativeAssetCall proxies the diff --git a/cmd/simulator/key/key.go b/cmd/simulator/key/key.go index c4d1c6266f..826bde3afe 100644 --- a/cmd/simulator/key/key.go +++ b/cmd/simulator/key/key.go @@ -11,6 +11,7 @@ import ( "path/filepath" "github.com/ava-labs/libevm/common" + ethcrypto "github.com/ava-labs/libevm/crypto" ) diff --git a/cmd/simulator/load/funder.go b/cmd/simulator/load/funder.go index 7719e8a338..a017064dea 100644 --- a/cmd/simulator/load/funder.go +++ b/cmd/simulator/load/funder.go @@ -9,13 +9,15 @@ import ( "fmt" "math/big" + "github.com/ava-labs/libevm/common" + "github.com/ava-labs/libevm/core/types" + "github.com/ava-labs/libevm/log" + "github.com/ava-labs/coreth/cmd/simulator/key" "github.com/ava-labs/coreth/cmd/simulator/metrics" "github.com/ava-labs/coreth/cmd/simulator/txs" "github.com/ava-labs/coreth/ethclient" - "github.com/ava-labs/libevm/common" - "github.com/ava-labs/libevm/core/types" - "github.com/ava-labs/libevm/log" + ethparams "github.com/ava-labs/libevm/params" ) diff --git a/cmd/simulator/load/loader.go b/cmd/simulator/load/loader.go index f4cf1be86b..83290bae20 100644 --- a/cmd/simulator/load/loader.go +++ b/cmd/simulator/load/loader.go @@ -14,18 +14,20 @@ import ( "syscall" "time" + "github.com/ava-labs/libevm/common" + "github.com/ava-labs/libevm/core/types" + "github.com/ava-labs/libevm/log" + "golang.org/x/sync/errgroup" + "github.com/ava-labs/coreth/cmd/simulator/config" "github.com/ava-labs/coreth/cmd/simulator/key" "github.com/ava-labs/coreth/cmd/simulator/metrics" "github.com/ava-labs/coreth/cmd/simulator/txs" "github.com/ava-labs/coreth/ethclient" "github.com/ava-labs/coreth/params" - "github.com/ava-labs/libevm/common" - "github.com/ava-labs/libevm/core/types" + ethcrypto "github.com/ava-labs/libevm/crypto" - "github.com/ava-labs/libevm/log" ethparams "github.com/ava-labs/libevm/params" - "golang.org/x/sync/errgroup" ) const ( diff --git a/cmd/simulator/load/worker.go b/cmd/simulator/load/worker.go index 003dc2bf38..4f40a21ed0 100644 --- a/cmd/simulator/load/worker.go +++ b/cmd/simulator/load/worker.go @@ -8,11 +8,13 @@ import ( "fmt" "time" - "github.com/ava-labs/coreth/ethclient" - ethereum "github.com/ava-labs/libevm" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/types" "github.com/ava-labs/libevm/log" + + "github.com/ava-labs/coreth/ethclient" + + ethereum "github.com/ava-labs/libevm" ) type ethereumTxWorker struct { diff --git a/cmd/simulator/main/main.go b/cmd/simulator/main/main.go index bb0c5a1105..489896aae4 100644 --- a/cmd/simulator/main/main.go +++ b/cmd/simulator/main/main.go @@ -9,11 +9,13 @@ import ( "fmt" "os" + "github.com/spf13/pflag" + "github.com/ava-labs/coreth/cmd/simulator/config" "github.com/ava-labs/coreth/cmd/simulator/load" "github.com/ava-labs/coreth/log" + gethlog "github.com/ava-labs/libevm/log" - "github.com/spf13/pflag" ) func main() { diff --git a/cmd/simulator/metrics/metrics.go b/cmd/simulator/metrics/metrics.go index ff3e59e5be..cf1d2e88d7 100644 --- a/cmd/simulator/metrics/metrics.go +++ b/cmd/simulator/metrics/metrics.go @@ -69,7 +69,7 @@ func (m *Metrics) Serve(ctx context.Context, metricsPort string, metricsEndpoint ctx, cancel := context.WithCancel(ctx) // Create a prometheus server to expose individual tx metrics server := &http.Server{ - Addr: fmt.Sprintf(":%s", metricsPort), + Addr: ":" + metricsPort, } // Start up go routine to listen for SIGINT notifications to gracefully shut down server diff --git a/cmd/simulator/txs/agent.go b/cmd/simulator/txs/agent.go index 0abef90172..066d9bcc26 100644 --- a/cmd/simulator/txs/agent.go +++ b/cmd/simulator/txs/agent.go @@ -9,9 +9,10 @@ import ( "fmt" "time" - "github.com/ava-labs/coreth/cmd/simulator/metrics" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/log" + + "github.com/ava-labs/coreth/cmd/simulator/metrics" ) type THash interface { diff --git a/cmd/simulator/txs/tx_generator.go b/cmd/simulator/txs/tx_generator.go index a88bf3402f..ae32deb22b 100644 --- a/cmd/simulator/txs/tx_generator.go +++ b/cmd/simulator/txs/tx_generator.go @@ -8,8 +8,10 @@ import ( "crypto/ecdsa" "fmt" - "github.com/ava-labs/coreth/ethclient" "github.com/ava-labs/libevm/core/types" + + "github.com/ava-labs/coreth/ethclient" + ethcrypto "github.com/ava-labs/libevm/crypto" ) diff --git a/consensus/dummy/consensus.go b/consensus/dummy/consensus.go index d387585f3f..99371aaaa4 100644 --- a/consensus/dummy/consensus.go +++ b/consensus/dummy/consensus.go @@ -11,16 +11,17 @@ import ( "github.com/ava-labs/avalanchego/utils/timer/mockable" "github.com/ava-labs/avalanchego/vms/components/gas" + "github.com/ava-labs/libevm/common" + "github.com/ava-labs/libevm/core/state" + "github.com/ava-labs/libevm/core/types" + "github.com/ava-labs/libevm/trie" + "github.com/ava-labs/coreth/consensus" "github.com/ava-labs/coreth/consensus/misc/eip4844" "github.com/ava-labs/coreth/params" "github.com/ava-labs/coreth/params/extras" "github.com/ava-labs/coreth/plugin/evm/customtypes" "github.com/ava-labs/coreth/utils" - "github.com/ava-labs/libevm/common" - "github.com/ava-labs/libevm/core/state" - "github.com/ava-labs/libevm/core/types" - "github.com/ava-labs/libevm/trie" customheader "github.com/ava-labs/coreth/plugin/evm/header" ) diff --git a/consensus/dummy/consensus_test.go b/consensus/dummy/consensus_test.go index f6c3b8089e..f3c7195a10 100644 --- a/consensus/dummy/consensus_test.go +++ b/consensus/dummy/consensus_test.go @@ -8,10 +8,11 @@ import ( "math/big" "testing" - "github.com/ava-labs/coreth/plugin/evm/header" - "github.com/ava-labs/coreth/plugin/evm/upgrade/ap4" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/types" + + "github.com/ava-labs/coreth/plugin/evm/header" + "github.com/ava-labs/coreth/plugin/evm/upgrade/ap4" ) func TestVerifyBlockFee(t *testing.T) { diff --git a/core/blockchain_ext_test.go b/core/blockchain_ext_test.go index 6aa1529e80..8c20c97661 100644 --- a/core/blockchain_ext_test.go +++ b/core/blockchain_ext_test.go @@ -8,10 +8,6 @@ import ( "math/big" "testing" - "github.com/ava-labs/coreth/consensus/dummy" - "github.com/ava-labs/coreth/core/extstate" - "github.com/ava-labs/coreth/params" - "github.com/ava-labs/coreth/plugin/evm/upgrade/ap4" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/rawdb" "github.com/ava-labs/libevm/core/state" @@ -21,6 +17,11 @@ import ( ethparams "github.com/ava-labs/libevm/params" "github.com/holiman/uint256" "github.com/stretchr/testify/require" + + "github.com/ava-labs/coreth/consensus/dummy" + "github.com/ava-labs/coreth/core/extstate" + "github.com/ava-labs/coreth/params" + "github.com/ava-labs/coreth/plugin/evm/upgrade/ap4" ) var ( @@ -56,14 +57,16 @@ var ( } ) -type createFunc func(db ethdb.Database, gspec *Genesis, lastAcceptedHash common.Hash, dataPath string) (*BlockChain, error) -type ChainTest struct { - Name string - testFunc func( - t *testing.T, - create createFunc, - ) -} +type ( + createFunc func(db ethdb.Database, gspec *Genesis, lastAcceptedHash common.Hash, dataPath string) (*BlockChain, error) + ChainTest struct { + Name string + testFunc func( + t *testing.T, + create createFunc, + ) + } +) var tests = []ChainTest{ { diff --git a/core/blockchain_log_test.go b/core/blockchain_log_test.go index 8a266099c6..daf7a63a5e 100644 --- a/core/blockchain_log_test.go +++ b/core/blockchain_log_test.go @@ -8,16 +8,17 @@ import ( "strings" "testing" - "github.com/ava-labs/coreth/accounts/abi" - "github.com/ava-labs/coreth/consensus/dummy" - "github.com/ava-labs/coreth/params" - "github.com/ava-labs/coreth/plugin/evm/upgrade/ap3" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/rawdb" "github.com/ava-labs/libevm/core/types" "github.com/ava-labs/libevm/core/vm" "github.com/ava-labs/libevm/crypto" "github.com/stretchr/testify/require" + + "github.com/ava-labs/coreth/accounts/abi" + "github.com/ava-labs/coreth/consensus/dummy" + "github.com/ava-labs/coreth/params" + "github.com/ava-labs/coreth/plugin/evm/upgrade/ap3" ) func TestAcceptedLogsSubscription(t *testing.T) { diff --git a/core/extstate/database.go b/core/extstate/database.go index db1fbd864b..a9f4f4ac28 100644 --- a/core/extstate/database.go +++ b/core/extstate/database.go @@ -4,10 +4,11 @@ package extstate import ( - "github.com/ava-labs/coreth/triedb/firewood" "github.com/ava-labs/libevm/core/state" "github.com/ava-labs/libevm/ethdb" "github.com/ava-labs/libevm/triedb" + + "github.com/ava-labs/coreth/triedb/firewood" ) func NewDatabaseWithConfig(db ethdb.Database, config *triedb.Config) state.Database { diff --git a/core/extstate/database_test.go b/core/extstate/database_test.go index 1ef6eafd2a..43ac5fa3a4 100644 --- a/core/extstate/database_test.go +++ b/core/extstate/database_test.go @@ -10,8 +10,6 @@ import ( "slices" "testing" - "github.com/ava-labs/coreth/triedb/firewood" - "github.com/ava-labs/coreth/triedb/hashdb" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/rawdb" "github.com/ava-labs/libevm/core/state" @@ -22,6 +20,9 @@ import ( "github.com/ava-labs/libevm/triedb" "github.com/holiman/uint256" "github.com/stretchr/testify/require" + + "github.com/ava-labs/coreth/triedb/firewood" + "github.com/ava-labs/coreth/triedb/hashdb" ) const ( @@ -35,17 +36,15 @@ const ( maxStep ) -var ( - stepMap = map[byte]string{ - commit: "commit", - createAccount: "createAccount", - updateAccount: "updateAccount", - deleteAccount: "deleteAccount", - addStorage: "addStorage", - updateStorage: "updateStorage", - deleteStorage: "deleteStorage", - } -) +var stepMap = map[byte]string{ + commit: "commit", + createAccount: "createAccount", + updateAccount: "updateAccount", + deleteAccount: "deleteAccount", + addStorage: "addStorage", + updateStorage: "updateStorage", + deleteStorage: "deleteStorage", +} type fuzzState struct { require *require.Assertions @@ -99,14 +98,14 @@ func newFuzzState(t *testing.T) *fuzzState { return &fuzzState{ merkleTries: []*merkleTrie{ - &merkleTrie{ + { name: "hash", ethDatabase: hashState, accountTrie: hashTr, openStorageTries: make(map[common.Address]state.Trie), lastRoot: ethRoot, }, - &merkleTrie{ + { name: "firewood", ethDatabase: firewoodState, accountTrie: fwTr, diff --git a/core/extstate/firewood_database.go b/core/extstate/firewood_database.go index 7f917317a6..09cc41a0ab 100644 --- a/core/extstate/firewood_database.go +++ b/core/extstate/firewood_database.go @@ -6,9 +6,10 @@ package extstate import ( "fmt" - "github.com/ava-labs/coreth/triedb/firewood" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/state" + + "github.com/ava-labs/coreth/triedb/firewood" ) var ( diff --git a/core/extstate/options.go b/core/extstate/options.go index c12e562cd6..7c1672669a 100644 --- a/core/extstate/options.go +++ b/core/extstate/options.go @@ -4,8 +4,9 @@ package extstate import ( - "github.com/ava-labs/coreth/utils" "github.com/ava-labs/libevm/core/state" + + "github.com/ava-labs/coreth/utils" ) type workerPool struct { diff --git a/core/extstate/statedb.go b/core/extstate/statedb.go index 99686dca3a..eb39f009cd 100644 --- a/core/extstate/statedb.go +++ b/core/extstate/statedb.go @@ -6,14 +6,15 @@ package extstate import ( "math/big" - "github.com/ava-labs/coreth/params" - "github.com/ava-labs/coreth/plugin/evm/customtypes" - "github.com/ava-labs/coreth/predicate" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/state" "github.com/ava-labs/libevm/core/types" "github.com/ava-labs/libevm/libevm/stateconf" "github.com/holiman/uint256" + + "github.com/ava-labs/coreth/params" + "github.com/ava-labs/coreth/plugin/evm/customtypes" + "github.com/ava-labs/coreth/predicate" ) // Register the state key normalization to libevm's [state.StateDB]. This will diff --git a/core/extstate/statedb_multicoin_test.go b/core/extstate/statedb_multicoin_test.go index f041d6750c..ee52c00919 100644 --- a/core/extstate/statedb_multicoin_test.go +++ b/core/extstate/statedb_multicoin_test.go @@ -7,8 +7,6 @@ import ( "math/big" "testing" - "github.com/ava-labs/coreth/core/state/snapshot" - "github.com/ava-labs/coreth/plugin/evm/customtypes" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/rawdb" "github.com/ava-labs/libevm/core/state" @@ -17,6 +15,9 @@ import ( "github.com/ava-labs/libevm/libevm/stateconf" "github.com/holiman/uint256" "github.com/stretchr/testify/require" + + "github.com/ava-labs/coreth/core/state/snapshot" + "github.com/ava-labs/coreth/plugin/evm/customtypes" ) func TestMultiCoinOperations(t *testing.T) { diff --git a/core/predicate_check.go b/core/predicate_check.go index 6c385a726f..6dc64edcce 100644 --- a/core/predicate_check.go +++ b/core/predicate_check.go @@ -8,12 +8,13 @@ import ( "fmt" "github.com/ava-labs/avalanchego/utils/set" - "github.com/ava-labs/coreth/params" - "github.com/ava-labs/coreth/precompile/precompileconfig" - "github.com/ava-labs/coreth/predicate" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/types" "github.com/ava-labs/libevm/log" + + "github.com/ava-labs/coreth/params" + "github.com/ava-labs/coreth/precompile/precompileconfig" + "github.com/ava-labs/coreth/predicate" ) var ErrMissingPredicateContext = errors.New("missing predicate context") diff --git a/core/predicate_check_test.go b/core/predicate_check_test.go index aebf5bcee6..a6965224fe 100644 --- a/core/predicate_check_test.go +++ b/core/predicate_check_test.go @@ -9,12 +9,13 @@ import ( "github.com/ava-labs/avalanchego/snow/engine/snowman/block" "github.com/ava-labs/avalanchego/utils/set" - "github.com/ava-labs/coreth/params" - "github.com/ava-labs/coreth/precompile/precompileconfig" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/types" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" + + "github.com/ava-labs/coreth/params" + "github.com/ava-labs/coreth/precompile/precompileconfig" ) type predicateCheckTest struct { diff --git a/core/state/snapshot/snapshot_ext.go b/core/state/snapshot/snapshot_ext.go index 47af438fd3..9c608026d1 100644 --- a/core/state/snapshot/snapshot_ext.go +++ b/core/state/snapshot/snapshot_ext.go @@ -6,9 +6,10 @@ package snapshot import ( "time" - "github.com/ava-labs/coreth/utils" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/ethdb" + + "github.com/ava-labs/coreth/utils" ) func (t *Tree) DiskAccountIterator(seek common.Hash) AccountIterator { diff --git a/core/state_manager_test.go b/core/state_manager_test.go index f102780608..e80ba59dd2 100644 --- a/core/state_manager_test.go +++ b/core/state_manager_test.go @@ -7,9 +7,8 @@ import ( "math/big" "testing" - "github.com/ava-labs/libevm/core/types" - "github.com/ava-labs/libevm/common" + "github.com/ava-labs/libevm/core/types" "github.com/stretchr/testify/assert" ) @@ -25,13 +24,16 @@ func (t *MockTrieDB) Dereference(root common.Hash) error { t.LastDereference = root return nil } + func (t *MockTrieDB) Commit(root common.Hash, report bool) error { t.LastCommit = root return nil } + func (t *MockTrieDB) Size() (common.StorageSize, common.StorageSize, common.StorageSize) { return 0, 0, 0 } + func (t *MockTrieDB) Cap(limit common.StorageSize) error { return nil } diff --git a/core/state_processor_ext.go b/core/state_processor_ext.go index 3d30ccd7a1..bd06a35565 100644 --- a/core/state_processor_ext.go +++ b/core/state_processor_ext.go @@ -8,12 +8,13 @@ import ( "fmt" "math/big" + "github.com/ava-labs/libevm/core/state" + "github.com/ava-labs/libevm/log" + "github.com/ava-labs/coreth/core/extstate" "github.com/ava-labs/coreth/params" "github.com/ava-labs/coreth/precompile/contract" "github.com/ava-labs/coreth/precompile/modules" - "github.com/ava-labs/libevm/core/state" - "github.com/ava-labs/libevm/log" ) // ApplyPrecompileActivations checks if any of the precompiles specified by the chain config are enabled or disabled by the block diff --git a/eth/chain_with_final_block.go b/eth/chain_with_final_block.go index 77655d1ae0..c870b997a1 100644 --- a/eth/chain_with_final_block.go +++ b/eth/chain_with_final_block.go @@ -5,8 +5,9 @@ package eth import ( - "github.com/ava-labs/coreth/core" "github.com/ava-labs/libevm/core/types" + + "github.com/ava-labs/coreth/core" ) const blocksToKeep = 604_800 // Approx. 2 weeks worth of blocks assuming 2s block time diff --git a/eth/gasprice/fee_info_provider_test.go b/eth/gasprice/fee_info_provider_test.go index 9d5260d95f..b9dde66dca 100644 --- a/eth/gasprice/fee_info_provider_test.go +++ b/eth/gasprice/fee_info_provider_test.go @@ -9,11 +9,12 @@ import ( "sync" "testing" - "github.com/ava-labs/coreth/core" - "github.com/ava-labs/coreth/params" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/types" "github.com/stretchr/testify/require" + + "github.com/ava-labs/coreth/core" + "github.com/ava-labs/coreth/params" ) func TestFeeInfoProvider(t *testing.T) { diff --git a/ethclient/ethclient_ext.go b/ethclient/ethclient_ext.go index d56caa2b4b..5accebf2ea 100644 --- a/ethclient/ethclient_ext.go +++ b/ethclient/ethclient_ext.go @@ -8,16 +8,18 @@ import ( "encoding/json" "math/big" - "github.com/ava-labs/coreth/accounts/abi/bind" - "github.com/ava-labs/coreth/interfaces" - "github.com/ava-labs/coreth/rpc" - ethereum "github.com/ava-labs/libevm" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/common/hexutil" "github.com/ava-labs/libevm/core/types" // Force-load precompiles to trigger registration _ "github.com/ava-labs/coreth/precompile/registry" + + "github.com/ava-labs/coreth/accounts/abi/bind" + "github.com/ava-labs/coreth/interfaces" + "github.com/ava-labs/coreth/rpc" + + ethereum "github.com/ava-labs/libevm" ) // Verify that [Client] implements required interfaces diff --git a/interfaces/interfaces.go b/interfaces/interfaces.go index 1a9b3545a0..b58065653d 100644 --- a/interfaces/interfaces.go +++ b/interfaces/interfaces.go @@ -8,9 +8,10 @@ import ( "errors" "math/big" - ethereum "github.com/ava-labs/libevm" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/types" + + ethereum "github.com/ava-labs/libevm" ) // NotFound is returned by API methods if the requested item does not exist. diff --git a/internal/ethapi/api.coreth_test.go b/internal/ethapi/api.coreth_test.go index 6080aedfac..3cd04c41ab 100644 --- a/internal/ethapi/api.coreth_test.go +++ b/internal/ethapi/api.coreth_test.go @@ -8,11 +8,12 @@ import ( "math/big" "testing" - "github.com/ava-labs/coreth/params" - "github.com/ava-labs/coreth/plugin/evm/upgrade/acp176" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/common/hexutil" "github.com/stretchr/testify/require" + + "github.com/ava-labs/coreth/params" + "github.com/ava-labs/coreth/plugin/evm/upgrade/acp176" ) type testSuggestPriceOptionsBackend struct { diff --git a/internal/ethapi/api_extra.go b/internal/ethapi/api_extra.go index f32bb7cfab..ebe35d289e 100644 --- a/internal/ethapi/api_extra.go +++ b/internal/ethapi/api_extra.go @@ -8,12 +8,13 @@ import ( "errors" "fmt" - "github.com/ava-labs/coreth/core" - "github.com/ava-labs/coreth/params" - "github.com/ava-labs/coreth/rpc" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/common/hexutil" "github.com/ava-labs/libevm/rlp" + + "github.com/ava-labs/coreth/core" + "github.com/ava-labs/coreth/params" + "github.com/ava-labs/coreth/rpc" ) // GetChainConfig returns the chain config. @@ -114,7 +115,7 @@ func (s *BlockChainAPI) stateQueryBlockNumberAllowed(blockNumOrHash rpc.BlockNum } else if blockHash, ok := blockNumOrHash.Hash(); ok { block, err := s.b.BlockByHash(context.Background(), blockHash) if err != nil { - return fmt.Errorf("failed to get block from hash: %s", err) + return fmt.Errorf("failed to get block from hash: %w", err) } else if block == nil { return fmt.Errorf("block from hash %s doesn't exist", blockHash) } diff --git a/internal/ethapi/api_extra_test.go b/internal/ethapi/api_extra_test.go index a8a8c7c304..bff0d71b60 100644 --- a/internal/ethapi/api_extra_test.go +++ b/internal/ethapi/api_extra_test.go @@ -4,15 +4,16 @@ package ethapi import ( - "fmt" + "errors" "math/big" "testing" - "github.com/ava-labs/coreth/rpc" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/types" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" + + "github.com/ava-labs/coreth/rpc" ) func TestBlockChainAPI_stateQueryBlockNumberAllowed(t *testing.T) { @@ -86,7 +87,7 @@ func TestBlockChainAPI_stateQueryBlockNumberAllowed(t *testing.T) { backend.EXPECT().LastAcceptedBlock().Return(makeBlockWithNumber(2200)) backend.EXPECT(). BlockByHash(gomock.Any(), gomock.Any()). - Return(nil, fmt.Errorf("test error")) + Return(nil, errors.New("test error")) return backend }, wantErrMessage: "failed to get block from hash: test error", diff --git a/nativeasset/contract.go b/nativeasset/contract.go index 60c553579c..08fd0c08b2 100644 --- a/nativeasset/contract.go +++ b/nativeasset/contract.go @@ -7,11 +7,12 @@ import ( "fmt" "math/big" - "github.com/ava-labs/coreth/precompile/contract" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/vm" "github.com/ava-labs/libevm/log" "github.com/holiman/uint256" + + "github.com/ava-labs/coreth/precompile/contract" ) // PrecompiledContractsApricot contains the default set of pre-compiled Ethereum diff --git a/nativeasset/contract_test.go b/nativeasset/contract_test.go index 6cca4045eb..101924c722 100644 --- a/nativeasset/contract_test.go +++ b/nativeasset/contract_test.go @@ -7,13 +7,9 @@ import ( "math/big" "testing" - "github.com/ava-labs/coreth/core/extstate" - . "github.com/ava-labs/coreth/nativeasset" - "github.com/ava-labs/coreth/params" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/rawdb" "github.com/ava-labs/libevm/core/state" - ethtypes "github.com/ava-labs/libevm/core/types" "github.com/ava-labs/libevm/core/vm" ethparams "github.com/ava-labs/libevm/params" "github.com/holiman/uint256" @@ -22,6 +18,13 @@ import ( // Force import core to register the VM hooks. // This allows testing the precompiles by exercising the EVM. _ "github.com/ava-labs/coreth/core" + + "github.com/ava-labs/coreth/core/extstate" + "github.com/ava-labs/coreth/params" + + ethtypes "github.com/ava-labs/libevm/core/types" + + . "github.com/ava-labs/coreth/nativeasset" ) // CanTransfer checks whether there are enough funds in the address' account to make a transfer. diff --git a/network/network.go b/network/network.go index fb58bc1af3..847fca39df 100644 --- a/network/network.go +++ b/network/network.go @@ -10,11 +10,6 @@ import ( "sync" "time" - "golang.org/x/sync/semaphore" - - "github.com/ava-labs/libevm/log" - "github.com/prometheus/client_golang/prometheus" - "github.com/ava-labs/avalanchego/codec" "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/network/p2p" @@ -24,6 +19,9 @@ import ( "github.com/ava-labs/avalanchego/utils" "github.com/ava-labs/avalanchego/utils/set" "github.com/ava-labs/avalanchego/version" + "github.com/ava-labs/libevm/log" + "github.com/prometheus/client_golang/prometheus" + "golang.org/x/sync/semaphore" "github.com/ava-labs/coreth/network/stats" "github.com/ava-labs/coreth/plugin/evm/message" diff --git a/network/network_test.go b/network/network_test.go index cd80b40906..bdf2e83409 100644 --- a/network/network_test.go +++ b/network/network_test.go @@ -12,22 +12,20 @@ import ( "testing" "time" + "github.com/ava-labs/avalanchego/codec" + "github.com/ava-labs/avalanchego/codec/linearcodec" + "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/network/p2p" "github.com/ava-labs/avalanchego/snow/engine/common" "github.com/ava-labs/avalanchego/snow/engine/enginetest" "github.com/ava-labs/avalanchego/snow/snowtest" "github.com/ava-labs/avalanchego/utils/set" + "github.com/ava-labs/avalanchego/version" "github.com/prometheus/client_golang/prometheus" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/ava-labs/coreth/plugin/evm/message" - - "github.com/ava-labs/avalanchego/codec" - "github.com/ava-labs/avalanchego/codec/linearcodec" - "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/avalanchego/version" ) const ( diff --git a/network/peer_tracker.go b/network/peer_tracker.go index 12f37b812e..789d22ef52 100644 --- a/network/peer_tracker.go +++ b/network/peer_tracker.go @@ -9,13 +9,12 @@ import ( "time" "github.com/ava-labs/avalanchego/ids" - utils_math "github.com/ava-labs/avalanchego/utils/math" "github.com/ava-labs/avalanchego/utils/set" "github.com/ava-labs/avalanchego/version" - "github.com/ava-labs/libevm/log" - "github.com/ava-labs/libevm/metrics" + + safemath "github.com/ava-labs/avalanchego/utils/math" ) const ( @@ -34,7 +33,7 @@ const ( // information we track on a given peer type peerInfo struct { version *version.Application - bandwidth utils_math.Averager + bandwidth safemath.Averager } // peerTracker tracks the bandwidth of responses coming from peers, @@ -46,10 +45,10 @@ type peerTracker struct { numTrackedPeers metrics.Gauge trackedPeers set.Set[ids.NodeID] // peers that we have sent a request to numResponsivePeers metrics.Gauge - responsivePeers set.Set[ids.NodeID] // peers that responded to the last request they were sent - bandwidthHeap utils_math.AveragerHeap // tracks bandwidth peers are responding with + responsivePeers set.Set[ids.NodeID] // peers that responded to the last request they were sent + bandwidthHeap safemath.AveragerHeap // tracks bandwidth peers are responding with averageBandwidthMetric metrics.GaugeFloat64 - averageBandwidth utils_math.Averager + averageBandwidth safemath.Averager } func NewPeerTracker() *peerTracker { @@ -59,9 +58,9 @@ func NewPeerTracker() *peerTracker { trackedPeers: make(set.Set[ids.NodeID]), numResponsivePeers: metrics.GetOrRegisterGauge("net_responsive_peers", nil), responsivePeers: make(set.Set[ids.NodeID]), - bandwidthHeap: utils_math.NewMaxAveragerHeap(), + bandwidthHeap: safemath.NewMaxAveragerHeap(), averageBandwidthMetric: metrics.GetOrRegisterGaugeFloat64("net_average_bandwidth", nil), - averageBandwidth: utils_math.NewAverager(0, bandwidthHalflife, time.Now()), + averageBandwidth: safemath.NewAverager(0, bandwidthHalflife, time.Now()), } } @@ -82,7 +81,7 @@ func (p *peerTracker) shouldTrackNewPeer() bool { // getResponsivePeer returns a random [ids.NodeID] of a peer that has responded // to a request. -func (p *peerTracker) getResponsivePeer() (ids.NodeID, utils_math.Averager, bool) { +func (p *peerTracker) getResponsivePeer() (ids.NodeID, safemath.Averager, bool) { nodeID, ok := p.responsivePeers.Peek() if !ok { return ids.NodeID{}, nil, false @@ -114,7 +113,7 @@ func (p *peerTracker) GetAnyPeer(minVersion *version.Application) (ids.NodeID, b nodeID ids.NodeID ok bool random bool - averager utils_math.Averager + averager safemath.Averager ) if rand.Float64() < randomPeerProbability { random = true @@ -145,7 +144,7 @@ func (p *peerTracker) TrackBandwidth(nodeID ids.NodeID, bandwidth float64) { now := time.Now() if peer.bandwidth == nil { - peer.bandwidth = utils_math.NewAverager(bandwidth, bandwidthHalflife, now) + peer.bandwidth = safemath.NewAverager(bandwidth, bandwidthHalflife, now) } else { peer.bandwidth.Observe(bandwidth, now) } diff --git a/params/config_extra.go b/params/config_extra.go index 77e95242c9..1d908e20b6 100644 --- a/params/config_extra.go +++ b/params/config_extra.go @@ -10,6 +10,7 @@ import ( "math/big" "github.com/ava-labs/avalanchego/upgrade" + "github.com/ava-labs/coreth/params/extras" "github.com/ava-labs/coreth/utils" ) diff --git a/params/config_extra_test.go b/params/config_extra_test.go index c9fc8b5b1b..b9e5b9414b 100644 --- a/params/config_extra_test.go +++ b/params/config_extra_test.go @@ -8,9 +8,10 @@ import ( "testing" "github.com/ava-labs/avalanchego/upgrade/upgradetest" + "github.com/stretchr/testify/require" + "github.com/ava-labs/coreth/params/extras" "github.com/ava-labs/coreth/utils" - "github.com/stretchr/testify/require" ) func TestSetEthUpgrades(t *testing.T) { diff --git a/params/config_libevm.go b/params/config_libevm.go index 5552728274..49566d34c7 100644 --- a/params/config_libevm.go +++ b/params/config_libevm.go @@ -6,10 +6,12 @@ package params import ( "math/big" + "github.com/ava-labs/libevm/common" + "github.com/ava-labs/coreth/params/extras" "github.com/ava-labs/coreth/precompile/modules" "github.com/ava-labs/coreth/precompile/precompileconfig" - "github.com/ava-labs/libevm/common" + ethparams "github.com/ava-labs/libevm/params" ) diff --git a/params/extras/config.go b/params/extras/config.go index f21a62a43c..99c529712b 100644 --- a/params/extras/config.go +++ b/params/extras/config.go @@ -9,8 +9,10 @@ import ( "math/big" "github.com/ava-labs/avalanchego/snow" - "github.com/ava-labs/coreth/utils" "github.com/ava-labs/libevm/common" + + "github.com/ava-labs/coreth/utils" + ethparams "github.com/ava-labs/libevm/params" ) @@ -156,7 +158,7 @@ func (c *ChainConfig) Description() string { if err != nil { upgradeConfigBytes = []byte("cannot marshal UpgradeConfig") } - banner += fmt.Sprintf("Upgrade Config: %s", string(upgradeConfigBytes)) + banner += "Upgrade Config: " + string(upgradeConfigBytes) banner += "\n" return banner } diff --git a/params/extras/config_extra_test.go b/params/extras/config_extra_test.go index fe63ac1f77..272b44ff5f 100644 --- a/params/extras/config_extra_test.go +++ b/params/extras/config_extra_test.go @@ -6,8 +6,9 @@ package extras import ( "testing" - "github.com/ava-labs/coreth/utils" "github.com/stretchr/testify/assert" + + "github.com/ava-labs/coreth/utils" ) func TestIsTimestampForked(t *testing.T) { diff --git a/params/extras/network_upgrades.go b/params/extras/network_upgrades.go index 474376c7cb..18d04e2877 100644 --- a/params/extras/network_upgrades.go +++ b/params/extras/network_upgrades.go @@ -6,9 +6,12 @@ package extras import ( "fmt" "reflect" + "strconv" "github.com/ava-labs/avalanchego/upgrade" + "github.com/ava-labs/coreth/utils" + ethparams "github.com/ava-labs/libevm/params" ) @@ -284,5 +287,5 @@ func ptrToString(val *uint64) string { if val == nil { return "nil" } - return fmt.Sprintf("%d", *val) + return strconv.FormatUint(*val, 10) } diff --git a/params/extras/precompile_upgrade.go b/params/extras/precompile_upgrade.go index 859add0188..757d793362 100644 --- a/params/extras/precompile_upgrade.go +++ b/params/extras/precompile_upgrade.go @@ -8,10 +8,12 @@ import ( "errors" "fmt" + "github.com/ava-labs/libevm/common" + "github.com/ava-labs/coreth/precompile/modules" "github.com/ava-labs/coreth/precompile/precompileconfig" "github.com/ava-labs/coreth/utils" - "github.com/ava-labs/libevm/common" + ethparams "github.com/ava-labs/libevm/params" ) diff --git a/params/extras/rules.go b/params/extras/rules.go index f353c2dcb6..80a27cf08f 100644 --- a/params/extras/rules.go +++ b/params/extras/rules.go @@ -4,8 +4,9 @@ package extras import ( - "github.com/ava-labs/coreth/precompile/precompileconfig" "github.com/ava-labs/libevm/common" + + "github.com/ava-labs/coreth/precompile/precompileconfig" ) type Rules struct { diff --git a/params/hooks_libevm.go b/params/hooks_libevm.go index 7818a4b3bd..73d81d5275 100644 --- a/params/hooks_libevm.go +++ b/params/hooks_libevm.go @@ -10,17 +10,19 @@ import ( "slices" "github.com/ava-labs/avalanchego/snow" + "github.com/ava-labs/libevm/common" + "github.com/ava-labs/libevm/core/vm" + "github.com/ava-labs/libevm/libevm" + "github.com/ava-labs/libevm/libevm/legacy" + "github.com/ava-labs/coreth/nativeasset" "github.com/ava-labs/coreth/params/extras" - customheader "github.com/ava-labs/coreth/plugin/evm/header" "github.com/ava-labs/coreth/precompile/contract" "github.com/ava-labs/coreth/precompile/modules" "github.com/ava-labs/coreth/precompile/precompileconfig" "github.com/ava-labs/coreth/predicate" - "github.com/ava-labs/libevm/common" - "github.com/ava-labs/libevm/core/vm" - "github.com/ava-labs/libevm/libevm" - "github.com/ava-labs/libevm/libevm/legacy" + + customheader "github.com/ava-labs/coreth/plugin/evm/header" ethparams "github.com/ava-labs/libevm/params" ) diff --git a/plugin/evm/admin.go b/plugin/evm/admin.go index 060a83ece2..7060778f31 100644 --- a/plugin/evm/admin.go +++ b/plugin/evm/admin.go @@ -9,8 +9,9 @@ import ( "github.com/ava-labs/avalanchego/api" "github.com/ava-labs/avalanchego/utils/profiler" - "github.com/ava-labs/coreth/plugin/evm/client" "github.com/ava-labs/libevm/log" + + "github.com/ava-labs/coreth/plugin/evm/client" ) // Admin is the API service for admin API calls diff --git a/plugin/evm/atomic/atomictest/ops.go b/plugin/evm/atomic/atomictest/ops.go index 9449f892a2..244b282b15 100644 --- a/plugin/evm/atomic/atomictest/ops.go +++ b/plugin/evm/atomic/atomictest/ops.go @@ -4,9 +4,11 @@ package atomictest import ( - avalancheatomic "github.com/ava-labs/avalanchego/chains/atomic" "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/coreth/plugin/evm/atomic" + + avalancheatomic "github.com/ava-labs/avalanchego/chains/atomic" ) func ConvertToAtomicOps(tx *atomic.Tx) (map[ids.ID]*avalancheatomic.Requests, error) { diff --git a/plugin/evm/atomic/atomictest/tx.go b/plugin/evm/atomic/atomictest/tx.go index 922068d4ac..d9b778afc1 100644 --- a/plugin/evm/atomic/atomictest/tx.go +++ b/plugin/evm/atomic/atomictest/tx.go @@ -6,9 +6,6 @@ package atomictest import ( "math/rand" - "github.com/ava-labs/coreth/plugin/evm/atomic" - - avalancheatomic "github.com/ava-labs/avalanchego/chains/atomic" "github.com/ava-labs/avalanchego/codec" "github.com/ava-labs/avalanchego/codec/linearcodec" "github.com/ava-labs/avalanchego/ids" @@ -16,7 +13,11 @@ import ( "github.com/ava-labs/avalanchego/utils" "github.com/ava-labs/avalanchego/utils/set" "github.com/ava-labs/avalanchego/utils/wrappers" + "github.com/ava-labs/coreth/params/extras" + "github.com/ava-labs/coreth/plugin/evm/atomic" + + avalancheatomic "github.com/ava-labs/avalanchego/chains/atomic" ) // TODO: Remove this and use actual codec and transactions (export, import) diff --git a/plugin/evm/atomic/export_tx.go b/plugin/evm/atomic/export_tx.go index 5f3c6123d4..067973f364 100644 --- a/plugin/evm/atomic/export_tx.go +++ b/plugin/evm/atomic/export_tx.go @@ -9,15 +9,9 @@ import ( "fmt" "math/big" - "github.com/ava-labs/coreth/params/extras" - "github.com/ava-labs/coreth/plugin/evm/upgrade/ap0" - "github.com/ava-labs/coreth/plugin/evm/upgrade/ap5" - "github.com/holiman/uint256" - "github.com/ava-labs/avalanchego/chains/atomic" "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/snow" - avalancheutils "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" @@ -28,6 +22,13 @@ import ( "github.com/ava-labs/avalanchego/vms/secp256k1fx" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/log" + "github.com/holiman/uint256" + + "github.com/ava-labs/coreth/params/extras" + "github.com/ava-labs/coreth/plugin/evm/upgrade/ap0" + "github.com/ava-labs/coreth/plugin/evm/upgrade/ap5" + + avalancheutils "github.com/ava-labs/avalanchego/utils" ) var ( diff --git a/plugin/evm/atomic/import_tx.go b/plugin/evm/atomic/import_tx.go index 5adf697a39..3a211cf248 100644 --- a/plugin/evm/atomic/import_tx.go +++ b/plugin/evm/atomic/import_tx.go @@ -10,11 +10,6 @@ import ( "math/big" "slices" - "github.com/ava-labs/coreth/params/extras" - "github.com/ava-labs/coreth/plugin/evm/upgrade/ap0" - "github.com/ava-labs/coreth/plugin/evm/upgrade/ap5" - "github.com/holiman/uint256" - "github.com/ava-labs/avalanchego/chains/atomic" "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/snow" @@ -27,6 +22,11 @@ import ( "github.com/ava-labs/avalanchego/vms/secp256k1fx" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/log" + "github.com/holiman/uint256" + + "github.com/ava-labs/coreth/params/extras" + "github.com/ava-labs/coreth/plugin/evm/upgrade/ap0" + "github.com/ava-labs/coreth/plugin/evm/upgrade/ap5" ) var ( diff --git a/plugin/evm/atomic/state/atomic_backend.go b/plugin/evm/atomic/state/atomic_backend.go index bb5942cef1..dce65e0346 100644 --- a/plugin/evm/atomic/state/atomic_backend.go +++ b/plugin/evm/atomic/state/atomic_backend.go @@ -8,9 +8,6 @@ import ( "fmt" "time" - "github.com/ava-labs/coreth/plugin/evm/atomic" - - avalancheatomic "github.com/ava-labs/avalanchego/chains/atomic" "github.com/ava-labs/avalanchego/codec" "github.com/ava-labs/avalanchego/database" "github.com/ava-labs/avalanchego/ids" @@ -18,6 +15,10 @@ import ( "github.com/ava-labs/avalanchego/utils/wrappers" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/log" + + "github.com/ava-labs/coreth/plugin/evm/atomic" + + avalancheatomic "github.com/ava-labs/avalanchego/chains/atomic" ) const ( diff --git a/plugin/evm/atomic/state/atomic_repository.go b/plugin/evm/atomic/state/atomic_repository.go index 336de997b2..34ccc73942 100644 --- a/plugin/evm/atomic/state/atomic_repository.go +++ b/plugin/evm/atomic/state/atomic_repository.go @@ -8,11 +8,6 @@ import ( "fmt" "time" - "github.com/ava-labs/coreth/plugin/evm/atomic" - - "github.com/ava-labs/libevm/common" - "github.com/ava-labs/libevm/log" - "github.com/ava-labs/avalanchego/codec" "github.com/ava-labs/avalanchego/database" "github.com/ava-labs/avalanchego/database/prefixdb" @@ -21,6 +16,10 @@ import ( "github.com/ava-labs/avalanchego/utils" "github.com/ava-labs/avalanchego/utils/units" "github.com/ava-labs/avalanchego/utils/wrappers" + "github.com/ava-labs/libevm/common" + "github.com/ava-labs/libevm/log" + + "github.com/ava-labs/coreth/plugin/evm/atomic" ) const ( diff --git a/plugin/evm/atomic/state/atomic_repository_test.go b/plugin/evm/atomic/state/atomic_repository_test.go index 096f99dfe2..efeed2fa25 100644 --- a/plugin/evm/atomic/state/atomic_repository_test.go +++ b/plugin/evm/atomic/state/atomic_repository_test.go @@ -6,21 +6,21 @@ package state import ( "testing" - "github.com/ava-labs/coreth/plugin/evm/atomic" - "github.com/ava-labs/coreth/plugin/evm/atomic/atomictest" - - avalancheatomic "github.com/ava-labs/avalanchego/chains/atomic" "github.com/ava-labs/avalanchego/codec" "github.com/ava-labs/avalanchego/database" + "github.com/ava-labs/avalanchego/database/memdb" "github.com/ava-labs/avalanchego/database/prefixdb" "github.com/ava-labs/avalanchego/database/versiondb" + "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/utils" "github.com/ava-labs/avalanchego/utils/set" "github.com/ava-labs/avalanchego/utils/wrappers" "github.com/stretchr/testify/assert" - "github.com/ava-labs/avalanchego/database/memdb" - "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/coreth/plugin/evm/atomic" + "github.com/ava-labs/coreth/plugin/evm/atomic/atomictest" + + avalancheatomic "github.com/ava-labs/avalanchego/chains/atomic" ) // addTxs writes [txsPerHeight] txs for heights ranging in [fromHeight, toHeight) directly to [acceptedAtomicTxDB], diff --git a/plugin/evm/atomic/state/atomic_state.go b/plugin/evm/atomic/state/atomic_state.go index db33d3d5d8..f8bd0ca51e 100644 --- a/plugin/evm/atomic/state/atomic_state.go +++ b/plugin/evm/atomic/state/atomic_state.go @@ -6,13 +6,14 @@ package state import ( "fmt" - "github.com/ava-labs/coreth/plugin/evm/atomic" - - avalancheatomic "github.com/ava-labs/avalanchego/chains/atomic" "github.com/ava-labs/avalanchego/database" "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/log" + + "github.com/ava-labs/coreth/plugin/evm/atomic" + + avalancheatomic "github.com/ava-labs/avalanchego/chains/atomic" ) // atomicState implements the AtomicState interface using diff --git a/plugin/evm/atomic/state/atomic_trie.go b/plugin/evm/atomic/state/atomic_trie.go index adaaee327e..0fbdd59ff5 100644 --- a/plugin/evm/atomic/state/atomic_trie.go +++ b/plugin/evm/atomic/state/atomic_trie.go @@ -6,17 +6,10 @@ package state import ( "fmt" - "github.com/ava-labs/coreth/plugin/evm/atomic" - - avalancheatomic "github.com/ava-labs/avalanchego/chains/atomic" "github.com/ava-labs/avalanchego/codec" - avalanchedatabase "github.com/ava-labs/avalanchego/database" "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/utils/units" "github.com/ava-labs/avalanchego/utils/wrappers" - - "github.com/ava-labs/coreth/plugin/evm/database" - "github.com/ava-labs/coreth/triedb/hashdb" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/rawdb" "github.com/ava-labs/libevm/core/types" @@ -25,6 +18,13 @@ import ( "github.com/ava-labs/libevm/trie" "github.com/ava-labs/libevm/trie/trienode" "github.com/ava-labs/libevm/triedb" + + "github.com/ava-labs/coreth/plugin/evm/atomic" + "github.com/ava-labs/coreth/plugin/evm/database" + "github.com/ava-labs/coreth/triedb/hashdb" + + avalancheatomic "github.com/ava-labs/avalanchego/chains/atomic" + avalanchedatabase "github.com/ava-labs/avalanchego/database" ) const ( diff --git a/plugin/evm/atomic/state/atomic_trie_iterator.go b/plugin/evm/atomic/state/atomic_trie_iterator.go index 41cff3b1b4..8bc9c897f7 100644 --- a/plugin/evm/atomic/state/atomic_trie_iterator.go +++ b/plugin/evm/atomic/state/atomic_trie_iterator.go @@ -11,7 +11,6 @@ import ( "github.com/ava-labs/avalanchego/codec" "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/utils/wrappers" - "github.com/ava-labs/libevm/trie" ) diff --git a/plugin/evm/atomic/state/atomic_trie_iterator_test.go b/plugin/evm/atomic/state/atomic_trie_iterator_test.go index afa7cb3777..e12655f1af 100644 --- a/plugin/evm/atomic/state/atomic_trie_iterator_test.go +++ b/plugin/evm/atomic/state/atomic_trie_iterator_test.go @@ -6,9 +6,6 @@ package state import ( "testing" - "github.com/ava-labs/coreth/plugin/evm/atomic/atomictest" - - avalancheatomic "github.com/ava-labs/avalanchego/chains/atomic" "github.com/ava-labs/avalanchego/database/memdb" "github.com/ava-labs/avalanchego/database/versiondb" "github.com/ava-labs/avalanchego/ids" @@ -16,6 +13,10 @@ import ( "github.com/ava-labs/libevm/common" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + + "github.com/ava-labs/coreth/plugin/evm/atomic/atomictest" + + avalancheatomic "github.com/ava-labs/avalanchego/chains/atomic" ) func TestIteratorCanIterate(t *testing.T) { diff --git a/plugin/evm/atomic/state/atomic_trie_test.go b/plugin/evm/atomic/state/atomic_trie_test.go index 2f165f3d7c..4e9d19c72c 100644 --- a/plugin/evm/atomic/state/atomic_trie_test.go +++ b/plugin/evm/atomic/state/atomic_trie_test.go @@ -8,15 +8,6 @@ import ( "encoding/hex" "testing" - "github.com/ava-labs/coreth/plugin/evm/atomic" - "github.com/ava-labs/coreth/plugin/evm/atomic/atomictest" - - "github.com/prometheus/client_golang/prometheus" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - avalancheatomic "github.com/ava-labs/avalanchego/chains/atomic" "github.com/ava-labs/avalanchego/codec" "github.com/ava-labs/avalanchego/database" "github.com/ava-labs/avalanchego/database/leveldb" @@ -31,6 +22,14 @@ import ( "github.com/ava-labs/libevm/core/types" "github.com/ava-labs/libevm/rlp" "github.com/ava-labs/libevm/trie/trienode" + "github.com/prometheus/client_golang/prometheus" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/ava-labs/coreth/plugin/evm/atomic" + "github.com/ava-labs/coreth/plugin/evm/atomic/atomictest" + + avalancheatomic "github.com/ava-labs/avalanchego/chains/atomic" ) const testCommitInterval = 100 diff --git a/plugin/evm/atomic/sync/extender.go b/plugin/evm/atomic/sync/extender.go index 5a6a1c4e70..3544d239dd 100644 --- a/plugin/evm/atomic/sync/extender.go +++ b/plugin/evm/atomic/sync/extender.go @@ -6,11 +6,11 @@ import ( "context" "fmt" - "github.com/ava-labs/coreth/plugin/evm/atomic/state" - "github.com/ava-labs/avalanchego/database/versiondb" + "github.com/ava-labs/coreth/plugin/evm/atomic/state" "github.com/ava-labs/coreth/plugin/evm/message" + synccommon "github.com/ava-labs/coreth/sync" syncclient "github.com/ava-labs/coreth/sync/client" ) diff --git a/plugin/evm/atomic/sync/leaf_handler.go b/plugin/evm/atomic/sync/leaf_handler.go index 57c03448af..c738696373 100644 --- a/plugin/evm/atomic/sync/leaf_handler.go +++ b/plugin/evm/atomic/sync/leaf_handler.go @@ -9,12 +9,12 @@ import ( "github.com/ava-labs/avalanchego/codec" "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/libevm/metrics" + "github.com/ava-labs/libevm/triedb" + "github.com/ava-labs/coreth/plugin/evm/message" "github.com/ava-labs/coreth/sync/handlers" "github.com/ava-labs/coreth/sync/handlers/stats" - - "github.com/ava-labs/libevm/metrics" - "github.com/ava-labs/libevm/triedb" ) var ( diff --git a/plugin/evm/atomic/sync/summary.go b/plugin/evm/atomic/sync/summary.go index 56f84ea64b..109122460f 100644 --- a/plugin/evm/atomic/sync/summary.go +++ b/plugin/evm/atomic/sync/summary.go @@ -8,12 +8,11 @@ import ( "fmt" "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/coreth/plugin/evm/message" - "github.com/ava-labs/avalanchego/snow/engine/snowman/block" - "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/crypto" + + "github.com/ava-labs/coreth/plugin/evm/message" ) var _ message.Syncable = (*Summary)(nil) diff --git a/plugin/evm/atomic/sync/summary_parser.go b/plugin/evm/atomic/sync/summary_parser.go index ebe3be1895..e35446086b 100644 --- a/plugin/evm/atomic/sync/summary_parser.go +++ b/plugin/evm/atomic/sync/summary_parser.go @@ -6,8 +6,9 @@ import ( "fmt" "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/coreth/plugin/evm/message" "github.com/ava-labs/libevm/crypto" + + "github.com/ava-labs/coreth/plugin/evm/message" ) var _ message.SyncableParser = (*summaryParser)(nil) diff --git a/plugin/evm/atomic/sync/summary_provider.go b/plugin/evm/atomic/sync/summary_provider.go index 7577a5199f..a10be898e1 100644 --- a/plugin/evm/atomic/sync/summary_provider.go +++ b/plugin/evm/atomic/sync/summary_provider.go @@ -5,14 +5,12 @@ package sync import ( "fmt" - "github.com/ava-labs/coreth/plugin/evm/atomic/state" - "github.com/ava-labs/avalanchego/snow/engine/snowman/block" - - "github.com/ava-labs/coreth/sync" - "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/types" + + "github.com/ava-labs/coreth/plugin/evm/atomic/state" + "github.com/ava-labs/coreth/sync" ) var _ sync.SummaryProvider = (*SummaryProvider)(nil) diff --git a/plugin/evm/atomic/sync/summary_test.go b/plugin/evm/atomic/sync/summary_test.go index ca6c9c5118..b617546a76 100644 --- a/plugin/evm/atomic/sync/summary_test.go +++ b/plugin/evm/atomic/sync/summary_test.go @@ -10,9 +10,10 @@ import ( "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/snow/engine/snowman/block" - "github.com/ava-labs/coreth/plugin/evm/message" "github.com/ava-labs/libevm/common" "github.com/stretchr/testify/require" + + "github.com/ava-labs/coreth/plugin/evm/message" ) func TestMarshalSummary(t *testing.T) { diff --git a/plugin/evm/atomic/sync/syncer.go b/plugin/evm/atomic/sync/syncer.go index d0f1746236..974ad102bd 100644 --- a/plugin/evm/atomic/sync/syncer.go +++ b/plugin/evm/atomic/sync/syncer.go @@ -10,18 +10,16 @@ import ( "errors" "fmt" - atomicstate "github.com/ava-labs/coreth/plugin/evm/atomic/state" - "github.com/ava-labs/avalanchego/database/versiondb" "github.com/ava-labs/avalanchego/utils/wrappers" - "github.com/ava-labs/libevm/common" + "github.com/ava-labs/libevm/trie" "github.com/ava-labs/coreth/plugin/evm/message" + + atomicstate "github.com/ava-labs/coreth/plugin/evm/atomic/state" synccommon "github.com/ava-labs/coreth/sync" syncclient "github.com/ava-labs/coreth/sync/client" - - "github.com/ava-labs/libevm/trie" ) const ( diff --git a/plugin/evm/atomic/sync/syncer_test.go b/plugin/evm/atomic/sync/syncer_test.go index 1ec861ed5a..e7e20bded6 100644 --- a/plugin/evm/atomic/sync/syncer_test.go +++ b/plugin/evm/atomic/sync/syncer_test.go @@ -10,24 +10,23 @@ import ( "math/rand" "testing" - "github.com/ava-labs/coreth/plugin/evm/atomic/atomictest" - "github.com/ava-labs/coreth/plugin/evm/atomic/state" - - "github.com/stretchr/testify/require" - "github.com/ava-labs/avalanchego/database" "github.com/ava-labs/avalanchego/database/memdb" "github.com/ava-labs/avalanchego/database/versiondb" - - "github.com/ava-labs/coreth/plugin/evm/message" - syncclient "github.com/ava-labs/coreth/sync/client" - "github.com/ava-labs/coreth/sync/handlers" - handlerstats "github.com/ava-labs/coreth/sync/handlers/stats" - "github.com/ava-labs/coreth/sync/statesync/statesynctest" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/rawdb" "github.com/ava-labs/libevm/trie" "github.com/ava-labs/libevm/triedb" + "github.com/stretchr/testify/require" + + "github.com/ava-labs/coreth/plugin/evm/atomic/atomictest" + "github.com/ava-labs/coreth/plugin/evm/atomic/state" + "github.com/ava-labs/coreth/plugin/evm/message" + "github.com/ava-labs/coreth/sync/handlers" + "github.com/ava-labs/coreth/sync/statesync/statesynctest" + + syncclient "github.com/ava-labs/coreth/sync/client" + handlerstats "github.com/ava-labs/coreth/sync/handlers/stats" ) const ( diff --git a/plugin/evm/atomic/tx.go b/plugin/evm/atomic/tx.go index 096161a0e8..463c83f555 100644 --- a/plugin/evm/atomic/tx.go +++ b/plugin/evm/atomic/tx.go @@ -10,11 +10,6 @@ import ( "math/big" "sort" - "github.com/ava-labs/libevm/common" - "github.com/holiman/uint256" - - "github.com/ava-labs/coreth/params/extras" - "github.com/ava-labs/avalanchego/chains/atomic" "github.com/ava-labs/avalanchego/codec" "github.com/ava-labs/avalanchego/ids" @@ -26,6 +21,10 @@ import ( "github.com/ava-labs/avalanchego/utils/wrappers" "github.com/ava-labs/avalanchego/vms/components/verify" "github.com/ava-labs/avalanchego/vms/secp256k1fx" + "github.com/ava-labs/libevm/common" + "github.com/holiman/uint256" + + "github.com/ava-labs/coreth/params/extras" ) var _ gossip.Gossipable = (*Tx)(nil) diff --git a/plugin/evm/atomic/tx_test.go b/plugin/evm/atomic/tx_test.go index 41c2181f91..1b10fe6f20 100644 --- a/plugin/evm/atomic/tx_test.go +++ b/plugin/evm/atomic/tx_test.go @@ -11,9 +11,10 @@ import ( "github.com/ava-labs/avalanchego/utils/units" "github.com/ava-labs/avalanchego/vms/components/avax" "github.com/ava-labs/avalanchego/vms/secp256k1fx" - "github.com/ava-labs/coreth/plugin/evm/upgrade/ap5" "github.com/holiman/uint256" "github.com/stretchr/testify/require" + + "github.com/ava-labs/coreth/plugin/evm/upgrade/ap5" ) func TestEffectiveGasPrice(t *testing.T) { diff --git a/plugin/evm/atomic/txpool/mempool.go b/plugin/evm/atomic/txpool/mempool.go index 7788ff2ed8..7fdd9b3a30 100644 --- a/plugin/evm/atomic/txpool/mempool.go +++ b/plugin/evm/atomic/txpool/mempool.go @@ -9,11 +9,12 @@ import ( "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/network/p2p/gossip" - "github.com/ava-labs/coreth/plugin/evm/atomic" - "github.com/ava-labs/coreth/plugin/evm/config" "github.com/ava-labs/libevm/log" "github.com/holiman/uint256" "github.com/prometheus/client_golang/prometheus" + + "github.com/ava-labs/coreth/plugin/evm/atomic" + "github.com/ava-labs/coreth/plugin/evm/config" ) var ( diff --git a/plugin/evm/atomic/txpool/mempool_test.go b/plugin/evm/atomic/txpool/mempool_test.go index f98a53d466..e7b7befac2 100644 --- a/plugin/evm/atomic/txpool/mempool_test.go +++ b/plugin/evm/atomic/txpool/mempool_test.go @@ -9,11 +9,12 @@ import ( "github.com/ava-labs/avalanchego/snow/snowtest" "github.com/ava-labs/avalanchego/utils/bloom" + "github.com/prometheus/client_golang/prometheus" + "github.com/stretchr/testify/require" + "github.com/ava-labs/coreth/plugin/evm/atomic" "github.com/ava-labs/coreth/plugin/evm/atomic/atomictest" "github.com/ava-labs/coreth/plugin/evm/config" - "github.com/prometheus/client_golang/prometheus" - "github.com/stretchr/testify/require" ) func TestMempoolAddTx(t *testing.T) { diff --git a/plugin/evm/atomic/txpool/tx_heap.go b/plugin/evm/atomic/txpool/tx_heap.go index dc3c5f6839..5042eb0c34 100644 --- a/plugin/evm/atomic/txpool/tx_heap.go +++ b/plugin/evm/atomic/txpool/tx_heap.go @@ -6,10 +6,10 @@ package txpool import ( "container/heap" - "github.com/ava-labs/coreth/plugin/evm/atomic" + "github.com/ava-labs/avalanchego/ids" "github.com/holiman/uint256" - "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/coreth/plugin/evm/atomic" ) type txEntry struct { diff --git a/plugin/evm/atomic/txpool/tx_heap_test.go b/plugin/evm/atomic/txpool/tx_heap_test.go index 14fc911c8a..bac88a4ac8 100644 --- a/plugin/evm/atomic/txpool/tx_heap_test.go +++ b/plugin/evm/atomic/txpool/tx_heap_test.go @@ -6,10 +6,10 @@ package txpool import ( "testing" - "github.com/ava-labs/coreth/plugin/evm/atomic" "github.com/holiman/uint256" - "github.com/stretchr/testify/assert" + + "github.com/ava-labs/coreth/plugin/evm/atomic" ) func TestTxHeap(t *testing.T) { diff --git a/plugin/evm/atomic/txpool/txs.go b/plugin/evm/atomic/txpool/txs.go index 800f3246e9..df934e04ee 100644 --- a/plugin/evm/atomic/txpool/txs.go +++ b/plugin/evm/atomic/txpool/txs.go @@ -9,9 +9,9 @@ import ( "github.com/ava-labs/avalanchego/cache/lru" "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/snow" + "github.com/ava-labs/libevm/log" "github.com/ava-labs/coreth/plugin/evm/atomic" - "github.com/ava-labs/libevm/log" ) const discardedTxsCacheSize = 50 diff --git a/plugin/evm/atomic/vm/api.go b/plugin/evm/atomic/vm/api.go index 640dd7ba37..30b4673a59 100644 --- a/plugin/evm/atomic/vm/api.go +++ b/plugin/evm/atomic/vm/api.go @@ -14,10 +14,11 @@ import ( "github.com/ava-labs/avalanchego/utils/json" "github.com/ava-labs/avalanchego/utils/set" "github.com/ava-labs/avalanchego/vms/components/avax" + "github.com/ava-labs/libevm/log" + "github.com/ava-labs/coreth/plugin/evm/atomic" "github.com/ava-labs/coreth/plugin/evm/atomic/txpool" "github.com/ava-labs/coreth/plugin/evm/client" - "github.com/ava-labs/libevm/log" ) const ( diff --git a/plugin/evm/atomic/vm/block_extension.go b/plugin/evm/atomic/vm/block_extension.go index 935dc2d007..c73addf99a 100644 --- a/plugin/evm/atomic/vm/block_extension.go +++ b/plugin/evm/atomic/vm/block_extension.go @@ -7,19 +7,18 @@ import ( "errors" "fmt" - "github.com/ava-labs/coreth/plugin/evm/atomic" - "github.com/ava-labs/avalanchego/database" - safemath "github.com/ava-labs/avalanchego/utils/math" + "github.com/ava-labs/libevm/common" + "github.com/ava-labs/libevm/log" "github.com/ava-labs/coreth/params/extras" + "github.com/ava-labs/coreth/plugin/evm/atomic" "github.com/ava-labs/coreth/plugin/evm/customtypes" "github.com/ava-labs/coreth/plugin/evm/extension" "github.com/ava-labs/coreth/plugin/evm/upgrade/ap5" "github.com/ava-labs/coreth/utils" - "github.com/ava-labs/libevm/common" - "github.com/ava-labs/libevm/log" + safemath "github.com/ava-labs/avalanchego/utils/math" ) var ( @@ -260,7 +259,7 @@ func (be *blockExtension) verifyUTXOsPresent(atomicTxs []*atomic.Tx) error { return err } if _, err := vm.Ctx.SharedMemory.Get(chainID, requests.RemoveRequests); err != nil { - return fmt.Errorf("%w: %s", ErrMissingUTXOs, err) + return fmt.Errorf("%w: %w", ErrMissingUTXOs, err) } } return nil diff --git a/plugin/evm/atomic/vm/export_tx_test.go b/plugin/evm/atomic/vm/export_tx_test.go index 2201c869b0..44e1c826d6 100644 --- a/plugin/evm/atomic/vm/export_tx_test.go +++ b/plugin/evm/atomic/vm/export_tx_test.go @@ -9,25 +9,27 @@ import ( "math/big" "testing" - avalancheatomic "github.com/ava-labs/avalanchego/chains/atomic" "github.com/ava-labs/avalanchego/ids" - commonEng "github.com/ava-labs/avalanchego/snow/engine/common" "github.com/ava-labs/avalanchego/snow/snowtest" "github.com/ava-labs/avalanchego/upgrade/upgradetest" - avalancheutils "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/units" "github.com/ava-labs/avalanchego/vms/components/avax" "github.com/ava-labs/avalanchego/vms/secp256k1fx" + "github.com/ava-labs/libevm/common" + "github.com/holiman/uint256" + "github.com/stretchr/testify/require" + "github.com/ava-labs/coreth/core/extstate" "github.com/ava-labs/coreth/params/extras" "github.com/ava-labs/coreth/plugin/evm/atomic" "github.com/ava-labs/coreth/plugin/evm/vmtest" "github.com/ava-labs/coreth/utils" - "github.com/ava-labs/libevm/common" - "github.com/holiman/uint256" - "github.com/stretchr/testify/require" + + avalancheatomic "github.com/ava-labs/avalanchego/chains/atomic" + commonEng "github.com/ava-labs/avalanchego/snow/engine/common" + avalancheutils "github.com/ava-labs/avalanchego/utils" ) // createExportTxOptions adds funds to shared memory, imports them, and returns a list of export transactions diff --git a/plugin/evm/atomic/vm/ext_data_hashes.go b/plugin/evm/atomic/vm/ext_data_hashes.go index af310db66a..75d40d95b7 100644 --- a/plugin/evm/atomic/vm/ext_data_hashes.go +++ b/plugin/evm/atomic/vm/ext_data_hashes.go @@ -4,10 +4,11 @@ package vm import ( - _ "embed" "encoding/json" "github.com/ava-labs/libevm/common" + + _ "embed" ) var ( diff --git a/plugin/evm/atomic/vm/gossiper_atomic_gossiping_test.go b/plugin/evm/atomic/vm/gossiper_atomic_gossiping_test.go index 99aa9375b6..079eab5420 100644 --- a/plugin/evm/atomic/vm/gossiper_atomic_gossiping_test.go +++ b/plugin/evm/atomic/vm/gossiper_atomic_gossiping_test.go @@ -17,10 +17,10 @@ import ( "github.com/stretchr/testify/assert" "google.golang.org/protobuf/proto" - commonEng "github.com/ava-labs/avalanchego/snow/engine/common" - "github.com/ava-labs/coreth/plugin/evm/atomic" "github.com/ava-labs/coreth/plugin/evm/vmtest" + + commonEng "github.com/ava-labs/avalanchego/snow/engine/common" ) // show that a txID discovered from gossip is requested to the same node only if diff --git a/plugin/evm/atomic/vm/import_tx_test.go b/plugin/evm/atomic/vm/import_tx_test.go index f6ca03d856..10ecbfe527 100644 --- a/plugin/evm/atomic/vm/import_tx_test.go +++ b/plugin/evm/atomic/vm/import_tx_test.go @@ -9,26 +9,27 @@ import ( "strings" "testing" + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/snow/snowtest" + "github.com/ava-labs/avalanchego/upgrade/upgradetest" + "github.com/ava-labs/avalanchego/utils/constants" + "github.com/ava-labs/avalanchego/utils/crypto/secp256k1" + "github.com/ava-labs/avalanchego/utils/set" + "github.com/ava-labs/avalanchego/vms/components/avax" + "github.com/ava-labs/avalanchego/vms/secp256k1fx" + "github.com/ava-labs/libevm/common" + "github.com/holiman/uint256" + "github.com/stretchr/testify/require" + "github.com/ava-labs/coreth/core/extstate" "github.com/ava-labs/coreth/plugin/evm/atomic" "github.com/ava-labs/coreth/plugin/evm/upgrade/ap0" "github.com/ava-labs/coreth/plugin/evm/vmtest" "github.com/ava-labs/coreth/utils" - "github.com/ava-labs/libevm/common" - "github.com/holiman/uint256" - "github.com/stretchr/testify/require" avalancheatomic "github.com/ava-labs/avalanchego/chains/atomic" - "github.com/ava-labs/avalanchego/ids" commonEng "github.com/ava-labs/avalanchego/snow/engine/common" - "github.com/ava-labs/avalanchego/snow/snowtest" - "github.com/ava-labs/avalanchego/upgrade/upgradetest" avalancheutils "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/set" - "github.com/ava-labs/avalanchego/vms/components/avax" - "github.com/ava-labs/avalanchego/vms/secp256k1fx" ) // createImportTxOptions adds a UTXO to shared memory and generates a list of import transactions sending this UTXO diff --git a/plugin/evm/atomic/vm/syncervm_test.go b/plugin/evm/atomic/vm/syncervm_test.go index 073ea34d1a..5f8bf38cdf 100644 --- a/plugin/evm/atomic/vm/syncervm_test.go +++ b/plugin/evm/atomic/vm/syncervm_test.go @@ -9,6 +9,10 @@ import ( "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/utils/hashing" "github.com/ava-labs/avalanchego/utils/units" + "github.com/ava-labs/libevm/common" + "github.com/ava-labs/libevm/core/types" + "github.com/stretchr/testify/require" + "github.com/ava-labs/coreth/consensus/dummy" "github.com/ava-labs/coreth/core" "github.com/ava-labs/coreth/core/extstate" @@ -18,11 +22,6 @@ import ( "github.com/ava-labs/coreth/plugin/evm/extension" "github.com/ava-labs/coreth/plugin/evm/vmtest" "github.com/ava-labs/coreth/predicate" - - "github.com/ava-labs/libevm/common" - "github.com/ava-labs/libevm/core/types" - - "github.com/stretchr/testify/require" ) func TestAtomicSyncerVM(t *testing.T) { diff --git a/plugin/evm/atomic/vm/tx_gossip_test.go b/plugin/evm/atomic/vm/tx_gossip_test.go index 65c43067f4..47b10fcc76 100644 --- a/plugin/evm/atomic/vm/tx_gossip_test.go +++ b/plugin/evm/atomic/vm/tx_gossip_test.go @@ -10,7 +10,6 @@ import ( "testing" "time" - avalancheatomic "github.com/ava-labs/avalanchego/chains/atomic" "github.com/ava-labs/avalanchego/database/memdb" "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/network/p2p" @@ -20,16 +19,13 @@ import ( "github.com/ava-labs/avalanchego/snow/engine/enginetest" "github.com/ava-labs/avalanchego/snow/snowtest" "github.com/ava-labs/avalanchego/snow/validators" - agoUtils "github.com/ava-labs/avalanchego/utils" "github.com/ava-labs/avalanchego/utils/crypto/secp256k1" "github.com/ava-labs/avalanchego/utils/logging" "github.com/ava-labs/avalanchego/utils/set" - "github.com/prometheus/client_golang/prometheus" - "github.com/stretchr/testify/require" - "github.com/ava-labs/avalanchego/vms/components/avax" "github.com/ava-labs/avalanchego/vms/secp256k1fx" - + "github.com/prometheus/client_golang/prometheus" + "github.com/stretchr/testify/require" "google.golang.org/protobuf/proto" "github.com/ava-labs/coreth/plugin/evm/atomic" @@ -37,6 +33,9 @@ import ( "github.com/ava-labs/coreth/plugin/evm/vmtest" "github.com/ava-labs/coreth/utils" "github.com/ava-labs/coreth/utils/utilstest" + + avalancheatomic "github.com/ava-labs/avalanchego/chains/atomic" + agoUtils "github.com/ava-labs/avalanchego/utils" ) func TestAtomicTxGossip(t *testing.T) { diff --git a/plugin/evm/atomic/vm/tx_semantic_verifier.go b/plugin/evm/atomic/vm/tx_semantic_verifier.go index 7bdb4e2c47..e8c8c9106e 100644 --- a/plugin/evm/atomic/vm/tx_semantic_verifier.go +++ b/plugin/evm/atomic/vm/tx_semantic_verifier.go @@ -16,6 +16,7 @@ import ( "github.com/ava-labs/avalanchego/vms/components/avax" "github.com/ava-labs/avalanchego/vms/platformvm/fx" "github.com/ava-labs/avalanchego/vms/secp256k1fx" + "github.com/ava-labs/coreth/params/extras" "github.com/ava-labs/coreth/plugin/evm/atomic" "github.com/ava-labs/coreth/plugin/evm/extension" diff --git a/plugin/evm/atomic/vm/tx_test.go b/plugin/evm/atomic/vm/tx_test.go index 404edae2da..e852f6ca53 100644 --- a/plugin/evm/atomic/vm/tx_test.go +++ b/plugin/evm/atomic/vm/tx_test.go @@ -7,18 +7,17 @@ import ( "math/big" "testing" - "github.com/stretchr/testify/require" - + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/snow" + "github.com/ava-labs/avalanchego/upgrade/upgradetest" "github.com/ava-labs/libevm/common" + "github.com/stretchr/testify/require" "github.com/ava-labs/coreth/params/extras" "github.com/ava-labs/coreth/plugin/evm/atomic" "github.com/ava-labs/coreth/utils" avalancheatomic "github.com/ava-labs/avalanchego/chains/atomic" - "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/avalanchego/snow" - "github.com/ava-labs/avalanchego/upgrade/upgradetest" ) func TestCalculateDynamicFee(t *testing.T) { diff --git a/plugin/evm/atomic/vm/vm.go b/plugin/evm/atomic/vm/vm.go index f836a01ef8..f03e6ab70a 100644 --- a/plugin/evm/atomic/vm/vm.go +++ b/plugin/evm/atomic/vm/vm.go @@ -11,23 +11,13 @@ import ( "net/http" "sync" - atomicsync "github.com/ava-labs/coreth/plugin/evm/atomic/sync" - - "github.com/ava-labs/coreth/plugin/evm/atomic" - atomicstate "github.com/ava-labs/coreth/plugin/evm/atomic/state" - "github.com/ava-labs/coreth/plugin/evm/atomic/txpool" - "github.com/ava-labs/avalanchego/codec" "github.com/ava-labs/avalanchego/codec/linearcodec" - avalanchedatabase "github.com/ava-labs/avalanchego/database" "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/network/p2p" - avalanchegossip "github.com/ava-labs/avalanchego/network/p2p/gossip" "github.com/ava-labs/avalanchego/snow" "github.com/ava-labs/avalanchego/snow/consensus/snowman" - avalanchecommon "github.com/ava-labs/avalanchego/snow/engine/common" "github.com/ava-labs/avalanchego/snow/engine/snowman/block" - avalancheutils "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/logging" @@ -36,25 +26,34 @@ import ( "github.com/ava-labs/avalanchego/utils/units" "github.com/ava-labs/avalanchego/vms/components/avax" "github.com/ava-labs/avalanchego/vms/secp256k1fx" + "github.com/ava-labs/libevm/common" + "github.com/ava-labs/libevm/core/state" + "github.com/ava-labs/libevm/core/types" + "github.com/ava-labs/libevm/log" "github.com/ava-labs/coreth/consensus/dummy" "github.com/ava-labs/coreth/core/extstate" "github.com/ava-labs/coreth/params" "github.com/ava-labs/coreth/params/extras" + "github.com/ava-labs/coreth/plugin/evm/atomic" + "github.com/ava-labs/coreth/plugin/evm/atomic/txpool" "github.com/ava-labs/coreth/plugin/evm/config" "github.com/ava-labs/coreth/plugin/evm/customtypes" "github.com/ava-labs/coreth/plugin/evm/extension" "github.com/ava-labs/coreth/plugin/evm/gossip" - customheader "github.com/ava-labs/coreth/plugin/evm/header" "github.com/ava-labs/coreth/plugin/evm/message" "github.com/ava-labs/coreth/plugin/evm/upgrade/ap5" "github.com/ava-labs/coreth/plugin/evm/vmerrors" "github.com/ava-labs/coreth/utils" "github.com/ava-labs/coreth/utils/rpc" - "github.com/ava-labs/libevm/common" - "github.com/ava-labs/libevm/core/state" - "github.com/ava-labs/libevm/core/types" - "github.com/ava-labs/libevm/log" + + avalanchedatabase "github.com/ava-labs/avalanchego/database" + avalanchegossip "github.com/ava-labs/avalanchego/network/p2p/gossip" + avalanchecommon "github.com/ava-labs/avalanchego/snow/engine/common" + avalancheutils "github.com/ava-labs/avalanchego/utils" + atomicstate "github.com/ava-labs/coreth/plugin/evm/atomic/state" + atomicsync "github.com/ava-labs/coreth/plugin/evm/atomic/sync" + customheader "github.com/ava-labs/coreth/plugin/evm/header" ) var ( diff --git a/plugin/evm/atomic/vm/vm_test.go b/plugin/evm/atomic/vm/vm_test.go index 0605540475..c215ec6227 100644 --- a/plugin/evm/atomic/vm/vm_test.go +++ b/plugin/evm/atomic/vm/vm_test.go @@ -14,10 +14,8 @@ import ( "testing" "time" - avalancheatomic "github.com/ava-labs/avalanchego/chains/atomic" "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/snow" - commonEng "github.com/ava-labs/avalanchego/snow/engine/common" "github.com/ava-labs/avalanchego/upgrade/upgradetest" "github.com/ava-labs/avalanchego/utils/crypto/secp256k1" "github.com/ava-labs/avalanchego/utils/hashing" @@ -26,6 +24,11 @@ import ( "github.com/ava-labs/avalanchego/vms/components/avax" "github.com/ava-labs/avalanchego/vms/components/chain" "github.com/ava-labs/avalanchego/vms/secp256k1fx" + "github.com/ava-labs/libevm/common" + "github.com/ava-labs/libevm/core/types" + "github.com/ava-labs/libevm/rlp" + "github.com/ava-labs/libevm/trie" + "github.com/stretchr/testify/require" "github.com/ava-labs/coreth/core" "github.com/ava-labs/coreth/core/extstate" @@ -41,12 +44,8 @@ import ( "github.com/ava-labs/coreth/plugin/evm/vmtest" "github.com/ava-labs/coreth/utils/utilstest" - "github.com/ava-labs/libevm/common" - "github.com/ava-labs/libevm/core/types" - "github.com/ava-labs/libevm/rlp" - "github.com/ava-labs/libevm/trie" - - "github.com/stretchr/testify/require" + avalancheatomic "github.com/ava-labs/avalanchego/chains/atomic" + commonEng "github.com/ava-labs/avalanchego/snow/engine/common" ) func newAtomicTestVM() *VM { @@ -111,10 +110,10 @@ func addUTXOs(sharedMemory *avalancheatomic.Memory, ctx *snow.Context, utxos map for addr, avaxAmount := range utxos { txID, err := ids.ToID(hashing.ComputeHash256(addr.Bytes())) if err != nil { - return fmt.Errorf("Failed to generate txID from addr: %s", err) + return fmt.Errorf("Failed to generate txID from addr: %w", err) } if _, err := addUTXO(sharedMemory, ctx, txID, 0, ctx.AVAXAssetID, avaxAmount, addr); err != nil { - return fmt.Errorf("Failed to add UTXO to shared memory: %s", err) + return fmt.Errorf("Failed to add UTXO to shared memory: %w", err) } } return nil diff --git a/plugin/evm/block_builder.go b/plugin/evm/block_builder.go index d281ddb4fa..d8dd0c5d5f 100644 --- a/plugin/evm/block_builder.go +++ b/plugin/evm/block_builder.go @@ -8,16 +8,17 @@ import ( "sync" "time" + "github.com/ava-labs/avalanchego/snow" + "github.com/ava-labs/avalanchego/utils/lock" + "github.com/ava-labs/libevm/log" "github.com/holiman/uint256" "go.uber.org/zap" - "github.com/ava-labs/avalanchego/snow" - commonEng "github.com/ava-labs/avalanchego/snow/engine/common" - "github.com/ava-labs/avalanchego/utils/lock" "github.com/ava-labs/coreth/core" "github.com/ava-labs/coreth/core/txpool" "github.com/ava-labs/coreth/plugin/evm/extension" - "github.com/ava-labs/libevm/log" + + commonEng "github.com/ava-labs/avalanchego/snow/engine/common" ) // Minimum amount of time to wait after building a block before attempting to build a block diff --git a/plugin/evm/client/client.go b/plugin/evm/client/client.go index d61259cb1f..866ca4da11 100644 --- a/plugin/evm/client/client.go +++ b/plugin/evm/client/client.go @@ -8,16 +8,15 @@ import ( "errors" "fmt" - "github.com/ava-labs/coreth/plugin/evm/atomic" - - "golang.org/x/exp/slog" - "github.com/ava-labs/avalanchego/api" "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/utils/formatting" "github.com/ava-labs/avalanchego/utils/formatting/address" "github.com/ava-labs/avalanchego/utils/json" "github.com/ava-labs/avalanchego/utils/rpc" + "golang.org/x/exp/slog" + + "github.com/ava-labs/coreth/plugin/evm/atomic" "github.com/ava-labs/coreth/plugin/evm/config" ) diff --git a/plugin/evm/config/config.go b/plugin/evm/config/config.go index 5df6dd7422..b69f1047eb 100644 --- a/plugin/evm/config/config.go +++ b/plugin/evm/config/config.go @@ -5,6 +5,7 @@ package config import ( "encoding/json" + "errors" "fmt" "time" @@ -240,14 +241,14 @@ func (c *Config) validate(networkID uint32) error { } if !c.Pruning && c.OfflinePruning { - return fmt.Errorf("cannot run offline pruning while pruning is disabled") + return errors.New("cannot run offline pruning while pruning is disabled") } // If pruning is enabled, the commit interval must be non-zero so the node commits state tries every CommitInterval blocks. if c.Pruning && c.CommitInterval == 0 { - return fmt.Errorf("cannot use commit interval of 0 with pruning enabled") + return errors.New("cannot use commit interval of 0 with pruning enabled") } if c.Pruning && c.StateHistory == 0 { - return fmt.Errorf("cannot use state history of 0 with pruning enabled") + return errors.New("cannot use state history of 0 with pruning enabled") } if c.PushGossipPercentStake < 0 || c.PushGossipPercentStake > 1 { diff --git a/plugin/evm/config/default_config.go b/plugin/evm/config/default_config.go index be85c14170..86f981a8c9 100644 --- a/plugin/evm/config/default_config.go +++ b/plugin/evm/config/default_config.go @@ -6,8 +6,9 @@ package config import ( "time" - "github.com/ava-labs/coreth/utils" "github.com/ava-labs/libevm/common" + + "github.com/ava-labs/coreth/utils" ) const defaultCommitInterval = 4096 diff --git a/plugin/evm/customtypes/block_ext.go b/plugin/evm/customtypes/block_ext.go index c38a2f89d1..c78e144d5c 100644 --- a/plugin/evm/customtypes/block_ext.go +++ b/plugin/evm/customtypes/block_ext.go @@ -8,8 +8,9 @@ import ( "slices" "github.com/ava-labs/libevm/common" - ethtypes "github.com/ava-labs/libevm/core/types" "github.com/ava-labs/libevm/rlp" + + ethtypes "github.com/ava-labs/libevm/core/types" ) // SetBlockExtra sets the [BlockBodyExtra] `extra` in the [Block] `b`. diff --git a/plugin/evm/customtypes/header_ext.go b/plugin/evm/customtypes/header_ext.go index 3da93a27b6..e01a8e1bb6 100644 --- a/plugin/evm/customtypes/header_ext.go +++ b/plugin/evm/customtypes/header_ext.go @@ -9,8 +9,9 @@ import ( "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/common/hexutil" - ethtypes "github.com/ava-labs/libevm/core/types" "github.com/ava-labs/libevm/rlp" + + ethtypes "github.com/ava-labs/libevm/core/types" ) // GetHeaderExtra returns the [HeaderExtra] from the given [Header]. @@ -168,6 +169,8 @@ func (h *HeaderSerializable) updateToExtras(extras *HeaderExtra) { // HeaderSerializable defines the header of a block in the Ethereum blockchain, // as it is to be serialized into RLP and JSON. Note it must be exported so that // rlpgen can generate the serialization code from it. +// +//nolint:tagalign type HeaderSerializable struct { ParentHash common.Hash `json:"parentHash" gencodec:"required"` UncleHash common.Hash `json:"sha3Uncles" gencodec:"required"` diff --git a/plugin/evm/eth_gossiper.go b/plugin/evm/eth_gossiper.go index 324c5e1b70..c78ece544e 100644 --- a/plugin/evm/eth_gossiper.go +++ b/plugin/evm/eth_gossiper.go @@ -11,18 +11,18 @@ import ( "sync" "sync/atomic" - ethcommon "github.com/ava-labs/libevm/common" - "github.com/ava-labs/libevm/log" - "github.com/prometheus/client_golang/prometheus" - "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/network/p2p/gossip" + "github.com/ava-labs/libevm/core/types" + "github.com/ava-labs/libevm/log" + "github.com/prometheus/client_golang/prometheus" "github.com/ava-labs/coreth/core" "github.com/ava-labs/coreth/core/txpool" "github.com/ava-labs/coreth/eth" "github.com/ava-labs/coreth/plugin/evm/config" - "github.com/ava-labs/libevm/core/types" + + ethcommon "github.com/ava-labs/libevm/common" ) const pendingTxsBuffer = 10 diff --git a/plugin/evm/extension/config.go b/plugin/evm/extension/config.go index 833707a448..e9d1c3e2e6 100644 --- a/plugin/evm/extension/config.go +++ b/plugin/evm/extension/config.go @@ -12,10 +12,10 @@ import ( "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/network/p2p" "github.com/ava-labs/avalanchego/snow/consensus/snowman" - avalanchecommon "github.com/ava-labs/avalanchego/snow/engine/common" - "github.com/ava-labs/avalanchego/utils/timer/mockable" - "github.com/ava-labs/avalanchego/snow/engine/snowman/block" + "github.com/ava-labs/avalanchego/utils/timer/mockable" + "github.com/ava-labs/libevm/common" + "github.com/ava-labs/libevm/core/types" "github.com/prometheus/client_golang/prometheus" "github.com/ava-labs/coreth/consensus/dummy" @@ -24,12 +24,11 @@ import ( "github.com/ava-labs/coreth/params/extras" "github.com/ava-labs/coreth/plugin/evm/config" "github.com/ava-labs/coreth/plugin/evm/message" - synccommon "github.com/ava-labs/coreth/sync" "github.com/ava-labs/coreth/sync/handlers" - vmsync "github.com/ava-labs/coreth/sync/vm" - "github.com/ava-labs/libevm/common" - "github.com/ava-labs/libevm/core/types" + avalanchecommon "github.com/ava-labs/avalanchego/snow/engine/common" + synccommon "github.com/ava-labs/coreth/sync" + vmsync "github.com/ava-labs/coreth/sync/vm" ) var ( diff --git a/plugin/evm/gossip_test.go b/plugin/evm/gossip_test.go index 8c71c559e7..6e4a8e1e4a 100644 --- a/plugin/evm/gossip_test.go +++ b/plugin/evm/gossip_test.go @@ -10,12 +10,6 @@ import ( "time" "github.com/ava-labs/avalanchego/network/p2p/gossip" - "github.com/ava-labs/coreth/consensus/dummy" - "github.com/ava-labs/coreth/core" - "github.com/ava-labs/coreth/core/txpool" - "github.com/ava-labs/coreth/core/txpool/legacypool" - "github.com/ava-labs/coreth/params" - "github.com/ava-labs/coreth/utils" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/rawdb" "github.com/ava-labs/libevm/core/types" @@ -24,6 +18,13 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + + "github.com/ava-labs/coreth/consensus/dummy" + "github.com/ava-labs/coreth/core" + "github.com/ava-labs/coreth/core/txpool" + "github.com/ava-labs/coreth/core/txpool/legacypool" + "github.com/ava-labs/coreth/params" + "github.com/ava-labs/coreth/utils" ) func TestGossipEthTxMarshaller(t *testing.T) { diff --git a/plugin/evm/gossiper_eth_gossiping_test.go b/plugin/evm/gossiper_eth_gossiping_test.go index 883d5e67fb..413630cf19 100644 --- a/plugin/evm/gossiper_eth_gossiping_test.go +++ b/plugin/evm/gossiper_eth_gossiping_test.go @@ -15,18 +15,16 @@ import ( "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/utils/set" - - commonEng "github.com/ava-labs/avalanchego/snow/engine/common" - "github.com/ava-labs/libevm/common" + "github.com/ava-labs/libevm/core/types" "github.com/ava-labs/libevm/crypto" - "github.com/stretchr/testify/assert" "github.com/ava-labs/coreth/core" "github.com/ava-labs/coreth/params" "github.com/ava-labs/coreth/plugin/evm/vmtest" - "github.com/ava-labs/libevm/core/types" + + commonEng "github.com/ava-labs/avalanchego/snow/engine/common" ) func fundAddressByGenesis(addrs []common.Address) (string, error) { diff --git a/plugin/evm/header/base_fee.go b/plugin/evm/header/base_fee.go index 30b016c84a..4d400cbdc5 100644 --- a/plugin/evm/header/base_fee.go +++ b/plugin/evm/header/base_fee.go @@ -8,8 +8,9 @@ import ( "fmt" "math/big" - "github.com/ava-labs/coreth/params/extras" "github.com/ava-labs/libevm/core/types" + + "github.com/ava-labs/coreth/params/extras" ) var errEstimateBaseFeeWithoutActivation = errors.New("cannot estimate base fee for chain without apricot phase 3 scheduled") diff --git a/plugin/evm/header/base_fee_test.go b/plugin/evm/header/base_fee_test.go index f9313dc912..e8b7689e8f 100644 --- a/plugin/evm/header/base_fee_test.go +++ b/plugin/evm/header/base_fee_test.go @@ -8,6 +8,10 @@ import ( "testing" "github.com/ava-labs/avalanchego/vms/components/gas" + "github.com/ava-labs/libevm/common" + "github.com/ava-labs/libevm/core/types" + "github.com/stretchr/testify/require" + "github.com/ava-labs/coreth/params/extras" "github.com/ava-labs/coreth/plugin/evm/customtypes" "github.com/ava-labs/coreth/plugin/evm/upgrade/acp176" @@ -16,9 +20,6 @@ import ( "github.com/ava-labs/coreth/plugin/evm/upgrade/ap5" "github.com/ava-labs/coreth/plugin/evm/upgrade/etna" "github.com/ava-labs/coreth/utils" - "github.com/ava-labs/libevm/common" - "github.com/ava-labs/libevm/core/types" - "github.com/stretchr/testify/require" ) func TestBaseFee(t *testing.T) { diff --git a/plugin/evm/header/block_gas_cost.go b/plugin/evm/header/block_gas_cost.go index 2dc5452354..4d289e11f2 100644 --- a/plugin/evm/header/block_gas_cost.go +++ b/plugin/evm/header/block_gas_cost.go @@ -7,12 +7,13 @@ import ( "errors" "math/big" + "github.com/ava-labs/libevm/common" + "github.com/ava-labs/libevm/core/types" + "github.com/ava-labs/coreth/params/extras" "github.com/ava-labs/coreth/plugin/evm/customtypes" "github.com/ava-labs/coreth/plugin/evm/upgrade/ap4" "github.com/ava-labs/coreth/plugin/evm/upgrade/ap5" - "github.com/ava-labs/libevm/common" - "github.com/ava-labs/libevm/core/types" ) var ( diff --git a/plugin/evm/header/block_gas_cost_test.go b/plugin/evm/header/block_gas_cost_test.go index e3ffe2e55c..fab07be688 100644 --- a/plugin/evm/header/block_gas_cost_test.go +++ b/plugin/evm/header/block_gas_cost_test.go @@ -7,14 +7,15 @@ import ( "math/big" "testing" + "github.com/ava-labs/libevm/core/types" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/ava-labs/coreth/params/extras" "github.com/ava-labs/coreth/plugin/evm/customtypes" "github.com/ava-labs/coreth/plugin/evm/upgrade/ap4" "github.com/ava-labs/coreth/plugin/evm/upgrade/ap5" "github.com/ava-labs/coreth/utils" - "github.com/ava-labs/libevm/core/types" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) func TestBlockGasCost(t *testing.T) { diff --git a/plugin/evm/header/dynamic_fee_state.go b/plugin/evm/header/dynamic_fee_state.go index 59113c21aa..c90c459a79 100644 --- a/plugin/evm/header/dynamic_fee_state.go +++ b/plugin/evm/header/dynamic_fee_state.go @@ -7,11 +7,12 @@ import ( "fmt" "github.com/ava-labs/avalanchego/vms/components/gas" + "github.com/ava-labs/libevm/common" + "github.com/ava-labs/libevm/core/types" + "github.com/ava-labs/coreth/params/extras" "github.com/ava-labs/coreth/plugin/evm/customtypes" "github.com/ava-labs/coreth/plugin/evm/upgrade/acp176" - "github.com/ava-labs/libevm/common" - "github.com/ava-labs/libevm/core/types" ) // feeStateBeforeBlock takes the previous header and the timestamp of its child diff --git a/plugin/evm/header/dynamic_fee_windower.go b/plugin/evm/header/dynamic_fee_windower.go index c855e30c1c..6d63e4f1cb 100644 --- a/plugin/evm/header/dynamic_fee_windower.go +++ b/plugin/evm/header/dynamic_fee_windower.go @@ -8,15 +8,16 @@ import ( "fmt" "math/big" + "github.com/ava-labs/libevm/common" + "github.com/ava-labs/libevm/common/math" + "github.com/ava-labs/libevm/core/types" + "github.com/ava-labs/coreth/params/extras" "github.com/ava-labs/coreth/plugin/evm/customtypes" "github.com/ava-labs/coreth/plugin/evm/upgrade/ap3" "github.com/ava-labs/coreth/plugin/evm/upgrade/ap4" "github.com/ava-labs/coreth/plugin/evm/upgrade/ap5" "github.com/ava-labs/coreth/plugin/evm/upgrade/etna" - "github.com/ava-labs/libevm/common" - "github.com/ava-labs/libevm/common/math" - "github.com/ava-labs/libevm/core/types" ) var ( diff --git a/plugin/evm/header/extra.go b/plugin/evm/header/extra.go index aab9415814..b537c7d025 100644 --- a/plugin/evm/header/extra.go +++ b/plugin/evm/header/extra.go @@ -9,11 +9,12 @@ import ( "fmt" "github.com/ava-labs/avalanchego/vms/components/gas" + "github.com/ava-labs/libevm/core/types" + "github.com/ava-labs/coreth/params/extras" "github.com/ava-labs/coreth/plugin/evm/upgrade/acp176" "github.com/ava-labs/coreth/plugin/evm/upgrade/ap0" "github.com/ava-labs/coreth/plugin/evm/upgrade/ap3" - "github.com/ava-labs/libevm/core/types" ) var ( diff --git a/plugin/evm/header/extra_test.go b/plugin/evm/header/extra_test.go index 0bbe26ba8e..b483a64dc4 100644 --- a/plugin/evm/header/extra_test.go +++ b/plugin/evm/header/extra_test.go @@ -8,6 +8,9 @@ import ( "testing" "github.com/ava-labs/avalanchego/vms/components/gas" + "github.com/ava-labs/libevm/core/types" + "github.com/stretchr/testify/require" + "github.com/ava-labs/coreth/params/extras" "github.com/ava-labs/coreth/plugin/evm/customtypes" "github.com/ava-labs/coreth/plugin/evm/upgrade/acp176" @@ -16,8 +19,6 @@ import ( "github.com/ava-labs/coreth/plugin/evm/upgrade/ap4" "github.com/ava-labs/coreth/plugin/evm/upgrade/ap5" "github.com/ava-labs/coreth/utils" - "github.com/ava-labs/libevm/core/types" - "github.com/stretchr/testify/require" ) func TestExtraPrefix(t *testing.T) { diff --git a/plugin/evm/header/gas_limit.go b/plugin/evm/header/gas_limit.go index fc43f2546b..80e7f5ce25 100644 --- a/plugin/evm/header/gas_limit.go +++ b/plugin/evm/header/gas_limit.go @@ -8,13 +8,14 @@ import ( "fmt" "github.com/ava-labs/avalanchego/utils/math" + "github.com/ava-labs/libevm/core/types" + "github.com/ava-labs/coreth/params/extras" "github.com/ava-labs/coreth/plugin/evm/customtypes" "github.com/ava-labs/coreth/plugin/evm/upgrade/ap0" "github.com/ava-labs/coreth/plugin/evm/upgrade/ap1" "github.com/ava-labs/coreth/plugin/evm/upgrade/ap5" "github.com/ava-labs/coreth/plugin/evm/upgrade/cortina" - "github.com/ava-labs/libevm/core/types" ) var ( diff --git a/plugin/evm/header/gas_limit_test.go b/plugin/evm/header/gas_limit_test.go index 7752b0321c..21f0a5bb38 100644 --- a/plugin/evm/header/gas_limit_test.go +++ b/plugin/evm/header/gas_limit_test.go @@ -9,6 +9,10 @@ import ( "github.com/ava-labs/avalanchego/utils/math" "github.com/ava-labs/avalanchego/vms/components/gas" + "github.com/ava-labs/libevm/common" + "github.com/ava-labs/libevm/core/types" + "github.com/stretchr/testify/require" + "github.com/ava-labs/coreth/params/extras" "github.com/ava-labs/coreth/plugin/evm/customtypes" "github.com/ava-labs/coreth/plugin/evm/upgrade/acp176" @@ -16,9 +20,6 @@ import ( "github.com/ava-labs/coreth/plugin/evm/upgrade/ap1" "github.com/ava-labs/coreth/plugin/evm/upgrade/ap5" "github.com/ava-labs/coreth/plugin/evm/upgrade/cortina" - "github.com/ava-labs/libevm/common" - "github.com/ava-labs/libevm/core/types" - "github.com/stretchr/testify/require" ) func TestGasLimit(t *testing.T) { diff --git a/plugin/evm/imports_test.go b/plugin/evm/imports_test.go index ce7562e9eb..f41866908b 100644 --- a/plugin/evm/imports_test.go +++ b/plugin/evm/imports_test.go @@ -18,7 +18,7 @@ func getDependencies(packageName string) (map[string]struct{}, error) { cfg := &packages.Config{Mode: packages.NeedImports | packages.NeedName} pkgs, err := packages.Load(cfg, packageName) if err != nil { - return nil, fmt.Errorf("failed to load package: %v", err) + return nil, fmt.Errorf("failed to load package: %w", err) } if len(pkgs) == 0 || pkgs[0].Errors != nil { diff --git a/plugin/evm/log/log.go b/plugin/evm/log/log.go index e62641cb02..f0e6a51e75 100644 --- a/plugin/evm/log/log.go +++ b/plugin/evm/log/log.go @@ -10,9 +10,11 @@ import ( "runtime" "strings" + "golang.org/x/exp/slog" + "github.com/ava-labs/coreth/log" + ethlog "github.com/ava-labs/libevm/log" - "golang.org/x/exp/slog" ) type Logger struct { @@ -28,7 +30,7 @@ func InitLogger(alias string, level string, jsonFormat bool, writer io.Writer) ( var handler slog.Handler if jsonFormat { - chainStr := fmt.Sprintf("%s Chain", alias) + chainStr := alias + " Chain" handler = log.JSONHandlerWithLevel(writer, logLevel) handler = &addContext{Handler: handler, logger: chainStr} } else { diff --git a/plugin/evm/message/block_request.go b/plugin/evm/message/block_request.go index 1521e63f45..716a611262 100644 --- a/plugin/evm/message/block_request.go +++ b/plugin/evm/message/block_request.go @@ -8,7 +8,6 @@ import ( "fmt" "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/libevm/common" ) diff --git a/plugin/evm/message/block_sync_summary.go b/plugin/evm/message/block_sync_summary.go index d3683ec40c..45f80052b1 100644 --- a/plugin/evm/message/block_sync_summary.go +++ b/plugin/evm/message/block_sync_summary.go @@ -9,7 +9,6 @@ import ( "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/snow/engine/snowman/block" - "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/crypto" ) diff --git a/plugin/evm/message/block_sync_summary_provider.go b/plugin/evm/message/block_sync_summary_provider.go index 959ae4d0b7..f751bf6fa7 100644 --- a/plugin/evm/message/block_sync_summary_provider.go +++ b/plugin/evm/message/block_sync_summary_provider.go @@ -4,7 +4,6 @@ package message import ( "github.com/ava-labs/avalanchego/snow/engine/snowman/block" - "github.com/ava-labs/libevm/core/types" ) diff --git a/plugin/evm/message/syncable.go b/plugin/evm/message/syncable.go index 3a3690b8f9..a5019f85ab 100644 --- a/plugin/evm/message/syncable.go +++ b/plugin/evm/message/syncable.go @@ -4,9 +4,8 @@ package message import ( - "github.com/ava-labs/libevm/common" - "github.com/ava-labs/avalanchego/snow/engine/snowman/block" + "github.com/ava-labs/libevm/common" ) type Syncable interface { diff --git a/plugin/evm/network_handler.go b/plugin/evm/network_handler.go index 7eae1a7483..a7089394f5 100644 --- a/plugin/evm/network_handler.go +++ b/plugin/evm/network_handler.go @@ -8,13 +8,15 @@ import ( "github.com/ava-labs/avalanchego/codec" "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/coreth/plugin/evm/message" - syncHandlers "github.com/ava-labs/coreth/sync/handlers" - "github.com/ava-labs/coreth/sync/handlers/stats" - "github.com/ava-labs/coreth/warp" "github.com/ava-labs/libevm/ethdb" "github.com/ava-labs/libevm/log" "github.com/ava-labs/libevm/triedb" + + "github.com/ava-labs/coreth/plugin/evm/message" + "github.com/ava-labs/coreth/sync/handlers/stats" + "github.com/ava-labs/coreth/warp" + + syncHandlers "github.com/ava-labs/coreth/sync/handlers" ) var _ message.RequestHandler = (*networkHandler)(nil) diff --git a/plugin/evm/prestate_tracer_test.go b/plugin/evm/prestate_tracer_test.go index ee52e5406a..6e787298e4 100644 --- a/plugin/evm/prestate_tracer_test.go +++ b/plugin/evm/prestate_tracer_test.go @@ -12,14 +12,17 @@ import ( "testing" "unicode" - "github.com/ava-labs/coreth/core" - "github.com/ava-labs/coreth/eth/tracers" - "github.com/ava-labs/coreth/tests" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/common/math" "github.com/ava-labs/libevm/core/rawdb" "github.com/ava-labs/libevm/core/types" "github.com/ava-labs/libevm/core/vm" + + "github.com/ava-labs/coreth/core" + "github.com/ava-labs/coreth/eth/tracers" + "github.com/ava-labs/coreth/tests" + + ethtypes "github.com/ava-labs/libevm/core/types" ) func TestPrestateWithDiffModeANTTracer(t *testing.T) { diff --git a/plugin/evm/syncervm_test.go b/plugin/evm/syncervm_test.go index 8b90ef8e5c..bb5b088c73 100644 --- a/plugin/evm/syncervm_test.go +++ b/plugin/evm/syncervm_test.go @@ -6,17 +6,16 @@ package evm import ( "testing" + "github.com/ava-labs/libevm/common" + "github.com/ava-labs/libevm/core/types" + "github.com/stretchr/testify/require" + "github.com/ava-labs/coreth/consensus/dummy" "github.com/ava-labs/coreth/core" "github.com/ava-labs/coreth/params" "github.com/ava-labs/coreth/plugin/evm/extension" "github.com/ava-labs/coreth/plugin/evm/vmtest" "github.com/ava-labs/coreth/predicate" - - "github.com/ava-labs/libevm/common" - "github.com/ava-labs/libevm/core/types" - - "github.com/stretchr/testify/require" ) func TestEVMSyncerVM(t *testing.T) { diff --git a/plugin/evm/tx_gossip_test.go b/plugin/evm/tx_gossip_test.go index b6a831e4b3..8cac4e475d 100644 --- a/plugin/evm/tx_gossip_test.go +++ b/plugin/evm/tx_gossip_test.go @@ -20,20 +20,20 @@ import ( "github.com/ava-labs/avalanchego/snow/engine/enginetest" "github.com/ava-labs/avalanchego/snow/snowtest" "github.com/ava-labs/avalanchego/snow/validators" - agoUtils "github.com/ava-labs/avalanchego/utils" "github.com/ava-labs/avalanchego/utils/crypto/secp256k1" "github.com/ava-labs/avalanchego/utils/logging" "github.com/ava-labs/avalanchego/utils/set" + "github.com/ava-labs/libevm/core/types" "github.com/prometheus/client_golang/prometheus" "github.com/stretchr/testify/require" - "google.golang.org/protobuf/proto" "github.com/ava-labs/coreth/plugin/evm/config" "github.com/ava-labs/coreth/plugin/evm/upgrade/ap0" "github.com/ava-labs/coreth/plugin/evm/vmtest" "github.com/ava-labs/coreth/utils" - "github.com/ava-labs/libevm/core/types" + + agoUtils "github.com/ava-labs/avalanchego/utils" ) func TestEthTxGossip(t *testing.T) { diff --git a/plugin/evm/upgrade/acp176/acp176.go b/plugin/evm/upgrade/acp176/acp176.go index b5346515bd..24f7772789 100644 --- a/plugin/evm/upgrade/acp176/acp176.go +++ b/plugin/evm/upgrade/acp176/acp176.go @@ -13,10 +13,11 @@ import ( "math/big" "sort" - safemath "github.com/ava-labs/avalanchego/utils/math" "github.com/ava-labs/avalanchego/utils/wrappers" "github.com/ava-labs/avalanchego/vms/components/gas" "github.com/holiman/uint256" + + safemath "github.com/ava-labs/avalanchego/utils/math" ) const ( diff --git a/plugin/evm/upgrade/ap0/params.go b/plugin/evm/upgrade/ap0/params.go index 5942017b1e..cd24a3ee8a 100644 --- a/plugin/evm/upgrade/ap0/params.go +++ b/plugin/evm/upgrade/ap0/params.go @@ -6,6 +6,7 @@ package ap0 import ( "github.com/ava-labs/avalanchego/utils/units" + "github.com/ava-labs/coreth/utils" ) diff --git a/plugin/evm/upgrade/ap3/window.go b/plugin/evm/upgrade/ap3/window.go index 2e93d7720c..bcc8982768 100644 --- a/plugin/evm/upgrade/ap3/window.go +++ b/plugin/evm/upgrade/ap3/window.go @@ -11,7 +11,9 @@ import ( "math" "github.com/ava-labs/avalanchego/utils/wrappers" + "github.com/ava-labs/coreth/utils" + safemath "github.com/ava-labs/libevm/common/math" ) diff --git a/plugin/evm/upgrade/ap4/cost.go b/plugin/evm/upgrade/ap4/cost.go index a86d50cff5..beffbeaed7 100644 --- a/plugin/evm/upgrade/ap4/cost.go +++ b/plugin/evm/upgrade/ap4/cost.go @@ -8,8 +8,9 @@ package ap4 import ( "math" - safemath "github.com/ava-labs/avalanchego/utils/math" "github.com/ava-labs/coreth/utils" + + safemath "github.com/ava-labs/avalanchego/utils/math" ) const ( diff --git a/plugin/evm/vm.go b/plugin/evm/vm.go index e26ae62537..c90a8bf49b 100644 --- a/plugin/evm/vm.go +++ b/plugin/evm/vm.go @@ -17,18 +17,44 @@ import ( "sync" "time" + "github.com/ava-labs/avalanchego/cache/lru" "github.com/ava-labs/avalanchego/cache/metercacher" + "github.com/ava-labs/avalanchego/codec" + "github.com/ava-labs/avalanchego/database" + "github.com/ava-labs/avalanchego/database/versiondb" + "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/network/p2p" "github.com/ava-labs/avalanchego/network/p2p/acp118" - "github.com/ava-labs/coreth/network" - "github.com/ava-labs/coreth/plugin/evm/customrawdb" - "github.com/ava-labs/coreth/plugin/evm/extension" - "github.com/ava-labs/coreth/plugin/evm/gossip" - "github.com/ava-labs/coreth/plugin/evm/vmerrors" + "github.com/ava-labs/avalanchego/snow" + "github.com/ava-labs/avalanchego/snow/consensus/snowman" + "github.com/ava-labs/avalanchego/snow/engine/snowman/block" + "github.com/ava-labs/avalanchego/utils/perms" + "github.com/ava-labs/avalanchego/utils/profiler" + "github.com/ava-labs/avalanchego/utils/timer/mockable" + "github.com/ava-labs/avalanchego/utils/units" + "github.com/ava-labs/avalanchego/vms/components/chain" + "github.com/ava-labs/avalanchego/vms/components/gas" "github.com/ava-labs/firewood-go-ethhash/ffi" + "github.com/ava-labs/libevm/common" + "github.com/ava-labs/libevm/core/rawdb" + "github.com/ava-labs/libevm/core/types" + "github.com/ava-labs/libevm/ethdb" + "github.com/ava-labs/libevm/log" + "github.com/ava-labs/libevm/metrics" + "github.com/ava-labs/libevm/rlp" + "github.com/ava-labs/libevm/triedb" "github.com/prometheus/client_golang/prometheus" - avalanchegoprometheus "github.com/ava-labs/avalanchego/vms/evm/metrics/prometheus" + // Force-load precompiles to trigger registration + _ "github.com/ava-labs/coreth/precompile/registry" + // Force-load tracer engine to trigger registration + // + // We must import this package (not referenced elsewhere) so that the native "callTracer" + // is added to a map of client-accessible tracers. In geth, this is done + // inside of cmd/geth. + _ "github.com/ava-labs/libevm/eth/tracers/js" + _ "github.com/ava-labs/libevm/eth/tracers/native" + "github.com/ava-labs/coreth/consensus/dummy" "github.com/ava-labs/coreth/constants" "github.com/ava-labs/coreth/core" @@ -36,65 +62,35 @@ import ( "github.com/ava-labs/coreth/eth" "github.com/ava-labs/coreth/eth/ethconfig" "github.com/ava-labs/coreth/miner" + "github.com/ava-labs/coreth/network" "github.com/ava-labs/coreth/node" "github.com/ava-labs/coreth/params" "github.com/ava-labs/coreth/params/extras" "github.com/ava-labs/coreth/plugin/evm/config" - corethlog "github.com/ava-labs/coreth/plugin/evm/log" + "github.com/ava-labs/coreth/plugin/evm/customrawdb" + "github.com/ava-labs/coreth/plugin/evm/extension" + "github.com/ava-labs/coreth/plugin/evm/gossip" "github.com/ava-labs/coreth/plugin/evm/message" "github.com/ava-labs/coreth/plugin/evm/upgrade/acp176" - vmsync "github.com/ava-labs/coreth/sync/vm" - "github.com/ava-labs/coreth/triedb/hashdb" - - "github.com/ava-labs/libevm/core/rawdb" - "github.com/ava-labs/libevm/core/types" - "github.com/ava-labs/libevm/metrics" - ethparams "github.com/ava-labs/libevm/params" - "github.com/ava-labs/libevm/triedb" - - warpcontract "github.com/ava-labs/coreth/precompile/contracts/warp" + "github.com/ava-labs/coreth/plugin/evm/vmerrors" "github.com/ava-labs/coreth/precompile/precompileconfig" "github.com/ava-labs/coreth/rpc" - statesyncclient "github.com/ava-labs/coreth/sync/client" "github.com/ava-labs/coreth/sync/client/stats" "github.com/ava-labs/coreth/sync/handlers" - handlerstats "github.com/ava-labs/coreth/sync/handlers/stats" - utilsrpc "github.com/ava-labs/coreth/utils/rpc" + "github.com/ava-labs/coreth/triedb/hashdb" "github.com/ava-labs/coreth/warp" - "github.com/ava-labs/libevm/common" - "github.com/ava-labs/libevm/ethdb" - "github.com/ava-labs/libevm/log" - "github.com/ava-labs/libevm/rlp" - - "github.com/ava-labs/avalanchego/cache/lru" - "github.com/ava-labs/avalanchego/codec" - "github.com/ava-labs/avalanchego/database" - "github.com/ava-labs/avalanchego/database/versiondb" - "github.com/ava-labs/avalanchego/ids" avalanchegossip "github.com/ava-labs/avalanchego/network/p2p/gossip" - "github.com/ava-labs/avalanchego/snow" - "github.com/ava-labs/avalanchego/snow/consensus/snowman" commonEng "github.com/ava-labs/avalanchego/snow/engine/common" - "github.com/ava-labs/avalanchego/snow/engine/snowman/block" avalancheUtils "github.com/ava-labs/avalanchego/utils" - "github.com/ava-labs/avalanchego/utils/perms" - "github.com/ava-labs/avalanchego/utils/profiler" - "github.com/ava-labs/avalanchego/utils/timer/mockable" - "github.com/ava-labs/avalanchego/utils/units" - "github.com/ava-labs/avalanchego/vms/components/chain" - "github.com/ava-labs/avalanchego/vms/components/gas" - - // Force-load tracer engine to trigger registration - // - // We must import this package (not referenced elsewhere) so that the native "callTracer" - // is added to a map of client-accessible tracers. In geth, this is done - // inside of cmd/geth. - _ "github.com/ava-labs/libevm/eth/tracers/js" - _ "github.com/ava-labs/libevm/eth/tracers/native" - - // Force-load precompiles to trigger registration - _ "github.com/ava-labs/coreth/precompile/registry" + avalanchegoprometheus "github.com/ava-labs/avalanchego/vms/evm/metrics/prometheus" + corethlog "github.com/ava-labs/coreth/plugin/evm/log" + warpcontract "github.com/ava-labs/coreth/precompile/contracts/warp" + statesyncclient "github.com/ava-labs/coreth/sync/client" + handlerstats "github.com/ava-labs/coreth/sync/handlers/stats" + vmsync "github.com/ava-labs/coreth/sync/vm" + utilsrpc "github.com/ava-labs/coreth/utils/rpc" + ethparams "github.com/ava-labs/libevm/params" ) var ( diff --git a/plugin/evm/vm_database.go b/plugin/evm/vm_database.go index 4719cee542..146c3f2657 100644 --- a/plugin/evm/vm_database.go +++ b/plugin/evm/vm_database.go @@ -6,13 +6,15 @@ package evm import ( "time" - avalanchedatabase "github.com/ava-labs/avalanchego/database" "github.com/ava-labs/avalanchego/database/prefixdb" "github.com/ava-labs/avalanchego/database/versiondb" - "github.com/ava-labs/coreth/plugin/evm/database" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/rawdb" "github.com/ava-labs/libevm/log" + + "github.com/ava-labs/coreth/plugin/evm/database" + + avalanchedatabase "github.com/ava-labs/avalanchego/database" ) // initializeDBs initializes the databases used by the VM. diff --git a/plugin/evm/vm_extensible.go b/plugin/evm/vm_extensible.go index caf90d330e..43ad676ade 100644 --- a/plugin/evm/vm_extensible.go +++ b/plugin/evm/vm_extensible.go @@ -10,12 +10,14 @@ import ( "github.com/ava-labs/avalanchego/database/versiondb" "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/network/p2p" + "github.com/prometheus/client_golang/prometheus" + "github.com/ava-labs/coreth/eth" "github.com/ava-labs/coreth/params" "github.com/ava-labs/coreth/plugin/evm/config" "github.com/ava-labs/coreth/plugin/evm/extension" + vmsync "github.com/ava-labs/coreth/sync/vm" - "github.com/prometheus/client_golang/prometheus" ) var _ extension.InnerVM = (*VM)(nil) diff --git a/plugin/evm/vm_test.go b/plugin/evm/vm_test.go index 500ad7b455..3e678f9c55 100644 --- a/plugin/evm/vm_test.go +++ b/plugin/evm/vm_test.go @@ -18,12 +18,18 @@ import ( "github.com/ava-labs/avalanchego/database" "github.com/ava-labs/avalanchego/ids" - commonEng "github.com/ava-labs/avalanchego/snow/engine/common" "github.com/ava-labs/avalanchego/snow/snowtest" "github.com/ava-labs/avalanchego/upgrade" "github.com/ava-labs/avalanchego/upgrade/upgradetest" "github.com/ava-labs/avalanchego/utils/timer/mockable" "github.com/ava-labs/avalanchego/vms/components/chain" + "github.com/ava-labs/libevm/common" + "github.com/ava-labs/libevm/core/types" + "github.com/ava-labs/libevm/log" + "github.com/ava-labs/libevm/trie" + "github.com/holiman/uint256" + "github.com/stretchr/testify/require" + "github.com/ava-labs/coreth/consensus/dummy" "github.com/ava-labs/coreth/constants" "github.com/ava-labs/coreth/core" @@ -39,13 +45,8 @@ import ( "github.com/ava-labs/coreth/plugin/evm/vmtest" "github.com/ava-labs/coreth/rpc" "github.com/ava-labs/coreth/utils" - "github.com/ava-labs/libevm/common" - "github.com/ava-labs/libevm/core/types" - "github.com/ava-labs/libevm/log" - "github.com/ava-labs/libevm/trie" - "github.com/holiman/uint256" - "github.com/stretchr/testify/require" + commonEng "github.com/ava-labs/avalanchego/snow/engine/common" ethparams "github.com/ava-labs/libevm/params" ) diff --git a/plugin/evm/vm_warp_test.go b/plugin/evm/vm_warp_test.go index df177e14a0..ff5c437c02 100644 --- a/plugin/evm/vm_warp_test.go +++ b/plugin/evm/vm_warp_test.go @@ -11,44 +11,46 @@ import ( "testing" "time" - _ "embed" - "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/network/p2p" "github.com/ava-labs/avalanchego/network/p2p/acp118" "github.com/ava-labs/avalanchego/proto/pb/sdk" - commonEng "github.com/ava-labs/avalanchego/snow/engine/common" "github.com/ava-labs/avalanchego/snow/engine/enginetest" "github.com/ava-labs/avalanchego/snow/engine/snowman/block" "github.com/ava-labs/avalanchego/snow/validators" "github.com/ava-labs/avalanchego/snow/validators/validatorstest" "github.com/ava-labs/avalanchego/upgrade" "github.com/ava-labs/avalanchego/upgrade/upgradetest" - avagoUtils "github.com/ava-labs/avalanchego/utils" "github.com/ava-labs/avalanchego/utils/constants" "github.com/ava-labs/avalanchego/utils/crypto/bls" "github.com/ava-labs/avalanchego/utils/crypto/bls/signer/localsigner" "github.com/ava-labs/avalanchego/utils/set" "github.com/ava-labs/avalanchego/vms/components/chain" - avalancheWarp "github.com/ava-labs/avalanchego/vms/platformvm/warp" "github.com/ava-labs/avalanchego/vms/platformvm/warp/payload" + "github.com/ava-labs/libevm/common" + "github.com/ava-labs/libevm/core/rawdb" + "github.com/ava-labs/libevm/core/types" + "github.com/ava-labs/libevm/crypto" + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/proto" + + _ "embed" + "github.com/ava-labs/coreth/eth/tracers" "github.com/ava-labs/coreth/params" "github.com/ava-labs/coreth/params/extras" - customheader "github.com/ava-labs/coreth/plugin/evm/header" "github.com/ava-labs/coreth/plugin/evm/upgrade/ap0" "github.com/ava-labs/coreth/plugin/evm/vmtest" "github.com/ava-labs/coreth/precompile/contract" - warpcontract "github.com/ava-labs/coreth/precompile/contracts/warp" "github.com/ava-labs/coreth/predicate" "github.com/ava-labs/coreth/utils" "github.com/ava-labs/coreth/warp" - "github.com/ava-labs/libevm/common" - "github.com/ava-labs/libevm/core/rawdb" - "github.com/ava-labs/libevm/core/types" - "github.com/ava-labs/libevm/crypto" - "github.com/stretchr/testify/require" - "google.golang.org/protobuf/proto" + + commonEng "github.com/ava-labs/avalanchego/snow/engine/common" + avagoUtils "github.com/ava-labs/avalanchego/utils" + avalancheWarp "github.com/ava-labs/avalanchego/vms/platformvm/warp" + customheader "github.com/ava-labs/coreth/plugin/evm/header" + warpcontract "github.com/ava-labs/coreth/precompile/contracts/warp" ) var ( diff --git a/plugin/evm/vmtest/genesis.go b/plugin/evm/vmtest/genesis.go index 2da9eb591f..fb7ee908b9 100644 --- a/plugin/evm/vmtest/genesis.go +++ b/plugin/evm/vmtest/genesis.go @@ -8,7 +8,6 @@ import ( "math/big" "testing" - avalancheatomic "github.com/ava-labs/avalanchego/chains/atomic" "github.com/ava-labs/avalanchego/database/memdb" "github.com/ava-labs/avalanchego/database/prefixdb" "github.com/ava-labs/avalanchego/ids" @@ -17,14 +16,15 @@ import ( "github.com/ava-labs/avalanchego/upgrade" "github.com/ava-labs/avalanchego/upgrade/upgradetest" "github.com/ava-labs/avalanchego/utils/crypto/secp256k1" + "github.com/ava-labs/libevm/common" + "github.com/ava-labs/libevm/core/types" "github.com/ava-labs/coreth/core" "github.com/ava-labs/coreth/params" "github.com/ava-labs/coreth/params/extras" "github.com/ava-labs/coreth/plugin/evm/upgrade/ap3" - "github.com/ava-labs/libevm/common" - "github.com/ava-labs/libevm/core/types" + avalancheatomic "github.com/ava-labs/avalanchego/chains/atomic" ) var ( diff --git a/plugin/evm/vmtest/test_syncervm.go b/plugin/evm/vmtest/test_syncervm.go index 7d5fd46637..9753c2a6a5 100644 --- a/plugin/evm/vmtest/test_syncervm.go +++ b/plugin/evm/vmtest/test_syncervm.go @@ -11,20 +11,23 @@ import ( "testing" "time" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - avalancheatomic "github.com/ava-labs/avalanchego/chains/atomic" - avalanchedatabase "github.com/ava-labs/avalanchego/database" "github.com/ava-labs/avalanchego/database/prefixdb" "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/snow" - commonEng "github.com/ava-labs/avalanchego/snow/engine/common" "github.com/ava-labs/avalanchego/snow/engine/enginetest" "github.com/ava-labs/avalanchego/snow/engine/snowman/block" "github.com/ava-labs/avalanchego/upgrade/upgradetest" "github.com/ava-labs/avalanchego/utils/set" "github.com/ava-labs/avalanchego/vms/components/chain" + "github.com/ava-labs/libevm/common" + "github.com/ava-labs/libevm/core/rawdb" + "github.com/ava-labs/libevm/core/types" + "github.com/ava-labs/libevm/ethdb" + "github.com/ava-labs/libevm/log" + "github.com/ava-labs/libevm/rlp" + "github.com/ava-labs/libevm/trie" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/ava-labs/coreth/consensus/dummy" "github.com/ava-labs/coreth/constants" @@ -35,18 +38,14 @@ import ( "github.com/ava-labs/coreth/plugin/evm/database" "github.com/ava-labs/coreth/plugin/evm/extension" "github.com/ava-labs/coreth/predicate" - statesyncclient "github.com/ava-labs/coreth/sync/client" "github.com/ava-labs/coreth/sync/statesync/statesynctest" - vmsync "github.com/ava-labs/coreth/sync/vm" "github.com/ava-labs/coreth/utils/utilstest" - "github.com/ava-labs/libevm/common" - "github.com/ava-labs/libevm/core/rawdb" - "github.com/ava-labs/libevm/core/types" - "github.com/ava-labs/libevm/ethdb" - "github.com/ava-labs/libevm/log" - "github.com/ava-labs/libevm/rlp" - "github.com/ava-labs/libevm/trie" + avalancheatomic "github.com/ava-labs/avalanchego/chains/atomic" + avalanchedatabase "github.com/ava-labs/avalanchego/database" + commonEng "github.com/ava-labs/avalanchego/snow/engine/common" + statesyncclient "github.com/ava-labs/coreth/sync/client" + vmsync "github.com/ava-labs/coreth/sync/vm" ) type SyncerVMTest struct { diff --git a/plugin/evm/vmtest/test_vm.go b/plugin/evm/vmtest/test_vm.go index edc4ef3b19..d703c3c1cc 100644 --- a/plugin/evm/vmtest/test_vm.go +++ b/plugin/evm/vmtest/test_vm.go @@ -10,20 +10,22 @@ import ( "testing" "github.com/ava-labs/avalanchego/api/metrics" - avalancheatomic "github.com/ava-labs/avalanchego/chains/atomic" "github.com/ava-labs/avalanchego/database/prefixdb" "github.com/ava-labs/avalanchego/snow" "github.com/ava-labs/avalanchego/snow/consensus/snowman" - commonEng "github.com/ava-labs/avalanchego/snow/engine/common" - commoneng "github.com/ava-labs/avalanchego/snow/engine/common" "github.com/ava-labs/avalanchego/snow/engine/enginetest" "github.com/ava-labs/avalanchego/upgrade/upgradetest" - "github.com/ava-labs/coreth/plugin/evm/customrawdb" - "github.com/ava-labs/coreth/plugin/evm/extension" - "github.com/ava-labs/coreth/utils/utilstest" "github.com/ava-labs/libevm/core/rawdb" "github.com/ava-labs/libevm/core/types" "github.com/stretchr/testify/require" + + "github.com/ava-labs/coreth/plugin/evm/customrawdb" + "github.com/ava-labs/coreth/plugin/evm/extension" + "github.com/ava-labs/coreth/utils/utilstest" + + avalancheatomic "github.com/ava-labs/avalanchego/chains/atomic" + commonEng "github.com/ava-labs/avalanchego/snow/engine/common" + commoneng "github.com/ava-labs/avalanchego/snow/engine/common" ) var Schemes = []string{rawdb.HashScheme, customrawdb.FirewoodScheme} diff --git a/plugin/evm/wrapped_block.go b/plugin/evm/wrapped_block.go index f4cad07a45..1dddb89561 100644 --- a/plugin/evm/wrapped_block.go +++ b/plugin/evm/wrapped_block.go @@ -11,6 +11,16 @@ import ( "math/big" "time" + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/snow/consensus/snowman" + "github.com/ava-labs/avalanchego/snow/engine/snowman/block" + "github.com/ava-labs/libevm/common" + "github.com/ava-labs/libevm/core/rawdb" + "github.com/ava-labs/libevm/core/types" + "github.com/ava-labs/libevm/log" + "github.com/ava-labs/libevm/rlp" + "github.com/ava-labs/libevm/trie" + "github.com/ava-labs/coreth/constants" "github.com/ava-labs/coreth/core" "github.com/ava-labs/coreth/params" @@ -22,17 +32,6 @@ import ( "github.com/ava-labs/coreth/plugin/evm/upgrade/ap1" "github.com/ava-labs/coreth/precompile/precompileconfig" "github.com/ava-labs/coreth/predicate" - - "github.com/ava-labs/libevm/common" - "github.com/ava-labs/libevm/core/rawdb" - "github.com/ava-labs/libevm/core/types" - "github.com/ava-labs/libevm/log" - "github.com/ava-labs/libevm/rlp" - "github.com/ava-labs/libevm/trie" - - "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/avalanchego/snow/consensus/snowman" - "github.com/ava-labs/avalanchego/snow/engine/snowman/block" ) var ( @@ -416,7 +415,7 @@ func (b *wrappedBlock) syntacticVerify() error { return fmt.Errorf("invalid parentBeaconRoot: have %x, expected empty hash", ethHeader.ParentBeaconRoot) } if ethHeader.BlobGasUsed == nil { - return fmt.Errorf("blob gas used must not be nil in Cancun") + return errors.New("blob gas used must not be nil in Cancun") } else if *ethHeader.BlobGasUsed > 0 { return fmt.Errorf("blobs not enabled on avalanche networks: used %d blob gas, expected 0", *ethHeader.BlobGasUsed) } diff --git a/plugin/factory/factory.go b/plugin/factory/factory.go index 9a33ae1b00..f7e67b507c 100644 --- a/plugin/factory/factory.go +++ b/plugin/factory/factory.go @@ -8,9 +8,10 @@ import ( "github.com/ava-labs/avalanchego/snow/engine/snowman/block" "github.com/ava-labs/avalanchego/utils/logging" "github.com/ava-labs/avalanchego/vms" - atomicvm "github.com/ava-labs/coreth/plugin/evm/atomic/vm" "github.com/ava-labs/coreth/plugin/evm" + + atomicvm "github.com/ava-labs/coreth/plugin/evm/atomic/vm" ) var ( diff --git a/precompile/contract/interfaces.go b/precompile/contract/interfaces.go index 23dd88537a..a896d6f862 100644 --- a/precompile/contract/interfaces.go +++ b/precompile/contract/interfaces.go @@ -8,12 +8,14 @@ import ( "math/big" "github.com/ava-labs/avalanchego/snow" - "github.com/ava-labs/coreth/precompile/precompileconfig" "github.com/ava-labs/libevm/common" - ethtypes "github.com/ava-labs/libevm/core/types" "github.com/ava-labs/libevm/core/vm" "github.com/ava-labs/libevm/libevm/stateconf" "github.com/holiman/uint256" + + "github.com/ava-labs/coreth/precompile/precompileconfig" + + ethtypes "github.com/ava-labs/libevm/core/types" ) // StatefulPrecompiledContract is the interface for executing a precompiled contract diff --git a/precompile/contract/utils.go b/precompile/contract/utils.go index ad32db33ef..7ec7424d2d 100644 --- a/precompile/contract/utils.go +++ b/precompile/contract/utils.go @@ -8,9 +8,10 @@ import ( "regexp" "strings" - "github.com/ava-labs/coreth/accounts/abi" "github.com/ava-labs/libevm/core/vm" "github.com/ava-labs/libevm/crypto" + + "github.com/ava-labs/coreth/accounts/abi" ) // Gas costs for stateful precompiles diff --git a/precompile/contracts/warp/config.go b/precompile/contracts/warp/config.go index aea1eef146..1f6772cfee 100644 --- a/precompile/contracts/warp/config.go +++ b/precompile/contracts/warp/config.go @@ -10,12 +10,14 @@ import ( "github.com/ava-labs/avalanchego/vms/platformvm/warp" "github.com/ava-labs/avalanchego/vms/platformvm/warp/payload" - "github.com/ava-labs/coreth/precompile/precompileconfig" - "github.com/ava-labs/coreth/predicate" - warpValidators "github.com/ava-labs/coreth/warp/validators" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/common/math" "github.com/ava-labs/libevm/log" + + "github.com/ava-labs/coreth/precompile/precompileconfig" + "github.com/ava-labs/coreth/predicate" + + warpValidators "github.com/ava-labs/coreth/warp/validators" ) const ( @@ -155,20 +157,20 @@ func (c *Config) PredicateGas(predicateBytes []byte) (uint64, error) { unpackedPredicateBytes, err := predicate.UnpackPredicate(predicateBytes) if err != nil { - return 0, fmt.Errorf("%w: %s", errInvalidPredicateBytes, err) + return 0, fmt.Errorf("%w: %w", errInvalidPredicateBytes, err) } warpMessage, err := warp.ParseMessage(unpackedPredicateBytes) if err != nil { - return 0, fmt.Errorf("%w: %s", errInvalidWarpMsg, err) + return 0, fmt.Errorf("%w: %w", errInvalidWarpMsg, err) } _, err = payload.Parse(warpMessage.Payload) if err != nil { - return 0, fmt.Errorf("%w: %s", errInvalidWarpMsgPayload, err) + return 0, fmt.Errorf("%w: %w", errInvalidWarpMsgPayload, err) } numSigners, err := warpMessage.Signature.NumSigners() if err != nil { - return 0, fmt.Errorf("%w: %s", errCannotGetNumSigners, err) + return 0, fmt.Errorf("%w: %w", errCannotGetNumSigners, err) } signerGas, overflow := math.SafeMul(uint64(numSigners), GasCostPerWarpSigner) if overflow { diff --git a/precompile/contracts/warp/config_test.go b/precompile/contracts/warp/config_test.go index c859d5319e..577d881153 100644 --- a/precompile/contracts/warp/config_test.go +++ b/precompile/contracts/warp/config_test.go @@ -7,10 +7,11 @@ import ( "fmt" "testing" + "go.uber.org/mock/gomock" + "github.com/ava-labs/coreth/precompile/precompileconfig" "github.com/ava-labs/coreth/precompile/precompiletest" "github.com/ava-labs/coreth/utils" - "go.uber.org/mock/gomock" ) func TestVerify(t *testing.T) { diff --git a/precompile/contracts/warp/contract.go b/precompile/contracts/warp/contract.go index 5624fdb752..0ab42afe86 100644 --- a/precompile/contracts/warp/contract.go +++ b/precompile/contracts/warp/contract.go @@ -9,15 +9,15 @@ import ( "github.com/ava-labs/avalanchego/vms/platformvm/warp" "github.com/ava-labs/avalanchego/vms/platformvm/warp/payload" - "github.com/ava-labs/coreth/accounts/abi" - "github.com/ava-labs/coreth/precompile/contract" + "github.com/ava-labs/libevm/common" + "github.com/ava-labs/libevm/common/math" "github.com/ava-labs/libevm/core/types" + "github.com/ava-labs/libevm/core/vm" _ "embed" - "github.com/ava-labs/libevm/common" - "github.com/ava-labs/libevm/common/math" - "github.com/ava-labs/libevm/core/vm" + "github.com/ava-labs/coreth/accounts/abi" + "github.com/ava-labs/coreth/precompile/contract" ) const ( @@ -248,7 +248,7 @@ func sendWarpMessage(accessibleState contract.AccessibleState, caller common.Add // unpack the arguments payloadData, err := UnpackSendWarpMessageInput(input) if err != nil { - return nil, remainingGas, fmt.Errorf("%w: %s", errInvalidSendInput, err) + return nil, remainingGas, fmt.Errorf("%w: %w", errInvalidSendInput, err) } var ( diff --git a/precompile/contracts/warp/contract_test.go b/precompile/contracts/warp/contract_test.go index d993c50bd4..925562ad36 100644 --- a/precompile/contracts/warp/contract_test.go +++ b/precompile/contracts/warp/contract_test.go @@ -10,16 +10,18 @@ import ( "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/snow/snowtest" - agoUtils "github.com/ava-labs/avalanchego/utils" "github.com/ava-labs/avalanchego/utils/set" - avalancheWarp "github.com/ava-labs/avalanchego/vms/platformvm/warp" "github.com/ava-labs/avalanchego/vms/platformvm/warp/payload" - "github.com/ava-labs/coreth/precompile/contract" - "github.com/ava-labs/coreth/precompile/precompiletest" - "github.com/ava-labs/coreth/predicate" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/vm" "github.com/stretchr/testify/require" + + "github.com/ava-labs/coreth/precompile/contract" + "github.com/ava-labs/coreth/precompile/precompiletest" + "github.com/ava-labs/coreth/predicate" + + agoUtils "github.com/ava-labs/avalanchego/utils" + avalancheWarp "github.com/ava-labs/avalanchego/vms/platformvm/warp" ) func TestGetBlockchainID(t *testing.T) { diff --git a/precompile/contracts/warp/contract_warp_handler.go b/precompile/contracts/warp/contract_warp_handler.go index c712ead842..debf5bcf55 100644 --- a/precompile/contracts/warp/contract_warp_handler.go +++ b/precompile/contracts/warp/contract_warp_handler.go @@ -9,11 +9,12 @@ import ( "github.com/ava-labs/avalanchego/utils/set" "github.com/ava-labs/avalanchego/vms/platformvm/warp" "github.com/ava-labs/avalanchego/vms/platformvm/warp/payload" - "github.com/ava-labs/coreth/precompile/contract" - "github.com/ava-labs/coreth/predicate" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/common/math" "github.com/ava-labs/libevm/core/vm" + + "github.com/ava-labs/coreth/precompile/contract" + "github.com/ava-labs/coreth/predicate" ) var ( @@ -53,7 +54,7 @@ func handleWarpMessage(accessibleState contract.AccessibleState, input []byte, s warpIndexInput, err := UnpackGetVerifiedWarpMessageInput(input) if err != nil { - return nil, remainingGas, fmt.Errorf("%w: %s", errInvalidIndexInput, err) + return nil, remainingGas, fmt.Errorf("%w: %w", errInvalidIndexInput, err) } if warpIndexInput > math.MaxInt32 { return nil, remainingGas, fmt.Errorf("%w: larger than MaxInt32", errInvalidIndexInput) @@ -80,11 +81,11 @@ func handleWarpMessage(accessibleState contract.AccessibleState, input []byte, s // hit an error during execution. unpackedPredicateBytes, err := predicate.UnpackPredicate(predicateBytes) if err != nil { - return nil, remainingGas, fmt.Errorf("%w: %s", errInvalidPredicateBytes, err) + return nil, remainingGas, fmt.Errorf("%w: %w", errInvalidPredicateBytes, err) } warpMessage, err := warp.ParseMessage(unpackedPredicateBytes) if err != nil { - return nil, remainingGas, fmt.Errorf("%w: %s", errInvalidWarpMsg, err) + return nil, remainingGas, fmt.Errorf("%w: %w", errInvalidWarpMsg, err) } res, err := handler.handleMessage(warpMessage) if err != nil { @@ -102,7 +103,7 @@ func (addressedPayloadHandler) packFailed() []byte { func (addressedPayloadHandler) handleMessage(warpMessage *warp.Message) ([]byte, error) { addressedPayload, err := payload.ParseAddressedCall(warpMessage.UnsignedMessage.Payload) if err != nil { - return nil, fmt.Errorf("%w: %s", errInvalidAddressedPayload, err) + return nil, fmt.Errorf("%w: %w", errInvalidAddressedPayload, err) } return PackGetVerifiedWarpMessageOutput(GetVerifiedWarpMessageOutput{ Message: WarpMessage{ @@ -123,7 +124,7 @@ func (blockHashHandler) packFailed() []byte { func (blockHashHandler) handleMessage(warpMessage *warp.Message) ([]byte, error) { blockHashPayload, err := payload.ParseHash(warpMessage.UnsignedMessage.Payload) if err != nil { - return nil, fmt.Errorf("%w: %s", errInvalidBlockHashPayload, err) + return nil, fmt.Errorf("%w: %w", errInvalidBlockHashPayload, err) } return PackGetVerifiedWarpBlockHashOutput(GetVerifiedWarpBlockHashOutput{ WarpBlockHash: WarpBlockHash{ diff --git a/precompile/contracts/warp/module.go b/precompile/contracts/warp/module.go index 8da74f26e8..713352a8ee 100644 --- a/precompile/contracts/warp/module.go +++ b/precompile/contracts/warp/module.go @@ -6,11 +6,11 @@ package warp import ( "fmt" + "github.com/ava-labs/libevm/common" + "github.com/ava-labs/coreth/precompile/contract" "github.com/ava-labs/coreth/precompile/modules" "github.com/ava-labs/coreth/precompile/precompileconfig" - - "github.com/ava-labs/libevm/common" ) var _ contract.Configurator = (*configurator)(nil) diff --git a/precompile/contracts/warp/predicate_test.go b/precompile/contracts/warp/predicate_test.go index ec225eaa45..d2928395b6 100644 --- a/precompile/contracts/warp/predicate_test.go +++ b/precompile/contracts/warp/predicate_test.go @@ -15,18 +15,20 @@ import ( "github.com/ava-labs/avalanchego/snow/snowtest" "github.com/ava-labs/avalanchego/snow/validators" "github.com/ava-labs/avalanchego/snow/validators/validatorstest" - agoUtils "github.com/ava-labs/avalanchego/utils" "github.com/ava-labs/avalanchego/utils/constants" "github.com/ava-labs/avalanchego/utils/crypto/bls" "github.com/ava-labs/avalanchego/utils/crypto/bls/signer/localsigner" "github.com/ava-labs/avalanchego/utils/set" - avalancheWarp "github.com/ava-labs/avalanchego/vms/platformvm/warp" "github.com/ava-labs/avalanchego/vms/platformvm/warp/payload" + "github.com/stretchr/testify/require" + "github.com/ava-labs/coreth/precompile/precompileconfig" "github.com/ava-labs/coreth/precompile/precompiletest" "github.com/ava-labs/coreth/predicate" "github.com/ava-labs/coreth/utils" - "github.com/stretchr/testify/require" + + agoUtils "github.com/ava-labs/avalanchego/utils" + avalancheWarp "github.com/ava-labs/avalanchego/vms/platformvm/warp" ) const pChainHeight uint64 = 1337 diff --git a/precompile/contracts/warp/signature_verification_test.go b/precompile/contracts/warp/signature_verification_test.go index 3c4334a366..1223b11a36 100644 --- a/precompile/contracts/warp/signature_verification_test.go +++ b/precompile/contracts/warp/signature_verification_test.go @@ -14,9 +14,10 @@ import ( "github.com/ava-labs/avalanchego/utils/constants" "github.com/ava-labs/avalanchego/utils/crypto/bls" "github.com/ava-labs/avalanchego/utils/set" - avalancheWarp "github.com/ava-labs/avalanchego/vms/platformvm/warp" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" + + avalancheWarp "github.com/ava-labs/avalanchego/vms/platformvm/warp" ) type signatureTest struct { diff --git a/precompile/modules/module.go b/precompile/modules/module.go index 69c36b846f..bfb90f79d5 100644 --- a/precompile/modules/module.go +++ b/precompile/modules/module.go @@ -6,8 +6,9 @@ package modules import ( "bytes" - "github.com/ava-labs/coreth/precompile/contract" "github.com/ava-labs/libevm/common" + + "github.com/ava-labs/coreth/precompile/contract" ) type Module struct { diff --git a/precompile/modules/registerer.go b/precompile/modules/registerer.go index 8af396ee95..0b0f0df451 100644 --- a/precompile/modules/registerer.go +++ b/precompile/modules/registerer.go @@ -7,9 +7,10 @@ import ( "fmt" "sort" + "github.com/ava-labs/libevm/common" + "github.com/ava-labs/coreth/constants" "github.com/ava-labs/coreth/utils" - "github.com/ava-labs/libevm/common" ) var ( diff --git a/precompile/modules/registerer_test.go b/precompile/modules/registerer_test.go index e1e2140b58..269404f57b 100644 --- a/precompile/modules/registerer_test.go +++ b/precompile/modules/registerer_test.go @@ -7,9 +7,10 @@ import ( "math/big" "testing" - "github.com/ava-labs/coreth/constants" "github.com/ava-labs/libevm/common" "github.com/stretchr/testify/require" + + "github.com/ava-labs/coreth/constants" ) func TestInsertSortedByAddress(t *testing.T) { diff --git a/precompile/precompiletest/test_config.go b/precompile/precompiletest/test_config.go index 1749d5532a..e0d5a0141e 100644 --- a/precompile/precompiletest/test_config.go +++ b/precompile/precompiletest/test_config.go @@ -6,9 +6,10 @@ package precompiletest import ( "testing" - "github.com/ava-labs/coreth/precompile/precompileconfig" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" + + "github.com/ava-labs/coreth/precompile/precompileconfig" ) // ConfigVerifyTest is a test case for verifying a config diff --git a/precompile/precompiletest/test_precompile.go b/precompile/precompiletest/test_precompile.go index adb29b1e6d..311a44fbbf 100644 --- a/precompile/precompiletest/test_precompile.go +++ b/precompile/precompiletest/test_precompile.go @@ -9,15 +9,16 @@ import ( "time" "github.com/ava-labs/avalanchego/snow/snowtest" - "github.com/ava-labs/coreth/core/extstate" - "github.com/ava-labs/coreth/precompile/contract" - "github.com/ava-labs/coreth/precompile/modules" - "github.com/ava-labs/coreth/precompile/precompileconfig" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/rawdb" "github.com/ava-labs/libevm/core/state" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" + + "github.com/ava-labs/coreth/core/extstate" + "github.com/ava-labs/coreth/precompile/contract" + "github.com/ava-labs/coreth/precompile/modules" + "github.com/ava-labs/coreth/precompile/precompileconfig" ) // PrecompileTest is a test case for a precompile diff --git a/precompile/precompiletest/test_predicate.go b/precompile/precompiletest/test_predicate.go index c04e0c35a9..7aa0e4c5b7 100644 --- a/precompile/precompiletest/test_predicate.go +++ b/precompile/precompiletest/test_predicate.go @@ -7,8 +7,9 @@ import ( "testing" "time" - "github.com/ava-labs/coreth/precompile/precompileconfig" "github.com/stretchr/testify/require" + + "github.com/ava-labs/coreth/precompile/precompileconfig" ) // PredicateTest defines a unit test/benchmark for verifying a precompile predicate. diff --git a/predicate/predicate_bytes.go b/predicate/predicate_bytes.go index 6eb5693afa..6ecc34b6aa 100644 --- a/predicate/predicate_bytes.go +++ b/predicate/predicate_bytes.go @@ -4,6 +4,7 @@ package predicate import ( + "errors" "fmt" "github.com/ava-labs/libevm/common" @@ -17,10 +18,10 @@ import ( var EndByte = byte(0xff) var ( - ErrInvalidAllZeroBytes = fmt.Errorf("predicate specified invalid all zero bytes") - ErrInvalidPadding = fmt.Errorf("predicate specified invalid padding") - ErrInvalidEndDelimiter = fmt.Errorf("invalid end delimiter") - ErrorInvalidExtraData = fmt.Errorf("header extra data too short for predicate verification") + ErrInvalidAllZeroBytes = errors.New("predicate specified invalid all zero bytes") + ErrInvalidPadding = errors.New("predicate specified invalid padding") + ErrInvalidEndDelimiter = errors.New("invalid end delimiter") + ErrorInvalidExtraData = errors.New("header extra data too short for predicate verification") ) // PackPredicate packs [predicate] by delimiting the actual message with [PredicateEndByte] diff --git a/predicate/predicate_slots.go b/predicate/predicate_slots.go index 342cdda04d..b6712fcce6 100644 --- a/predicate/predicate_slots.go +++ b/predicate/predicate_slots.go @@ -4,9 +4,10 @@ package predicate import ( - "github.com/ava-labs/coreth/utils" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/types" + + "github.com/ava-labs/coreth/utils" ) type PredicaterExistChecker interface { diff --git a/predicate/predicate_tx.go b/predicate/predicate_tx.go index da04df652b..81f390bd81 100644 --- a/predicate/predicate_tx.go +++ b/predicate/predicate_tx.go @@ -6,9 +6,10 @@ package predicate import ( "math/big" - "github.com/ava-labs/coreth/utils" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/types" + + "github.com/ava-labs/coreth/utils" ) // NewPredicateTx returns a transaction with the predicateAddress/predicateBytes tuple diff --git a/scripts/lint.sh b/scripts/lint.sh index ac8beb42d1..a7cf7d03c6 100755 --- a/scripts/lint.sh +++ b/scripts/lint.sh @@ -21,6 +21,7 @@ grep -P 'lint.sh' scripts/lint.sh &>/dev/null || ( # Read excluded directories into arrays DEFAULT_FILES=() UPSTREAM_FILES=() +AVALANCHE_LINT_FILE="" function read_dirs { local upstream_folders_file="./scripts/upstream_files.txt" # Read the upstream_folders file into an array @@ -43,6 +44,9 @@ function read_dirs { local -a upstream_find_args=() local -a upstream_exclude_args=() for line in "${upstream_folders[@]}"; do + # Skip empty lines + [[ -z "$line" ]] && continue + if [[ "$line" == !* ]]; then # Excluding files with ! upstream_exclude_args+=(! -path "./${line:1}") @@ -69,17 +73,39 @@ function read_dirs { # Now find default files (exclude already licensed ones) mapfile -t DEFAULT_FILES < <(find . "${find_filters[@]}" "${default_exclude_args[@]}") + + # copy avalanche file to temp directory to edit + AVALANCHE_LINT_FILE="$(mktemp -d)/.avalanche-golangci.yml" + echo "Avalanche lint file at: $AVALANCHE_LINT_FILE" + cp .avalanche-golangci.yml "$AVALANCHE_LINT_FILE" + + # Exclude all upstream files dynamically + echo " paths-except:" >> "$AVALANCHE_LINT_FILE" + for f in "${UPSTREAM_FILES[@]}"; do + # exclude pre-pended "./" + echo " - \"${f:2}\$\"" >> "$AVALANCHE_LINT_FILE" + done } -# by default, "./scripts/lint.sh" runs all linft tests +# by default, "./scripts/lint.sh" runs all lint tests # to run only "license_header" test # TESTS='license_header' ./scripts/lint.sh -TESTS=${TESTS:-"golangci_lint license_header require_error_is_no_funcs_as_params single_import interface_compliance_nil require_no_error_inline_func import_testing_only_in_tests"} +TESTS=${TESTS:-"golangci_lint avalanche_golangci_lint license_header require_error_is_no_funcs_as_params single_import interface_compliance_nil require_no_error_inline_func import_testing_only_in_tests"} function test_golangci_lint { go run github.com/golangci/golangci-lint/cmd/golangci-lint@v1.63 run --config .golangci.yml } +function test_avalanche_golangci_lint { + if [[ ! -f $AVALANCHE_LINT_FILE ]]; then + return 0 + fi + + go run github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.1.6 run \ + --config "$AVALANCHE_LINT_FILE" \ + || return 1 +} + # automatically checks license headers # to modify the file headers (if missing), remove "--verify" flag # TESTS='license_header' ADDLICENSE_FLAGS="--debug" ./scripts/lint.sh diff --git a/sync/blocksync/syncer.go b/sync/blocksync/syncer.go index 353f76963a..22226f9378 100644 --- a/sync/blocksync/syncer.go +++ b/sync/blocksync/syncer.go @@ -7,12 +7,13 @@ import ( "context" "fmt" - synccommon "github.com/ava-labs/coreth/sync" - statesyncclient "github.com/ava-labs/coreth/sync/client" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/rawdb" "github.com/ava-labs/libevm/ethdb" "github.com/ava-labs/libevm/log" + + synccommon "github.com/ava-labs/coreth/sync" + statesyncclient "github.com/ava-labs/coreth/sync/client" ) const blocksPerRequest = 32 diff --git a/sync/blocksync/syncer_test.go b/sync/blocksync/syncer_test.go index e2e4946ca9..2211f14b65 100644 --- a/sync/blocksync/syncer_test.go +++ b/sync/blocksync/syncer_test.go @@ -9,13 +9,6 @@ import ( "math/big" "testing" - "github.com/ava-labs/coreth/consensus/dummy" - "github.com/ava-labs/coreth/core" - "github.com/ava-labs/coreth/params" - "github.com/ava-labs/coreth/plugin/evm/message" - syncclient "github.com/ava-labs/coreth/sync/client" - "github.com/ava-labs/coreth/sync/handlers" - handlerstats "github.com/ava-labs/coreth/sync/handlers/stats" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/rawdb" "github.com/ava-labs/libevm/core/types" @@ -23,6 +16,15 @@ import ( "github.com/ava-labs/libevm/ethdb" ethparams "github.com/ava-labs/libevm/params" "github.com/stretchr/testify/require" + + "github.com/ava-labs/coreth/consensus/dummy" + "github.com/ava-labs/coreth/core" + "github.com/ava-labs/coreth/params" + "github.com/ava-labs/coreth/plugin/evm/message" + "github.com/ava-labs/coreth/sync/handlers" + + syncclient "github.com/ava-labs/coreth/sync/client" + handlerstats "github.com/ava-labs/coreth/sync/handlers/stats" ) func TestBlockSyncer_ParameterizedTests(t *testing.T) { diff --git a/sync/client/client.go b/sync/client/client.go index 849cf5a17f..14c4837dec 100644 --- a/sync/client/client.go +++ b/sync/client/client.go @@ -14,9 +14,6 @@ import ( "github.com/ava-labs/avalanchego/codec" "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/version" - "github.com/ava-labs/coreth/network" - "github.com/ava-labs/coreth/plugin/evm/message" - "github.com/ava-labs/coreth/sync/client/stats" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/rawdb" "github.com/ava-labs/libevm/core/types" @@ -25,6 +22,10 @@ import ( "github.com/ava-labs/libevm/log" "github.com/ava-labs/libevm/trie" + "github.com/ava-labs/coreth/network" + "github.com/ava-labs/coreth/plugin/evm/message" + "github.com/ava-labs/coreth/sync/client/stats" + ethparams "github.com/ava-labs/libevm/params" ) @@ -141,7 +142,7 @@ func parseLeafsResponse(codec codec.Manager, reqIntf message.Request, data []byt // An empty response (no more keys) requires a merkle proof if len(leafsResponse.Keys) == 0 && len(leafsResponse.ProofVals) == 0 { - return nil, 0, fmt.Errorf("empty key response must include merkle proof") + return nil, 0, errors.New("empty key response must include merkle proof") } var proof ethdb.Database @@ -172,7 +173,7 @@ func parseLeafsResponse(codec codec.Manager, reqIntf message.Request, data []byt // Also ensures the keys are in monotonically increasing order more, err := trie.VerifyRangeProof(leafsRequest.Root, firstKey, leafsResponse.Keys, leafsResponse.Vals, proof) if err != nil { - return nil, 0, fmt.Errorf("%s due to %w", errInvalidRangeProof, err) + return nil, 0, fmt.Errorf("%w due to %w", errInvalidRangeProof, err) } // Set the [More] flag to indicate if there are more leaves to the right of the last key in the response @@ -204,7 +205,7 @@ func (c *client) GetBlocks(ctx context.Context, hash common.Hash, height uint64, func (c *client) parseBlocks(codec codec.Manager, req message.Request, data []byte) (interface{}, int, error) { var response message.BlockResponse if _, err := codec.Unmarshal(data, &response); err != nil { - return nil, 0, fmt.Errorf("%s: %w", errUnmarshalResponse, err) + return nil, 0, fmt.Errorf("%w: %w", errUnmarshalResponse, err) } if len(response.Blocks) == 0 { return nil, 0, errEmptyResponse @@ -222,7 +223,7 @@ func (c *client) parseBlocks(codec codec.Manager, req message.Request, data []by for i, blkBytes := range response.Blocks { block, err := c.blockParser.ParseEthBlock(blkBytes) if err != nil { - return nil, 0, fmt.Errorf("%s: %w", errUnmarshalResponse, err) + return nil, 0, fmt.Errorf("%w: %w", errUnmarshalResponse, err) } if block.Hash() != hash { @@ -304,7 +305,7 @@ func (c *client) get(ctx context.Context, request message.Request, parseFn parse // If the context has finished, return the context error early. if ctxErr := ctx.Err(); ctxErr != nil { if lastErr != nil { - return nil, fmt.Errorf("request failed after %d attempts with last error %w and ctx error %s", attempt, lastErr, ctxErr) + return nil, fmt.Errorf("request failed after %d attempts with last error %w and ctx error %w", attempt, lastErr, ctxErr) } else { return nil, ctxErr } diff --git a/sync/client/client_test.go b/sync/client/client_test.go index d143fa05e1..60a94b0c77 100644 --- a/sync/client/client_test.go +++ b/sync/client/client_test.go @@ -11,24 +11,24 @@ import ( "strings" "testing" - "github.com/stretchr/testify/assert" - "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/libevm/common" + "github.com/ava-labs/libevm/core/rawdb" + "github.com/ava-labs/libevm/core/types" + "github.com/ava-labs/libevm/crypto" + ethparams "github.com/ava-labs/libevm/params" + "github.com/ava-labs/libevm/triedb" + "github.com/stretchr/testify/assert" "github.com/ava-labs/coreth/consensus/dummy" "github.com/ava-labs/coreth/core" "github.com/ava-labs/coreth/params" "github.com/ava-labs/coreth/plugin/evm/message" - clientstats "github.com/ava-labs/coreth/sync/client/stats" "github.com/ava-labs/coreth/sync/handlers" - handlerstats "github.com/ava-labs/coreth/sync/handlers/stats" "github.com/ava-labs/coreth/sync/statesync/statesynctest" - "github.com/ava-labs/libevm/common" - "github.com/ava-labs/libevm/core/rawdb" - "github.com/ava-labs/libevm/core/types" - "github.com/ava-labs/libevm/crypto" - ethparams "github.com/ava-labs/libevm/params" - "github.com/ava-labs/libevm/triedb" + + clientstats "github.com/ava-labs/coreth/sync/client/stats" + handlerstats "github.com/ava-labs/coreth/sync/handlers/stats" ) func TestGetCode(t *testing.T) { diff --git a/sync/client/leaf_syncer.go b/sync/client/leaf_syncer.go index 6cf766a217..05199c7eb7 100644 --- a/sync/client/leaf_syncer.go +++ b/sync/client/leaf_syncer.go @@ -9,10 +9,11 @@ import ( "errors" "fmt" - "github.com/ava-labs/coreth/plugin/evm/message" - "github.com/ava-labs/coreth/utils" "github.com/ava-labs/libevm/common" "golang.org/x/sync/errgroup" + + "github.com/ava-labs/coreth/plugin/evm/message" + "github.com/ava-labs/coreth/utils" ) var ErrFailedToFetchLeafs = errors.New("failed to fetch leafs") @@ -138,7 +139,7 @@ func (c *CallbackLeafSyncer) syncTask(ctx context.Context, task LeafSyncTask) er } if len(leafsResponse.Keys) == 0 { - return fmt.Errorf("found no keys in a response with more set to true") + return errors.New("found no keys in a response with more set to true") } // Update start to be one bit past the last returned key for the next request. // Note: since more was true, this cannot cause an overflow. diff --git a/sync/client/stats/stats.go b/sync/client/stats/stats.go index cd9b3eb0e7..b7495dc953 100644 --- a/sync/client/stats/stats.go +++ b/sync/client/stats/stats.go @@ -7,8 +7,9 @@ import ( "fmt" "time" - "github.com/ava-labs/coreth/plugin/evm/message" "github.com/ava-labs/libevm/metrics" + + "github.com/ava-labs/coreth/plugin/evm/message" ) var ( @@ -41,12 +42,12 @@ type messageMetric struct { func NewMessageMetric(name string) MessageMetric { return &messageMetric{ - requested: metrics.GetOrRegisterCounter(fmt.Sprintf("%s_requested", name), nil), - succeeded: metrics.GetOrRegisterCounter(fmt.Sprintf("%s_succeeded", name), nil), - failed: metrics.GetOrRegisterCounter(fmt.Sprintf("%s_failed", name), nil), - invalidResponse: metrics.GetOrRegisterCounter(fmt.Sprintf("%s_invalid_response", name), nil), - received: metrics.GetOrRegisterCounter(fmt.Sprintf("%s_received", name), nil), - requestLatency: metrics.GetOrRegisterTimer(fmt.Sprintf("%s_request_latency", name), nil), + requested: metrics.GetOrRegisterCounter(name+"_requested", nil), + succeeded: metrics.GetOrRegisterCounter(name+"_succeeded", nil), + failed: metrics.GetOrRegisterCounter(name+"_failed", nil), + invalidResponse: metrics.GetOrRegisterCounter(name+"_invalid_response", nil), + received: metrics.GetOrRegisterCounter(name+"_received", nil), + requestLatency: metrics.GetOrRegisterTimer(name+"_request_latency", nil), } } diff --git a/sync/client/test_client.go b/sync/client/test_client.go index 85ec4a758e..36469fd84c 100644 --- a/sync/client/test_client.go +++ b/sync/client/test_client.go @@ -10,11 +10,12 @@ import ( "github.com/ava-labs/avalanchego/codec" "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/coreth/plugin/evm/message" - "github.com/ava-labs/coreth/sync/handlers" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/types" "github.com/ava-labs/libevm/rlp" + + "github.com/ava-labs/coreth/plugin/evm/message" + "github.com/ava-labs/coreth/sync/handlers" ) var ( @@ -151,7 +152,7 @@ func newTestBlockParser() *testBlockParser { func (t *testBlockParser) ParseEthBlock(b []byte) (*types.Block, error) { block := new(types.Block) if err := rlp.DecodeBytes(b, block); err != nil { - return nil, fmt.Errorf("%s: %w", errUnmarshalResponse, err) + return nil, fmt.Errorf("%w: %w", errUnmarshalResponse, err) } return block, nil diff --git a/sync/client/test_network.go b/sync/client/test_network.go index af0d26ccd1..8ada659eee 100644 --- a/sync/client/test_network.go +++ b/sync/client/test_network.go @@ -8,9 +8,9 @@ import ( "errors" "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/coreth/network" - "github.com/ava-labs/avalanchego/version" + + "github.com/ava-labs/coreth/network" ) var _ network.SyncedNetworkClient = (*testNetwork)(nil) diff --git a/sync/handlers/block_request.go b/sync/handlers/block_request.go index a18cc1c489..fe5f413134 100644 --- a/sync/handlers/block_request.go +++ b/sync/handlers/block_request.go @@ -11,11 +11,11 @@ import ( "github.com/ava-labs/avalanchego/codec" "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/utils/units" + "github.com/ava-labs/libevm/common" + "github.com/ava-labs/libevm/log" "github.com/ava-labs/coreth/plugin/evm/message" "github.com/ava-labs/coreth/sync/handlers/stats" - "github.com/ava-labs/libevm/common" - "github.com/ava-labs/libevm/log" ) const ( diff --git a/sync/handlers/block_request_test.go b/sync/handlers/block_request_test.go index 6db3ac489d..2f20edbd88 100644 --- a/sync/handlers/block_request_test.go +++ b/sync/handlers/block_request_test.go @@ -10,12 +10,6 @@ import ( "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/utils/units" - "github.com/ava-labs/coreth/consensus/dummy" - "github.com/ava-labs/coreth/core" - "github.com/ava-labs/coreth/params" - "github.com/ava-labs/coreth/plugin/evm/message" - "github.com/ava-labs/coreth/sync/handlers/stats" - "github.com/ava-labs/coreth/sync/handlers/stats/statstest" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/rawdb" "github.com/ava-labs/libevm/core/types" @@ -23,6 +17,13 @@ import ( "github.com/ava-labs/libevm/rlp" "github.com/ava-labs/libevm/triedb" "github.com/stretchr/testify/assert" + + "github.com/ava-labs/coreth/consensus/dummy" + "github.com/ava-labs/coreth/core" + "github.com/ava-labs/coreth/params" + "github.com/ava-labs/coreth/plugin/evm/message" + "github.com/ava-labs/coreth/sync/handlers/stats" + "github.com/ava-labs/coreth/sync/handlers/stats/statstest" ) type blockRequestTest struct { diff --git a/sync/handlers/code_request.go b/sync/handlers/code_request.go index ce4e96fde0..05a5914bf9 100644 --- a/sync/handlers/code_request.go +++ b/sync/handlers/code_request.go @@ -9,13 +9,13 @@ import ( "github.com/ava-labs/avalanchego/codec" "github.com/ava-labs/avalanchego/ids" - - "github.com/ava-labs/coreth/plugin/evm/message" - "github.com/ava-labs/coreth/sync/handlers/stats" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/rawdb" "github.com/ava-labs/libevm/ethdb" "github.com/ava-labs/libevm/log" + + "github.com/ava-labs/coreth/plugin/evm/message" + "github.com/ava-labs/coreth/sync/handlers/stats" ) // CodeRequestHandler is a peer.RequestHandler for message.CodeRequest diff --git a/sync/handlers/code_request_test.go b/sync/handlers/code_request_test.go index 3107b5913d..97b49aae1b 100644 --- a/sync/handlers/code_request_test.go +++ b/sync/handlers/code_request_test.go @@ -9,14 +9,16 @@ import ( "testing" "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/coreth/plugin/evm/message" - "github.com/ava-labs/coreth/sync/handlers/stats/statstest" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/rawdb" "github.com/ava-labs/libevm/crypto" "github.com/ava-labs/libevm/ethdb/memorydb" - ethparams "github.com/ava-labs/libevm/params" "github.com/stretchr/testify/assert" + + "github.com/ava-labs/coreth/plugin/evm/message" + "github.com/ava-labs/coreth/sync/handlers/stats/statstest" + + ethparams "github.com/ava-labs/libevm/params" ) func TestCodeRequestHandler(t *testing.T) { diff --git a/sync/handlers/handler.go b/sync/handlers/handler.go index 967bcb8e36..dfc61a54e6 100644 --- a/sync/handlers/handler.go +++ b/sync/handlers/handler.go @@ -4,9 +4,10 @@ package handlers import ( - "github.com/ava-labs/coreth/core/state/snapshot" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/types" + + "github.com/ava-labs/coreth/core/state/snapshot" ) type BlockProvider interface { diff --git a/sync/handlers/leafs_request.go b/sync/handlers/leafs_request.go index d50c210c59..ae95f33e50 100644 --- a/sync/handlers/leafs_request.go +++ b/sync/handlers/leafs_request.go @@ -11,11 +11,6 @@ import ( "github.com/ava-labs/avalanchego/codec" "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/coreth/core/state/snapshot" - "github.com/ava-labs/coreth/plugin/evm/message" - "github.com/ava-labs/coreth/sync/handlers/stats" - "github.com/ava-labs/coreth/sync/syncutils" - "github.com/ava-labs/coreth/utils" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/types" "github.com/ava-labs/libevm/ethdb" @@ -23,6 +18,12 @@ import ( "github.com/ava-labs/libevm/log" "github.com/ava-labs/libevm/trie" "github.com/ava-labs/libevm/triedb" + + "github.com/ava-labs/coreth/core/state/snapshot" + "github.com/ava-labs/coreth/plugin/evm/message" + "github.com/ava-labs/coreth/sync/handlers/stats" + "github.com/ava-labs/coreth/sync/syncutils" + "github.com/ava-labs/coreth/utils" ) var _ LeafRequestHandler = (*leafsRequestHandler)(nil) diff --git a/sync/handlers/leafs_request_test.go b/sync/handlers/leafs_request_test.go index 1ccd50b271..19a40cb688 100644 --- a/sync/handlers/leafs_request_test.go +++ b/sync/handlers/leafs_request_test.go @@ -10,10 +10,6 @@ import ( "testing" "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/coreth/core/state/snapshot" - "github.com/ava-labs/coreth/plugin/evm/message" - "github.com/ava-labs/coreth/sync/handlers/stats/statstest" - "github.com/ava-labs/coreth/sync/statesync/statesynctest" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/rawdb" "github.com/ava-labs/libevm/core/types" @@ -22,6 +18,11 @@ import ( "github.com/ava-labs/libevm/trie" "github.com/ava-labs/libevm/triedb" "github.com/stretchr/testify/assert" + + "github.com/ava-labs/coreth/core/state/snapshot" + "github.com/ava-labs/coreth/plugin/evm/message" + "github.com/ava-labs/coreth/sync/handlers/stats/statstest" + "github.com/ava-labs/coreth/sync/statesync/statesynctest" ) func TestLeafsRequestHandler_OnLeafsRequest(t *testing.T) { diff --git a/sync/handlers/test_providers.go b/sync/handlers/test_providers.go index 8ebea9f77b..7bf6ff26b2 100644 --- a/sync/handlers/test_providers.go +++ b/sync/handlers/test_providers.go @@ -4,9 +4,10 @@ package handlers import ( - "github.com/ava-labs/coreth/core/state/snapshot" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/types" + + "github.com/ava-labs/coreth/core/state/snapshot" ) var ( diff --git a/sync/statesync/code_syncer.go b/sync/statesync/code_syncer.go index 143a3ad5ba..ab4e74ecf1 100644 --- a/sync/statesync/code_syncer.go +++ b/sync/statesync/code_syncer.go @@ -10,14 +10,16 @@ import ( "sync" "github.com/ava-labs/avalanchego/utils/set" - "github.com/ava-labs/coreth/plugin/evm/customrawdb" - "github.com/ava-labs/coreth/plugin/evm/message" - synccommon "github.com/ava-labs/coreth/sync" - statesyncclient "github.com/ava-labs/coreth/sync/client" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/rawdb" "github.com/ava-labs/libevm/ethdb" "golang.org/x/sync/errgroup" + + "github.com/ava-labs/coreth/plugin/evm/customrawdb" + "github.com/ava-labs/coreth/plugin/evm/message" + + synccommon "github.com/ava-labs/coreth/sync" + statesyncclient "github.com/ava-labs/coreth/sync/client" ) const ( diff --git a/sync/statesync/code_syncer_test.go b/sync/statesync/code_syncer_test.go index 517e2443d9..d58e5e09cc 100644 --- a/sync/statesync/code_syncer_test.go +++ b/sync/statesync/code_syncer_test.go @@ -9,20 +9,19 @@ import ( "testing" "github.com/ava-labs/avalanchego/utils" - - "github.com/ava-labs/coreth/plugin/evm/customrawdb" - "github.com/ava-labs/coreth/plugin/evm/message" - statesyncclient "github.com/ava-labs/coreth/sync/client" - "github.com/ava-labs/coreth/sync/handlers" - handlerstats "github.com/ava-labs/coreth/sync/handlers/stats" - "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/rawdb" "github.com/ava-labs/libevm/crypto" "github.com/ava-labs/libevm/ethdb" "github.com/ava-labs/libevm/ethdb/memorydb" - "github.com/stretchr/testify/require" + + "github.com/ava-labs/coreth/plugin/evm/customrawdb" + "github.com/ava-labs/coreth/plugin/evm/message" + "github.com/ava-labs/coreth/sync/handlers" + + statesyncclient "github.com/ava-labs/coreth/sync/client" + handlerstats "github.com/ava-labs/coreth/sync/handlers/stats" ) type codeSyncerTest struct { diff --git a/sync/statesync/state_syncer.go b/sync/statesync/state_syncer.go index 48362d9ceb..3dd1e965f0 100644 --- a/sync/statesync/state_syncer.go +++ b/sync/statesync/state_syncer.go @@ -8,14 +8,16 @@ import ( "fmt" "sync" - "github.com/ava-labs/coreth/core/state/snapshot" - synccommon "github.com/ava-labs/coreth/sync" - syncclient "github.com/ava-labs/coreth/sync/client" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/ethdb" "github.com/ava-labs/libevm/log" "github.com/ava-labs/libevm/triedb" "golang.org/x/sync/errgroup" + + "github.com/ava-labs/coreth/core/state/snapshot" + + synccommon "github.com/ava-labs/coreth/sync" + syncclient "github.com/ava-labs/coreth/sync/client" ) const ( diff --git a/sync/statesync/statesynctest/test_sync.go b/sync/statesync/statesynctest/test_sync.go index cb0c65daca..515484938b 100644 --- a/sync/statesync/statesynctest/test_sync.go +++ b/sync/statesync/statesynctest/test_sync.go @@ -6,10 +6,11 @@ package statesynctest import ( "testing" - "github.com/ava-labs/coreth/utils/utilstest" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/types" "github.com/ava-labs/libevm/triedb" + + "github.com/ava-labs/coreth/utils/utilstest" ) // FillAccountsWithOverlappingStorage adds [numAccounts] randomly generated accounts to the secure trie at [root] diff --git a/sync/statesync/statesynctest/test_trie.go b/sync/statesync/statesynctest/test_trie.go index 9724fe33f3..0f207f7651 100644 --- a/sync/statesync/statesynctest/test_trie.go +++ b/sync/statesync/statesynctest/test_trie.go @@ -9,17 +9,17 @@ import ( "testing" "github.com/ava-labs/avalanchego/utils/wrappers" - "github.com/ava-labs/coreth/utils/utilstest" + "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/types" + "github.com/ava-labs/libevm/ethdb" + "github.com/ava-labs/libevm/rlp" "github.com/ava-labs/libevm/trie" "github.com/ava-labs/libevm/trie/trienode" "github.com/ava-labs/libevm/triedb" "github.com/holiman/uint256" - - "github.com/ava-labs/libevm/common" - "github.com/ava-labs/libevm/ethdb" - "github.com/ava-labs/libevm/rlp" "github.com/stretchr/testify/assert" + + "github.com/ava-labs/coreth/utils/utilstest" ) // GenerateTrie creates a trie with [numKeys] key-value pairs inside of [trieDB]. diff --git a/sync/statesync/sync_test.go b/sync/statesync/sync_test.go index 77153b9e4a..3ede12ba59 100644 --- a/sync/statesync/sync_test.go +++ b/sync/statesync/sync_test.go @@ -11,13 +11,6 @@ import ( "sync/atomic" "testing" - "github.com/ava-labs/coreth/core/state/snapshot" - "github.com/ava-labs/coreth/plugin/evm/customrawdb" - "github.com/ava-labs/coreth/plugin/evm/message" - statesyncclient "github.com/ava-labs/coreth/sync/client" - "github.com/ava-labs/coreth/sync/handlers" - handlerstats "github.com/ava-labs/coreth/sync/handlers/stats" - "github.com/ava-labs/coreth/sync/statesync/statesynctest" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/rawdb" "github.com/ava-labs/libevm/core/types" @@ -27,6 +20,15 @@ import ( "github.com/ava-labs/libevm/trie" "github.com/ava-labs/libevm/triedb" "github.com/stretchr/testify/require" + + "github.com/ava-labs/coreth/core/state/snapshot" + "github.com/ava-labs/coreth/plugin/evm/customrawdb" + "github.com/ava-labs/coreth/plugin/evm/message" + "github.com/ava-labs/coreth/sync/handlers" + "github.com/ava-labs/coreth/sync/statesync/statesynctest" + + statesyncclient "github.com/ava-labs/coreth/sync/client" + handlerstats "github.com/ava-labs/coreth/sync/handlers/stats" ) const testRequestSize = 1024 diff --git a/sync/statesync/trie_queue.go b/sync/statesync/trie_queue.go index bbdffc18cf..f95594216e 100644 --- a/sync/statesync/trie_queue.go +++ b/sync/statesync/trie_queue.go @@ -4,9 +4,10 @@ package statesync import ( - "github.com/ava-labs/coreth/plugin/evm/customrawdb" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/ethdb" + + "github.com/ava-labs/coreth/plugin/evm/customrawdb" ) // trieQueue persists storage trie roots with their associated diff --git a/sync/statesync/trie_segments.go b/sync/statesync/trie_segments.go index d92a0b8ecc..6ae7fd4163 100644 --- a/sync/statesync/trie_segments.go +++ b/sync/statesync/trie_segments.go @@ -11,15 +11,17 @@ import ( "sync" "github.com/ava-labs/avalanchego/utils/wrappers" - "github.com/ava-labs/coreth/plugin/evm/customrawdb" - "github.com/ava-labs/coreth/plugin/evm/message" - syncclient "github.com/ava-labs/coreth/sync/client" - "github.com/ava-labs/coreth/utils" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/rawdb" "github.com/ava-labs/libevm/ethdb" "github.com/ava-labs/libevm/log" "github.com/ava-labs/libevm/trie" + + "github.com/ava-labs/coreth/plugin/evm/customrawdb" + "github.com/ava-labs/coreth/plugin/evm/message" + "github.com/ava-labs/coreth/utils" + + syncclient "github.com/ava-labs/coreth/sync/client" ) var ( diff --git a/sync/statesync/trie_sync_stats.go b/sync/statesync/trie_sync_stats.go index 4695464d19..f3b6f7b461 100644 --- a/sync/statesync/trie_sync_stats.go +++ b/sync/statesync/trie_sync_stats.go @@ -8,11 +8,12 @@ import ( "sync" "time" - utils_math "github.com/ava-labs/avalanchego/utils/math" "github.com/ava-labs/avalanchego/utils/timer" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/log" "github.com/ava-labs/libevm/metrics" + + safemath "github.com/ava-labs/avalanchego/utils/math" ) const ( @@ -27,7 +28,7 @@ type trieSyncStats struct { lock sync.Mutex lastUpdated time.Time - leafsRate utils_math.Averager + leafsRate safemath.Averager triesRemaining int triesSynced int @@ -118,7 +119,7 @@ func (t *trieSyncStats) trieDone(root common.Hash) { func (t *trieSyncStats) updateETA(sinceUpdate time.Duration, now time.Time) time.Duration { leafsRate := float64(t.leafsSinceUpdate) / sinceUpdate.Seconds() if t.leafsRate == nil { - t.leafsRate = utils_math.NewAverager(leafsRate, leafRateHalfLife, now) + t.leafsRate = safemath.NewAverager(leafsRate, leafRateHalfLife, now) } else { t.leafsRate.Observe(leafsRate, now) } diff --git a/sync/statesync/trie_sync_tasks.go b/sync/statesync/trie_sync_tasks.go index 082c2691fc..d5abc8a83c 100644 --- a/sync/statesync/trie_sync_tasks.go +++ b/sync/statesync/trie_sync_tasks.go @@ -6,7 +6,6 @@ package statesync import ( "fmt" - "github.com/ava-labs/coreth/sync/syncutils" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/rawdb" "github.com/ava-labs/libevm/core/types" @@ -14,6 +13,8 @@ import ( "github.com/ava-labs/libevm/log" "github.com/ava-labs/libevm/rlp" "github.com/ava-labs/libevm/trie" + + "github.com/ava-labs/coreth/sync/syncutils" ) var ( diff --git a/sync/syncutils/iterators.go b/sync/syncutils/iterators.go index 9f39759c8a..c443852208 100644 --- a/sync/syncutils/iterators.go +++ b/sync/syncutils/iterators.go @@ -4,9 +4,10 @@ package syncutils import ( - "github.com/ava-labs/coreth/core/state/snapshot" "github.com/ava-labs/libevm/core/types" "github.com/ava-labs/libevm/ethdb" + + "github.com/ava-labs/coreth/core/state/snapshot" ) var ( diff --git a/sync/types.go b/sync/types.go index 3c6973f3cd..7ec8ded099 100644 --- a/sync/types.go +++ b/sync/types.go @@ -8,9 +8,11 @@ import ( "github.com/ava-labs/avalanchego/database/versiondb" "github.com/ava-labs/avalanchego/snow/engine/snowman/block" + "github.com/ava-labs/libevm/core/types" + "github.com/ava-labs/coreth/plugin/evm/message" + syncclient "github.com/ava-labs/coreth/sync/client" - "github.com/ava-labs/libevm/core/types" ) // Syncer is the common interface for all sync operations. diff --git a/sync/vm/client.go b/sync/vm/client.go index cb854225da..b713295b42 100644 --- a/sync/vm/client.go +++ b/sync/vm/client.go @@ -8,24 +8,25 @@ import ( "fmt" "sync" - synccommon "github.com/ava-labs/coreth/sync" - "github.com/ava-labs/coreth/sync/blocksync" - syncclient "github.com/ava-labs/coreth/sync/client" - "github.com/ava-labs/coreth/sync/statesync" - "github.com/ava-labs/avalanchego/database" "github.com/ava-labs/avalanchego/database/versiondb" "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/snow/engine/snowman/block" "github.com/ava-labs/avalanchego/vms/components/chain" - "github.com/ava-labs/coreth/core/state/snapshot" - "github.com/ava-labs/coreth/eth" - "github.com/ava-labs/coreth/params" - "github.com/ava-labs/coreth/plugin/evm/message" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/types" "github.com/ava-labs/libevm/ethdb" "github.com/ava-labs/libevm/log" + + "github.com/ava-labs/coreth/core/state/snapshot" + "github.com/ava-labs/coreth/eth" + "github.com/ava-labs/coreth/params" + "github.com/ava-labs/coreth/plugin/evm/message" + "github.com/ava-labs/coreth/sync/blocksync" + "github.com/ava-labs/coreth/sync/statesync" + + synccommon "github.com/ava-labs/coreth/sync" + syncclient "github.com/ava-labs/coreth/sync/client" ) const ( diff --git a/sync/vm/registry.go b/sync/vm/registry.go index ebe90f9796..5296576e5a 100644 --- a/sync/vm/registry.go +++ b/sync/vm/registry.go @@ -7,8 +7,9 @@ import ( "context" "fmt" - synccommon "github.com/ava-labs/coreth/sync" "github.com/ava-labs/libevm/log" + + synccommon "github.com/ava-labs/coreth/sync" ) // SyncerTask represents a single syncer with its name for identification. diff --git a/sync/vm/server.go b/sync/vm/server.go index 75affdd620..d5d40c33eb 100644 --- a/sync/vm/server.go +++ b/sync/vm/server.go @@ -4,18 +4,19 @@ package vm import ( "context" + "errors" "fmt" "github.com/ava-labs/avalanchego/database" "github.com/ava-labs/avalanchego/snow/engine/snowman/block" + "github.com/ava-labs/libevm/log" "github.com/ava-labs/coreth/core" - synccommon "github.com/ava-labs/coreth/sync" - "github.com/ava-labs/libevm/log" + synccommon "github.com/ava-labs/coreth/sync" ) -var errProviderNotSet = fmt.Errorf("provider not set") +var errProviderNotSet = errors.New("provider not set") type server struct { chain *core.BlockChain diff --git a/tests/utils/proposervm.go b/tests/utils/proposervm.go index 6d7c81f6bf..204de2bb8e 100644 --- a/tests/utils/proposervm.go +++ b/tests/utils/proposervm.go @@ -9,13 +9,15 @@ import ( "math/big" "time" - "github.com/ava-labs/coreth/accounts/abi/bind" - "github.com/ava-labs/coreth/ethclient" - "github.com/ava-labs/coreth/plugin/evm/upgrade/ap1" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/types" "github.com/ava-labs/libevm/crypto" "github.com/ava-labs/libevm/log" + + "github.com/ava-labs/coreth/accounts/abi/bind" + "github.com/ava-labs/coreth/ethclient" + "github.com/ava-labs/coreth/plugin/evm/upgrade/ap1" + ethparams "github.com/ava-labs/libevm/params" ) diff --git a/tests/warp/aggregator/aggregator.go b/tests/warp/aggregator/aggregator.go index cf564ed27d..0755f98d2f 100644 --- a/tests/warp/aggregator/aggregator.go +++ b/tests/warp/aggregator/aggregator.go @@ -7,12 +7,13 @@ import ( "context" "fmt" - "github.com/ava-labs/libevm/log" - "github.com/ava-labs/avalanchego/utils/crypto/bls" "github.com/ava-labs/avalanchego/utils/set" - avalancheWarp "github.com/ava-labs/avalanchego/vms/platformvm/warp" + "github.com/ava-labs/libevm/log" + "github.com/ava-labs/coreth/precompile/contracts/warp" + + avalancheWarp "github.com/ava-labs/avalanchego/vms/platformvm/warp" ) type AggregateSignatureResult struct { diff --git a/tests/warp/aggregator/aggregator_test.go b/tests/warp/aggregator/aggregator_test.go index 9c5894ec28..1997983f13 100644 --- a/tests/warp/aggregator/aggregator_test.go +++ b/tests/warp/aggregator/aggregator_test.go @@ -8,13 +8,12 @@ import ( "errors" "testing" - "github.com/stretchr/testify/require" - - "go.uber.org/mock/gomock" - "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/utils/crypto/bls" "github.com/ava-labs/avalanchego/utils/crypto/bls/signer/localsigner" + "github.com/stretchr/testify/require" + "go.uber.org/mock/gomock" + avalancheWarp "github.com/ava-labs/avalanchego/vms/platformvm/warp" ) diff --git a/tests/warp/aggregator/signature_getter.go b/tests/warp/aggregator/signature_getter.go index 8f03814f5e..b7a2ebc073 100644 --- a/tests/warp/aggregator/signature_getter.go +++ b/tests/warp/aggregator/signature_getter.go @@ -5,6 +5,7 @@ package aggregator import ( "context" + "errors" "fmt" "time" @@ -12,8 +13,9 @@ import ( "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/proto/pb/sdk" "github.com/ava-labs/avalanchego/utils/crypto/bls" - avalancheWarp "github.com/ava-labs/avalanchego/vms/platformvm/warp" "google.golang.org/protobuf/proto" + + avalancheWarp "github.com/ava-labs/avalanchego/vms/platformvm/warp" ) const ( @@ -92,7 +94,7 @@ func (s *NetworkSignatureGetter) GetSignature(ctx context.Context, nodeID ids.No } if len(response.Signature) == 0 { - return nil, fmt.Errorf("received empty signature response") + return nil, errors.New("received empty signature response") } blsSignature, err := bls.SignatureFromBytes(response.Signature[:]) if err != nil { diff --git a/tests/warp/fetcher.go b/tests/warp/fetcher.go index 467677a238..0e91b70b58 100644 --- a/tests/warp/fetcher.go +++ b/tests/warp/fetcher.go @@ -9,8 +9,9 @@ import ( "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/utils/crypto/bls" - avalancheWarp "github.com/ava-labs/avalanchego/vms/platformvm/warp" "github.com/ava-labs/avalanchego/vms/platformvm/warp/payload" + + avalancheWarp "github.com/ava-labs/avalanchego/vms/platformvm/warp" warpBackend "github.com/ava-labs/coreth/warp" ) diff --git a/tests/warp/warp_test.go b/tests/warp/warp_test.go index 252840263f..cee844ee73 100644 --- a/tests/warp/warp_test.go +++ b/tests/warp/warp_test.go @@ -13,13 +13,6 @@ import ( "testing" "time" - ginkgo "github.com/onsi/ginkgo/v2" - - "github.com/stretchr/testify/require" - - "github.com/ava-labs/libevm/common" - "github.com/ava-labs/libevm/crypto" - "github.com/ava-labs/avalanchego/api/info" "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/snow/validators" @@ -28,8 +21,11 @@ import ( "github.com/ava-labs/avalanchego/utils/constants" "github.com/ava-labs/avalanchego/vms/platformvm" "github.com/ava-labs/avalanchego/vms/platformvm/api" - avalancheWarp "github.com/ava-labs/avalanchego/vms/platformvm/warp" "github.com/ava-labs/avalanchego/vms/platformvm/warp/payload" + "github.com/ava-labs/libevm/common" + "github.com/ava-labs/libevm/core/types" + "github.com/ava-labs/libevm/crypto" + "github.com/stretchr/testify/require" "github.com/ava-labs/coreth/accounts/abi/bind" "github.com/ava-labs/coreth/cmd/simulator/key" @@ -42,9 +38,11 @@ import ( "github.com/ava-labs/coreth/predicate" "github.com/ava-labs/coreth/tests/utils" "github.com/ava-labs/coreth/tests/warp/aggregator" + + avalancheWarp "github.com/ava-labs/avalanchego/vms/platformvm/warp" warpBackend "github.com/ava-labs/coreth/warp" ethereum "github.com/ava-labs/libevm" - "github.com/ava-labs/libevm/core/types" + ginkgo "github.com/onsi/ginkgo/v2" ) var ( diff --git a/triedb/firewood/database.go b/triedb/firewood/database.go index 529076caa0..741a8bee9f 100644 --- a/triedb/firewood/database.go +++ b/triedb/firewood/database.go @@ -4,6 +4,7 @@ package firewood import ( + "errors" "fmt" "os" "path/filepath" @@ -125,7 +126,7 @@ func New(config *Config) *Database { func validatePath(trieConfig *Config) (*ffi.Config, error) { if trieConfig.FilePath == "" { - return nil, fmt.Errorf("firewood database file path must be set") + return nil, errors.New("firewood database file path must be set") } // Check that the directory exists @@ -134,7 +135,7 @@ func validatePath(trieConfig *Config) (*ffi.Config, error) { if err != nil { if os.IsNotExist(err) { log.Info("Database directory not found, creating", "path", dir) - if err := os.MkdirAll(dir, 0755); err != nil { + if err := os.MkdirAll(dir, 0o755); err != nil { return nil, fmt.Errorf("error creating database directory: %w", err) } } else { @@ -533,7 +534,7 @@ func (db *Database) getProposalHash(parentRoot common.Hash, keys, values [][]byt // Propose from the database root. p, err = db.fwDisk.Propose(keys, values) if err != nil { - return common.Hash{}, fmt.Errorf("firewood: error proposing from root %s: %v", parentRoot.Hex(), err) + return common.Hash{}, fmt.Errorf("firewood: error proposing from root %s: %w", parentRoot.Hex(), err) } } else { // Find any proposal with the given parent root. @@ -547,7 +548,7 @@ func (db *Database) getProposalHash(parentRoot common.Hash, keys, values [][]byt p, err = rootProposal.Propose(keys, values) if err != nil { - return common.Hash{}, fmt.Errorf("firewood: error proposing from parent proposal %s: %v", parentRoot.Hex(), err) + return common.Hash{}, fmt.Errorf("firewood: error proposing from parent proposal %s: %w", parentRoot.Hex(), err) } } ffiHashCount.Inc(1) diff --git a/utils/metered_cache.go b/utils/metered_cache.go index d3e9b791eb..f754f87ff8 100644 --- a/utils/metered_cache.go +++ b/utils/metered_cache.go @@ -4,7 +4,6 @@ package utils import ( - "fmt" "sync/atomic" "time" @@ -45,13 +44,13 @@ func NewMeteredCache(size int, namespace string, updateFrequency uint64) *Metere } if namespace != "" { // only register stats if a namespace is provided. - mc.entriesCount = metrics.GetOrRegisterGauge(fmt.Sprintf("%s/entriesCount", namespace), nil) - mc.bytesSize = metrics.GetOrRegisterGauge(fmt.Sprintf("%s/bytesSize", namespace), nil) - mc.collisions = metrics.GetOrRegisterGauge(fmt.Sprintf("%s/collisions", namespace), nil) - mc.gets = metrics.GetOrRegisterGauge(fmt.Sprintf("%s/gets", namespace), nil) - mc.sets = metrics.GetOrRegisterGauge(fmt.Sprintf("%s/sets", namespace), nil) - mc.misses = metrics.GetOrRegisterGauge(fmt.Sprintf("%s/misses", namespace), nil) - mc.statsTime = metrics.GetOrRegisterGauge(fmt.Sprintf("%s/statsTime", namespace), nil) + mc.entriesCount = metrics.GetOrRegisterGauge(namespace+"/entriesCount", nil) + mc.bytesSize = metrics.GetOrRegisterGauge(namespace+"/bytesSize", nil) + mc.collisions = metrics.GetOrRegisterGauge(namespace+"/collisions", nil) + mc.gets = metrics.GetOrRegisterGauge(namespace+"/gets", nil) + mc.sets = metrics.GetOrRegisterGauge(namespace+"/sets", nil) + mc.misses = metrics.GetOrRegisterGauge(namespace+"/misses", nil) + mc.statsTime = metrics.GetOrRegisterGauge(namespace+"/statsTime", nil) } return mc } diff --git a/warp/backend.go b/warp/backend.go index 55fa8828e7..d410c9db57 100644 --- a/warp/backend.go +++ b/warp/backend.go @@ -14,9 +14,10 @@ import ( "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/network/p2p/acp118" "github.com/ava-labs/avalanchego/snow/consensus/snowman" - avalancheWarp "github.com/ava-labs/avalanchego/vms/platformvm/warp" "github.com/ava-labs/avalanchego/vms/platformvm/warp/payload" "github.com/ava-labs/libevm/log" + + avalancheWarp "github.com/ava-labs/avalanchego/vms/platformvm/warp" ) var ( diff --git a/warp/backend_test.go b/warp/backend_test.go index 47797a735a..43eb3a0ca3 100644 --- a/warp/backend_test.go +++ b/warp/backend_test.go @@ -13,10 +13,12 @@ import ( "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/utils" "github.com/ava-labs/avalanchego/utils/crypto/bls/signer/localsigner" - avalancheWarp "github.com/ava-labs/avalanchego/vms/platformvm/warp" "github.com/ava-labs/avalanchego/vms/platformvm/warp/payload" - "github.com/ava-labs/coreth/warp/warptest" "github.com/stretchr/testify/require" + + "github.com/ava-labs/coreth/warp/warptest" + + avalancheWarp "github.com/ava-labs/avalanchego/vms/platformvm/warp" ) var ( diff --git a/warp/client.go b/warp/client.go index 08ae65bc89..f034dd526f 100644 --- a/warp/client.go +++ b/warp/client.go @@ -8,8 +8,9 @@ import ( "fmt" "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/coreth/rpc" "github.com/ava-labs/libevm/common/hexutil" + + "github.com/ava-labs/coreth/rpc" ) var _ Client = (*client)(nil) diff --git a/warp/service.go b/warp/service.go index d477b61d70..31d5c0e724 100644 --- a/warp/service.go +++ b/warp/service.go @@ -13,10 +13,11 @@ import ( "github.com/ava-labs/avalanchego/snow" "github.com/ava-labs/avalanchego/vms/platformvm/warp" "github.com/ava-labs/avalanchego/vms/platformvm/warp/payload" - warpprecompile "github.com/ava-labs/coreth/precompile/contracts/warp" - warpValidators "github.com/ava-labs/coreth/warp/validators" "github.com/ava-labs/libevm/common/hexutil" "github.com/ava-labs/libevm/log" + + warpprecompile "github.com/ava-labs/coreth/precompile/contracts/warp" + warpValidators "github.com/ava-labs/coreth/warp/validators" ) var errNoValidators = errors.New("cannot aggregate signatures from subnet with no validators") diff --git a/warp/verifier_backend.go b/warp/verifier_backend.go index 48f4eadedc..8738d1639a 100644 --- a/warp/verifier_backend.go +++ b/warp/verifier_backend.go @@ -9,8 +9,9 @@ import ( "github.com/ava-labs/avalanchego/database" "github.com/ava-labs/avalanchego/snow/engine/common" - avalancheWarp "github.com/ava-labs/avalanchego/vms/platformvm/warp" "github.com/ava-labs/avalanchego/vms/platformvm/warp/payload" + + avalancheWarp "github.com/ava-labs/avalanchego/vms/platformvm/warp" ) const ( diff --git a/warp/verifier_backend_test.go b/warp/verifier_backend_test.go index 25d85da5f5..54033eed1e 100644 --- a/warp/verifier_backend_test.go +++ b/warp/verifier_backend_test.go @@ -17,11 +17,13 @@ import ( "github.com/ava-labs/avalanchego/snow/engine/common" "github.com/ava-labs/avalanchego/snow/snowtest" "github.com/ava-labs/avalanchego/vms/evm/metrics/metricstest" - avalancheWarp "github.com/ava-labs/avalanchego/vms/platformvm/warp" "github.com/ava-labs/avalanchego/vms/platformvm/warp/payload" - "github.com/ava-labs/coreth/warp/warptest" "github.com/stretchr/testify/require" "google.golang.org/protobuf/proto" + + "github.com/ava-labs/coreth/warp/warptest" + + avalancheWarp "github.com/ava-labs/avalanchego/vms/platformvm/warp" ) func TestAddressedCallSignatures(t *testing.T) { From 00b808b73cad997aa6b85b0087f250b96af5fcf8 Mon Sep 17 00:00:00 2001 From: Jonathan Oppenheimer Date: Tue, 19 Aug 2025 16:26:14 -0400 Subject: [PATCH 17/20] lint --- params/protocol_params.go | 194 +++++++++++++++++++++++++++++ plugin/evm/prestate_tracer_test.go | 2 - 2 files changed, 194 insertions(+), 2 deletions(-) create mode 100644 params/protocol_params.go diff --git a/params/protocol_params.go b/params/protocol_params.go new file mode 100644 index 0000000000..92d5a3cce4 --- /dev/null +++ b/params/protocol_params.go @@ -0,0 +1,194 @@ +// Copyright (C) 2019-2025, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. +// +// This file is a derived work, based on the go-ethereum library whose original +// notices appear below. +// +// It is distributed under a license compatible with the licensing terms of the +// original code from which it is derived. +// +// Much love to the original authors for their work. +// ********** +// Copyright 2015 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 params + +import ( + "math/big" + + "github.com/ava-labs/libevm/common" +) + +const ( + GenesisGasLimit uint64 = 4712388 // Gas limit of the Genesis block. + + ExpByteGas uint64 = 10 // Times ceil(log256(exponent)) for the EXP instruction. + SloadGas uint64 = 50 // Multiplied by the number of 32-byte words that are copied (round up) for any *COPY operation and added. + CallValueTransferGas uint64 = 9000 // Paid for CALL when the value transfer is non-zero. + CallNewAccountGas uint64 = 25000 // Paid for CALL when the destination address didn't exist prior. + TxGas uint64 = 21000 // Per transaction not creating a contract. NOTE: Not payable on data of calls between transactions. + TxGasContractCreation uint64 = 53000 // Per transaction that creates a contract. NOTE: Not payable on data of calls between transactions. + TxDataZeroGas uint64 = 4 // Per byte of data attached to a transaction that equals zero. NOTE: Not payable on data of calls between transactions. + QuadCoeffDiv uint64 = 512 // Divisor for the quadratic particle of the memory cost equation. + LogDataGas uint64 = 8 // Per byte in a LOG* operation's data. + CallStipend uint64 = 2300 // Free gas given at beginning of call. + + Keccak256Gas uint64 = 30 // Once per KECCAK256 operation. + Keccak256WordGas uint64 = 6 // Once per word of the KECCAK256 operation's data. + InitCodeWordGas uint64 = 2 // Once per word of the init code when creating a contract. + + SstoreSetGas uint64 = 20000 // Once per SSTORE operation. + SstoreResetGas uint64 = 5000 // Once per SSTORE operation if the zeroness changes from zero. + SstoreClearGas uint64 = 5000 // Once per SSTORE operation if the zeroness doesn't change. + SstoreRefundGas uint64 = 15000 // Once per SSTORE operation if the zeroness changes to zero. + + NetSstoreNoopGas uint64 = 200 // Once per SSTORE operation if the value doesn't change. + NetSstoreInitGas uint64 = 20000 // Once per SSTORE operation from clean zero. + NetSstoreCleanGas uint64 = 5000 // Once per SSTORE operation from clean non-zero. + NetSstoreDirtyGas uint64 = 200 // Once per SSTORE operation from dirty. + + NetSstoreClearRefund uint64 = 15000 // Once per SSTORE operation for clearing an originally existing storage slot + NetSstoreResetRefund uint64 = 4800 // Once per SSTORE operation for resetting to the original non-zero value + NetSstoreResetClearRefund uint64 = 19800 // Once per SSTORE operation for resetting to the original zero value + + SstoreSentryGasEIP2200 uint64 = 2300 // Minimum gas required to be present for an SSTORE call, not consumed + SstoreSetGasEIP2200 uint64 = 20000 // Once per SSTORE operation from clean zero to non-zero + SstoreResetGasEIP2200 uint64 = 5000 // Once per SSTORE operation from clean non-zero to something else + SstoreClearsScheduleRefundEIP2200 uint64 = 15000 // Once per SSTORE operation for clearing an originally existing storage slot + + ColdAccountAccessCostEIP2929 = uint64(2600) // COLD_ACCOUNT_ACCESS_COST + ColdSloadCostEIP2929 = uint64(2100) // COLD_SLOAD_COST + WarmStorageReadCostEIP2929 = uint64(100) // WARM_STORAGE_READ_COST + + // In EIP-2200: SstoreResetGas was 5000. + // In EIP-2929: SstoreResetGas was changed to '5000 - COLD_SLOAD_COST'. + // In EIP-3529: SSTORE_CLEARS_SCHEDULE is defined as SSTORE_RESET_GAS + ACCESS_LIST_STORAGE_KEY_COST + // Which becomes: 5000 - 2100 + 1900 = 4800 + SstoreClearsScheduleRefundEIP3529 uint64 = SstoreResetGasEIP2200 - ColdSloadCostEIP2929 + TxAccessListStorageKeyGas + + JumpdestGas uint64 = 1 // Once per JUMPDEST operation. + EpochDuration uint64 = 30000 // Duration between proof-of-work epochs. + + CreateDataGas uint64 = 200 // + CallCreateDepth uint64 = 1024 // Maximum depth of call/create stack. + ExpGas uint64 = 10 // Once per EXP instruction + LogGas uint64 = 375 // Per LOG* operation. + CopyGas uint64 = 3 // + StackLimit uint64 = 1024 // Maximum size of VM stack allowed. + TierStepGas uint64 = 0 // Once per operation, for a selection of them. + LogTopicGas uint64 = 375 // Multiplied by the * of the LOG*, per LOG transaction. e.g. LOG0 incurs 0 * c_txLogTopicGas, LOG4 incurs 4 * c_txLogTopicGas. + CreateGas uint64 = 32000 // Once per CREATE operation & contract-creation transaction. + Create2Gas uint64 = 32000 // Once per CREATE2 operation + SelfdestructRefundGas uint64 = 24000 // Refunded following a selfdestruct operation. + MemoryGas uint64 = 3 // Times the address of the (highest referenced byte in memory + 1). NOTE: referencing happens on read, write and in instructions such as RETURN and CALL. + + TxDataNonZeroGasFrontier uint64 = 68 // Per byte of data attached to a transaction that is not equal to zero. NOTE: Not payable on data of calls between transactions. + TxDataNonZeroGasEIP2028 uint64 = 16 // Per byte of non zero data attached to a transaction after EIP 2028 (part in Istanbul) + TxAccessListAddressGas uint64 = 2400 // Per address specified in EIP 2930 access list + TxAccessListStorageKeyGas uint64 = 1900 // Per storage key specified in EIP 2930 access list + + // These have been changed during the course of the chain + CallGasFrontier uint64 = 40 // Once per CALL operation & message call transaction. + CallGasEIP150 uint64 = 700 // Static portion of gas for CALL-derivates after EIP 150 (Tangerine) + BalanceGasFrontier uint64 = 20 // The cost of a BALANCE operation + BalanceGasEIP150 uint64 = 400 // The cost of a BALANCE operation after Tangerine + BalanceGasEIP1884 uint64 = 700 // The cost of a BALANCE operation after EIP 1884 (part of Istanbul) + ExtcodeSizeGasFrontier uint64 = 20 // Cost of EXTCODESIZE before EIP 150 (Tangerine) + ExtcodeSizeGasEIP150 uint64 = 700 // Cost of EXTCODESIZE after EIP 150 (Tangerine) + SloadGasFrontier uint64 = 50 + SloadGasEIP150 uint64 = 200 + SloadGasEIP1884 uint64 = 800 // Cost of SLOAD after EIP 1884 (part of Istanbul) + SloadGasEIP2200 uint64 = 800 // Cost of SLOAD after EIP 2200 (part of Istanbul) + ExtcodeHashGasConstantinople uint64 = 400 // Cost of EXTCODEHASH (introduced in Constantinople) + ExtcodeHashGasEIP1884 uint64 = 700 // Cost of EXTCODEHASH after EIP 1884 (part in Istanbul) + SelfdestructGasEIP150 uint64 = 5000 // Cost of SELFDESTRUCT post EIP 150 (Tangerine) + + // EXP has a dynamic portion depending on the size of the exponent + ExpByteFrontier uint64 = 10 // was set to 10 in Frontier + ExpByteEIP158 uint64 = 50 // was raised to 50 during Eip158 (Spurious Dragon) + + // Extcodecopy has a dynamic AND a static cost. This represents only the + // static portion of the gas. It was changed during EIP 150 (Tangerine) + ExtcodeCopyBaseFrontier uint64 = 20 + ExtcodeCopyBaseEIP150 uint64 = 700 + + // CreateBySelfdestructGas is used when the refunded account is one that does + // not exist. This logic is similar to call. + // Introduced in Tangerine Whistle (Eip 150) + CreateBySelfdestructGas uint64 = 25000 + + MaxCodeSize = 24576 // Maximum bytecode to permit for a contract + MaxInitCodeSize = 2 * MaxCodeSize // Maximum initcode to permit in a creation transaction and create instructions + + // Precompiled contract gas prices + + EcrecoverGas uint64 = 3000 // Elliptic curve sender recovery gas price + Sha256BaseGas uint64 = 60 // Base price for a SHA256 operation + Sha256PerWordGas uint64 = 12 // Per-word price for a SHA256 operation + Ripemd160BaseGas uint64 = 600 // Base price for a RIPEMD160 operation + Ripemd160PerWordGas uint64 = 120 // Per-word price for a RIPEMD160 operation + IdentityBaseGas uint64 = 15 // Base price for a data copy operation + IdentityPerWordGas uint64 = 3 // Per-work price for a data copy operation + + Bn256AddGasByzantium uint64 = 500 // Byzantium gas needed for an elliptic curve addition + Bn256AddGasIstanbul uint64 = 150 // Gas needed for an elliptic curve addition + Bn256ScalarMulGasByzantium uint64 = 40000 // Byzantium gas needed for an elliptic curve scalar multiplication + Bn256ScalarMulGasIstanbul uint64 = 6000 // Gas needed for an elliptic curve scalar multiplication + Bn256PairingBaseGasByzantium uint64 = 100000 // Byzantium base price for an elliptic curve pairing check + Bn256PairingBaseGasIstanbul uint64 = 45000 // Base price for an elliptic curve pairing check + Bn256PairingPerPointGasByzantium uint64 = 80000 // Byzantium per-point price for an elliptic curve pairing check + Bn256PairingPerPointGasIstanbul uint64 = 34000 // Per-point price for an elliptic curve pairing check + + Bls12381G1AddGas uint64 = 600 // Price for BLS12-381 elliptic curve G1 point addition + Bls12381G1MulGas uint64 = 12000 // Price for BLS12-381 elliptic curve G1 point scalar multiplication + Bls12381G2AddGas uint64 = 4500 // Price for BLS12-381 elliptic curve G2 point addition + Bls12381G2MulGas uint64 = 55000 // Price for BLS12-381 elliptic curve G2 point scalar multiplication + Bls12381PairingBaseGas uint64 = 115000 // Base gas price for BLS12-381 elliptic curve pairing check + Bls12381PairingPerPairGas uint64 = 23000 // Per-point pair gas price for BLS12-381 elliptic curve pairing check + Bls12381MapG1Gas uint64 = 5500 // Gas price for BLS12-381 mapping field element to G1 operation + Bls12381MapG2Gas uint64 = 110000 // Gas price for BLS12-381 mapping field element to G2 operation + + // The Refund Quotient is the cap on how much of the used gas can be refunded. Before EIP-3529, + // up to half the consumed gas could be refunded. Redefined as 1/5th in EIP-3529 + RefundQuotient uint64 = 2 + RefundQuotientEIP3529 uint64 = 5 + + BlobTxBytesPerFieldElement = 32 // Size in bytes of a field element + BlobTxFieldElementsPerBlob = 4096 // Number of field elements stored in a single data blob + BlobTxBlobGasPerBlob = 1 << 17 // Gas consumption of a single data blob (== blob byte size) + BlobTxMinBlobGasprice = 1 // Minimum gas price for data blobs + BlobTxBlobGaspriceUpdateFraction = 3338477 // Controls the maximum rate of change for blob gas price + BlobTxPointEvaluationPrecompileGas = 50000 // Gas price for the point evaluation precompile. + + BlobTxTargetBlobGasPerBlock = 3 * BlobTxBlobGasPerBlob // Target consumable blob gas for data blobs per block (for 1559-like pricing) + MaxBlobGasPerBlock = 6 * BlobTxBlobGasPerBlob // Maximum consumable blob gas for data blobs per block +) + +// Gas discount table for BLS12-381 G1 and G2 multi exponentiation operations +var Bls12381MultiExpDiscountTable = [128]uint64{1200, 888, 764, 641, 594, 547, 500, 453, 438, 423, 408, 394, 379, 364, 349, 334, 330, 326, 322, 318, 314, 310, 306, 302, 298, 294, 289, 285, 281, 277, 273, 269, 268, 266, 265, 263, 262, 260, 259, 257, 256, 254, 253, 251, 250, 248, 247, 245, 244, 242, 241, 239, 238, 236, 235, 233, 232, 231, 229, 228, 226, 225, 223, 222, 221, 220, 219, 219, 218, 217, 216, 216, 215, 214, 213, 213, 212, 211, 211, 210, 209, 208, 208, 207, 206, 205, 205, 204, 203, 202, 202, 201, 200, 199, 199, 198, 197, 196, 196, 195, 194, 193, 193, 192, 191, 191, 190, 189, 188, 188, 187, 186, 185, 185, 184, 183, 182, 182, 181, 180, 179, 179, 178, 177, 176, 176, 175, 174} + +var ( + DifficultyBoundDivisor = big.NewInt(2048) // The bound divisor of the difficulty, used in the update calculations. + GenesisDifficulty = big.NewInt(131072) // Difficulty of the Genesis block. + MinimumDifficulty = big.NewInt(131072) // The minimum that the difficulty may ever be. + DurationLimit = big.NewInt(13) // The decision boundary on the blocktime duration used to determine whether difficulty should go up or not. + + // BeaconRootsStorageAddress is the address where historical beacon roots are stored as per EIP-4788 + BeaconRootsStorageAddress = common.HexToAddress("0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02") + // SystemAddress is where the system-transaction is sent from as per EIP-4788 + SystemAddress common.Address = common.HexToAddress("0xfffffffffffffffffffffffffffffffffffffffe") +) diff --git a/plugin/evm/prestate_tracer_test.go b/plugin/evm/prestate_tracer_test.go index 6e787298e4..c985d00a2b 100644 --- a/plugin/evm/prestate_tracer_test.go +++ b/plugin/evm/prestate_tracer_test.go @@ -21,8 +21,6 @@ import ( "github.com/ava-labs/coreth/core" "github.com/ava-labs/coreth/eth/tracers" "github.com/ava-labs/coreth/tests" - - ethtypes "github.com/ava-labs/libevm/core/types" ) func TestPrestateWithDiffModeANTTracer(t *testing.T) { From d5a3fa5e8a13fec5dd9354ee4b27746895b05434 Mon Sep 17 00:00:00 2001 From: Jonathan Oppenheimer Date: Tue, 19 Aug 2025 16:27:49 -0400 Subject: [PATCH 18/20] getting it right --- params/protocol_params.go | 194 -------------------------- plugin/evm/atomic/vm/syncervm_test.go | 5 +- plugin/evm/syncervm_test.go | 5 +- 3 files changed, 6 insertions(+), 198 deletions(-) delete mode 100644 params/protocol_params.go diff --git a/params/protocol_params.go b/params/protocol_params.go deleted file mode 100644 index 92d5a3cce4..0000000000 --- a/params/protocol_params.go +++ /dev/null @@ -1,194 +0,0 @@ -// Copyright (C) 2019-2025, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. -// -// This file is a derived work, based on the go-ethereum library whose original -// notices appear below. -// -// It is distributed under a license compatible with the licensing terms of the -// original code from which it is derived. -// -// Much love to the original authors for their work. -// ********** -// Copyright 2015 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 params - -import ( - "math/big" - - "github.com/ava-labs/libevm/common" -) - -const ( - GenesisGasLimit uint64 = 4712388 // Gas limit of the Genesis block. - - ExpByteGas uint64 = 10 // Times ceil(log256(exponent)) for the EXP instruction. - SloadGas uint64 = 50 // Multiplied by the number of 32-byte words that are copied (round up) for any *COPY operation and added. - CallValueTransferGas uint64 = 9000 // Paid for CALL when the value transfer is non-zero. - CallNewAccountGas uint64 = 25000 // Paid for CALL when the destination address didn't exist prior. - TxGas uint64 = 21000 // Per transaction not creating a contract. NOTE: Not payable on data of calls between transactions. - TxGasContractCreation uint64 = 53000 // Per transaction that creates a contract. NOTE: Not payable on data of calls between transactions. - TxDataZeroGas uint64 = 4 // Per byte of data attached to a transaction that equals zero. NOTE: Not payable on data of calls between transactions. - QuadCoeffDiv uint64 = 512 // Divisor for the quadratic particle of the memory cost equation. - LogDataGas uint64 = 8 // Per byte in a LOG* operation's data. - CallStipend uint64 = 2300 // Free gas given at beginning of call. - - Keccak256Gas uint64 = 30 // Once per KECCAK256 operation. - Keccak256WordGas uint64 = 6 // Once per word of the KECCAK256 operation's data. - InitCodeWordGas uint64 = 2 // Once per word of the init code when creating a contract. - - SstoreSetGas uint64 = 20000 // Once per SSTORE operation. - SstoreResetGas uint64 = 5000 // Once per SSTORE operation if the zeroness changes from zero. - SstoreClearGas uint64 = 5000 // Once per SSTORE operation if the zeroness doesn't change. - SstoreRefundGas uint64 = 15000 // Once per SSTORE operation if the zeroness changes to zero. - - NetSstoreNoopGas uint64 = 200 // Once per SSTORE operation if the value doesn't change. - NetSstoreInitGas uint64 = 20000 // Once per SSTORE operation from clean zero. - NetSstoreCleanGas uint64 = 5000 // Once per SSTORE operation from clean non-zero. - NetSstoreDirtyGas uint64 = 200 // Once per SSTORE operation from dirty. - - NetSstoreClearRefund uint64 = 15000 // Once per SSTORE operation for clearing an originally existing storage slot - NetSstoreResetRefund uint64 = 4800 // Once per SSTORE operation for resetting to the original non-zero value - NetSstoreResetClearRefund uint64 = 19800 // Once per SSTORE operation for resetting to the original zero value - - SstoreSentryGasEIP2200 uint64 = 2300 // Minimum gas required to be present for an SSTORE call, not consumed - SstoreSetGasEIP2200 uint64 = 20000 // Once per SSTORE operation from clean zero to non-zero - SstoreResetGasEIP2200 uint64 = 5000 // Once per SSTORE operation from clean non-zero to something else - SstoreClearsScheduleRefundEIP2200 uint64 = 15000 // Once per SSTORE operation for clearing an originally existing storage slot - - ColdAccountAccessCostEIP2929 = uint64(2600) // COLD_ACCOUNT_ACCESS_COST - ColdSloadCostEIP2929 = uint64(2100) // COLD_SLOAD_COST - WarmStorageReadCostEIP2929 = uint64(100) // WARM_STORAGE_READ_COST - - // In EIP-2200: SstoreResetGas was 5000. - // In EIP-2929: SstoreResetGas was changed to '5000 - COLD_SLOAD_COST'. - // In EIP-3529: SSTORE_CLEARS_SCHEDULE is defined as SSTORE_RESET_GAS + ACCESS_LIST_STORAGE_KEY_COST - // Which becomes: 5000 - 2100 + 1900 = 4800 - SstoreClearsScheduleRefundEIP3529 uint64 = SstoreResetGasEIP2200 - ColdSloadCostEIP2929 + TxAccessListStorageKeyGas - - JumpdestGas uint64 = 1 // Once per JUMPDEST operation. - EpochDuration uint64 = 30000 // Duration between proof-of-work epochs. - - CreateDataGas uint64 = 200 // - CallCreateDepth uint64 = 1024 // Maximum depth of call/create stack. - ExpGas uint64 = 10 // Once per EXP instruction - LogGas uint64 = 375 // Per LOG* operation. - CopyGas uint64 = 3 // - StackLimit uint64 = 1024 // Maximum size of VM stack allowed. - TierStepGas uint64 = 0 // Once per operation, for a selection of them. - LogTopicGas uint64 = 375 // Multiplied by the * of the LOG*, per LOG transaction. e.g. LOG0 incurs 0 * c_txLogTopicGas, LOG4 incurs 4 * c_txLogTopicGas. - CreateGas uint64 = 32000 // Once per CREATE operation & contract-creation transaction. - Create2Gas uint64 = 32000 // Once per CREATE2 operation - SelfdestructRefundGas uint64 = 24000 // Refunded following a selfdestruct operation. - MemoryGas uint64 = 3 // Times the address of the (highest referenced byte in memory + 1). NOTE: referencing happens on read, write and in instructions such as RETURN and CALL. - - TxDataNonZeroGasFrontier uint64 = 68 // Per byte of data attached to a transaction that is not equal to zero. NOTE: Not payable on data of calls between transactions. - TxDataNonZeroGasEIP2028 uint64 = 16 // Per byte of non zero data attached to a transaction after EIP 2028 (part in Istanbul) - TxAccessListAddressGas uint64 = 2400 // Per address specified in EIP 2930 access list - TxAccessListStorageKeyGas uint64 = 1900 // Per storage key specified in EIP 2930 access list - - // These have been changed during the course of the chain - CallGasFrontier uint64 = 40 // Once per CALL operation & message call transaction. - CallGasEIP150 uint64 = 700 // Static portion of gas for CALL-derivates after EIP 150 (Tangerine) - BalanceGasFrontier uint64 = 20 // The cost of a BALANCE operation - BalanceGasEIP150 uint64 = 400 // The cost of a BALANCE operation after Tangerine - BalanceGasEIP1884 uint64 = 700 // The cost of a BALANCE operation after EIP 1884 (part of Istanbul) - ExtcodeSizeGasFrontier uint64 = 20 // Cost of EXTCODESIZE before EIP 150 (Tangerine) - ExtcodeSizeGasEIP150 uint64 = 700 // Cost of EXTCODESIZE after EIP 150 (Tangerine) - SloadGasFrontier uint64 = 50 - SloadGasEIP150 uint64 = 200 - SloadGasEIP1884 uint64 = 800 // Cost of SLOAD after EIP 1884 (part of Istanbul) - SloadGasEIP2200 uint64 = 800 // Cost of SLOAD after EIP 2200 (part of Istanbul) - ExtcodeHashGasConstantinople uint64 = 400 // Cost of EXTCODEHASH (introduced in Constantinople) - ExtcodeHashGasEIP1884 uint64 = 700 // Cost of EXTCODEHASH after EIP 1884 (part in Istanbul) - SelfdestructGasEIP150 uint64 = 5000 // Cost of SELFDESTRUCT post EIP 150 (Tangerine) - - // EXP has a dynamic portion depending on the size of the exponent - ExpByteFrontier uint64 = 10 // was set to 10 in Frontier - ExpByteEIP158 uint64 = 50 // was raised to 50 during Eip158 (Spurious Dragon) - - // Extcodecopy has a dynamic AND a static cost. This represents only the - // static portion of the gas. It was changed during EIP 150 (Tangerine) - ExtcodeCopyBaseFrontier uint64 = 20 - ExtcodeCopyBaseEIP150 uint64 = 700 - - // CreateBySelfdestructGas is used when the refunded account is one that does - // not exist. This logic is similar to call. - // Introduced in Tangerine Whistle (Eip 150) - CreateBySelfdestructGas uint64 = 25000 - - MaxCodeSize = 24576 // Maximum bytecode to permit for a contract - MaxInitCodeSize = 2 * MaxCodeSize // Maximum initcode to permit in a creation transaction and create instructions - - // Precompiled contract gas prices - - EcrecoverGas uint64 = 3000 // Elliptic curve sender recovery gas price - Sha256BaseGas uint64 = 60 // Base price for a SHA256 operation - Sha256PerWordGas uint64 = 12 // Per-word price for a SHA256 operation - Ripemd160BaseGas uint64 = 600 // Base price for a RIPEMD160 operation - Ripemd160PerWordGas uint64 = 120 // Per-word price for a RIPEMD160 operation - IdentityBaseGas uint64 = 15 // Base price for a data copy operation - IdentityPerWordGas uint64 = 3 // Per-work price for a data copy operation - - Bn256AddGasByzantium uint64 = 500 // Byzantium gas needed for an elliptic curve addition - Bn256AddGasIstanbul uint64 = 150 // Gas needed for an elliptic curve addition - Bn256ScalarMulGasByzantium uint64 = 40000 // Byzantium gas needed for an elliptic curve scalar multiplication - Bn256ScalarMulGasIstanbul uint64 = 6000 // Gas needed for an elliptic curve scalar multiplication - Bn256PairingBaseGasByzantium uint64 = 100000 // Byzantium base price for an elliptic curve pairing check - Bn256PairingBaseGasIstanbul uint64 = 45000 // Base price for an elliptic curve pairing check - Bn256PairingPerPointGasByzantium uint64 = 80000 // Byzantium per-point price for an elliptic curve pairing check - Bn256PairingPerPointGasIstanbul uint64 = 34000 // Per-point price for an elliptic curve pairing check - - Bls12381G1AddGas uint64 = 600 // Price for BLS12-381 elliptic curve G1 point addition - Bls12381G1MulGas uint64 = 12000 // Price for BLS12-381 elliptic curve G1 point scalar multiplication - Bls12381G2AddGas uint64 = 4500 // Price for BLS12-381 elliptic curve G2 point addition - Bls12381G2MulGas uint64 = 55000 // Price for BLS12-381 elliptic curve G2 point scalar multiplication - Bls12381PairingBaseGas uint64 = 115000 // Base gas price for BLS12-381 elliptic curve pairing check - Bls12381PairingPerPairGas uint64 = 23000 // Per-point pair gas price for BLS12-381 elliptic curve pairing check - Bls12381MapG1Gas uint64 = 5500 // Gas price for BLS12-381 mapping field element to G1 operation - Bls12381MapG2Gas uint64 = 110000 // Gas price for BLS12-381 mapping field element to G2 operation - - // The Refund Quotient is the cap on how much of the used gas can be refunded. Before EIP-3529, - // up to half the consumed gas could be refunded. Redefined as 1/5th in EIP-3529 - RefundQuotient uint64 = 2 - RefundQuotientEIP3529 uint64 = 5 - - BlobTxBytesPerFieldElement = 32 // Size in bytes of a field element - BlobTxFieldElementsPerBlob = 4096 // Number of field elements stored in a single data blob - BlobTxBlobGasPerBlob = 1 << 17 // Gas consumption of a single data blob (== blob byte size) - BlobTxMinBlobGasprice = 1 // Minimum gas price for data blobs - BlobTxBlobGaspriceUpdateFraction = 3338477 // Controls the maximum rate of change for blob gas price - BlobTxPointEvaluationPrecompileGas = 50000 // Gas price for the point evaluation precompile. - - BlobTxTargetBlobGasPerBlock = 3 * BlobTxBlobGasPerBlob // Target consumable blob gas for data blobs per block (for 1559-like pricing) - MaxBlobGasPerBlock = 6 * BlobTxBlobGasPerBlob // Maximum consumable blob gas for data blobs per block -) - -// Gas discount table for BLS12-381 G1 and G2 multi exponentiation operations -var Bls12381MultiExpDiscountTable = [128]uint64{1200, 888, 764, 641, 594, 547, 500, 453, 438, 423, 408, 394, 379, 364, 349, 334, 330, 326, 322, 318, 314, 310, 306, 302, 298, 294, 289, 285, 281, 277, 273, 269, 268, 266, 265, 263, 262, 260, 259, 257, 256, 254, 253, 251, 250, 248, 247, 245, 244, 242, 241, 239, 238, 236, 235, 233, 232, 231, 229, 228, 226, 225, 223, 222, 221, 220, 219, 219, 218, 217, 216, 216, 215, 214, 213, 213, 212, 211, 211, 210, 209, 208, 208, 207, 206, 205, 205, 204, 203, 202, 202, 201, 200, 199, 199, 198, 197, 196, 196, 195, 194, 193, 193, 192, 191, 191, 190, 189, 188, 188, 187, 186, 185, 185, 184, 183, 182, 182, 181, 180, 179, 179, 178, 177, 176, 176, 175, 174} - -var ( - DifficultyBoundDivisor = big.NewInt(2048) // The bound divisor of the difficulty, used in the update calculations. - GenesisDifficulty = big.NewInt(131072) // Difficulty of the Genesis block. - MinimumDifficulty = big.NewInt(131072) // The minimum that the difficulty may ever be. - DurationLimit = big.NewInt(13) // The decision boundary on the blocktime duration used to determine whether difficulty should go up or not. - - // BeaconRootsStorageAddress is the address where historical beacon roots are stored as per EIP-4788 - BeaconRootsStorageAddress = common.HexToAddress("0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02") - // SystemAddress is where the system-transaction is sent from as per EIP-4788 - SystemAddress common.Address = common.HexToAddress("0xfffffffffffffffffffffffffffffffffffffffe") -) diff --git a/plugin/evm/atomic/vm/syncervm_test.go b/plugin/evm/atomic/vm/syncervm_test.go index 5f8bf38cdf..1e91e94d1b 100644 --- a/plugin/evm/atomic/vm/syncervm_test.go +++ b/plugin/evm/atomic/vm/syncervm_test.go @@ -16,12 +16,13 @@ import ( "github.com/ava-labs/coreth/consensus/dummy" "github.com/ava-labs/coreth/core" "github.com/ava-labs/coreth/core/extstate" - "github.com/ava-labs/coreth/params" "github.com/ava-labs/coreth/plugin/evm/atomic" "github.com/ava-labs/coreth/plugin/evm/atomic/atomictest" "github.com/ava-labs/coreth/plugin/evm/extension" "github.com/ava-labs/coreth/plugin/evm/vmtest" "github.com/ava-labs/coreth/predicate" + + ethparams "github.com/ava-labs/libevm/params" ) func TestAtomicSyncerVM(t *testing.T) { @@ -64,7 +65,7 @@ func TestAtomicSyncerVM(t *testing.T) { includedAtomicTxs = append(includedAtomicTxs, exportTx) default: // Generate simple transfer transactions. pk := vmtest.TestKeys[0].ToECDSA() - tx := types.NewTransaction(gen.TxNonce(vmtest.TestEthAddrs[0]), vmtest.TestEthAddrs[1], common.Big1, params.TxGas, vmtest.InitialBaseFee, nil) + tx := types.NewTransaction(gen.TxNonce(vmtest.TestEthAddrs[0]), vmtest.TestEthAddrs[1], common.Big1, ethparams.TxGas, vmtest.InitialBaseFee, nil) signedTx, err := types.SignTx(tx, types.NewEIP155Signer(vm.Ethereum().BlockChain().Config().ChainID), pk) require.NoError(t, err) gen.AddTx(signedTx) diff --git a/plugin/evm/syncervm_test.go b/plugin/evm/syncervm_test.go index bb5b088c73..78e370c313 100644 --- a/plugin/evm/syncervm_test.go +++ b/plugin/evm/syncervm_test.go @@ -12,10 +12,11 @@ import ( "github.com/ava-labs/coreth/consensus/dummy" "github.com/ava-labs/coreth/core" - "github.com/ava-labs/coreth/params" "github.com/ava-labs/coreth/plugin/evm/extension" "github.com/ava-labs/coreth/plugin/evm/vmtest" "github.com/ava-labs/coreth/predicate" + + ethparams "github.com/ava-labs/libevm/params" ) func TestEVMSyncerVM(t *testing.T) { @@ -26,7 +27,7 @@ func TestEVMSyncerVM(t *testing.T) { require.NoError(t, err) gen.AppendExtra(b) - tx := types.NewTransaction(gen.TxNonce(vmtest.TestEthAddrs[0]), vmtest.TestEthAddrs[1], common.Big1, params.TxGas, vmtest.InitialBaseFee, nil) + tx := types.NewTransaction(gen.TxNonce(vmtest.TestEthAddrs[0]), vmtest.TestEthAddrs[1], common.Big1, ethparams.TxGas, vmtest.InitialBaseFee, nil) signedTx, err := types.SignTx(tx, types.NewEIP155Signer(vm.Ethereum().BlockChain().Config().ChainID), vmtest.TestKeys[0].ToECDSA()) require.NoError(t, err) gen.AddTx(signedTx) From dc8cbd7632015c1a560c19482d1848e49dbecaa6 Mon Sep 17 00:00:00 2001 From: Jonathan Oppenheimer Date: Tue, 19 Aug 2025 16:28:43 -0400 Subject: [PATCH 19/20] lint --- core/blockchain_ext_test.go | 3 ++- nativeasset/contract_test.go | 2 +- params/protocol_params_test.go | 3 ++- sync/blocksync/syncer_test.go | 2 +- sync/client/client_test.go | 2 +- sync/handlers/code_request_test.go | 1 + 6 files changed, 8 insertions(+), 5 deletions(-) diff --git a/core/blockchain_ext_test.go b/core/blockchain_ext_test.go index 8c20c97661..cf3434211e 100644 --- a/core/blockchain_ext_test.go +++ b/core/blockchain_ext_test.go @@ -14,7 +14,6 @@ import ( "github.com/ava-labs/libevm/core/types" "github.com/ava-labs/libevm/crypto" "github.com/ava-labs/libevm/ethdb" - ethparams "github.com/ava-labs/libevm/params" "github.com/holiman/uint256" "github.com/stretchr/testify/require" @@ -22,6 +21,8 @@ import ( "github.com/ava-labs/coreth/core/extstate" "github.com/ava-labs/coreth/params" "github.com/ava-labs/coreth/plugin/evm/upgrade/ap4" + + ethparams "github.com/ava-labs/libevm/params" ) var ( diff --git a/nativeasset/contract_test.go b/nativeasset/contract_test.go index 101924c722..95cd169d1f 100644 --- a/nativeasset/contract_test.go +++ b/nativeasset/contract_test.go @@ -11,7 +11,6 @@ import ( "github.com/ava-labs/libevm/core/rawdb" "github.com/ava-labs/libevm/core/state" "github.com/ava-labs/libevm/core/vm" - ethparams "github.com/ava-labs/libevm/params" "github.com/holiman/uint256" "github.com/stretchr/testify/assert" @@ -23,6 +22,7 @@ import ( "github.com/ava-labs/coreth/params" ethtypes "github.com/ava-labs/libevm/core/types" + ethparams "github.com/ava-labs/libevm/params" . "github.com/ava-labs/coreth/nativeasset" ) diff --git a/params/protocol_params_test.go b/params/protocol_params_test.go index f02ce59cca..23b2fedd41 100644 --- a/params/protocol_params_test.go +++ b/params/protocol_params_test.go @@ -7,8 +7,9 @@ import ( "testing" "github.com/ava-labs/libevm/common" - ethparams "github.com/ava-labs/libevm/params" "github.com/stretchr/testify/assert" + + ethparams "github.com/ava-labs/libevm/params" ) // TestUpstreamParamsValues detects when a params value changes upstream to prevent a subtle change diff --git a/sync/blocksync/syncer_test.go b/sync/blocksync/syncer_test.go index 2211f14b65..e5b9a05480 100644 --- a/sync/blocksync/syncer_test.go +++ b/sync/blocksync/syncer_test.go @@ -14,7 +14,6 @@ import ( "github.com/ava-labs/libevm/core/types" "github.com/ava-labs/libevm/crypto" "github.com/ava-labs/libevm/ethdb" - ethparams "github.com/ava-labs/libevm/params" "github.com/stretchr/testify/require" "github.com/ava-labs/coreth/consensus/dummy" @@ -25,6 +24,7 @@ import ( syncclient "github.com/ava-labs/coreth/sync/client" handlerstats "github.com/ava-labs/coreth/sync/handlers/stats" + ethparams "github.com/ava-labs/libevm/params" ) func TestBlockSyncer_ParameterizedTests(t *testing.T) { diff --git a/sync/client/client_test.go b/sync/client/client_test.go index d067d25b3d..57a1dabc93 100644 --- a/sync/client/client_test.go +++ b/sync/client/client_test.go @@ -27,8 +27,8 @@ import ( "github.com/ava-labs/coreth/sync/statesync/statesynctest" clientstats "github.com/ava-labs/coreth/sync/client/stats" - ethparams "github.com/ava-labs/libevm/params" handlerstats "github.com/ava-labs/coreth/sync/handlers/stats" + ethparams "github.com/ava-labs/libevm/params" ) func TestGetCode(t *testing.T) { diff --git a/sync/handlers/code_request_test.go b/sync/handlers/code_request_test.go index 2afb20584f..97b49aae1b 100644 --- a/sync/handlers/code_request_test.go +++ b/sync/handlers/code_request_test.go @@ -14,6 +14,7 @@ import ( "github.com/ava-labs/libevm/crypto" "github.com/ava-labs/libevm/ethdb/memorydb" "github.com/stretchr/testify/assert" + "github.com/ava-labs/coreth/plugin/evm/message" "github.com/ava-labs/coreth/sync/handlers/stats/statstest" From ebd48580fa0c463ed50a8b46f7b92d7eadf14b75 Mon Sep 17 00:00:00 2001 From: Jonathan Oppenheimer Date: Mon, 25 Aug 2025 14:34:31 -0400 Subject: [PATCH 20/20] lint --- core/blockchain_ext_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/blockchain_ext_test.go b/core/blockchain_ext_test.go index e1af07656e..ac269e7b6c 100644 --- a/core/blockchain_ext_test.go +++ b/core/blockchain_ext_test.go @@ -271,7 +271,7 @@ func InsertChainAcceptSingleBlockTest(t *testing.T, create createFunc) { // This call generates a chain of 3 blocks. signer := types.HomesteadSigner{} - _, chain, _, err := GenerateChainWithGenesis(gspec, blockchain.engine, 3, 10, func(i int, gen *BlockGen) { + _, chain, _, err := GenerateChainWithGenesis(gspec, blockchain.engine, 3, 10, func(_ int, gen *BlockGen) { tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(10000), ethparams.TxGas, nil, nil), signer, key1) gen.AddTx(tx) }) @@ -1600,7 +1600,7 @@ func ReexecBlocksTest(t *testing.T, create ReexecTestFunc) { // This call generates a chain of 10 blocks. signer := types.HomesteadSigner{} - _, chain, _, err := GenerateChainWithGenesis(gspec, blockchain.engine, 10, 10, func(i int, gen *BlockGen) { + _, chain, _, err := GenerateChainWithGenesis(gspec, blockchain.engine, 10, 10, func(_ int, gen *BlockGen) { tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(10000), ethparams.TxGas, nil, nil), signer, key1) gen.AddTx(tx) }) @@ -1734,7 +1734,7 @@ func ReexecMaxBlocksTest(t *testing.T, create ReexecTestFunc) { numAcceptedBlocks := 2*newCommitInterval - 1 signer := types.HomesteadSigner{} - _, chain, _, err := GenerateChainWithGenesis(gspec, blockchain.engine, genNumBlocks, 10, func(i int, gen *BlockGen) { + _, chain, _, err := GenerateChainWithGenesis(gspec, blockchain.engine, genNumBlocks, 10, func(_ int, gen *BlockGen) { tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(10000), ethparams.TxGas, nil, nil), signer, key1) gen.AddTx(tx) })