From 8e9dbc0f25cdd7ed9c13f674ad38c47a018977d7 Mon Sep 17 00:00:00 2001 From: Darioush Jalali Date: Thu, 29 Aug 2024 09:21:56 -0700 Subject: [PATCH 1/9] Bump avago master (#1316) * bump avalanchego to master * go mod tidy --- go.mod | 2 +- go.sum | 4 +- precompile/contracts/warp/predicate_test.go | 11 ----- .../warp/signature_verification_test.go | 44 ++++++++++++------- scripts/versions.sh | 2 +- warp/validators/state_test.go | 3 +- 6 files changed, 33 insertions(+), 33 deletions(-) diff --git a/go.mod b/go.mod index f033cf4918..2e56608d7d 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.21.12 require ( github.com/VictoriaMetrics/fastcache v1.12.1 github.com/antithesishq/antithesis-sdk-go v0.3.8 - github.com/ava-labs/avalanchego v1.11.11-0.20240821175119-35c66e33f0dc + github.com/ava-labs/avalanchego v1.11.11-monitoring-url.0.20240827194054-0117ab96791c github.com/cespare/cp v0.1.0 github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233 github.com/davecgh/go-spew v1.1.1 diff --git a/go.sum b/go.sum index 17e8f63025..e4768cae01 100644 --- a/go.sum +++ b/go.sum @@ -58,8 +58,8 @@ github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax github.com/antithesishq/antithesis-sdk-go v0.3.8 h1:OvGoHxIcOXFJLyn9IJQ5DzByZ3YVAWNBc394ObzDRb8= github.com/antithesishq/antithesis-sdk-go v0.3.8/go.mod h1:IUpT2DPAKh6i/YhSbt6Gl3v2yvUZjmKncl7U91fup7E= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= -github.com/ava-labs/avalanchego v1.11.11-0.20240821175119-35c66e33f0dc h1:cUz1N+LJIeQAR0Z6zTBiuZ7s8GqIE5QQbRWs423VFRA= -github.com/ava-labs/avalanchego v1.11.11-0.20240821175119-35c66e33f0dc/go.mod h1:UkyrRDXK2E15Lq2abyae2Pt+JsWvgsg1pe0/AtoMyAM= +github.com/ava-labs/avalanchego v1.11.11-monitoring-url.0.20240827194054-0117ab96791c h1:WAbq2clkTkoxELkCePFe7sqwOvD32SZPzoWtciUmzQI= +github.com/ava-labs/avalanchego v1.11.11-monitoring-url.0.20240827194054-0117ab96791c/go.mod h1:UkyrRDXK2E15Lq2abyae2Pt+JsWvgsg1pe0/AtoMyAM= github.com/ava-labs/coreth v0.13.8-fixed-genesis-upgrade.0.20240813194342-7635a96aa180 h1:6aIHp7wbyGVYdhHVQUbG7BEcbCMEQ5SYopPPJyipyvk= github.com/ava-labs/coreth v0.13.8-fixed-genesis-upgrade.0.20240813194342-7635a96aa180/go.mod h1:/wNBVq7J7wlC2Kbov7kk6LV5xZvau7VF9zwTVOeyAjY= github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g= diff --git a/precompile/contracts/warp/predicate_test.go b/precompile/contracts/warp/predicate_test.go index 23b82644a5..1c7b08a7c0 100644 --- a/precompile/contracts/warp/predicate_test.go +++ b/precompile/contracts/warp/predicate_test.go @@ -25,7 +25,6 @@ import ( "github.com/ava-labs/subnet-evm/predicate" "github.com/ava-labs/subnet-evm/utils" "github.com/stretchr/testify/require" - "go.uber.org/mock/gomock" ) const pChainHeight uint64 = 1337 @@ -49,7 +48,6 @@ var ( numTestVdrs = 10_000 testVdrs []*testValidator vdrs map[ids.NodeID]*validators.GetValidatorOutput - tests []signatureTest predicateTests = make(map[string]testutils.PredicateTest) ) @@ -132,15 +130,6 @@ func newTestValidator() *testValidator { } } -type signatureTest struct { - name string - stateF func(*gomock.Controller) validators.State - quorumNum uint64 - quorumDen uint64 - msgF func(*require.Assertions) *avalancheWarp.Message - err error -} - // createWarpMessage constructs a signed warp message using the global variable [unsignedMsg] // and the first [numKeys] signatures from [blsSignatures] func createWarpMessage(numKeys int) *avalancheWarp.Message { diff --git a/precompile/contracts/warp/signature_verification_test.go b/precompile/contracts/warp/signature_verification_test.go index dadefeb4d6..5b54cd29b8 100644 --- a/precompile/contracts/warp/signature_verification_test.go +++ b/precompile/contracts/warp/signature_verification_test.go @@ -10,6 +10,7 @@ import ( "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/snow/validators" + "github.com/ava-labs/avalanchego/snow/validators/validatorsmock" "github.com/ava-labs/avalanchego/utils/crypto/bls" "github.com/ava-labs/avalanchego/utils/set" avalancheWarp "github.com/ava-labs/avalanchego/vms/platformvm/warp" @@ -17,14 +18,23 @@ import ( "go.uber.org/mock/gomock" ) -// This test copies the test coverage from https://github.com/ava-labs/avalanchego/blob/v1.10.0/vms/platformvm/warp/signature_test.go#L137. +type signatureTest struct { + name string + stateF func(*gomock.Controller) validators.State + quorumNum uint64 + quorumDen uint64 + msgF func(*require.Assertions) *avalancheWarp.Message + err error +} + +// This test copies the test coverage from https://github.com/ava-labs/avalanchego/blob/0117ab96/vms/platformvm/warp/signature_test.go#L137. // These tests are only expected to fail if there is a breaking change in AvalancheGo that unexpectedly changes behavior. func TestSignatureVerification(t *testing.T) { - tests = []signatureTest{ + tests := []signatureTest{ { name: "can't get subnetID", stateF: func(ctrl *gomock.Controller) validators.State { - state := validators.NewMockState(ctrl) + state := validatorsmock.NewState(ctrl) state.EXPECT().GetSubnetID(gomock.Any(), sourceChainID).Return(sourceSubnetID, errTest) return state }, @@ -50,7 +60,7 @@ func TestSignatureVerification(t *testing.T) { { name: "can't get validator set", stateF: func(ctrl *gomock.Controller) validators.State { - state := validators.NewMockState(ctrl) + state := validatorsmock.NewState(ctrl) state.EXPECT().GetSubnetID(gomock.Any(), sourceChainID).Return(sourceSubnetID, nil) state.EXPECT().GetValidatorSet(gomock.Any(), pChainHeight, sourceSubnetID).Return(nil, errTest) return state @@ -77,7 +87,7 @@ func TestSignatureVerification(t *testing.T) { { name: "weight overflow", stateF: func(ctrl *gomock.Controller) validators.State { - state := validators.NewMockState(ctrl) + state := validatorsmock.NewState(ctrl) state.EXPECT().GetSubnetID(gomock.Any(), sourceChainID).Return(sourceSubnetID, nil) state.EXPECT().GetValidatorSet(gomock.Any(), pChainHeight, sourceSubnetID).Return(map[ids.NodeID]*validators.GetValidatorOutput{ testVdrs[0].nodeID: { @@ -117,7 +127,7 @@ func TestSignatureVerification(t *testing.T) { { name: "invalid bit set index", stateF: func(ctrl *gomock.Controller) validators.State { - state := validators.NewMockState(ctrl) + state := validatorsmock.NewState(ctrl) state.EXPECT().GetSubnetID(gomock.Any(), sourceChainID).Return(sourceSubnetID, nil) state.EXPECT().GetValidatorSet(gomock.Any(), pChainHeight, sourceSubnetID).Return(vdrs, nil) return state @@ -147,7 +157,7 @@ func TestSignatureVerification(t *testing.T) { { name: "unknown index", stateF: func(ctrl *gomock.Controller) validators.State { - state := validators.NewMockState(ctrl) + state := validatorsmock.NewState(ctrl) state.EXPECT().GetSubnetID(gomock.Any(), sourceChainID).Return(sourceSubnetID, nil) state.EXPECT().GetValidatorSet(gomock.Any(), pChainHeight, sourceSubnetID).Return(vdrs, nil) return state @@ -180,7 +190,7 @@ func TestSignatureVerification(t *testing.T) { { name: "insufficient weight", stateF: func(ctrl *gomock.Controller) validators.State { - state := validators.NewMockState(ctrl) + state := validatorsmock.NewState(ctrl) state.EXPECT().GetSubnetID(gomock.Any(), sourceChainID).Return(sourceSubnetID, nil) state.EXPECT().GetValidatorSet(gomock.Any(), pChainHeight, sourceSubnetID).Return(vdrs, nil) return state @@ -224,7 +234,7 @@ func TestSignatureVerification(t *testing.T) { { name: "can't parse sig", stateF: func(ctrl *gomock.Controller) validators.State { - state := validators.NewMockState(ctrl) + state := validatorsmock.NewState(ctrl) state.EXPECT().GetSubnetID(gomock.Any(), sourceChainID).Return(sourceSubnetID, nil) state.EXPECT().GetValidatorSet(gomock.Any(), pChainHeight, sourceSubnetID).Return(vdrs, nil) return state @@ -258,7 +268,7 @@ func TestSignatureVerification(t *testing.T) { { name: "no validators", stateF: func(ctrl *gomock.Controller) validators.State { - state := validators.NewMockState(ctrl) + state := validatorsmock.NewState(ctrl) state.EXPECT().GetSubnetID(gomock.Any(), sourceChainID).Return(sourceSubnetID, nil) state.EXPECT().GetValidatorSet(gomock.Any(), pChainHeight, sourceSubnetID).Return(nil, nil) return state @@ -293,7 +303,7 @@ func TestSignatureVerification(t *testing.T) { { name: "invalid signature (substitute)", stateF: func(ctrl *gomock.Controller) validators.State { - state := validators.NewMockState(ctrl) + state := validatorsmock.NewState(ctrl) state.EXPECT().GetSubnetID(gomock.Any(), sourceChainID).Return(sourceSubnetID, nil) state.EXPECT().GetValidatorSet(gomock.Any(), pChainHeight, sourceSubnetID).Return(vdrs, nil) return state @@ -337,7 +347,7 @@ func TestSignatureVerification(t *testing.T) { { name: "invalid signature (missing one)", stateF: func(ctrl *gomock.Controller) validators.State { - state := validators.NewMockState(ctrl) + state := validatorsmock.NewState(ctrl) state.EXPECT().GetSubnetID(gomock.Any(), sourceChainID).Return(sourceSubnetID, nil) state.EXPECT().GetValidatorSet(gomock.Any(), pChainHeight, sourceSubnetID).Return(vdrs, nil) return state @@ -377,7 +387,7 @@ func TestSignatureVerification(t *testing.T) { { name: "invalid signature (extra one)", stateF: func(ctrl *gomock.Controller) validators.State { - state := validators.NewMockState(ctrl) + state := validatorsmock.NewState(ctrl) state.EXPECT().GetSubnetID(gomock.Any(), sourceChainID).Return(sourceSubnetID, nil) state.EXPECT().GetValidatorSet(gomock.Any(), pChainHeight, sourceSubnetID).Return(vdrs, nil) return state @@ -422,7 +432,7 @@ func TestSignatureVerification(t *testing.T) { { name: "valid signature", stateF: func(ctrl *gomock.Controller) validators.State { - state := validators.NewMockState(ctrl) + state := validatorsmock.NewState(ctrl) state.EXPECT().GetSubnetID(gomock.Any(), sourceChainID).Return(sourceSubnetID, nil) state.EXPECT().GetValidatorSet(gomock.Any(), pChainHeight, sourceSubnetID).Return(vdrs, nil) return state @@ -466,7 +476,7 @@ func TestSignatureVerification(t *testing.T) { { name: "valid signature (boundary)", stateF: func(ctrl *gomock.Controller) validators.State { - state := validators.NewMockState(ctrl) + state := validatorsmock.NewState(ctrl) state.EXPECT().GetSubnetID(gomock.Any(), sourceChainID).Return(sourceSubnetID, nil) state.EXPECT().GetValidatorSet(gomock.Any(), pChainHeight, sourceSubnetID).Return(vdrs, nil) return state @@ -510,7 +520,7 @@ func TestSignatureVerification(t *testing.T) { { name: "valid signature (missing key)", stateF: func(ctrl *gomock.Controller) validators.State { - state := validators.NewMockState(ctrl) + state := validatorsmock.NewState(ctrl) state.EXPECT().GetSubnetID(gomock.Any(), sourceChainID).Return(sourceSubnetID, nil) state.EXPECT().GetValidatorSet(gomock.Any(), pChainHeight, sourceSubnetID).Return(map[ids.NodeID]*validators.GetValidatorOutput{ testVdrs[0].nodeID: { @@ -571,7 +581,7 @@ func TestSignatureVerification(t *testing.T) { { name: "valid signature (duplicate key)", stateF: func(ctrl *gomock.Controller) validators.State { - state := validators.NewMockState(ctrl) + state := validatorsmock.NewState(ctrl) state.EXPECT().GetSubnetID(gomock.Any(), sourceChainID).Return(sourceSubnetID, nil) state.EXPECT().GetValidatorSet(gomock.Any(), pChainHeight, sourceSubnetID).Return(map[ids.NodeID]*validators.GetValidatorOutput{ testVdrs[0].nodeID: { diff --git a/scripts/versions.sh b/scripts/versions.sh index 908b8603ee..faee809b7c 100644 --- a/scripts/versions.sh +++ b/scripts/versions.sh @@ -4,7 +4,7 @@ # shellcheck disable=SC2034 # Don't export them as they're used in the context of other calls -AVALANCHE_VERSION=${AVALANCHE_VERSION:-'35c66e33'} +AVALANCHE_VERSION=${AVALANCHE_VERSION:-'0117ab96'} GINKGO_VERSION=${GINKGO_VERSION:-'v2.2.0'} # This won't be used, but it's here to make code syncs easier diff --git a/warp/validators/state_test.go b/warp/validators/state_test.go index b3dc67ac51..2a67415865 100644 --- a/warp/validators/state_test.go +++ b/warp/validators/state_test.go @@ -9,6 +9,7 @@ import ( "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/snow/validators" + "github.com/ava-labs/avalanchego/snow/validators/validatorsmock" "github.com/ava-labs/avalanchego/utils/constants" "github.com/ava-labs/subnet-evm/utils" "github.com/stretchr/testify/require" @@ -22,7 +23,7 @@ func TestGetValidatorSetPrimaryNetwork(t *testing.T) { mySubnetID := ids.GenerateTestID() otherSubnetID := ids.GenerateTestID() - mockState := validators.NewMockState(ctrl) + mockState := validatorsmock.NewState(ctrl) snowCtx := utils.TestSnowContext() snowCtx.SubnetID = mySubnetID snowCtx.ValidatorState = mockState From ef2f4fddd3b927f11a641bec5be054ba3b3abc95 Mon Sep 17 00:00:00 2001 From: Darioush Jalali Date: Thu, 29 Aug 2024 14:11:05 -0700 Subject: [PATCH 2/9] properly check err in ut (#1324) --- plugin/evm/gossiper_eth_gossiping_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin/evm/gossiper_eth_gossiping_test.go b/plugin/evm/gossiper_eth_gossiping_test.go index bf59b39f51..8eb140c3a8 100644 --- a/plugin/evm/gossiper_eth_gossiping_test.go +++ b/plugin/evm/gossiper_eth_gossiping_test.go @@ -111,7 +111,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) - 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") From 252592ae48ef7d3bc5d302f3d519d11010db88f2 Mon Sep 17 00:00:00 2001 From: Darioush Jalali Date: Fri, 30 Aug 2024 06:26:48 -0700 Subject: [PATCH 3/9] blockchain: Fix blockCache for rejected blocks (#1326) --- core/blockchain.go | 29 +++-------------------------- core/test_blockchain.go | 1 + 2 files changed, 4 insertions(+), 26 deletions(-) diff --git a/core/blockchain.go b/core/blockchain.go index 6a855d907c..25a471d1d9 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -1226,6 +1226,9 @@ func (bc *BlockChain) Reject(block *types.Block) error { return fmt.Errorf("failed to write delete block batch: %w", err) } + // Remove the block from the block cache (ignore return value of whether it was in the cache) + _ = bc.blockCache.Remove(block.Hash()) + return nil } @@ -1782,32 +1785,6 @@ func (bc *BlockChain) reportBlock(block *types.Block, receipts types.Receipts, e log.Debug(reason.String()) } -func (bc *BlockChain) RemoveRejectedBlocks(start, end uint64) error { - batch := bc.db.NewBatch() - - for i := start; i < end; i++ { - hashes := rawdb.ReadAllHashes(bc.db, i) - canonicalBlock := bc.GetBlockByNumber((i)) - if canonicalBlock == nil { - return fmt.Errorf("failed to retrieve block by number at height %d", i) - } - canonicalHash := canonicalBlock.Hash() - for _, hash := range hashes { - if hash == canonicalHash { - continue - } - rawdb.DeleteBlock(batch, hash, i) - } - - if err := batch.Write(); err != nil { - return fmt.Errorf("failed to write delete rejected block batch at height %d", i) - } - batch.Reset() - } - - return nil -} - // reprocessBlock reprocesses a previously accepted block. This is often used // to regenerate previously pruned state tries. func (bc *BlockChain) reprocessBlock(parent *types.Block, current *types.Block) (common.Hash, error) { diff --git a/core/test_blockchain.go b/core/test_blockchain.go index 0a109c6551..3a45592614 100644 --- a/core/test_blockchain.go +++ b/core/test_blockchain.go @@ -494,6 +494,7 @@ func TestAcceptNonCanonicalBlock(t *testing.T, create func(db ethdb.Database, gs if err := blockchain.Reject(chain1[i]); err != nil { t.Fatal(err) } + require.False(t, blockchain.HasBlock(chain1[i].Hash(), chain1[i].NumberU64())) } lastAcceptedBlock := blockchain.LastConsensusAcceptedBlock() From a929cb3f2878010de541229d229105807e4fdce8 Mon Sep 17 00:00:00 2001 From: Darioush Jalali Date: Mon, 2 Sep 2024 07:56:46 -0700 Subject: [PATCH 4/9] Code sync to go-ethereum v1.13.14 (#1152) * all changes from v.13.2-x branch * squash initial changes * remove unneeded files * fix lint * gas estimation fix * accounts/keystore: fix test with sleep * rpc fixes * try with a smaller limit * logging * change the trim fn * remove main_test from rpc * txpool: remove unused "PendingFrom" * set local-txs-enabled in load tests * ready to format * format: remove upstream go-ethereum * format: rename packages as fork * Revert "format: remove avalanche header" This reverts commit 09d505bd5e906ac186643061437e665b116b5bf6. * fix logging * deadcode linting * more linting * fix mock * fix unused * expecteds * passing UTs * add comment * unused * resolve some open issues * undo script change * test resillience * fix compile * wip * test is fixed * fix expecteds * nits * fix merge * remove todos * try * try * try * docker stuff * fix * does this work * fix * hardcode for now * fix * hardcode for now * remove TODO comments * fix * make txindexer more similar to upstream * peer/network: move ctx check * enforce tips in miner * align nits with arr4n * remove use of deprecated type core.GenesisAccount * Revert "peer/network: move ctx check" This reverts commit 147da5feb225fd176001a396a72cb7b30b25c39b. * fix ctx check * fix ctx again * undo vm_test changes * reduce diffs with ARR4N's branch * update for enforcing gasTip * unbuffer chan * update to use commit(true) * use TestOnlyAllowDuplicateBlocks * Revert "update to use commit(true)" This reverts commit 71d949ee86f32175ff647d684c85c2f61b11b977. * overflow in state_transition err * add http body limit flag (#1327) * Tail lock revisited (#1328) * avoid modifying the pointer * improve readability * prevent race in tail reading * improve repairTxIndexTail readability * use shared func --------- Co-authored-by: Ceyhun Onur --- .github/workflows/publish_docker.yml | 1 - accounts/abi/abi.go | 2 +- accounts/abi/bind/backend.go | 47 +- accounts/abi/bind/backends/simulated.go | 978 +---------- accounts/abi/bind/backends/simulated_test.go | 1498 ----------------- accounts/abi/bind/base.go | 4 +- accounts/abi/bind/base_test.go | 2 +- accounts/abi/bind/bind_test.go | 115 +- accounts/abi/bind/util_test.go | 42 +- accounts/abi/topics_test.go | 4 +- accounts/keystore/passphrase.go | 2 +- accounts/scwallet/hub.go | 2 +- cmd/evm/README.md | 2 +- cmd/evm/internal/t8ntool/execution.go | 23 +- cmd/evm/internal/t8ntool/transition.go | 17 +- cmd/evm/runner.go | 6 +- cmd/evm/staterunner.go | 18 +- cmd/evm/transition-test.sh | 2 +- consensus/dummy/consensus.go | 49 +- core/bench_test.go | 17 +- core/block_validator_test.go | 2 + core/blockchain.go | 171 +- core/blockchain_reader.go | 44 +- core/blockchain_repair_test.go | 6 +- core/blockchain_test.go | 255 +-- core/chain_makers.go | 12 +- core/chain_makers_test.go | 6 +- core/error.go | 6 + core/evm.go | 6 +- core/gen_genesis.go | 77 +- core/genesis.go | 90 +- core/genesis_test.go | 39 +- core/rawdb/accessors_trie.go | 2 +- core/rawdb/chain_iterator.go | 72 +- core/rawdb/chain_iterator_test.go | 10 +- core/rawdb/database.go | 2 + core/rlp_test.go | 2 +- core/state/database.go | 13 +- core/state/journal.go | 7 +- core/state/pruner/bloom.go | 21 +- core/state/pruner/pruner.go | 9 +- core/state/snapshot/context.go | 2 + core/state/snapshot/difflayer.go | 59 +- core/state/snapshot/disklayer.go | 4 +- core/state/snapshot/disklayer_test.go | 4 +- core/state/snapshot/generate.go | 3 +- core/state/snapshot/generate_test.go | 125 +- core/state/snapshot/journal.go | 4 +- core/state/snapshot/snapshot.go | 6 +- core/state/snapshot/snapshot_test.go | 12 +- core/state/state_object.go | 24 +- core/state/state_test.go | 239 ++- core/state/statedb.go | 40 +- core/state/statedb_fuzz_test.go | 15 +- core/state/statedb_test.go | 128 +- core/state/trie_prefetcher_test.go | 7 +- core/state_processor.go | 2 +- core/state_processor_test.go | 14 +- core/state_transition.go | 46 +- core/test_blockchain.go | 58 +- core/txindexer.go | 218 +++ core/txindexer_test.go | 258 +++ core/txpool/blobpool/blobpool.go | 172 +- core/txpool/blobpool/blobpool_test.go | 219 ++- core/txpool/blobpool/config.go | 4 +- core/txpool/blobpool/evictheap.go | 2 +- core/txpool/blobpool/limbo.go | 6 +- core/txpool/blobpool/metrics.go | 31 +- core/txpool/blobpool/priority_test.go | 2 +- core/txpool/errors.go | 6 + core/txpool/legacypool/journal.go | 7 +- core/txpool/legacypool/legacypool.go | 69 +- core/txpool/legacypool/legacypool2_test.go | 23 +- core/txpool/legacypool/legacypool_test.go | 56 +- core/txpool/legacypool/list.go | 40 +- core/txpool/legacypool/list_test.go | 41 +- core/txpool/subpool.go | 44 +- core/txpool/txpool.go | 107 +- core/txpool/validation.go | 36 +- core/types/account.go | 87 + .../gen_account.go} | 44 +- core/types/gen_account_rlp.go | 5 +- core/types/hashing_test.go | 9 +- core/types/state_account.go | 12 +- core/types/transaction.go | 5 + core/types/transaction_marshalling.go | 11 + core/types/transaction_signing_test.go | 54 +- core/types/tx_blob.go | 14 +- core/types/tx_blob_test.go | 9 +- core/vm/contract.go | 8 +- core/vm/contracts.go | 1 - core/vm/contracts_test.go | 2 +- core/vm/eips.go | 2 +- core/vm/evm.go | 41 +- core/vm/gas_table_test.go | 13 +- core/vm/instructions.go | 45 +- core/vm/instructions_test.go | 2 +- core/vm/interface.go | 7 +- core/vm/interpreter.go | 2 +- core/vm/interpreter_test.go | 6 +- core/vm/jump_table.go | 2 +- core/vm/jump_table_test.go | 2 +- core/vm/operations_acl.go | 11 +- core/vm/runtime/runtime.go | 9 +- core/vm/runtime/runtime_test.go | 5 +- eth/api_backend.go | 24 +- eth/api_debug_test.go | 10 +- eth/backend.go | 6 +- eth/filters/api.go | 10 +- eth/filters/filter_test.go | 10 +- eth/gasestimator/gasestimator.go | 4 +- eth/gasprice/feehistory.go | 4 +- eth/gasprice/gasprice_test.go | 4 +- eth/state_accessor.go | 19 +- eth/tracers/api.go | 10 +- eth/tracers/api_extra_test.go | 8 +- eth/tracers/api_test.go | 14 +- .../internal/tracetest/calltrace_test.go | 45 +- .../internal/tracetest/flat_calltrace_test.go | 14 +- .../internal/tracetest/prestate_test.go | 15 +- .../create_failed.json | 4 +- .../js/internal/tracers/call_tracer_legacy.js | 2 +- eth/tracers/js/tracer_test.go | 17 +- eth/tracers/logger/logger_test.go | 3 +- eth/tracers/native/call.go | 2 +- eth/tracers/native/prestate.go | 4 +- eth/tracers/tracers_test.go | 24 +- ethclient/ethclient.go | 6 +- ethclient/simulated/backend.go | 279 +++ ethclient/simulated/backend_test.go | 318 ++++ ethclient/simulated/options.go | 41 + ethclient/simulated/options_test.go | 74 + .../subnetevmclient/subnet_evm_client.go | 6 + go.mod | 20 +- go.sum | 40 +- interfaces/interfaces.go | 20 + internal/debug/flags.go | 16 +- internal/ethapi/api.go | 132 +- internal/ethapi/api_test.go | 416 ++++- internal/ethapi/backend.go | 2 +- internal/ethapi/errors.go | 21 + .../testdata/eth_getBlockByHash-hash-1.json | 6 +- .../eth_getBlockByHash-hash-genesis.json | 4 +- ...h_getBlockByHash-hash-latest-1-fullTx.json | 8 +- .../eth_getBlockByHash-hash-latest.json | 6 +- .../eth_getBlockByNumber-number-0.json | 4 +- .../eth_getBlockByNumber-number-1.json | 6 +- .../eth_getBlockByNumber-number-latest-1.json | 8 +- .../eth_getBlockByNumber-tag-latest.json | 6 +- ...h_getBlockReceipts-block-with-blob-tx.json | 4 +- ...eceipts-block-with-contract-create-tx.json | 4 +- ...ockReceipts-block-with-dynamic-fee-tx.json | 4 +- ...ts-block-with-legacy-contract-call-tx.json | 6 +- ...eceipts-block-with-legacy-transfer-tx.json | 4 +- .../eth_getBlockReceipts-tag-latest.json | 4 +- .../testdata/eth_getHeaderByHash-hash-0.json | 4 +- .../testdata/eth_getHeaderByHash-hash-1.json | 6 +- .../eth_getHeaderByHash-hash-latest-1.json | 6 +- .../eth_getHeaderByHash-hash-latest.json | 6 +- .../eth_getHeaderByNumber-number-0.json | 4 +- .../eth_getHeaderByNumber-number-1.json | 6 +- ...eth_getHeaderByNumber-number-latest-1.json | 6 +- .../eth_getHeaderByNumber-tag-latest.json | 6 +- .../eth_getTransactionReceipt-blob-tx.json | 4 +- ...TransactionReceipt-create-contract-tx.json | 4 +- ...eipt-create-contract-with-access-list.json | 4 +- ...ansactionReceipt-dynamic-tx-with-logs.json | 4 +- ...TransactionReceipt-normal-transfer-tx.json | 4 +- .../eth_getTransactionReceipt-with-logs.json | 6 +- internal/ethapi/transaction_args.go | 252 ++- internal/ethapi/transaction_args_test.go | 115 +- internal/flags/flags.go | 10 +- internal/flags/helpers.go | 4 +- log/logger.go | 4 +- metrics/counter.go | 2 +- metrics/gauge.go | 6 +- metrics/gauge_float64.go | 2 +- metrics/gauge_info.go | 2 +- metrics/healthcheck.go | 2 +- metrics/histogram.go | 2 +- miner/miner.go | 3 +- miner/ordering.go | 41 +- miner/ordering_test.go | 13 +- miner/worker.go | 85 +- node/defaults.go | 22 + params/config.go | 5 +- params/protocol_params.go | 1 - params/version.go | 2 +- peer/network.go | 10 + plugin/evm/block_builder.go | 5 +- plugin/evm/config.go | 3 + plugin/evm/gossip.go | 2 +- plugin/evm/gossip_test.go | 4 +- plugin/evm/gossiper_eth_gossiping_test.go | 4 +- plugin/evm/log_test.go | 19 + plugin/evm/network_handler.go | 4 +- plugin/evm/static_service_test.go | 13 +- plugin/evm/syncervm_test.go | 3 +- plugin/evm/vm.go | 17 +- plugin/evm/vm_test.go | 6 +- plugin/evm/vm_upgrade_bytes_test.go | 15 +- precompile/contract/interfaces.go | 5 +- precompile/contract/mocks.go | 7 +- precompile/contracts/nativeminter/contract.go | 4 +- .../contracts/nativeminter/contract_test.go | 22 +- precompile/contracts/nativeminter/module.go | 6 +- rpc/http.go | 18 +- rpc/http_test.go | 8 +- rpc/server.go | 9 + rpc/types.go | 7 +- rpc/websocket_test.go | 4 +- signer/core/apitypes/types.go | 2 +- stateupgrade/interfaces.go | 3 +- stateupgrade/state_upgrade.go | 4 +- sync/client/client_test.go | 8 +- sync/handlers/block_request_test.go | 10 +- sync/handlers/leafs_request.go | 5 +- sync/handlers/leafs_request_test.go | 3 +- sync/statesync/state_syncer.go | 6 +- sync/statesync/sync_test.go | 77 +- sync/statesync/test_sync.go | 8 +- sync/syncutils/test_trie.go | 17 +- tests/gen_stenv.go | 86 + tests/state_test_util.go | 195 ++- trie/committer.go | 6 +- trie/database_test.go | 144 +- trie/iterator_test.go | 41 +- trie/proof.go | 2 +- trie/proof_test.go | 18 +- trie/secure_trie.go | 26 +- trie/secure_trie_test.go | 8 +- trie/stacktrie_fuzzer_test.go | 8 +- trie/stacktrie_test.go | 10 +- trie/sync_test.go | 6 +- trie/tracer_test.go | 31 +- trie/trie.go | 5 +- trie/trie_reader.go | 33 +- trie/trie_test.go | 215 ++- trie/verkle.go | 9 +- trie/verkle_test.go | 14 +- {trie => triedb}/database.go | 39 +- triedb/database/database.go | 48 + {trie/triedb => triedb}/hashdb/database.go | 0 {trie/triedb => triedb}/pathdb/database.go | 0 .../triedb => triedb}/pathdb/database_test.go | 4 +- {trie/triedb => triedb}/pathdb/difflayer.go | 0 .../pathdb/difflayer_test.go | 0 {trie/triedb => triedb}/pathdb/disklayer.go | 0 {trie/triedb => triedb}/pathdb/errors.go | 0 {trie/triedb => triedb}/pathdb/history.go | 0 .../triedb => triedb}/pathdb/history_test.go | 0 {trie/triedb => triedb}/pathdb/journal.go | 0 {trie/triedb => triedb}/pathdb/layertree.go | 0 {trie/triedb => triedb}/pathdb/metrics.go | 0 {trie/triedb => triedb}/pathdb/nodebuffer.go | 0 {trie/triedb => triedb}/pathdb/testutils.go | 0 {trie => triedb}/preimages.go | 2 +- 257 files changed, 5083 insertions(+), 4790 deletions(-) delete mode 100644 accounts/abi/bind/backends/simulated_test.go create mode 100644 core/txindexer.go create mode 100644 core/txindexer_test.go create mode 100644 core/types/account.go rename core/{gen_genesis_account.go => types/gen_account.go} (61%) create mode 100644 ethclient/simulated/backend.go create mode 100644 ethclient/simulated/backend_test.go create mode 100644 ethclient/simulated/options.go create mode 100644 ethclient/simulated/options_test.go create mode 100644 node/defaults.go create mode 100644 plugin/evm/log_test.go create mode 100644 tests/gen_stenv.go rename {trie => triedb}/database.go (91%) create mode 100644 triedb/database/database.go rename {trie/triedb => triedb}/hashdb/database.go (100%) rename {trie/triedb => triedb}/pathdb/database.go (100%) rename {trie/triedb => triedb}/pathdb/database_test.go (99%) rename {trie/triedb => triedb}/pathdb/difflayer.go (100%) rename {trie/triedb => triedb}/pathdb/difflayer_test.go (100%) rename {trie/triedb => triedb}/pathdb/disklayer.go (100%) rename {trie/triedb => triedb}/pathdb/errors.go (100%) rename {trie/triedb => triedb}/pathdb/history.go (100%) rename {trie/triedb => triedb}/pathdb/history_test.go (100%) rename {trie/triedb => triedb}/pathdb/journal.go (100%) rename {trie/triedb => triedb}/pathdb/layertree.go (100%) rename {trie/triedb => triedb}/pathdb/metrics.go (100%) rename {trie/triedb => triedb}/pathdb/nodebuffer.go (100%) rename {trie/triedb => triedb}/pathdb/testutils.go (100%) rename {trie => triedb}/preimages.go (99%) diff --git a/.github/workflows/publish_docker.yml b/.github/workflows/publish_docker.yml index 009909c1ba..5a16349cf5 100644 --- a/.github/workflows/publish_docker.yml +++ b/.github/workflows/publish_docker.yml @@ -15,7 +15,6 @@ on: branches: - master - jobs: publish_docker_image: name: Publish Docker Image diff --git a/accounts/abi/abi.go b/accounts/abi/abi.go index 785e0a4eb9..1a6e224090 100644 --- a/accounts/abi/abi.go +++ b/accounts/abi/abi.go @@ -39,7 +39,7 @@ import ( ) // The ABI holds information about a contract's context and available -// invokable methods. It will allow you to type check function calls and +// invocable methods. It will allow you to type check function calls and // packs data accordingly. type ABI struct { Constructor Method diff --git a/accounts/abi/bind/backend.go b/accounts/abi/bind/backend.go index 298723dfd2..b95a05532d 100644 --- a/accounts/abi/bind/backend.go +++ b/accounts/abi/bind/backend.go @@ -94,6 +94,11 @@ type BlockHashContractCaller interface { // used when the user does not provide some needed values, but rather leaves it up // to the transactor to decide. type ContractTransactor interface { + interfaces.GasEstimator + interfaces.GasPricer + interfaces.GasPricer1559 + interfaces.TransactionSender + // HeaderByNumber returns a block header from the current canonical chain. If // number is nil, the latest known header is returned. HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) @@ -101,40 +106,8 @@ type ContractTransactor interface { // AcceptedCodeAt returns the code of the given account in the accepted state. AcceptedCodeAt(ctx context.Context, account common.Address) ([]byte, error) - // AcceptedNonceAt retrieves the current accepted nonce associated with an account. - AcceptedNonceAt(ctx context.Context, account common.Address) (uint64, error) - - // SuggestGasPrice retrieves the currently suggested gas price to allow a timely - // execution of a transaction. - SuggestGasPrice(ctx context.Context) (*big.Int, error) - - // SuggestGasTipCap retrieves the currently suggested 1559 priority fee to allow - // a timely execution of a transaction. - SuggestGasTipCap(ctx context.Context) (*big.Int, error) - - // EstimateGas tries to estimate the gas needed to execute a specific - // transaction based on the current pending state of the backend blockchain. - // There is no guarantee that this is the true gas limit requirement as other - // transactions may be added or removed by miners, but it should provide a basis - // for setting a reasonable default. - EstimateGas(ctx context.Context, call interfaces.CallMsg) (gas uint64, err error) - - // SendTransaction injects the transaction into the pending pool for execution. - SendTransaction(ctx context.Context, tx *types.Transaction) error -} - -// ContractFilterer defines the methods needed to access log events using one-off -// queries or continuous event subscriptions. -type ContractFilterer interface { - // FilterLogs executes a log filter operation, blocking during execution and - // returning all the results in one batch. - // - // TODO(karalabe): Deprecate when the subscription one can return past data too. - FilterLogs(ctx context.Context, query interfaces.FilterQuery) ([]types.Log, error) - - // SubscribeFilterLogs creates a background log filtering operation, returning - // a subscription immediately, which can be used to stream the found events. - SubscribeFilterLogs(ctx context.Context, query interfaces.FilterQuery, ch chan<- types.Log) (interfaces.Subscription, error) + // NonceAt retrieves the nonce associated with an account. + NonceAt(ctx context.Context, account common.Address, blockNum *big.Int) (uint64, error) } // DeployBackend wraps the operations needed by WaitMined and WaitDeployed. @@ -143,6 +116,12 @@ type DeployBackend interface { CodeAt(ctx context.Context, account common.Address, blockNumber *big.Int) ([]byte, error) } +// ContractFilterer defines the methods needed to access log events using one-off +// queries or continuous event subscriptions. +type ContractFilterer interface { + interfaces.LogFilterer +} + // ContractBackend defines the methods needed to work with contracts on a read-write basis. type ContractBackend interface { ContractCaller diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go index ee15adcb42..141f30f9c8 100644 --- a/accounts/abi/bind/backends/simulated.go +++ b/accounts/abi/bind/backends/simulated.go @@ -28,34 +28,12 @@ package backends import ( "context" - "errors" - "fmt" - "math/big" - "sync" - "time" - "github.com/ava-labs/subnet-evm/eth" - "github.com/ava-labs/subnet-evm/vmerrs" - - "github.com/ava-labs/subnet-evm/accounts/abi" "github.com/ava-labs/subnet-evm/accounts/abi/bind" - "github.com/ava-labs/subnet-evm/consensus/dummy" - "github.com/ava-labs/subnet-evm/core" - "github.com/ava-labs/subnet-evm/core/bloombits" - "github.com/ava-labs/subnet-evm/core/rawdb" - "github.com/ava-labs/subnet-evm/core/state" "github.com/ava-labs/subnet-evm/core/types" - "github.com/ava-labs/subnet-evm/core/vm" - "github.com/ava-labs/subnet-evm/eth/filters" + "github.com/ava-labs/subnet-evm/ethclient/simulated" "github.com/ava-labs/subnet-evm/interfaces" - "github.com/ava-labs/subnet-evm/params" - "github.com/ava-labs/subnet-evm/rpc" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/ethereum/go-ethereum/common/math" - "github.com/ethereum/go-ethereum/ethdb" - "github.com/ethereum/go-ethereum/event" - "github.com/ethereum/go-ethereum/log" ) // Verify that SimulatedBackend implements required interfaces @@ -76,953 +54,29 @@ var ( _ interfaces.AcceptedContractCaller = (*SimulatedBackend)(nil) ) -var ( - errBlockNumberUnsupported = errors.New("simulatedBackend cannot access blocks other than the latest block") - errBlockHashUnsupported = errors.New("simulatedBackend cannot access blocks by hash other than the latest block") - errBlockDoesNotExist = errors.New("block does not exist in blockchain") - errTransactionDoesNotExist = errors.New("transaction does not exist") -) - -// SimulatedBackend implements bind.ContractBackend, simulating a blockchain in -// the background. Its main purpose is to allow for easy testing of contract bindings. -// Simulated backend implements the following interfaces: -// ChainReader, ChainStateReader, ContractBackend, ContractCaller, ContractFilterer, ContractTransactor, -// DeployBackend, GasEstimator, GasPricer, LogFilterer, AcceptedContractCaller, TransactionReader, and TransactionSender +// SimulatedBackend is a simulated blockchain. +// Deprecated: use package github.com/ava-labs/subnet-evm/ethclient/simulated instead. type SimulatedBackend struct { - database ethdb.Database // In memory database to store our testing data - blockchain *core.BlockChain // Ethereum blockchain to handle the consensus - - mu sync.Mutex - acceptedBlock *types.Block // Currently accepted block that will be imported on request - acceptedState *state.StateDB // Currently accepted state that will be the active on request - - events *filters.EventSystem // for filtering log events live - filterSystem *filters.FilterSystem // for filtering database logs - - config *params.ChainConfig + *simulated.Backend + simulated.Client } -// NewSimulatedBackendWithDatabase creates a new binding backend based on the given database -// and uses a simulated blockchain for testing purposes. -// A simulated backend always uses chainID 1337. -func NewSimulatedBackendWithDatabase(database ethdb.Database, alloc core.GenesisAlloc, gasLimit uint64) *SimulatedBackend { - copyConfig := *params.TestChainConfig - copyConfig.ChainID = big.NewInt(1337) - genesis := core.Genesis{ - Config: ©Config, - GasLimit: gasLimit, - Alloc: alloc, - } - cacheConfig := &core.CacheConfig{} - blockchain, _ := core.NewBlockChain(database, cacheConfig, &genesis, dummy.NewCoinbaseFaker(), vm.Config{}, common.Hash{}, false) - - backend := &SimulatedBackend{ - database: database, - blockchain: blockchain, - config: genesis.Config, - } - - filterBackend := &filterBackend{database, blockchain, backend} - backend.filterSystem = filters.NewFilterSystem(filterBackend, filters.Config{}) - backend.events = filters.NewEventSystem(backend.filterSystem) - - header := backend.blockchain.CurrentBlock() - block := backend.blockchain.GetBlock(header.Hash(), header.Number.Uint64()) - - backend.rollback(block) - return backend +// Fork sets the head to a new block, which is based on the provided parentHash. +func (b *SimulatedBackend) Fork(ctx context.Context, parentHash common.Hash) error { + return b.Backend.Fork(parentHash) } // NewSimulatedBackend creates a new binding backend using a simulated blockchain // for testing purposes. -// A simulated backend always uses chainID 1337. -func NewSimulatedBackend(alloc core.GenesisAlloc, gasLimit uint64) *SimulatedBackend { - return NewSimulatedBackendWithDatabase(rawdb.NewMemoryDatabase(), alloc, gasLimit) -} - -// Close terminates the underlying blockchain's update loop. -func (b *SimulatedBackend) Close() error { - b.blockchain.Stop() - return nil -} - -// Commit imports all the accepted transactions as a single block and starts a -// fresh new state. -func (b *SimulatedBackend) Commit(accept bool) common.Hash { - b.mu.Lock() - defer b.mu.Unlock() - - if _, err := b.blockchain.InsertChain([]*types.Block{b.acceptedBlock}); err != nil { - panic(err) // This cannot happen unless the simulator is wrong, fail in that case - } - if accept { - if err := b.blockchain.Accept(b.acceptedBlock); err != nil { - panic(err) - } - b.blockchain.DrainAcceptorQueue() - } - blockHash := b.acceptedBlock.Hash() - - // Using the last inserted block here makes it possible to build on a side - // chain after a fork. - b.rollback(b.acceptedBlock) - - return blockHash -} - -// Rollback aborts all accepted transactions, reverting to the last committed state. -func (b *SimulatedBackend) Rollback() { - b.mu.Lock() - defer b.mu.Unlock() - - header := b.blockchain.CurrentBlock() - block := b.blockchain.GetBlock(header.Hash(), header.Number.Uint64()) - - b.rollback(block) -} - -func (b *SimulatedBackend) rollback(parent *types.Block) { - blocks, _, _ := core.GenerateChain(b.config, parent, dummy.NewFaker(), b.database, 1, 10, func(int, *core.BlockGen) {}) - - b.acceptedBlock = blocks[0] - b.acceptedState, _ = state.New(b.acceptedBlock.Root(), b.blockchain.StateCache(), nil) -} - -// Fork creates a side-chain that can be used to simulate reorgs. // -// This function should be called with the ancestor block where the new side -// chain should be started. Transactions (old and new) can then be applied on -// top and Commit-ed. -// -// Note, the side-chain will only become canonical (and trigger the events) when -// it becomes longer. Until then CallContract will still operate on the current -// canonical chain. -// -// There is a % chance that the side chain becomes canonical at the same length -// to simulate live network behavior. -func (b *SimulatedBackend) Fork(ctx context.Context, parent common.Hash) error { - b.mu.Lock() - defer b.mu.Unlock() - - if len(b.acceptedBlock.Transactions()) != 0 { - return errors.New("accepted block dirty") - } - block, err := b.blockByHash(ctx, parent) - if err != nil { - return err - } - b.rollback(block) - return nil -} - -// stateByBlockNumber retrieves a state by a given blocknumber. -func (b *SimulatedBackend) stateByBlockNumber(ctx context.Context, blockNumber *big.Int) (*state.StateDB, error) { - if blockNumber == nil || blockNumber.Cmp(b.blockchain.CurrentBlock().Number) == 0 { - return b.blockchain.State() - } - block, err := b.blockByNumber(ctx, blockNumber) - if err != nil { - return nil, err - } - return b.blockchain.StateAt(block.Root()) -} - -// CodeAt returns the code associated with a certain account in the blockchain. -func (b *SimulatedBackend) CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error) { - b.mu.Lock() - defer b.mu.Unlock() - - stateDB, err := b.stateByBlockNumber(ctx, blockNumber) - if err != nil { - return nil, err - } - return stateDB.GetCode(contract), nil -} - -// CodeAtHash returns the code associated with a certain account in the blockchain. -func (b *SimulatedBackend) CodeAtHash(ctx context.Context, contract common.Address, blockHash common.Hash) ([]byte, error) { - b.mu.Lock() - defer b.mu.Unlock() - - header, err := b.headerByHash(blockHash) - if err != nil { - return nil, err - } - - stateDB, err := b.blockchain.StateAt(header.Root) - if err != nil { - return nil, err - } - - return stateDB.GetCode(contract), nil -} - -// BalanceAt returns the wei balance of a certain account in the blockchain. -func (b *SimulatedBackend) BalanceAt(ctx context.Context, contract common.Address, blockNumber *big.Int) (*big.Int, error) { - b.mu.Lock() - defer b.mu.Unlock() - - stateDB, err := b.stateByBlockNumber(ctx, blockNumber) - if err != nil { - return nil, err - } - return stateDB.GetBalance(contract), nil -} - -// NonceAt returns the nonce of a certain account in the blockchain. -func (b *SimulatedBackend) NonceAt(ctx context.Context, contract common.Address, blockNumber *big.Int) (uint64, error) { - b.mu.Lock() - defer b.mu.Unlock() - - stateDB, err := b.stateByBlockNumber(ctx, blockNumber) - if err != nil { - return 0, err - } - return stateDB.GetNonce(contract), nil -} - -// StorageAt returns the value of key in the storage of an account in the blockchain. -func (b *SimulatedBackend) StorageAt(ctx context.Context, contract common.Address, key common.Hash, blockNumber *big.Int) ([]byte, error) { - b.mu.Lock() - defer b.mu.Unlock() - - stateDB, err := b.stateByBlockNumber(ctx, blockNumber) - if err != nil { - return nil, err - } - val := stateDB.GetState(contract, key) - return val[:], nil -} - -// TransactionReceipt returns the receipt of a transaction. -func (b *SimulatedBackend) TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) { - b.mu.Lock() - defer b.mu.Unlock() - - receipt, _, _, _ := rawdb.ReadReceipt(b.database, txHash, b.config) - if receipt == nil { - return nil, interfaces.NotFound - } - return receipt, nil -} - -// TransactionByHash checks the pool of accepted transactions in addition to the -// blockchain. The isAccepted return value indicates whether the transaction has been -// mined yet. Note that the transaction may not be part of the canonical chain even if -// it's not accepted. -func (b *SimulatedBackend) TransactionByHash(ctx context.Context, txHash common.Hash) (*types.Transaction, bool, error) { - b.mu.Lock() - defer b.mu.Unlock() - - tx := b.acceptedBlock.Transaction(txHash) - if tx != nil { - return tx, true, nil - } - tx, _, _, _ = rawdb.ReadTransaction(b.database, txHash) - if tx != nil { - return tx, false, nil - } - return nil, false, interfaces.NotFound -} - -// BlockByHash retrieves a block based on the block hash. -func (b *SimulatedBackend) BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) { - b.mu.Lock() - defer b.mu.Unlock() - - return b.blockByHash(ctx, hash) -} - -// blockByHash retrieves a block based on the block hash without Locking. -func (b *SimulatedBackend) blockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) { - if hash == b.acceptedBlock.Hash() { - return b.acceptedBlock, nil - } - - block := b.blockchain.GetBlockByHash(hash) - if block != nil { - return block, nil - } - - return nil, errBlockDoesNotExist -} - -// BlockByNumber retrieves a block from the database by number, caching it -// (associated with its hash) if found. -func (b *SimulatedBackend) BlockByNumber(ctx context.Context, number *big.Int) (*types.Block, error) { - b.mu.Lock() - defer b.mu.Unlock() - - return b.blockByNumber(ctx, number) -} - -// blockByNumber retrieves a block from the database by number, caching it -// (associated with its hash) if found without Lock. -func (b *SimulatedBackend) blockByNumber(ctx context.Context, number *big.Int) (*types.Block, error) { - if number == nil || number.Cmp(b.acceptedBlock.Number()) == 0 { - return b.blockByHash(ctx, b.blockchain.CurrentBlock().Hash()) - } - - block := b.blockchain.GetBlockByNumber(uint64(number.Int64())) - if block == nil { - return nil, errBlockDoesNotExist - } - - return block, nil -} - -// HeaderByHash returns a block header from the current canonical chain. -func (b *SimulatedBackend) HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) { - b.mu.Lock() - defer b.mu.Unlock() - return b.headerByHash(hash) -} - -// headerByHash retrieves a header from the database by hash without Lock. -func (b *SimulatedBackend) headerByHash(hash common.Hash) (*types.Header, error) { - if hash == b.acceptedBlock.Hash() { - return b.acceptedBlock.Header(), nil - } - - header := b.blockchain.GetHeaderByHash(hash) - if header == nil { - return nil, errBlockDoesNotExist - } - - return header, nil -} - -// HeaderByNumber returns a block header from the current canonical chain. If number is -// nil, the latest known header is returned. -func (b *SimulatedBackend) HeaderByNumber(ctx context.Context, block *big.Int) (*types.Header, error) { - b.mu.Lock() - defer b.mu.Unlock() - - if block == nil || block.Cmp(b.acceptedBlock.Number()) == 0 { - return b.blockchain.CurrentHeader(), nil - } - - return b.blockchain.GetHeaderByNumber(uint64(block.Int64())), nil -} - -// TransactionCount returns the number of transactions in a given block. -func (b *SimulatedBackend) TransactionCount(ctx context.Context, blockHash common.Hash) (uint, error) { - b.mu.Lock() - defer b.mu.Unlock() - - if blockHash == b.acceptedBlock.Hash() { - return uint(b.acceptedBlock.Transactions().Len()), nil - } - - block := b.blockchain.GetBlockByHash(blockHash) - if block == nil { - return uint(0), errBlockDoesNotExist - } - - return uint(block.Transactions().Len()), nil -} - -// TransactionInBlock returns the transaction for a specific block at a specific index. -func (b *SimulatedBackend) TransactionInBlock(ctx context.Context, blockHash common.Hash, index uint) (*types.Transaction, error) { - b.mu.Lock() - defer b.mu.Unlock() - - if blockHash == b.acceptedBlock.Hash() { - transactions := b.acceptedBlock.Transactions() - if uint(len(transactions)) < index+1 { - return nil, errTransactionDoesNotExist - } - - return transactions[index], nil - } - - block := b.blockchain.GetBlockByHash(blockHash) - if block == nil { - return nil, errBlockDoesNotExist - } - - transactions := block.Transactions() - if uint(len(transactions)) < index+1 { - return nil, errTransactionDoesNotExist - } - - return transactions[index], nil -} - -// AcceptedCodeAt returns the code associated with an account in the accepted state. -func (b *SimulatedBackend) AcceptedCodeAt(ctx context.Context, contract common.Address) ([]byte, error) { - b.mu.Lock() - defer b.mu.Unlock() - - return b.acceptedState.GetCode(contract), nil -} - -func newRevertError(result *core.ExecutionResult) *revertError { - reason, errUnpack := abi.UnpackRevert(result.Revert()) - err := errors.New("execution reverted") - if errUnpack == nil { - err = fmt.Errorf("execution reverted: %v", reason) - } - return &revertError{ - error: err, - reason: hexutil.Encode(result.Revert()), - } -} - -// revertError is an API error that encompasses an EVM revert with JSON error -// code and a binary data blob. -type revertError struct { - error - reason string // revert reason hex encoded -} - -// ErrorCode returns the JSON error code for a revert. -// See: https://github.com/ethereum/wiki/wiki/JSON-RPC-Error-Codes-Improvement-Proposal -func (e *revertError) ErrorCode() int { - return 3 -} - -// ErrorData returns the hex encoded revert reason. -func (e *revertError) ErrorData() interface{} { - return e.reason -} - -// CallContract executes a contract call. -func (b *SimulatedBackend) CallContract(ctx context.Context, call interfaces.CallMsg, blockNumber *big.Int) ([]byte, error) { - b.mu.Lock() - defer b.mu.Unlock() - - if blockNumber != nil && blockNumber.Cmp(b.blockchain.CurrentBlock().Number) != 0 { - return nil, errBlockNumberUnsupported - } - return b.callContractAtHead(ctx, call) -} - -// CallContractAtHash executes a contract call on a specific block hash. -func (b *SimulatedBackend) CallContractAtHash(ctx context.Context, call interfaces.CallMsg, blockHash common.Hash) ([]byte, error) { - b.mu.Lock() - defer b.mu.Unlock() - - if blockHash != b.blockchain.CurrentBlock().Hash() { - return nil, errBlockHashUnsupported - } - return b.callContractAtHead(ctx, call) -} - -// callContractAtHead executes a contract call against the latest block state. -func (b *SimulatedBackend) callContractAtHead(ctx context.Context, call interfaces.CallMsg) ([]byte, error) { - stateDB, err := b.blockchain.State() - if err != nil { - return nil, err - } - res, err := b.callContract(ctx, call, b.blockchain.CurrentBlock(), stateDB) - if err != nil { - return nil, err - } - // If the result contains a revert reason, try to unpack and return it. - if len(res.Revert()) > 0 { - return nil, newRevertError(res) - } - return res.Return(), res.Err -} - -// AcceptedCallContract executes a contract call on the accepted state. -func (b *SimulatedBackend) AcceptedCallContract(ctx context.Context, call interfaces.CallMsg) ([]byte, error) { - b.mu.Lock() - defer b.mu.Unlock() - defer b.acceptedState.RevertToSnapshot(b.acceptedState.Snapshot()) - - res, err := b.callContract(ctx, call, b.acceptedBlock.Header(), b.acceptedState) - if err != nil { - return nil, err - } - // If the result contains a revert reason, try to unpack and return it. - if len(res.Revert()) > 0 { - return nil, newRevertError(res) - } - return res.Return(), res.Err -} - -// AcceptedNonceAt implements AcceptedStateReader.AcceptedNonceAt, retrieving -// the nonce currently accepted for the account. -func (b *SimulatedBackend) AcceptedNonceAt(ctx context.Context, account common.Address) (uint64, error) { - b.mu.Lock() - defer b.mu.Unlock() - - return b.acceptedState.GetOrNewStateObject(account).Nonce(), nil -} - -// SuggestGasPrice implements ContractTransactor.SuggestGasPrice. Since the simulated -// chain doesn't have miners, we just return a gas price of 1 for any call. -func (b *SimulatedBackend) SuggestGasPrice(ctx context.Context) (*big.Int, error) { - b.mu.Lock() - defer b.mu.Unlock() - - if b.acceptedBlock.Header().BaseFee != nil { - return b.acceptedBlock.Header().BaseFee, nil - } - return big.NewInt(1), nil -} - -// SuggestGasTipCap implements ContractTransactor.SuggestGasTipCap. Since the simulated -// chain doesn't have miners, we just return a gas tip of 1 for any call. -func (b *SimulatedBackend) SuggestGasTipCap(ctx context.Context) (*big.Int, error) { - return big.NewInt(1), nil -} - -// EstimateGas executes the requested code against the currently accepted block/state and -// returns the used amount of gas. -func (b *SimulatedBackend) EstimateGas(ctx context.Context, call interfaces.CallMsg) (uint64, error) { - b.mu.Lock() - defer b.mu.Unlock() - - // Determine the lowest and highest possible gas limits to binary search in between - var ( - lo uint64 = params.TxGas - 1 - hi uint64 - cap uint64 - ) - if call.Gas >= params.TxGas { - hi = call.Gas - } else { - hi = b.acceptedBlock.GasLimit() - } - // Normalize the max fee per gas the call is willing to spend. - var feeCap *big.Int - if call.GasPrice != nil && (call.GasFeeCap != nil || call.GasTipCap != nil) { - return 0, errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified") - } else if call.GasPrice != nil { - feeCap = call.GasPrice - } else if call.GasFeeCap != nil { - feeCap = call.GasFeeCap - } else { - feeCap = common.Big0 - } - // Recap the highest gas allowance with account's balance. - if feeCap.BitLen() != 0 { - balance := b.acceptedState.GetBalance(call.From) // from can't be nil - available := new(big.Int).Set(balance) - if call.Value != nil { - if call.Value.Cmp(available) >= 0 { - return 0, core.ErrInsufficientFundsForTransfer - } - available.Sub(available, call.Value) - } - allowance := new(big.Int).Div(available, feeCap) - if allowance.IsUint64() && hi > allowance.Uint64() { - transfer := call.Value - if transfer == nil { - transfer = new(big.Int) - } - log.Info("Gas estimation capped by limited funds", "original", hi, "balance", balance, - "sent", transfer, "feecap", feeCap, "fundable", allowance) - hi = allowance.Uint64() - } - } - cap = hi - - // Create a helper to check if a gas allowance results in an executable transaction - executable := func(gas uint64) (bool, *core.ExecutionResult, error) { - call.Gas = gas - - snapshot := b.acceptedState.Snapshot() - res, err := b.callContract(ctx, call, b.acceptedBlock.Header(), b.acceptedState) - b.acceptedState.RevertToSnapshot(snapshot) - - if err != nil { - if errors.Is(err, core.ErrIntrinsicGas) { - return true, nil, nil // Special case, raise gas limit - } - return true, nil, err // Bail out - } - return res.Failed(), res, nil - } - // Execute the binary search and hone in on an executable gas limit - for lo+1 < hi { - mid := (hi + lo) / 2 - failed, _, err := executable(mid) - - // If the error is not nil(consensus error), it means the provided message - // call or transaction will never be accepted no matter how much gas it is - // assigned. Return the error directly, don't struggle any more - if err != nil { - return 0, err - } - if failed { - lo = mid - } else { - hi = mid - } - } - // Reject the transaction as invalid if it still fails at the highest allowance - if hi == cap { - failed, result, err := executable(hi) - if err != nil { - return 0, err - } - if failed { - if result != nil && !errors.Is(result.Err, vmerrs.ErrOutOfGas) { - if len(result.Revert()) > 0 { - return 0, newRevertError(result) - } - return 0, result.Err - } - // Otherwise, the specified gas cap is too low - return 0, fmt.Errorf("gas required exceeds allowance (%d)", cap) - } - } - return hi, nil -} - -// callContract implements common code between normal and accepted contract calls. -// state is modified during execution, make sure to copy it if necessary. -func (b *SimulatedBackend) callContract(ctx context.Context, call interfaces.CallMsg, header *types.Header, stateDB *state.StateDB) (*core.ExecutionResult, error) { - // Gas prices post 1559 need to be initialized - if call.GasPrice != nil && (call.GasFeeCap != nil || call.GasTipCap != nil) { - return nil, errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified") - } - if !b.blockchain.Config().IsSubnetEVM(header.Time) { - // If there's no basefee, then it must be a non-1559 execution - if call.GasPrice == nil { - call.GasPrice = new(big.Int) - } - call.GasFeeCap, call.GasTipCap = call.GasPrice, call.GasPrice - } else { - // A basefee is provided, necessitating 1559-type execution - if call.GasPrice != nil { - // User specified the legacy gas field, convert to 1559 gas typing - call.GasFeeCap, call.GasTipCap = call.GasPrice, call.GasPrice - } else { - // User specified 1559 gas fields (or none), use those - if call.GasFeeCap == nil { - call.GasFeeCap = new(big.Int) - } - if call.GasTipCap == nil { - call.GasTipCap = new(big.Int) - } - // Backfill the legacy gasPrice for EVM execution, unless we're all zeroes - call.GasPrice = new(big.Int) - if call.GasFeeCap.BitLen() > 0 || call.GasTipCap.BitLen() > 0 { - call.GasPrice = math.BigMin(new(big.Int).Add(call.GasTipCap, header.BaseFee), call.GasFeeCap) - } - } - } - // Ensure message is initialized properly. - if call.Gas == 0 { - call.Gas = 10 * header.GasLimit - } - if call.Value == nil { - call.Value = new(big.Int) - } - - // Set infinite balance to the fake caller account. - from := stateDB.GetOrNewStateObject(call.From) - from.SetBalance(math.MaxBig256) - - // Execute the call. - msg := &core.Message{ - From: call.From, - To: call.To, - Value: call.Value, - GasLimit: call.Gas, - GasPrice: call.GasPrice, - GasFeeCap: call.GasFeeCap, - GasTipCap: call.GasTipCap, - Data: call.Data, - AccessList: call.AccessList, - SkipAccountChecks: true, - } - - // Create a new environment which holds all relevant information - // about the transaction and calling mechanisms. - txContext := core.NewEVMTxContext(msg) - evmContext := core.NewEVMBlockContext(header, b.blockchain, nil) - vmEnv := vm.NewEVM(evmContext, txContext, stateDB, b.config, vm.Config{NoBaseFee: true}) - gasPool := new(core.GasPool).AddGas(math.MaxUint64) - - return core.ApplyMessage(vmEnv, msg, gasPool) -} - -// SendTransaction updates the accepted block to include the given transaction. -func (b *SimulatedBackend) SendTransaction(ctx context.Context, tx *types.Transaction) error { - b.mu.Lock() - defer b.mu.Unlock() - - // Get the last block - block, err := b.blockByHash(ctx, b.acceptedBlock.ParentHash()) - if err != nil { - return errors.New("could not fetch parent") - } - // Check transaction validity - signer := types.MakeSigner(b.blockchain.Config(), block.Number(), block.Time()) - sender, err := types.Sender(signer, tx) - if err != nil { - return fmt.Errorf("invalid transaction: %v", err) - } - nonce := b.acceptedState.GetNonce(sender) - if tx.Nonce() != nonce { - return fmt.Errorf("invalid transaction nonce: got %d, want %d", tx.Nonce(), nonce) - } - // Include tx in chain - blocks, _, err := core.GenerateChain(b.config, block, dummy.NewETHFaker(), b.database, 1, 10, func(number int, block *core.BlockGen) { - for _, tx := range b.acceptedBlock.Transactions() { - block.AddTxWithChain(b.blockchain, tx) - } - block.AddTxWithChain(b.blockchain, tx) - }) - if err != nil { - return err - } - stateDB, err := b.blockchain.State() - if err != nil { - return err - } - b.acceptedBlock = blocks[0] - b.acceptedState, _ = state.New(b.acceptedBlock.Root(), stateDB.Database(), nil) - return nil -} - -// FilterLogs executes a log filter operation, blocking during execution and -// returning all the results in one batch. +// A simulated backend always uses chainID 1337. // -// TODO(karalabe): Deprecate when the subscription one can return past data too. -func (b *SimulatedBackend) FilterLogs(ctx context.Context, query interfaces.FilterQuery) ([]types.Log, error) { - var filter *filters.Filter - if query.BlockHash != nil { - // Block filter requested, construct a single-shot filter - filter = b.filterSystem.NewBlockFilter(*query.BlockHash, query.Addresses, query.Topics) - } else { - // Initialize unset filter boundaries to run from genesis to chain head - from := int64(0) - if query.FromBlock != nil { - from = query.FromBlock.Int64() - } - to := int64(-1) - if query.ToBlock != nil { - to = query.ToBlock.Int64() - } - // Construct the range filter - filter = b.filterSystem.NewRangeFilter(from, to, query.Addresses, query.Topics) - } - // Run the filter and return all the logs - logs, err := filter.Logs(ctx) - if err != nil { - return nil, err +// Deprecated: please use simulated.Backend from package +// github.com/ava-labs/subnet-evm/ethclient/simulated instead. +func NewSimulatedBackend(alloc types.GenesisAlloc, gasLimit uint64) *SimulatedBackend { + b := simulated.NewBackend(alloc, simulated.WithBlockGasLimit(gasLimit)) + return &SimulatedBackend{ + Backend: b, + Client: b.Client(), } - res := make([]types.Log, len(logs)) - for i, nLog := range logs { - res[i] = *nLog - } - return res, nil -} - -// SubscribeFilterLogs creates a background log filtering operation, returning a -// subscription immediately, which can be used to stream the found events. -func (b *SimulatedBackend) SubscribeFilterLogs(ctx context.Context, query interfaces.FilterQuery, ch chan<- types.Log) (interfaces.Subscription, error) { - // Subscribe to contract events - sink := make(chan []*types.Log) - - sub, err := b.events.SubscribeLogs(query, sink) - if err != nil { - return nil, err - } - // Since we're getting logs in batches, we need to flatten them into a plain stream - return event.NewSubscription(func(quit <-chan struct{}) error { - defer sub.Unsubscribe() - for { - select { - case logs := <-sink: - for _, nlog := range logs { - select { - case ch <- *nlog: - case err := <-sub.Err(): - return err - case <-quit: - return nil - } - } - case err := <-sub.Err(): - return err - case <-quit: - return nil - } - } - }), nil -} - -// SubscribeNewHead returns an event subscription for a new header. -func (b *SimulatedBackend) SubscribeNewHead(ctx context.Context, ch chan<- *types.Header) (interfaces.Subscription, error) { - // subscribe to a new head - sink := make(chan *types.Header) - sub := b.events.SubscribeNewHeads(sink) - - return event.NewSubscription(func(quit <-chan struct{}) error { - defer sub.Unsubscribe() - for { - select { - case head := <-sink: - select { - case ch <- head: - case err := <-sub.Err(): - return err - case <-quit: - return nil - } - case err := <-sub.Err(): - return err - case <-quit: - return nil - } - } - }), nil -} - -// AdjustTime adds a time shift to the simulated clock. -// It can only be called on empty blocks. -func (b *SimulatedBackend) AdjustTime(adjustment time.Duration) error { - b.mu.Lock() - defer b.mu.Unlock() - - if len(b.acceptedBlock.Transactions()) != 0 { - return errors.New("could not adjust time on non-empty block") - } - block := b.blockchain.GetBlockByHash(b.acceptedBlock.ParentHash()) - if block == nil { - return errors.New("could not find parent") - } - - blocks, _, _ := core.GenerateChain(b.config, block, dummy.NewFaker(), b.database, 1, 10, func(number int, block *core.BlockGen) { - block.OffsetTime(int64(adjustment.Seconds())) - }) - stateDB, err := b.blockchain.State() - if err != nil { - return err - } - b.acceptedBlock = blocks[0] - b.acceptedState, _ = state.New(b.acceptedBlock.Root(), stateDB.Database(), nil) - return nil -} - -// Blockchain returns the underlying blockchain. -func (b *SimulatedBackend) Blockchain() *core.BlockChain { - return b.blockchain -} - -// filterBackend implements filters.Backend to support filtering for logs without -// taking bloom-bits acceleration structures into account. -type filterBackend struct { - db ethdb.Database - bc *core.BlockChain - backend *SimulatedBackend -} - -func (fb *filterBackend) SubscribeChainAcceptedEvent(ch chan<- core.ChainEvent) event.Subscription { - return fb.bc.SubscribeChainAcceptedEvent(ch) -} - -func (fb *filterBackend) SubscribeAcceptedLogsEvent(ch chan<- []*types.Log) event.Subscription { - return fb.bc.SubscribeAcceptedLogsEvent(ch) -} - -func (fb *filterBackend) SubscribeAcceptedTransactionEvent(ch chan<- core.NewTxsEvent) event.Subscription { - return fb.bc.SubscribeAcceptedTransactionEvent(ch) -} - -func (fb *filterBackend) IsAllowUnfinalizedQueries() bool { - return false -} - -func (fb *filterBackend) LastAcceptedBlock() *types.Block { - return fb.bc.LastAcceptedBlock() -} - -func (fb *filterBackend) GetMaxBlocksPerRequest() int64 { - return eth.DefaultSettings.MaxBlocksPerRequest -} - -func (fb *filterBackend) ChainDb() ethdb.Database { return fb.db } - -func (fb *filterBackend) EventMux() *event.TypeMux { panic("not supported") } - -func (fb *filterBackend) HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error) { - switch number { - case rpc.PendingBlockNumber, rpc.FinalizedBlockNumber: - if block := fb.backend.acceptedBlock; block != nil { - return block.Header(), nil - } - return nil, nil - case rpc.LatestBlockNumber: - return fb.bc.CurrentHeader(), nil - default: - return fb.bc.GetHeaderByNumber(uint64(number.Int64())), nil - } -} - -func (fb *filterBackend) HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) { - return fb.bc.GetHeaderByHash(hash), nil -} - -func (fb *filterBackend) GetBody(ctx context.Context, hash common.Hash, number rpc.BlockNumber) (*types.Body, error) { - if body := fb.bc.GetBody(hash); body != nil { - return body, nil - } - return nil, errors.New("block body not found") -} - -func (fb *filterBackend) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) { - number := rawdb.ReadHeaderNumber(fb.db, hash) - if number == nil { - return nil, nil - } - header := rawdb.ReadHeader(fb.db, hash, *number) - if header == nil { - return nil, nil - } - return rawdb.ReadReceipts(fb.db, hash, *number, header.Time, fb.bc.Config()), nil -} - -func (fb *filterBackend) GetLogs(ctx context.Context, hash common.Hash, number uint64) ([][]*types.Log, error) { - logs := rawdb.ReadLogs(fb.db, hash, number) - return logs, nil -} - -func (fb *filterBackend) SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) event.Subscription { - return nullSubscription() -} - -func (fb *filterBackend) SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription { - return fb.bc.SubscribeChainEvent(ch) -} - -func (fb *filterBackend) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription { - return fb.bc.SubscribeRemovedLogsEvent(ch) -} - -func (fb *filterBackend) SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription { - return fb.bc.SubscribeLogsEvent(ch) -} - -func (fb *filterBackend) SubscribePendingLogsEvent(ch chan<- []*types.Log) event.Subscription { - return nullSubscription() -} - -func (fb *filterBackend) BloomStatus() (uint64, uint64) { return 4096, 0 } - -func (fb *filterBackend) ServiceFilter(ctx context.Context, ms *bloombits.MatcherSession) { - panic("not supported") -} - -func (fb *filterBackend) ChainConfig() *params.ChainConfig { - panic("not supported") -} - -func (fb *filterBackend) CurrentHeader() *types.Header { - panic("not supported") -} - -func nullSubscription() event.Subscription { - return event.NewSubscription(func(quit <-chan struct{}) error { - <-quit - return nil - }) } diff --git a/accounts/abi/bind/backends/simulated_test.go b/accounts/abi/bind/backends/simulated_test.go deleted file mode 100644 index dd99f55d7e..0000000000 --- a/accounts/abi/bind/backends/simulated_test.go +++ /dev/null @@ -1,1498 +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 2019 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 backends - -import ( - "bytes" - "context" - "errors" - "math/big" - "math/rand" - "reflect" - "strings" - "testing" - "time" - - "github.com/ava-labs/subnet-evm/accounts/abi" - "github.com/ava-labs/subnet-evm/accounts/abi/bind" - "github.com/ava-labs/subnet-evm/core" - "github.com/ava-labs/subnet-evm/core/types" - "github.com/ava-labs/subnet-evm/interfaces" - "github.com/ava-labs/subnet-evm/params" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/crypto" -) - -func TestSimulatedBackend(t *testing.T) { - t.Parallel() - var gasLimit uint64 = 8000029 - key, _ := crypto.GenerateKey() // nolint: gosec - auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) - genAlloc := make(core.GenesisAlloc) - genAlloc[auth.From] = core.GenesisAccount{Balance: big.NewInt(9223372036854775807)} - - sim := NewSimulatedBackend(genAlloc, gasLimit) - defer sim.Close() - - // should return an error if the tx is not found - txHash := common.HexToHash("2") - _, isPending, err := sim.TransactionByHash(context.Background(), txHash) - - if isPending { - t.Fatal("transaction should not be pending") - } - if err != interfaces.NotFound { - t.Fatalf("err should be `interfaces.NotFound` but received %v", err) - } - - // generate a transaction and confirm you can retrieve it - head, _ := sim.HeaderByNumber(context.Background(), nil) // Should be child's, good enough - gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1)) - - code := `6060604052600a8060106000396000f360606040526008565b00` - var gas uint64 = 3000000 - tx := types.NewContractCreation(0, big.NewInt(0), gas, gasPrice, common.FromHex(code)) - tx, _ = types.SignTx(tx, types.HomesteadSigner{}, key) - - err = sim.SendTransaction(context.Background(), tx) - if err != nil { - t.Fatal("error sending transaction") - } - - txHash = tx.Hash() - _, isPending, err = sim.TransactionByHash(context.Background(), txHash) - if err != nil { - t.Fatalf("error getting transaction with hash: %v", txHash.String()) - } - if !isPending { - t.Fatal("transaction should have pending status") - } - - sim.Commit(true) - _, isPending, err = sim.TransactionByHash(context.Background(), txHash) - if err != nil { - t.Fatalf("error getting transaction with hash: %v", txHash.String()) - } - if isPending { - t.Fatal("transaction should not have pending status") - } -} - -var testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") - -// the following is based on this contract: -// -// contract T { -// event received(address sender, uint amount, bytes memo); -// event receivedAddr(address sender); -// -// function receive(bytes calldata memo) external payable returns (string memory res) { -// emit received(msg.sender, msg.value, memo); -// emit receivedAddr(msg.sender); -// return "hello world"; -// } -// } -const abiJSON = `[ { "constant": false, "inputs": [ { "name": "memo", "type": "bytes" } ], "name": "receive", "outputs": [ { "name": "res", "type": "string" } ], "payable": true, "stateMutability": "payable", "type": "function" }, { "anonymous": false, "inputs": [ { "indexed": false, "name": "sender", "type": "address" }, { "indexed": false, "name": "amount", "type": "uint256" }, { "indexed": false, "name": "memo", "type": "bytes" } ], "name": "received", "type": "event" }, { "anonymous": false, "inputs": [ { "indexed": false, "name": "sender", "type": "address" } ], "name": "receivedAddr", "type": "event" } ]` -const abiBin = `0x608060405234801561001057600080fd5b506102a0806100206000396000f3fe60806040526004361061003b576000357c010000000000000000000000000000000000000000000000000000000090048063a69b6ed014610040575b600080fd5b6100b76004803603602081101561005657600080fd5b810190808035906020019064010000000081111561007357600080fd5b82018360208201111561008557600080fd5b803590602001918460018302840111640100000000831117156100a757600080fd5b9091929391929390505050610132565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156100f75780820151818401526020810190506100dc565b50505050905090810190601f1680156101245780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b60607f75fd880d39c1daf53b6547ab6cb59451fc6452d27caa90e5b6649dd8293b9eed33348585604051808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001848152602001806020018281038252848482818152602001925080828437600081840152601f19601f8201169050808301925050509550505050505060405180910390a17f46923992397eac56cf13058aced2a1871933622717e27b24eabc13bf9dd329c833604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a16040805190810160405280600b81526020017f68656c6c6f20776f726c6400000000000000000000000000000000000000000081525090509291505056fea165627a7a72305820ff0c57dad254cfeda48c9cfb47f1353a558bccb4d1bc31da1dae69315772d29e0029` -const deployedCode = `60806040526004361061003b576000357c010000000000000000000000000000000000000000000000000000000090048063a69b6ed014610040575b600080fd5b6100b76004803603602081101561005657600080fd5b810190808035906020019064010000000081111561007357600080fd5b82018360208201111561008557600080fd5b803590602001918460018302840111640100000000831117156100a757600080fd5b9091929391929390505050610132565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156100f75780820151818401526020810190506100dc565b50505050905090810190601f1680156101245780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b60607f75fd880d39c1daf53b6547ab6cb59451fc6452d27caa90e5b6649dd8293b9eed33348585604051808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001848152602001806020018281038252848482818152602001925080828437600081840152601f19601f8201169050808301925050509550505050505060405180910390a17f46923992397eac56cf13058aced2a1871933622717e27b24eabc13bf9dd329c833604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a16040805190810160405280600b81526020017f68656c6c6f20776f726c6400000000000000000000000000000000000000000081525090509291505056fea165627a7a72305820ff0c57dad254cfeda48c9cfb47f1353a558bccb4d1bc31da1dae69315772d29e0029` - -// expected return value contains "hello world" -var expectedReturn = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} - -func simTestBackend(testAddr common.Address) *SimulatedBackend { - return NewSimulatedBackend( - core.GenesisAlloc{ - testAddr: {Balance: new(big.Int).Mul(big.NewInt(10000000000000000), big.NewInt(1000))}, - }, 10000000, - ) -} - -func TestNewSimulatedBackend(t *testing.T) { - t.Parallel() - testAddr := crypto.PubkeyToAddress(testKey.PublicKey) - expectedBal := new(big.Int).Mul(big.NewInt(10000000000000000), big.NewInt(1000)) - sim := simTestBackend(testAddr) - defer sim.Close() - - stateDB, _ := sim.blockchain.State() - bal := stateDB.GetBalance(testAddr) - if bal.Cmp(expectedBal) != 0 { - t.Errorf("expected balance for test address not received. expected: %v actual: %v", expectedBal, bal) - } -} - -func TestAdjustTime(t *testing.T) { - t.Parallel() - sim := NewSimulatedBackend( - core.GenesisAlloc{}, 10000000, - ) - defer sim.Close() - - prevTime := sim.acceptedBlock.Time() - if err := sim.AdjustTime(time.Second); err != nil { - t.Error(err) - } - newTime := sim.acceptedBlock.Time() - - if newTime-prevTime != uint64(time.Second.Seconds()) { - t.Errorf("adjusted time not equal to a second. prev: %v, new: %v", prevTime, newTime) - } -} - -func TestNewAdjustTimeFail(t *testing.T) { - t.Parallel() - testAddr := crypto.PubkeyToAddress(testKey.PublicKey) - sim := simTestBackend(testAddr) - defer sim.blockchain.Stop() - - // Create tx and send - head, _ := sim.HeaderByNumber(context.Background(), 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) - signer := types.NewLondonSigner(big.NewInt(1337)) - signedTx, err := types.SignTx(tx, signer, testKey) - if err != nil { - t.Errorf("could not sign tx: %v", err) - } - sim.SendTransaction(context.Background(), signedTx) - // AdjustTime should fail on non-empty block - if err := sim.AdjustTime(time.Second); err == nil { - t.Error("Expected adjust time to error on non-empty block") - } - sim.Commit(false) - - prevTime := sim.acceptedBlock.Time() - if err := sim.AdjustTime(time.Minute); err != nil { - t.Error(err) - } - newTime := sim.acceptedBlock.Time() - if newTime-prevTime != uint64(time.Minute.Seconds()) { - t.Errorf("adjusted time not equal to a minute. prev: %v, new: %v", prevTime, newTime) - } - // Put a transaction after adjusting time - tx2 := types.NewTransaction(1, testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil) - signer = types.NewLondonSigner(big.NewInt(1337)) - signedTx2, err := types.SignTx(tx2, signer, testKey) - if err != nil { - t.Errorf("could not sign tx: %v", err) - } - sim.SendTransaction(context.Background(), signedTx2) - sim.Commit(false) - newTime = sim.acceptedBlock.Time() - if newTime-prevTime >= uint64(time.Minute.Seconds()) { - t.Errorf("time adjusted, but shouldn't be: prev: %v, new: %v", prevTime, newTime) - } -} - -func TestBalanceAt(t *testing.T) { - t.Parallel() - testAddr := crypto.PubkeyToAddress(testKey.PublicKey) - expectedBal := new(big.Int).Mul(big.NewInt(10000000000000000), big.NewInt(1000)) - sim := simTestBackend(testAddr) - defer sim.Close() - bgCtx := context.Background() - - bal, err := sim.BalanceAt(bgCtx, testAddr, nil) - if err != nil { - t.Error(err) - } - - if bal.Cmp(expectedBal) != 0 { - t.Errorf("expected balance for test address not received. expected: %v actual: %v", expectedBal, bal) - } -} - -func TestBlockByHash(t *testing.T) { - t.Parallel() - sim := NewSimulatedBackend( - core.GenesisAlloc{}, 10000000, - ) - defer sim.Close() - bgCtx := context.Background() - - block, err := sim.BlockByNumber(bgCtx, nil) - if err != nil { - t.Errorf("could not get recent block: %v", err) - } - blockByHash, err := sim.BlockByHash(bgCtx, block.Hash()) - if err != nil { - t.Errorf("could not get recent block: %v", err) - } - - if block.Hash() != blockByHash.Hash() { - t.Errorf("did not get expected block") - } -} - -func TestBlockByNumber(t *testing.T) { - t.Parallel() - sim := NewSimulatedBackend( - core.GenesisAlloc{}, 10000000, - ) - defer sim.Close() - bgCtx := context.Background() - - block, err := sim.BlockByNumber(bgCtx, nil) - if err != nil { - t.Errorf("could not get recent block: %v", err) - } - if block.NumberU64() != 0 { - t.Errorf("did not get most recent block, instead got block number %v", block.NumberU64()) - } - - // create one block - sim.Commit(false) - - block, err = sim.BlockByNumber(bgCtx, nil) - if err != nil { - t.Errorf("could not get recent block: %v", err) - } - if block.NumberU64() != 1 { - t.Errorf("did not get most recent block, instead got block number %v", block.NumberU64()) - } - - blockByNumber, err := sim.BlockByNumber(bgCtx, big.NewInt(1)) - if err != nil { - t.Errorf("could not get block by number: %v", err) - } - if blockByNumber.Hash() != block.Hash() { - t.Errorf("did not get the same block with height of 1 as before") - } -} - -func TestNonceAt(t *testing.T) { - t.Parallel() - testAddr := crypto.PubkeyToAddress(testKey.PublicKey) - - sim := simTestBackend(testAddr) - defer sim.Close() - bgCtx := context.Background() - - nonce, err := sim.NonceAt(bgCtx, testAddr, big.NewInt(0)) - if err != nil { - t.Errorf("could not get nonce for test addr: %v", err) - } - - if nonce != uint64(0) { - t.Errorf("received incorrect nonce. expected 0, got %v", nonce) - } - - // create a signed transaction to send - head, _ := sim.HeaderByNumber(context.Background(), nil) // Should be child's, good enough - gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1)) - - tx := types.NewTransaction(nonce, testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil) - signer := types.NewLondonSigner(big.NewInt(1337)) - signedTx, err := types.SignTx(tx, signer, testKey) - if err != nil { - t.Errorf("could not sign tx: %v", err) - } - - // send tx to simulated backend - err = sim.SendTransaction(bgCtx, signedTx) - if err != nil { - t.Errorf("could not add tx to pending block: %v", err) - } - sim.Commit(false) - - newNonce, err := sim.NonceAt(bgCtx, testAddr, big.NewInt(1)) - if err != nil { - t.Errorf("could not get nonce for test addr: %v", err) - } - - if newNonce != nonce+uint64(1) { - t.Errorf("received incorrect nonce. expected 1, got %v", nonce) - } - // create some more blocks - sim.Commit(false) - // Check that we can get data for an older block/state - newNonce, err = sim.NonceAt(bgCtx, testAddr, big.NewInt(1)) - if err != nil { - t.Fatalf("could not get nonce for test addr: %v", err) - } - if newNonce != nonce+uint64(1) { - t.Fatalf("received incorrect nonce. expected 1, got %v", nonce) - } -} - -func TestSendTransaction(t *testing.T) { - t.Parallel() - testAddr := crypto.PubkeyToAddress(testKey.PublicKey) - - sim := simTestBackend(testAddr) - defer sim.Close() - bgCtx := context.Background() - - // create a signed transaction to send - head, _ := sim.HeaderByNumber(context.Background(), nil) // Should be child's, good enough - gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1)) - - tx := types.NewTransaction(uint64(0), testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil) - signer := types.NewLondonSigner(big.NewInt(1337)) - signedTx, err := types.SignTx(tx, signer, testKey) - if err != nil { - t.Errorf("could not sign tx: %v", err) - } - - // send tx to simulated backend - err = sim.SendTransaction(bgCtx, signedTx) - if err != nil { - t.Errorf("could not add tx to pending block: %v", err) - } - sim.Commit(false) - - block, err := sim.BlockByNumber(bgCtx, big.NewInt(1)) - if err != nil { - t.Errorf("could not get block at height 1: %v", err) - } - - if signedTx.Hash() != block.Transactions()[0].Hash() { - t.Errorf("did not commit sent transaction. expected hash %v got hash %v", block.Transactions()[0].Hash(), signedTx.Hash()) - } -} - -func TestTransactionByHash(t *testing.T) { - t.Parallel() - testAddr := crypto.PubkeyToAddress(testKey.PublicKey) - - sim := NewSimulatedBackend( - core.GenesisAlloc{ - testAddr: {Balance: new(big.Int).Mul(big.NewInt(10000000000000000), big.NewInt(1000))}, - }, 10000000, - ) - defer sim.Close() - bgCtx := context.Background() - - // create a signed transaction to send - head, _ := sim.HeaderByNumber(context.Background(), nil) // Should be child's, good enough - gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1)) - - tx := types.NewTransaction(uint64(0), testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil) - signer := types.NewLondonSigner(big.NewInt(1337)) - signedTx, err := types.SignTx(tx, signer, testKey) - if err != nil { - t.Errorf("could not sign tx: %v", err) - } - - // send tx to simulated backend - err = sim.SendTransaction(bgCtx, signedTx) - if err != nil { - t.Errorf("could not add tx to pending block: %v", err) - } - - // ensure tx is committed pending - receivedTx, pending, err := sim.TransactionByHash(bgCtx, signedTx.Hash()) - if err != nil { - t.Errorf("could not get transaction by hash %v: %v", signedTx.Hash(), err) - } - if !pending { - t.Errorf("expected transaction to be in pending state") - } - if receivedTx.Hash() != signedTx.Hash() { - t.Errorf("did not received committed transaction. expected hash %v got hash %v", signedTx.Hash(), receivedTx.Hash()) - } - - sim.Commit(true) - - // ensure tx is not and committed pending - receivedTx, pending, err = sim.TransactionByHash(bgCtx, signedTx.Hash()) - if err != nil { - t.Errorf("could not get transaction by hash %v: %v", signedTx.Hash(), err) - } - if pending { - t.Errorf("expected transaction to not be in pending state") - } - if receivedTx.Hash() != signedTx.Hash() { - t.Errorf("did not received committed transaction. expected hash %v got hash %v", signedTx.Hash(), receivedTx.Hash()) - } -} - -func TestEstimateGas(t *testing.T) { - t.Parallel() - /* - pragma solidity ^0.6.4; - contract GasEstimation { - function PureRevert() public { revert(); } - function Revert() public { revert("revert reason");} - function OOG() public { for (uint i = 0; ; i++) {}} - function Assert() public { assert(false);} - function Valid() public {} - } - */ - const contractAbi = "[{\"inputs\":[],\"name\":\"Assert\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"OOG\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"PureRevert\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"Revert\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"Valid\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]" - const contractBin = "0x60806040523480156100115760006000fd5b50610017565b61016e806100266000396000f3fe60806040523480156100115760006000fd5b506004361061005c5760003560e01c806350f6fe3414610062578063aa8b1d301461006c578063b9b046f914610076578063d8b9839114610080578063e09fface1461008a5761005c565b60006000fd5b61006a610094565b005b6100746100ad565b005b61007e6100b5565b005b6100886100c2565b005b610092610135565b005b6000600090505b5b808060010191505061009b565b505b565b60006000fd5b565b600015156100bf57fe5b5b565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600d8152602001807f72657665727420726561736f6e0000000000000000000000000000000000000081526020015060200191505060405180910390fd5b565b5b56fea2646970667358221220345bbcbb1a5ecf22b53a78eaebf95f8ee0eceff6d10d4b9643495084d2ec934a64736f6c63430006040033" - - key, _ := crypto.GenerateKey() - addr := crypto.PubkeyToAddress(key.PublicKey) - opts, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) - - sim := NewSimulatedBackend(core.GenesisAlloc{addr: {Balance: big.NewInt(params.Ether)}}, 10000000) - defer sim.Close() - - parsed, _ := abi.JSON(strings.NewReader(contractAbi)) - contractAddr, _, _, _ := bind.DeployContract(opts, parsed, common.FromHex(contractBin), sim) - sim.Commit(false) - - var cases = []struct { - name string - message interfaces.CallMsg - expect uint64 - expectError error - expectData interface{} - }{ - {"plain transfer(valid)", interfaces.CallMsg{ - From: addr, - To: &addr, - Gas: 0, - GasPrice: big.NewInt(0), - Value: big.NewInt(1), - Data: nil, - }, params.TxGas, nil, nil}, - - {"plain transfer(invalid)", interfaces.CallMsg{ - From: addr, - To: &contractAddr, - Gas: 0, - GasPrice: big.NewInt(0), - Value: big.NewInt(1), - Data: nil, - }, 0, errors.New("execution reverted"), nil}, - - {"Revert", interfaces.CallMsg{ - From: addr, - To: &contractAddr, - Gas: 0, - GasPrice: big.NewInt(0), - Value: nil, - Data: common.Hex2Bytes("d8b98391"), - }, 0, errors.New("execution reverted: revert reason"), "0x08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d72657665727420726561736f6e00000000000000000000000000000000000000"}, - - {"PureRevert", interfaces.CallMsg{ - From: addr, - To: &contractAddr, - Gas: 0, - GasPrice: big.NewInt(0), - Value: nil, - Data: common.Hex2Bytes("aa8b1d30"), - }, 0, errors.New("execution reverted"), nil}, - - {"OOG", interfaces.CallMsg{ - From: addr, - To: &contractAddr, - Gas: 100000, - GasPrice: big.NewInt(0), - Value: nil, - Data: common.Hex2Bytes("50f6fe34"), - }, 0, errors.New("gas required exceeds allowance (100000)"), nil}, - - {"Assert", interfaces.CallMsg{ - From: addr, - To: &contractAddr, - Gas: 100000, - GasPrice: big.NewInt(0), - Value: nil, - Data: common.Hex2Bytes("b9b046f9"), - }, 0, errors.New("invalid opcode: INVALID"), nil}, - - {"Valid", interfaces.CallMsg{ - From: addr, - To: &contractAddr, - Gas: 100000, - GasPrice: big.NewInt(0), - Value: nil, - Data: common.Hex2Bytes("e09fface"), - }, 21275, nil, nil}, - } - for _, c := range cases { - got, err := sim.EstimateGas(context.Background(), c.message) - if c.expectError != nil { - if err == nil { - t.Fatalf("Expect error, got nil") - } - if c.expectError.Error() != err.Error() { - t.Fatalf("Expect error, want %v, got %v", c.expectError, err) - } - if c.expectData != nil { - if err, ok := err.(*revertError); !ok { - t.Fatalf("Expect revert error, got %T", err) - } else if !reflect.DeepEqual(err.ErrorData(), c.expectData) { - t.Fatalf("Error data mismatch, want %v, got %v", c.expectData, err.ErrorData()) - } - } - continue - } - if got != c.expect { - t.Fatalf("Gas estimation mismatch, want %d, got %d", c.expect, got) - } - } -} - -func TestEstimateGasWithPrice(t *testing.T) { - t.Parallel() - key, _ := crypto.GenerateKey() - addr := crypto.PubkeyToAddress(key.PublicKey) - - sim := NewSimulatedBackend(core.GenesisAlloc{addr: {Balance: big.NewInt(params.Ether*2 + 2e17)}}, 10000000) - defer sim.Close() - - recipient := common.HexToAddress("deadbeef") - var cases = []struct { - name string - message interfaces.CallMsg - expect uint64 - expectError error - }{ - {"EstimateWithoutPrice", interfaces.CallMsg{ - From: addr, - To: &recipient, - Gas: 0, - GasPrice: big.NewInt(0), - Value: big.NewInt(100000000000), - Data: nil, - }, 21000, nil}, - - {"EstimateWithPrice", interfaces.CallMsg{ - From: addr, - To: &recipient, - Gas: 0, - GasPrice: big.NewInt(225000000000), - Value: big.NewInt(100000000000), - Data: nil, - }, 21000, nil}, - - {"EstimateWithVeryHighPrice", interfaces.CallMsg{ - From: addr, - To: &recipient, - Gas: 0, - GasPrice: big.NewInt(1e14), // gascost = 2.1ether - Value: big.NewInt(1e17), // the remaining balance for fee is 2.1ether - Data: nil, - }, 21000, nil}, - - {"EstimateWithSuperhighPrice", interfaces.CallMsg{ - From: addr, - To: &recipient, - Gas: 0, - GasPrice: big.NewInt(2e14), // gascost = 4.2ether - Value: big.NewInt(100000000000), - Data: nil, - }, 21000, errors.New("gas required exceeds allowance (10999)")}, // 10999=(2.2ether-1000wei)/(2e14) - - {"EstimateEIP1559WithHighFees", interfaces.CallMsg{ - From: addr, - To: &addr, - Gas: 0, - GasFeeCap: big.NewInt(1e14), // maxgascost = 2.1ether - GasTipCap: big.NewInt(1), - Value: big.NewInt(1e17), // the remaining balance for fee is 2.1ether - Data: nil, - }, params.TxGas, nil}, - - {"EstimateEIP1559WithSuperHighFees", interfaces.CallMsg{ - From: addr, - To: &addr, - Gas: 0, - GasFeeCap: big.NewInt(1e14), // maxgascost = 2.1ether - GasTipCap: big.NewInt(1), - Value: big.NewInt(1e17 + 1), // the remaining balance for fee is 2.1ether - Data: nil, - }, params.TxGas, errors.New("gas required exceeds allowance (20999)")}, // 20999=(2.2ether-0.1ether-1wei)/(1e14) - } - for i, c := range cases { - got, err := sim.EstimateGas(context.Background(), c.message) - if c.expectError != nil { - if err == nil { - t.Fatalf("test %d: expect error, got nil", i) - } - if c.expectError.Error() != err.Error() { - t.Fatalf("test %d: expect error, want %v, got %v", i, c.expectError, err) - } - continue - } - if c.expectError == nil && err != nil { - t.Fatalf("test %d: didn't expect error, got %v", i, err) - } - if got != c.expect { - t.Fatalf("test %d: gas estimation mismatch, want %d, got %d", i, c.expect, got) - } - } -} - -func TestHeaderByHash(t *testing.T) { - t.Parallel() - testAddr := crypto.PubkeyToAddress(testKey.PublicKey) - - sim := simTestBackend(testAddr) - defer sim.Close() - bgCtx := context.Background() - - header, err := sim.HeaderByNumber(bgCtx, nil) - if err != nil { - t.Errorf("could not get recent block: %v", err) - } - headerByHash, err := sim.HeaderByHash(bgCtx, header.Hash()) - if err != nil { - t.Errorf("could not get recent block: %v", err) - } - - if header.Hash() != headerByHash.Hash() { - t.Errorf("did not get expected block") - } -} - -func TestHeaderByNumber(t *testing.T) { - t.Parallel() - testAddr := crypto.PubkeyToAddress(testKey.PublicKey) - - sim := simTestBackend(testAddr) - defer sim.Close() - bgCtx := context.Background() - - latestBlockHeader, err := sim.HeaderByNumber(bgCtx, nil) - if err != nil { - t.Errorf("could not get header for tip of chain: %v", err) - } - if latestBlockHeader == nil { - t.Errorf("received a nil block header") - } else if latestBlockHeader.Number.Uint64() != uint64(0) { - t.Errorf("expected block header number 0, instead got %v", latestBlockHeader.Number.Uint64()) - } - - sim.Commit(false) - - latestBlockHeader, err = sim.HeaderByNumber(bgCtx, nil) - if err != nil { - t.Errorf("could not get header for blockheight of 1: %v", err) - } - - blockHeader, err := sim.HeaderByNumber(bgCtx, big.NewInt(1)) - if err != nil { - t.Errorf("could not get header for blockheight of 1: %v", err) - } - - if blockHeader.Hash() != latestBlockHeader.Hash() { - t.Errorf("block header and latest block header are not the same") - } - if blockHeader.Number.Int64() != int64(1) { - t.Errorf("did not get blockheader for block 1. instead got block %v", blockHeader.Number.Int64()) - } - - block, err := sim.BlockByNumber(bgCtx, big.NewInt(1)) - if err != nil { - t.Errorf("could not get block for blockheight of 1: %v", err) - } - - if block.Hash() != blockHeader.Hash() { - t.Errorf("block hash and block header hash do not match. expected %v, got %v", block.Hash(), blockHeader.Hash()) - } -} - -func TestTransactionCount(t *testing.T) { - t.Parallel() - testAddr := crypto.PubkeyToAddress(testKey.PublicKey) - - sim := simTestBackend(testAddr) - defer sim.Close() - bgCtx := context.Background() - currentBlock, err := sim.BlockByNumber(bgCtx, nil) - if err != nil || currentBlock == nil { - t.Error("could not get current block") - } - - count, err := sim.TransactionCount(bgCtx, currentBlock.Hash()) - if err != nil { - t.Error("could not get current block's transaction count") - } - - if count != 0 { - t.Errorf("expected transaction count of %v does not match actual count of %v", 0, count) - } - // create a signed transaction to send - head, _ := sim.HeaderByNumber(context.Background(), nil) // Should be child's, good enough - gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1)) - - tx := types.NewTransaction(uint64(0), testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil) - signer := types.NewLondonSigner(big.NewInt(1337)) - signedTx, err := types.SignTx(tx, signer, testKey) - if err != nil { - t.Errorf("could not sign tx: %v", err) - } - - // send tx to simulated backend - err = sim.SendTransaction(bgCtx, signedTx) - if err != nil { - t.Errorf("could not add tx to pending block: %v", err) - } - - sim.Commit(false) - - lastBlock, err := sim.BlockByNumber(bgCtx, nil) - if err != nil { - t.Errorf("could not get header for tip of chain: %v", err) - } - - count, err = sim.TransactionCount(bgCtx, lastBlock.Hash()) - if err != nil { - t.Error("could not get current block's transaction count") - } - - if count != 1 { - t.Errorf("expected transaction count of %v does not match actual count of %v", 1, count) - } -} - -func TestTransactionInBlock(t *testing.T) { - t.Parallel() - testAddr := crypto.PubkeyToAddress(testKey.PublicKey) - - sim := simTestBackend(testAddr) - defer sim.Close() - bgCtx := context.Background() - - transaction, err := sim.TransactionInBlock(bgCtx, sim.acceptedBlock.Hash(), uint(0)) - if err == nil && err != errTransactionDoesNotExist { - t.Errorf("expected a transaction does not exist error to be received but received %v", err) - } - if transaction != nil { - t.Errorf("expected transaction to be nil but received %v", transaction) - } - - // expect accepted nonce to be 0 since account has not been used - acceptedNonce, err := sim.AcceptedNonceAt(bgCtx, testAddr) - if err != nil { - t.Errorf("did not get the pending nonce: %v", err) - } - - if acceptedNonce != uint64(0) { - t.Errorf("expected pending nonce of 0 got %v", acceptedNonce) - } - // create a signed transaction to send - head, _ := sim.HeaderByNumber(context.Background(), nil) // Should be child's, good enough - gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1)) - - tx := types.NewTransaction(uint64(0), testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil) - signer := types.NewLondonSigner(big.NewInt(1337)) - signedTx, err := types.SignTx(tx, signer, testKey) - if err != nil { - t.Errorf("could not sign tx: %v", err) - } - - // send tx to simulated backend - err = sim.SendTransaction(bgCtx, signedTx) - if err != nil { - t.Errorf("could not add tx to pending block: %v", err) - } - - sim.Commit(false) - - lastBlock, err := sim.BlockByNumber(bgCtx, nil) - if err != nil { - t.Errorf("could not get header for tip of chain: %v", err) - } - - transaction, err = sim.TransactionInBlock(bgCtx, lastBlock.Hash(), uint(1)) - if err == nil && err != errTransactionDoesNotExist { - t.Errorf("expected a transaction does not exist error to be received but received %v", err) - } - if transaction != nil { - t.Errorf("expected transaction to be nil but received %v", transaction) - } - - transaction, err = sim.TransactionInBlock(bgCtx, lastBlock.Hash(), uint(0)) - if err != nil { - t.Errorf("could not get transaction in the lastest block with hash %v: %v", lastBlock.Hash().String(), err) - } - - if signedTx.Hash().String() != transaction.Hash().String() { - t.Errorf("received transaction that did not match the sent transaction. expected hash %v, got hash %v", signedTx.Hash().String(), transaction.Hash().String()) - } -} - -func TestAcceptedNonceAt(t *testing.T) { - t.Parallel() - testAddr := crypto.PubkeyToAddress(testKey.PublicKey) - - sim := simTestBackend(testAddr) - defer sim.Close() - bgCtx := context.Background() - - // expect accepted nonce to be 0 since account has not been used - acceptedNonce, err := sim.AcceptedNonceAt(bgCtx, testAddr) - if err != nil { - t.Errorf("did not get the accepted nonce: %v", err) - } - - if acceptedNonce != uint64(0) { - t.Errorf("expected accepted nonce of 0 got %v", acceptedNonce) - } - - // create a signed transaction to send - head, _ := sim.HeaderByNumber(context.Background(), nil) // Should be child's, good enough - gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1)) - - tx := types.NewTransaction(uint64(0), testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil) - signer := types.NewLondonSigner(big.NewInt(1337)) - signedTx, err := types.SignTx(tx, signer, testKey) - if err != nil { - t.Errorf("could not sign tx: %v", err) - } - - // send tx to simulated backend - err = sim.SendTransaction(bgCtx, signedTx) - if err != nil { - t.Errorf("could not add tx to pending block: %v", err) - } - - // expect accepted nonce to be 1 since account has submitted one transaction - acceptedNonce, err = sim.AcceptedNonceAt(bgCtx, testAddr) - if err != nil { - t.Errorf("did not get the accepted nonce: %v", err) - } - - if acceptedNonce != uint64(1) { - t.Errorf("expected accepted nonce of 1 got %v", acceptedNonce) - } - - // make a new transaction with a nonce of 1 - tx = types.NewTransaction(uint64(1), testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil) - signer = types.NewLondonSigner(big.NewInt(1337)) - signedTx, err = types.SignTx(tx, signer, testKey) - if err != nil { - t.Errorf("could not sign tx: %v", err) - } - err = sim.SendTransaction(bgCtx, signedTx) - if err != nil { - t.Errorf("could not send tx: %v", err) - } - - // expect accepted nonce to be 2 since account now has two transactions - acceptedNonce, err = sim.AcceptedNonceAt(bgCtx, testAddr) - if err != nil { - t.Errorf("did not get the accepted nonce: %v", err) - } - - if acceptedNonce != uint64(2) { - t.Errorf("expected accepted nonce of 2 got %v", acceptedNonce) - } -} - -func TestTransactionReceipt(t *testing.T) { - t.Parallel() - testAddr := crypto.PubkeyToAddress(testKey.PublicKey) - - sim := simTestBackend(testAddr) - defer sim.Close() - bgCtx := context.Background() - - // create a signed transaction to send - head, _ := sim.HeaderByNumber(context.Background(), nil) // Should be child's, good enough - gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1)) - - tx := types.NewTransaction(uint64(0), testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil) - signer := types.NewLondonSigner(big.NewInt(1337)) - signedTx, err := types.SignTx(tx, signer, testKey) - if err != nil { - t.Errorf("could not sign tx: %v", err) - } - - // send tx to simulated backend - err = sim.SendTransaction(bgCtx, signedTx) - if err != nil { - t.Errorf("could not add tx to pending block: %v", err) - } - sim.Commit(true) - - receipt, err := sim.TransactionReceipt(bgCtx, signedTx.Hash()) - if err != nil { - t.Errorf("could not get transaction receipt: %v", err) - } - - if receipt.ContractAddress != testAddr && receipt.TxHash != signedTx.Hash() { - t.Errorf("received receipt is not correct: %v", receipt) - } -} - -func TestSuggestGasPrice(t *testing.T) { - t.Parallel() - sim := NewSimulatedBackend( - core.GenesisAlloc{}, - 10000000, - ) - defer sim.Close() - bgCtx := context.Background() - gasPrice, err := sim.SuggestGasPrice(bgCtx) - if err != nil { - t.Errorf("could not get gas price: %v", err) - } - if gasPrice.Uint64() != sim.acceptedBlock.Header().BaseFee.Uint64() { - t.Errorf("gas price was not expected value of %v. actual: %v", sim.acceptedBlock.Header().BaseFee.Uint64(), gasPrice.Uint64()) - } -} - -func TestAcceptedCodeAt(t *testing.T) { - t.Parallel() - testAddr := crypto.PubkeyToAddress(testKey.PublicKey) - sim := simTestBackend(testAddr) - defer sim.Close() - bgCtx := context.Background() - code, err := sim.CodeAt(bgCtx, testAddr, nil) - if err != nil { - t.Errorf("could not get code at test addr: %v", err) - } - if len(code) != 0 { - t.Errorf("got code for account that does not have contract code") - } - - parsed, err := abi.JSON(strings.NewReader(abiJSON)) - if err != nil { - t.Errorf("could not get code at test addr: %v", err) - } - auth, _ := bind.NewKeyedTransactorWithChainID(testKey, big.NewInt(1337)) - contractAddr, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex(abiBin), sim) - if err != nil { - t.Errorf("could not deploy contract: %v tx: %v contract: %v", err, tx, contract) - } - - code, err = sim.AcceptedCodeAt(bgCtx, contractAddr) - if err != nil { - t.Errorf("could not get code at test addr: %v", err) - } - if len(code) == 0 { - t.Errorf("did not get code for account that has contract code") - } - // ensure code received equals code deployed - if !bytes.Equal(code, common.FromHex(deployedCode)) { - t.Errorf("code received did not match expected deployed code:\n expected %v\n actual %v", common.FromHex(deployedCode), code) - } -} - -func TestCodeAt(t *testing.T) { - t.Parallel() - testAddr := crypto.PubkeyToAddress(testKey.PublicKey) - sim := simTestBackend(testAddr) - defer sim.Close() - bgCtx := context.Background() - code, err := sim.CodeAt(bgCtx, testAddr, nil) - if err != nil { - t.Errorf("could not get code at test addr: %v", err) - } - if len(code) != 0 { - t.Errorf("got code for account that does not have contract code") - } - - parsed, err := abi.JSON(strings.NewReader(abiJSON)) - if err != nil { - t.Errorf("could not get code at test addr: %v", err) - } - auth, _ := bind.NewKeyedTransactorWithChainID(testKey, big.NewInt(1337)) - contractAddr, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex(abiBin), sim) - if err != nil { - t.Errorf("could not deploy contract: %v tx: %v contract: %v", err, tx, contract) - } - - sim.Commit(false) - code, err = sim.CodeAt(bgCtx, contractAddr, nil) - if err != nil { - t.Errorf("could not get code at test addr: %v", err) - } - if len(code) == 0 { - t.Errorf("did not get code for account that has contract code") - } - // ensure code received equals code deployed - if !bytes.Equal(code, common.FromHex(deployedCode)) { - t.Errorf("code received did not match expected deployed code:\n expected %v\n actual %v", common.FromHex(deployedCode), code) - } -} - -func TestCodeAtHash(t *testing.T) { - t.Parallel() - testAddr := crypto.PubkeyToAddress(testKey.PublicKey) - sim := simTestBackend(testAddr) - defer sim.Close() - bgCtx := context.Background() - code, err := sim.CodeAtHash(bgCtx, testAddr, sim.Blockchain().CurrentHeader().Hash()) - if err != nil { - t.Errorf("could not get code at test addr: %v", err) - } - if len(code) != 0 { - t.Errorf("got code for account that does not have contract code") - } - - parsed, err := abi.JSON(strings.NewReader(abiJSON)) - if err != nil { - t.Errorf("could not get code at test addr: %v", err) - } - auth, _ := bind.NewKeyedTransactorWithChainID(testKey, big.NewInt(1337)) - contractAddr, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex(abiBin), sim) - if err != nil { - t.Errorf("could not deploy contract: %v tx: %v contract: %v", err, tx, contract) - } - - blockHash := sim.Commit(true) - code, err = sim.CodeAtHash(bgCtx, contractAddr, blockHash) - if err != nil { - t.Errorf("could not get code at test addr: %v", err) - } - if len(code) == 0 { - t.Errorf("did not get code for account that has contract code") - } - // ensure code received equals code deployed - if !bytes.Equal(code, common.FromHex(deployedCode)) { - t.Errorf("code received did not match expected deployed code:\n expected %v\n actual %v", common.FromHex(deployedCode), code) - } -} - -// When receive("X") is called with sender 0x00... and value 1, it produces this tx receipt: -// -// receipt{status=1 cgas=23949 bloom=00000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000040200000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 logs=[log: b6818c8064f645cd82d99b59a1a267d6d61117ef [75fd880d39c1daf53b6547ab6cb59451fc6452d27caa90e5b6649dd8293b9eed] 000000000000000000000000376c47978271565f56deb45495afa69e59c16ab200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000158 9ae378b6d4409eada347a5dc0c180f186cb62dc68fcc0f043425eb917335aa28 0 95d429d309bb9d753954195fe2d69bd140b4ae731b9b5b605c34323de162cf00 0]} -func TestPendingAndCallContract(t *testing.T) { - t.Parallel() - testAddr := crypto.PubkeyToAddress(testKey.PublicKey) - sim := simTestBackend(testAddr) - defer sim.Close() - bgCtx := context.Background() - - parsed, err := abi.JSON(strings.NewReader(abiJSON)) - if err != nil { - t.Errorf("could not get code at test addr: %v", err) - } - contractAuth, _ := bind.NewKeyedTransactorWithChainID(testKey, big.NewInt(1337)) - addr, _, _, err := bind.DeployContract(contractAuth, parsed, common.FromHex(abiBin), sim) - if err != nil { - t.Errorf("could not deploy contract: %v", err) - } - - input, err := parsed.Pack("receive", []byte("X")) - if err != nil { - t.Errorf("could not pack receive function on contract: %v", err) - } - - // make sure you can call the contract in accepted state - res, err := sim.AcceptedCallContract(bgCtx, interfaces.CallMsg{ - From: testAddr, - To: &addr, - Data: input, - }) - if err != nil { - t.Errorf("could not call receive method on contract: %v", err) - } - if len(res) == 0 { - t.Errorf("result of contract call was empty: %v", res) - } - - // while comparing against the byte array is more exact, also compare against the human readable string for readability - if !bytes.Equal(res, expectedReturn) || !strings.Contains(string(res), "hello world") { - t.Errorf("response from calling contract was expected to be 'hello world' instead received %v", string(res)) - } - - blockHash := sim.Commit(false) - - // make sure you can call the contract - res, err = sim.CallContract(bgCtx, interfaces.CallMsg{ - From: testAddr, - To: &addr, - Data: input, - }, nil) - if err != nil { - t.Errorf("could not call receive method on contract: %v", err) - } - if len(res) == 0 { - t.Errorf("result of contract call was empty: %v", res) - } - - if !bytes.Equal(res, expectedReturn) || !strings.Contains(string(res), "hello world") { - t.Errorf("response from calling contract was expected to be 'hello world' instead received %v", string(res)) - } - - // make sure you can call the contract by hash - res, err = sim.CallContractAtHash(bgCtx, interfaces.CallMsg{ - From: testAddr, - To: &addr, - Data: input, - }, blockHash) - if err != nil { - t.Errorf("could not call receive method on contract: %v", err) - } - if len(res) == 0 { - t.Errorf("result of contract call was empty: %v", res) - } - - if !bytes.Equal(res, expectedReturn) || !strings.Contains(string(res), "hello world") { - t.Errorf("response from calling contract was expected to be 'hello world' instead received %v", string(res)) - } -} - -// This test is based on the following contract: -/* -contract Reverter { - function revertString() public pure{ - require(false, "some error"); - } - function revertNoString() public pure { - require(false, ""); - } - function revertASM() public pure { - assembly { - revert(0x0, 0x0) - } - } - function noRevert() public pure { - assembly { - // Assembles something that looks like require(false, "some error") but is not reverted - mstore(0x0, 0x08c379a000000000000000000000000000000000000000000000000000000000) - mstore(0x4, 0x0000000000000000000000000000000000000000000000000000000000000020) - mstore(0x24, 0x000000000000000000000000000000000000000000000000000000000000000a) - mstore(0x44, 0x736f6d65206572726f7200000000000000000000000000000000000000000000) - return(0x0, 0x64) - } - } -}*/ -func TestCallContractRevert(t *testing.T) { - t.Parallel() - testAddr := crypto.PubkeyToAddress(testKey.PublicKey) - sim := simTestBackend(testAddr) - defer sim.Close() - bgCtx := context.Background() - - reverterABI := `[{"inputs": [],"name": "noRevert","outputs": [],"stateMutability": "pure","type": "function"},{"inputs": [],"name": "revertASM","outputs": [],"stateMutability": "pure","type": "function"},{"inputs": [],"name": "revertNoString","outputs": [],"stateMutability": "pure","type": "function"},{"inputs": [],"name": "revertString","outputs": [],"stateMutability": "pure","type": "function"}]` - reverterBin := "608060405234801561001057600080fd5b506101d3806100206000396000f3fe608060405234801561001057600080fd5b506004361061004c5760003560e01c80634b409e01146100515780639b340e361461005b5780639bd6103714610065578063b7246fc11461006f575b600080fd5b610059610079565b005b6100636100ca565b005b61006d6100cf565b005b610077610145565b005b60006100c8576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526000815260200160200191505060405180910390fd5b565b600080fd5b6000610143576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600a8152602001807f736f6d65206572726f720000000000000000000000000000000000000000000081525060200191505060405180910390fd5b565b7f08c379a0000000000000000000000000000000000000000000000000000000006000526020600452600a6024527f736f6d65206572726f720000000000000000000000000000000000000000000060445260646000f3fea2646970667358221220cdd8af0609ec4996b7360c7c780bad5c735740c64b1fffc3445aa12d37f07cb164736f6c63430006070033" - - parsed, err := abi.JSON(strings.NewReader(reverterABI)) - if err != nil { - t.Errorf("could not get code at test addr: %v", err) - } - contractAuth, _ := bind.NewKeyedTransactorWithChainID(testKey, big.NewInt(1337)) - addr, _, _, err := bind.DeployContract(contractAuth, parsed, common.FromHex(reverterBin), sim) - if err != nil { - t.Errorf("could not deploy contract: %v", err) - } - - inputs := make(map[string]interface{}, 3) - inputs["revertASM"] = nil - inputs["revertNoString"] = "" - inputs["revertString"] = "some error" - - call := make([]func([]byte) ([]byte, error), 2) - call[0] = func(input []byte) ([]byte, error) { - return sim.AcceptedCallContract(bgCtx, interfaces.CallMsg{ - From: testAddr, - To: &addr, - Data: input, - }) - } - call[1] = func(input []byte) ([]byte, error) { - return sim.CallContract(bgCtx, interfaces.CallMsg{ - From: testAddr, - To: &addr, - Data: input, - }, nil) - } - - // Run pending calls then commit - for _, cl := range call { - for key, val := range inputs { - input, err := parsed.Pack(key) - if err != nil { - t.Errorf("could not pack %v function on contract: %v", key, err) - } - - res, err := cl(input) - if err == nil { - t.Errorf("call to %v was not reverted", key) - } - if res != nil { - t.Errorf("result from %v was not nil: %v", key, res) - } - if val != nil { - rerr, ok := err.(*revertError) - if !ok { - t.Errorf("expect revert error") - } - if rerr.Error() != "execution reverted: "+val.(string) { - t.Errorf("error was malformed: got %v want %v", rerr.Error(), val) - } - } else { - // revert(0x0,0x0) - if err.Error() != "execution reverted" { - t.Errorf("error was malformed: got %v want %v", err, "execution reverted") - } - } - } - input, err := parsed.Pack("noRevert") - if err != nil { - t.Errorf("could not pack noRevert function on contract: %v", err) - } - res, err := cl(input) - if err != nil { - t.Error("call to noRevert was reverted") - } - if res == nil { - t.Errorf("result from noRevert was nil") - } - sim.Commit(false) - } -} - -// TestFork check that the chain length after a reorg is correct. -// Steps: -// 1. Save the current block which will serve as parent for the fork. -// 2. Mine n blocks with n ∈ [0, 20]. -// 3. Assert that the chain length is n. -// 4. Fork by using the parent block as ancestor. -// 5. Mine n+1 blocks which should trigger a reorg. -// 6. Assert that the chain length is n+1. -// Since Commit() was called 2n+1 times in total, -// having a chain length of just n+1 means that a reorg occurred. -func TestFork(t *testing.T) { - t.Parallel() - testAddr := crypto.PubkeyToAddress(testKey.PublicKey) - sim := simTestBackend(testAddr) - defer sim.Close() - // 1. - parent := sim.blockchain.CurrentBlock() - // 2. - n := int(rand.Int31n(21)) - for i := 0; i < n; i++ { - sim.Commit(false) - } - // 3. - if sim.blockchain.CurrentBlock().Number.Uint64() != uint64(n) { - t.Error("wrong chain length") - } - // 4. - sim.Fork(context.Background(), parent.Hash()) - // 5. - for i := 0; i < n+1; i++ { - sim.Commit(false) - } - // 6. - if sim.blockchain.CurrentBlock().Number.Uint64() != uint64(n+1) { - t.Error("wrong chain length") - } -} - -/* -Example contract to test event emission: - - pragma solidity >=0.7.0 <0.9.0; - contract Callable { - event Called(); - function Call() public { emit Called(); } - } -*/ -// The fork tests are commented out because transactions are not indexed in subnet-evm until they are marked -// as accepted, which breaks the logic of these tests. -// const callableAbi = "[{\"anonymous\":false,\"inputs\":[],\"name\":\"Called\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"Call\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]" - -// const callableBin = "6080604052348015600f57600080fd5b5060998061001e6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c806334e2292114602d575b600080fd5b60336035565b005b7f81fab7a4a0aa961db47eefc81f143a5220e8c8495260dd65b1356f1d19d3c7b860405160405180910390a156fea2646970667358221220029436d24f3ac598ceca41d4d712e13ced6d70727f4cdc580667de66d2f51d8b64736f6c63430008010033" - -// // TestForkLogsReborn check that the simulated reorgs -// // correctly remove and reborn logs. -// // Steps: -// // 1. Deploy the Callable contract. -// // 2. Set up an event subscription. -// // 3. Save the current block which will serve as parent for the fork. -// // 4. Send a transaction. -// // 5. Check that the event was included. -// // 6. Fork by using the parent block as ancestor. -// // 7. Mine two blocks to trigger a reorg. -// // 8. Check that the event was removed. -// // 9. Re-send the transaction and mine a block. -// // 10. Check that the event was reborn. -// func TestForkLogsReborn(t *testing.T) { -// t.Parallel() -// testAddr := crypto.PubkeyToAddress(testKey.PublicKey) -// sim := simTestBackend(testAddr) -// defer sim.Close() -// // 1. -// parsed, _ := abi.JSON(strings.NewReader(callableAbi)) -// auth, _ := bind.NewKeyedTransactorWithChainID(testKey, big.NewInt(1337)) -// _, _, contract, err := bind.DeployContract(auth, parsed, common.FromHex(callableBin), sim) -// if err != nil { -// t.Errorf("deploying contract: %v", err) -// } -// sim.Commit() -// // 2. -// logs, sub, err := contract.WatchLogs(nil, "Called") -// if err != nil { -// t.Errorf("watching logs: %v", err) -// } -// defer sub.Unsubscribe() -// // 3. -// parent := sim.blockchain.CurrentBlock() -// // 4. -// tx, err := contract.Transact(auth, "Call") -// if err != nil { -// t.Errorf("transacting: %v", err) -// } -// sim.Commit() -// // 5. -// log := <-logs -// if log.TxHash != tx.Hash() { -// t.Error("wrong event tx hash") -// } -// if log.Removed { -// t.Error("Event should be included") -// } -// // 6. -// if err := sim.Fork(context.Background(), parent.Hash()); err != nil { -// t.Errorf("forking: %v", err) -// } -// // 7. -// sim.Commit() -// sim.Commit() -// // 8. -// log = <-logs -// if log.TxHash != tx.Hash() { -// t.Error("wrong event tx hash") -// } -// if !log.Removed { -// t.Error("Event should be removed") -// } -// // 9. -// if err := sim.SendTransaction(context.Background(), tx); err != nil { -// t.Errorf("sending transaction: %v", err) -// } -// sim.Commit() -// // 10. -// log = <-logs -// if log.TxHash != tx.Hash() { -// t.Error("wrong event tx hash") -// } -// if log.Removed { -// t.Error("Event should be included") -// } -// } -// -// // TestForkResendTx checks that re-sending a TX after a fork -// // is possible and does not cause a "nonce mismatch" panic. -// // Steps: -// // 1. Save the current block which will serve as parent for the fork. -// // 2. Send a transaction. -// // 3. Check that the TX is included in block 1. -// // 4. Fork by using the parent block as ancestor. -// // 5. Mine a block, Re-send the transaction and mine another one. -// // 6. Check that the TX is now included in block 2. -// func TestForkResendTx(t *testing.T) { -// t.Parallel() -// testAddr := crypto.PubkeyToAddress(testKey.PublicKey) -// sim := simTestBackend(testAddr) -// defer sim.Close() -// // 1. -// parent := sim.blockchain.CurrentBlock() -// // 2. -// head, _ := sim.HeaderByNumber(context.Background(), 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) -// signer := types.NewLondonSigner(big.NewInt(1337)) -// tx, _ := types.SignTx(_tx, signer, testKey) -// sim.SendTransaction(context.Background(), tx) -// sim.Commit(false) -// // 3. -// receipt, _ := sim.TransactionReceipt(context.Background(), tx.Hash()) -// if h := receipt.BlockNumber.Uint64(); h != 1 { -// t.Errorf("TX included in wrong block: %d", h) -// } -// // 4. -// if err := sim.Fork(context.Background(), parent.Hash()); err != nil { -// t.Errorf("forking: %v", err) -// } -// // 5. -// sim.Commit(false) -// if err := sim.SendTransaction(context.Background(), tx); err != nil { -// t.Errorf("sending transaction: %v", err) -// } -// sim.Commit(false) -// // 6. -// receipt, _ = sim.TransactionReceipt(context.Background(), tx.Hash()) -// if h := receipt.BlockNumber.Uint64(); h != 2 { -// t.Errorf("TX included in wrong block: %d", h) -// } -// } - -func TestCommitReturnValue(t *testing.T) { - t.Parallel() - testAddr := crypto.PubkeyToAddress(testKey.PublicKey) - sim := simTestBackend(testAddr) - defer sim.Close() - - startBlockHeight := sim.blockchain.CurrentBlock().Number.Uint64() - - // Test if Commit returns the correct block hash - h1 := sim.Commit(true) - if h1 != sim.blockchain.CurrentBlock().Hash() { - t.Error("Commit did not return the hash of the last block.") - } - - // Create a block in the original chain (containing a transaction to force different block hashes) - head, _ := sim.HeaderByNumber(context.Background(), 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.SignTx(_tx, types.HomesteadSigner{}, testKey) - sim.SendTransaction(context.Background(), tx) - h2 := sim.Commit(false) - - // Create another block in the original chain - sim.Commit(false) - - // Fork at the first bock - if err := sim.Fork(context.Background(), h1); err != nil { - t.Errorf("forking: %v", err) - } - - // Test if Commit returns the correct block hash after the reorg - h2fork := sim.Commit(false) - if h2 == h2fork { - t.Error("The block in the fork and the original block are the same block!") - } - if sim.blockchain.GetHeader(h2fork, startBlockHeight+2) == nil { - t.Error("Could not retrieve the just created block (side-chain)") - } -} - -// TestAdjustTimeAfterFork ensures that after a fork, AdjustTime uses the pending fork -// block's parent rather than the canonical head's parent. -func TestAdjustTimeAfterFork(t *testing.T) { - t.Parallel() - testAddr := crypto.PubkeyToAddress(testKey.PublicKey) - sim := simTestBackend(testAddr) - defer sim.Close() - - sim.Commit(false) // h1 - h1 := sim.blockchain.CurrentHeader().Hash() - sim.Commit(false) // h2 - sim.Fork(context.Background(), h1) - sim.AdjustTime(1 * time.Second) - sim.Commit(false) - - head := sim.blockchain.CurrentHeader() - if head.Number == common.Big2 && head.ParentHash != h1 { - t.Errorf("failed to build block on fork") - } -} diff --git a/accounts/abi/bind/base.go b/accounts/abi/bind/base.go index 9aff177c1c..50c4a74ac1 100644 --- a/accounts/abi/bind/base.go +++ b/accounts/abi/bind/base.go @@ -37,6 +37,7 @@ import ( "github.com/ava-labs/subnet-evm/accounts/abi" "github.com/ava-labs/subnet-evm/core/types" "github.com/ava-labs/subnet-evm/interfaces" + "github.com/ava-labs/subnet-evm/rpc" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/event" @@ -387,7 +388,8 @@ func (c *BoundContract) estimateGasLimit(opts *TransactOpts, contract *common.Ad func (c *BoundContract) getNonce(opts *TransactOpts) (uint64, error) { if opts.Nonce == nil { - return c.transactor.AcceptedNonceAt(ensureContext(opts.Context), opts.From) + pendingBlock := big.NewInt(int64(rpc.PendingBlockNumber)) + return c.transactor.NonceAt(ensureContext(opts.Context), opts.From, pendingBlock) } else { return opts.Nonce.Uint64(), nil } diff --git a/accounts/abi/bind/base_test.go b/accounts/abi/bind/base_test.go index 0f420a32eb..8ed30c23a4 100644 --- a/accounts/abi/bind/base_test.go +++ b/accounts/abi/bind/base_test.go @@ -63,7 +63,7 @@ func (mt *mockTransactor) AcceptedCodeAt(ctx context.Context, account common.Add return []byte{1}, nil } -func (mt *mockTransactor) AcceptedNonceAt(ctx context.Context, account common.Address) (uint64, error) { +func (mt *mockTransactor) NonceAt(ctx context.Context, account common.Address, blockNum *big.Int) (uint64, error) { return 0, nil } diff --git a/accounts/abi/bind/bind_test.go b/accounts/abi/bind/bind_test.go index 4ccb21ba91..e2832add64 100644 --- a/accounts/abi/bind/bind_test.go +++ b/accounts/abi/bind/bind_test.go @@ -299,7 +299,7 @@ var bindTests = []struct { "github.com/ava-labs/subnet-evm/accounts/abi/bind" "github.com/ava-labs/subnet-evm/accounts/abi/bind/backends" - "github.com/ava-labs/subnet-evm/core" + "github.com/ava-labs/subnet-evm/core/types" "github.com/ethereum/go-ethereum/crypto" `, ` @@ -307,7 +307,7 @@ var bindTests = []struct { key, _ := crypto.GenerateKey() auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) - sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(1000000000000000000)}}, 10000000) + sim := backends.NewSimulatedBackend(types.GenesisAlloc{auth.From: {Balance: big.NewInt(1000000000000000000)}}, 10000000) defer sim.Close() // Deploy an interaction tester contract and call a transaction on it @@ -315,6 +315,7 @@ var bindTests = []struct { if err != nil { t.Fatalf("Failed to deploy interactor contract: %v", err) } + sim.Commit(false) if _, err := interactor.Transact(auth, "Transact string"); err != nil { t.Fatalf("Failed to transact with interactor contract: %v", err) } @@ -354,7 +355,7 @@ var bindTests = []struct { "github.com/ava-labs/subnet-evm/accounts/abi/bind" "github.com/ava-labs/subnet-evm/accounts/abi/bind/backends" - "github.com/ava-labs/subnet-evm/core" + "github.com/ava-labs/subnet-evm/core/types" "github.com/ethereum/go-ethereum/crypto" `, ` @@ -362,7 +363,7 @@ var bindTests = []struct { key, _ := crypto.GenerateKey() auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) - sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(1000000000000000000)}}, 10000000) + sim := backends.NewSimulatedBackend(types.GenesisAlloc{auth.From: {Balance: big.NewInt(1000000000000000000)}}, 10000000) defer sim.Close() // Deploy a tuple tester contract and execute a structured call on it @@ -400,7 +401,7 @@ var bindTests = []struct { "github.com/ava-labs/subnet-evm/accounts/abi/bind" "github.com/ava-labs/subnet-evm/accounts/abi/bind/backends" - "github.com/ava-labs/subnet-evm/core" + "github.com/ava-labs/subnet-evm/core/types" "github.com/ethereum/go-ethereum/crypto" `, ` @@ -408,7 +409,7 @@ var bindTests = []struct { key, _ := crypto.GenerateKey() auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) - sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(1000000000000000000)}}, 10000000) + sim := backends.NewSimulatedBackend(types.GenesisAlloc{auth.From: {Balance: big.NewInt(1000000000000000000)}}, 10000000) defer sim.Close() // Deploy a tuple tester contract and execute a structured call on it @@ -458,7 +459,7 @@ var bindTests = []struct { "github.com/ava-labs/subnet-evm/accounts/abi/bind" "github.com/ava-labs/subnet-evm/accounts/abi/bind/backends" "github.com/ethereum/go-ethereum/common" - "github.com/ava-labs/subnet-evm/core" + "github.com/ava-labs/subnet-evm/core/types" "github.com/ethereum/go-ethereum/crypto" `, ` @@ -466,7 +467,7 @@ var bindTests = []struct { key, _ := crypto.GenerateKey() auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) - sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(1000000000000000000)}}, 10000000) + sim := backends.NewSimulatedBackend(types.GenesisAlloc{auth.From: {Balance: big.NewInt(1000000000000000000)}}, 10000000) defer sim.Close() // Deploy a slice tester contract and execute a n array call on it @@ -506,7 +507,7 @@ var bindTests = []struct { "github.com/ava-labs/subnet-evm/accounts/abi/bind" "github.com/ava-labs/subnet-evm/accounts/abi/bind/backends" - "github.com/ava-labs/subnet-evm/core" + "github.com/ava-labs/subnet-evm/core/types" "github.com/ethereum/go-ethereum/crypto" `, ` @@ -514,7 +515,7 @@ var bindTests = []struct { key, _ := crypto.GenerateKey() auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) - sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(1000000000000000000)}}, 10000000) + sim := backends.NewSimulatedBackend(types.GenesisAlloc{auth.From: {Balance: big.NewInt(1000000000000000000)}}, 10000000) defer sim.Close() // Deploy a default method invoker contract and execute its default method @@ -522,6 +523,7 @@ var bindTests = []struct { if err != nil { t.Fatalf("Failed to deploy defaulter contract: %v", err) } + sim.Commit(false) if _, err := (&DefaulterRaw{defaulter}).Transfer(auth); err != nil { t.Fatalf("Failed to invoke default method: %v", err) } @@ -572,7 +574,7 @@ var bindTests = []struct { "github.com/ava-labs/subnet-evm/accounts/abi/bind" "github.com/ava-labs/subnet-evm/accounts/abi/bind/backends" - "github.com/ava-labs/subnet-evm/core" + "github.com/ava-labs/subnet-evm/core/types" "github.com/ethereum/go-ethereum/crypto" `, ` @@ -580,7 +582,7 @@ var bindTests = []struct { key, _ := crypto.GenerateKey() auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) - sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(1000000000000000000)}}, 10000000) + sim := backends.NewSimulatedBackend(types.GenesisAlloc{auth.From: {Balance: big.NewInt(1000000000000000000)}}, 10000000) defer sim.Close() // Deploy a structs method invoker contract and execute its default method @@ -618,12 +620,12 @@ var bindTests = []struct { "github.com/ava-labs/subnet-evm/accounts/abi/bind" "github.com/ava-labs/subnet-evm/accounts/abi/bind/backends" "github.com/ethereum/go-ethereum/common" - "github.com/ava-labs/subnet-evm/core" + "github.com/ava-labs/subnet-evm/core/types" `, ` // Create a simulator and wrap a non-deployed contract - sim := backends.NewSimulatedBackend(core.GenesisAlloc{}, uint64(10000000000)) + sim := backends.NewSimulatedBackend(types.GenesisAlloc{}, uint64(10000000000)) defer sim.Close() nonexistent, err := NewNonExistent(common.Address{}, sim) @@ -657,12 +659,12 @@ var bindTests = []struct { "github.com/ava-labs/subnet-evm/accounts/abi/bind" "github.com/ava-labs/subnet-evm/accounts/abi/bind/backends" "github.com/ethereum/go-ethereum/common" - "github.com/ava-labs/subnet-evm/core" + "github.com/ava-labs/subnet-evm/core/types" `, ` // Create a simulator and wrap a non-deployed contract - sim := backends.NewSimulatedBackend(core.GenesisAlloc{}, uint64(10000000000)) + sim := backends.NewSimulatedBackend(types.GenesisAlloc{}, uint64(10000000000)) defer sim.Close() nonexistent, err := NewNonExistentStruct(common.Address{}, sim) @@ -704,7 +706,7 @@ var bindTests = []struct { "github.com/ava-labs/subnet-evm/accounts/abi/bind" "github.com/ava-labs/subnet-evm/accounts/abi/bind/backends" - "github.com/ava-labs/subnet-evm/core" + "github.com/ava-labs/subnet-evm/core/types" "github.com/ethereum/go-ethereum/crypto" `, ` @@ -712,7 +714,7 @@ var bindTests = []struct { key, _ := crypto.GenerateKey() auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) - sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(1000000000000000000)}}, 10000000) + sim := backends.NewSimulatedBackend(types.GenesisAlloc{auth.From: {Balance: big.NewInt(1000000000000000000)}}, 10000000) defer sim.Close() // Deploy a funky gas pattern contract @@ -754,7 +756,7 @@ var bindTests = []struct { "github.com/ava-labs/subnet-evm/accounts/abi/bind" "github.com/ava-labs/subnet-evm/accounts/abi/bind/backends" "github.com/ethereum/go-ethereum/common" - "github.com/ava-labs/subnet-evm/core" + "github.com/ava-labs/subnet-evm/core/types" "github.com/ethereum/go-ethereum/crypto" `, ` @@ -762,7 +764,7 @@ var bindTests = []struct { key, _ := crypto.GenerateKey() auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) - sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(1000000000000000000)}}, 10000000) + sim := backends.NewSimulatedBackend(types.GenesisAlloc{auth.From: {Balance: big.NewInt(1000000000000000000)}}, 10000000) defer sim.Close() // Deploy a sender tester contract and execute a structured call on it @@ -829,7 +831,7 @@ var bindTests = []struct { "github.com/ava-labs/subnet-evm/accounts/abi/bind" "github.com/ava-labs/subnet-evm/accounts/abi/bind/backends" - "github.com/ava-labs/subnet-evm/core" + "github.com/ava-labs/subnet-evm/core/types" "github.com/ethereum/go-ethereum/crypto" `, ` @@ -837,7 +839,7 @@ var bindTests = []struct { key, _ := crypto.GenerateKey() auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) - sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(1000000000000000000)}}, 10000000) + sim := backends.NewSimulatedBackend(types.GenesisAlloc{auth.From: {Balance: big.NewInt(1000000000000000000)}}, 10000000) defer sim.Close() // Deploy a underscorer tester contract and execute a structured call on it @@ -923,7 +925,7 @@ var bindTests = []struct { "github.com/ava-labs/subnet-evm/accounts/abi/bind" "github.com/ava-labs/subnet-evm/accounts/abi/bind/backends" "github.com/ethereum/go-ethereum/common" - "github.com/ava-labs/subnet-evm/core" + "github.com/ava-labs/subnet-evm/core/types" "github.com/ethereum/go-ethereum/crypto" `, ` @@ -931,7 +933,7 @@ var bindTests = []struct { key, _ := crypto.GenerateKey() auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) - sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(1000000000000000000)}}, 10000000) + sim := backends.NewSimulatedBackend(types.GenesisAlloc{auth.From: {Balance: big.NewInt(1000000000000000000)}}, 10000000) defer sim.Close() // Deploy an eventer contract @@ -1113,7 +1115,7 @@ var bindTests = []struct { "github.com/ava-labs/subnet-evm/accounts/abi/bind" "github.com/ava-labs/subnet-evm/accounts/abi/bind/backends" - "github.com/ava-labs/subnet-evm/core" + "github.com/ava-labs/subnet-evm/core/types" "github.com/ethereum/go-ethereum/crypto" `, ` @@ -1121,7 +1123,7 @@ var bindTests = []struct { key, _ := crypto.GenerateKey() auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) - sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(1000000000000000000)}}, 10000000) + sim := backends.NewSimulatedBackend(types.GenesisAlloc{auth.From: {Balance: big.NewInt(1000000000000000000)}}, 10000000) defer sim.Close() //deploy the test contract @@ -1248,7 +1250,7 @@ var bindTests = []struct { "github.com/ava-labs/subnet-evm/accounts/abi/bind" "github.com/ava-labs/subnet-evm/accounts/abi/bind/backends" - "github.com/ava-labs/subnet-evm/core" + "github.com/ava-labs/subnet-evm/core/types" "github.com/ethereum/go-ethereum/crypto" `, @@ -1256,7 +1258,7 @@ var bindTests = []struct { key, _ := crypto.GenerateKey() auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) - sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(1000000000000000000)}}, 10000000) + sim := backends.NewSimulatedBackend(types.GenesisAlloc{auth.From: {Balance: big.NewInt(1000000000000000000)}}, 10000000) defer sim.Close() _, _, contract, err := DeployTuple(auth, sim) @@ -1390,7 +1392,7 @@ var bindTests = []struct { "github.com/ava-labs/subnet-evm/accounts/abi/bind" "github.com/ava-labs/subnet-evm/accounts/abi/bind/backends" - "github.com/ava-labs/subnet-evm/core" + "github.com/ava-labs/subnet-evm/core/types" "github.com/ethereum/go-ethereum/crypto" `, ` @@ -1398,7 +1400,7 @@ var bindTests = []struct { key, _ := crypto.GenerateKey() auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) - sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(1000000000000000000)}}, 10000000) + sim := backends.NewSimulatedBackend(types.GenesisAlloc{auth.From: {Balance: big.NewInt(1000000000000000000)}}, 10000000) defer sim.Close() //deploy the test contract @@ -1456,14 +1458,14 @@ var bindTests = []struct { "github.com/ava-labs/subnet-evm/accounts/abi/bind" "github.com/ava-labs/subnet-evm/accounts/abi/bind/backends" - "github.com/ava-labs/subnet-evm/core" + "github.com/ava-labs/subnet-evm/core/types" "github.com/ethereum/go-ethereum/crypto" `, ` // Initialize test accounts key, _ := crypto.GenerateKey() auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) - sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: new(big.Int).Mul(big.NewInt(10000000000000000), big.NewInt(1000))}}, 10000000) + sim := backends.NewSimulatedBackend(types.GenesisAlloc{auth.From: {Balance: new(big.Int).Mul(big.NewInt(10000000000000000), big.NewInt(1000))}}, 10000000) defer sim.Close() // deploy the test contract @@ -1566,7 +1568,7 @@ var bindTests = []struct { "github.com/ava-labs/subnet-evm/accounts/abi/bind" "github.com/ava-labs/subnet-evm/accounts/abi/bind/backends" "github.com/ethereum/go-ethereum/crypto" - "github.com/ava-labs/subnet-evm/core" + "github.com/ava-labs/subnet-evm/core/types" `, ` // Initialize test accounts @@ -1574,7 +1576,7 @@ var bindTests = []struct { addr := crypto.PubkeyToAddress(key.PublicKey) // Deploy registrar contract - sim := backends.NewSimulatedBackend(core.GenesisAlloc{addr: {Balance: new(big.Int).Mul(big.NewInt(10000000000000000), big.NewInt(1000))}}, 10000000) + sim := backends.NewSimulatedBackend(types.GenesisAlloc{addr: {Balance: new(big.Int).Mul(big.NewInt(10000000000000000), big.NewInt(1000))}}, 10000000) defer sim.Close() transactOpts, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) @@ -1629,14 +1631,14 @@ var bindTests = []struct { "github.com/ava-labs/subnet-evm/accounts/abi/bind" "github.com/ava-labs/subnet-evm/accounts/abi/bind/backends" "github.com/ethereum/go-ethereum/crypto" - "github.com/ava-labs/subnet-evm/core" + "github.com/ava-labs/subnet-evm/core/types" `, ` key, _ := crypto.GenerateKey() addr := crypto.PubkeyToAddress(key.PublicKey) // Deploy registrar contract - sim := backends.NewSimulatedBackend(core.GenesisAlloc{addr: {Balance: big.NewInt(1000000000000000000)}}, 10000000) + sim := backends.NewSimulatedBackend(types.GenesisAlloc{addr: {Balance: big.NewInt(1000000000000000000)}}, 10000000) defer sim.Close() transactOpts, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) @@ -1690,7 +1692,7 @@ var bindTests = []struct { "github.com/ava-labs/subnet-evm/accounts/abi/bind" "github.com/ava-labs/subnet-evm/accounts/abi/bind/backends" - "github.com/ava-labs/subnet-evm/core" + "github.com/ava-labs/subnet-evm/core/types" "github.com/ethereum/go-ethereum/crypto" `, ` @@ -1698,7 +1700,7 @@ var bindTests = []struct { key, _ := crypto.GenerateKey() auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) - sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(1000000000000000000)}}, 10000000) + sim := backends.NewSimulatedBackend(types.GenesisAlloc{auth.From: {Balance: big.NewInt(1000000000000000000)}}, 10000000) defer sim.Close() // Deploy a tester contract and execute a structured call on it @@ -1751,15 +1753,14 @@ var bindTests = []struct { "github.com/ava-labs/subnet-evm/accounts/abi/bind" "github.com/ava-labs/subnet-evm/accounts/abi/bind/backends" - "github.com/ava-labs/subnet-evm/core" + "github.com/ava-labs/subnet-evm/core/types" "github.com/ethereum/go-ethereum/crypto" `, ` key, _ := crypto.GenerateKey() addr := crypto.PubkeyToAddress(key.PublicKey) - - sim := backends.NewSimulatedBackend(core.GenesisAlloc{addr: {Balance: big.NewInt(1000000000000000000)}}, 1000000) + sim := backends.NewSimulatedBackend(types.GenesisAlloc{addr: {Balance: big.NewInt(1000000000000000000)}}, 1000000) defer sim.Close() opts, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) @@ -1840,14 +1841,14 @@ var bindTests = []struct { "github.com/ava-labs/subnet-evm/accounts/abi/bind" "github.com/ava-labs/subnet-evm/accounts/abi/bind/backends" - "github.com/ava-labs/subnet-evm/core" + "github.com/ava-labs/subnet-evm/core/types" "github.com/ethereum/go-ethereum/crypto" `, ` var ( key, _ = crypto.GenerateKey() user, _ = bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) - sim = backends.NewSimulatedBackend(core.GenesisAlloc{user.From: {Balance: big.NewInt(1000000000000000000)}}, 10000000) + sim = backends.NewSimulatedBackend(types.GenesisAlloc{user.From: {Balance: big.NewInt(1000000000000000000)}}, 10000000) ) defer sim.Close() @@ -1905,18 +1906,19 @@ var bindTests = []struct { []string{"0x6080604052348015600f57600080fd5b5060998061001e6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063726c638214602d575b600080fd5b60336035565b005b60405163024876cd60e61b815260016004820152600260248201526003604482015260640160405180910390fdfea264697066735822122093f786a1bc60216540cd999fbb4a6109e0fef20abcff6e9107fb2817ca968f3c64736f6c63430008070033"}, []string{`[{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"MyError","type":"error"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"MyError1","type":"error"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"MyError2","type":"error"},{"inputs":[{"internalType":"uint256","name":"a","type":"uint256"},{"internalType":"uint256","name":"b","type":"uint256"},{"internalType":"uint256","name":"c","type":"uint256"}],"name":"MyError3","type":"error"},{"inputs":[],"name":"Error","outputs":[],"stateMutability":"pure","type":"function"}]`}, ` + "context" "math/big" "github.com/ava-labs/subnet-evm/accounts/abi/bind" "github.com/ava-labs/subnet-evm/accounts/abi/bind/backends" - "github.com/ava-labs/subnet-evm/core" + "github.com/ava-labs/subnet-evm/core/types" "github.com/ethereum/go-ethereum/crypto" `, ` var ( key, _ = crypto.GenerateKey() user, _ = bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) - sim = backends.NewSimulatedBackend(core.GenesisAlloc{user.From: {Balance: big.NewInt(1000000000000000000)}}, 10000000) + sim = backends.NewSimulatedBackend(types.GenesisAlloc{user.From: {Balance: big.NewInt(1000000000000000000)}}, 10000000) ) defer sim.Close() @@ -1925,7 +1927,7 @@ var bindTests = []struct { t.Fatal(err) } sim.Commit(true) - _, err = bind.WaitDeployed(nil, sim, tx) + _, err = bind.WaitDeployed(context.Background(), sim, tx) if err != nil { t.Error(err) } @@ -1956,18 +1958,19 @@ var bindTests = []struct { bytecode: []string{`0x608060405234801561001057600080fd5b506040516101c43803806101c48339818101604052810190610032919061014a565b50610177565b6000604051905090565b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6100958261004c565b810181811067ffffffffffffffff821117156100b4576100b361005d565b5b80604052505050565b60006100c7610038565b90506100d3828261008c565b919050565b6000819050919050565b6100eb816100d8565b81146100f657600080fd5b50565b600081519050610108816100e2565b92915050565b60006020828403121561012457610123610047565b5b61012e60206100bd565b9050600061013e848285016100f9565b60008301525092915050565b6000602082840312156101605761015f610042565b5b600061016e8482850161010e565b91505092915050565b603f806101856000396000f3fe6080604052600080fdfea2646970667358221220cdffa667affecefac5561f65f4a4ba914204a8d4eb859d8cd426fb306e5c12a364736f6c634300080a0033`}, abi: []string{`[{"inputs":[{"components":[{"internalType":"uint256","name":"field","type":"uint256"}],"internalType":"struct ConstructorWithStructParam.StructType","name":"st","type":"tuple"}],"stateMutability":"nonpayable","type":"constructor"}]`}, imports: ` + "context" "math/big" "github.com/ava-labs/subnet-evm/accounts/abi/bind" "github.com/ava-labs/subnet-evm/accounts/abi/bind/backends" - "github.com/ava-labs/subnet-evm/core" + "github.com/ava-labs/subnet-evm/core/types" "github.com/ethereum/go-ethereum/crypto" `, tester: ` var ( key, _ = crypto.GenerateKey() user, _ = bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) - sim = backends.NewSimulatedBackend(core.GenesisAlloc{user.From: {Balance: big.NewInt(1000000000000000000)}}, 10000000) + sim = backends.NewSimulatedBackend(types.GenesisAlloc{user.From: {Balance: big.NewInt(1000000000000000000)}}, 10000000) ) defer sim.Close() @@ -1977,7 +1980,7 @@ var bindTests = []struct { } sim.Commit(true) - if _, err = bind.WaitDeployed(nil, sim, tx); err != nil { + if _, err = bind.WaitDeployed(context.Background(), sim, tx); err != nil { t.Logf("Deployment tx: %+v", tx) t.Errorf("bind.WaitDeployed(nil, %T, ) got err %v; want nil err", sim, err) } @@ -2003,11 +2006,12 @@ var bindTests = []struct { bytecode: []string{"0x608060405234801561001057600080fd5b5061042b806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c8063c2bb515f1461003b578063cce7b04814610059575b600080fd5b610043610075565b60405161005091906101af565b60405180910390f35b610073600480360381019061006e91906103ac565b6100b5565b005b61007d6100b8565b604051806040016040528060405180602001604052806000815250815260200160405180602001604052806000815250815250905090565b50565b604051806040016040528060608152602001606081525090565b600081519050919050565b600082825260208201905092915050565b60005b8381101561010c5780820151818401526020810190506100f1565b8381111561011b576000848401525b50505050565b6000601f19601f8301169050919050565b600061013d826100d2565b61014781856100dd565b93506101578185602086016100ee565b61016081610121565b840191505092915050565b600060408301600083015184820360008601526101888282610132565b915050602083015184820360208601526101a28282610132565b9150508091505092915050565b600060208201905081810360008301526101c9818461016b565b905092915050565b6000604051905090565b600080fd5b600080fd5b600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b61022282610121565b810181811067ffffffffffffffff82111715610241576102406101ea565b5b80604052505050565b60006102546101d1565b90506102608282610219565b919050565b600080fd5b600080fd5b600080fd5b600067ffffffffffffffff82111561028f5761028e6101ea565b5b61029882610121565b9050602081019050919050565b82818337600083830152505050565b60006102c76102c284610274565b61024a565b9050828152602081018484840111156102e3576102e261026f565b5b6102ee8482856102a5565b509392505050565b600082601f83011261030b5761030a61026a565b5b813561031b8482602086016102b4565b91505092915050565b60006040828403121561033a576103396101e5565b5b610344604061024a565b9050600082013567ffffffffffffffff81111561036457610363610265565b5b610370848285016102f6565b600083015250602082013567ffffffffffffffff81111561039457610393610265565b5b6103a0848285016102f6565b60208301525092915050565b6000602082840312156103c2576103c16101db565b5b600082013567ffffffffffffffff8111156103e0576103df6101e0565b5b6103ec84828501610324565b9150509291505056fea264697066735822122033bca1606af9b6aeba1673f98c52003cec19338539fb44b86690ce82c51483b564736f6c634300080e0033"}, abi: []string{`[ { "anonymous": false, "inputs": [ { "indexed": false, "internalType": "int256", "name": "msg", "type": "int256" }, { "indexed": false, "internalType": "int256", "name": "_msg", "type": "int256" } ], "name": "log", "type": "event" }, { "inputs": [ { "components": [ { "internalType": "bytes", "name": "data", "type": "bytes" }, { "internalType": "bytes", "name": "_data", "type": "bytes" } ], "internalType": "struct oracle.request", "name": "req", "type": "tuple" } ], "name": "addRequest", "outputs": [], "stateMutability": "pure", "type": "function" }, { "inputs": [], "name": "getRequest", "outputs": [ { "components": [ { "internalType": "bytes", "name": "data", "type": "bytes" }, { "internalType": "bytes", "name": "_data", "type": "bytes" } ], "internalType": "struct oracle.request", "name": "", "type": "tuple" } ], "stateMutability": "pure", "type": "function" } ]`}, imports: ` + "context" "math/big" "github.com/ava-labs/subnet-evm/accounts/abi/bind" "github.com/ava-labs/subnet-evm/accounts/abi/bind/backends" - "github.com/ava-labs/subnet-evm/core" + "github.com/ava-labs/subnet-evm/core/types" "github.com/ethereum/go-ethereum/crypto" `, tester: ` @@ -2015,7 +2019,7 @@ var bindTests = []struct { gasCeil = uint64(30000000) // Note: from geth's ethconfig.Defaults.Miner.GasCeil key, _ = crypto.GenerateKey() user, _ = bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) - sim = backends.NewSimulatedBackend(core.GenesisAlloc{user.From: {Balance: big.NewInt(1000000000000000000)}}, gasCeil) + sim = backends.NewSimulatedBackend(types.GenesisAlloc{user.From: {Balance: big.NewInt(1000000000000000000)}}, gasCeil) ) defer sim.Close() @@ -2025,7 +2029,7 @@ var bindTests = []struct { } sim.Commit(true) - if _, err = bind.WaitDeployed(nil, sim, tx); err != nil { + if _, err = bind.WaitDeployed(context.Background(), sim, tx); err != nil { t.Logf("Deployment tx: %+v", tx) t.Errorf("bind.WaitDeployed(nil, %T, ) got err %v; want nil err", sim, err) } @@ -2043,11 +2047,12 @@ var bindTests = []struct { bytecode: []string{"0x608060405234801561001057600080fd5b5060dc8061001f6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063527a119f14602d575b600080fd5b60436004803603810190603f9190605b565b6045565b005b50565b6000813590506055816092565b92915050565b600060208284031215606e57606d608d565b5b6000607a848285016048565b91505092915050565b6000819050919050565b600080fd5b6099816083565b811460a357600080fd5b5056fea2646970667358221220d4f4525e2615516394055d369fb17df41c359e5e962734f27fd683ea81fd9db164736f6c63430008070033"}, abi: []string{`[{"inputs":[{"internalType":"uint256","name":"range","type":"uint256"}],"name":"functionWithKeywordParameter","outputs":[],"stateMutability":"pure","type":"function"}]`}, imports: ` + "context" "math/big" "github.com/ava-labs/subnet-evm/accounts/abi/bind" "github.com/ava-labs/subnet-evm/accounts/abi/bind/backends" - "github.com/ava-labs/subnet-evm/core" + "github.com/ava-labs/subnet-evm/core/types" "github.com/ethereum/go-ethereum/crypto" `, tester: ` @@ -2055,7 +2060,7 @@ var bindTests = []struct { gasCeil = uint64(30000000) // Note: from geth's ethconfig.Defaults.Miner.GasCeil key, _ = crypto.GenerateKey() user, _ = bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) - sim = backends.NewSimulatedBackend(core.GenesisAlloc{user.From: {Balance: big.NewInt(1000000000000000000)}}, gasCeil) + sim = backends.NewSimulatedBackend(types.GenesisAlloc{user.From: {Balance: big.NewInt(1000000000000000000)}}, gasCeil) ) _, tx, _, err := DeployRangeKeyword(user, sim) if err != nil { @@ -2063,7 +2068,7 @@ var bindTests = []struct { } sim.Commit(true) - if _, err = bind.WaitDeployed(nil, sim, tx); err != nil { + if _, err = bind.WaitDeployed(context.Background(), sim, tx); err != nil { t.Errorf("error deploying the contract: %v", err) } `, diff --git a/accounts/abi/bind/util_test.go b/accounts/abi/bind/util_test.go index db46bcbbb6..93b03be810 100644 --- a/accounts/abi/bind/util_test.go +++ b/accounts/abi/bind/util_test.go @@ -34,9 +34,9 @@ import ( "time" "github.com/ava-labs/subnet-evm/accounts/abi/bind" - "github.com/ava-labs/subnet-evm/accounts/abi/bind/backends" - "github.com/ava-labs/subnet-evm/core" "github.com/ava-labs/subnet-evm/core/types" + "github.com/ava-labs/subnet-evm/ethclient/simulated" + "github.com/ava-labs/subnet-evm/params" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" ) @@ -65,21 +65,19 @@ var waitDeployedTests = map[string]struct { func TestWaitDeployed(t *testing.T) { t.Parallel() for name, test := range waitDeployedTests { - backend := backends.NewSimulatedBackend( - core.GenesisAlloc{ + backend := simulated.NewBackend( + types.GenesisAlloc{ crypto.PubkeyToAddress(testKey.PublicKey): {Balance: new(big.Int).Mul(big.NewInt(10000000000000000), big.NewInt(1000))}, }, - 10000000, ) defer backend.Close() // Create the transaction - head, _ := backend.HeaderByNumber(context.Background(), nil) // Should be child's, good enough - gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1)) + head, _ := backend.Client().HeaderByNumber(context.Background(), nil) // Should be child's, good enough + gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(params.GWei)) tx := types.NewContractCreation(0, big.NewInt(0), test.gas, gasPrice, common.FromHex(test.code)) - signer := types.NewLondonSigner(big.NewInt(1337)) - tx, _ = types.SignTx(tx, signer, testKey) + tx, _ = types.SignTx(tx, types.LatestSignerForChainID(big.NewInt(1337)), testKey) // Wait for it to get mined in the background. var ( @@ -89,13 +87,13 @@ func TestWaitDeployed(t *testing.T) { ctx = context.Background() ) go func() { - address, err = bind.WaitDeployed(ctx, backend, tx) + address, err = bind.WaitDeployed(ctx, backend.Client(), tx) close(mined) }() // Send and mine the transaction. - if err := backend.SendTransaction(ctx, tx); err != nil { - t.Errorf("Failed to send transaction: %s", err) + if err := backend.Client().SendTransaction(ctx, tx); err != nil { + t.Fatalf("Failed to send transaction: %s", err) } backend.Commit(true) @@ -114,42 +112,40 @@ func TestWaitDeployed(t *testing.T) { } func TestWaitDeployedCornerCases(t *testing.T) { - t.Parallel() - backend := backends.NewSimulatedBackend( - core.GenesisAlloc{ + backend := simulated.NewBackend( + types.GenesisAlloc{ crypto.PubkeyToAddress(testKey.PublicKey): {Balance: big.NewInt(1000000000000000000)}, }, - 10000000, ) defer backend.Close() - head, _ := backend.HeaderByNumber(context.Background(), nil) // Should be child's, good enough + head, _ := backend.Client().HeaderByNumber(context.Background(), nil) // Should be child's, good enough gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1)) // Create a transaction to an account. code := "6060604052600a8060106000396000f360606040526008565b00" tx := types.NewTransaction(0, common.HexToAddress("0x01"), big.NewInt(0), 3000000, gasPrice, common.FromHex(code)) - tx, _ = types.SignTx(tx, types.HomesteadSigner{}, testKey) + tx, _ = types.SignTx(tx, types.LatestSigner(params.TestChainConfig), testKey) ctx, cancel := context.WithCancel(context.Background()) defer cancel() - backend.SendTransaction(ctx, tx) + backend.Client().SendTransaction(ctx, tx) backend.Commit(true) notContractCreation := errors.New("tx is not contract creation") - if _, err := bind.WaitDeployed(ctx, backend, tx); err.Error() != notContractCreation.Error() { + if _, err := bind.WaitDeployed(ctx, backend.Client(), tx); err.Error() != notContractCreation.Error() { t.Errorf("error mismatch: want %q, got %q, ", notContractCreation, err) } // Create a transaction that is not mined. tx = types.NewContractCreation(1, big.NewInt(0), 3000000, gasPrice, common.FromHex(code)) - tx, _ = types.SignTx(tx, types.HomesteadSigner{}, testKey) + tx, _ = types.SignTx(tx, types.LatestSigner(params.TestChainConfig), testKey) go func() { contextCanceled := errors.New("context canceled") - if _, err := bind.WaitDeployed(ctx, backend, tx); err.Error() != contextCanceled.Error() { + if _, err := bind.WaitDeployed(ctx, backend.Client(), tx); err.Error() != contextCanceled.Error() { t.Errorf("error mismatch: want %q, got %q, ", contextCanceled, err) } }() - backend.SendTransaction(ctx, tx) + backend.Client().SendTransaction(ctx, tx) cancel() } diff --git a/accounts/abi/topics_test.go b/accounts/abi/topics_test.go index 8e3c83cbda..70522b6ce6 100644 --- a/accounts/abi/topics_test.go +++ b/accounts/abi/topics_test.go @@ -36,8 +36,6 @@ import ( "github.com/ethereum/go-ethereum/crypto" ) -var MaxHash = common.HexToHash("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") - func TestMakeTopics(t *testing.T) { t.Parallel() type args struct { @@ -86,7 +84,7 @@ func TestMakeTopics(t *testing.T) { {big.NewInt(math.MinInt64)}, }}, [][]common.Hash{ - {MaxHash}, + {common.MaxHash}, {common.HexToHash("ffffffffffffffffffffffffffffffffffffffffffffffff8000000000000000")}, }, false, diff --git a/accounts/keystore/passphrase.go b/accounts/keystore/passphrase.go index 98cf02ba37..7b1ce5945a 100644 --- a/accounts/keystore/passphrase.go +++ b/accounts/keystore/passphrase.go @@ -146,7 +146,7 @@ func (ks keyStorePassphrase) JoinPath(filename string) string { return filepath.Join(ks.keysDirPath, filename) } -// Encryptdata encrypts the data given as 'data' with the password 'auth'. +// EncryptDataV3 encrypts the data given as 'data' with the password 'auth'. func EncryptDataV3(data, auth []byte, scryptN, scryptP int) (CryptoJSON, error) { salt := make([]byte, 32) if _, err := io.ReadFull(rand.Reader, salt); err != nil { diff --git a/accounts/scwallet/hub.go b/accounts/scwallet/hub.go index 2a933651fb..3a81e084cf 100644 --- a/accounts/scwallet/hub.go +++ b/accounts/scwallet/hub.go @@ -251,7 +251,7 @@ func (hub *Hub) refreshWallets() { card.Disconnect(pcsc.LeaveCard) continue } - // Card connected, start tracking in amongs the wallets + // Card connected, start tracking among the wallets hub.wallets[reader] = wallet events = append(events, accounts.WalletEvent{Wallet: wallet, Kind: accounts.WalletArrived}) } diff --git a/cmd/evm/README.md b/cmd/evm/README.md index 6c70e0dd3b..6306dbf892 100644 --- a/cmd/evm/README.md +++ b/cmd/evm/README.md @@ -214,7 +214,7 @@ exitcode:3 OK The chain configuration to be used for a transition is specified via the `--state.fork` CLI flag. A list of possible values and configurations can be -found in [`tests/init.go`](tests/init.go). +found in [`tests/init.go`](../../tests/init.go). #### Examples ##### Basic usage diff --git a/cmd/evm/internal/t8ntool/execution.go b/cmd/evm/internal/t8ntool/execution.go index ea4bf549dc..f44e01d7ac 100644 --- a/cmd/evm/internal/t8ntool/execution.go +++ b/cmd/evm/internal/t8ntool/execution.go @@ -38,18 +38,20 @@ import ( "github.com/ava-labs/subnet-evm/core/vm" "github.com/ava-labs/subnet-evm/params" "github.com/ava-labs/subnet-evm/trie" + "github.com/ava-labs/subnet-evm/triedb" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/rlp" + "github.com/holiman/uint256" "golang.org/x/crypto/sha3" ) type Prestate struct { - Env stEnv `json:"env"` - Pre core.GenesisAlloc `json:"pre"` + Env stEnv `json:"env"` + Pre types.GenesisAlloc `json:"pre"` } // ExecutionResult contains the execution status after running a state test, any @@ -148,6 +150,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, rejectedTxs []*rejectedTx includedTxs types.Transactions gasUsed = uint64(0) + blobGasUsed = uint64(0) receipts = make(types.Receipts, 0) txIndex = 0 ) @@ -198,7 +201,6 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, evm := vm.NewEVM(vmContext, vm.TxContext{}, statedb, chainConfig, vmConfig) core.ProcessBeaconBlockRoot(*beaconRoot, evm, statedb) } - var blobGasUsed uint64 for i := 0; txIt.Next(); i++ { tx, err := txIt.Tx() @@ -219,15 +221,15 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, rejectedTxs = append(rejectedTxs, &rejectedTx{i, err.Error()}) continue } + txBlobGas := uint64(0) if tx.Type() == types.BlobTxType { - txBlobGas := uint64(params.BlobTxBlobGasPerBlob * len(tx.BlobHashes())) + txBlobGas = uint64(params.BlobTxBlobGasPerBlob * len(tx.BlobHashes())) if used, max := blobGasUsed+txBlobGas, uint64(params.MaxBlobGasPerBlock); used > max { err := fmt.Errorf("blob gas (%d) would exceed maximum allowance %d", used, max) log.Warn("rejected tx", "index", i, "err", err) rejectedTxs = append(rejectedTxs, &rejectedTx{i, err.Error()}) continue } - blobGasUsed += txBlobGas } tracer, err := getTracerFn(txIndex, tx.Hash()) if err != nil { @@ -256,6 +258,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, if hashError != nil { return nil, nil, nil, NewError(ErrorMissingBlockhash, hashError) } + blobGasUsed += txBlobGas gasUsed += msgResult.UsedGas // Receipt: @@ -316,9 +319,9 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, reward.Sub(reward, new(big.Int).SetUint64(ommer.Delta)) reward.Mul(reward, blockReward) reward.Div(reward, big.NewInt(8)) - statedb.AddBalance(ommer.Address, reward) + statedb.AddBalance(ommer.Address, uint256.MustFromBig(reward)) } - statedb.AddBalance(pre.Env.Coinbase, minerReward) + statedb.AddBalance(pre.Env.Coinbase, uint256.MustFromBig(minerReward)) } // Commit block root, err := statedb.Commit(vmContext.BlockNumber.Uint64(), chainConfig.IsEIP158(vmContext.BlockNumber), false) @@ -351,13 +354,13 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, return statedb, execRs, body, nil } -func MakePreState(db ethdb.Database, accounts core.GenesisAlloc) *state.StateDB { - sdb := state.NewDatabaseWithConfig(db, &trie.Config{Preimages: true}) +func MakePreState(db ethdb.Database, accounts types.GenesisAlloc) *state.StateDB { + sdb := state.NewDatabaseWithConfig(db, &triedb.Config{Preimages: true}) statedb, _ := state.New(types.EmptyRootHash, sdb, nil) for addr, a := range accounts { statedb.SetCode(addr, a.Code) statedb.SetNonce(addr, a.Nonce) - statedb.SetBalance(addr, a.Balance) + statedb.SetBalance(addr, uint256.MustFromBig(a.Balance)) for k, v := range a.Storage { statedb.SetState(addr, k, v) } diff --git a/cmd/evm/internal/t8ntool/transition.go b/cmd/evm/internal/t8ntool/transition.go index 5bb33c1e91..d2d957875d 100644 --- a/cmd/evm/internal/t8ntool/transition.go +++ b/cmd/evm/internal/t8ntool/transition.go @@ -35,7 +35,6 @@ import ( "path" "github.com/ava-labs/subnet-evm/consensus/dummy" - "github.com/ava-labs/subnet-evm/core" "github.com/ava-labs/subnet-evm/core/state" "github.com/ava-labs/subnet-evm/core/types" "github.com/ava-labs/subnet-evm/core/vm" @@ -84,10 +83,10 @@ var ( ) type input struct { - Alloc core.GenesisAlloc `json:"alloc,omitempty"` - Env *stEnv `json:"env,omitempty"` - Txs []*txWithKey `json:"txs,omitempty"` - TxRlp string `json:"txsRlp,omitempty"` + Alloc types.GenesisAlloc `json:"alloc,omitempty"` + Env *stEnv `json:"env,omitempty"` + Txs []*txWithKey `json:"txs,omitempty"` + TxRlp string `json:"txsRlp,omitempty"` } func Transition(ctx *cli.Context) error { @@ -198,7 +197,7 @@ func Transition(ctx *cli.Context) error { if err != nil { return err } - // Dump the excution result + // Dump the execution result collector := make(Alloc) s.DumpToCollector(collector, nil) return dispatchOutput(ctx, baseDir, result, collector, body) @@ -251,7 +250,7 @@ func applyCancunChecks(env *stEnv, chainConfig *params.ChainConfig) error { return nil } -type Alloc map[common.Address]core.GenesisAccount +type Alloc map[common.Address]types.Account func (g Alloc) OnRoot(common.Hash) {} @@ -259,7 +258,7 @@ func (g Alloc) OnAccount(addr *common.Address, dumpAccount state.DumpAccount) { if addr == nil { return } - balance, _ := new(big.Int).SetString(dumpAccount.Balance, 10) + balance, _ := new(big.Int).SetString(dumpAccount.Balance, 0) var storage map[common.Hash]common.Hash if dumpAccount.Storage != nil { storage = make(map[common.Hash]common.Hash) @@ -267,7 +266,7 @@ func (g Alloc) OnAccount(addr *common.Address, dumpAccount state.DumpAccount) { storage[k] = common.HexToHash(v) } } - genesisAccount := core.GenesisAccount{ + genesisAccount := types.Account{ Code: dumpAccount.Code, Storage: storage, Balance: balance, diff --git a/cmd/evm/runner.go b/cmd/evm/runner.go index d182715ca4..48902609ba 100644 --- a/cmd/evm/runner.go +++ b/cmd/evm/runner.go @@ -47,8 +47,8 @@ import ( "github.com/ava-labs/subnet-evm/eth/tracers/logger" "github.com/ava-labs/subnet-evm/internal/flags" "github.com/ava-labs/subnet-evm/params" - "github.com/ava-labs/subnet-evm/trie" - "github.com/ava-labs/subnet-evm/trie/triedb/hashdb" + "github.com/ava-labs/subnet-evm/triedb" + "github.com/ava-labs/subnet-evm/triedb/hashdb" "github.com/ethereum/go-ethereum/common" "github.com/urfave/cli/v2" ) @@ -158,7 +158,7 @@ func runCmd(ctx *cli.Context) error { } db := rawdb.NewMemoryDatabase() - triedb := trie.NewDatabase(db, &trie.Config{ + triedb := triedb.NewDatabase(db, &triedb.Config{ Preimages: preimages, HashDB: hashdb.Defaults, }) diff --git a/cmd/evm/staterunner.go b/cmd/evm/staterunner.go index db8aca19e8..2c0e77385d 100644 --- a/cmd/evm/staterunner.go +++ b/cmd/evm/staterunner.go @@ -34,7 +34,6 @@ import ( "github.com/ava-labs/subnet-evm/core/rawdb" "github.com/ava-labs/subnet-evm/core/state" - "github.com/ava-labs/subnet-evm/core/state/snapshot" "github.com/ava-labs/subnet-evm/core/vm" "github.com/ava-labs/subnet-evm/eth/tracers/logger" "github.com/ava-labs/subnet-evm/tests" @@ -100,26 +99,27 @@ func runStateTest(fname string, cfg vm.Config, jsonOut, dump bool) error { if err != nil { return err } - var tests map[string]tests.StateTest - if err := json.Unmarshal(src, &tests); err != nil { + var testsByName map[string]tests.StateTest + if err := json.Unmarshal(src, &testsByName); err != nil { return err } + // Iterate over all the tests, run them and aggregate the results - results := make([]StatetestResult, 0, len(tests)) - for key, test := range tests { + results := make([]StatetestResult, 0, len(testsByName)) + for key, test := range testsByName { for _, st := range test.Subtests() { // Run the test and aggregate the result result := &StatetestResult{Name: key, Fork: st.Fork, Pass: true} - test.Run(st, cfg, false, rawdb.HashScheme, func(err error, snaps *snapshot.Tree, statedb *state.StateDB) { + test.Run(st, cfg, false, rawdb.HashScheme, func(err error, tstate *tests.StateTestState) { var root common.Hash - if statedb != nil { - root = statedb.IntermediateRoot(false) + if tstate.StateDB != nil { + root = tstate.StateDB.IntermediateRoot(false) result.Root = &root if jsonOut { fmt.Fprintf(os.Stderr, "{\"stateRoot\": \"%#x\"}\n", root) } if dump { // Dump any state to aid debugging - cpy, _ := state.New(root, statedb.Database(), nil) + cpy, _ := state.New(root, tstate.StateDB.Database(), nil) dump := cpy.RawDump(nil) result.State = &dump } diff --git a/cmd/evm/transition-test.sh b/cmd/evm/transition-test.sh index 52fe01e25f..48bc8b6d98 100755 --- a/cmd/evm/transition-test.sh +++ b/cmd/evm/transition-test.sh @@ -103,7 +103,7 @@ type Env struct { CurrentTimestamp uint64 `json:"currentTimestamp"` Withdrawals []*Withdrawal `json:"withdrawals"` // optional - CurrentDifficulty *big.Int `json:"currentDifficuly"` + CurrentDifficulty *big.Int `json:"currentDifficulty"` CurrentRandom *big.Int `json:"currentRandom"` CurrentBaseFee *big.Int `json:"currentBaseFee"` ParentDifficulty *big.Int `json:"parentDifficulty"` diff --git a/consensus/dummy/consensus.go b/consensus/dummy/consensus.go index e73f94ec4d..49c6253beb 100644 --- a/consensus/dummy/consensus.go +++ b/consensus/dummy/consensus.go @@ -70,6 +70,13 @@ func NewFakerWithMode(mode Mode) *DummyEngine { } } +func NewFakerWithModeAndClock(mode Mode, clock *mockable.Clock) *DummyEngine { + return &DummyEngine{ + clock: clock, + consensusMode: mode, + } +} + func NewCoinbaseFaker() *DummyEngine { return &DummyEngine{ clock: &mockable.Clock{}, @@ -85,8 +92,8 @@ func NewFullFaker() *DummyEngine { } // verifyCoinbase checks that the coinbase is valid for the given [header] and [parent]. -func (self *DummyEngine) verifyCoinbase(config *params.ChainConfig, header *types.Header, parent *types.Header, chain consensus.ChainHeaderReader) error { - if self.consensusMode.ModeSkipCoinbase { +func (eng *DummyEngine) verifyCoinbase(config *params.ChainConfig, header *types.Header, parent *types.Header, chain consensus.ChainHeaderReader) error { + if eng.consensusMode.ModeSkipCoinbase { return nil } // get the coinbase configured at parent @@ -107,7 +114,7 @@ func (self *DummyEngine) verifyCoinbase(config *params.ChainConfig, header *type return nil } -func (self *DummyEngine) verifyHeaderGasFields(config *params.ChainConfig, header *types.Header, parent *types.Header, chain consensus.ChainHeaderReader) error { +func (eng *DummyEngine) verifyHeaderGasFields(config *params.ChainConfig, header *types.Header, parent *types.Header, chain consensus.ChainHeaderReader) error { // Verify that the gas limit is <= 2^63-1 if header.GasLimit > params.MaxGasLimit { return fmt.Errorf("invalid gasLimit: have %v, max %v", header.GasLimit, params.MaxGasLimit) @@ -192,7 +199,7 @@ func (self *DummyEngine) verifyHeaderGasFields(config *params.ChainConfig, heade } // modified from consensus.go -func (self *DummyEngine) verifyHeader(chain consensus.ChainHeaderReader, header *types.Header, parent *types.Header, uncle bool) error { +func (eng *DummyEngine) verifyHeader(chain consensus.ChainHeaderReader, header *types.Header, parent *types.Header, uncle bool) error { config := chain.Config() // Ensure that we do not verify an uncle if uncle { @@ -213,16 +220,16 @@ func (self *DummyEngine) verifyHeader(chain consensus.ChainHeaderReader, header } } // Ensure gas-related header fields are correct - if err := self.verifyHeaderGasFields(config, header, parent, chain); err != nil { + if err := eng.verifyHeaderGasFields(config, header, parent, chain); err != nil { return err } // Ensure that coinbase is valid - if err := self.verifyCoinbase(config, header, parent, chain); err != nil { + if err := eng.verifyCoinbase(config, header, parent, chain); err != nil { return err } // Verify the header's timestamp - if header.Time > uint64(self.clock.Time().Add(allowedFutureBlockTime).Unix()) { + if header.Time > uint64(eng.clock.Time().Add(allowedFutureBlockTime).Unix()) { return consensus.ErrFutureBlock } // Verify the header's timestamp is not earlier than parent's @@ -259,13 +266,13 @@ func (self *DummyEngine) verifyHeader(chain consensus.ChainHeaderReader, header return nil } -func (self *DummyEngine) Author(header *types.Header) (common.Address, error) { +func (*DummyEngine) Author(header *types.Header) (common.Address, error) { return header.Coinbase, nil } -func (self *DummyEngine) VerifyHeader(chain consensus.ChainHeaderReader, header *types.Header) error { +func (eng *DummyEngine) VerifyHeader(chain consensus.ChainHeaderReader, header *types.Header) error { // If we're running a full engine faking, accept any input as valid - if self.consensusMode.ModeSkipHeader { + if eng.consensusMode.ModeSkipHeader { return nil } // Short circuit if the header is known, or it's parent not @@ -278,28 +285,28 @@ func (self *DummyEngine) VerifyHeader(chain consensus.ChainHeaderReader, header return consensus.ErrUnknownAncestor } // Sanity checks passed, do a proper verification - return self.verifyHeader(chain, header, parent, false) + return eng.verifyHeader(chain, header, parent, false) } -func (self *DummyEngine) VerifyUncles(chain consensus.ChainReader, block *types.Block) error { +func (*DummyEngine) VerifyUncles(chain consensus.ChainReader, block *types.Block) error { if len(block.Uncles()) > 0 { return errUnclesUnsupported } return nil } -func (self *DummyEngine) Prepare(chain consensus.ChainHeaderReader, header *types.Header) error { +func (*DummyEngine) Prepare(chain consensus.ChainHeaderReader, header *types.Header) error { header.Difficulty = big.NewInt(1) return nil } -func (self *DummyEngine) verifyBlockFee( +func (eng *DummyEngine) verifyBlockFee( baseFee *big.Int, requiredBlockGasCost *big.Int, txs []*types.Transaction, receipts []*types.Receipt, ) error { - if self.consensusMode.ModeSkipBlockFee { + if eng.consensusMode.ModeSkipBlockFee { return nil } if baseFee == nil || baseFee.Sign() <= 0 { @@ -352,7 +359,7 @@ func (self *DummyEngine) verifyBlockFee( return nil } -func (self *DummyEngine) Finalize(chain consensus.ChainHeaderReader, block *types.Block, parent *types.Header, state *state.StateDB, receipts []*types.Receipt) error { +func (eng *DummyEngine) Finalize(chain consensus.ChainHeaderReader, block *types.Block, parent *types.Header, state *state.StateDB, receipts []*types.Receipt) error { if chain.Config().IsSubnetEVM(block.Time()) { // we use the parent to determine the fee config // since the current block has not been finalized yet. @@ -376,7 +383,7 @@ func (self *DummyEngine) Finalize(chain consensus.ChainHeaderReader, block *type return fmt.Errorf("invalid blockGasCost: have %d, want %d", blockBlockGasCost, blockGasCost) } // Verify the block fee was paid. - if err := self.verifyBlockFee( + if err := eng.verifyBlockFee( block.BaseFee(), block.BlockGasCost(), block.Transactions(), @@ -389,7 +396,7 @@ func (self *DummyEngine) Finalize(chain consensus.ChainHeaderReader, block *type return nil } -func (self *DummyEngine) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, parent *types.Header, state *state.StateDB, txs []*types.Transaction, +func (eng *DummyEngine) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, parent *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt, ) (*types.Block, error) { if chain.Config().IsSubnetEVM(header.Time) { @@ -409,7 +416,7 @@ func (self *DummyEngine) FinalizeAndAssemble(chain consensus.ChainHeaderReader, parent.Time, header.Time, ) // Verify that this block covers the block fee. - if err := self.verifyBlockFee( + if err := eng.verifyBlockFee( header.BaseFee, header.BlockGasCost, txs, @@ -427,10 +434,10 @@ func (self *DummyEngine) FinalizeAndAssemble(chain consensus.ChainHeaderReader, ), nil } -func (self *DummyEngine) CalcDifficulty(chain consensus.ChainHeaderReader, time uint64, parent *types.Header) *big.Int { +func (*DummyEngine) CalcDifficulty(chain consensus.ChainHeaderReader, time uint64, parent *types.Header) *big.Int { return big.NewInt(1) } -func (self *DummyEngine) Close() error { +func (*DummyEngine) Close() error { return nil } diff --git a/core/bench_test.go b/core/bench_test.go index d7b353fb8c..0e8f7444bf 100644 --- a/core/bench_test.go +++ b/core/bench_test.go @@ -175,7 +175,7 @@ func benchInsertChain(b *testing.B, disk bool, gen func(int, *BlockGen)) { // generator function. gspec := &Genesis{ Config: params.TestChainConfig, - Alloc: GenesisAlloc{benchRootAddr: {Balance: benchRootFunds}}, + Alloc: types.GenesisAlloc{benchRootAddr: {Balance: benchRootFunds}}, } _, chain, _, _ := GenerateChainWithGenesis(gspec, dummy.NewCoinbaseFaker(), b.N, 10, gen) @@ -229,7 +229,7 @@ func BenchmarkChainWrite_full_500k(b *testing.B) { // makeChainForBench writes a given number of headers or empty blocks/receipts // into a database. -func makeChainForBench(db ethdb.Database, full bool, count uint64) { +func makeChainForBench(db ethdb.Database, genesis *Genesis, full bool, count uint64) { var hash common.Hash for n := uint64(0); n < count; n++ { header := &types.Header{ @@ -241,13 +241,16 @@ func makeChainForBench(db ethdb.Database, full bool, count uint64) { TxHash: types.EmptyTxsHash, ReceiptHash: types.EmptyReceiptsHash, } + if n == 0 { + header = genesis.ToBlock().Header() + } hash = header.Hash() rawdb.WriteHeader(db, header) rawdb.WriteCanonicalHash(db, hash, n) if n == 0 { - rawdb.WriteChainConfig(db, hash, params.TestChainConfig) + rawdb.WriteChainConfig(db, hash, genesis.Config) } rawdb.WriteHeadHeaderHash(db, hash) @@ -260,13 +263,14 @@ func makeChainForBench(db ethdb.Database, full bool, count uint64) { } func benchWriteChain(b *testing.B, full bool, count uint64) { + genesis := &Genesis{Config: params.TestChainConfig} for i := 0; i < b.N; i++ { dir := b.TempDir() db, err := rawdb.NewLevelDBDatabase(dir, 128, 1024, "", false) if err != nil { b.Fatalf("error opening database at %v: %v", dir, err) } - makeChainForBench(db, full, count) + makeChainForBench(db, genesis, full, count) db.Close() } } @@ -278,7 +282,8 @@ func benchReadChain(b *testing.B, full bool, count uint64) { if err != nil { b.Fatalf("error opening database at %v: %v", dir, err) } - makeChainForBench(db, full, count) + genesis := &Genesis{Config: params.TestChainConfig} + makeChainForBench(db, genesis, full, count) db.Close() b.ReportAllocs() @@ -289,7 +294,7 @@ func benchReadChain(b *testing.B, full bool, count uint64) { if err != nil { b.Fatalf("error opening database at %v: %v", dir, err) } - chain, err := NewBlockChain(db, DefaultCacheConfig, nil, dummy.NewFaker(), vm.Config{}, common.Hash{}, false) + chain, err := NewBlockChain(db, DefaultCacheConfig, genesis, dummy.NewFaker(), vm.Config{}, common.Hash{}, false) if err != nil { b.Fatalf("error creating chain: %v", err) } diff --git a/core/block_validator_test.go b/core/block_validator_test.go index 1ea33274b5..b0bc93ff5f 100644 --- a/core/block_validator_test.go +++ b/core/block_validator_test.go @@ -30,6 +30,8 @@ import ( "testing" ) +// TODO: Add TestHeaderVerification + func TestCalcGasLimit(t *testing.T) { for i, tc := range []struct { pGasLimit uint64 diff --git a/core/blockchain.go b/core/blockchain.go index 25a471d1d9..a1272336e3 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -51,8 +51,9 @@ import ( "github.com/ava-labs/subnet-evm/metrics" "github.com/ava-labs/subnet-evm/params" "github.com/ava-labs/subnet-evm/trie" - "github.com/ava-labs/subnet-evm/trie/triedb/hashdb" - "github.com/ava-labs/subnet-evm/trie/triedb/pathdb" + "github.com/ava-labs/subnet-evm/triedb" + "github.com/ava-labs/subnet-evm/triedb/hashdb" + "github.com/ava-labs/subnet-evm/triedb/pathdb" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/lru" "github.com/ethereum/go-ethereum/ethdb" @@ -193,8 +194,8 @@ type CacheConfig struct { } // triedbConfig derives the configures for trie database. -func (c *CacheConfig) triedbConfig() *trie.Config { - config := &trie.Config{Preimages: c.Preimages} +func (c *CacheConfig) triedbConfig() *triedb.Config { + config := &triedb.Config{Preimages: c.Preimages} if c.StateScheme == rawdb.HashScheme { config.HashDB = &hashdb.Config{ CleanCacheSize: c.TrieCleanLimit * 1024 * 1024, @@ -234,6 +235,13 @@ func DefaultCacheConfigWithScheme(scheme string) *CacheConfig { return &config } +// txLookup is wrapper over transaction lookup along with the corresponding +// transaction object. +type txLookup struct { + lookup *rawdb.LegacyTxLookupEntry + transaction *types.Transaction +} + // BlockChain represents the canonical chain given a database with a genesis // block. The Blockchain manages chain imports, reverts, chain reorganisations. // @@ -252,10 +260,11 @@ type BlockChain struct { chainConfig *params.ChainConfig // Chain & network configuration cacheConfig *CacheConfig // Cache configuration for pruning - db ethdb.Database // Low level persistent database to store final content in - snaps *snapshot.Tree // Snapshot tree for fast trie leaf access - triedb *trie.Database // The database handler for maintaining trie nodes. - stateCache state.Database // State database to reuse between imports (contains state cache) + db ethdb.Database // Low level persistent database to store final content in + snaps *snapshot.Tree // Snapshot tree for fast trie leaf access + triedb *triedb.Database // The database handler for maintaining trie nodes. + stateCache state.Database // State database to reuse between imports (contains state cache) + txIndexer *txIndexer // Transaction indexer, might be nil if not enabled stateManager TrieWriter hc *HeaderChain @@ -277,13 +286,13 @@ type BlockChain struct { currentBlock atomic.Pointer[types.Header] // Current head of the block chain - bodyCache *lru.Cache[common.Hash, *types.Body] // Cache for the most recent block bodies - receiptsCache *lru.Cache[common.Hash, []*types.Receipt] // Cache for the most recent receipts per block - blockCache *lru.Cache[common.Hash, *types.Block] // Cache for the most recent entire blocks - txLookupCache *lru.Cache[common.Hash, *rawdb.LegacyTxLookupEntry] // Cache for the most recent transaction lookup data. - badBlocks *lru.Cache[common.Hash, *badBlock] // Cache for bad blocks - feeConfigCache *lru.Cache[common.Hash, *cacheableFeeConfig] // Cache for the most recent feeConfig lookup data. - coinbaseConfigCache *lru.Cache[common.Hash, *cacheableCoinbaseConfig] // Cache for the most recent coinbaseConfig lookup data. + bodyCache *lru.Cache[common.Hash, *types.Body] // Cache for the most recent block bodies + receiptsCache *lru.Cache[common.Hash, []*types.Receipt] // Cache for the most recent receipts per block + blockCache *lru.Cache[common.Hash, *types.Block] // Cache for the most recent entire blocks + txLookupCache *lru.Cache[common.Hash, txLookup] // Cache for the most recent transaction lookup data. + badBlocks *lru.Cache[common.Hash, *badBlock] // Cache for bad blocks + feeConfigCache *lru.Cache[common.Hash, *cacheableFeeConfig] // Cache for the most recent feeConfig lookup data. + coinbaseConfigCache *lru.Cache[common.Hash, *cacheableCoinbaseConfig] // Cache for the most recent coinbaseConfig lookup data. stopping atomic.Bool // false if chain is running, true when stopped @@ -351,7 +360,7 @@ func NewBlockChain( return nil, errCacheConfigNotSpecified } // Open trie database with provided config - triedb := trie.NewDatabase(db, cacheConfig.triedbConfig()) + triedb := triedb.NewDatabase(db, cacheConfig.triedbConfig()) // Setup the genesis block, commit the provided genesis specification // to database if the genesis block is not present yet, or load the @@ -379,7 +388,7 @@ func NewBlockChain( bodyCache: lru.NewCache[common.Hash, *types.Body](bodyCacheLimit), receiptsCache: lru.NewCache[common.Hash, []*types.Receipt](receiptsCacheLimit), blockCache: lru.NewCache[common.Hash, *types.Block](blockCacheLimit), - txLookupCache: lru.NewCache[common.Hash, *rawdb.LegacyTxLookupEntry](txLookupCacheLimit), + txLookupCache: lru.NewCache[common.Hash, txLookup](txLookupCacheLimit), badBlocks: lru.NewCache[common.Hash, *badBlock](badBlockLimit), feeConfigCache: lru.NewCache[common.Hash, *cacheableFeeConfig](feeConfigCacheLimit), coinbaseConfigCache: lru.NewCache[common.Hash, *cacheableCoinbaseConfig](coinbaseConfigCacheLimit), @@ -450,113 +459,19 @@ func NewBlockChain( // if txlookup limit is 0 (uindexing disabled), we don't need to repair the tx index tail. if bc.cacheConfig.TransactionHistory != 0 { latestStateSynced := rawdb.GetLatestSyncPerformed(bc.db) - bc.setTxIndexTail(latestStateSynced) + bc.repairTxIndexTail(latestStateSynced) } // Start processing accepted blocks effects in the background go bc.startAcceptor() - // Start tx indexer/unindexer if required. + // Start tx indexer if it's enabled. if bc.cacheConfig.TransactionHistory != 0 { - bc.wg.Add(1) - var ( - headCh = make(chan ChainEvent, 1) // Buffered to avoid locking up the event feed - sub = bc.SubscribeChainAcceptedEvent(headCh) - ) - go func() { - defer bc.wg.Done() - if sub == nil { - log.Warn("could not create chain accepted subscription to unindex txs") - return - } - defer sub.Unsubscribe() - - bc.maintainTxIndex(headCh) - }() + bc.txIndexer = newTxIndexer(bc.cacheConfig.TransactionHistory, bc) } return bc, nil } -// unindexBlocks unindexes transactions depending on user configuration -func (bc *BlockChain) unindexBlocks(tail uint64, head uint64, done chan struct{}) { - start := time.Now() - txLookupLimit := bc.cacheConfig.TransactionHistory - bc.txIndexTailLock.Lock() - defer func() { - txUnindexTimer.Inc(time.Since(start).Milliseconds()) - bc.txIndexTailLock.Unlock() - close(done) - bc.wg.Done() - }() - - // If head is 0, it means the chain is just initialized and no blocks are inserted, - // so don't need to indexing anything. - if head == 0 { - return - } - - if head-txLookupLimit+1 >= tail { - // Unindex a part of stale indices and forward index tail to HEAD-limit - rawdb.UnindexTransactions(bc.db, tail, head-txLookupLimit+1, bc.quit) - } -} - -// maintainTxIndex is responsible for the deletion of the -// transaction index. This does not support reconstruction of removed indexes. -// Invariant: If TxLookupLimit is 0, it means all tx indices will be preserved. -// Meaning that this function should never be called. -func (bc *BlockChain) maintainTxIndex(headCh <-chan ChainEvent) { - txLookupLimit := bc.cacheConfig.TransactionHistory - - // If the user just upgraded to a new version which supports transaction - // index pruning, write the new tail and remove anything older. - if rawdb.ReadTxIndexTail(bc.db) == nil { - rawdb.WriteTxIndexTail(bc.db, 0) - } - - // Any reindexing done, start listening to chain events and moving the index window - var ( - done chan struct{} // Non-nil if background unindexing or reindexing routine is active. - ) - log.Info("Initialized transaction unindexer", "limit", txLookupLimit) - - // Launch the initial processing if chain is not empty. This step is - // useful in these scenarios that chain has no progress and indexer - // is never triggered. - if head := bc.CurrentBlock(); head != nil && head.Number.Uint64() > txLookupLimit { - done = make(chan struct{}) - tail := rawdb.ReadTxIndexTail(bc.db) - bc.wg.Add(1) - go bc.unindexBlocks(*tail, head.Number.Uint64(), done) - } - - for { - select { - case head := <-headCh: - headNum := head.Block.NumberU64() - if headNum < txLookupLimit { - break - } - - if done == nil { - done = make(chan struct{}) - // Note: tail will not be nil since it is initialized in this function. - tail := rawdb.ReadTxIndexTail(bc.db) - bc.wg.Add(1) - go bc.unindexBlocks(*tail, headNum, done) - } - case <-done: - done = nil - case <-bc.quit: - if done != nil { - log.Info("Waiting background transaction unindexer to exit") - <-done - } - return - } - } -} - // writeBlockAcceptedIndices writes any indices that must be persisted for accepted block. // This includes the following: // - transaction lookup indices @@ -957,7 +872,7 @@ func (bc *BlockChain) ValidateCanonicalChain() error { // Ensure that all of the transactions have been stored correctly in the canonical // chain for txIndex, tx := range txs { - txLookup := bc.GetTransactionLookup(tx.Hash()) + txLookup, _, _ := bc.GetTransactionLookup(tx.Hash()) if txLookup == nil { return fmt.Errorf("failed to find transaction %s", tx.Hash().String()) } @@ -1014,6 +929,10 @@ func (bc *BlockChain) stopWithoutSaving() { if !bc.stopping.CompareAndSwap(false, true) { return } + // Signal shutdown tx indexer. + if bc.txIndexer != nil { + bc.txIndexer.close() + } log.Info("Closing quit channel") close(bc.quit) @@ -1434,7 +1353,7 @@ func (bc *BlockChain) insertBlock(block *types.Block, writes bool) error { // The chain importer is starting and stopping trie prefetchers. If a bad // block or other error is hit however, an early return may not properly // terminate the background threads. This defer ensures that we clean up - // and dangling prefetcher, without defering each and holding on live refs. + // and dangling prefetcher, without deferring each and holding on live refs. if activeState != nil { activeState.StopPrefetcher() } @@ -1646,6 +1565,12 @@ func (bc *BlockChain) reorg(oldHead *types.Header, newHead *types.Block) error { } else { log.Debug("Preference change (rewind to ancestor) occurred", "oldnum", oldHead.Number, "oldhash", oldHead.Hash(), "newnum", newHead.Number(), "newhash", newHead.Hash()) } + // Reset the tx lookup cache in case to clear stale txlookups. + // This is done before writing any new chain data to avoid the + // weird scenario that canonical chain is changed while the + // stale lookups are still cached. + bc.txLookupCache.Purge() + // Insert the new chain(except the head block(reverse order)), // taking care of the proper incremental order. for i := len(newChain) - 1; i >= 1; i-- { @@ -2199,7 +2124,7 @@ func (bc *BlockChain) ResetToStateSyncedBlock(block *types.Block) error { // if txlookup limit is 0 (uindexing disabled), we don't need to repair the tx index tail. if bc.cacheConfig.TransactionHistory != 0 { - bc.setTxIndexTail(block.NumberU64()) + bc.repairTxIndexTail(block.NumberU64()) } // Update all in-memory chain markers @@ -2235,18 +2160,12 @@ func (bc *BlockChain) CacheConfig() *CacheConfig { return bc.cacheConfig } -func (bc *BlockChain) setTxIndexTail(newTail uint64) error { +func (bc *BlockChain) repairTxIndexTail(newTail uint64) error { bc.txIndexTailLock.Lock() defer bc.txIndexTailLock.Unlock() - tailP := rawdb.ReadTxIndexTail(bc.db) - var tailV uint64 - if tailP != nil { - tailV = *tailP - } - - if newTail > tailV { - log.Info("Repairing tx index tail", "old", tailV, "new", newTail) + if curr := rawdb.ReadTxIndexTail(bc.db); curr == nil || *curr < newTail { + log.Info("Repairing tx index tail", "old", curr, "new", newTail) rawdb.WriteTxIndexTail(bc.db, newTail) } return nil diff --git a/core/blockchain_reader.go b/core/blockchain_reader.go index 554c20e730..eba02b712c 100644 --- a/core/blockchain_reader.go +++ b/core/blockchain_reader.go @@ -40,7 +40,7 @@ import ( "github.com/ava-labs/subnet-evm/params" "github.com/ava-labs/subnet-evm/precompile/contracts/feemanager" "github.com/ava-labs/subnet-evm/precompile/contracts/rewardmanager" - "github.com/ava-labs/subnet-evm/trie" + "github.com/ava-labs/subnet-evm/triedb" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/event" ) @@ -203,20 +203,37 @@ func (bc *BlockChain) GetCanonicalHash(number uint64) common.Hash { return bc.hc.GetCanonicalHash(number) } -// GetTransactionLookup retrieves the lookup associate with the given transaction -// hash from the cache or database. -func (bc *BlockChain) GetTransactionLookup(hash common.Hash) *rawdb.LegacyTxLookupEntry { +// GetTransactionLookup retrieves the lookup along with the transaction +// itself associate with the given transaction hash. +// +// An error will be returned if the transaction is not found, and background +// indexing for transactions is still in progress. The transaction might be +// reachable shortly once it's indexed. +// +// A null will be returned in the transaction is not found and background +// transaction indexing is already finished. The transaction is not existent +// from the node's perspective. +func (bc *BlockChain) GetTransactionLookup(hash common.Hash) (*rawdb.LegacyTxLookupEntry, *types.Transaction, error) { // Short circuit if the txlookup already in the cache, retrieve otherwise - if lookup, exist := bc.txLookupCache.Get(hash); exist { - return lookup + if item, exist := bc.txLookupCache.Get(hash); exist { + return item.lookup, item.transaction, nil } tx, blockHash, blockNumber, txIndex := rawdb.ReadTransaction(bc.db, hash) if tx == nil { - return nil + // The transaction is already indexed, the transaction is either + // not existent or not in the range of index, returning null. + return nil, nil, nil } - lookup := &rawdb.LegacyTxLookupEntry{BlockHash: blockHash, BlockIndex: blockNumber, Index: txIndex} - bc.txLookupCache.Add(hash, lookup) - return lookup + lookup := &rawdb.LegacyTxLookupEntry{ + BlockHash: blockHash, + BlockIndex: blockNumber, + Index: txIndex, + } + bc.txLookupCache.Add(hash, txLookup{ + lookup: lookup, + transaction: tx, + }) + return lookup, tx, nil } // HasState checks if state trie is fully present in the database or not. @@ -288,10 +305,15 @@ func (bc *BlockChain) GetVMConfig() *vm.Config { } // TrieDB retrieves the low level trie database used for data storage. -func (bc *BlockChain) TrieDB() *trie.Database { +func (bc *BlockChain) TrieDB() *triedb.Database { return bc.triedb } +// HeaderChain returns the underlying header chain. +func (bc *BlockChain) HeaderChain() *HeaderChain { + return bc.hc +} + // SubscribeRemovedLogsEvent registers a subscription of RemovedLogsEvent. func (bc *BlockChain) SubscribeRemovedLogsEvent(ch chan<- RemovedLogsEvent) event.Subscription { return bc.scope.Track(bc.rmLogsFeed.Subscribe(ch)) diff --git a/core/blockchain_repair_test.go b/core/blockchain_repair_test.go index f124b0efaa..3772aa8da9 100644 --- a/core/blockchain_repair_test.go +++ b/core/blockchain_repair_test.go @@ -39,7 +39,7 @@ import ( "github.com/ava-labs/subnet-evm/core/types" "github.com/ava-labs/subnet-evm/core/vm" "github.com/ava-labs/subnet-evm/params" - "github.com/ava-labs/subnet-evm/trie" + "github.com/ava-labs/subnet-evm/triedb" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" "github.com/stretchr/testify/require" @@ -564,7 +564,7 @@ func testRepairWithScheme(t *testing.T, tt *rewindTest, snapshots bool, scheme s var sideblocks types.Blocks if tt.sidechainBlocks > 0 { genDb := rawdb.NewMemoryDatabase() - gspec.MustCommit(genDb, trie.NewDatabase(genDb, nil)) + 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, common.Big1, nil), signer, key1) @@ -577,7 +577,7 @@ func testRepairWithScheme(t *testing.T, tt *rewindTest, snapshots bool, scheme s } } genDb := rawdb.NewMemoryDatabase() - gspec.MustCommit(genDb, trie.NewDatabase(genDb, nil)) + gspec.MustCommit(genDb, triedb.NewDatabase(genDb, nil)) 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)) diff --git a/core/blockchain_test.go b/core/blockchain_test.go index 8fad072f9e..d1d124f668 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -21,7 +21,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" - "github.com/stretchr/testify/require" + "github.com/holiman/uint256" ) var ( @@ -314,7 +314,7 @@ func testRepopulateMissingTriesParallel(t *testing.T, parallelism int) { genesisBalance := big.NewInt(1000000) gspec := &Genesis{ Config: ¶ms.ChainConfig{HomesteadBlock: new(big.Int), FeeConfig: params.DefaultFeeConfig}, - Alloc: GenesisAlloc{addr1: {Balance: genesisBalance}}, + Alloc: types.GenesisAlloc{addr1: {Balance: genesisBalance}}, } blockchain, err := createBlockChain(chainDB, pruningConfig, gspec, common.Hash{}) @@ -427,7 +427,7 @@ func TestUngracefulAsyncShutdown(t *testing.T) { genesisBalance := big.NewInt(1000000) gspec := &Genesis{ Config: ¶ms.ChainConfig{HomesteadBlock: new(big.Int), FeeConfig: params.DefaultFeeConfig}, - Alloc: GenesisAlloc{addr1: {Balance: genesisBalance}}, + Alloc: types.GenesisAlloc{addr1: {Balance: genesisBalance}}, } blockchain, err := create(chainDB, gspec, common.Hash{}) @@ -481,13 +481,13 @@ func TestUngracefulAsyncShutdown(t *testing.T) { // After inserting all blocks, we should confirm that txs added after the // async worker shutdown cannot be found. for _, tx := range foundTxs { - txLookup := blockchain.GetTransactionLookup(tx) + txLookup, _, _ := blockchain.GetTransactionLookup(tx) if txLookup == nil { t.Fatalf("missing transaction: %v", tx) } } for _, tx := range missingTxs { - txLookup := blockchain.GetTransactionLookup(tx) + txLookup, _, _ := blockchain.GetTransactionLookup(tx) if txLookup != nil { t.Fatalf("transaction should be missing: %v", tx) } @@ -499,9 +499,10 @@ func TestUngracefulAsyncShutdown(t *testing.T) { if nonce != 10 { return fmt.Errorf("expected nonce addr1: 10, found nonce: %d", nonce) } - transferredFunds := big.NewInt(100000) + transferredFunds := uint256.MustFromBig(big.NewInt(100000)) balance1 := sdb.GetBalance(addr1) - expectedBalance1 := new(big.Int).Sub(genesisBalance, transferredFunds) + genesisBalance := uint256.MustFromBig(genesisBalance) + expectedBalance1 := new(uint256.Int).Sub(genesisBalance, transferredFunds) if balance1.Cmp(expectedBalance1) != 0 { return fmt.Errorf("expected addr1 balance: %d, found balance: %d", expectedBalance1, balance1) } @@ -530,7 +531,7 @@ func TestUngracefulAsyncShutdown(t *testing.T) { // We should confirm all transactions can now be queried for _, tx := range allTxs { - txLookup := bc.GetTransactionLookup(tx) + txLookup, _, _ := bc.GetTransactionLookup(tx) if txLookup == nil { t.Fatalf("missing transaction: %v", tx) } @@ -538,207 +539,6 @@ func TestUngracefulAsyncShutdown(t *testing.T) { } } -func TestTransactionIndices(t *testing.T) { - // Configure and generate a sample block chain - require := require.New(t) - var ( - key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") - key2, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a") - addr1 = crypto.PubkeyToAddress(key1.PublicKey) - addr2 = crypto.PubkeyToAddress(key2.PublicKey) - funds = big.NewInt(10000000000000) - gspec = &Genesis{ - Config: ¶ms.ChainConfig{HomesteadBlock: new(big.Int)}, - Alloc: GenesisAlloc{addr1: {Balance: funds}}, - } - signer = types.LatestSigner(gspec.Config) - ) - genDb, blocks, _, err := GenerateChainWithGenesis(gspec, dummy.NewFaker(), 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) - require.NoError(err) - block.AddTx(tx) - }) - require.NoError(err) - - blocks2, _, err := GenerateChain(gspec.Config, blocks[len(blocks)-1], dummy.NewFaker(), 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) - require.NoError(err) - block.AddTx(tx) - }) - require.NoError(err) - - conf := &CacheConfig{ - TrieCleanLimit: 256, - TrieDirtyLimit: 256, - TrieDirtyCommitTarget: 20, - TriePrefetcherParallelism: 4, - Pruning: true, - CommitInterval: 4096, - SnapshotLimit: 256, - SnapshotNoBuild: true, // Ensure the test errors if snapshot initialization fails - AcceptorQueueLimit: 64, - } - - // Init block chain and check all needed indices has been indexed. - chainDB := rawdb.NewMemoryDatabase() - chain, err := createBlockChain(chainDB, conf, gspec, common.Hash{}) - require.NoError(err) - - _, err = chain.InsertChain(blocks) - require.NoError(err) - - for _, block := range blocks { - err := chain.Accept(block) - require.NoError(err) - } - chain.DrainAcceptorQueue() - - lastAcceptedBlock := blocks[len(blocks)-1] - require.Equal(lastAcceptedBlock.Hash(), chain.CurrentHeader().Hash()) - - CheckTxIndices(t, nil, lastAcceptedBlock.NumberU64(), chain.db, false) // check all indices has been indexed - chain.Stop() - - // Reconstruct a block chain which only reserves limited tx indices - // 128 blocks were previously indexed. Now we add a new block at each test step. - limits := []uint64{ - 0, /* tip: 129 reserve all (don't run) */ - 131, /* tip: 130 reserve all */ - 140, /* tip: 131 reserve all */ - 64, /* tip: 132, limit:64 */ - 32, /* tip: 133, limit:32 */ - } - for i, l := range limits { - t.Run(fmt.Sprintf("test-%d, limit: %d", i+1, l), func(t *testing.T) { - conf.TransactionHistory = l - - chain, err := createBlockChain(chainDB, conf, gspec, lastAcceptedBlock.Hash()) - require.NoError(err) - - tail := getTail(l, lastAcceptedBlock.NumberU64()) - // check if startup indices are correct - CheckTxIndices(t, tail, lastAcceptedBlock.NumberU64(), chain.db, false) - - newBlks := blocks2[i : i+1] - _, err = chain.InsertChain(newBlks) // Feed chain a higher block to trigger indices updater. - require.NoError(err) - - lastAcceptedBlock = newBlks[0] - err = chain.Accept(lastAcceptedBlock) // Accept the block to trigger indices updater. - require.NoError(err) - chain.DrainAcceptorQueue() - - tail = getTail(l, lastAcceptedBlock.NumberU64()) - // check if indices are updated correctly - CheckTxIndices(t, tail, lastAcceptedBlock.NumberU64(), chain.db, false) - chain.Stop() - }) - } -} - -func getTail(limit uint64, lastAccepted uint64) *uint64 { - if limit == 0 { - return nil - } - var tail uint64 - if lastAccepted > limit { - // tail should be the oldest block number which is indexed - // i.e the first block number that's in the lookup range - tail = lastAccepted - limit + 1 - } - return &tail -} - -func TestTransactionSkipIndexing(t *testing.T) { - // Configure and generate a sample block chain - require := require.New(t) - var ( - key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") - key2, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a") - addr1 = crypto.PubkeyToAddress(key1.PublicKey) - addr2 = crypto.PubkeyToAddress(key2.PublicKey) - funds = big.NewInt(10000000000000) - gspec = &Genesis{ - Config: ¶ms.ChainConfig{HomesteadBlock: new(big.Int)}, - Alloc: GenesisAlloc{addr1: {Balance: funds}}, - } - signer = types.LatestSigner(gspec.Config) - ) - genDb, blocks, _, err := GenerateChainWithGenesis(gspec, dummy.NewCoinbaseFaker(), 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) - require.NoError(err) - block.AddTx(tx) - }) - require.NoError(err) - - blocks2, _, err := GenerateChain(gspec.Config, blocks[len(blocks)-1], dummy.NewCoinbaseFaker(), 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) - require.NoError(err) - block.AddTx(tx) - }) - require.NoError(err) - - conf := &CacheConfig{ - TrieCleanLimit: 256, - TrieDirtyLimit: 256, - TrieDirtyCommitTarget: 20, - TriePrefetcherParallelism: 4, - Pruning: true, - CommitInterval: 4096, - SnapshotLimit: 256, - SnapshotNoBuild: true, // Ensure the test errors if snapshot initialization fails - AcceptorQueueLimit: 64, - SkipTxIndexing: true, - } - - // test1: Init block chain and check all indices has been skipped. - chainDB := rawdb.NewMemoryDatabase() - chain, err := createAndInsertChain(chainDB, conf, gspec, blocks, common.Hash{}, - func(b *types.Block) { - bNumber := b.NumberU64() - checkTxIndicesHelper(t, nil, bNumber+1, bNumber+1, bNumber, chainDB, false) // check all indices has been skipped - }) - require.NoError(err) - chain.Stop() - - // test2: specify lookuplimit with tx index skipping enabled. Blocks should not be indexed but tail should be updated. - conf.TransactionHistory = 2 - chainDB = rawdb.NewMemoryDatabase() - chain, err = createAndInsertChain(chainDB, conf, gspec, blocks, common.Hash{}, - func(b *types.Block) { - bNumber := b.NumberU64() - tail := bNumber - conf.TransactionHistory + 1 - checkTxIndicesHelper(t, &tail, bNumber+1, bNumber+1, bNumber, chainDB, false) // check all indices has been skipped - }) - require.NoError(err) - chain.Stop() - - // test3: tx index skipping and unindexer disabled. Blocks should be indexed and tail should be updated. - conf.TransactionHistory = 0 - conf.SkipTxIndexing = false - chainDB = rawdb.NewMemoryDatabase() - chain, err = createAndInsertChain(chainDB, conf, gspec, blocks, common.Hash{}, - func(b *types.Block) { - bNumber := b.NumberU64() - checkTxIndicesHelper(t, nil, 0, bNumber, bNumber, chainDB, false) // check all indices has been indexed - }) - require.NoError(err) - chain.Stop() - - // now change tx index skipping to true and check that the indices are skipped for the last block - // and old indices are removed up to the tail, but [tail, current) indices are still there. - conf.TransactionHistory = 2 - conf.SkipTxIndexing = true - chain, err = createAndInsertChain(chainDB, conf, gspec, blocks2[0:1], chain.CurrentHeader().Hash(), - func(b *types.Block) { - bNumber := b.NumberU64() - tail := bNumber - conf.TransactionHistory + 1 - checkTxIndicesHelper(t, &tail, tail, bNumber-1, bNumber, chainDB, false) - }) - require.NoError(err) - chain.Stop() -} - // TestCanonicalHashMarker tests all the canonical hash markers are updated/deleted // correctly in case reorg is called. func TestCanonicalHashMarker(t *testing.T) { @@ -785,7 +585,7 @@ func testCanonicalHashMarker(t *testing.T, scheme string) { var ( gspec = &Genesis{ Config: params.TestChainConfig, - Alloc: GenesisAlloc{}, + Alloc: types.GenesisAlloc{}, BaseFee: big.NewInt(params.TestInitialBaseFee), } engine = dummy.NewCoinbaseFaker() @@ -948,7 +748,7 @@ func testCreateThenDelete(t *testing.T, config *params.ChainConfig) { }...) gspec := &Genesis{ Config: config, - Alloc: GenesisAlloc{ + Alloc: types.GenesisAlloc{ address: {Balance: funds}, }, } @@ -1034,7 +834,7 @@ func TestDeleteThenCreate(t *testing.T) { gspec := &Genesis{ Config: params.TestChainConfig, - Alloc: GenesisAlloc{ + Alloc: types.GenesisAlloc{ address: {Balance: funds}, }, } @@ -1150,7 +950,7 @@ func TestTransientStorageReset(t *testing.T) { }...) gspec := &Genesis{ Config: params.TestChainConfig, - Alloc: GenesisAlloc{ + Alloc: types.GenesisAlloc{ address: {Balance: funds}, }, } @@ -1218,7 +1018,7 @@ func TestEIP3651(t *testing.T) { gspec = &Genesis{ Config: params.TestChainConfig, Timestamp: uint64(upgrade.InitiallyActiveTime.Unix()), - Alloc: GenesisAlloc{ + Alloc: types.GenesisAlloc{ addr1: {Balance: funds}, addr2: {Balance: funds}, // The address 0xAAAA sloads 0x00 and 0x01 @@ -1296,7 +1096,7 @@ func TestEIP3651(t *testing.T) { // Note this differs from go-ethereum where the miner receives the gasUsed * block baseFee, // as our handling of the coinbase payment is different. // Note we use block.GasUsed() here as there is only one tx. - actual := state.GetBalance(block.Coinbase()) + actual := state.GetBalance(block.Coinbase()).ToBig() tx := block.Transactions()[0] gasPrice := new(big.Int).Add(block.BaseFee(), tx.EffectiveGasTipValue(block.BaseFee())) expected := new(big.Int).SetUint64(block.GasUsed() * gasPrice.Uint64()) @@ -1307,31 +1107,8 @@ func TestEIP3651(t *testing.T) { // 4: Ensure the tx sender paid for the gasUsed * (block baseFee + effectiveGasTip). // Note this differs from go-ethereum where the miner receives the gasUsed * block baseFee, // as our handling of the coinbase payment is different. - actual = new(big.Int).Sub(funds, state.GetBalance(addr1)) + actual = new(big.Int).Sub(funds, state.GetBalance(addr1).ToBig()) if actual.Cmp(expected) != 0 { t.Fatalf("sender balance incorrect: expected %d, got %d", expected, actual) } } - -func createAndInsertChain(db ethdb.Database, cacheConfig *CacheConfig, gspec *Genesis, blocks types.Blocks, lastAcceptedHash common.Hash, accepted func(*types.Block)) (*BlockChain, error) { - chain, err := createBlockChain(db, cacheConfig, gspec, lastAcceptedHash) - if err != nil { - return nil, err - } - _, err = chain.InsertChain(blocks) - if err != nil { - return nil, err - } - for _, block := range blocks { - err := chain.Accept(block) - if err != nil { - return nil, err - } - chain.DrainAcceptorQueue() - if accepted != nil { - accepted(block) - } - } - - return chain, nil -} diff --git a/core/chain_makers.go b/core/chain_makers.go index e34330d44f..ad5c106cf3 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -40,9 +40,10 @@ import ( "github.com/ava-labs/subnet-evm/core/types" "github.com/ava-labs/subnet-evm/core/vm" "github.com/ava-labs/subnet-evm/params" - "github.com/ava-labs/subnet-evm/trie" + "github.com/ava-labs/subnet-evm/triedb" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/ethdb" + "github.com/holiman/uint256" ) // BlockGen creates blocks for testing. @@ -169,7 +170,7 @@ func (b *BlockGen) AddTxWithVMConfig(tx *types.Transaction, config vm.Config) { } // GetBalance returns the balance of the given address at the generated block. -func (b *BlockGen) GetBalance(addr common.Address) *big.Int { +func (b *BlockGen) GetBalance(addr common.Address) *uint256.Int { return b.statedb.GetBalance(addr) } @@ -279,7 +280,7 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse } cm := newChainMaker(parent, config, engine) - genblock := func(i int, parent *types.Block, triedb *trie.Database, statedb *state.StateDB) (*types.Block, types.Receipts, error) { + genblock := func(i int, parent *types.Block, triedb *triedb.Database, statedb *state.StateDB) (*types.Block, types.Receipts, error) { b := &BlockGen{i: i, cm: cm, parent: parent, statedb: statedb, engine: engine} b.header = cm.makeHeader(parent, gap, statedb, b.engine) @@ -311,8 +312,9 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse } return block, b.receipts, nil } + // Forcibly use hash-based state scheme for retaining all nodes in disk. - triedb := trie.NewDatabase(db, trie.HashDefaults) + triedb := triedb.NewDatabase(db, triedb.HashDefaults) defer triedb.Close() for i := 0; i < n; i++ { @@ -360,7 +362,7 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse // then generate chain on top. func GenerateChainWithGenesis(genesis *Genesis, engine consensus.Engine, n int, gap uint64, gen func(int, *BlockGen)) (ethdb.Database, []*types.Block, []types.Receipts, error) { db := rawdb.NewMemoryDatabase() - triedb := trie.NewDatabase(db, trie.HashDefaults) + triedb := triedb.NewDatabase(db, triedb.HashDefaults) defer triedb.Close() _, err := genesis.Commit(db, triedb) if err != nil { diff --git a/core/chain_makers_test.go b/core/chain_makers_test.go index c3f6f576e4..19e27299bc 100644 --- a/core/chain_makers_test.go +++ b/core/chain_makers_test.go @@ -35,7 +35,7 @@ import ( "github.com/ava-labs/subnet-evm/core/types" "github.com/ava-labs/subnet-evm/core/vm" "github.com/ava-labs/subnet-evm/params" - "github.com/ava-labs/subnet-evm/trie" + "github.com/ava-labs/subnet-evm/triedb" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" ) @@ -55,9 +55,9 @@ func ExampleGenerateChain() { // Ensure that key1 has some funds in the genesis block. gspec := &Genesis{ Config: ¶ms.ChainConfig{HomesteadBlock: new(big.Int)}, - Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(1000000)}}, + Alloc: types.GenesisAlloc{addr1: {Balance: big.NewInt(1000000)}}, } - genesis := gspec.MustCommit(genDb, trie.NewDatabase(genDb, trie.HashDefaults)) + genesis := gspec.MustCommit(genDb, triedb.NewDatabase(genDb, triedb.HashDefaults)) // This call generates a chain of 3 blocks. The function runs for // each block and adds different features to gen based on the diff --git a/core/error.go b/core/error.go index eac66628d6..2a23e691d0 100644 --- a/core/error.go +++ b/core/error.go @@ -109,4 +109,10 @@ var ( // ErrBlobFeeCapTooLow is returned if the transaction fee cap is less than the // blob gas fee of the block. ErrBlobFeeCapTooLow = errors.New("max fee per blob gas less than block blob gas fee") + + // ErrMissingBlobHashes is returned if a blob transaction has no blob hashes. + ErrMissingBlobHashes = errors.New("blob transaction missing blob hashes") + + // ErrBlobTxCreate is returned if a blob transaction has no explicit to field. + ErrBlobTxCreate = errors.New("blob transaction of type create") ) diff --git a/core/evm.go b/core/evm.go index 326a530af4..0b732bb8ae 100644 --- a/core/evm.go +++ b/core/evm.go @@ -36,7 +36,7 @@ import ( "github.com/ava-labs/subnet-evm/predicate" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/log" - //"github.com/ethereum/go-ethereum/log" + "github.com/holiman/uint256" ) // ChainContext supports retrieving headers and consensus parameters from the @@ -165,12 +165,12 @@ func GetHashFn(ref *types.Header, chain ChainContext) func(n uint64) common.Hash // CanTransfer checks whether there are enough funds in the address' account to make a transfer. // This does not take the necessary gas in to account to make the transfer valid. -func CanTransfer(db vm.StateDB, addr common.Address, amount *big.Int) bool { +func CanTransfer(db vm.StateDB, addr common.Address, amount *uint256.Int) bool { return db.GetBalance(addr).Cmp(amount) >= 0 } // Transfer subtracts amount from sender and adds amount to recipient using the given Db -func Transfer(db vm.StateDB, sender, recipient common.Address, amount *big.Int) { +func Transfer(db vm.StateDB, sender, recipient common.Address, amount *uint256.Int) { db.SubBalance(sender, amount) db.AddBalance(recipient, amount) } diff --git a/core/gen_genesis.go b/core/gen_genesis.go index d2938b70d0..13f61a27f6 100644 --- a/core/gen_genesis.go +++ b/core/gen_genesis.go @@ -7,6 +7,7 @@ import ( "errors" "math/big" + "github.com/ava-labs/subnet-evm/core/types" "github.com/ava-labs/subnet-evm/params" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" @@ -18,24 +19,24 @@ var _ = (*genesisSpecMarshaling)(nil) // MarshalJSON marshals as JSON. func (g Genesis) MarshalJSON() ([]byte, error) { type Genesis struct { - Config *params.ChainConfig `json:"config"` - Nonce math.HexOrDecimal64 `json:"nonce"` - Timestamp math.HexOrDecimal64 `json:"timestamp"` - ExtraData hexutil.Bytes `json:"extraData"` - GasLimit math.HexOrDecimal64 `json:"gasLimit" gencodec:"required"` - Difficulty *math.HexOrDecimal256 `json:"difficulty" gencodec:"required"` - Mixhash common.Hash `json:"mixHash"` - Coinbase common.Address `json:"coinbase"` - Alloc map[common.UnprefixedAddress]GenesisAccount `json:"alloc" gencodec:"required"` - AirdropHash common.Hash `json:"airdropHash"` - AirdropAmount *math.HexOrDecimal256 `json:"airdropAmount"` - AirdropData []byte `json:"-"` - Number math.HexOrDecimal64 `json:"number"` - GasUsed math.HexOrDecimal64 `json:"gasUsed"` - ParentHash common.Hash `json:"parentHash"` - BaseFee *math.HexOrDecimal256 `json:"baseFeePerGas"` - ExcessBlobGas *math.HexOrDecimal64 `json:"excessBlobGas"` - BlobGasUsed *math.HexOrDecimal64 `json:"blobGasUsed"` + Config *params.ChainConfig `json:"config"` + Nonce math.HexOrDecimal64 `json:"nonce"` + Timestamp math.HexOrDecimal64 `json:"timestamp"` + ExtraData hexutil.Bytes `json:"extraData"` + GasLimit math.HexOrDecimal64 `json:"gasLimit" gencodec:"required"` + Difficulty *math.HexOrDecimal256 `json:"difficulty" gencodec:"required"` + Mixhash common.Hash `json:"mixHash"` + Coinbase common.Address `json:"coinbase"` + Alloc map[common.UnprefixedAddress]types.Account `json:"alloc" gencodec:"required"` + AirdropHash common.Hash `json:"airdropHash"` + AirdropAmount *math.HexOrDecimal256 `json:"airdropAmount"` + AirdropData []byte `json:"-"` + Number math.HexOrDecimal64 `json:"number"` + GasUsed math.HexOrDecimal64 `json:"gasUsed"` + ParentHash common.Hash `json:"parentHash"` + BaseFee *math.HexOrDecimal256 `json:"baseFeePerGas"` + ExcessBlobGas *math.HexOrDecimal64 `json:"excessBlobGas"` + BlobGasUsed *math.HexOrDecimal64 `json:"blobGasUsed"` } var enc Genesis enc.Config = g.Config @@ -47,7 +48,7 @@ func (g Genesis) MarshalJSON() ([]byte, error) { enc.Mixhash = g.Mixhash enc.Coinbase = g.Coinbase if g.Alloc != nil { - enc.Alloc = make(map[common.UnprefixedAddress]GenesisAccount, len(g.Alloc)) + enc.Alloc = make(map[common.UnprefixedAddress]types.Account, len(g.Alloc)) for k, v := range g.Alloc { enc.Alloc[common.UnprefixedAddress(k)] = v } @@ -67,24 +68,24 @@ func (g Genesis) MarshalJSON() ([]byte, error) { // UnmarshalJSON unmarshals from JSON. func (g *Genesis) UnmarshalJSON(input []byte) error { type Genesis struct { - Config *params.ChainConfig `json:"config"` - Nonce *math.HexOrDecimal64 `json:"nonce"` - Timestamp *math.HexOrDecimal64 `json:"timestamp"` - ExtraData *hexutil.Bytes `json:"extraData"` - GasLimit *math.HexOrDecimal64 `json:"gasLimit" gencodec:"required"` - Difficulty *math.HexOrDecimal256 `json:"difficulty" gencodec:"required"` - Mixhash *common.Hash `json:"mixHash"` - Coinbase *common.Address `json:"coinbase"` - Alloc map[common.UnprefixedAddress]GenesisAccount `json:"alloc" gencodec:"required"` - AirdropHash *common.Hash `json:"airdropHash"` - AirdropAmount *math.HexOrDecimal256 `json:"airdropAmount"` - AirdropData []byte `json:"-"` - Number *math.HexOrDecimal64 `json:"number"` - GasUsed *math.HexOrDecimal64 `json:"gasUsed"` - ParentHash *common.Hash `json:"parentHash"` - BaseFee *math.HexOrDecimal256 `json:"baseFeePerGas"` - ExcessBlobGas *math.HexOrDecimal64 `json:"excessBlobGas"` - BlobGasUsed *math.HexOrDecimal64 `json:"blobGasUsed"` + Config *params.ChainConfig `json:"config"` + Nonce *math.HexOrDecimal64 `json:"nonce"` + Timestamp *math.HexOrDecimal64 `json:"timestamp"` + ExtraData *hexutil.Bytes `json:"extraData"` + GasLimit *math.HexOrDecimal64 `json:"gasLimit" gencodec:"required"` + Difficulty *math.HexOrDecimal256 `json:"difficulty" gencodec:"required"` + Mixhash *common.Hash `json:"mixHash"` + Coinbase *common.Address `json:"coinbase"` + Alloc map[common.UnprefixedAddress]types.Account `json:"alloc" gencodec:"required"` + AirdropHash *common.Hash `json:"airdropHash"` + AirdropAmount *math.HexOrDecimal256 `json:"airdropAmount"` + AirdropData []byte `json:"-"` + Number *math.HexOrDecimal64 `json:"number"` + GasUsed *math.HexOrDecimal64 `json:"gasUsed"` + ParentHash *common.Hash `json:"parentHash"` + BaseFee *math.HexOrDecimal256 `json:"baseFeePerGas"` + ExcessBlobGas *math.HexOrDecimal64 `json:"excessBlobGas"` + BlobGasUsed *math.HexOrDecimal64 `json:"blobGasUsed"` } var dec Genesis if err := json.Unmarshal(input, &dec); err != nil { @@ -119,7 +120,7 @@ func (g *Genesis) UnmarshalJSON(input []byte) error { if dec.Alloc == nil { return errors.New("missing required field 'alloc' for Genesis") } - g.Alloc = make(GenesisAlloc, len(dec.Alloc)) + g.Alloc = make(types.GenesisAlloc, len(dec.Alloc)) for k, v := range dec.Alloc { g.Alloc[common.Address(k)] = v } diff --git a/core/genesis.go b/core/genesis.go index 1b786345a2..7d044289fe 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -27,8 +27,6 @@ package core import ( - "bytes" - "encoding/hex" "encoding/json" "errors" "fmt" @@ -40,20 +38,27 @@ import ( "github.com/ava-labs/subnet-evm/core/types" "github.com/ava-labs/subnet-evm/params" "github.com/ava-labs/subnet-evm/trie" - "github.com/ava-labs/subnet-evm/trie/triedb/pathdb" + "github.com/ava-labs/subnet-evm/triedb" + "github.com/ava-labs/subnet-evm/triedb/pathdb" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/log" + "github.com/holiman/uint256" ) //go:generate go run github.com/fjl/gencodec -type Genesis -field-override genesisSpecMarshaling -out gen_genesis.go -//go:generate go run github.com/fjl/gencodec -type GenesisAccount -field-override genesisAccountMarshaling -out gen_genesis_account.go var errGenesisNoConfig = errors.New("genesis has no chain configuration") +// Deprecated: use types.GenesisAccount instead. +type GenesisAccount = types.Account + +// Deprecated: use types.GenesisAlloc instead. +type GenesisAlloc = types.GenesisAlloc + type Airdrop struct { // Address strings are hex-formatted common.Address Address common.Address `json:"address"` @@ -70,7 +75,7 @@ type Genesis struct { Difficulty *big.Int `json:"difficulty" gencodec:"required"` Mixhash common.Hash `json:"mixHash"` Coinbase common.Address `json:"coinbase"` - Alloc GenesisAlloc `json:"alloc" gencodec:"required"` + Alloc types.GenesisAlloc `json:"alloc" gencodec:"required"` AirdropHash common.Hash `json:"airdropHash"` AirdropAmount *big.Int `json:"airdropAmount"` AirdropData []byte `json:"-"` // provided in a separate file, not serialized in this struct. @@ -85,30 +90,6 @@ type Genesis struct { BlobGasUsed *uint64 `json:"blobGasUsed"` // EIP-4844 } -// GenesisAlloc specifies the initial state that is part of the genesis block. -type GenesisAlloc map[common.Address]GenesisAccount - -func (ga *GenesisAlloc) UnmarshalJSON(data []byte) error { - m := make(map[common.UnprefixedAddress]GenesisAccount) - if err := json.Unmarshal(data, &m); err != nil { - return err - } - *ga = make(GenesisAlloc) - for addr, a := range m { - (*ga)[common.Address(addr)] = a - } - return nil -} - -// GenesisAccount is an account in the state of the genesis block. -type GenesisAccount struct { - Code []byte `json:"code,omitempty"` - Storage map[common.Hash]common.Hash `json:"storage,omitempty"` - Balance *big.Int `json:"balance" gencodec:"required"` - Nonce uint64 `json:"nonce,omitempty"` - PrivateKey []byte `json:"secretKey,omitempty"` // for tests -} - // field type overrides for gencodec type genesisSpecMarshaling struct { Nonce math.HexOrDecimal64 @@ -118,41 +99,13 @@ type genesisSpecMarshaling struct { GasUsed math.HexOrDecimal64 Number math.HexOrDecimal64 Difficulty *math.HexOrDecimal256 - Alloc map[common.UnprefixedAddress]GenesisAccount + Alloc map[common.UnprefixedAddress]types.Account BaseFee *math.HexOrDecimal256 AirdropAmount *math.HexOrDecimal256 ExcessBlobGas *math.HexOrDecimal64 BlobGasUsed *math.HexOrDecimal64 } -type genesisAccountMarshaling struct { - Code hexutil.Bytes - Balance *math.HexOrDecimal256 - Nonce math.HexOrDecimal64 - Storage map[storageJSON]storageJSON - PrivateKey hexutil.Bytes -} - -// storageJSON represents a 256 bit byte array, but allows less than 256 bits when -// unmarshaling from hex. -type storageJSON common.Hash - -func (h *storageJSON) UnmarshalText(text []byte) error { - text = bytes.TrimPrefix(text, []byte("0x")) - if len(text) > 64 { - return fmt.Errorf("too many hex characters in storage key/value %q", text) - } - offset := len(h) - len(text)/2 // pad on the left - if _, err := hex.Decode(h[offset:], text); err != nil { - return fmt.Errorf("invalid hex storage key/value %q", text) - } - return nil -} - -func (h storageJSON) MarshalText() ([]byte, error) { - return hexutil.Bytes(h[:]).MarshalText() -} - // GenesisMismatchError is raised when trying to overwrite an existing // genesis block with an incompatible one. type GenesisMismatchError struct { @@ -179,7 +132,7 @@ func (e *GenesisMismatchError) Error() string { // specify a fork block below the local head block). In case of a conflict, the // error is a *params.ConfigCompatError and the new, unwritten config is returned. func SetupGenesisBlock( - db ethdb.Database, triedb *trie.Database, genesis *Genesis, lastAcceptedHash common.Hash, skipChainConfigCheckCompatible bool, + db ethdb.Database, triedb *triedb.Database, genesis *Genesis, lastAcceptedHash common.Hash, skipChainConfigCheckCompatible bool, ) (*params.ChainConfig, common.Hash, error) { if genesis == nil { return nil, common.Hash{}, ErrNoGenesis @@ -269,21 +222,21 @@ func (g *Genesis) IsVerkle() bool { // ToBlock returns the genesis block according to genesis specification. func (g *Genesis) ToBlock() *types.Block { db := rawdb.NewMemoryDatabase() - return g.toBlock(db, trie.NewDatabase(db, g.trieConfig())) + return g.toBlock(db, triedb.NewDatabase(db, g.trieConfig())) } -func (g *Genesis) trieConfig() *trie.Config { +func (g *Genesis) trieConfig() *triedb.Config { if !g.IsVerkle() { return nil } - return &trie.Config{ + return &triedb.Config{ PathDB: pathdb.Defaults, IsVerkle: true, } } // TODO: migrate this function to "flush" for more similarity with upstream. -func (g *Genesis) toBlock(db ethdb.Database, triedb *trie.Database) *types.Block { +func (g *Genesis) toBlock(db ethdb.Database, triedb *triedb.Database) *types.Block { statedb, err := state.New(types.EmptyRootHash, state.NewDatabaseWithNodeDB(db, triedb), nil) if err != nil { panic(err) @@ -298,8 +251,9 @@ func (g *Genesis) toBlock(db ethdb.Database, triedb *trie.Database) *types.Block if err := json.Unmarshal(g.AirdropData, &airdrop); err != nil { panic(err) } + airdropAmount := uint256.MustFromBig(g.AirdropAmount) for _, alloc := range airdrop { - statedb.SetBalance(alloc.Address, g.AirdropAmount) + statedb.SetBalance(alloc.Address, airdropAmount) } log.Debug( "applied airdrop allocation", @@ -331,7 +285,7 @@ func (g *Genesis) toBlock(db ethdb.Database, triedb *trie.Database) *types.Block // Do custom allocation after airdrop in case an address shows up in standard // allocation for addr, account := range g.Alloc { - statedb.SetBalance(addr, account.Balance) + statedb.SetBalance(addr, uint256.MustFromBig(account.Balance)) statedb.SetCode(addr, account.Code) statedb.SetNonce(addr, account.Nonce) for key, value := range account.Storage { @@ -385,7 +339,7 @@ func (g *Genesis) toBlock(db ethdb.Database, triedb *trie.Database) *types.Block // Commit writes the block and state of a genesis specification to the database. // The block is committed as the canonical head block. -func (g *Genesis) Commit(db ethdb.Database, triedb *trie.Database) (*types.Block, error) { +func (g *Genesis) Commit(db ethdb.Database, triedb *triedb.Database) (*types.Block, error) { block := g.toBlock(db, triedb) if block.Number().Sign() != 0 { return nil, errors.New("can't commit genesis block with number > 0") @@ -412,7 +366,7 @@ func (g *Genesis) Commit(db ethdb.Database, triedb *trie.Database) (*types.Block // MustCommit writes the genesis block and state to db, panicking on error. // The block is committed as the canonical head block. -func (g *Genesis) MustCommit(db ethdb.Database, triedb *trie.Database) *types.Block { +func (g *Genesis) MustCommit(db ethdb.Database, triedb *triedb.Database) *types.Block { block, err := g.Commit(db, triedb) if err != nil { panic(err) @@ -444,7 +398,7 @@ func GenesisBlockForTesting(db ethdb.Database, addr common.Address, balance *big Alloc: GenesisAlloc{addr: {Balance: balance}}, BaseFee: big.NewInt(params.TestMaxBaseFee), } - return g.MustCommit(db, trie.NewDatabase(db, trie.HashDefaults)) + return g.MustCommit(db, triedb.NewDatabase(db, triedb.HashDefaults)) } // ReadBlockByHash reads the block with the given hash from the database. diff --git a/core/genesis_test.go b/core/genesis_test.go index 4b4023e27b..d515f9dddf 100644 --- a/core/genesis_test.go +++ b/core/genesis_test.go @@ -42,7 +42,8 @@ import ( "github.com/ava-labs/subnet-evm/precompile/allowlist" "github.com/ava-labs/subnet-evm/precompile/contracts/deployerallowlist" "github.com/ava-labs/subnet-evm/trie" - "github.com/ava-labs/subnet-evm/trie/triedb/pathdb" + "github.com/ava-labs/subnet-evm/triedb" + "github.com/ava-labs/subnet-evm/triedb/pathdb" "github.com/ava-labs/subnet-evm/utils" "github.com/davecgh/go-spew/spew" "github.com/ethereum/go-ethereum/common" @@ -51,7 +52,7 @@ import ( "github.com/stretchr/testify/require" ) -func setupGenesisBlock(db ethdb.Database, triedb *trie.Database, genesis *Genesis, lastAcceptedHash common.Hash) (*params.ChainConfig, common.Hash, error) { +func setupGenesisBlock(db ethdb.Database, triedb *triedb.Database, genesis *Genesis, lastAcceptedHash common.Hash) (*params.ChainConfig, common.Hash, error) { return SetupGenesisBlock(db, triedb, genesis, lastAcceptedHash, false) } @@ -75,7 +76,7 @@ func testSetupGenesis(t *testing.T, scheme string) { customghash = common.HexToHash("0x4a12fe7bf8d40d152d7e9de22337b115186a4662aa3a97217b36146202bbfc66") customg = Genesis{ Config: &preSubnetConfig, - Alloc: GenesisAlloc{ + Alloc: types.GenesisAlloc{ {1}: {Balance: big.NewInt(1), Storage: map[common.Hash]common.Hash{{1}: {1}}}, }, GasLimit: preSubnetConfig.FeeConfig.GasLimit.Uint64(), @@ -97,7 +98,7 @@ func testSetupGenesis(t *testing.T, scheme string) { { name: "genesis without ChainConfig", fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) { - return setupGenesisBlock(db, trie.NewDatabase(db, newDbConfig(scheme)), new(Genesis), common.Hash{}) + return setupGenesisBlock(db, triedb.NewDatabase(db, newDbConfig(scheme)), new(Genesis), common.Hash{}) }, wantErr: errGenesisNoConfig, wantConfig: nil, @@ -105,7 +106,7 @@ func testSetupGenesis(t *testing.T, scheme string) { { name: "no block in DB, genesis == nil", fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) { - return setupGenesisBlock(db, trie.NewDatabase(db, newDbConfig(scheme)), nil, common.Hash{}) + return setupGenesisBlock(db, triedb.NewDatabase(db, newDbConfig(scheme)), nil, common.Hash{}) }, wantErr: ErrNoGenesis, wantConfig: nil, @@ -113,7 +114,7 @@ func testSetupGenesis(t *testing.T, scheme string) { { name: "custom block in DB, genesis == nil", fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) { - tdb := trie.NewDatabase(db, newDbConfig(scheme)) + tdb := triedb.NewDatabase(db, newDbConfig(scheme)) customg.Commit(db, tdb) return setupGenesisBlock(db, tdb, nil, common.Hash{}) }, @@ -123,7 +124,7 @@ func testSetupGenesis(t *testing.T, scheme string) { { name: "compatible config in DB", fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) { - tdb := trie.NewDatabase(db, newDbConfig(scheme)) + tdb := triedb.NewDatabase(db, newDbConfig(scheme)) oldcustomg.Commit(db, tdb) return setupGenesisBlock(db, tdb, &customg, customghash) }, @@ -135,7 +136,7 @@ func testSetupGenesis(t *testing.T, scheme string) { fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) { // Commit the 'old' genesis block with SubnetEVM transition at 90. // Advance to block #4, past the SubnetEVM transition block of customg. - tdb := trie.NewDatabase(db, newDbConfig(scheme)) + tdb := triedb.NewDatabase(db, newDbConfig(scheme)) genesis, err := oldcustomg.Commit(db, tdb) if err != nil { t.Fatal(err) @@ -224,7 +225,7 @@ func TestStatefulPrecompilesConfigure(t *testing.T) { genesis := &Genesis{ Config: config, - Alloc: GenesisAlloc{ + Alloc: types.GenesisAlloc{ {1}: {Balance: big.NewInt(1), Storage: map[common.Hash]common.Hash{{1}: {1}}}, }, GasLimit: config.FeeConfig.GasLimit.Uint64(), @@ -235,7 +236,7 @@ func TestStatefulPrecompilesConfigure(t *testing.T) { genesisBlock := genesis.ToBlock() genesisRoot := genesisBlock.Root() - _, _, err := setupGenesisBlock(db, trie.NewDatabase(db, trie.HashDefaults), genesis, genesisBlock.Hash()) + _, _, err := setupGenesisBlock(db, triedb.NewDatabase(db, triedb.HashDefaults), genesis, genesisBlock.Hash()) if err != nil { t.Fatal(err) } @@ -257,7 +258,7 @@ func TestPrecompileActivationAfterHeaderBlock(t *testing.T) { db := rawdb.NewMemoryDatabase() customg := Genesis{ Config: params.TestChainConfig, - Alloc: GenesisAlloc{ + Alloc: types.GenesisAlloc{ {1}: {Balance: big.NewInt(1), Storage: map[common.Hash]common.Hash{{1}: {1}}}, }, GasLimit: params.TestChainConfig.FeeConfig.GasLimit.Uint64(), @@ -297,7 +298,7 @@ func TestPrecompileActivationAfterHeaderBlock(t *testing.T) { require.Less(bc.lastAccepted.Time(), *contractDeployerConfig.Timestamp()) // This should not return any error since the last accepted block is before the activation block. - config, _, err := setupGenesisBlock(db, trie.NewDatabase(db, nil), &customg, bc.lastAccepted.Hash()) + config, _, err := setupGenesisBlock(db, triedb.NewDatabase(db, nil), &customg, bc.lastAccepted.Hash()) require.NoError(err) if !reflect.DeepEqual(config, customg.Config) { t.Errorf("returned %v\nwant %v", config, customg.Config) @@ -309,14 +310,14 @@ func TestGenesisWriteUpgradesRegression(t *testing.T) { config := *params.TestChainConfig genesis := &Genesis{ Config: &config, - Alloc: GenesisAlloc{ + Alloc: types.GenesisAlloc{ {1}: {Balance: big.NewInt(1), Storage: map[common.Hash]common.Hash{{1}: {1}}}, }, GasLimit: config.FeeConfig.GasLimit.Uint64(), } db := rawdb.NewMemoryDatabase() - trieDB := trie.NewDatabase(db, trie.HashDefaults) + trieDB := triedb.NewDatabase(db, triedb.HashDefaults) genesisBlock := genesis.MustCommit(db, trieDB) _, _, err := SetupGenesisBlock(db, trieDB, genesis, genesisBlock.Hash(), false) @@ -346,11 +347,11 @@ func TestGenesisWriteUpgradesRegression(t *testing.T) { require.NoError(err) } -func newDbConfig(scheme string) *trie.Config { +func newDbConfig(scheme string) *triedb.Config { if scheme == rawdb.HashScheme { - return trie.HashDefaults + return triedb.HashDefaults } - return &trie.Config{PathDB: pathdb.Defaults} + return &triedb.Config{PathDB: pathdb.Defaults} } func TestVerkleGenesisCommit(t *testing.T) { @@ -375,7 +376,7 @@ func TestVerkleGenesisCommit(t *testing.T) { Config: verkleConfig, Timestamp: verkleTime, Difficulty: big.NewInt(0), - Alloc: GenesisAlloc{ + Alloc: types.GenesisAlloc{ {1}: {Balance: big.NewInt(1), Storage: map[common.Hash]common.Hash{{1}: {1}}}, }, } @@ -387,7 +388,7 @@ func TestVerkleGenesisCommit(t *testing.T) { } db := rawdb.NewMemoryDatabase() - triedb := trie.NewDatabase(db, &trie.Config{IsVerkle: true, PathDB: pathdb.Defaults}) + triedb := triedb.NewDatabase(db, &triedb.Config{IsVerkle: true, PathDB: pathdb.Defaults}) block := genesis.MustCommit(db, triedb) if !bytes.Equal(block.Root().Bytes(), expected) { t.Fatalf("invalid genesis state root, expected %x, got %x", expected, got) diff --git a/core/rawdb/accessors_trie.go b/core/rawdb/accessors_trie.go index 5633c01b7f..e148a4280b 100644 --- a/core/rawdb/accessors_trie.go +++ b/core/rawdb/accessors_trie.go @@ -303,7 +303,7 @@ func ReadStateScheme(db ethdb.Reader) string { return PathScheme } // The root node might be deleted during the initial snap sync, check - // the persistentstent state id then. + // the persistent state id then. if id := ReadPersistentStateID(db); id != 0 { return PathScheme } diff --git a/core/rawdb/chain_iterator.go b/core/rawdb/chain_iterator.go index ee3f6b3978..88133218b5 100644 --- a/core/rawdb/chain_iterator.go +++ b/core/rawdb/chain_iterator.go @@ -135,7 +135,7 @@ func iterateTransactions(db ethdb.Database, from uint64, to uint64, reverse bool // // There is a passed channel, the whole procedure will be interrupted if any // signal received. -func indexTransactions(db ethdb.Database, from uint64, to uint64, interrupt chan struct{}, hook func(uint64) bool) { +func indexTransactions(db ethdb.Database, from uint64, to uint64, interrupt chan struct{}, hook func(uint64) bool, report bool) { // short circuit for invalid range if from >= to { return @@ -145,13 +145,13 @@ func indexTransactions(db ethdb.Database, from uint64, to uint64, interrupt chan batch = db.NewBatch() start = time.Now() logged = start.Add(-7 * time.Second) + // Since we iterate in reverse, we expect the first number to come // in to be [to-1]. Therefore, setting lastNum to means that the - // prqueue gap-evaluation will work correctly - lastNum = to - queue = prque.New[int64, *blockTxHashes](nil) - // for stats reporting - blocks, txs = 0, 0 + // queue gap-evaluation will work correctly + lastNum = to + queue = prque.New[int64, *blockTxHashes](nil) + blocks, txs = 0, 0 // for stats reporting ) for chanDelivery := range hashesCh { // Push the delivery into the queue and process contiguous ranges. @@ -197,37 +197,41 @@ func indexTransactions(db ethdb.Database, from uint64, to uint64, interrupt chan log.Crit("Failed writing batch to db", "error", err) return } + logger := log.Debug + if report { + logger = log.Info + } select { case <-interrupt: - log.Debug("Transaction indexing interrupted", "blocks", blocks, "txs", txs, "tail", lastNum, "elapsed", common.PrettyDuration(time.Since(start))) + logger("Transaction indexing interrupted", "blocks", blocks, "txs", txs, "tail", lastNum, "elapsed", common.PrettyDuration(time.Since(start))) default: - log.Debug("Indexed transactions", "blocks", blocks, "txs", txs, "tail", lastNum, "elapsed", common.PrettyDuration(time.Since(start))) + logger("Indexed transactions", "blocks", blocks, "txs", txs, "tail", lastNum, "elapsed", common.PrettyDuration(time.Since(start))) } } -// // IndexTransactions creates txlookup indices of the specified block range. The from -// // is included while to is excluded. -// // -// // This function iterates canonical chain in reverse order, it has one main advantage: -// // We can write tx index tail flag periodically even without the whole indexing -// // procedure is finished. So that we can resume indexing procedure next time quickly. -// // -// // There is a passed channel, the whole procedure will be interrupted if any -// // signal received. -// func IndexTransactions(db ethdb.Database, from uint64, to uint64, interrupt chan struct{}) { -// indexTransactions(db, from, to, interrupt, nil) -// } +// IndexTransactions creates txlookup indices of the specified block range. The from +// is included while to is excluded. +// +// This function iterates canonical chain in reverse order, it has one main advantage: +// We can write tx index tail flag periodically even without the whole indexing +// procedure is finished. So that we can resume indexing procedure next time quickly. +// +// There is a passed channel, the whole procedure will be interrupted if any +// signal received. +func IndexTransactions(db ethdb.Database, from uint64, to uint64, interrupt chan struct{}, report bool) { + indexTransactions(db, from, to, interrupt, nil, report) +} // indexTransactionsForTesting is the internal debug version with an additional hook. func indexTransactionsForTesting(db ethdb.Database, from uint64, to uint64, interrupt chan struct{}, hook func(uint64) bool) { - indexTransactions(db, from, to, interrupt, hook) + indexTransactions(db, from, to, interrupt, hook, false) } // unindexTransactions removes txlookup indices of the specified block range. // // There is a passed channel, the whole procedure will be interrupted if any // signal received. -func unindexTransactions(db ethdb.Database, from uint64, to uint64, interrupt chan struct{}, hook func(uint64) bool) { +func unindexTransactions(db ethdb.Database, from uint64, to uint64, interrupt chan struct{}, hook func(uint64) bool, report bool) { // short circuit for invalid range if from >= to { return @@ -237,12 +241,12 @@ func unindexTransactions(db ethdb.Database, from uint64, to uint64, interrupt ch batch = db.NewBatch() start = time.Now() logged = start.Add(-7 * time.Second) + // we expect the first number to come in to be [from]. Therefore, setting - // nextNum to from means that the prqueue gap-evaluation will work correctly - nextNum = from - queue = prque.New[int64, *blockTxHashes](nil) - // for stats reporting - blocks, txs = 0, 0 + // nextNum to from means that the queue gap-evaluation will work correctly + nextNum = from + queue = prque.New[int64, *blockTxHashes](nil) + blocks, txs = 0, 0 // for stats reporting ) // Otherwise spin up the concurrent iterator and unindexer for delivery := range hashesCh { @@ -289,11 +293,15 @@ func unindexTransactions(db ethdb.Database, from uint64, to uint64, interrupt ch log.Crit("Failed writing batch to db", "error", err) return } + logger := log.Debug + if report { + logger = log.Info + } select { case <-interrupt: - log.Debug("Transaction unindexing interrupted", "blocks", blocks, "txs", txs, "tail", to, "elapsed", common.PrettyDuration(time.Since(start))) + logger("Transaction unindexing interrupted", "blocks", blocks, "txs", txs, "tail", to, "elapsed", common.PrettyDuration(time.Since(start))) default: - log.Debug("Unindexed transactions", "blocks", blocks, "txs", txs, "tail", to, "elapsed", common.PrettyDuration(time.Since(start))) + logger("Unindexed transactions", "blocks", blocks, "txs", txs, "tail", to, "elapsed", common.PrettyDuration(time.Since(start))) } } @@ -302,11 +310,11 @@ func unindexTransactions(db ethdb.Database, from uint64, to uint64, interrupt ch // // There is a passed channel, the whole procedure will be interrupted if any // signal received. -func UnindexTransactions(db ethdb.Database, from uint64, to uint64, interrupt chan struct{}) { - unindexTransactions(db, from, to, interrupt, nil) +func UnindexTransactions(db ethdb.Database, from uint64, to uint64, interrupt chan struct{}, report bool) { + unindexTransactions(db, from, to, interrupt, nil, report) } // unindexTransactionsForTesting is the internal debug version with an additional hook. func unindexTransactionsForTesting(db ethdb.Database, from uint64, to uint64, interrupt chan struct{}, hook func(uint64) bool) { - unindexTransactions(db, from, to, interrupt, hook) + unindexTransactions(db, from, to, interrupt, hook, false) } diff --git a/core/rawdb/chain_iterator_test.go b/core/rawdb/chain_iterator_test.go index 6242741a76..caca8d94af 100644 --- a/core/rawdb/chain_iterator_test.go +++ b/core/rawdb/chain_iterator_test.go @@ -172,18 +172,18 @@ func TestIndexTransactions(t *testing.T) { t.Fatalf("Transaction tail mismatch") } } - indexTransactionsForTesting(chainDb, 5, 11, nil, nil) + IndexTransactions(chainDb, 5, 11, nil, false) verify(5, 11, true, 5) verify(0, 5, false, 5) - indexTransactionsForTesting(chainDb, 0, 5, nil, nil) + IndexTransactions(chainDb, 0, 5, nil, false) verify(0, 11, true, 0) - UnindexTransactions(chainDb, 0, 5, nil) + UnindexTransactions(chainDb, 0, 5, nil, false) verify(5, 11, true, 5) verify(0, 5, false, 5) - UnindexTransactions(chainDb, 5, 11, nil) + UnindexTransactions(chainDb, 5, 11, nil, false) verify(0, 11, false, 11) // Testing corner cases @@ -200,7 +200,7 @@ func TestIndexTransactions(t *testing.T) { }) verify(9, 11, true, 9) verify(0, 9, false, 9) - indexTransactionsForTesting(chainDb, 0, 9, nil, nil) + IndexTransactions(chainDb, 0, 9, nil, false) signal = make(chan struct{}) var once2 sync.Once diff --git a/core/rawdb/database.go b/core/rawdb/database.go index 85fa6ac114..edf26c6427 100644 --- a/core/rawdb/database.go +++ b/core/rawdb/database.go @@ -460,3 +460,5 @@ func ClearPrefix(db ethdb.KeyValueStore, prefix []byte, keyLen int) error { } return batch.Write() } + +/// TODO: Consider adding ReadChainMetadata diff --git a/core/rlp_test.go b/core/rlp_test.go index f2b30c5908..56fb3ce3d5 100644 --- a/core/rlp_test.go +++ b/core/rlp_test.go @@ -51,7 +51,7 @@ func getBlock(transactions int, uncles int, dataSize int) *types.Block { funds = big.NewInt(50000 * 225000000000 * 200) gspec = &Genesis{ Config: params.TestChainConfig, - Alloc: GenesisAlloc{address: {Balance: funds}}, + Alloc: types.GenesisAlloc{address: {Balance: funds}}, } ) // We need to generate as many blocks +1 as uncles diff --git a/core/state/database.go b/core/state/database.go index 1ef3dd27f2..2e310707ac 100644 --- a/core/state/database.go +++ b/core/state/database.go @@ -35,6 +35,7 @@ import ( "github.com/ava-labs/subnet-evm/trie" "github.com/ava-labs/subnet-evm/trie/trienode" "github.com/ava-labs/subnet-evm/trie/utils" + "github.com/ava-labs/subnet-evm/triedb" "github.com/crate-crypto/go-ipa/banderwagon" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/lru" @@ -77,7 +78,7 @@ type Database interface { DiskDB() ethdb.KeyValueStore // TrieDB returns the underlying trie database for managing trie nodes. - TrieDB() *trie.Database + TrieDB() *triedb.Database } // Trie is a Ethereum Merkle Patricia trie. @@ -160,17 +161,17 @@ func NewDatabase(db ethdb.Database) Database { // NewDatabaseWithConfig creates a backing store for state. The returned database // is safe for concurrent use and retains a lot of collapsed RLP trie nodes in a // large memory cache. -func NewDatabaseWithConfig(db ethdb.Database, config *trie.Config) Database { +func NewDatabaseWithConfig(db ethdb.Database, config *triedb.Config) Database { return &cachingDB{ disk: db, codeSizeCache: lru.NewCache[common.Hash, int](codeSizeCacheSize), codeCache: lru.NewSizeConstrainedCache[common.Hash, []byte](codeCacheSize), - triedb: trie.NewDatabase(db, config), + triedb: triedb.NewDatabase(db, config), } } // NewDatabaseWithNodeDB creates a state database with an already initialized node database. -func NewDatabaseWithNodeDB(db ethdb.Database, triedb *trie.Database) Database { +func NewDatabaseWithNodeDB(db ethdb.Database, triedb *triedb.Database) Database { return &cachingDB{ disk: db, codeSizeCache: lru.NewCache[common.Hash, int](codeSizeCacheSize), @@ -183,7 +184,7 @@ type cachingDB struct { disk ethdb.KeyValueStore codeSizeCache *lru.Cache[common.Hash, int] codeCache *lru.SizeConstrainedCache[common.Hash, []byte] - triedb *trie.Database + triedb *triedb.Database } // OpenTrie opens the main account trie at a specific root hash. @@ -253,6 +254,6 @@ func (db *cachingDB) DiskDB() ethdb.KeyValueStore { } // TrieDB retrieves any intermediate trie-node caching layer. -func (db *cachingDB) TrieDB() *trie.Database { +func (db *cachingDB) TrieDB() *triedb.Database { return db.triedb } diff --git a/core/state/journal.go b/core/state/journal.go index 4ba90fba5f..5e19f2df79 100644 --- a/core/state/journal.go +++ b/core/state/journal.go @@ -27,9 +27,8 @@ package state import ( - "math/big" - "github.com/ethereum/go-ethereum/common" + "github.com/holiman/uint256" ) // journalEntry is a modification entry in the state change journal that can be @@ -113,13 +112,13 @@ type ( selfDestructChange struct { account *common.Address prev bool // whether account had already self-destructed - prevbalance *big.Int + prevbalance *uint256.Int } // Changes to individual accounts. balanceChange struct { account *common.Address - prev *big.Int + prev *uint256.Int } nonceChange struct { account *common.Address diff --git a/core/state/pruner/bloom.go b/core/state/pruner/bloom.go index ca9d8f0a0b..060a952f37 100644 --- a/core/state/pruner/bloom.go +++ b/core/state/pruner/bloom.go @@ -37,17 +37,10 @@ import ( bloomfilter "github.com/holiman/bloomfilter/v2" ) -// stateBloomHasher is a wrapper around a byte blob to satisfy the interface API -// requirements of the bloom library used. It's used to convert a trie hash or -// contract code hash into a 64 bit mini hash. -type stateBloomHasher []byte - -func (f stateBloomHasher) Write(p []byte) (n int, err error) { panic("not implemented") } -func (f stateBloomHasher) Sum(b []byte) []byte { panic("not implemented") } -func (f stateBloomHasher) Reset() { panic("not implemented") } -func (f stateBloomHasher) BlockSize() int { panic("not implemented") } -func (f stateBloomHasher) Size() int { return 8 } -func (f stateBloomHasher) Sum64() uint64 { return binary.BigEndian.Uint64(f) } +// stateBloomHash is used to convert a trie hash or contract code hash into a 64 bit mini hash. +func stateBloomHash(f []byte) uint64 { + return binary.BigEndian.Uint64(f) +} // stateBloom is a bloom filter used during the state conversion(snapshot->state). // The keys of all generated entries will be recorded here so that in the pruning @@ -123,10 +116,10 @@ func (bloom *stateBloom) Put(key []byte, value []byte) error { if !isCode { return errors.New("invalid entry") } - bloom.bloom.Add(stateBloomHasher(codeKey)) + bloom.bloom.AddHash(stateBloomHash(codeKey)) return nil } - bloom.bloom.Add(stateBloomHasher(key)) + bloom.bloom.AddHash(stateBloomHash(key)) return nil } @@ -138,5 +131,5 @@ func (bloom *stateBloom) Delete(key []byte) error { panic("not supported") } // - If it says yes, the key may be contained // - If it says no, the key is definitely not contained. func (bloom *stateBloom) Contain(key []byte) bool { - return bloom.bloom.Contains(stateBloomHasher(key)) + return bloom.bloom.ContainsHash(stateBloomHash(key)) } diff --git a/core/state/pruner/pruner.go b/core/state/pruner/pruner.go index 1fdd4ce22b..96e27d28cd 100644 --- a/core/state/pruner/pruner.go +++ b/core/state/pruner/pruner.go @@ -41,6 +41,7 @@ import ( "github.com/ava-labs/subnet-evm/core/state/snapshot" "github.com/ava-labs/subnet-evm/core/types" "github.com/ava-labs/subnet-evm/trie" + "github.com/ava-labs/subnet-evm/triedb" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/log" @@ -96,7 +97,7 @@ func NewPruner(db ethdb.Database, config Config) (*Pruner, error) { return nil, errors.New("failed to load head block") } // Offline pruning is only supported in legacy hash based scheme. - triedb := trie.NewDatabase(db, trie.HashDefaults) + triedb := triedb.NewDatabase(db, triedb.HashDefaults) // Note: we refuse to start a pruning session unless the snapshot disk layer exists, which should prevent // us from ever needing to enter RecoverPruning in an invalid pruning session (a session where we do not have @@ -135,7 +136,7 @@ func prune(maindb ethdb.Database, stateBloom *stateBloom, bloomPath string, star // the trie nodes(and codes) belong to the active state will be filtered // out. A very small part of stale tries will also be filtered because of // the false-positive rate of bloom filter. But the assumption is held here - // that the false-positive is low enough(~0.05%). The probablity of the + // that the false-positive is low enough(~0.05%). The probability of the // dangling node is the state root is super low. So the dangling nodes in // theory will never ever be visited again. var ( @@ -347,7 +348,7 @@ func extractGenesis(db ethdb.Database, stateBloom *stateBloom) error { if genesis == nil { return errors.New("missing genesis block") } - t, err := trie.NewStateTrie(trie.StateTrieID(genesis.Root()), trie.NewDatabase(db, trie.HashDefaults)) + t, err := trie.NewStateTrie(trie.StateTrieID(genesis.Root()), triedb.NewDatabase(db, triedb.HashDefaults)) if err != nil { return err } @@ -371,7 +372,7 @@ func extractGenesis(db ethdb.Database, stateBloom *stateBloom) error { } if acc.Root != types.EmptyRootHash { id := trie.StorageTrieID(genesis.Root(), common.BytesToHash(accIter.LeafKey()), acc.Root) - storageTrie, err := trie.NewStateTrie(id, trie.NewDatabase(db, trie.HashDefaults)) + storageTrie, err := trie.NewStateTrie(id, triedb.NewDatabase(db, triedb.HashDefaults)) if err != nil { return err } diff --git a/core/state/snapshot/context.go b/core/state/snapshot/context.go index d513db4855..35b22c6ea3 100644 --- a/core/state/snapshot/context.go +++ b/core/state/snapshot/context.go @@ -38,6 +38,8 @@ import ( "github.com/ethereum/go-ethereum/log" ) +// generatorStats is a collection of statistics gathered by the snapshot generator +// for logging purposes. type generatorStats struct { wiping chan struct{} // Notification channel if wiping is in progress origin uint64 // Origin prefix where generation started diff --git a/core/state/snapshot/difflayer.go b/core/state/snapshot/difflayer.go index 664cb91721..62175cea25 100644 --- a/core/state/snapshot/difflayer.go +++ b/core/state/snapshot/difflayer.go @@ -53,7 +53,7 @@ var ( aggregatorMemoryLimit = uint64(4 * 1024 * 1024) // aggregatorItemLimit is an approximate number of items that will end up - // in the agregator layer before it's flushed out to disk. A plain account + // in the aggregator layer before it's flushed out to disk. A plain account // weighs around 14B (+hash), a storage slot 32B (+hash), a deleted slot // 0B (+hash). Slots are mostly set/unset in lockstep, so that average at // 16B (+hash). All in all, the average entry seems to be 15+32=47B. Use a @@ -135,47 +135,20 @@ type diffLayer struct { lock sync.RWMutex } -// destructBloomHasher is a wrapper around a common.Hash to satisfy the interface -// API requirements of the bloom library used. It's used to convert a destruct -// event into a 64 bit mini hash. -type destructBloomHasher common.Hash - -func (h destructBloomHasher) Write(p []byte) (n int, err error) { panic("not implemented") } -func (h destructBloomHasher) Sum(b []byte) []byte { panic("not implemented") } -func (h destructBloomHasher) Reset() { panic("not implemented") } -func (h destructBloomHasher) BlockSize() int { panic("not implemented") } -func (h destructBloomHasher) Size() int { return 8 } -func (h destructBloomHasher) Sum64() uint64 { +// destructBloomHash is used to convert a destruct event into a 64 bit mini hash. +func destructBloomHash(h common.Hash) uint64 { return binary.BigEndian.Uint64(h[bloomDestructHasherOffset : bloomDestructHasherOffset+8]) } -// accountBloomHasher is a wrapper around a common.Hash to satisfy the interface -// API requirements of the bloom library used. It's used to convert an account -// hash into a 64 bit mini hash. -type accountBloomHasher common.Hash - -func (h accountBloomHasher) Write(p []byte) (n int, err error) { panic("not implemented") } -func (h accountBloomHasher) Sum(b []byte) []byte { panic("not implemented") } -func (h accountBloomHasher) Reset() { panic("not implemented") } -func (h accountBloomHasher) BlockSize() int { panic("not implemented") } -func (h accountBloomHasher) Size() int { return 8 } -func (h accountBloomHasher) Sum64() uint64 { +// accountBloomHash is used to convert an account hash into a 64 bit mini hash. +func accountBloomHash(h common.Hash) uint64 { return binary.BigEndian.Uint64(h[bloomAccountHasherOffset : bloomAccountHasherOffset+8]) } -// storageBloomHasher is a wrapper around a [2]common.Hash to satisfy the interface -// API requirements of the bloom library used. It's used to convert an account -// hash into a 64 bit mini hash. -type storageBloomHasher [2]common.Hash - -func (h storageBloomHasher) Write(p []byte) (n int, err error) { panic("not implemented") } -func (h storageBloomHasher) Sum(b []byte) []byte { panic("not implemented") } -func (h storageBloomHasher) Reset() { panic("not implemented") } -func (h storageBloomHasher) BlockSize() int { panic("not implemented") } -func (h storageBloomHasher) Size() int { return 8 } -func (h storageBloomHasher) Sum64() uint64 { - return binary.BigEndian.Uint64(h[0][bloomStorageHasherOffset:bloomStorageHasherOffset+8]) ^ - binary.BigEndian.Uint64(h[1][bloomStorageHasherOffset:bloomStorageHasherOffset+8]) +// storageBloomHash is used to convert an account hash and a storage hash into a 64 bit mini hash. +func storageBloomHash(h0, h1 common.Hash) uint64 { + return binary.BigEndian.Uint64(h0[bloomStorageHasherOffset:bloomStorageHasherOffset+8]) ^ + binary.BigEndian.Uint64(h1[bloomStorageHasherOffset:bloomStorageHasherOffset+8]) } // newDiffLayer creates a new diff on top of an existing snapshot, whether that's a low @@ -245,14 +218,14 @@ func (dl *diffLayer) rebloom(origin *diskLayer) { } // Iterate over all the accounts and storage slots and index them for hash := range dl.destructSet { - dl.diffed.Add(destructBloomHasher(hash)) + dl.diffed.AddHash(destructBloomHash(hash)) } for hash := range dl.accountData { - dl.diffed.Add(accountBloomHasher(hash)) + dl.diffed.AddHash(accountBloomHash(hash)) } for accountHash, slots := range dl.storageData { for storageHash := range slots { - dl.diffed.Add(storageBloomHasher{accountHash, storageHash}) + dl.diffed.AddHash(storageBloomHash(accountHash, storageHash)) } } // Calculate the current false positive rate and update the error rate meter. @@ -318,9 +291,9 @@ func (dl *diffLayer) AccountRLP(hash common.Hash) ([]byte, error) { } // Check the bloom filter first whether there's even a point in reaching into // all the maps in all the layers below - hit := dl.diffed.Contains(accountBloomHasher(hash)) + hit := dl.diffed.ContainsHash(accountBloomHash(hash)) if !hit { - hit = dl.diffed.Contains(destructBloomHasher(hash)) + hit = dl.diffed.ContainsHash(destructBloomHash(hash)) } var origin *diskLayer if !hit { @@ -389,9 +362,9 @@ func (dl *diffLayer) Storage(accountHash, storageHash common.Hash) ([]byte, erro dl.lock.RUnlock() return nil, ErrSnapshotStale } - hit := dl.diffed.Contains(storageBloomHasher{accountHash, storageHash}) + hit := dl.diffed.ContainsHash(storageBloomHash(accountHash, storageHash)) if !hit { - hit = dl.diffed.Contains(destructBloomHasher(accountHash)) + hit = dl.diffed.ContainsHash(destructBloomHash(accountHash)) } var origin *diskLayer if !hit { diff --git a/core/state/snapshot/disklayer.go b/core/state/snapshot/disklayer.go index 5e317b1a32..478b6716b1 100644 --- a/core/state/snapshot/disklayer.go +++ b/core/state/snapshot/disklayer.go @@ -33,7 +33,7 @@ import ( "github.com/ava-labs/subnet-evm/core/rawdb" "github.com/ava-labs/subnet-evm/core/types" - "github.com/ava-labs/subnet-evm/trie" + "github.com/ava-labs/subnet-evm/triedb" "github.com/ava-labs/subnet-evm/utils" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/ethdb" @@ -43,7 +43,7 @@ import ( // diskLayer is a low level persistent snapshot built on top of a key-value store. type diskLayer struct { diskdb ethdb.KeyValueStore // Key-value store containing the base snapshot - triedb *trie.Database // Trie node cache for reconstruction purposes + triedb *triedb.Database // Trie node cache for reconstruction purposes cache *utils.MeteredCache // Cache to avoid hitting the disk for direct access blockHash common.Hash // Block hash of the base snapshot diff --git a/core/state/snapshot/disklayer_test.go b/core/state/snapshot/disklayer_test.go index c1fb2f5a36..c5ebf65f86 100644 --- a/core/state/snapshot/disklayer_test.go +++ b/core/state/snapshot/disklayer_test.go @@ -144,7 +144,7 @@ func TestDiskMerge(t *testing.T) { // Retrieve all the data through the disk layer and validate it base = snaps.Snapshot(diffRoot) if _, ok := base.(*diskLayer); !ok { - t.Fatalf("update not flattend into the disk layer") + t.Fatalf("update not flattened into the disk layer") } // assertAccount ensures that an account matches the given blob. @@ -363,7 +363,7 @@ func TestDiskPartialMerge(t *testing.T) { // Retrieve all the data through the disk layer and validate it base = snaps.Snapshot(diffRoot) if _, ok := base.(*diskLayer); !ok { - t.Fatalf("test %d: update not flattend into the disk layer", i) + t.Fatalf("test %d: update not flattened into the disk layer", i) } assertAccount(accNoModNoCache, accNoModNoCache[:]) assertAccount(accNoModCache, accNoModCache[:]) diff --git a/core/state/snapshot/generate.go b/core/state/snapshot/generate.go index 67d470530b..35539ec3a7 100644 --- a/core/state/snapshot/generate.go +++ b/core/state/snapshot/generate.go @@ -34,6 +34,7 @@ import ( "github.com/ava-labs/subnet-evm/core/rawdb" "github.com/ava-labs/subnet-evm/core/types" "github.com/ava-labs/subnet-evm/trie" + "github.com/ava-labs/subnet-evm/triedb" "github.com/ava-labs/subnet-evm/utils" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/ethdb" @@ -49,7 +50,7 @@ const ( // generateSnapshot regenerates a brand new snapshot based on an existing state // database and head block asynchronously. The snapshot is returned immediately // and generation is continued in the background until done. -func generateSnapshot(diskdb ethdb.KeyValueStore, triedb *trie.Database, cache int, blockHash, root common.Hash, wiper chan struct{}) *diskLayer { +func generateSnapshot(diskdb ethdb.KeyValueStore, triedb *triedb.Database, cache int, blockHash, root common.Hash, wiper chan struct{}) *diskLayer { // Wipe any previously existing snapshot from the database if no wiper is // currently in progress. if wiper == nil { diff --git a/core/state/snapshot/generate_test.go b/core/state/snapshot/generate_test.go index 956cc589a3..d50b31211f 100644 --- a/core/state/snapshot/generate_test.go +++ b/core/state/snapshot/generate_test.go @@ -28,7 +28,6 @@ package snapshot import ( "fmt" - "math/big" "os" "testing" "time" @@ -36,13 +35,15 @@ import ( "github.com/ava-labs/subnet-evm/core/rawdb" "github.com/ava-labs/subnet-evm/core/types" "github.com/ava-labs/subnet-evm/trie" - "github.com/ava-labs/subnet-evm/trie/triedb/hashdb" - "github.com/ava-labs/subnet-evm/trie/triedb/pathdb" "github.com/ava-labs/subnet-evm/trie/trienode" + "github.com/ava-labs/subnet-evm/triedb" + "github.com/ava-labs/subnet-evm/triedb/hashdb" + "github.com/ava-labs/subnet-evm/triedb/pathdb" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/rlp" + "github.com/holiman/uint256" "golang.org/x/crypto/sha3" ) @@ -70,9 +71,9 @@ func testGeneration(t *testing.T, scheme string) { var helper = newHelper(scheme) stRoot := helper.makeStorageTrie(common.Hash{}, []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, false) - helper.addTrieAccount("acc-1", &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) - helper.addTrieAccount("acc-2", &types.StateAccount{Balance: big.NewInt(2), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}) - helper.addTrieAccount("acc-3", &types.StateAccount{Balance: big.NewInt(3), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) + helper.addTrieAccount("acc-1", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) + helper.addTrieAccount("acc-2", &types.StateAccount{Balance: uint256.NewInt(2), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}) + helper.addTrieAccount("acc-3", &types.StateAccount{Balance: uint256.NewInt(3), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) helper.makeStorageTrie(hashData([]byte("acc-1")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) helper.makeStorageTrie(hashData([]byte("acc-3")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) @@ -110,16 +111,16 @@ func testGenerateExistentState(t *testing.T, scheme string) { var helper = newHelper(scheme) stRoot := helper.makeStorageTrie(hashData([]byte("acc-1")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) - helper.addTrieAccount("acc-1", &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) - helper.addSnapAccount("acc-1", &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) + helper.addTrieAccount("acc-1", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) + helper.addSnapAccount("acc-1", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) helper.addSnapStorage("acc-1", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}) - helper.addTrieAccount("acc-2", &types.StateAccount{Balance: big.NewInt(2), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}) - helper.addSnapAccount("acc-2", &types.StateAccount{Balance: big.NewInt(2), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}) + helper.addTrieAccount("acc-2", &types.StateAccount{Balance: uint256.NewInt(2), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}) + helper.addSnapAccount("acc-2", &types.StateAccount{Balance: uint256.NewInt(2), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}) stRoot = helper.makeStorageTrie(hashData([]byte("acc-3")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) - helper.addTrieAccount("acc-3", &types.StateAccount{Balance: big.NewInt(3), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) - helper.addSnapAccount("acc-3", &types.StateAccount{Balance: big.NewInt(3), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) + helper.addTrieAccount("acc-3", &types.StateAccount{Balance: uint256.NewInt(3), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) + helper.addSnapAccount("acc-3", &types.StateAccount{Balance: uint256.NewInt(3), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) helper.addSnapStorage("acc-3", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}) root, snap := helper.CommitAndGenerate() @@ -166,20 +167,20 @@ func checkSnapRoot(t *testing.T, snap *diskLayer, trieRoot common.Hash) { type testHelper struct { diskdb ethdb.Database - triedb *trie.Database + triedb *triedb.Database accTrie *trie.StateTrie nodes *trienode.MergedNodeSet } func newHelper(scheme string) *testHelper { diskdb := rawdb.NewMemoryDatabase() - config := &trie.Config{} + config := &triedb.Config{} if scheme == rawdb.PathScheme { config.PathDB = &pathdb.Config{} // disable caching } else { config.HashDB = &hashdb.Config{} // disable caching } - triedb := trie.NewDatabase(diskdb, config) + triedb := triedb.NewDatabase(diskdb, config) accTrie, _ := trie.NewStateTrie(trie.StateTrieID(types.EmptyRootHash), triedb) return &testHelper{ diskdb: diskdb, @@ -270,28 +271,28 @@ func testGenerateExistentStateWithWrongStorage(t *testing.T, scheme string) { helper := newHelper(scheme) // Account one, empty root but non-empty database - helper.addAccount("acc-1", &types.StateAccount{Balance: big.NewInt(1), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}) + helper.addAccount("acc-1", &types.StateAccount{Balance: uint256.NewInt(1), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}) helper.addSnapStorage("acc-1", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}) // Account two, non empty root but empty database stRoot := helper.makeStorageTrie(hashData([]byte("acc-2")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) - helper.addAccount("acc-2", &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) + helper.addAccount("acc-2", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) // Miss slots { // Account three, non empty root but misses slots in the beginning helper.makeStorageTrie(hashData([]byte("acc-3")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) - helper.addAccount("acc-3", &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) + helper.addAccount("acc-3", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) helper.addSnapStorage("acc-3", []string{"key-2", "key-3"}, []string{"val-2", "val-3"}) // Account four, non empty root but misses slots in the middle helper.makeStorageTrie(hashData([]byte("acc-4")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) - helper.addAccount("acc-4", &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) + helper.addAccount("acc-4", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) helper.addSnapStorage("acc-4", []string{"key-1", "key-3"}, []string{"val-1", "val-3"}) // Account five, non empty root but misses slots in the end helper.makeStorageTrie(hashData([]byte("acc-5")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) - helper.addAccount("acc-5", &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) + helper.addAccount("acc-5", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) helper.addSnapStorage("acc-5", []string{"key-1", "key-2"}, []string{"val-1", "val-2"}) } @@ -299,22 +300,22 @@ func testGenerateExistentStateWithWrongStorage(t *testing.T, scheme string) { { // Account six, non empty root but wrong slots in the beginning helper.makeStorageTrie(hashData([]byte("acc-6")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) - helper.addAccount("acc-6", &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) + helper.addAccount("acc-6", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) helper.addSnapStorage("acc-6", []string{"key-1", "key-2", "key-3"}, []string{"badval-1", "val-2", "val-3"}) // Account seven, non empty root but wrong slots in the middle helper.makeStorageTrie(hashData([]byte("acc-7")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) - helper.addAccount("acc-7", &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) + helper.addAccount("acc-7", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) helper.addSnapStorage("acc-7", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "badval-2", "val-3"}) // Account eight, non empty root but wrong slots in the end helper.makeStorageTrie(hashData([]byte("acc-8")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) - helper.addAccount("acc-8", &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) + helper.addAccount("acc-8", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) helper.addSnapStorage("acc-8", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "badval-3"}) // Account 9, non empty root but rotated slots helper.makeStorageTrie(hashData([]byte("acc-9")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) - helper.addAccount("acc-9", &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) + helper.addAccount("acc-9", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) helper.addSnapStorage("acc-9", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-3", "val-2"}) } @@ -322,17 +323,17 @@ func testGenerateExistentStateWithWrongStorage(t *testing.T, scheme string) { { // Account 10, non empty root but extra slots in the beginning helper.makeStorageTrie(hashData([]byte("acc-10")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) - helper.addAccount("acc-10", &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) + helper.addAccount("acc-10", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) helper.addSnapStorage("acc-10", []string{"key-0", "key-1", "key-2", "key-3"}, []string{"val-0", "val-1", "val-2", "val-3"}) // Account 11, non empty root but extra slots in the middle helper.makeStorageTrie(hashData([]byte("acc-11")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) - helper.addAccount("acc-11", &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) + helper.addAccount("acc-11", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) helper.addSnapStorage("acc-11", []string{"key-1", "key-2", "key-2-1", "key-3"}, []string{"val-1", "val-2", "val-2-1", "val-3"}) // Account 12, non empty root but extra slots in the end helper.makeStorageTrie(hashData([]byte("acc-12")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) - helper.addAccount("acc-12", &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) + helper.addAccount("acc-12", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) helper.addSnapStorage("acc-12", []string{"key-1", "key-2", "key-3", "key-4"}, []string{"val-1", "val-2", "val-3", "val-4"}) } @@ -377,25 +378,25 @@ func testGenerateExistentStateWithWrongAccounts(t *testing.T, scheme string) { // Missing accounts, only in the trie { - helper.addTrieAccount("acc-1", &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) // Beginning - helper.addTrieAccount("acc-4", &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) // Middle - helper.addTrieAccount("acc-6", &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) // End + helper.addTrieAccount("acc-1", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) // Beginning + helper.addTrieAccount("acc-4", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) // Middle + helper.addTrieAccount("acc-6", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) // End } // Wrong accounts { - helper.addTrieAccount("acc-2", &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) - helper.addSnapAccount("acc-2", &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: common.Hex2Bytes("0x1234")}) + helper.addTrieAccount("acc-2", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) + helper.addSnapAccount("acc-2", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: common.Hex2Bytes("0x1234")}) - helper.addTrieAccount("acc-3", &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) - helper.addSnapAccount("acc-3", &types.StateAccount{Balance: big.NewInt(1), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}) + helper.addTrieAccount("acc-3", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) + helper.addSnapAccount("acc-3", &types.StateAccount{Balance: uint256.NewInt(1), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}) } // Extra accounts, only in the snap { - helper.addSnapAccount("acc-0", &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) // before the beginning - helper.addSnapAccount("acc-5", &types.StateAccount{Balance: big.NewInt(1), Root: types.EmptyRootHash, CodeHash: common.Hex2Bytes("0x1234")}) // Middle - helper.addSnapAccount("acc-7", &types.StateAccount{Balance: big.NewInt(1), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}) // after the end + helper.addSnapAccount("acc-0", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) // before the beginning + helper.addSnapAccount("acc-5", &types.StateAccount{Balance: uint256.NewInt(1), Root: types.EmptyRootHash, CodeHash: common.Hex2Bytes("0x1234")}) // Middle + helper.addSnapAccount("acc-7", &types.StateAccount{Balance: uint256.NewInt(1), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}) // after the end } root, snap := helper.CommitAndGenerate() @@ -429,9 +430,9 @@ func testGenerateCorruptAccountTrie(t *testing.T, scheme string) { // without any storage slots to keep the test smaller. helper := newHelper(scheme) - helper.addTrieAccount("acc-1", &types.StateAccount{Balance: big.NewInt(1), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}) // 0xc7a30f39aff471c95d8a837497ad0e49b65be475cc0953540f80cfcdbdcd9074 - helper.addTrieAccount("acc-2", &types.StateAccount{Balance: big.NewInt(2), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}) // 0x65145f923027566669a1ae5ccac66f945b55ff6eaeb17d2ea8e048b7d381f2d7 - helper.addTrieAccount("acc-3", &types.StateAccount{Balance: big.NewInt(3), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}) // 0x19ead688e907b0fab07176120dceec244a72aff2f0aa51e8b827584e378772f4 + helper.addTrieAccount("acc-1", &types.StateAccount{Balance: uint256.NewInt(1), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}) // 0xc7a30f39aff471c95d8a837497ad0e49b65be475cc0953540f80cfcdbdcd9074 + helper.addTrieAccount("acc-2", &types.StateAccount{Balance: uint256.NewInt(2), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}) // 0x65145f923027566669a1ae5ccac66f945b55ff6eaeb17d2ea8e048b7d381f2d7 + helper.addTrieAccount("acc-3", &types.StateAccount{Balance: uint256.NewInt(3), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}) // 0x19ead688e907b0fab07176120dceec244a72aff2f0aa51e8b827584e378772f4 root := helper.Commit() // Root: 0xa04693ea110a31037fb5ee814308a6f1d76bdab0b11676bdf4541d2de55ba978 @@ -473,11 +474,11 @@ func testGenerateMissingStorageTrie(t *testing.T, scheme string) { acc3 = hashData([]byte("acc-3")) helper = newHelper(scheme) ) - stRoot := helper.makeStorageTrie(hashData([]byte("acc-1")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) // 0xddefcd9376dd029653ef384bd2f0a126bb755fe84fdcc9e7cf421ba454f2bc67 - helper.addTrieAccount("acc-1", &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) // 0x9250573b9c18c664139f3b6a7a8081b7d8f8916a8fcc5d94feec6c29f5fd4e9e - helper.addTrieAccount("acc-2", &types.StateAccount{Balance: big.NewInt(2), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}) // 0x65145f923027566669a1ae5ccac66f945b55ff6eaeb17d2ea8e048b7d381f2d7 + stRoot := helper.makeStorageTrie(hashData([]byte("acc-1")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) // 0xddefcd9376dd029653ef384bd2f0a126bb755fe84fdcc9e7cf421ba454f2bc67 + helper.addTrieAccount("acc-1", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) // 0x9250573b9c18c664139f3b6a7a8081b7d8f8916a8fcc5d94feec6c29f5fd4e9e + helper.addTrieAccount("acc-2", &types.StateAccount{Balance: uint256.NewInt(2), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}) // 0x65145f923027566669a1ae5ccac66f945b55ff6eaeb17d2ea8e048b7d381f2d7 stRoot = helper.makeStorageTrie(hashData([]byte("acc-3")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) - helper.addTrieAccount("acc-3", &types.StateAccount{Balance: big.NewInt(3), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) // 0x50815097425d000edfc8b3a4a13e175fc2bdcfee8bdfbf2d1ff61041d3c235b2 + helper.addTrieAccount("acc-3", &types.StateAccount{Balance: uint256.NewInt(3), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) // 0x50815097425d000edfc8b3a4a13e175fc2bdcfee8bdfbf2d1ff61041d3c235b2 root := helper.Commit() @@ -513,11 +514,11 @@ func testGenerateCorruptStorageTrie(t *testing.T, scheme string) { // two of which also has the same 3-slot storage trie attached. helper := newHelper(scheme) - stRoot := helper.makeStorageTrie(hashData([]byte("acc-1")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) // 0xddefcd9376dd029653ef384bd2f0a126bb755fe84fdcc9e7cf421ba454f2bc67 - helper.addTrieAccount("acc-1", &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) // 0x9250573b9c18c664139f3b6a7a8081b7d8f8916a8fcc5d94feec6c29f5fd4e9e - helper.addTrieAccount("acc-2", &types.StateAccount{Balance: big.NewInt(2), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}) // 0x65145f923027566669a1ae5ccac66f945b55ff6eaeb17d2ea8e048b7d381f2d7 + stRoot := helper.makeStorageTrie(hashData([]byte("acc-1")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) // 0xddefcd9376dd029653ef384bd2f0a126bb755fe84fdcc9e7cf421ba454f2bc67 + helper.addTrieAccount("acc-1", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) // 0x9250573b9c18c664139f3b6a7a8081b7d8f8916a8fcc5d94feec6c29f5fd4e9e + helper.addTrieAccount("acc-2", &types.StateAccount{Balance: uint256.NewInt(2), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}) // 0x65145f923027566669a1ae5ccac66f945b55ff6eaeb17d2ea8e048b7d381f2d7 stRoot = helper.makeStorageTrie(hashData([]byte("acc-3")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) - helper.addTrieAccount("acc-3", &types.StateAccount{Balance: big.NewInt(3), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) // 0x50815097425d000edfc8b3a4a13e175fc2bdcfee8bdfbf2d1ff61041d3c235b2 + helper.addTrieAccount("acc-3", &types.StateAccount{Balance: uint256.NewInt(3), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) // 0x50815097425d000edfc8b3a4a13e175fc2bdcfee8bdfbf2d1ff61041d3c235b2 root := helper.Commit() @@ -557,7 +558,7 @@ func testGenerateWithExtraAccounts(t *testing.T, scheme string) { []string{"val-1", "val-2", "val-3", "val-4", "val-5"}, true, ) - acc := &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()} + acc := &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()} val, _ := rlp.EncodeToBytes(acc) helper.accTrie.MustUpdate([]byte("acc-1"), val) // 0x9250573b9c18c664139f3b6a7a8081b7d8f8916a8fcc5d94feec6c29f5fd4e9e @@ -577,7 +578,7 @@ func testGenerateWithExtraAccounts(t *testing.T, scheme string) { []string{"val-1", "val-2", "val-3", "val-4", "val-5"}, true, ) - acc := &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()} + acc := &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()} val, _ := rlp.EncodeToBytes(acc) key := hashData([]byte("acc-2")) rawdb.WriteAccountSnapshot(helper.diskdb, key, val) @@ -633,7 +634,7 @@ func testGenerateWithManyExtraAccounts(t *testing.T, scheme string) { []string{"val-1", "val-2", "val-3"}, true, ) - acc := &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()} + acc := &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()} val, _ := rlp.EncodeToBytes(acc) helper.accTrie.MustUpdate([]byte("acc-1"), val) // 0x9250573b9c18c664139f3b6a7a8081b7d8f8916a8fcc5d94feec6c29f5fd4e9e @@ -647,7 +648,7 @@ func testGenerateWithManyExtraAccounts(t *testing.T, scheme string) { { // 100 accounts exist only in snapshot for i := 0; i < 1000; i++ { - acc := &types.StateAccount{Balance: big.NewInt(int64(i)), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()} + acc := &types.StateAccount{Balance: uint256.NewInt(uint64(i)), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()} val, _ := rlp.EncodeToBytes(acc) key := hashData([]byte(fmt.Sprintf("acc-%d", i))) rawdb.WriteAccountSnapshot(helper.diskdb, key, val) @@ -688,7 +689,7 @@ func testGenerateWithExtraBeforeAndAfter(t *testing.T, scheme string) { } helper := newHelper(scheme) { - acc := &types.StateAccount{Balance: big.NewInt(1), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()} + acc := &types.StateAccount{Balance: uint256.NewInt(1), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()} val, _ := rlp.EncodeToBytes(acc) helper.accTrie.MustUpdate(common.HexToHash("0x03").Bytes(), val) helper.accTrie.MustUpdate(common.HexToHash("0x07").Bytes(), val) @@ -729,7 +730,7 @@ func testGenerateWithMalformedSnapdata(t *testing.T, scheme string) { } helper := newHelper(scheme) { - acc := &types.StateAccount{Balance: big.NewInt(1), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()} + acc := &types.StateAccount{Balance: uint256.NewInt(1), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()} val, _ := rlp.EncodeToBytes(acc) helper.accTrie.MustUpdate(common.HexToHash("0x03").Bytes(), val) @@ -771,7 +772,7 @@ func testGenerateFromEmptySnap(t *testing.T, scheme string) { for i := 0; i < 400; i++ { stRoot := helper.makeStorageTrie(hashData([]byte(fmt.Sprintf("acc-%d", i))), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) helper.addTrieAccount(fmt.Sprintf("acc-%d", i), - &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) + &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) } root, snap := helper.CommitAndGenerate() t.Logf("Root: %#x\n", root) // Root: 0x6f7af6d2e1a1bf2b84a3beb3f8b64388465fbc1e274ca5d5d3fc787ca78f59e4 @@ -812,7 +813,7 @@ func testGenerateWithIncompleteStorage(t *testing.T, scheme string) { for i := 0; i < 8; i++ { accKey := fmt.Sprintf("acc-%d", i) stRoot := helper.makeStorageTrie(hashData([]byte(accKey)), stKeys, stVals, true) - helper.addAccount(accKey, &types.StateAccount{Balance: big.NewInt(int64(i)), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) + helper.addAccount(accKey, &types.StateAccount{Balance: uint256.NewInt(uint64(i)), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) var moddedKeys []string var moddedVals []string for ii := 0; ii < 8; ii++ { @@ -909,11 +910,11 @@ func testGenerateCompleteSnapshotWithDanglingStorage(t *testing.T, scheme string var helper = newHelper(scheme) stRoot := helper.makeStorageTrie(hashData([]byte("acc-1")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) - helper.addAccount("acc-1", &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) - helper.addAccount("acc-2", &types.StateAccount{Balance: big.NewInt(1), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}) + helper.addAccount("acc-1", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) + helper.addAccount("acc-2", &types.StateAccount{Balance: uint256.NewInt(1), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}) helper.makeStorageTrie(hashData([]byte("acc-3")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) - helper.addAccount("acc-3", &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) + helper.addAccount("acc-3", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) helper.addSnapStorage("acc-1", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}) helper.addSnapStorage("acc-3", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}) @@ -949,11 +950,11 @@ func testGenerateBrokenSnapshotWithDanglingStorage(t *testing.T, scheme string) var helper = newHelper(scheme) stRoot := helper.makeStorageTrie(hashData([]byte("acc-1")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) - helper.addTrieAccount("acc-1", &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) - helper.addTrieAccount("acc-2", &types.StateAccount{Balance: big.NewInt(2), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}) + helper.addTrieAccount("acc-1", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) + helper.addTrieAccount("acc-2", &types.StateAccount{Balance: uint256.NewInt(2), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}) helper.makeStorageTrie(hashData([]byte("acc-3")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) - helper.addTrieAccount("acc-3", &types.StateAccount{Balance: big.NewInt(3), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) + helper.addTrieAccount("acc-3", &types.StateAccount{Balance: uint256.NewInt(3), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) populateDangling(helper.diskdb) diff --git a/core/state/snapshot/journal.go b/core/state/snapshot/journal.go index f31570f791..6f8d82311f 100644 --- a/core/state/snapshot/journal.go +++ b/core/state/snapshot/journal.go @@ -33,7 +33,7 @@ import ( "time" "github.com/ava-labs/subnet-evm/core/rawdb" - "github.com/ava-labs/subnet-evm/trie" + "github.com/ava-labs/subnet-evm/triedb" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/log" @@ -56,7 +56,7 @@ type journalGenerator struct { // loadSnapshot loads a pre-existing state snapshot backed by a key-value // store. If loading the snapshot from disk is successful, this function also // returns a boolean indicating whether or not the snapshot is fully generated. -func loadSnapshot(diskdb ethdb.KeyValueStore, triedb *trie.Database, cache int, blockHash, root common.Hash, noBuild bool) (snapshot, bool, error) { +func loadSnapshot(diskdb ethdb.KeyValueStore, triedb *triedb.Database, cache int, blockHash, root common.Hash, noBuild bool) (snapshot, bool, error) { // Retrieve the block number and hash of the snapshot, failing if no snapshot // is present in the database (or crashed mid-update). baseBlockHash := rawdb.ReadSnapshotBlockHash(diskdb) diff --git a/core/state/snapshot/snapshot.go b/core/state/snapshot/snapshot.go index 01effb2040..3490d743bf 100644 --- a/core/state/snapshot/snapshot.go +++ b/core/state/snapshot/snapshot.go @@ -37,7 +37,7 @@ import ( "github.com/ava-labs/subnet-evm/core/rawdb" "github.com/ava-labs/subnet-evm/core/types" "github.com/ava-labs/subnet-evm/metrics" - "github.com/ava-labs/subnet-evm/trie" + "github.com/ava-labs/subnet-evm/triedb" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/log" @@ -186,7 +186,7 @@ type Config struct { type Tree struct { config Config // Snapshots configurations diskdb ethdb.KeyValueStore // Persistent database to store the snapshot - triedb *trie.Database // In-memory cache to access the trie through + triedb *triedb.Database // In-memory cache to access the trie through // Collection of all known layers // blockHash -> snapshot blockLayers map[common.Hash]snapshot @@ -208,7 +208,7 @@ type Tree struct { // If the snapshot is missing or the disk layer is broken, the snapshot will be // reconstructed using both the existing data and the state trie. // The repair happens on a background thread. -func New(config Config, diskdb ethdb.KeyValueStore, triedb *trie.Database, blockHash, root common.Hash) (*Tree, error) { +func New(config Config, diskdb ethdb.KeyValueStore, triedb *triedb.Database, blockHash, root common.Hash) (*Tree, error) { // Create a new, empty snapshot tree snap := &Tree{ config: config, diff --git a/core/state/snapshot/snapshot_test.go b/core/state/snapshot/snapshot_test.go index 36a4bb55eb..4d4b476d55 100644 --- a/core/state/snapshot/snapshot_test.go +++ b/core/state/snapshot/snapshot_test.go @@ -29,7 +29,6 @@ package snapshot import ( crand "crypto/rand" "fmt" - "math/big" "math/rand" "testing" "time" @@ -38,6 +37,7 @@ import ( "github.com/ava-labs/subnet-evm/core/types" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/rlp" + "github.com/holiman/uint256" ) // randomHash generates a random blob of data and returns it as a hash. @@ -52,7 +52,7 @@ func randomHash() common.Hash { // randomAccount generates a random account and returns it RLP encoded. func randomAccount() []byte { a := &types.StateAccount{ - Balance: big.NewInt(rand.Int63()), + Balance: uint256.NewInt(rand.Uint64()), Nonce: rand.Uint64(), Root: randomHash(), CodeHash: types.EmptyCodeHash[:], @@ -610,19 +610,19 @@ func TestRebloomOnFlatten(t *testing.T) { t.Fatal("snapshot should be a diffLayer") } - if hitsA != dl.diffed.Contains(accountBloomHasher(addrA)) { + if hitsA != dl.diffed.ContainsHash(accountBloomHash(addrA)) { t.Errorf("expected bloom filter to return %t but got %t", hitsA, !hitsA) } - if hitsB != dl.diffed.Contains(accountBloomHasher(addrB)) { + if hitsB != dl.diffed.ContainsHash(accountBloomHash(addrB)) { t.Errorf("expected bloom filter to return %t but got %t", hitsB, !hitsB) } - if hitsC != dl.diffed.Contains(accountBloomHasher(addrC)) { + if hitsC != dl.diffed.ContainsHash(accountBloomHash(addrC)) { t.Errorf("expected bloom filter to return %t but got %t", hitsC, !hitsC) } - if hitsD != dl.diffed.Contains(accountBloomHasher(addrD)) { + if hitsD != dl.diffed.ContainsHash(accountBloomHash(addrD)) { t.Errorf("expected bloom filter to return %t but got %t", hitsD, !hitsD) } } diff --git a/core/state/state_object.go b/core/state/state_object.go index 9d06e32a27..fbff5a1c48 100644 --- a/core/state/state_object.go +++ b/core/state/state_object.go @@ -30,7 +30,6 @@ import ( "bytes" "fmt" "io" - "math/big" "time" "github.com/ava-labs/subnet-evm/core/types" @@ -39,6 +38,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/rlp" + "github.com/holiman/uint256" ) type Code []byte @@ -103,7 +103,7 @@ type stateObject struct { // empty returns whether the account is considered empty. func (s *stateObject) empty() bool { - return s.data.Nonce == 0 && s.data.Balance.Sign() == 0 && bytes.Equal(s.data.CodeHash, types.EmptyCodeHash.Bytes()) + return s.data.Nonce == 0 && s.data.Balance.IsZero() && bytes.Equal(s.data.CodeHash, types.EmptyCodeHash.Bytes()) } // newObject creates a state object. @@ -415,36 +415,36 @@ func (s *stateObject) commit() (*trienode.NodeSet, error) { // AddBalance adds amount to s's balance. // It is used to add funds to the destination account of a transfer. -func (s *stateObject) AddBalance(amount *big.Int) { +func (s *stateObject) AddBalance(amount *uint256.Int) { // EIP161: We must check emptiness for the objects such that the account // clearing (0,0,0 objects) can take effect. - if amount.Sign() == 0 { + if amount.IsZero() { if s.empty() { s.touch() } return } - s.SetBalance(new(big.Int).Add(s.Balance(), amount)) + s.SetBalance(new(uint256.Int).Add(s.Balance(), amount)) } // SubBalance removes amount from s's balance. // It is used to remove funds from the origin account of a transfer. -func (s *stateObject) SubBalance(amount *big.Int) { - if amount.Sign() == 0 { +func (s *stateObject) SubBalance(amount *uint256.Int) { + if amount.IsZero() { return } - s.SetBalance(new(big.Int).Sub(s.Balance(), amount)) + s.SetBalance(new(uint256.Int).Sub(s.Balance(), amount)) } -func (s *stateObject) SetBalance(amount *big.Int) { +func (s *stateObject) SetBalance(amount *uint256.Int) { s.db.journal.append(balanceChange{ account: &s.address, - prev: new(big.Int).Set(s.data.Balance), + prev: new(uint256.Int).Set(s.data.Balance), }) s.setBalance(amount) } -func (s *stateObject) setBalance(amount *big.Int) { +func (s *stateObject) setBalance(amount *uint256.Int) { s.data.Balance = amount } @@ -543,7 +543,7 @@ func (s *stateObject) CodeHash() []byte { return s.data.CodeHash } -func (s *stateObject) Balance() *big.Int { +func (s *stateObject) Balance() *uint256.Int { return s.data.Balance } diff --git a/core/state/state_test.go b/core/state/state_test.go index 547a599611..e0c3cb41ac 100644 --- a/core/state/state_test.go +++ b/core/state/state_test.go @@ -29,15 +29,15 @@ package state import ( "bytes" "encoding/json" - "math/big" "testing" "github.com/ava-labs/subnet-evm/core/rawdb" "github.com/ava-labs/subnet-evm/core/types" - "github.com/ava-labs/subnet-evm/trie" + "github.com/ava-labs/subnet-evm/triedb" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" + "github.com/holiman/uint256" ) type stateEnv struct { @@ -51,21 +51,78 @@ func newStateEnv() *stateEnv { return &stateEnv{db: db, state: sdb} } +func TestDump(t *testing.T) { + db := rawdb.NewMemoryDatabase() + tdb := NewDatabaseWithConfig(db, &triedb.Config{Preimages: true}) + sdb, _ := New(types.EmptyRootHash, tdb, nil) + s := &stateEnv{db: db, state: sdb} + + // generate a few entries + obj1 := s.state.getOrNewStateObject(common.BytesToAddress([]byte{0x01})) + obj1.AddBalance(uint256.NewInt(22)) + obj2 := s.state.getOrNewStateObject(common.BytesToAddress([]byte{0x01, 0x02})) + obj2.SetCode(crypto.Keccak256Hash([]byte{3, 3, 3, 3, 3, 3, 3}), []byte{3, 3, 3, 3, 3, 3, 3}) + obj3 := s.state.getOrNewStateObject(common.BytesToAddress([]byte{0x02})) + obj3.SetBalance(uint256.NewInt(44)) + + // write some of them to the trie + s.state.updateStateObject(obj1) + s.state.updateStateObject(obj2) + root, _ := s.state.Commit(0, false, false) + + // check that DumpToCollector contains the state objects that are in trie + s.state, _ = New(root, tdb, nil) + got := string(s.state.Dump(nil)) + want := `{ + "root": "71edff0130dd2385947095001c73d9e28d862fc286fca2b922ca6f6f3cddfdd2", + "accounts": { + "0x0000000000000000000000000000000000000001": { + "balance": "22", + "nonce": 0, + "root": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "codeHash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "address": "0x0000000000000000000000000000000000000001", + "key": "0x1468288056310c82aa4c01a7e12a10f8111a0560e72b700555479031b86c357d" + }, + "0x0000000000000000000000000000000000000002": { + "balance": "44", + "nonce": 0, + "root": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "codeHash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "address": "0x0000000000000000000000000000000000000002", + "key": "0xd52688a8f926c816ca1e079067caba944f158e764817b83fc43594370ca9cf62" + }, + "0x0000000000000000000000000000000000000102": { + "balance": "0", + "nonce": 0, + "root": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "codeHash": "0x87874902497a5bb968da31a2998d8f22e949d1ef6214bcdedd8bae24cca4b9e3", + "code": "0x03030303030303", + "address": "0x0000000000000000000000000000000000000102", + "key": "0xa17eacbc25cda025e81db9c5c62868822c73ce097cee2a63e33a2e41268358a1" + } + } +}` + if got != want { + t.Errorf("DumpToCollector mismatch:\ngot: %s\nwant: %s\n", got, want) + } +} + func TestIterativeDump(t *testing.T) { db := rawdb.NewMemoryDatabase() - tdb := NewDatabaseWithConfig(db, &trie.Config{Preimages: true}) + tdb := NewDatabaseWithConfig(db, &triedb.Config{Preimages: true}) sdb, _ := New(types.EmptyRootHash, tdb, nil) s := &stateEnv{db: db, state: sdb} // generate a few entries - obj1 := s.state.GetOrNewStateObject(common.BytesToAddress([]byte{0x01})) - obj1.AddBalance(big.NewInt(22)) - obj2 := s.state.GetOrNewStateObject(common.BytesToAddress([]byte{0x01, 0x02})) + obj1 := s.state.getOrNewStateObject(common.BytesToAddress([]byte{0x01})) + obj1.AddBalance(uint256.NewInt(22)) + obj2 := s.state.getOrNewStateObject(common.BytesToAddress([]byte{0x01, 0x02})) obj2.SetCode(crypto.Keccak256Hash([]byte{3, 3, 3, 3, 3, 3, 3}), []byte{3, 3, 3, 3, 3, 3, 3}) - obj3 := s.state.GetOrNewStateObject(common.BytesToAddress([]byte{0x02})) - obj3.SetBalance(big.NewInt(44)) - obj4 := s.state.GetOrNewStateObject(common.BytesToAddress([]byte{0x00})) - obj4.AddBalance(big.NewInt(1337)) + obj3 := s.state.getOrNewStateObject(common.BytesToAddress([]byte{0x02})) + obj3.SetBalance(uint256.NewInt(44)) + obj4 := s.state.getOrNewStateObject(common.BytesToAddress([]byte{0x00})) + obj4.AddBalance(uint256.NewInt(1337)) // write some of them to the trie s.state.updateStateObject(obj1) @@ -87,3 +144,165 @@ func TestIterativeDump(t *testing.T) { t.Errorf("DumpToCollector mismatch:\ngot: %s\nwant: %s\n", got, want) } } + +func TestNull(t *testing.T) { + s := newStateEnv() + address := common.HexToAddress("0x823140710bf13990e4500136726d8b55") + s.state.CreateAccount(address) + //value := common.FromHex("0x823140710bf13990e4500136726d8b55") + var value common.Hash + + s.state.SetState(address, common.Hash{}, value) + s.state.Commit(0, false, false) + + if value := s.state.GetState(address, common.Hash{}); value != (common.Hash{}) { + t.Errorf("expected empty current value, got %x", value) + } + if value := s.state.GetCommittedState(address, common.Hash{}); value != (common.Hash{}) { + t.Errorf("expected empty committed value, got %x", value) + } +} + +func TestSnapshot(t *testing.T) { + stateobjaddr := common.BytesToAddress([]byte("aa")) + var storageaddr common.Hash + data1 := common.BytesToHash([]byte{42}) + data2 := common.BytesToHash([]byte{43}) + s := newStateEnv() + + // snapshot the genesis state + genesis := s.state.Snapshot() + + // set initial state object value + s.state.SetState(stateobjaddr, storageaddr, data1) + snapshot := s.state.Snapshot() + + // set a new state object value, revert it and ensure correct content + s.state.SetState(stateobjaddr, storageaddr, data2) + s.state.RevertToSnapshot(snapshot) + + if v := s.state.GetState(stateobjaddr, storageaddr); v != data1 { + t.Errorf("wrong storage value %v, want %v", v, data1) + } + if v := s.state.GetCommittedState(stateobjaddr, storageaddr); v != (common.Hash{}) { + t.Errorf("wrong committed storage value %v, want %v", v, common.Hash{}) + } + + // revert up to the genesis state and ensure correct content + s.state.RevertToSnapshot(genesis) + if v := s.state.GetState(stateobjaddr, storageaddr); v != (common.Hash{}) { + t.Errorf("wrong storage value %v, want %v", v, common.Hash{}) + } + if v := s.state.GetCommittedState(stateobjaddr, storageaddr); v != (common.Hash{}) { + t.Errorf("wrong committed storage value %v, want %v", v, common.Hash{}) + } +} + +func TestSnapshotEmpty(t *testing.T) { + s := newStateEnv() + s.state.RevertToSnapshot(s.state.Snapshot()) +} + +func TestSnapshot2(t *testing.T) { + state, _ := New(types.EmptyRootHash, NewDatabase(rawdb.NewMemoryDatabase()), nil) + + stateobjaddr0 := common.BytesToAddress([]byte("so0")) + stateobjaddr1 := common.BytesToAddress([]byte("so1")) + var storageaddr common.Hash + + data0 := common.BytesToHash([]byte{17}) + data1 := common.BytesToHash([]byte{18}) + + state.SetState(stateobjaddr0, storageaddr, data0) + state.SetState(stateobjaddr1, storageaddr, data1) + + // db, trie are already non-empty values + so0 := state.getStateObject(stateobjaddr0) + so0.SetBalance(uint256.NewInt(42)) + so0.SetNonce(43) + so0.SetCode(crypto.Keccak256Hash([]byte{'c', 'a', 'f', 'e'}), []byte{'c', 'a', 'f', 'e'}) + so0.selfDestructed = false + so0.deleted = false + state.setStateObject(so0) + + root, _ := state.Commit(0, false, false) + state, _ = New(root, state.db, nil) + + // and one with deleted == true + so1 := state.getStateObject(stateobjaddr1) + so1.SetBalance(uint256.NewInt(52)) + so1.SetNonce(53) + so1.SetCode(crypto.Keccak256Hash([]byte{'c', 'a', 'f', 'e', '2'}), []byte{'c', 'a', 'f', 'e', '2'}) + so1.selfDestructed = true + so1.deleted = true + state.setStateObject(so1) + + so1 = state.getStateObject(stateobjaddr1) + if so1 != nil { + t.Fatalf("deleted object not nil when getting") + } + + snapshot := state.Snapshot() + state.RevertToSnapshot(snapshot) + + so0Restored := state.getStateObject(stateobjaddr0) + // Update lazily-loaded values before comparing. + so0Restored.GetState(storageaddr) + so0Restored.Code() + // non-deleted is equal (restored) + compareStateObjects(so0Restored, so0, t) + + // deleted should be nil, both before and after restore of state copy + so1Restored := state.getStateObject(stateobjaddr1) + if so1Restored != nil { + t.Fatalf("deleted object not nil after restoring snapshot: %+v", so1Restored) + } +} + +func compareStateObjects(so0, so1 *stateObject, t *testing.T) { + if so0.Address() != so1.Address() { + t.Fatalf("Address mismatch: have %v, want %v", so0.address, so1.address) + } + if so0.Balance().Cmp(so1.Balance()) != 0 { + t.Fatalf("Balance mismatch: have %v, want %v", so0.Balance(), so1.Balance()) + } + if so0.Nonce() != so1.Nonce() { + t.Fatalf("Nonce mismatch: have %v, want %v", so0.Nonce(), so1.Nonce()) + } + if so0.data.Root != so1.data.Root { + t.Errorf("Root mismatch: have %x, want %x", so0.data.Root[:], so1.data.Root[:]) + } + if !bytes.Equal(so0.CodeHash(), so1.CodeHash()) { + t.Fatalf("CodeHash mismatch: have %v, want %v", so0.CodeHash(), so1.CodeHash()) + } + if !bytes.Equal(so0.code, so1.code) { + t.Fatalf("Code mismatch: have %v, want %v", so0.code, so1.code) + } + + if len(so1.dirtyStorage) != len(so0.dirtyStorage) { + t.Errorf("Dirty storage size mismatch: have %d, want %d", len(so1.dirtyStorage), len(so0.dirtyStorage)) + } + for k, v := range so1.dirtyStorage { + if so0.dirtyStorage[k] != v { + t.Errorf("Dirty storage key %x mismatch: have %v, want %v", k, so0.dirtyStorage[k], v) + } + } + for k, v := range so0.dirtyStorage { + if so1.dirtyStorage[k] != v { + t.Errorf("Dirty storage key %x mismatch: have %v, want none.", k, v) + } + } + if len(so1.originStorage) != len(so0.originStorage) { + t.Errorf("Origin storage size mismatch: have %d, want %d", len(so1.originStorage), len(so0.originStorage)) + } + for k, v := range so1.originStorage { + if so0.originStorage[k] != v { + t.Errorf("Origin storage key %x mismatch: have %v, want %v", k, so0.originStorage[k], v) + } + } + for k, v := range so0.originStorage { + if so1.originStorage[k] != v { + t.Errorf("Origin storage key %x mismatch: have %v, want none.", k, v) + } + } +} diff --git a/core/state/statedb.go b/core/state/statedb.go index 67ea4dd458..9779bec6fe 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -29,7 +29,6 @@ package state import ( "fmt" - "math/big" "sort" "time" @@ -45,6 +44,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/log" + "github.com/holiman/uint256" ) const ( @@ -333,12 +333,12 @@ func (s *StateDB) Empty(addr common.Address) bool { } // GetBalance retrieves the balance from the given address or 0 if object not found -func (s *StateDB) GetBalance(addr common.Address) *big.Int { +func (s *StateDB) GetBalance(addr common.Address) *uint256.Int { stateObject := s.getStateObject(addr) if stateObject != nil { return stateObject.Balance() } - return new(big.Int).Set(common.Big0) + return common.U2560 } // GetNonce retrieves the nonce from the given address or 0 if object not found @@ -384,10 +384,10 @@ func (s *StateDB) GetCodeSize(addr common.Address) int { func (s *StateDB) GetCodeHash(addr common.Address) common.Hash { stateObject := s.getStateObject(addr) - if stateObject == nil { - return common.Hash{} + if stateObject != nil { + return common.BytesToHash(stateObject.CodeHash()) } - return common.BytesToHash(stateObject.CodeHash()) + return common.Hash{} } // GetState retrieves a value from the given account's storage trie. @@ -426,44 +426,44 @@ func (s *StateDB) HasSelfDestructed(addr common.Address) bool { */ // AddBalance adds amount to the account associated with addr. -func (s *StateDB) AddBalance(addr common.Address, amount *big.Int) { - stateObject := s.GetOrNewStateObject(addr) +func (s *StateDB) AddBalance(addr common.Address, amount *uint256.Int) { + stateObject := s.getOrNewStateObject(addr) if stateObject != nil { stateObject.AddBalance(amount) } } // SubBalance subtracts amount from the account associated with addr. -func (s *StateDB) SubBalance(addr common.Address, amount *big.Int) { - stateObject := s.GetOrNewStateObject(addr) +func (s *StateDB) SubBalance(addr common.Address, amount *uint256.Int) { + stateObject := s.getOrNewStateObject(addr) if stateObject != nil { stateObject.SubBalance(amount) } } -func (s *StateDB) SetBalance(addr common.Address, amount *big.Int) { - stateObject := s.GetOrNewStateObject(addr) +func (s *StateDB) SetBalance(addr common.Address, amount *uint256.Int) { + stateObject := s.getOrNewStateObject(addr) if stateObject != nil { stateObject.SetBalance(amount) } } func (s *StateDB) SetNonce(addr common.Address, nonce uint64) { - stateObject := s.GetOrNewStateObject(addr) + stateObject := s.getOrNewStateObject(addr) if stateObject != nil { stateObject.SetNonce(nonce) } } func (s *StateDB) SetCode(addr common.Address, code []byte) { - stateObject := s.GetOrNewStateObject(addr) + stateObject := s.getOrNewStateObject(addr) if stateObject != nil { stateObject.SetCode(crypto.Keccak256Hash(code), code) } } func (s *StateDB) SetState(addr common.Address, key, value common.Hash) { - stateObject := s.GetOrNewStateObject(addr) + stateObject := s.getOrNewStateObject(addr) if stateObject != nil { stateObject.SetState(key, value) } @@ -484,7 +484,7 @@ func (s *StateDB) SetStorage(addr common.Address, storage map[common.Hash]common if _, ok := s.stateObjectsDestruct[addr]; !ok { s.stateObjectsDestruct[addr] = nil } - stateObject := s.GetOrNewStateObject(addr) + stateObject := s.getOrNewStateObject(addr) for k, v := range storage { stateObject.SetState(k, v) } @@ -503,10 +503,10 @@ func (s *StateDB) SelfDestruct(addr common.Address) { s.journal.append(selfDestructChange{ account: &addr, prev: stateObject.selfDestructed, - prevbalance: new(big.Int).Set(stateObject.Balance()), + prevbalance: new(uint256.Int).Set(stateObject.Balance()), }) stateObject.markSelfdestructed() - stateObject.data.Balance = new(big.Int) + stateObject.data.Balance = new(uint256.Int) } func (s *StateDB) Selfdestruct6780(addr common.Address) { @@ -667,8 +667,8 @@ func (s *StateDB) setStateObject(object *stateObject) { s.stateObjects[object.Address()] = object } -// GetOrNewStateObject retrieves a state object or create a new state object if nil. -func (s *StateDB) GetOrNewStateObject(addr common.Address) *stateObject { +// getOrNewStateObject retrieves a state object or create a new state object if nil. +func (s *StateDB) getOrNewStateObject(addr common.Address) *stateObject { stateObject := s.getStateObject(addr) if stateObject == nil { stateObject, _ = s.createObject(addr) diff --git a/core/state/statedb_fuzz_test.go b/core/state/statedb_fuzz_test.go index c149d073ba..47b22b104e 100644 --- a/core/state/statedb_fuzz_test.go +++ b/core/state/statedb_fuzz_test.go @@ -32,7 +32,6 @@ import ( "errors" "fmt" "math" - "math/big" "math/rand" "reflect" "strings" @@ -43,11 +42,13 @@ import ( "github.com/ava-labs/subnet-evm/core/state/snapshot" "github.com/ava-labs/subnet-evm/core/types" "github.com/ava-labs/subnet-evm/trie" - "github.com/ava-labs/subnet-evm/trie/triedb/pathdb" "github.com/ava-labs/subnet-evm/trie/triestate" + "github.com/ava-labs/subnet-evm/triedb" + "github.com/ava-labs/subnet-evm/triedb/pathdb" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/rlp" + "github.com/holiman/uint256" ) // A stateTest checks that the state changes are correctly captured. Instances @@ -70,7 +71,7 @@ func newStateTestAction(addr common.Address, r *rand.Rand, index int) testAction { name: "SetBalance", fn: func(a testAction, s *StateDB) { - s.SetBalance(addr, big.NewInt(a.args[0])) + s.SetBalance(addr, uint256.NewInt(uint64(a.args[0]))) }, args: make([]int64, 1), }, @@ -191,7 +192,7 @@ func (test *stateTest) run() bool { storageList = append(storageList, copy2DSet(states.Storages)) } disk = rawdb.NewMemoryDatabase() - tdb = trie.NewDatabase(disk, &trie.Config{PathDB: pathdb.Defaults}) + tdb = triedb.NewDatabase(disk, &triedb.Config{PathDB: pathdb.Defaults}) sdb = NewDatabaseWithNodeDB(disk, tdb) byzantium = rand.Intn(2) == 0 ) @@ -261,7 +262,7 @@ func (test *stateTest) run() bool { // - the account was indeed not present in trie // - the account is present in new trie, nil->nil is regarded as invalid // - the slots transition is correct -func (test *stateTest) verifyAccountCreation(next common.Hash, db *trie.Database, otr, ntr *trie.Trie, addr common.Address, slots map[common.Hash][]byte) error { +func (test *stateTest) verifyAccountCreation(next common.Hash, db *triedb.Database, otr, ntr *trie.Trie, addr common.Address, slots map[common.Hash][]byte) error { // Verify account change addrHash := crypto.Keccak256Hash(addr.Bytes()) oBlob, err := otr.Get(addrHash.Bytes()) @@ -312,7 +313,7 @@ func (test *stateTest) verifyAccountCreation(next common.Hash, db *trie.Database // - the account was indeed present in trie // - the account in old trie matches the provided value // - the slots transition is correct -func (test *stateTest) verifyAccountUpdate(next common.Hash, db *trie.Database, otr, ntr *trie.Trie, addr common.Address, origin []byte, slots map[common.Hash][]byte) error { +func (test *stateTest) verifyAccountUpdate(next common.Hash, db *triedb.Database, otr, ntr *trie.Trie, addr common.Address, origin []byte, slots map[common.Hash][]byte) error { // Verify account change addrHash := crypto.Keccak256Hash(addr.Bytes()) oBlob, err := otr.Get(addrHash.Bytes()) @@ -366,7 +367,7 @@ func (test *stateTest) verifyAccountUpdate(next common.Hash, db *trie.Database, return nil } -func (test *stateTest) verify(root common.Hash, next common.Hash, db *trie.Database, accountsOrigin map[common.Address][]byte, storagesOrigin map[common.Address]map[common.Hash][]byte) error { +func (test *stateTest) verify(root common.Hash, next common.Hash, db *triedb.Database, accountsOrigin map[common.Address][]byte, storagesOrigin map[common.Address]map[common.Hash][]byte) error { otr, err := trie.New(trie.StateTrieID(root), db) if err != nil { return err diff --git a/core/state/statedb_test.go b/core/state/statedb_test.go index 7c603cbe4f..60d7ee3b41 100644 --- a/core/state/statedb_test.go +++ b/core/state/statedb_test.go @@ -32,7 +32,6 @@ import ( "errors" "fmt" "math" - "math/big" "math/rand" "reflect" "strings" @@ -44,9 +43,10 @@ import ( "github.com/ava-labs/subnet-evm/core/state/snapshot" "github.com/ava-labs/subnet-evm/core/types" "github.com/ava-labs/subnet-evm/trie" - "github.com/ava-labs/subnet-evm/trie/triedb/hashdb" - "github.com/ava-labs/subnet-evm/trie/triedb/pathdb" "github.com/ava-labs/subnet-evm/trie/trienode" + "github.com/ava-labs/subnet-evm/triedb" + "github.com/ava-labs/subnet-evm/triedb/hashdb" + "github.com/ava-labs/subnet-evm/triedb/pathdb" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/rlp" @@ -59,14 +59,14 @@ func TestUpdateLeaks(t *testing.T) { // Create an empty state database var ( db = rawdb.NewMemoryDatabase() - tdb = trie.NewDatabase(db, nil) + tdb = triedb.NewDatabase(db, nil) ) state, _ := New(types.EmptyRootHash, NewDatabaseWithNodeDB(db, tdb), nil) // Update it with some accounts for i := byte(0); i < 255; i++ { addr := common.BytesToAddress([]byte{i}) - state.AddBalance(addr, big.NewInt(int64(11*i))) + state.AddBalance(addr, uint256.NewInt(uint64(11*i))) state.SetNonce(addr, uint64(42*i)) if i%2 == 0 { state.SetState(addr, common.BytesToHash([]byte{i, i, i}), common.BytesToHash([]byte{i, i, i, i})) @@ -95,13 +95,13 @@ func TestIntermediateLeaks(t *testing.T) { // Create two state databases, one transitioning to the final state, the other final from the beginning transDb := rawdb.NewMemoryDatabase() finalDb := rawdb.NewMemoryDatabase() - transNdb := trie.NewDatabase(transDb, nil) - finalNdb := trie.NewDatabase(finalDb, nil) + transNdb := triedb.NewDatabase(transDb, nil) + finalNdb := triedb.NewDatabase(finalDb, nil) transState, _ := New(types.EmptyRootHash, NewDatabaseWithNodeDB(transDb, transNdb), nil) finalState, _ := New(types.EmptyRootHash, NewDatabaseWithNodeDB(finalDb, finalNdb), nil) modify := func(state *StateDB, addr common.Address, i, tweak byte) { - state.SetBalance(addr, big.NewInt(int64(11*i)+int64(tweak))) + state.SetBalance(addr, uint256.NewInt(uint64(11*i)+uint64(tweak))) state.SetNonce(addr, uint64(42*i+tweak)) if i%2 == 0 { state.SetState(addr, common.Hash{i, i, i, 0}, common.Hash{}) @@ -176,8 +176,8 @@ func TestCopy(t *testing.T) { orig, _ := New(types.EmptyRootHash, NewDatabase(rawdb.NewMemoryDatabase()), nil) for i := byte(0); i < 255; i++ { - obj := orig.GetOrNewStateObject(common.BytesToAddress([]byte{i})) - obj.AddBalance(big.NewInt(int64(i))) + obj := orig.getOrNewStateObject(common.BytesToAddress([]byte{i})) + obj.AddBalance(uint256.NewInt(uint64(i))) orig.updateStateObject(obj) } orig.Finalise(false) @@ -190,13 +190,13 @@ func TestCopy(t *testing.T) { // modify all in memory for i := byte(0); i < 255; i++ { - origObj := orig.GetOrNewStateObject(common.BytesToAddress([]byte{i})) - copyObj := copy.GetOrNewStateObject(common.BytesToAddress([]byte{i})) - ccopyObj := ccopy.GetOrNewStateObject(common.BytesToAddress([]byte{i})) + origObj := orig.getOrNewStateObject(common.BytesToAddress([]byte{i})) + copyObj := copy.getOrNewStateObject(common.BytesToAddress([]byte{i})) + ccopyObj := ccopy.getOrNewStateObject(common.BytesToAddress([]byte{i})) - origObj.AddBalance(big.NewInt(2 * int64(i))) - copyObj.AddBalance(big.NewInt(3 * int64(i))) - ccopyObj.AddBalance(big.NewInt(4 * int64(i))) + origObj.AddBalance(uint256.NewInt(2 * uint64(i))) + copyObj.AddBalance(uint256.NewInt(3 * uint64(i))) + ccopyObj.AddBalance(uint256.NewInt(4 * uint64(i))) orig.updateStateObject(origObj) copy.updateStateObject(copyObj) @@ -218,17 +218,17 @@ func TestCopy(t *testing.T) { // Verify that the three states have been updated independently for i := byte(0); i < 255; i++ { - origObj := orig.GetOrNewStateObject(common.BytesToAddress([]byte{i})) - copyObj := copy.GetOrNewStateObject(common.BytesToAddress([]byte{i})) - ccopyObj := ccopy.GetOrNewStateObject(common.BytesToAddress([]byte{i})) + origObj := orig.getOrNewStateObject(common.BytesToAddress([]byte{i})) + copyObj := copy.getOrNewStateObject(common.BytesToAddress([]byte{i})) + ccopyObj := ccopy.getOrNewStateObject(common.BytesToAddress([]byte{i})) - if want := big.NewInt(3 * int64(i)); origObj.Balance().Cmp(want) != 0 { + if want := uint256.NewInt(3 * uint64(i)); origObj.Balance().Cmp(want) != 0 { t.Errorf("orig obj %d: balance mismatch: have %v, want %v", i, origObj.Balance(), want) } - if want := big.NewInt(4 * int64(i)); copyObj.Balance().Cmp(want) != 0 { + if want := uint256.NewInt(4 * uint64(i)); copyObj.Balance().Cmp(want) != 0 { t.Errorf("copy obj %d: balance mismatch: have %v, want %v", i, copyObj.Balance(), want) } - if want := big.NewInt(5 * int64(i)); ccopyObj.Balance().Cmp(want) != 0 { + if want := uint256.NewInt(5 * uint64(i)); ccopyObj.Balance().Cmp(want) != 0 { t.Errorf("copy obj %d: balance mismatch: have %v, want %v", i, ccopyObj.Balance(), want) } } @@ -276,14 +276,14 @@ func newTestAction(addr common.Address, r *rand.Rand) testAction { { name: "SetBalance", fn: func(a testAction, s *StateDB) { - s.SetBalance(addr, big.NewInt(a.args[0])) + s.SetBalance(addr, uint256.NewInt(uint64(a.args[0]))) }, args: make([]int64, 1), }, { name: "AddBalance", fn: func(a testAction, s *StateDB) { - s.AddBalance(addr, big.NewInt(a.args[0])) + s.AddBalance(addr, uint256.NewInt(uint64(a.args[0]))) }, args: make([]int64, 1), }, @@ -541,12 +541,12 @@ func (test *snapshotTest) checkEqual(state, checkstate *StateDB) error { func TestTouchDelete(t *testing.T) { s := newStateEnv() - s.state.GetOrNewStateObject(common.Address{}) + s.state.getOrNewStateObject(common.Address{}) root, _ := s.state.Commit(0, false, false) s.state, _ = NewWithSnapshot(root, s.state.db, s.state.snap) snapshot := s.state.Snapshot() - s.state.AddBalance(common.Address{}, new(big.Int)) + s.state.AddBalance(common.Address{}, new(uint256.Int)) if len(s.state.journal.dirties) != 1 { t.Fatal("expected one dirty state object") @@ -562,7 +562,7 @@ func TestTouchDelete(t *testing.T) { func TestCopyOfCopy(t *testing.T) { state, _ := New(types.EmptyRootHash, NewDatabase(rawdb.NewMemoryDatabase()), nil) addr := common.HexToAddress("aaaa") - state.SetBalance(addr, big.NewInt(42)) + state.SetBalance(addr, uint256.NewInt(42)) if got := state.Copy().GetBalance(addr).Uint64(); got != 42 { t.Fatalf("1st copy fail, expected 42, got %v", got) @@ -585,11 +585,11 @@ func TestCopyCommitCopy(t *testing.T) { skey := common.HexToHash("aaa") sval := common.HexToHash("bbb") - state.SetBalance(addr, big.NewInt(42)) // Change the account trie - state.SetCode(addr, []byte("hello")) // Change an external metadata - state.SetState(addr, skey, sval) // Change the storage trie + state.SetBalance(addr, uint256.NewInt(42)) // Change the account trie + state.SetCode(addr, []byte("hello")) // Change an external metadata + state.SetState(addr, skey, sval) // Change the storage trie - if balance := state.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 { + if balance := state.GetBalance(addr); balance.Cmp(uint256.NewInt(42)) != 0 { t.Fatalf("initial balance mismatch: have %v, want %v", balance, 42) } if code := state.GetCode(addr); !bytes.Equal(code, []byte("hello")) { @@ -603,7 +603,7 @@ func TestCopyCommitCopy(t *testing.T) { } // Copy the non-committed state database and check pre/post commit balance copyOne := state.Copy() - if balance := copyOne.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 { + if balance := copyOne.GetBalance(addr); balance.Cmp(uint256.NewInt(42)) != 0 { t.Fatalf("first copy pre-commit balance mismatch: have %v, want %v", balance, 42) } if code := copyOne.GetCode(addr); !bytes.Equal(code, []byte("hello")) { @@ -617,7 +617,7 @@ func TestCopyCommitCopy(t *testing.T) { } // Copy the copy and check the balance once more copyTwo := copyOne.Copy() - if balance := copyTwo.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 { + if balance := copyTwo.GetBalance(addr); balance.Cmp(uint256.NewInt(42)) != 0 { t.Fatalf("second copy balance mismatch: have %v, want %v", balance, 42) } if code := copyTwo.GetCode(addr); !bytes.Equal(code, []byte("hello")) { @@ -632,7 +632,7 @@ func TestCopyCommitCopy(t *testing.T) { // Commit state, ensure states can be loaded from disk root, _ := state.Commit(0, false, false) state, _ = New(root, tdb, nil) - if balance := state.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 { + if balance := state.GetBalance(addr); balance.Cmp(uint256.NewInt(42)) != 0 { t.Fatalf("state post-commit balance mismatch: have %v, want %v", balance, 42) } if code := state.GetCode(addr); !bytes.Equal(code, []byte("hello")) { @@ -658,11 +658,11 @@ func TestCopyCopyCommitCopy(t *testing.T) { skey := common.HexToHash("aaa") sval := common.HexToHash("bbb") - state.SetBalance(addr, big.NewInt(42)) // Change the account trie - state.SetCode(addr, []byte("hello")) // Change an external metadata - state.SetState(addr, skey, sval) // Change the storage trie + state.SetBalance(addr, uint256.NewInt(42)) // Change the account trie + state.SetCode(addr, []byte("hello")) // Change an external metadata + state.SetState(addr, skey, sval) // Change the storage trie - if balance := state.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 { + if balance := state.GetBalance(addr); balance.Cmp(uint256.NewInt(42)) != 0 { t.Fatalf("initial balance mismatch: have %v, want %v", balance, 42) } if code := state.GetCode(addr); !bytes.Equal(code, []byte("hello")) { @@ -676,7 +676,7 @@ func TestCopyCopyCommitCopy(t *testing.T) { } // Copy the non-committed state database and check pre/post commit balance copyOne := state.Copy() - if balance := copyOne.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 { + if balance := copyOne.GetBalance(addr); balance.Cmp(uint256.NewInt(42)) != 0 { t.Fatalf("first copy balance mismatch: have %v, want %v", balance, 42) } if code := copyOne.GetCode(addr); !bytes.Equal(code, []byte("hello")) { @@ -690,7 +690,7 @@ func TestCopyCopyCommitCopy(t *testing.T) { } // Copy the copy and check the balance once more copyTwo := copyOne.Copy() - if balance := copyTwo.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 { + if balance := copyTwo.GetBalance(addr); balance.Cmp(uint256.NewInt(42)) != 0 { t.Fatalf("second copy pre-commit balance mismatch: have %v, want %v", balance, 42) } if code := copyTwo.GetCode(addr); !bytes.Equal(code, []byte("hello")) { @@ -704,7 +704,7 @@ func TestCopyCopyCommitCopy(t *testing.T) { } // Copy the copy-copy and check the balance once more copyThree := copyTwo.Copy() - if balance := copyThree.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 { + if balance := copyThree.GetBalance(addr); balance.Cmp(uint256.NewInt(42)) != 0 { t.Fatalf("third copy balance mismatch: have %v, want %v", balance, 42) } if code := copyThree.GetCode(addr); !bytes.Equal(code, []byte("hello")) { @@ -727,11 +727,11 @@ func TestCommitCopy(t *testing.T) { skey := common.HexToHash("aaa") sval := common.HexToHash("bbb") - state.SetBalance(addr, big.NewInt(42)) // Change the account trie - state.SetCode(addr, []byte("hello")) // Change an external metadata - state.SetState(addr, skey, sval) // Change the storage trie + state.SetBalance(addr, uint256.NewInt(42)) // Change the account trie + state.SetCode(addr, []byte("hello")) // Change an external metadata + state.SetState(addr, skey, sval) // Change the storage trie - if balance := state.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 { + if balance := state.GetBalance(addr); balance.Cmp(uint256.NewInt(42)) != 0 { t.Fatalf("initial balance mismatch: have %v, want %v", balance, 42) } if code := state.GetCode(addr); !bytes.Equal(code, []byte("hello")) { @@ -746,7 +746,7 @@ func TestCommitCopy(t *testing.T) { // Copy the committed state database, the copied one is not functional. state.Commit(0, true, false) copied := state.Copy() - if balance := copied.GetBalance(addr); balance.Cmp(big.NewInt(0)) != 0 { + if balance := copied.GetBalance(addr); balance.Cmp(uint256.NewInt(0)) != 0 { t.Fatalf("unexpected balance: have %v", balance) } if code := copied.GetCode(addr); code != nil { @@ -776,7 +776,7 @@ func TestDeleteCreateRevert(t *testing.T) { state, _ := New(types.EmptyRootHash, NewDatabase(rawdb.NewMemoryDatabase()), nil) addr := common.BytesToAddress([]byte("so")) - state.SetBalance(addr, big.NewInt(1)) + state.SetBalance(addr, uint256.NewInt(1)) root, _ := state.Commit(0, false, false) state, _ = NewWithSnapshot(root, state.db, state.snap) @@ -786,7 +786,7 @@ func TestDeleteCreateRevert(t *testing.T) { state.Finalise(true) id := state.Snapshot() - state.SetBalance(addr, big.NewInt(2)) + state.SetBalance(addr, uint256.NewInt(2)) state.RevertToSnapshot(id) // Commit the entire state and make sure we don't crash and have the correct state @@ -809,34 +809,34 @@ func TestMissingTrieNodes(t *testing.T) { func testMissingTrieNodes(t *testing.T, scheme string) { // Create an initial state with a few accounts var ( - triedb *trie.Database - memDb = rawdb.NewMemoryDatabase() + tdb *triedb.Database + memDb = rawdb.NewMemoryDatabase() ) if scheme == rawdb.PathScheme { - triedb = trie.NewDatabase(memDb, &trie.Config{PathDB: &pathdb.Config{ + tdb = triedb.NewDatabase(memDb, &triedb.Config{PathDB: &pathdb.Config{ CleanCacheSize: 0, DirtyCacheSize: 0, }}) // disable caching } else { - triedb = trie.NewDatabase(memDb, &trie.Config{HashDB: &hashdb.Config{ + tdb = triedb.NewDatabase(memDb, &triedb.Config{HashDB: &hashdb.Config{ CleanCacheSize: 0, }}) // disable caching } - db := NewDatabaseWithNodeDB(memDb, triedb) + db := NewDatabaseWithNodeDB(memDb, tdb) var root common.Hash state, _ := New(types.EmptyRootHash, db, nil) addr := common.BytesToAddress([]byte("so")) { - state.SetBalance(addr, big.NewInt(1)) + state.SetBalance(addr, uint256.NewInt(1)) state.SetCode(addr, []byte{1, 2, 3}) a2 := common.BytesToAddress([]byte("another")) - state.SetBalance(a2, big.NewInt(100)) + state.SetBalance(a2, uint256.NewInt(100)) state.SetCode(a2, []byte{1, 2, 4}) root, _ = state.Commit(0, false, false) t.Logf("root: %x", root) // force-flush - triedb.Commit(root, false) + tdb.Commit(root, false) } // Create a new state on the old root state, _ = New(root, db, nil) @@ -856,7 +856,7 @@ func testMissingTrieNodes(t *testing.T, scheme string) { t.Errorf("expected %d, got %d", exp, got) } // Modify the state - state.SetBalance(addr, big.NewInt(2)) + state.SetBalance(addr, uint256.NewInt(2)) root, err := state.Commit(0, false, false) if err == nil { t.Fatalf("expected error, got root :%x", root) @@ -1043,7 +1043,7 @@ func TestFlushOrderDataLoss(t *testing.T) { // Create a state trie with many accounts and slots var ( memdb = rawdb.NewMemoryDatabase() - triedb = trie.NewDatabase(memdb, nil) + triedb = triedb.NewDatabase(memdb, nil) statedb = NewDatabaseWithNodeDB(memdb, triedb) state, _ = New(types.EmptyRootHash, statedb, nil) ) @@ -1115,7 +1115,7 @@ func TestStateDBTransientStorage(t *testing.T) { func TestResetObject(t *testing.T) { var ( disk = rawdb.NewMemoryDatabase() - tdb = trie.NewDatabase(disk, nil) + tdb = triedb.NewDatabase(disk, nil) db = NewDatabaseWithNodeDB(disk, tdb) snaps, _ = snapshot.New(snapshot.Config{CacheSize: 10}, disk, tdb, common.Hash{}, types.EmptyRootHash) state, _ = New(types.EmptyRootHash, db, snaps) @@ -1124,13 +1124,13 @@ func TestResetObject(t *testing.T) { slotB = common.HexToHash("0x2") ) // Initialize account with balance and storage in first transaction. - state.SetBalance(addr, big.NewInt(1)) + state.SetBalance(addr, uint256.NewInt(1)) state.SetState(addr, slotA, common.BytesToHash([]byte{0x1})) state.IntermediateRoot(true) // Reset account and mutate balance and storages state.CreateAccount(addr) - state.SetBalance(addr, big.NewInt(2)) + state.SetBalance(addr, uint256.NewInt(2)) state.SetState(addr, slotB, common.BytesToHash([]byte{0x2})) root, _ := state.CommitWithSnap(0, true, snaps, common.Hash{}, common.Hash{}, false) @@ -1149,14 +1149,14 @@ func TestResetObject(t *testing.T) { func TestDeleteStorage(t *testing.T) { var ( disk = rawdb.NewMemoryDatabase() - tdb = trie.NewDatabase(disk, nil) + tdb = triedb.NewDatabase(disk, nil) db = NewDatabaseWithNodeDB(disk, tdb) snaps, _ = snapshot.New(snapshot.Config{CacheSize: 10}, disk, tdb, common.Hash{}, types.EmptyRootHash) state, _ = New(types.EmptyRootHash, db, snaps) addr = common.HexToAddress("0x1") ) // Initialize account and populate storage - state.SetBalance(addr, big.NewInt(1)) + state.SetBalance(addr, uint256.NewInt(1)) state.CreateAccount(addr) for i := 0; i < 1000; i++ { slot := common.Hash(uint256.NewInt(uint64(i)).Bytes32()) @@ -1168,7 +1168,7 @@ func TestDeleteStorage(t *testing.T) { fastState, _ := New(root, db, snaps) slowState, _ := New(root, db, nil) - obj := fastState.GetOrNewStateObject(addr) + obj := fastState.getOrNewStateObject(addr) storageRoot := obj.data.Root _, _, fastNodes, err := fastState.deleteStorage(addr, crypto.Keccak256Hash(addr[:]), storageRoot) diff --git a/core/state/trie_prefetcher_test.go b/core/state/trie_prefetcher_test.go index 588d251561..b8edcbb6a8 100644 --- a/core/state/trie_prefetcher_test.go +++ b/core/state/trie_prefetcher_test.go @@ -34,6 +34,7 @@ import ( "github.com/ava-labs/subnet-evm/core/rawdb" "github.com/ava-labs/subnet-evm/core/types" "github.com/ethereum/go-ethereum/common" + "github.com/holiman/uint256" ) const maxConcurrency = 4 @@ -46,9 +47,9 @@ func filledStateDB() *StateDB { skey := common.HexToHash("aaa") sval := common.HexToHash("bbb") - state.SetBalance(addr, big.NewInt(42)) // Change the account trie - state.SetCode(addr, []byte("hello")) // Change an external metadata - state.SetState(addr, skey, sval) // Change the storage trie + state.SetBalance(addr, uint256.NewInt(42)) // Change the account trie + state.SetCode(addr, []byte("hello")) // Change an external metadata + state.SetState(addr, skey, sval) // Change the storage trie for i := 0; i < 100; i++ { sk := common.BigToHash(big.NewInt(int64(i))) state.SetState(addr, sk, sk) // Change the storage trie diff --git a/core/state_processor.go b/core/state_processor.go index fb839cc187..7853810f43 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -199,7 +199,7 @@ func ProcessBeaconBlockRoot(beaconRoot common.Hash, vmenv *vm.EVM, statedb *stat } vmenv.Reset(NewEVMTxContext(msg), statedb) statedb.AddAddressToAccessList(params.BeaconRootsStorageAddress) - _, _, _ = vmenv.Call(vm.AccountRef(msg.From), *msg.To, msg.Data, 30_000_000, common.Big0) + _, _, _ = 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 ad35b811bd..f2e0a770d2 100644 --- a/core/state_processor_test.go +++ b/core/state_processor_test.go @@ -113,8 +113,8 @@ func TestStateProcessorErrors(t *testing.T) { gspec = &Genesis{ Config: config, Timestamp: uint64(upgrade.InitiallyActiveTime.Unix()), - Alloc: GenesisAlloc{ - common.HexToAddress("0x71562b71999873DB5b286dF957af199Ec94617F7"): GenesisAccount{ + Alloc: types.GenesisAlloc{ + common.HexToAddress("0x71562b71999873DB5b286dF957af199Ec94617F7"): types.Account{ Balance: big.NewInt(4000000000000000000), // 4 ether Nonce: 0, }, @@ -219,7 +219,7 @@ func TestStateProcessorErrors(t *testing.T) { txs: []*types.Transaction{ mkDynamicTx(0, common.Address{}, params.TxGas, bigNumber, bigNumber), }, - want: "could not apply tx 0 [0xd82a0c2519acfeac9a948258c47e784acd20651d9d80f9a1c67b4137651c3a24]: insufficient funds for gas * price + value: address 0x71562b71999873DB5b286dF957af199Ec94617F7 have 4000000000000000000 want 2431633873983640103894990685182446064918669677978451844828609264166175722438635000", + want: "could not apply tx 0 [0xd82a0c2519acfeac9a948258c47e784acd20651d9d80f9a1c67b4137651c3a24]: insufficient funds for gas * price + value: address 0x71562b71999873DB5b286dF957af199Ec94617F7 required balance exceeds 256 bits", }, { // ErrMaxInitCodeSizeExceeded txs: []*types.Transaction{ @@ -269,8 +269,8 @@ func TestStateProcessorErrors(t *testing.T) { IstanbulBlock: big.NewInt(0), MuirGlacierBlock: big.NewInt(0), }, - Alloc: GenesisAlloc{ - common.HexToAddress("0x71562b71999873DB5b286dF957af199Ec94617F7"): GenesisAccount{ + Alloc: types.GenesisAlloc{ + common.HexToAddress("0x71562b71999873DB5b286dF957af199Ec94617F7"): types.Account{ Balance: big.NewInt(1000000000000000000), // 1 ether Nonce: 0, }, @@ -308,8 +308,8 @@ func TestStateProcessorErrors(t *testing.T) { db = rawdb.NewMemoryDatabase() gspec = &Genesis{ Config: config, - Alloc: GenesisAlloc{ - common.HexToAddress("0x71562b71999873DB5b286dF957af199Ec94617F7"): GenesisAccount{ + Alloc: types.GenesisAlloc{ + common.HexToAddress("0x71562b71999873DB5b286dF957af199Ec94617F7"): types.Account{ Balance: big.NewInt(1000000000000000000), // 1 ether Nonce: 0, Code: common.FromHex("0xB0B0FACE"), diff --git a/core/state_transition.go b/core/state_transition.go index a71bacc192..a4aff4b6bc 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -27,7 +27,6 @@ package core import ( - "errors" "fmt" "math" "math/big" @@ -40,6 +39,8 @@ import ( "github.com/ava-labs/subnet-evm/vmerrs" "github.com/ethereum/go-ethereum/common" cmath "github.com/ethereum/go-ethereum/common/math" + "github.com/ethereum/go-ethereum/crypto/kzg4844" + "github.com/holiman/uint256" ) // ExecutionResult includes all output after executing given evm @@ -311,7 +312,11 @@ func (st *StateTransition) buyGas() error { mgval.Add(mgval, blobFee) } } - if have, want := st.state.GetBalance(st.msg.From), balanceCheck; have.Cmp(want) < 0 { + balanceCheckU256, overflow := uint256.FromBig(balanceCheck) + if overflow { + return fmt.Errorf("%w: address %v required balance exceeds 256 bits", ErrInsufficientFunds, st.msg.From.Hex()) + } + if have, want := st.state.GetBalance(st.msg.From), balanceCheckU256; have.Cmp(want) < 0 { return fmt.Errorf("%w: address %v have %v want %v", ErrInsufficientFunds, st.msg.From.Hex(), have, want) } if err := st.gp.SubGas(st.msg.GasLimit); err != nil { @@ -320,7 +325,8 @@ func (st *StateTransition) buyGas() error { st.gasRemaining += st.msg.GasLimit st.initialGas = st.msg.GasLimit - st.state.SubBalance(st.msg.From, mgval) + mgvalU256, _ := uint256.FromBig(mgval) + st.state.SubBalance(st.msg.From, mgvalU256) return nil } @@ -386,13 +392,18 @@ func (st *StateTransition) preCheck() error { } // Check the blob version validity if msg.BlobHashes != nil { + // The to field of a blob tx type is mandatory, and a `BlobTx` transaction internally + // has it as a non-nillable value, so any msg derived from blob transaction has it non-nil. + // However, messages created through RPC (eth_call) don't have this restriction. + if msg.To == nil { + return ErrBlobTxCreate + } if len(msg.BlobHashes) == 0 { - return errors.New("blob transaction missing blob hashes") + return ErrMissingBlobHashes } for i, hash := range msg.BlobHashes { - if hash[0] != params.BlobTxHashVersion { - return fmt.Errorf("blob %d hash version mismatch (have %d, supported %d)", - i, hash[0], params.BlobTxHashVersion) + if !kzg4844.IsValidVersionedHash(hash[:]) { + return fmt.Errorf("blob %d has invalid hash version", i) } } } @@ -466,7 +477,11 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { st.gasRemaining -= gas // Check clause 6 - if msg.Value.Sign() > 0 && !st.evm.Context.CanTransfer(st.state, msg.From, msg.Value) { + value, overflow := uint256.FromBig(msg.Value) + if overflow { + return nil, fmt.Errorf("%w: address %v", ErrInsufficientFundsForTransfer, msg.From.Hex()) + } + if !value.IsZero() && !st.evm.Context.CanTransfer(st.state, msg.From, value) { return nil, fmt.Errorf("%w: address %v", ErrInsufficientFundsForTransfer, msg.From.Hex()) } @@ -485,14 +500,20 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { vmerr error // vm errors do not effect consensus and are therefore not assigned to err ) if contractCreation { - ret, _, st.gasRemaining, vmerr = st.evm.Create(sender, msg.Data, st.gasRemaining, msg.Value) + ret, _, st.gasRemaining, vmerr = st.evm.Create(sender, msg.Data, st.gasRemaining, value) } else { // Increment the nonce for the next transaction st.state.SetNonce(msg.From, st.state.GetNonce(sender.Address())+1) - ret, st.gasRemaining, vmerr = st.evm.Call(sender, st.to(), msg.Data, st.gasRemaining, msg.Value) + ret, st.gasRemaining, vmerr = st.evm.Call(sender, st.to(), msg.Data, st.gasRemaining, value) + } + price, overflow := uint256.FromBig(msg.GasPrice) + if overflow { + return nil, ErrGasUintOverflow } gasRefund := st.refundGas(rules.IsSubnetEVM) - st.state.AddBalance(st.evm.Context.Coinbase, new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), msg.GasPrice)) + fee := new(uint256.Int).SetUint64(st.gasUsed()) + fee.Mul(fee, price) + st.state.AddBalance(st.evm.Context.Coinbase, fee) return &ExecutionResult{ UsedGas: st.gasUsed(), @@ -515,7 +536,8 @@ func (st *StateTransition) refundGas(subnetEVM bool) uint64 { } // Return ETH for remaining gas, exchanged at the original rate. - remaining := new(big.Int).Mul(new(big.Int).SetUint64(st.gasRemaining), st.msg.GasPrice) + remaining := uint256.NewInt(st.gasRemaining) + remaining = remaining.Mul(remaining, uint256.MustFromBig(st.msg.GasPrice)) st.state.AddBalance(st.msg.From, remaining) // Also return remaining gas to the block gas counter so it is diff --git a/core/test_blockchain.go b/core/test_blockchain.go index 3a45592614..02f98b61cf 100644 --- a/core/test_blockchain.go +++ b/core/test_blockchain.go @@ -23,6 +23,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" + "github.com/holiman/uint256" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -238,9 +239,10 @@ func TestInsertChainAcceptSingleBlock(t *testing.T, create func(db ethdb.Databas if nonce != 1 { return fmt.Errorf("expected nonce addr1: 1, found nonce: %d", nonce) } - transferredFunds := big.NewInt(10000) + transferredFunds := uint256.MustFromBig(big.NewInt(10000)) balance1 := sdb.GetBalance(addr1) - expectedBalance1 := new(big.Int).Sub(genesisBalance, transferredFunds) + genesisBalance := uint256.MustFromBig(genesisBalance) + expectedBalance1 := new(uint256.Int).Sub(genesisBalance, transferredFunds) if balance1.Cmp(expectedBalance1) != 0 { return fmt.Errorf("expected addr1 balance: %d, found balance: %d", expectedBalance1, balance1) } @@ -274,7 +276,7 @@ func TestInsertLongForkedChain(t *testing.T, create func(db ethdb.Database, gspe genesisBalance := big.NewInt(1000000000) gspec := &Genesis{ Config: ¶ms.ChainConfig{HomesteadBlock: new(big.Int)}, - Alloc: GenesisAlloc{addr1: {Balance: genesisBalance}}, + Alloc: types.GenesisAlloc{addr1: {Balance: genesisBalance}}, } blockchain, err := create(chainDB, gspec, common.Hash{}) @@ -405,8 +407,9 @@ func TestInsertLongForkedChain(t *testing.T, create func(db ethdb.Database, gspe return fmt.Errorf("expected addr1 nonce: 129, found nonce %d", nonce1) } balance1 := sdb.GetBalance(addr1) - transferredFunds := new(big.Int).Mul(big.NewInt(129), big.NewInt(10000)) - expectedBalance := new(big.Int).Sub(genesisBalance, transferredFunds) + transferredFunds := uint256.NewInt(129 * 10_000) + genesisBalance := uint256.MustFromBig(genesisBalance) + expectedBalance := new(uint256.Int).Sub(genesisBalance, transferredFunds) if balance1.Cmp(expectedBalance) != 0 { return fmt.Errorf("expected addr1 balance: %d, found balance: %d", expectedBalance, balance1) } @@ -437,7 +440,7 @@ func TestAcceptNonCanonicalBlock(t *testing.T, create func(db ethdb.Database, gs genesisBalance := big.NewInt(1000000000) gspec := &Genesis{ Config: ¶ms.ChainConfig{HomesteadBlock: new(big.Int)}, - Alloc: GenesisAlloc{addr1: {Balance: genesisBalance}}, + Alloc: types.GenesisAlloc{addr1: {Balance: genesisBalance}}, } blockchain, err := create(chainDB, gspec, common.Hash{}) @@ -513,8 +516,9 @@ func TestAcceptNonCanonicalBlock(t *testing.T, create func(db ethdb.Database, gs return fmt.Errorf("expected addr1 nonce: 1, found nonce: %d", nonce1) } balance1 := sdb.GetBalance(addr1) - transferredFunds := big.NewInt(5000) - expectedBalance := new(big.Int).Sub(genesisBalance, transferredFunds) + transferredFunds := uint256.NewInt(5000) + genesisBalance := uint256.MustFromBig(genesisBalance) + expectedBalance := new(uint256.Int).Sub(genesisBalance, transferredFunds) if balance1.Cmp(expectedBalance) != 0 { return fmt.Errorf("expected balance1: %d, found balance: %d", expectedBalance, balance1) } @@ -545,7 +549,7 @@ func TestSetPreferenceRewind(t *testing.T, create func(db ethdb.Database, gspec genesisBalance := big.NewInt(1000000000) gspec := &Genesis{ Config: ¶ms.ChainConfig{HomesteadBlock: new(big.Int)}, - Alloc: GenesisAlloc{addr1: {Balance: genesisBalance}}, + Alloc: types.GenesisAlloc{addr1: {Balance: genesisBalance}}, } blockchain, err := create(chainDB, gspec, common.Hash{}) @@ -607,6 +611,7 @@ func TestSetPreferenceRewind(t *testing.T, create func(db ethdb.Database, gspec return fmt.Errorf("expected addr1 nonce: 0, found nonce: %d", nonce1) } balance1 := sdb.GetBalance(addr1) + genesisBalance := uint256.MustFromBig(genesisBalance) if balance1.Cmp(genesisBalance) != 0 { return fmt.Errorf("expected addr1 balance: %d, found balance: %d", genesisBalance, balance1) } @@ -615,7 +620,7 @@ func TestSetPreferenceRewind(t *testing.T, create func(db ethdb.Database, gspec return fmt.Errorf("expected addr2 nonce: 0, found nonce: %d", nonce2) } balance2 := sdb.GetBalance(addr2) - if balance2.Cmp(big.NewInt(0)) != 0 { + if balance2.Cmp(common.U2560) != 0 { return fmt.Errorf("expected addr2 balance: 0, found balance %d", balance2) } return nil @@ -640,9 +645,10 @@ func TestSetPreferenceRewind(t *testing.T, create func(db ethdb.Database, gspec if nonce != 1 { return fmt.Errorf("expected addr1 nonce: 1, found nonce: %d", nonce) } - transferredFunds := big.NewInt(10000) + transferredFunds := uint256.NewInt(10000) balance1 := sdb.GetBalance(addr1) - expectedBalance1 := new(big.Int).Sub(genesisBalance, transferredFunds) + genesisBalance := uint256.MustFromBig(genesisBalance) + expectedBalance1 := new(uint256.Int).Sub(genesisBalance, transferredFunds) if balance1.Cmp(expectedBalance1) != 0 { return fmt.Errorf("expected addr1 balance: %d, found balance %d", expectedBalance1, balance1) } @@ -796,6 +802,7 @@ func TestBuildOnVariousStages(t *testing.T, create func(db ethdb.Database, gspec return fmt.Errorf("expected nonce addr1: 5, found nonce: %d", nonce) } balance1 := sdb.GetBalance(addr1) + genesisBalance := uint256.MustFromBig(genesisBalance) expectedBalance1 := genesisBalance if balance1.Cmp(expectedBalance1) != 0 { return fmt.Errorf("expected addr1 balance: %d, found balance: %d", expectedBalance1, balance1) @@ -813,7 +820,7 @@ func TestBuildOnVariousStages(t *testing.T, create func(db ethdb.Database, gspec } balance3 := sdb.GetBalance(addr3) - expectedBalance3 := common.Big0 + expectedBalance3 := common.U2560 if balance3.Cmp(expectedBalance3) != 0 { return fmt.Errorf("expected addr3 balance: %d, found balance: %d", expectedBalance3, balance3) } @@ -879,7 +886,7 @@ func TestReorgReInsert(t *testing.T, create func(db ethdb.Database, gspec *Genes genesisBalance := big.NewInt(1000000000) gspec := &Genesis{ Config: ¶ms.ChainConfig{HomesteadBlock: new(big.Int)}, - Alloc: GenesisAlloc{addr1: {Balance: genesisBalance}}, + Alloc: types.GenesisAlloc{addr1: {Balance: genesisBalance}}, } blockchain, err := create(chainDB, gspec, common.Hash{}) @@ -939,8 +946,9 @@ func TestReorgReInsert(t *testing.T, create func(db ethdb.Database, gspec *Genes return fmt.Errorf("expected addr1 nonce: 3, found nonce: %d", nonce1) } balance1 := sdb.GetBalance(addr1) - transferredFunds := big.NewInt(30000) - expectedBalance := new(big.Int).Sub(genesisBalance, transferredFunds) + transferredFunds := uint256.NewInt(30000) + genesisBalance := uint256.MustFromBig(genesisBalance) + expectedBalance := new(uint256.Int).Sub(genesisBalance, transferredFunds) if balance1.Cmp(expectedBalance) != 0 { return fmt.Errorf("expected balance1: %d, found balance: %d", expectedBalance, balance1) } @@ -984,7 +992,7 @@ func TestAcceptBlockIdenticalStateRoot(t *testing.T, create func(db ethdb.Databa genesisBalance := big.NewInt(1000000000) gspec := &Genesis{ Config: ¶ms.ChainConfig{HomesteadBlock: new(big.Int)}, - Alloc: GenesisAlloc{addr1: {Balance: genesisBalance}}, + Alloc: types.GenesisAlloc{addr1: {Balance: genesisBalance}}, } blockchain, err := create(chainDB, gspec, common.Hash{}) @@ -1082,7 +1090,7 @@ func TestAcceptBlockIdenticalStateRoot(t *testing.T, create func(db ethdb.Databa return fmt.Errorf("expected addr1 nonce: 2, found nonce: %d", nonce1) } balance1 := sdb.GetBalance(addr1) - expectedBalance := common.Big0 + expectedBalance := common.U2560 if balance1.Cmp(expectedBalance) != 0 { return fmt.Errorf("expected balance1: %d, found balance: %d", expectedBalance, balance1) } @@ -1091,6 +1099,7 @@ func TestAcceptBlockIdenticalStateRoot(t *testing.T, create func(db ethdb.Databa return fmt.Errorf("expected addr2 nonce: 0, found nonce %d", nonce2) } balance2 := sdb.GetBalance(addr2) + genesisBalance := uint256.MustFromBig(genesisBalance) if balance2.Cmp(genesisBalance) != 0 { return fmt.Errorf("expected balance2: %d, found %d", genesisBalance, balance2) } @@ -1127,7 +1136,7 @@ func TestReprocessAcceptBlockIdenticalStateRoot(t *testing.T, create func(db eth genesisBalance := big.NewInt(1000000000) gspec := &Genesis{ Config: ¶ms.ChainConfig{HomesteadBlock: new(big.Int)}, - Alloc: GenesisAlloc{addr1: {Balance: genesisBalance}}, + Alloc: types.GenesisAlloc{addr1: {Balance: genesisBalance}}, } blockchain, err := create(chainDB, gspec, common.Hash{}) @@ -1249,7 +1258,7 @@ func TestReprocessAcceptBlockIdenticalStateRoot(t *testing.T, create func(db eth return fmt.Errorf("expected addr1 nonce: 2, found nonce: %d", nonce1) } balance1 := sdb.GetBalance(addr1) - expectedBalance := common.Big0 + expectedBalance := common.U2560 if balance1.Cmp(expectedBalance) != 0 { return fmt.Errorf("expected balance1: %d, found balance: %d", expectedBalance, balance1) } @@ -1258,6 +1267,7 @@ func TestReprocessAcceptBlockIdenticalStateRoot(t *testing.T, create func(db eth return fmt.Errorf("expected addr2 nonce: 0, found nonce %d", nonce2) } balance2 := sdb.GetBalance(addr2) + genesisBalance := uint256.MustFromBig(genesisBalance) if balance2.Cmp(genesisBalance) != 0 { return fmt.Errorf("expected balance2: %d, found %d", genesisBalance, balance2) } @@ -1383,7 +1393,7 @@ func TestInsertChainValidBlockFee(t *testing.T, create func(db ethdb.Database, g genesisBalance := new(big.Int).Mul(big.NewInt(1000000), big.NewInt(params.Ether)) gspec := &Genesis{ Config: params.TestChainConfig, - Alloc: GenesisAlloc{addr1: {Balance: genesisBalance}}, + Alloc: types.GenesisAlloc{addr1: {Balance: genesisBalance}}, } blockchain, err := create(chainDB, gspec, common.Hash{}) @@ -1435,10 +1445,12 @@ func TestInsertChainValidBlockFee(t *testing.T, create func(db ethdb.Database, g return fmt.Errorf("expected nonce addr1: 1, found nonce: %d", nonce) } balance1 := sdb.GetBalance(addr1) - expectedBalance1 := new(big.Int).Sub(genesisBalance, transfer) + transfer := uint256.MustFromBig(transfer) + genesisBalance := uint256.MustFromBig(genesisBalance) + expectedBalance1 := new(uint256.Int).Sub(genesisBalance, transfer) baseFee := params.DefaultFeeConfig.MinBaseFee feeSpend := new(big.Int).Mul(new(big.Int).Add(baseFee, tip), new(big.Int).SetUint64(params.TxGas)) - expectedBalance1.Sub(expectedBalance1, feeSpend) + 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.go b/core/txindexer.go new file mode 100644 index 0000000000..178f31d05f --- /dev/null +++ b/core/txindexer.go @@ -0,0 +1,218 @@ +// Copyright 2024 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 core + +import ( + "fmt" + "time" + + "github.com/ava-labs/subnet-evm/core/rawdb" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/log" +) + +// TxIndexProgress is the struct describing the progress for transaction indexing. +type TxIndexProgress struct { + Indexed uint64 // number of blocks whose transactions are indexed + Remaining uint64 // number of blocks whose transactions are not indexed yet +} + +// Done returns an indicator if the transaction indexing is finished. +func (progress TxIndexProgress) Done() bool { + return progress.Remaining == 0 +} + +// txIndexer is the module responsible for maintaining transaction indexes +// according to the configured indexing range by users. +type txIndexer struct { + // limit is the maximum number of blocks from head whose tx indexes + // are reserved: + // * 0: means the entire chain should be indexed + // * N: means the latest N blocks [HEAD-N+1, HEAD] should be indexed + // and all others shouldn't. + limit uint64 + db ethdb.Database + progress chan chan TxIndexProgress + term chan chan struct{} + closed chan struct{} + + chain *BlockChain +} + +// newTxIndexer initializes the transaction indexer. +func newTxIndexer(limit uint64, chain *BlockChain) *txIndexer { + indexer := &txIndexer{ + limit: limit, + db: chain.db, + progress: make(chan chan TxIndexProgress), + term: make(chan chan struct{}), + closed: make(chan struct{}), + chain: chain, + } + chain.wg.Add(1) + go func() { + defer chain.wg.Done() + indexer.loop(chain) + }() + + var msg string + if limit == 0 { + msg = "entire chain" + } else { + msg = fmt.Sprintf("last %d blocks", limit) + } + log.Info("Initialized transaction indexer", "range", msg) + + return indexer +} + +// run executes the scheduled indexing/unindexing task in a separate thread. +// If the stop channel is closed, the task should be terminated as soon as +// possible, the done channel will be closed once the task is finished. +func (indexer *txIndexer) run(tail *uint64, head uint64, stop chan struct{}, done chan struct{}) { + start := time.Now() + defer func() { + txUnindexTimer.Inc(time.Since(start).Milliseconds()) + close(done) + }() + + // Short circuit if chain is empty and nothing to index. + if head == 0 { + return + } + + // Defensively ensure tail is not nil. + tailValue := uint64(0) + if tail != nil { + // use intermediate variable to avoid modifying the pointer + tailValue = *tail + } + + if head-indexer.limit+1 >= tailValue { + // Unindex a part of stale indices and forward index tail to HEAD-limit + rawdb.UnindexTransactions(indexer.db, tailValue, head-indexer.limit+1, stop, false) + } +} + +// loop is the scheduler of the indexer, assigning indexing/unindexing tasks depending +// on the received chain event. +func (indexer *txIndexer) loop(chain *BlockChain) { + defer close(indexer.closed) + // Listening to chain events and manipulate the transaction indexes. + var ( + stop chan struct{} // Non-nil if background routine is active. + done chan struct{} // Non-nil if background routine is active. + lastHead uint64 // The latest announced chain head (whose tx indexes are assumed created) + + headCh = make(chan ChainEvent) + sub = chain.SubscribeChainAcceptedEvent(headCh) + ) + if sub == nil { + log.Warn("could not create chain accepted subscription to unindex txs") + return + } + defer sub.Unsubscribe() + + log.Info("Initialized transaction unindexer", "limit", indexer.limit) + + // Launch the initial processing if chain is not empty (head != genesis). + // This step is useful in these scenarios that chain has no progress. + if head := indexer.chain.CurrentBlock(); head != nil && head.Number.Uint64() > indexer.limit { + stop = make(chan struct{}) + done = make(chan struct{}) + lastHead = head.Number.Uint64() + indexer.chain.wg.Add(1) + go func() { + indexer.lockedRun(head.Number.Uint64(), stop, done) + }() + } + for { + select { + case head := <-headCh: + headNum := head.Block.NumberU64() + if headNum < indexer.limit { + break + } + + if done == nil { + stop = make(chan struct{}) + done = make(chan struct{}) + indexer.chain.wg.Add(1) + go func() { + indexer.lockedRun(headNum, stop, done) + }() + } + lastHead = head.Block.NumberU64() + case <-done: + stop = nil + done = nil + case ch := <-indexer.progress: + ch <- indexer.report(lastHead, rawdb.ReadTxIndexTail(indexer.db)) + case ch := <-indexer.term: + if stop != nil { + close(stop) + } + if done != nil { + log.Info("Waiting background transaction unindexer to exit") + <-done + } + close(ch) + return + } + } +} + +// report returns the tx indexing progress. +func (indexer *txIndexer) report(head uint64, tail *uint64) TxIndexProgress { + total := indexer.limit + if indexer.limit == 0 || total > head { + total = head + 1 // genesis included + } + var indexed uint64 + if tail != nil { + indexed = head - *tail + 1 + } + // The value of indexed might be larger than total if some blocks need + // to be unindexed, avoiding a negative remaining. + var remaining uint64 + if indexed < total { + remaining = total - indexed + } + return TxIndexProgress{ + Indexed: indexed, + Remaining: remaining, + } +} + +// close shutdown the indexer. Safe to be called for multiple times. +func (indexer *txIndexer) close() { + ch := make(chan struct{}) + select { + case indexer.term <- ch: + <-ch + case <-indexer.closed: + } +} + +// lockedRun runs the indexing/unindexing task in a locked manner. It reads +// the current tail index from the database. +func (indexer *txIndexer) lockedRun(head uint64, stop chan struct{}, done chan struct{}) { + indexer.chain.txIndexTailLock.Lock() + indexer.run(rawdb.ReadTxIndexTail(indexer.db), head, stop, done) + indexer.chain.txIndexTailLock.Unlock() + indexer.chain.wg.Done() +} diff --git a/core/txindexer_test.go b/core/txindexer_test.go new file mode 100644 index 0000000000..39d71d49ce --- /dev/null +++ b/core/txindexer_test.go @@ -0,0 +1,258 @@ +// Copyright 2024 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 core + +import ( + "fmt" + "math/big" + "testing" + + "github.com/ava-labs/subnet-evm/consensus/dummy" + "github.com/ava-labs/subnet-evm/core/rawdb" + "github.com/ava-labs/subnet-evm/core/types" + "github.com/ava-labs/subnet-evm/params" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/stretchr/testify/require" +) + +// Should we try to use the TestTxIndexer from upstream here instead +// or move this test to a new file eg, blockchain_extra_test.go? +func TestTransactionIndices(t *testing.T) { + // Configure and generate a sample block chain + require := require.New(t) + var ( + key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + key2, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a") + addr1 = crypto.PubkeyToAddress(key1.PublicKey) + addr2 = crypto.PubkeyToAddress(key2.PublicKey) + funds = big.NewInt(10000000000000) + gspec = &Genesis{ + Config: ¶ms.ChainConfig{HomesteadBlock: new(big.Int)}, + Alloc: GenesisAlloc{addr1: {Balance: funds}}, + } + signer = types.LatestSigner(gspec.Config) + ) + genDb, blocks, _, err := GenerateChainWithGenesis(gspec, dummy.NewFaker(), 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) + require.NoError(err) + block.AddTx(tx) + }) + require.NoError(err) + + blocks2, _, err := GenerateChain(gspec.Config, blocks[len(blocks)-1], dummy.NewFaker(), 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) + require.NoError(err) + block.AddTx(tx) + }) + require.NoError(err) + + conf := &CacheConfig{ + TrieCleanLimit: 256, + TrieDirtyLimit: 256, + TrieDirtyCommitTarget: 20, + TriePrefetcherParallelism: 4, + Pruning: true, + CommitInterval: 4096, + SnapshotLimit: 256, + SnapshotNoBuild: true, // Ensure the test errors if snapshot initialization fails + AcceptorQueueLimit: 64, + } + + // Init block chain and check all needed indices has been indexed. + chainDB := rawdb.NewMemoryDatabase() + chain, err := createBlockChain(chainDB, conf, gspec, common.Hash{}) + require.NoError(err) + + _, err = chain.InsertChain(blocks) + require.NoError(err) + + for _, block := range blocks { + err := chain.Accept(block) + require.NoError(err) + } + chain.DrainAcceptorQueue() + + lastAcceptedBlock := blocks[len(blocks)-1] + require.Equal(lastAcceptedBlock.Hash(), chain.CurrentHeader().Hash()) + + CheckTxIndices(t, nil, lastAcceptedBlock.NumberU64(), chain.db, false) // check all indices has been indexed + chain.Stop() + + // Reconstruct a block chain which only reserves limited tx indices + // 128 blocks were previously indexed. Now we add a new block at each test step. + limits := []uint64{ + 0, /* tip: 129 reserve all (don't run) */ + 131, /* tip: 130 reserve all */ + 140, /* tip: 131 reserve all */ + 64, /* tip: 132, limit:64 */ + 32, /* tip: 133, limit:32 */ + } + for i, l := range limits { + t.Run(fmt.Sprintf("test-%d, limit: %d", i+1, l), func(t *testing.T) { + conf.TransactionHistory = l + + chain, err := createBlockChain(chainDB, conf, gspec, lastAcceptedBlock.Hash()) + require.NoError(err) + + tail := getTail(l, lastAcceptedBlock.NumberU64()) + // check if startup indices are correct + CheckTxIndices(t, tail, lastAcceptedBlock.NumberU64(), chain.db, false) + + newBlks := blocks2[i : i+1] + _, err = chain.InsertChain(newBlks) // Feed chain a higher block to trigger indices updater. + require.NoError(err) + + lastAcceptedBlock = newBlks[0] + err = chain.Accept(lastAcceptedBlock) // Accept the block to trigger indices updater. + require.NoError(err) + chain.DrainAcceptorQueue() + + tail = getTail(l, lastAcceptedBlock.NumberU64()) + // check if indices are updated correctly + CheckTxIndices(t, tail, lastAcceptedBlock.NumberU64(), chain.db, false) + chain.Stop() + }) + } +} + +func getTail(limit uint64, lastAccepted uint64) *uint64 { + if limit == 0 { + return nil + } + var tail uint64 + if lastAccepted > limit { + // tail should be the oldest block number which is indexed + // i.e the first block number that's in the lookup range + tail = lastAccepted - limit + 1 + } + return &tail +} + +func TestTransactionSkipIndexing(t *testing.T) { + // Configure and generate a sample block chain + require := require.New(t) + var ( + key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + key2, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a") + addr1 = crypto.PubkeyToAddress(key1.PublicKey) + addr2 = crypto.PubkeyToAddress(key2.PublicKey) + funds = big.NewInt(10000000000000) + gspec = &Genesis{ + Config: ¶ms.ChainConfig{HomesteadBlock: new(big.Int)}, + Alloc: GenesisAlloc{addr1: {Balance: funds}}, + } + signer = types.LatestSigner(gspec.Config) + ) + genDb, blocks, _, err := GenerateChainWithGenesis(gspec, dummy.NewCoinbaseFaker(), 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) + require.NoError(err) + block.AddTx(tx) + }) + require.NoError(err) + + blocks2, _, err := GenerateChain(gspec.Config, blocks[len(blocks)-1], dummy.NewCoinbaseFaker(), 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) + require.NoError(err) + block.AddTx(tx) + }) + require.NoError(err) + + conf := &CacheConfig{ + TrieCleanLimit: 256, + TrieDirtyLimit: 256, + TrieDirtyCommitTarget: 20, + TriePrefetcherParallelism: 4, + Pruning: true, + CommitInterval: 4096, + SnapshotLimit: 256, + SnapshotNoBuild: true, // Ensure the test errors if snapshot initialization fails + AcceptorQueueLimit: 64, + SkipTxIndexing: true, + } + + // test1: Init block chain and check all indices has been skipped. + chainDB := rawdb.NewMemoryDatabase() + chain, err := createAndInsertChain(chainDB, conf, gspec, blocks, common.Hash{}, + func(b *types.Block) { + bNumber := b.NumberU64() + checkTxIndicesHelper(t, nil, bNumber+1, bNumber+1, bNumber, chainDB, false) // check all indices has been skipped + }) + require.NoError(err) + chain.Stop() + + // test2: specify lookuplimit with tx index skipping enabled. Blocks should not be indexed but tail should be updated. + conf.TransactionHistory = 2 + chainDB = rawdb.NewMemoryDatabase() + chain, err = createAndInsertChain(chainDB, conf, gspec, blocks, common.Hash{}, + func(b *types.Block) { + bNumber := b.NumberU64() + tail := bNumber - conf.TransactionHistory + 1 + checkTxIndicesHelper(t, &tail, bNumber+1, bNumber+1, bNumber, chainDB, false) // check all indices has been skipped + }) + require.NoError(err) + chain.Stop() + + // test3: tx index skipping and unindexer disabled. Blocks should be indexed and tail should be updated. + conf.TransactionHistory = 0 + conf.SkipTxIndexing = false + chainDB = rawdb.NewMemoryDatabase() + chain, err = createAndInsertChain(chainDB, conf, gspec, blocks, common.Hash{}, + func(b *types.Block) { + bNumber := b.NumberU64() + checkTxIndicesHelper(t, nil, 0, bNumber, bNumber, chainDB, false) // check all indices has been indexed + }) + require.NoError(err) + chain.Stop() + + // now change tx index skipping to true and check that the indices are skipped for the last block + // and old indices are removed up to the tail, but [tail, current) indices are still there. + conf.TransactionHistory = 2 + conf.SkipTxIndexing = true + chain, err = createAndInsertChain(chainDB, conf, gspec, blocks2[0:1], chain.CurrentHeader().Hash(), + func(b *types.Block) { + bNumber := b.NumberU64() + tail := bNumber - conf.TransactionHistory + 1 + checkTxIndicesHelper(t, &tail, tail, bNumber-1, bNumber, chainDB, false) + }) + require.NoError(err) + chain.Stop() +} + +func createAndInsertChain(db ethdb.Database, cacheConfig *CacheConfig, gspec *Genesis, blocks types.Blocks, lastAcceptedHash common.Hash, accepted func(*types.Block)) (*BlockChain, error) { + chain, err := createBlockChain(db, cacheConfig, gspec, lastAcceptedHash) + if err != nil { + return nil, err + } + _, err = chain.InsertChain(blocks) + if err != nil { + return nil, err + } + for _, block := range blocks { + err := chain.Accept(block) + if err != nil { + return nil, err + } + chain.DrainAcceptorQueue() + if accepted != nil { + accepted(block) + } + } + + return chain, nil +} diff --git a/core/txpool/blobpool/blobpool.go b/core/txpool/blobpool/blobpool.go index 8de9a0765b..470a2b4e39 100644 --- a/core/txpool/blobpool/blobpool.go +++ b/core/txpool/blobpool/blobpool.go @@ -278,7 +278,7 @@ func newBlobTxMeta(id uint64, size uint32, tx *types.Transaction) *blobTxMeta { // going up, crossing the smaller positive jump counter). As such, the pool // cares only about the min of the two delta values for eviction priority. // -// priority = min(delta-basefee, delta-blobfee) +// priority = min(deltaBasefee, deltaBlobfee) // // - The above very aggressive dimensionality and noise reduction should result // in transaction being grouped into a small number of buckets, the further @@ -290,7 +290,7 @@ func newBlobTxMeta(id uint64, size uint32, tx *types.Transaction) *blobTxMeta { // with high fee caps since it could enable pool wars. As such, any positive // priority will be grouped together. // -// priority = min(delta-basefee, delta-blobfee, 0) +// priority = min(deltaBasefee, deltaBlobfee, 0) // // Optimisation tradeoffs: // @@ -352,7 +352,7 @@ func (p *BlobPool) Filter(tx *types.Transaction) bool { // Init sets the gas price needed to keep a transaction in the pool and the chain // head to allow balance / nonce checks. The transaction journal will be loaded // from disk and filtered based on the provided starting settings. -func (p *BlobPool) Init(gasTip *big.Int, head *types.Header, reserve txpool.AddressReserver) error { +func (p *BlobPool) Init(gasTip uint64, head *types.Header, reserve txpool.AddressReserver) error { p.reserve = reserve var ( @@ -370,7 +370,7 @@ func (p *BlobPool) Init(gasTip *big.Int, head *types.Header, reserve txpool.Addr } } // Initialize the state with head block, or fallback to empty one in - // case the head state is not available(might occur when node is not + // case the head state is not available (might occur when node is not // fully synced). state, err := p.chain.StateAt(head.Root) if err != nil { @@ -381,14 +381,14 @@ func (p *BlobPool) Init(gasTip *big.Int, head *types.Header, reserve txpool.Addr } p.head, p.state = head, state - // Index all transactions on disk and delete anything inprocessable + // Index all transactions on disk and delete anything unprocessable var fails []uint64 index := func(id uint64, size uint32, blob []byte) { if p.parseTransaction(id, size, blob) != nil { fails = append(fails, id) } } - store, err := billy.Open(billy.Options{Path: queuedir}, newSlotter(), index) + store, err := billy.Open(billy.Options{Path: queuedir, Repair: true}, newSlotter(), index) if err != nil { return err } @@ -396,6 +396,8 @@ func (p *BlobPool) Init(gasTip *big.Int, head *types.Header, reserve txpool.Addr if len(fails) > 0 { log.Warn("Dropping invalidated blob transactions", "ids", fails) + dropInvalidMeter.Mark(int64(len(fails))) + for _, id := range fails { if err := p.store.Delete(id); err != nil { p.Close() @@ -426,7 +428,7 @@ func (p *BlobPool) Init(gasTip *big.Int, head *types.Header, reserve txpool.Addr var ( // basefee = uint256.MustFromBig(eip1559.CalcBaseFee(p.chain.Config(), p.head)) basefee = uint256.MustFromBig(baseFee) - blobfee = uint256.MustFromBig(big.NewInt(params.BlobTxMinBlobGasprice)) + blobfee = uint256.NewInt(params.BlobTxMinBlobGasprice) ) if p.head.ExcessBlobGas != nil { blobfee = uint256.MustFromBig(eip4844.CalcBlobFee(*p.head.ExcessBlobGas)) @@ -444,7 +446,7 @@ func (p *BlobPool) Init(gasTip *big.Int, head *types.Header, reserve txpool.Addr basefeeGauge.Update(int64(basefee.Uint64())) blobfeeGauge.Update(int64(blobfee.Uint64())) - p.SetGasTip(gasTip) + p.SetGasTip(new(big.Int).SetUint64(gasTip)) // Since the user might have modified their pool's capacity, evict anything // above the current allowance @@ -460,7 +462,7 @@ func (p *BlobPool) Init(gasTip *big.Int, head *types.Header, reserve txpool.Addr // Close closes down the underlying persistent store. func (p *BlobPool) Close() error { var errs []error - if p.limbo != nil { + if p.limbo != nil { // Close might be invoked due to error in constructor, before p,limbo is set if err := p.limbo.Close(); err != nil { errs = append(errs, err) } @@ -484,7 +486,7 @@ func (p *BlobPool) parseTransaction(id uint64, size uint32, blob []byte) error { tx := new(types.Transaction) if err := rlp.DecodeBytes(blob, tx); err != nil { // This path is impossible unless the disk data representation changes - // across restarts. For that ever unprobable case, recover gracefully + // across restarts. For that ever improbable case, recover gracefully // by ignoring this data entry. log.Error("Failed to decode blob pool entry", "id", id, "err", err) return err @@ -495,11 +497,17 @@ func (p *BlobPool) parseTransaction(id uint64, size uint32, blob []byte) error { } meta := newBlobTxMeta(id, size, tx) - + if _, exists := p.lookup[meta.hash]; exists { + // This path is only possible after a crash, where deleted items are not + // removed via the normal shutdown-startup procedure and thus may get + // partially resurrected. + log.Error("Rejecting duplicate blob pool entry", "id", id, "hash", tx.Hash()) + return errors.New("duplicate blob entry") + } sender, err := p.signer.Sender(tx) if err != nil { // This path is impossible unless the signature validity changes across - // restarts. For that ever unprobable case, recover gracefully by ignoring + // restarts. For that ever improbable case, recover gracefully by ignoring // this data entry. log.Error("Failed to recover blob tx sender", "id", id, "hash", tx.Hash(), "err", err) return err @@ -558,15 +566,17 @@ func (p *BlobPool) recheck(addr common.Address, inclusions map[common.Hash]uint6 } delete(p.index, addr) delete(p.spent, addr) - if inclusions != nil { // only during reorgs will the heap will be initialized + if inclusions != nil { // only during reorgs will the heap be initialized heap.Remove(p.evict, p.evict.index[addr]) } p.reserve(addr, false) if gapped { log.Warn("Dropping dangling blob transactions", "from", addr, "missing", next, "drop", nonces, "ids", ids) + dropDanglingMeter.Mark(int64(len(ids))) } else { log.Trace("Dropping filled blob transactions", "from", addr, "filled", nonces, "ids", ids) + dropFilledMeter.Mark(int64(len(ids))) } for _, id := range ids { if err := p.store.Delete(id); err != nil { @@ -597,6 +607,8 @@ func (p *BlobPool) recheck(addr common.Address, inclusions map[common.Hash]uint6 txs = txs[1:] } log.Trace("Dropping overlapped blob transactions", "from", addr, "overlapped", nonces, "ids", ids, "left", len(txs)) + dropOverlappedMeter.Mark(int64(len(ids))) + for _, id := range ids { if err := p.store.Delete(id); err != nil { log.Error("Failed to delete blob transaction", "from", addr, "id", id, "err", err) @@ -611,7 +623,7 @@ func (p *BlobPool) recheck(addr common.Address, inclusions map[common.Hash]uint6 txs[0].evictionBlobFeeJumps = txs[0].blobfeeJumps for i := 1; i < len(txs); i++ { - // If there's no nonce gap, initialize the evicion thresholds as the + // If there's no nonce gap, initialize the eviction thresholds as the // minimum between the cumulative thresholds and the current tx fees if txs[i].nonce == txs[i-1].nonce+1 { txs[i].evictionExecTip = txs[i-1].evictionExecTip @@ -628,10 +640,30 @@ func (p *BlobPool) recheck(addr common.Address, inclusions map[common.Hash]uint6 } continue } - // Sanity check that there's no double nonce. This case would be a coding - // error, but better know about it + // Sanity check that there's no double nonce. This case would generally + // be a coding error, so better know about it. + // + // Also, Billy behind the blobpool does not journal deletes. A process + // crash would result in previously deleted entities being resurrected. + // That could potentially cause a duplicate nonce to appear. if txs[i].nonce == txs[i-1].nonce { - log.Error("Duplicate nonce blob transaction", "from", addr, "nonce", txs[i].nonce) + id := p.lookup[txs[i].hash] + + log.Error("Dropping repeat nonce blob transaction", "from", addr, "nonce", txs[i].nonce, "id", id) + dropRepeatedMeter.Mark(1) + + p.spent[addr] = new(uint256.Int).Sub(p.spent[addr], txs[i].costCap) + p.stored -= uint64(txs[i].size) + delete(p.lookup, txs[i].hash) + + if err := p.store.Delete(id); err != nil { + log.Error("Failed to delete blob transaction", "from", addr, "id", id, "err", err) + } + txs = append(txs[:i], txs[i+1:]...) + p.index[addr] = txs + + i-- + continue } // Otherwise if there's a nonce gap evict all later transactions var ( @@ -649,6 +681,8 @@ func (p *BlobPool) recheck(addr common.Address, inclusions map[common.Hash]uint6 txs = txs[:i] log.Error("Dropping gapped blob transactions", "from", addr, "missing", txs[i-1].nonce+1, "drop", nonces, "ids", ids) + dropGappedMeter.Mark(int64(len(ids))) + for _, id := range ids { if err := p.store.Delete(id); err != nil { log.Error("Failed to delete blob transaction", "from", addr, "id", id, "err", err) @@ -660,7 +694,7 @@ func (p *BlobPool) recheck(addr common.Address, inclusions map[common.Hash]uint6 // Ensure that there's no over-draft, this is expected to happen when some // transactions get included without publishing on the network var ( - balance = uint256.MustFromBig(p.state.GetBalance(addr)) + balance = p.state.GetBalance(addr) spent = p.spent[addr] ) if spent.Cmp(balance) > 0 { @@ -685,7 +719,7 @@ func (p *BlobPool) recheck(addr common.Address, inclusions map[common.Hash]uint6 if len(txs) == 0 { delete(p.index, addr) delete(p.spent, addr) - if inclusions != nil { // only during reorgs will the heap will be initialized + if inclusions != nil { // only during reorgs will the heap be initialized heap.Remove(p.evict, p.evict.index[addr]) } p.reserve(addr, false) @@ -693,6 +727,8 @@ func (p *BlobPool) recheck(addr common.Address, inclusions map[common.Hash]uint6 p.index[addr] = txs } log.Warn("Dropping overdrafted blob transactions", "from", addr, "balance", balance, "spent", spent, "drop", nonces, "ids", ids) + dropOverdraftedMeter.Mark(int64(len(ids))) + for _, id := range ids { if err := p.store.Delete(id); err != nil { log.Error("Failed to delete blob transaction", "from", addr, "id", id, "err", err) @@ -723,6 +759,8 @@ func (p *BlobPool) recheck(addr common.Address, inclusions map[common.Hash]uint6 p.index[addr] = txs log.Warn("Dropping overcapped blob transactions", "from", addr, "kept", len(txs), "drop", nonces, "ids", ids) + dropOvercappedMeter.Mark(int64(len(ids))) + for _, id := range ids { if err := p.store.Delete(id); err != nil { log.Error("Failed to delete blob transaction", "from", addr, "id", id, "err", err) @@ -739,7 +777,7 @@ func (p *BlobPool) recheck(addr common.Address, inclusions map[common.Hash]uint6 // offload removes a tracked blob transaction from the pool and moves it into the // limbo for tracking until finality. // -// The method may log errors for various unexpcted scenarios but will not return +// The method may log errors for various unexpected scenarios but will not return // any of it since there's no clear error case. Some errors may be due to coding // issues, others caused by signers mining MEV stuff or swapping transactions. In // all cases, the pool needs to continue operating. @@ -797,7 +835,7 @@ func (p *BlobPool) Reset(oldHead, newHead *types.Header) { } } // Recheck the account's pooled transactions to drop included and - // invalidated one + // invalidated ones p.recheck(addr, inclusions) } if len(adds) > 0 { @@ -813,7 +851,7 @@ func (p *BlobPool) Reset(oldHead, newHead *types.Header) { log.Error("Failed to get fee config to reset blobpool fees", "err", err) return } - _, baseFee, err := dummy.EstimateNextBaseFee( + _, baseFeeBig, err := dummy.EstimateNextBaseFee( p.chain.Config(), feeConfig, p.head, @@ -826,7 +864,7 @@ func (p *BlobPool) Reset(oldHead, newHead *types.Header) { // Reset the price heap for the new set of basefee/blobfee pairs var ( // basefee = uint256.MustFromBig(eip1559.CalcBaseFee(p.chain.Config(), newHead)) - basefee = uint256.MustFromBig(baseFee) + basefee = uint256.MustFromBig(baseFeeBig) blobfee = uint256.MustFromBig(big.NewInt(params.BlobTxMinBlobGasprice)) ) if newHead.ExcessBlobGas != nil { @@ -996,7 +1034,7 @@ func (p *BlobPool) reinject(addr common.Address, txhash common.Hash) error { return err } - // Update the indixes and metrics + // Update the indices and metrics meta := newBlobTxMeta(id, p.store.Size(id), tx) if _, ok := p.index[addr]; !ok { if err := p.reserve(addr, true); err != nil { @@ -1063,6 +1101,8 @@ func (p *BlobPool) SetGasTip(tip *big.Int) { } // Clear out the transactions from the data store log.Warn("Dropping underpriced blob transaction", "from", addr, "rejected", tx.nonce, "tip", tx.execTipCap, "want", tip, "drop", nonces, "ids", ids) + dropUnderpricedMeter.Mark(int64(len(ids))) + for _, id := range ids { if err := p.store.Delete(id); err != nil { log.Error("Failed to delete dropped transaction", "id", id, "err", err) @@ -1210,7 +1250,7 @@ func (p *BlobPool) Get(hash common.Hash) *types.Transaction { } // Add inserts a set of blob transactions into the pool if they pass validation (both -// consensus validity and pool restictions). +// consensus validity and pool restrictions). func (p *BlobPool) Add(txs []*types.Transaction, local bool, sync bool) []error { var ( adds = make([]*types.Transaction, 0, len(txs)) @@ -1230,10 +1270,10 @@ func (p *BlobPool) Add(txs []*types.Transaction, local bool, sync bool) []error } // Add inserts a new blob transaction into the pool if it passes validation (both -// consensus validity and pool restictions). +// consensus validity and pool restrictions). func (p *BlobPool) add(tx *types.Transaction) (err error) { // The blob pool blocks on adding a transaction. This is because blob txs are - // only even pulled form the network, so this method will act as the overload + // only even pulled from the network, so this method will act as the overload // protection for fetches. waitStart := time.Now() p.lock.Lock() @@ -1247,6 +1287,22 @@ func (p *BlobPool) add(tx *types.Transaction) (err error) { // Ensure the transaction is valid from all perspectives if err := p.validateTx(tx); err != nil { log.Trace("Transaction validation failed", "hash", tx.Hash(), "err", err) + switch { + case errors.Is(err, txpool.ErrUnderpriced): + addUnderpricedMeter.Mark(1) + case errors.Is(err, core.ErrNonceTooLow): + addStaleMeter.Mark(1) + case errors.Is(err, core.ErrNonceTooHigh): + addGappedMeter.Mark(1) + case errors.Is(err, core.ErrInsufficientFunds): + addOverdraftedMeter.Mark(1) + case errors.Is(err, txpool.ErrAccountLimitExceeded): + addOvercappedMeter.Mark(1) + case errors.Is(err, txpool.ErrReplaceUnderpriced): + addNoreplaceMeter.Mark(1) + default: + addInvalidMeter.Mark(1) + } return err } // If the address is not yet known, request exclusivity to track the account @@ -1254,6 +1310,7 @@ func (p *BlobPool) add(tx *types.Transaction) (err error) { from, _ := types.Sender(p.signer, tx) // already validated above if _, ok := p.index[from]; !ok { if err := p.reserve(from, true); err != nil { + addNonExclusiveMeter.Mark(1) return err } defer func() { @@ -1293,6 +1350,8 @@ func (p *BlobPool) add(tx *types.Transaction) (err error) { } if len(p.index[from]) > offset { // Transaction replaces a previously queued one + dropReplacedMeter.Mark(1) + prev := p.index[from][offset] if err := p.store.Delete(prev.id); err != nil { // Shitty situation, but try to recover gracefully instead of going boom @@ -1371,6 +1430,7 @@ func (p *BlobPool) add(tx *types.Transaction) (err error) { } p.updateStorageMetrics() + addValidMeter.Mark(1) return nil } @@ -1404,7 +1464,7 @@ func (p *BlobPool) drop() { p.stored -= uint64(drop.size) delete(p.lookup, drop.hash) - // Remove the transaction from the pool's evicion heap: + // Remove the transaction from the pool's eviction heap: // - If the entire account was dropped, pop off the address // - Otherwise, if the new tail has better eviction caps, fix the heap if last { @@ -1420,7 +1480,9 @@ func (p *BlobPool) drop() { } } // Remove the transaction from the data store - log.Warn("Evicting overflown blob transaction", "from", from, "evicted", drop.nonce, "id", drop.id) + log.Debug("Evicting overflown blob transaction", "from", from, "evicted", drop.nonce, "id", drop.id) + dropOverflownMeter.Mark(1) + if err := p.store.Delete(drop.id); err != nil { log.Error("Failed to drop evicted transaction", "id", drop.id, "err", err) } @@ -1428,7 +1490,15 @@ func (p *BlobPool) drop() { // Pending retrieves all currently processable transactions, grouped by origin // account and sorted by nonce. -func (p *BlobPool) Pending(enforceTips bool) map[common.Address][]*txpool.LazyTransaction { +// +// The transactions can also be pre-filtered by the dynamic fee components to +// reduce allocations and load on downstream subsystems. +func (p *BlobPool) Pending(filter txpool.PendingFilter) map[common.Address][]*txpool.LazyTransaction { + // If only plain transactions are requested, this pool is unsuitable as it + // contains none, don't even bother. + if filter.OnlyPlainTxs { + return nil + } // Track the amount of time waiting to retrieve the list of pending blob txs // from the pool and the amount of time actually spent on assembling the data. // The latter will be pretty much moot, but we've kept it to have symmetric @@ -1438,20 +1508,40 @@ func (p *BlobPool) Pending(enforceTips bool) map[common.Address][]*txpool.LazyTr pendwaitHist.Update(time.Since(pendStart).Nanoseconds()) defer p.lock.RUnlock() - defer func(start time.Time) { - pendtimeHist.Update(time.Since(start).Nanoseconds()) - }(time.Now()) + execStart := time.Now() + defer func() { + pendtimeHist.Update(time.Since(execStart).Nanoseconds()) + }() - pending := make(map[common.Address][]*txpool.LazyTransaction) + pending := make(map[common.Address][]*txpool.LazyTransaction, len(p.index)) for addr, txs := range p.index { - var lazies []*txpool.LazyTransaction + lazies := make([]*txpool.LazyTransaction, 0, len(txs)) for _, tx := range txs { + // If transaction filtering was requested, discard badly priced ones + if filter.MinTip != nil && filter.BaseFee != nil { + if tx.execFeeCap.Lt(filter.BaseFee) { + break // basefee too low, cannot be included, discard rest of txs from the account + } + tip := new(uint256.Int).Sub(tx.execFeeCap, filter.BaseFee) + if tip.Gt(tx.execTipCap) { + tip = tx.execTipCap + } + if tip.Lt(filter.MinTip) { + break // allowed or remaining tip too low, cannot be included, discard rest of txs from the account + } + } + if filter.BlobFee != nil { + if tx.blobFeeCap.Lt(filter.BlobFee) { + break // blobfee too low, cannot be included, discard rest of txs from the account + } + } + // Transaction was accepted according to the filter, append to the pending list lazies = append(lazies, &txpool.LazyTransaction{ Pool: p, Hash: tx.hash, - Time: time.Now(), // TODO(karalabe): Maybe save these and use that? - GasFeeCap: tx.execFeeCap.ToBig(), - GasTipCap: tx.execTipCap.ToBig(), + Time: execStart, // TODO(karalabe): Maybe save these and use that? + GasFeeCap: tx.execFeeCap, + GasTipCap: tx.execTipCap, Gas: tx.execGas, BlobGas: tx.blobGas, }) @@ -1463,10 +1553,6 @@ func (p *BlobPool) Pending(enforceTips bool) map[common.Address][]*txpool.LazyTr return pending } -func (p *BlobPool) PendingWithBaseFee(enforceTips bool, baseFee *big.Int) map[common.Address][]*txpool.LazyTransaction { - return p.Pending(enforceTips) -} - // IteratePending iterates over [pool.pending] until [f] returns false. // The caller must not modify [tx]. Returns false if iteration was interrupted. func (pool *BlobPool) IteratePending(f func(tx *types.Transaction) bool) bool { @@ -1537,7 +1623,7 @@ func (p *BlobPool) updateStorageMetrics() { } // updateLimboMetrics retrieves a bunch of stats from the limbo store and pushes -// // them out as metrics. +// them out as metrics. func (p *BlobPool) updateLimboMetrics() { stats := p.limbo.store.Infos() diff --git a/core/txpool/blobpool/blobpool_test.go b/core/txpool/blobpool/blobpool_test.go index 95b3e48c68..27bf9f0eab 100644 --- a/core/txpool/blobpool/blobpool_test.go +++ b/core/txpool/blobpool/blobpool_test.go @@ -62,21 +62,9 @@ var ( emptyBlob = kzg4844.Blob{} emptyBlobCommit, _ = kzg4844.BlobToCommitment(emptyBlob) emptyBlobProof, _ = kzg4844.ComputeBlobProof(emptyBlob, emptyBlobCommit) - emptyBlobVHash = blobHash(emptyBlobCommit) + emptyBlobVHash = kzg4844.CalcBlobHashV1(sha256.New(), &emptyBlobCommit) ) -func blobHash(commit kzg4844.Commitment) common.Hash { - hasher := sha256.New() - hasher.Write(commit[:]) - hash := hasher.Sum(nil) - - var vhash common.Hash - vhash[0] = params.BlobTxHashVersion - copy(vhash[1:], hash[1:]) - - return vhash -} - // Chain configuration with Cancun enabled. // // TODO(karalabe): replace with params.MainnetChainConfig after Cancun. @@ -222,7 +210,7 @@ func makeTx(nonce uint64, gasTipCap uint64, gasFeeCap uint64, blobFeeCap uint64, return types.MustSignNewTx(key, types.LatestSigner(testChainConfig), blobtx) } -// makeUnsignedTx is a utility method to construct a random blob tranasaction +// makeUnsignedTx is a utility method to construct a random blob transaction // without signing it. func makeUnsignedTx(nonce uint64, gasTipCap uint64, gasFeeCap uint64, blobFeeCap uint64) *types.BlobTx { return &types.BlobTx{ @@ -342,7 +330,16 @@ func verifyPoolInternals(t *testing.T, pool *BlobPool) { // - 1. A transaction that cannot be decoded must be dropped // - 2. A transaction that cannot be recovered (bad signature) must be dropped // - 3. All transactions after a nonce gap must be dropped -// - 4. All transactions after an underpriced one (including it) must be dropped +// - 4. All transactions after an already included nonce must be dropped +// - 5. All transactions after an underpriced one (including it) must be dropped +// - 6. All transactions after an overdrafting sequence must be dropped +// - 7. All transactions exceeding the per-account limit must be dropped +// +// Furthermore, some strange corner-cases can also occur after a crash, as Billy's +// simplicity also allows it to resurrect past deleted entities: +// +// - 8. Fully duplicate transactions (matching hash) must be dropped +// - 9. Duplicate nonces from the same account must be dropped func TestOpenDrops(t *testing.T) { log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelTrace, true))) @@ -375,7 +372,7 @@ func TestOpenDrops(t *testing.T) { badsig, _ := store.Put(blob) // Insert a sequence of transactions with a nonce gap in between to verify - // that anything gapped will get evicted (case 3) + // that anything gapped will get evicted (case 3). var ( gapper, _ = crypto.GenerateKey() @@ -394,7 +391,7 @@ func TestOpenDrops(t *testing.T) { } } // Insert a sequence of transactions with a gapped starting nonce to verify - // that the entire set will get dropped. + // that the entire set will get dropped (case 3). var ( dangler, _ = crypto.GenerateKey() dangling = make(map[uint64]struct{}) @@ -407,7 +404,7 @@ func TestOpenDrops(t *testing.T) { dangling[id] = struct{}{} } // Insert a sequence of transactions with already passed nonces to veirfy - // that the entire set will get dropped. + // that the entire set will get dropped (case 4). var ( filler, _ = crypto.GenerateKey() filled = make(map[uint64]struct{}) @@ -419,8 +416,8 @@ func TestOpenDrops(t *testing.T) { id, _ := store.Put(blob) filled[id] = struct{}{} } - // Insert a sequence of transactions with partially passed nonces to veirfy - // that the included part of the set will get dropped + // Insert a sequence of transactions with partially passed nonces to verify + // that the included part of the set will get dropped (case 4). var ( overlapper, _ = crypto.GenerateKey() overlapped = make(map[uint64]struct{}) @@ -437,7 +434,7 @@ func TestOpenDrops(t *testing.T) { } } // Insert a sequence of transactions with an underpriced first to verify that - // the entire set will get dropped (case 4). + // the entire set will get dropped (case 5). var ( underpayer, _ = crypto.GenerateKey() underpaid = make(map[uint64]struct{}) @@ -456,7 +453,7 @@ func TestOpenDrops(t *testing.T) { } // Insert a sequence of transactions with an underpriced in between to verify - // that it and anything newly gapped will get evicted (case 4). + // that it and anything newly gapped will get evicted (case 5). var ( outpricer, _ = crypto.GenerateKey() outpriced = make(map[uint64]struct{}) @@ -478,7 +475,7 @@ func TestOpenDrops(t *testing.T) { } } // Insert a sequence of transactions fully overdrafted to verify that the - // entire set will get invalidated. + // entire set will get invalidated (case 6). var ( exceeder, _ = crypto.GenerateKey() exceeded = make(map[uint64]struct{}) @@ -496,7 +493,7 @@ func TestOpenDrops(t *testing.T) { exceeded[id] = struct{}{} } // Insert a sequence of transactions partially overdrafted to verify that part - // of the set will get invalidated. + // of the set will get invalidated (case 6). var ( overdrafter, _ = crypto.GenerateKey() overdrafted = make(map[uint64]struct{}) @@ -518,7 +515,7 @@ func TestOpenDrops(t *testing.T) { } } // Insert a sequence of transactions overflowing the account cap to verify - // that part of the set will get invalidated. + // that part of the set will get invalidated (case 7). var ( overcapper, _ = crypto.GenerateKey() overcapped = make(map[uint64]struct{}) @@ -533,21 +530,59 @@ func TestOpenDrops(t *testing.T) { overcapped[id] = struct{}{} } } + // Insert a batch of duplicated transactions to verify that only one of each + // version will remain (case 8). + var ( + duplicater, _ = crypto.GenerateKey() + duplicated = make(map[uint64]struct{}) + ) + for _, nonce := range []uint64{0, 1, 2} { + blob, _ := rlp.EncodeToBytes(makeTx(nonce, 1, 1, 1, duplicater)) + + for i := 0; i < int(nonce)+1; i++ { + id, _ := store.Put(blob) + if i == 0 { + valids[id] = struct{}{} + } else { + duplicated[id] = struct{}{} + } + } + } + // Insert a batch of duplicated nonces to verify that only one of each will + // remain (case 9). + var ( + repeater, _ = crypto.GenerateKey() + repeated = make(map[uint64]struct{}) + ) + for _, nonce := range []uint64{0, 1, 2} { + for i := 0; i < int(nonce)+1; i++ { + blob, _ := rlp.EncodeToBytes(makeTx(nonce, 1, uint64(i)+1 /* unique hashes */, 1, repeater)) + + id, _ := store.Put(blob) + if i == 0 { + valids[id] = struct{}{} + } else { + repeated[id] = struct{}{} + } + } + } store.Close() // Create a blob pool out of the pre-seeded data statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewDatabase(memorydb.New())), nil) - statedb.AddBalance(crypto.PubkeyToAddress(gapper.PublicKey), big.NewInt(1000000)) - statedb.AddBalance(crypto.PubkeyToAddress(dangler.PublicKey), big.NewInt(1000000)) - statedb.AddBalance(crypto.PubkeyToAddress(filler.PublicKey), big.NewInt(1000000)) + statedb.AddBalance(crypto.PubkeyToAddress(gapper.PublicKey), uint256.NewInt(1000000)) + statedb.AddBalance(crypto.PubkeyToAddress(dangler.PublicKey), uint256.NewInt(1000000)) + statedb.AddBalance(crypto.PubkeyToAddress(filler.PublicKey), uint256.NewInt(1000000)) statedb.SetNonce(crypto.PubkeyToAddress(filler.PublicKey), 3) - statedb.AddBalance(crypto.PubkeyToAddress(overlapper.PublicKey), big.NewInt(1000000)) + statedb.AddBalance(crypto.PubkeyToAddress(overlapper.PublicKey), uint256.NewInt(1000000)) statedb.SetNonce(crypto.PubkeyToAddress(overlapper.PublicKey), 2) - statedb.AddBalance(crypto.PubkeyToAddress(underpayer.PublicKey), big.NewInt(1000000)) - statedb.AddBalance(crypto.PubkeyToAddress(outpricer.PublicKey), big.NewInt(1000000)) - statedb.AddBalance(crypto.PubkeyToAddress(exceeder.PublicKey), big.NewInt(1000000)) - statedb.AddBalance(crypto.PubkeyToAddress(overdrafter.PublicKey), big.NewInt(1000000)) - statedb.AddBalance(crypto.PubkeyToAddress(overcapper.PublicKey), big.NewInt(10000000)) + statedb.AddBalance(crypto.PubkeyToAddress(underpayer.PublicKey), uint256.NewInt(1000000)) + statedb.AddBalance(crypto.PubkeyToAddress(outpricer.PublicKey), uint256.NewInt(1000000)) + statedb.AddBalance(crypto.PubkeyToAddress(exceeder.PublicKey), uint256.NewInt(1000000)) + statedb.AddBalance(crypto.PubkeyToAddress(overdrafter.PublicKey), uint256.NewInt(1000000)) + statedb.AddBalance(crypto.PubkeyToAddress(overcapper.PublicKey), uint256.NewInt(10000000)) + statedb.AddBalance(crypto.PubkeyToAddress(duplicater.PublicKey), uint256.NewInt(1000000)) + statedb.AddBalance(crypto.PubkeyToAddress(repeater.PublicKey), uint256.NewInt(1000000)) statedb.Commit(0, true, false) chain := &testBlockChain{ @@ -557,7 +592,7 @@ func TestOpenDrops(t *testing.T) { statedb: statedb, } pool := New(Config{Datadir: storage}, chain) - if err := pool.Init(big.NewInt(1), chain.CurrentBlock(), makeAddressReserver()); err != nil { + if err := pool.Init(1, chain.CurrentBlock(), makeAddressReserver()); err != nil { t.Fatalf("failed to create blob pool: %v", err) } defer pool.Close() @@ -591,6 +626,10 @@ func TestOpenDrops(t *testing.T) { t.Errorf("partially overdrafted transaction remained in storage: %d", tx.id) } else if _, ok := overcapped[tx.id]; ok { t.Errorf("overcapped transaction remained in storage: %d", tx.id) + } else if _, ok := duplicated[tx.id]; ok { + t.Errorf("duplicated transaction remained in storage: %d", tx.id) + } else if _, ok := repeated[tx.id]; ok { + t.Errorf("repeated nonce transaction remained in storage: %d", tx.id) } else { alive[tx.id] = struct{}{} } @@ -621,7 +660,7 @@ func TestOpenDrops(t *testing.T) { // Tests that transactions loaded from disk are indexed correctly. // -// - 1. Transactions must be groupped by sender, sorted by nonce +// - 1. Transactions must be grouped by sender, sorted by nonce // - 2. Eviction thresholds are calculated correctly for the sequences // - 3. Balance usage of an account is totals across all transactions func TestOpenIndex(t *testing.T) { @@ -635,7 +674,7 @@ func TestOpenIndex(t *testing.T) { store, _ := billy.Open(billy.Options{Path: filepath.Join(storage, pendingTransactionStore)}, newSlotter(), nil) // Insert a sequence of transactions with varying price points to check that - // the cumulative minimumw will be maintained. + // the cumulative minimum will be maintained. var ( key, _ = crypto.GenerateKey() addr = crypto.PubkeyToAddress(key.PublicKey) @@ -662,7 +701,7 @@ func TestOpenIndex(t *testing.T) { // Create a blob pool out of the pre-seeded data statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewDatabase(memorydb.New())), nil) - statedb.AddBalance(addr, big.NewInt(1_000_000_000)) + statedb.AddBalance(addr, uint256.NewInt(1_000_000_000)) statedb.Commit(0, true, false) chain := &testBlockChain{ @@ -672,7 +711,7 @@ func TestOpenIndex(t *testing.T) { statedb: statedb, } pool := New(Config{Datadir: storage}, chain) - if err := pool.Init(big.NewInt(1), chain.CurrentBlock(), makeAddressReserver()); err != nil { + if err := pool.Init(1, chain.CurrentBlock(), makeAddressReserver()); err != nil { t.Fatalf("failed to create blob pool: %v", err) } defer pool.Close() @@ -762,9 +801,9 @@ func TestOpenHeap(t *testing.T) { // Create a blob pool out of the pre-seeded data statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewDatabase(memorydb.New())), nil) - statedb.AddBalance(addr1, big.NewInt(1_000_000_000)) - statedb.AddBalance(addr2, big.NewInt(1_000_000_000)) - statedb.AddBalance(addr3, big.NewInt(1_000_000_000)) + statedb.AddBalance(addr1, uint256.NewInt(1_000_000_000)) + statedb.AddBalance(addr2, uint256.NewInt(1_000_000_000)) + statedb.AddBalance(addr3, uint256.NewInt(1_000_000_000)) statedb.Commit(0, true, false) chain := &testBlockChain{ @@ -774,7 +813,7 @@ func TestOpenHeap(t *testing.T) { statedb: statedb, } pool := New(Config{Datadir: storage}, chain) - if err := pool.Init(big.NewInt(1), chain.CurrentBlock(), makeAddressReserver()); err != nil { + if err := pool.Init(1, chain.CurrentBlock(), makeAddressReserver()); err != nil { t.Fatalf("failed to create blob pool: %v", err) } defer pool.Close() @@ -842,9 +881,9 @@ func TestOpenCap(t *testing.T) { for _, datacap := range []uint64{2 * (txAvgSize + blobSize), 100 * (txAvgSize + blobSize)} { // Create a blob pool out of the pre-seeded data, but cap it to 2 blob transaction statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewDatabase(memorydb.New())), nil) - statedb.AddBalance(addr1, big.NewInt(1_000_000_000)) - statedb.AddBalance(addr2, big.NewInt(1_000_000_000)) - statedb.AddBalance(addr3, big.NewInt(1_000_000_000)) + statedb.AddBalance(addr1, uint256.NewInt(1_000_000_000)) + statedb.AddBalance(addr2, uint256.NewInt(1_000_000_000)) + statedb.AddBalance(addr3, uint256.NewInt(1_000_000_000)) statedb.Commit(0, true, false) chain := &testBlockChain{ @@ -854,7 +893,7 @@ func TestOpenCap(t *testing.T) { statedb: statedb, } pool := New(Config{Datadir: storage, Datacap: datacap}, chain) - if err := pool.Init(big.NewInt(1), chain.CurrentBlock(), makeAddressReserver()); err != nil { + if err := pool.Init(1, chain.CurrentBlock(), makeAddressReserver()); err != nil { t.Fatalf("failed to create blob pool: %v", err) } // Verify that enough transactions have been dropped to get the pool's size @@ -1214,6 +1253,24 @@ func TestAdd(t *testing.T) { }, }, }, + // Blob transactions that don't meet the min blob gas price should be rejected + { + seeds: map[string]seed{ + "alice": {balance: 10000000}, + }, + adds: []addtx{ + { // New account, no previous txs, nonce 0, but blob fee cap too low + from: "alice", + tx: makeUnsignedTx(0, 1, 1, 0), + err: txpool.ErrUnderpriced, + }, + { // Same as above but blob fee cap equals minimum, should be accepted + from: "alice", + tx: makeUnsignedTx(0, 1, 1, params.BlobTxMinBlobGasprice), + err: nil, + }, + }, + }, } for i, tt := range tests { // Create a temporary folder for the persistent backend @@ -1234,8 +1291,8 @@ func TestAdd(t *testing.T) { keys[acc], _ = crypto.GenerateKey() addrs[acc] = crypto.PubkeyToAddress(keys[acc].PublicKey) - // Seed the state database with this acocunt - statedb.AddBalance(addrs[acc], new(big.Int).SetUint64(seed.balance)) + // Seed the state database with this account + statedb.AddBalance(addrs[acc], new(uint256.Int).SetUint64(seed.balance)) statedb.SetNonce(addrs[acc], seed.nonce) // Sign the seed transactions and store them in the data store @@ -1256,7 +1313,7 @@ func TestAdd(t *testing.T) { statedb: statedb, } pool := New(Config{Datadir: storage}, chain) - if err := pool.Init(big.NewInt(1), chain.CurrentBlock(), makeAddressReserver()); err != nil { + if err := pool.Init(1, chain.CurrentBlock(), makeAddressReserver()); err != nil { t.Fatalf("test %d: failed to create blob pool: %v", i, err) } verifyPoolInternals(t, pool) @@ -1274,3 +1331,65 @@ func TestAdd(t *testing.T) { pool.Close() } } + +// Benchmarks the time it takes to assemble the lazy pending transaction list +// from the pool contents. +func BenchmarkPoolPending100Mb(b *testing.B) { benchmarkPoolPending(b, 100_000_000) } +func BenchmarkPoolPending1GB(b *testing.B) { benchmarkPoolPending(b, 1_000_000_000) } +func BenchmarkPoolPending10GB(b *testing.B) { benchmarkPoolPending(b, 10_000_000_000) } + +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 + + var ( + basefee = uint64(1050) + blobfee = uint64(105) + signer = types.LatestSigner(testChainConfig) + statedb, _ = state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewDatabase(memorydb.New())), nil) + chain = &testBlockChain{ + config: testChainConfig, + basefee: uint256.NewInt(basefee), + blobfee: uint256.NewInt(blobfee), + statedb: statedb, + } + pool = New(Config{Datadir: ""}, chain) + ) + + if err := pool.Init(1, chain.CurrentBlock(), makeAddressReserver()); err != nil { + b.Fatalf("failed to create blob pool: %v", err) + } + // Fill the pool up with one random transaction from each account with the + // same price and everything to maximize the worst case scenario + for i := 0; i < int(capacity); i++ { + blobtx := makeUnsignedTx(0, 10, basefee+10, blobfee) + blobtx.R = uint256.NewInt(1) + blobtx.S = uint256.NewInt(uint64(100 + i)) + blobtx.V = uint256.NewInt(0) + tx := types.NewTx(blobtx) + addr, err := types.Sender(signer, tx) + if err != nil { + b.Fatal(err) + } + statedb.AddBalance(addr, uint256.NewInt(1_000_000_000)) + pool.add(tx) + } + statedb.Commit(0, true, false) + defer pool.Close() + + // Benchmark assembling the pending + b.ResetTimer() + b.ReportAllocs() + + for i := 0; i < b.N; i++ { + p := pool.Pending(txpool.PendingFilter{ + MinTip: uint256.NewInt(1), + BaseFee: chain.basefee, + BlobFee: chain.blobfee, + }) + if len(p) != int(capacity) { + b.Fatalf("have %d want %d", len(p), capacity) + } + } +} diff --git a/core/txpool/blobpool/config.go b/core/txpool/blobpool/config.go index 6015b1baf6..5df7885fb3 100644 --- a/core/txpool/blobpool/config.go +++ b/core/txpool/blobpool/config.go @@ -40,8 +40,8 @@ type Config struct { // DefaultConfig contains the default configurations for the transaction pool. var DefaultConfig = Config{ Datadir: "blobpool", - Datacap: 10 * 1024 * 1024 * 1024, - PriceBump: 100, // either have patience or be aggressive, no mushy ground + Datacap: 10 * 1024 * 1024 * 1024 / 4, // TODO(karalabe): /4 handicap for rollout, gradually bump back up to 10GB + PriceBump: 100, // either have patience or be aggressive, no mushy ground } // sanitize checks the provided user configurations and changes anything that's diff --git a/core/txpool/blobpool/evictheap.go b/core/txpool/blobpool/evictheap.go index fcdded9abd..0824ddf735 100644 --- a/core/txpool/blobpool/evictheap.go +++ b/core/txpool/blobpool/evictheap.go @@ -40,7 +40,7 @@ import ( // transaction from each account to determine which account to evict from. // // The heap internally tracks a slice of cheapest transactions from each account -// and a mapping from addresses to indices for direct removals/udates. +// and a mapping from addresses to indices for direct removals/updates. // // The goal of the heap is to decide which account has the worst bottleneck to // evict transactions from. diff --git a/core/txpool/blobpool/limbo.go b/core/txpool/blobpool/limbo.go index 9d97562f51..26d28f36cc 100644 --- a/core/txpool/blobpool/limbo.go +++ b/core/txpool/blobpool/limbo.go @@ -63,7 +63,7 @@ func newLimbo(datadir string) (*limbo, error) { index: make(map[common.Hash]uint64), groups: make(map[uint64]map[uint64]common.Hash), } - // Index all limboed blobs on disk and delete anything inprocessable + // Index all limboed blobs on disk and delete anything unprocessable var fails []uint64 index := func(id uint64, size uint32, data []byte) { if l.parseBlob(id, data) != nil { @@ -99,7 +99,7 @@ func (l *limbo) parseBlob(id uint64, data []byte) error { item := new(limboBlob) if err := rlp.DecodeBytes(data, item); err != nil { // This path is impossible unless the disk data representation changes - // across restarts. For that ever unprobable case, recover gracefully + // across restarts. For that ever improbable case, recover gracefully // by ignoring this data entry. log.Error("Failed to decode blob limbo entry", "id", id, "err", err) return err @@ -182,7 +182,7 @@ func (l *limbo) pull(tx common.Hash) (*types.Transaction, error) { // update changes the block number under which a blob transaction is tracked. This // method should be used when a reorg changes a transaction's inclusion block. // -// The method may log errors for various unexpcted scenarios but will not return +// The method may log errors for various unexpected scenarios but will not return // any of it since there's no clear error case. Some errors may be due to coding // issues, others caused by signers mining MEV stuff or swapping transactions. In // all cases, the pool needs to continue operating. diff --git a/core/txpool/blobpool/metrics.go b/core/txpool/blobpool/metrics.go index 41fb0e50ed..a65a345b22 100644 --- a/core/txpool/blobpool/metrics.go +++ b/core/txpool/blobpool/metrics.go @@ -75,8 +75,8 @@ var ( pooltipGauge = metrics.NewRegisteredGauge("blobpool/pooltip", nil) // addwait/time, resetwait/time and getwait/time track the rough health of - // the pool and whether or not it's capable of keeping up with the load from - // the network. + // the pool and whether it's capable of keeping up with the load from the + // network. addwaitHist = metrics.NewRegisteredHistogram("blobpool/addwait", nil, metrics.NewExpDecaySample(1028, 0.015)) addtimeHist = metrics.NewRegisteredHistogram("blobpool/addtime", nil, metrics.NewExpDecaySample(1028, 0.015)) getwaitHist = metrics.NewRegisteredHistogram("blobpool/getwait", nil, metrics.NewExpDecaySample(1028, 0.015)) @@ -85,4 +85,31 @@ var ( pendtimeHist = metrics.NewRegisteredHistogram("blobpool/pendtime", nil, metrics.NewExpDecaySample(1028, 0.015)) resetwaitHist = metrics.NewRegisteredHistogram("blobpool/resetwait", nil, metrics.NewExpDecaySample(1028, 0.015)) resettimeHist = metrics.NewRegisteredHistogram("blobpool/resettime", nil, metrics.NewExpDecaySample(1028, 0.015)) + + // The below metrics track various cases where transactions are dropped out + // of the pool. Most are exceptional, some are chain progression and some + // threshold cappings. + dropInvalidMeter = metrics.NewRegisteredMeter("blobpool/drop/invalid", nil) // Invalid transaction, consensus change or bugfix, neutral-ish + dropDanglingMeter = metrics.NewRegisteredMeter("blobpool/drop/dangling", nil) // First nonce gapped, bad + dropFilledMeter = metrics.NewRegisteredMeter("blobpool/drop/filled", nil) // State full-overlap, chain progress, ok + dropOverlappedMeter = metrics.NewRegisteredMeter("blobpool/drop/overlapped", nil) // State partial-overlap, chain progress, ok + dropRepeatedMeter = metrics.NewRegisteredMeter("blobpool/drop/repeated", nil) // Repeated nonce, bad + dropGappedMeter = metrics.NewRegisteredMeter("blobpool/drop/gapped", nil) // Non-first nonce gapped, bad + dropOverdraftedMeter = metrics.NewRegisteredMeter("blobpool/drop/overdrafted", nil) // Balance exceeded, bad + dropOvercappedMeter = metrics.NewRegisteredMeter("blobpool/drop/overcapped", nil) // Per-account cap exceeded, bad + dropOverflownMeter = metrics.NewRegisteredMeter("blobpool/drop/overflown", nil) // Global disk cap exceeded, neutral-ish + dropUnderpricedMeter = metrics.NewRegisteredMeter("blobpool/drop/underpriced", nil) // Gas tip changed, neutral + dropReplacedMeter = metrics.NewRegisteredMeter("blobpool/drop/replaced", nil) // Transaction replaced, neutral + + // The below metrics track various outcomes of transactions being added to + // the pool. + addInvalidMeter = metrics.NewRegisteredMeter("blobpool/add/invalid", nil) // Invalid transaction, reject, neutral + addUnderpricedMeter = metrics.NewRegisteredMeter("blobpool/add/underpriced", nil) // Gas tip too low, neutral + addStaleMeter = metrics.NewRegisteredMeter("blobpool/add/stale", nil) // Nonce already filled, reject, bad-ish + addGappedMeter = metrics.NewRegisteredMeter("blobpool/add/gapped", nil) // Nonce gapped, reject, bad-ish + addOverdraftedMeter = metrics.NewRegisteredMeter("blobpool/add/overdrafted", nil) // Balance exceeded, reject, neutral + addOvercappedMeter = metrics.NewRegisteredMeter("blobpool/add/overcapped", nil) // Per-account cap exceeded, reject, neutral + addNoreplaceMeter = metrics.NewRegisteredMeter("blobpool/add/noreplace", nil) // Replacement fees or tips too low, neutral + addNonExclusiveMeter = metrics.NewRegisteredMeter("blobpool/add/nonexclusive", nil) // Plain transaction from same account exists, reject, neutral + addValidMeter = metrics.NewRegisteredMeter("blobpool/add/valid", nil) // Valid transaction, add, neutral ) diff --git a/core/txpool/blobpool/priority_test.go b/core/txpool/blobpool/priority_test.go index 3c9523d512..7ff5c86bca 100644 --- a/core/txpool/blobpool/priority_test.go +++ b/core/txpool/blobpool/priority_test.go @@ -74,7 +74,7 @@ func BenchmarkDynamicFeeJumpCalculation(b *testing.B) { // Benchmarks how many priority recalculations can be done. func BenchmarkPriorityCalculation(b *testing.B) { // The basefee and blob fee is constant for all transactions across a block, - // so we can assume theit absolute jump counts can be pre-computed. + // so we can assume their absolute jump counts can be pre-computed. basefee := uint256.NewInt(17_200_000_000) // 17.2 Gwei is the 22.03.2023 zero-emission basefee, random number blobfee := uint256.NewInt(123_456_789_000) // Completely random, no idea what this will be diff --git a/core/txpool/errors.go b/core/txpool/errors.go index d9aa1527a3..80a12e76ed 100644 --- a/core/txpool/errors.go +++ b/core/txpool/errors.go @@ -64,4 +64,10 @@ var ( // ErrFutureReplacePending is returned if a future transaction replaces a pending // one. Future transactions should only be able to replace other future transactions. ErrFutureReplacePending = errors.New("future transaction tries to replace pending") + + // ErrAlreadyReserved is returned if the sender address has a pending transaction + // in a different subpool. For example, this error is returned in response to any + // input transaction of non-blob type when a blob transaction from this sender + // remains pending (and vice-versa). + ErrAlreadyReserved = errors.New("address already reserved") ) diff --git a/core/txpool/legacypool/journal.go b/core/txpool/legacypool/journal.go index 2065fb36e0..3e5d43e123 100644 --- a/core/txpool/legacypool/journal.go +++ b/core/txpool/legacypool/journal.go @@ -174,7 +174,12 @@ func (journal *journal) rotate(all map[common.Address]types.Transactions) error return err } journal.writer = sink - log.Info("Regenerated local transaction journal", "transactions", journaled, "accounts", len(all)) + + logger := log.Info + if len(all) == 0 { + logger = log.Debug + } + logger("Regenerated local transaction journal", "transactions", journaled, "accounts", len(all)) return nil } diff --git a/core/txpool/legacypool/legacypool.go b/core/txpool/legacypool/legacypool.go index 669c483e9f..31e9fdd95e 100644 --- a/core/txpool/legacypool/legacypool.go +++ b/core/txpool/legacypool/legacypool.go @@ -50,6 +50,7 @@ import ( "github.com/ethereum/go-ethereum/common/prque" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/log" + "github.com/holiman/uint256" ) const ( @@ -223,7 +224,7 @@ type LegacyPool struct { config Config chainconfig *params.ChainConfig chain BlockChain - gasTip atomic.Pointer[big.Int] + gasTip atomic.Pointer[uint256.Int] minimumFee *big.Int txFeed event.Feed signer types.Signer @@ -317,15 +318,15 @@ func (pool *LegacyPool) Filter(tx *types.Transaction) bool { // head to allow balance / nonce checks. The transaction journal will be loaded // from disk and filtered based on the provided starting settings. The internal // goroutines will be spun up and the pool deemed operational afterwards. -func (pool *LegacyPool) Init(gasTip *big.Int, head *types.Header, reserve txpool.AddressReserver) error { +func (pool *LegacyPool) Init(gasTip uint64, head *types.Header, reserve txpool.AddressReserver) error { // Set the address reserver to request exclusive access to pooled accounts pool.reserve = reserve // Set the basic pool parameters - pool.gasTip.Store(gasTip) + pool.gasTip.Store(uint256.NewInt(gasTip)) // Initialize the state with head block, or fallback to empty one in - // case the head state is not available(might occur when node is not + // case the head state is not available (might occur when node is not // fully synced). statedb, err := pool.chain.StateAt(head.Root) if err != nil { @@ -468,11 +469,13 @@ func (pool *LegacyPool) SetGasTip(tip *big.Int) { pool.mu.Lock() defer pool.mu.Unlock() - old := pool.gasTip.Load() - pool.gasTip.Store(new(big.Int).Set(tip)) - + var ( + newTip = uint256.MustFromBig(tip) + old = pool.gasTip.Load() + ) + pool.gasTip.Store(newTip) // If the min miner fee increased, remove transactions below the new threshold - if tip.Cmp(old) > 0 { + if newTip.Cmp(old) > 0 { // pool.priced is sorted by GasFeeCap, so we have to iterate through pool.all instead drop := pool.all.RemotesBelowTip(tip) for _, tx := range drop { @@ -480,7 +483,7 @@ func (pool *LegacyPool) SetGasTip(tip *big.Int) { } pool.priced.Removed(len(drop)) } - log.Info("Legacy pool tip threshold updated", "tip", tip) + log.Info("Legacy pool tip threshold updated", "tip", newTip) } func (pool *LegacyPool) SetMinFee(minFee *big.Int) { @@ -557,23 +560,33 @@ func (pool *LegacyPool) ContentFrom(addr common.Address) ([]*types.Transaction, } // Pending retrieves all currently processable transactions, grouped by origin -// account and sorted by nonce. The returned transaction set is a copy and can be -// freely modified by calling code. +// account and sorted by nonce. // -// The enforceTips parameter can be used to do an extra filtering on the pending -// transactions and only return those whose **effective** tip is large enough in -// the next pending execution environment. -func (pool *LegacyPool) Pending(enforceTips bool) map[common.Address][]*txpool.LazyTransaction { - return pool.PendingWithBaseFee(enforceTips, nil) -} - -// If baseFee is nil, then pool.priced.urgent.baseFee is used. -func (pool *LegacyPool) PendingWithBaseFee(enforceTips bool, baseFee *big.Int) map[common.Address][]*txpool.LazyTransaction { +// The transactions can also be pre-filtered by the dynamic fee components to +// reduce allocations and load on downstream subsystems. +func (pool *LegacyPool) Pending(filter txpool.PendingFilter) map[common.Address][]*txpool.LazyTransaction { + // If only blob transactions are requested, this pool is unsuitable as it + // contains none, don't even bother. + if filter.OnlyBlobTxs { + return nil + } pool.mu.Lock() defer pool.mu.Unlock() - if baseFee == nil { - baseFee = pool.priced.urgent.baseFee + // Convert the new uint256.Int types to the old big.Int ones used by the legacy pool + var ( + minTipBig *big.Int + baseFeeBig *big.Int + ) + if filter.MinTip != nil { + minTipBig = filter.MinTip.ToBig() + } + if filter.BaseFee != nil { + baseFeeBig = filter.BaseFee.ToBig() + } + + if baseFeeBig == nil { + baseFeeBig = pool.priced.urgent.baseFee } pending := make(map[common.Address][]*txpool.LazyTransaction, len(pool.pending)) @@ -581,9 +594,9 @@ func (pool *LegacyPool) PendingWithBaseFee(enforceTips bool, baseFee *big.Int) m txs := list.Flatten() // If the miner requests tip enforcement, cap the lists now - if enforceTips && !pool.locals.contains(addr) { + if minTipBig != nil && !pool.locals.contains(addr) { for i, tx := range txs { - if tx.EffectiveGasTipIntCmp(pool.gasTip.Load(), baseFee) < 0 { + if tx.EffectiveGasTipIntCmp(minTipBig, baseFeeBig) < 0 { txs = txs[:i] break } @@ -597,8 +610,8 @@ func (pool *LegacyPool) PendingWithBaseFee(enforceTips bool, baseFee *big.Int) m Hash: txs[i].Hash(), Tx: txs[i].WithoutBlobTxSidecar(), Time: txs[i].Time(), - GasFeeCap: txs[i].GasFeeCap(), - GasTipCap: txs[i].GasTipCap(), + GasFeeCap: uint256.MustFromBig(txs[i].GasFeeCap()), + GasTipCap: uint256.MustFromBig(txs[i].GasTipCap()), Gas: txs[i].Gas(), BlobGas: txs[i].BlobGas(), } @@ -661,7 +674,7 @@ func (pool *LegacyPool) validateTxBasics(tx *types.Transaction, local bool) erro 1< threshold; size-- { drops = append(drops, m.items[(*m.index)[size-1]]) delete(m.items, (*m.index)[size-1]) } *m.index = (*m.index)[:threshold] - heap.Init(m.index) + // The sorted m.index slice is still a valid heap, so there is no need to + // reheap after deleting tail items. // If we had a cache, shift the back m.cacheMu.Lock() @@ -281,19 +283,19 @@ type list struct { strict bool // Whether nonces are strictly continuous or not txs *sortedMap // Heap indexed sorted hash map of the transactions - costcap *big.Int // Price of the highest costing transaction (reset only if exceeds balance) - gascap uint64 // Gas limit of the highest spending transaction (reset only if exceeds block limit) - totalcost *big.Int // Total cost of all transactions in the list + costcap *uint256.Int // Price of the highest costing transaction (reset only if exceeds balance) + gascap uint64 // Gas limit of the highest spending transaction (reset only if exceeds block limit) + totalcost *uint256.Int // Total cost of all transactions in the list } -// newList create a new transaction list for maintaining nonce-indexable fast, +// newList creates a new transaction list for maintaining nonce-indexable fast, // gapped, sortable transaction lists. func newList(strict bool) *list { return &list{ strict: strict, txs: newSortedMap(), - costcap: new(big.Int), - totalcost: new(big.Int), + costcap: new(uint256.Int), + totalcost: new(uint256.Int), } } @@ -335,10 +337,15 @@ func (l *list) Add(tx *types.Transaction, priceBump uint64) (bool, *types.Transa l.subTotalCost([]*types.Transaction{old}) } // Add new tx cost to totalcost - l.totalcost.Add(l.totalcost, tx.Cost()) + cost, overflow := uint256.FromBig(tx.Cost()) + if overflow { + return false, nil + } + l.totalcost.Add(l.totalcost, cost) + // Otherwise overwrite the old transaction with the current one l.txs.Put(tx) - if cost := tx.Cost(); l.costcap.Cmp(cost) < 0 { + if l.costcap.Cmp(cost) < 0 { l.costcap = cost } if gas := tx.Gas(); l.gascap < gas { @@ -365,17 +372,17 @@ func (l *list) Forward(threshold uint64) types.Transactions { // a point in calculating all the costs or if the balance covers all. If the threshold // is lower than the costgas cap, the caps will be reset to a new high after removing // the newly invalidated transactions. -func (l *list) Filter(costLimit *big.Int, gasLimit uint64) (types.Transactions, types.Transactions) { +func (l *list) Filter(costLimit *uint256.Int, gasLimit uint64) (types.Transactions, types.Transactions) { // If all transactions are below the threshold, short circuit if l.costcap.Cmp(costLimit) <= 0 && l.gascap <= gasLimit { return nil, nil } - l.costcap = new(big.Int).Set(costLimit) // Lower the caps to the thresholds + l.costcap = new(uint256.Int).Set(costLimit) // Lower the caps to the thresholds l.gascap = gasLimit // Filter out all the transactions above the account's funds removed := l.txs.Filter(func(tx *types.Transaction) bool { - return tx.Gas() > gasLimit || tx.Cost().Cmp(costLimit) > 0 + return tx.Gas() > gasLimit || tx.Cost().Cmp(costLimit.ToBig()) > 0 }) if len(removed) == 0 { @@ -466,7 +473,10 @@ func (l *list) LastElement() *types.Transaction { // total cost of all transactions. func (l *list) subTotalCost(txs []*types.Transaction) { for _, tx := range txs { - l.totalcost.Sub(l.totalcost, tx.Cost()) + _, underflow := l.totalcost.SubOverflow(l.totalcost, uint256.MustFromBig(tx.Cost())) + if underflow { + panic("totalcost underflow") + } } } diff --git a/core/txpool/legacypool/list_test.go b/core/txpool/legacypool/list_test.go index d7ca91844e..28670fe953 100644 --- a/core/txpool/legacypool/list_test.go +++ b/core/txpool/legacypool/list_test.go @@ -32,7 +32,9 @@ import ( "testing" "github.com/ava-labs/subnet-evm/core/types" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" + "github.com/holiman/uint256" ) // Tests that transactions can be added to strict lists and list contents and @@ -61,6 +63,21 @@ func TestStrictListAdd(t *testing.T) { } } +// TestListAddVeryExpensive tests adding txs which exceed 256 bits in cost. It is +// expected that the list does not panic. +func TestListAddVeryExpensive(t *testing.T) { + key, _ := crypto.GenerateKey() + list := newList(true) + for i := 0; i < 3; i++ { + value := big.NewInt(100) + gasprice, _ := new(big.Int).SetString("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 0) + gaslimit := uint64(i) + tx, _ := types.SignTx(types.NewTransaction(uint64(i), common.Address{}, value, gaslimit, gasprice, nil), types.HomesteadSigner{}, key) + t.Logf("cost: %x bitlen: %d\n", tx.Cost(), tx.Cost().BitLen()) + list.Add(tx, DefaultConfig.PriceBump) + } +} + func BenchmarkListAdd(b *testing.B) { // Generate a list of transactions to insert key, _ := crypto.GenerateKey() @@ -70,7 +87,7 @@ func BenchmarkListAdd(b *testing.B) { txs[i] = transaction(uint64(i), 0, key) } // Insert the transactions in a random order - priceLimit := big.NewInt(int64(DefaultConfig.PriceLimit)) + priceLimit := uint256.NewInt(DefaultConfig.PriceLimit) b.ResetTimer() for i := 0; i < b.N; i++ { list := newList(true) @@ -80,3 +97,25 @@ func BenchmarkListAdd(b *testing.B) { } } } + +func BenchmarkListCapOneTx(b *testing.B) { + // Generate a list of transactions to insert + key, _ := crypto.GenerateKey() + + txs := make(types.Transactions, 32) + for i := 0; i < len(txs); i++ { + txs[i] = transaction(uint64(i), 0, key) + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + list := newList(true) + // Insert the transactions in a random order + for _, v := range rand.Perm(len(txs)) { + list.Add(txs[v], DefaultConfig.PriceBump) + } + b.StartTimer() + list.Cap(list.Len() - 1) + b.StopTimer() + } +} diff --git a/core/txpool/subpool.go b/core/txpool/subpool.go index 0a597c779d..5459154090 100644 --- a/core/txpool/subpool.go +++ b/core/txpool/subpool.go @@ -34,6 +34,7 @@ import ( "github.com/ava-labs/subnet-evm/core/types" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/event" + "github.com/holiman/uint256" ) // LazyTransaction contains a small subset of the transaction properties that is @@ -44,9 +45,9 @@ type LazyTransaction struct { Hash common.Hash // Transaction hash to pull up if needed Tx *types.Transaction // Transaction if already resolved - Time time.Time // Time when the transaction was first seen - GasFeeCap *big.Int // Maximum fee per gas the transaction may consume - GasTipCap *big.Int // Maximum miner tip per gas the transaction can pay + Time time.Time // Time when the transaction was first seen + GasFeeCap *uint256.Int // Maximum fee per gas the transaction may consume + GasTipCap *uint256.Int // Maximum miner tip per gas the transaction can pay Gas uint64 // Amount of gas required by the transaction BlobGas uint64 // Amount of blob gas required by the transaction @@ -54,11 +55,17 @@ type LazyTransaction struct { // Resolve retrieves the full transaction belonging to a lazy handle if it is still // maintained by the transaction pool. +// +// Note, the method will *not* cache the retrieved transaction if the original +// pool has not cached it. The idea being, that if the tx was too big to insert +// originally, silently saving it will cause more trouble down the line (and +// indeed seems to have caused a memory bloat in the original implementation +// which did just that). func (ltx *LazyTransaction) Resolve() *types.Transaction { - if ltx.Tx == nil { - ltx.Tx = ltx.Pool.Get(ltx.Hash) + if ltx.Tx != nil { + return ltx.Tx } - return ltx.Tx + return ltx.Pool.Get(ltx.Hash) } // LazyResolver is a minimal interface needed for a transaction pool to satisfy @@ -73,13 +80,28 @@ type LazyResolver interface { // may request (and relinquish) exclusive access to certain addresses. type AddressReserver func(addr common.Address, reserve bool) error +// PendingFilter is a collection of filter rules to allow retrieving a subset +// of transactions for announcement or mining. +// +// Note, the entries here are not arbitrary useful filters, rather each one has +// a very specific call site in mind and each one can be evaluated very cheaply +// by the pool implementations. Only add new ones that satisfy those constraints. +type PendingFilter struct { + MinTip *uint256.Int // Minimum miner tip required to include a transaction + BaseFee *uint256.Int // Minimum 1559 basefee needed to include a transaction + BlobFee *uint256.Int // Minimum 4844 blobfee needed to include a blob transaction + + OnlyPlainTxs bool // Return only plain EVM transactions (peer-join announces, block space filling) + OnlyBlobTxs bool // Return only blob transactions (block blob-space filling) +} + // SubPool represents a specialized transaction pool that lives on its own (e.g. // blob pool). Since independent of how many specialized pools we have, they do // need to be updated in lockstep and assemble into one coherent view for block // production, this interface defines the common methods that allow the primary // transaction pool to manage the subpools. type SubPool interface { - // Filter is a selector used to decide whether a transaction whould be added + // Filter is a selector used to decide whether a transaction would be added // to this particular subpool. Filter(tx *types.Transaction) bool @@ -90,7 +112,7 @@ type SubPool interface { // These should not be passed as a constructor argument - nor should the pools // start by themselves - in order to keep multiple subpools in lockstep with // one another. - Init(gasTip *big.Int, head *types.Header, reserve AddressReserver) error + Init(gasTip uint64, head *types.Header, reserve AddressReserver) error // Close terminates any background processing threads and releases any held // resources. @@ -120,8 +142,10 @@ type SubPool interface { // Pending retrieves all currently processable transactions, grouped by origin // account and sorted by nonce. - Pending(enforceTips bool) map[common.Address][]*LazyTransaction - PendingWithBaseFee(enforceTips bool, baseFee *big.Int) map[common.Address][]*LazyTransaction + // + // The transactions can also be pre-filtered by the dynamic fee components to + // reduce allocations and load on downstream subsystems. + Pending(filter PendingFilter) map[common.Address][]*LazyTransaction IteratePending(f func(tx *types.Transaction) bool) bool // Returns false if iteration was interrupted. // SubscribeTransactions subscribes to new transaction events. The subscriber diff --git a/core/txpool/txpool.go b/core/txpool/txpool.go index 7539001ac0..e65babe5a3 100644 --- a/core/txpool/txpool.go +++ b/core/txpool/txpool.go @@ -88,6 +88,9 @@ type TxPool struct { subs event.SubscriptionScope // Subscription scope to unsubscribe all on shutdown quit chan chan error // Quit channel to tear down the head updater + term chan struct{} // Termination channel to detect a closed pool + + sync chan chan error // Testing / simulator channel to block until internal reset is done gasTip atomic.Pointer[big.Int] // Remember last value set so it can be retrieved reorgFeed event.Feed @@ -95,7 +98,7 @@ type TxPool struct { // New creates a new transaction pool to gather, sort and filter inbound // transactions from the network. -func New(gasTip *big.Int, chain BlockChain, subpools []SubPool) (*TxPool, error) { +func New(gasTip uint64, chain BlockChain, subpools []SubPool) (*TxPool, error) { // Retrieve the current head so that all subpools and this main coordinator // pool will have the same starting state, even if the chain moves forward // during initialization. @@ -105,7 +108,11 @@ func New(gasTip *big.Int, chain BlockChain, subpools []SubPool) (*TxPool, error) subpools: subpools, reservations: make(map[common.Address]SubPool), quit: make(chan chan error), + term: make(chan struct{}), + sync: make(chan chan error), } + pool.gasTip.Store(new(big.Int).SetUint64(gasTip)) + for i, subpool := range subpools { if err := subpool.Init(gasTip, head, pool.reserver(i, subpool)); err != nil { for j := i - 1; j >= 0; j-- { @@ -114,7 +121,6 @@ func New(gasTip *big.Int, chain BlockChain, subpools []SubPool) (*TxPool, error) return nil, err } } - pool.gasTip.Store(gasTip) // Subscribe to chain head events to trigger subpool resets var ( @@ -146,7 +152,7 @@ func (p *TxPool) reserver(id int, subpool SubPool) AddressReserver { log.Error("pool attempted to reserve already-owned address", "address", addr) return nil // Ignore fault to give the pool a chance to recover while the bug gets fixed } - return errors.New("address already reserved") + return ErrAlreadyReserved } p.reservations[addr] = subpool if metrics.Enabled { @@ -205,6 +211,9 @@ func (p *TxPool) Close() error { // outside blockchain events as well as for various reporting and transaction // eviction events. func (p *TxPool) loop(head *types.Header, newHeadCh <-chan core.ChainHeadEvent) { + // Close the termination marker when the pool stops + defer close(p.term) + // Track the previous and current head to feed to an idle reset var ( oldHead = head @@ -214,13 +223,23 @@ func (p *TxPool) loop(head *types.Header, newHeadCh <-chan core.ChainHeadEvent) var ( resetBusy = make(chan struct{}, 1) // Allow 1 reset to run concurrently resetDone = make(chan *types.Header) + + resetForced bool // Whether a forced reset was requested, only used in simulator mode + resetWaiter chan error // Channel waiting on a forced reset, only used in simulator mode ) + // Notify the live reset waiter to not block if the txpool is closed. + defer func() { + if resetWaiter != nil { + resetWaiter <- errors.New("pool already terminated") + resetWaiter = nil + } + }() var errc chan error for errc == nil { // Something interesting might have happened, run a reset if there is // one needed but none is running. The resetter will run on its own // goroutine to allow chain head events to be consumed contiguously. - if newHead != oldHead { + if newHead != oldHead || resetForced { // Try to inject a busy marker and start a reset if successful select { case resetBusy <- struct{}{}: @@ -233,8 +252,17 @@ func (p *TxPool) loop(head *types.Header, newHeadCh <-chan core.ChainHeadEvent) resetDone <- newHead }(oldHead, newHead) + // If the reset operation was explicitly requested, consider it + // being fulfilled and drop the request marker. If it was not, + // this is a noop. + resetForced = false + default: - // Reset already running, wait until it finishes + // Reset already running, wait until it finishes. + // + // Note, this will not drop any forced reset request. If a forced + // reset was requested, but we were busy, then when the currently + // running reset finishes, a new one will be spun up. } } // Wait for the next chain head event or a previous reset finish @@ -248,8 +276,26 @@ func (p *TxPool) loop(head *types.Header, newHeadCh <-chan core.ChainHeadEvent) oldHead = head <-resetBusy + // If someone is waiting for a reset to finish, notify them, unless + // the forced op is still pending. In that case, wait another round + // of resets. + if resetWaiter != nil && !resetForced { + resetWaiter <- nil + resetWaiter = nil + } + case errc = <-p.quit: // Termination requested, break out on the next loop round + + case syncc := <-p.sync: + // Transaction pool is running inside a simulator, and we are about + // to create a new block. Request a forced sync operation to ensure + // that any running reset operation finishes to make block imports + // deterministic. On top of that, run a new reset operation to make + // transaction insertions deterministic instead of being stuck in a + // queue waiting for a reset. + resetForced = true + resetWaiter = syncc } } // Notify the closer of termination (no error possible for now) @@ -365,19 +411,12 @@ func (p *TxPool) AddRemotesSync(txs []*types.Transaction) []error { // account and sorted by nonce. The returned transaction set is a copy and can be // freely modified by calling code. // -// The enforceTips parameter can be used to do an extra filtering on the pending -// transactions and only return those whose **effective** tip is large enough in -// the next pending execution environment. -// account and sorted by nonce. -func (p *TxPool) Pending(enforceTips bool) map[common.Address][]*LazyTransaction { - return p.PendingWithBaseFee(enforceTips, nil) -} - -// If baseFee is nil, then pool.priced.urgent.baseFee is used. -func (p *TxPool) PendingWithBaseFee(enforceTips bool, baseFee *big.Int) map[common.Address][]*LazyTransaction { +// The transactions can also be pre-filtered by the dynamic fee components to +// reduce allocations and load on downstream subsystems. +func (p *TxPool) Pending(filter PendingFilter) map[common.Address][]*LazyTransaction { txs := make(map[common.Address][]*LazyTransaction) for _, subpool := range p.subpools { - for addr, set := range subpool.PendingWithBaseFee(enforceTips, baseFee) { + for addr, set := range subpool.Pending(filter) { txs[addr] = set } } @@ -386,13 +425,12 @@ func (p *TxPool) PendingWithBaseFee(enforceTips bool, baseFee *big.Int) map[comm // PendingSize returns the number of pending txs in the tx pool. // -// The enforceTips parameter can be used to do an extra filtering on the pending -// transactions and only return those whose **effective** tip is large enough in -// the next pending execution environment. -func (p *TxPool) PendingSize(enforceTips bool) int { +// The filter parameter can be used to do an extra filtering on the pending +// transactions. +func (p *TxPool) PendingSize(filter PendingFilter) int { count := 0 for _, subpool := range p.subpools { - for _, txs := range subpool.Pending(enforceTips) { + for _, txs := range subpool.Pending(filter) { count += len(txs) } } @@ -412,9 +450,13 @@ func (p *TxPool) IteratePending(f func(tx *types.Transaction) bool) { // SubscribeTransactions registers a subscription for new transaction events, // supporting feeding only newly seen or also resurrected transactions. func (p *TxPool) SubscribeTransactions(ch chan<- core.NewTxsEvent, reorgs bool) event.Subscription { - subs := make([]event.Subscription, len(p.subpools)) - for i, subpool := range p.subpools { - subs[i] = subpool.SubscribeTransactions(ch, reorgs) + subs := make([]event.Subscription, 0, len(p.subpools)) + for _, subpool := range p.subpools { + subpool := subpool.SubscribeTransactions(ch, reorgs) + if subpool == nil { + continue + } + subs = append(subs, subpool) } return p.subs.Track(event.JoinSubscriptions(subs...)) } @@ -512,3 +554,20 @@ func (p *TxPool) Status(hash common.Hash) TxStatus { } return TxStatusUnknown } + +// Sync is a helper method for unit tests or simulator runs where the chain events +// are arriving in quick succession, without any time in between them to run the +// internal background reset operations. This method will run an explicit reset +// operation to ensure the pool stabilises, thus avoiding flakey behavior. +// +// Note, do not use this in production / live code. In live code, the pool is +// meant to reset on a separate thread to avoid DoS vectors. +func (p *TxPool) Sync() error { + sync := make(chan error) + select { + case p.sync <- sync: + return <-sync + case <-p.term: + return errors.New("pool already terminated") + } +} diff --git a/core/txpool/validation.go b/core/txpool/validation.go index 6e80ac9a91..0e07f87fc3 100644 --- a/core/txpool/validation.go +++ b/core/txpool/validation.go @@ -42,6 +42,12 @@ import ( "github.com/ethereum/go-ethereum/log" ) +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) +) + // ValidationOptions define certain differences between transaction validation // across the different pools without having to duplicate those checks. type ValidationOptions struct { @@ -119,15 +125,17 @@ func ValidateTransaction(tx *types.Transaction, head *types.Header, signer types return err } if txGas := tx.Gas(); txGas < intrGas { - return fmt.Errorf("%w: address %v tx gas (%v) < intrinsic gas (%v)", core.ErrIntrinsicGas, from.Hex(), tx.Gas(), intrGas) + return fmt.Errorf("%w: address %v tx gas (%v), minimum needed %v", core.ErrIntrinsicGas, from.Hex(), txGas, intrGas) } - // Ensure the gasprice is high enough to cover the requirement of the calling - // pool and/or block producer + // Ensure the gasprice is high enough to cover the requirement of the calling pool if tx.GasTipCapIntCmp(opts.MinTip) < 0 { - return fmt.Errorf("%w: tip needed %v, tip permitted %v", ErrUnderpriced, opts.MinTip, tx.GasTipCap()) + return fmt.Errorf("%w: gas tip cap %v, minimum needed %v", ErrUnderpriced, tx.GasTipCap(), opts.MinTip) } - // Ensure blob transactions have valid commitments if tx.Type() == types.BlobTxType { + // Ensure the blob fee cap satisfies the minimum blob gas price + if tx.BlobGasFeeCapIntCmp(blobTxMinBlobGasPrice) < 0 { + return fmt.Errorf("%w: blob fee cap %v, minimum needed %v", ErrUnderpriced, tx.BlobGasFeeCap(), blobTxMinBlobGasPrice) + } sidecar := tx.BlobTxSidecar() if sidecar == nil { return fmt.Errorf("missing sidecar in blob transaction") @@ -141,6 +149,7 @@ func ValidateTransaction(tx *types.Transaction, head *types.Header, signer types 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) } + // Ensure commitments, proofs and hashes are valid if err := validateBlobSidecar(hashes, sidecar); err != nil { return err } @@ -161,17 +170,10 @@ func validateBlobSidecar(hashes []common.Hash, sidecar *types.BlobTxSidecar) err // Blob quantities match up, validate that the provers match with the // transaction hash before getting to the cryptography hasher := sha256.New() - for i, want := range hashes { - hasher.Write(sidecar.Commitments[i][:]) - hash := hasher.Sum(nil) - hasher.Reset() - - var vhash common.Hash - vhash[0] = params.BlobTxHashVersion - copy(vhash[1:], hash[1:]) - - if vhash != want { - return fmt.Errorf("blob %d: computed hash %#x mismatches transaction one %#x", i, vhash, want) + for i, vhash := range hashes { + computed := kzg4844.CalcBlobHashV1(hasher, &sidecar.Commitments[i]) + if vhash != computed { + return fmt.Errorf("blob %d: computed hash %#x mismatches transaction one %#x", i, computed, vhash) } } // Blob commitments match with the hashes in the transaction, verify the @@ -243,7 +245,7 @@ func ValidateTransactionWithState(tx *types.Transaction, signer types.Signer, op } // Ensure the transactor has enough funds to cover the transaction costs var ( - balance = opts.State.GetBalance(from) + balance = opts.State.GetBalance(from).ToBig() cost = tx.Cost() ) if balance.Cmp(cost) < 0 { diff --git a/core/types/account.go b/core/types/account.go new file mode 100644 index 0000000000..bb0f4ca02e --- /dev/null +++ b/core/types/account.go @@ -0,0 +1,87 @@ +// Copyright 2024 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 types + +import ( + "bytes" + "encoding/hex" + "encoding/json" + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/common/math" +) + +//go:generate go run github.com/fjl/gencodec -type Account -field-override accountMarshaling -out gen_account.go + +// Account represents an Ethereum account and its attached data. +// This type is used to specify accounts in the genesis block state, and +// is also useful for JSON encoding/decoding of accounts. +type Account struct { + Code []byte `json:"code,omitempty"` + Storage map[common.Hash]common.Hash `json:"storage,omitempty"` + Balance *big.Int `json:"balance" gencodec:"required"` + Nonce uint64 `json:"nonce,omitempty"` + + // used in tests + PrivateKey []byte `json:"secretKey,omitempty"` +} + +type accountMarshaling struct { + Code hexutil.Bytes + Balance *math.HexOrDecimal256 + Nonce math.HexOrDecimal64 + Storage map[storageJSON]storageJSON + PrivateKey hexutil.Bytes +} + +// storageJSON represents a 256 bit byte array, but allows less than 256 bits when +// unmarshaling from hex. +type storageJSON common.Hash + +func (h *storageJSON) UnmarshalText(text []byte) error { + text = bytes.TrimPrefix(text, []byte("0x")) + if len(text) > 64 { + return fmt.Errorf("too many hex characters in storage key/value %q", text) + } + offset := len(h) - len(text)/2 // pad on the left + if _, err := hex.Decode(h[offset:], text); err != nil { + return fmt.Errorf("invalid hex storage key/value %q", text) + } + return nil +} + +func (h storageJSON) MarshalText() ([]byte, error) { + return hexutil.Bytes(h[:]).MarshalText() +} + +// GenesisAlloc specifies the initial state of a genesis block. +type GenesisAlloc map[common.Address]Account + +func (ga *GenesisAlloc) UnmarshalJSON(data []byte) error { + m := make(map[common.UnprefixedAddress]Account) + if err := json.Unmarshal(data, &m); err != nil { + return err + } + *ga = make(GenesisAlloc) + for addr, a := range m { + (*ga)[common.Address(addr)] = a + } + return nil +} diff --git a/core/gen_genesis_account.go b/core/types/gen_account.go similarity index 61% rename from core/gen_genesis_account.go rename to core/types/gen_account.go index a9d47e6ba3..4e475896a7 100644 --- a/core/gen_genesis_account.go +++ b/core/types/gen_account.go @@ -1,6 +1,6 @@ // Code generated by github.com/fjl/gencodec. DO NOT EDIT. -package core +package types import ( "encoding/json" @@ -12,62 +12,62 @@ import ( "github.com/ethereum/go-ethereum/common/math" ) -var _ = (*genesisAccountMarshaling)(nil) +var _ = (*accountMarshaling)(nil) // MarshalJSON marshals as JSON. -func (g GenesisAccount) MarshalJSON() ([]byte, error) { - type GenesisAccount struct { +func (a Account) MarshalJSON() ([]byte, error) { + type Account struct { Code hexutil.Bytes `json:"code,omitempty"` Storage map[storageJSON]storageJSON `json:"storage,omitempty"` Balance *math.HexOrDecimal256 `json:"balance" gencodec:"required"` Nonce math.HexOrDecimal64 `json:"nonce,omitempty"` PrivateKey hexutil.Bytes `json:"secretKey,omitempty"` } - var enc GenesisAccount - enc.Code = g.Code - if g.Storage != nil { - enc.Storage = make(map[storageJSON]storageJSON, len(g.Storage)) - for k, v := range g.Storage { + var enc Account + enc.Code = a.Code + if a.Storage != nil { + enc.Storage = make(map[storageJSON]storageJSON, len(a.Storage)) + for k, v := range a.Storage { enc.Storage[storageJSON(k)] = storageJSON(v) } } - enc.Balance = (*math.HexOrDecimal256)(g.Balance) - enc.Nonce = math.HexOrDecimal64(g.Nonce) - enc.PrivateKey = g.PrivateKey + enc.Balance = (*math.HexOrDecimal256)(a.Balance) + enc.Nonce = math.HexOrDecimal64(a.Nonce) + enc.PrivateKey = a.PrivateKey return json.Marshal(&enc) } // UnmarshalJSON unmarshals from JSON. -func (g *GenesisAccount) UnmarshalJSON(input []byte) error { - type GenesisAccount struct { +func (a *Account) UnmarshalJSON(input []byte) error { + type Account struct { Code *hexutil.Bytes `json:"code,omitempty"` Storage map[storageJSON]storageJSON `json:"storage,omitempty"` Balance *math.HexOrDecimal256 `json:"balance" gencodec:"required"` Nonce *math.HexOrDecimal64 `json:"nonce,omitempty"` PrivateKey *hexutil.Bytes `json:"secretKey,omitempty"` } - var dec GenesisAccount + var dec Account if err := json.Unmarshal(input, &dec); err != nil { return err } if dec.Code != nil { - g.Code = *dec.Code + a.Code = *dec.Code } if dec.Storage != nil { - g.Storage = make(map[common.Hash]common.Hash, len(dec.Storage)) + a.Storage = make(map[common.Hash]common.Hash, len(dec.Storage)) for k, v := range dec.Storage { - g.Storage[common.Hash(k)] = common.Hash(v) + a.Storage[common.Hash(k)] = common.Hash(v) } } if dec.Balance == nil { - return errors.New("missing required field 'balance' for GenesisAccount") + return errors.New("missing required field 'balance' for Account") } - g.Balance = (*big.Int)(dec.Balance) + a.Balance = (*big.Int)(dec.Balance) if dec.Nonce != nil { - g.Nonce = uint64(*dec.Nonce) + a.Nonce = uint64(*dec.Nonce) } if dec.PrivateKey != nil { - g.PrivateKey = *dec.PrivateKey + a.PrivateKey = *dec.PrivateKey } return nil } diff --git a/core/types/gen_account_rlp.go b/core/types/gen_account_rlp.go index 951632fb58..3d2f67ab0f 100644 --- a/core/types/gen_account_rlp.go +++ b/core/types/gen_account_rlp.go @@ -15,10 +15,7 @@ func (obj *StateAccount) EncodeRLP(_w io.Writer) error { if obj.Balance == nil { w.Write(rlp.EmptyString) } else { - if obj.Balance.Sign() == -1 { - return rlp.ErrNegativeBigInt - } - w.WriteBigInt(obj.Balance) + w.WriteUint256(obj.Balance) } w.WriteBytes(obj.Root[:]) w.WriteBytes(obj.CodeHash) diff --git a/core/types/hashing_test.go b/core/types/hashing_test.go index 230ac97ebf..af2f72a8d5 100644 --- a/core/types/hashing_test.go +++ b/core/types/hashing_test.go @@ -37,6 +37,7 @@ import ( "github.com/ava-labs/subnet-evm/core/rawdb" "github.com/ava-labs/subnet-evm/core/types" "github.com/ava-labs/subnet-evm/trie" + "github.com/ava-labs/subnet-evm/triedb" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/crypto" @@ -49,7 +50,7 @@ func TestDeriveSha(t *testing.T) { t.Fatal(err) } for len(txs) < 1000 { - exp := types.DeriveSha(txs, trie.NewEmpty(trie.NewDatabase(rawdb.NewMemoryDatabase(), nil))) + exp := types.DeriveSha(txs, trie.NewEmpty(triedb.NewDatabase(rawdb.NewMemoryDatabase(), nil))) got := types.DeriveSha(txs, trie.NewStackTrie(nil)) if !bytes.Equal(got[:], exp[:]) { t.Fatalf("%d txs: got %x exp %x", len(txs), got, exp) @@ -96,7 +97,7 @@ func BenchmarkDeriveSha200(b *testing.B) { b.ResetTimer() b.ReportAllocs() for i := 0; i < b.N; i++ { - exp = types.DeriveSha(txs, trie.NewEmpty(trie.NewDatabase(rawdb.NewMemoryDatabase(), nil))) + exp = types.DeriveSha(txs, trie.NewEmpty(triedb.NewDatabase(rawdb.NewMemoryDatabase(), nil))) } }) @@ -117,7 +118,7 @@ func TestFuzzDeriveSha(t *testing.T) { rndSeed := mrand.Int() for i := 0; i < 10; i++ { seed := rndSeed + i - exp := types.DeriveSha(newDummy(i), trie.NewEmpty(trie.NewDatabase(rawdb.NewMemoryDatabase(), nil))) + exp := types.DeriveSha(newDummy(i), trie.NewEmpty(triedb.NewDatabase(rawdb.NewMemoryDatabase(), nil))) got := types.DeriveSha(newDummy(i), trie.NewStackTrie(nil)) if !bytes.Equal(got[:], exp[:]) { printList(newDummy(seed)) @@ -145,7 +146,7 @@ func TestDerivableList(t *testing.T) { }, } for i, tc := range tcs[1:] { - exp := types.DeriveSha(flatList(tc), trie.NewEmpty(trie.NewDatabase(rawdb.NewMemoryDatabase(), nil))) + exp := types.DeriveSha(flatList(tc), trie.NewEmpty(triedb.NewDatabase(rawdb.NewMemoryDatabase(), nil))) got := types.DeriveSha(flatList(tc), trie.NewStackTrie(nil)) if !bytes.Equal(got[:], exp[:]) { t.Fatalf("case %d: got %x exp %x", i, got, exp) diff --git a/core/types/state_account.go b/core/types/state_account.go index 16dc9c2795..96c270a944 100644 --- a/core/types/state_account.go +++ b/core/types/state_account.go @@ -28,10 +28,10 @@ package types import ( "bytes" - "math/big" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/rlp" + "github.com/holiman/uint256" ) //go:generate go run github.com/ethereum/go-ethereum/rlp/rlpgen -type StateAccount -out gen_account_rlp.go @@ -40,7 +40,7 @@ import ( // These objects are stored in the main account trie. type StateAccount struct { Nonce uint64 - Balance *big.Int + Balance *uint256.Int Root common.Hash // merkle root of the storage trie CodeHash []byte } @@ -48,7 +48,7 @@ type StateAccount struct { // NewEmptyStateAccount constructs an empty state account. func NewEmptyStateAccount() *StateAccount { return &StateAccount{ - Balance: new(big.Int), + Balance: new(uint256.Int), Root: EmptyRootHash, CodeHash: EmptyCodeHash.Bytes(), } @@ -56,9 +56,9 @@ func NewEmptyStateAccount() *StateAccount { // Copy returns a deep-copied state account object. func (acct *StateAccount) Copy() *StateAccount { - var balance *big.Int + var balance *uint256.Int if acct.Balance != nil { - balance = new(big.Int).Set(acct.Balance) + balance = new(uint256.Int).Set(acct.Balance) } return &StateAccount{ Nonce: acct.Nonce, @@ -73,7 +73,7 @@ func (acct *StateAccount) Copy() *StateAccount { // or slim format which replaces the empty root and code hash as nil byte slice. type SlimAccount struct { Nonce uint64 - Balance *big.Int + Balance *uint256.Int Root []byte // Nil if root equals to types.EmptyRootHash CodeHash []byte // Nil if hash equals to types.EmptyCodeHash } diff --git a/core/types/transaction.go b/core/types/transaction.go index bf33165a77..35602becae 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -29,6 +29,7 @@ package types import ( "bytes" "errors" + "fmt" "io" "math/big" "sync/atomic" @@ -330,6 +331,7 @@ func (tx *Transaction) Cost() *big.Int { // RawSignatureValues returns the V, R, S signature values of the transaction. // The return values should not be modified by the caller. +// The return values may be nil or zero, if the transaction is unsigned. func (tx *Transaction) RawSignatureValues() (v, r, s *big.Int) { return tx.inner.rawSignatureValues() } @@ -518,6 +520,9 @@ func (tx *Transaction) WithSignature(signer Signer, sig []byte) (*Transaction, e if err != nil { return nil, err } + if r == nil || s == nil || v == nil { + return nil, fmt.Errorf("%w: r: %s, s: %s, v: %s", ErrInvalidSig, r, s, v) + } cpy := tx.inner.copy() cpy.setSignatureValues(signer.ChainID(), v, r, s) return &Transaction{inner: cpy, time: tx.time}, nil diff --git a/core/types/transaction_marshalling.go b/core/types/transaction_marshalling.go index c1ebc8b514..3839efdc63 100644 --- a/core/types/transaction_marshalling.go +++ b/core/types/transaction_marshalling.go @@ -33,6 +33,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/crypto/kzg4844" "github.com/holiman/uint256" ) @@ -57,6 +58,11 @@ type txJSON struct { S *hexutil.Big `json:"s"` YParity *hexutil.Uint64 `json:"yParity,omitempty"` + // Blob transaction sidecar encoding: + Blobs []kzg4844.Blob `json:"blobs,omitempty"` + Commitments []kzg4844.Commitment `json:"commitments,omitempty"` + Proofs []kzg4844.Proof `json:"proofs,omitempty"` + // Only used for encoding: Hash common.Hash `json:"hash"` } @@ -152,6 +158,11 @@ func (tx *Transaction) MarshalJSON() ([]byte, error) { enc.S = (*hexutil.Big)(itx.S.ToBig()) yparity := itx.V.Uint64() enc.YParity = (*hexutil.Uint64)(&yparity) + if sidecar := itx.Sidecar; sidecar != nil { + enc.Blobs = itx.Sidecar.Blobs + enc.Commitments = itx.Sidecar.Commitments + enc.Proofs = itx.Sidecar.Proofs + } } return json.Marshal(&enc) } diff --git a/core/types/transaction_signing_test.go b/core/types/transaction_signing_test.go index 0976f59751..d717e6f717 100644 --- a/core/types/transaction_signing_test.go +++ b/core/types/transaction_signing_test.go @@ -28,9 +28,11 @@ package types import ( "errors" + "fmt" "math/big" "testing" + "github.com/ava-labs/subnet-evm/params" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/rlp" @@ -51,7 +53,7 @@ func TestEIP155Signing(t *testing.T) { t.Fatal(err) } if from != addr { - t.Errorf("exected from and address to be equal. Got %x want %x", from, addr) + t.Errorf("expected from and address to be equal. Got %x want %x", from, addr) } } @@ -149,3 +151,53 @@ func TestChainId(t *testing.T) { t.Error("expected no error") } } + +type nilSigner struct { + v, r, s *big.Int + Signer +} + +func (ns *nilSigner) SignatureValues(tx *Transaction, sig []byte) (r, s, v *big.Int, err error) { + return ns.v, ns.r, ns.s, nil +} + +// TestNilSigner ensures a faulty Signer implementation does not result in nil signature values or panics. +func TestNilSigner(t *testing.T) { + key, _ := crypto.GenerateKey() + innerSigner := LatestSignerForChainID(big.NewInt(1)) + for i, signer := range []Signer{ + &nilSigner{v: nil, r: nil, s: nil, Signer: innerSigner}, + &nilSigner{v: big.NewInt(1), r: big.NewInt(1), s: nil, Signer: innerSigner}, + &nilSigner{v: big.NewInt(1), r: nil, s: big.NewInt(1), Signer: innerSigner}, + &nilSigner{v: nil, r: big.NewInt(1), s: big.NewInt(1), Signer: innerSigner}, + } { + t.Run(fmt.Sprintf("signer_%d", i), func(t *testing.T) { + t.Run("legacy", func(t *testing.T) { + legacyTx := createTestLegacyTxInner() + _, err := SignNewTx(key, signer, legacyTx) + if !errors.Is(err, ErrInvalidSig) { + t.Fatal("expected signature values error, no nil result or panic") + } + }) + // test Blob tx specifically, since the signature value types changed + t.Run("blobtx", func(t *testing.T) { + blobtx := createEmptyBlobTxInner(false) + _, err := SignNewTx(key, signer, blobtx) + if !errors.Is(err, ErrInvalidSig) { + t.Fatal("expected signature values error, no nil result or panic") + } + }) + }) + } +} + +func createTestLegacyTxInner() *LegacyTx { + return &LegacyTx{ + Nonce: uint64(0), + To: nil, + Value: big.NewInt(0), + Gas: params.TxGas, + GasPrice: big.NewInt(params.GWei), + Data: nil, + } +} diff --git a/core/types/tx_blob.go b/core/types/tx_blob.go index d91845ff57..3e6872ae78 100644 --- a/core/types/tx_blob.go +++ b/core/types/tx_blob.go @@ -43,7 +43,7 @@ type BlobTx struct { BlobHashes []common.Hash // A blob transaction can optionally contain blobs. This field must be set when BlobTx - // is used to create a transaction for sigining. + // is used to create a transaction for signing. Sidecar *BlobTxSidecar `rlp:"-"` // Signature values @@ -61,9 +61,10 @@ type BlobTxSidecar struct { // BlobHashes computes the blob hashes of the given blobs. func (sc *BlobTxSidecar) BlobHashes() []common.Hash { + hasher := sha256.New() h := make([]common.Hash, len(sc.Commitments)) for i := range sc.Blobs { - h[i] = blobHash(&sc.Commitments[i]) + h[i] = kzg4844.CalcBlobHashV1(hasher, &sc.Commitments[i]) } return h } @@ -235,12 +236,3 @@ func (tx *BlobTx) decode(input []byte) error { } return nil } - -func blobHash(commit *kzg4844.Commitment) common.Hash { - hasher := sha256.New() - hasher.Write(commit[:]) - var vhash common.Hash - hasher.Sum(vhash[:0]) - vhash[0] = params.BlobTxHashVersion - return vhash -} diff --git a/core/types/tx_blob_test.go b/core/types/tx_blob_test.go index 44ac48cc6f..25d09e31ce 100644 --- a/core/types/tx_blob_test.go +++ b/core/types/tx_blob_test.go @@ -65,6 +65,12 @@ var ( ) func createEmptyBlobTx(key *ecdsa.PrivateKey, withSidecar bool) *Transaction { + blobtx := createEmptyBlobTxInner(withSidecar) + signer := NewCancunSigner(blobtx.ChainID.ToBig()) + return MustSignNewTx(key, signer, blobtx) +} + +func createEmptyBlobTxInner(withSidecar bool) *BlobTx { sidecar := &BlobTxSidecar{ Blobs: []kzg4844.Blob{emptyBlob}, Commitments: []kzg4844.Commitment{emptyBlobCommit}, @@ -85,6 +91,5 @@ func createEmptyBlobTx(key *ecdsa.PrivateKey, withSidecar bool) *Transaction { if withSidecar { blobtx.Sidecar = sidecar } - signer := NewCancunSigner(blobtx.ChainID.ToBig()) - return MustSignNewTx(key, signer, blobtx) + return blobtx } diff --git a/core/vm/contract.go b/core/vm/contract.go index ab541ccb0e..129b208eb7 100644 --- a/core/vm/contract.go +++ b/core/vm/contract.go @@ -27,8 +27,6 @@ package vm import ( - "math/big" - "github.com/ethereum/go-ethereum/common" "github.com/holiman/uint256" ) @@ -69,11 +67,11 @@ type Contract struct { Input []byte Gas uint64 - value *big.Int + value *uint256.Int } // NewContract returns a new contract environment for the execution of EVM. -func NewContract(caller ContractRef, object ContractRef, value *big.Int, gas uint64) *Contract { +func NewContract(caller ContractRef, object ContractRef, value *uint256.Int, gas uint64) *Contract { c := &Contract{CallerAddress: caller.Address(), caller: caller, self: object} if parent, ok := caller.(*Contract); ok { @@ -183,7 +181,7 @@ func (c *Contract) Address() common.Address { } // Value returns the contract's value (sent to it from it's caller) -func (c *Contract) Value() *big.Int { +func (c *Contract) Value() *uint256.Int { return c.value } diff --git a/core/vm/contracts.go b/core/vm/contracts.go index 4eacdd2456..f4d35b5ab6 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -306,7 +306,6 @@ type bigModExp struct { } var ( - big0 = big.NewInt(0) big1 = big.NewInt(1) big3 = big.NewInt(3) big4 = big.NewInt(4) diff --git a/core/vm/contracts_test.go b/core/vm/contracts_test.go index 8608fdf36d..80a2c33d18 100644 --- a/core/vm/contracts_test.go +++ b/core/vm/contracts_test.go @@ -233,7 +233,7 @@ func BenchmarkPrecompiledRipeMD(bench *testing.B) { benchmarkPrecompiled("03", t, bench) } -// Benchmarks the sample inputs from the identiy precompile. +// Benchmarks the sample inputs from the identity precompile. func BenchmarkPrecompiledIdentity(bench *testing.B) { t := precompiledTest{ Input: "38d18acb67d25c8bb9942764b62f18e17054f66a817bd4295423adf9ed98873e000000000000000000000000000000000000000000000000000000000000001b38d18acb67d25c8bb9942764b62f18e17054f66a817bd4295423adf9ed98873e789d1dd423d25f0772d2748d60f7e4b81bb14d086eba8e8e8efb6dcff8a4ae02", diff --git a/core/vm/eips.go b/core/vm/eips.go index 655bbb9fd3..80cd4e0ef0 100644 --- a/core/vm/eips.go +++ b/core/vm/eips.go @@ -95,7 +95,7 @@ func enable1884(jt *JumpTable) { } func opSelfBalance(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - balance, _ := uint256.FromBig(interpreter.evm.StateDB.GetBalance(scope.Contract.Address())) + balance := interpreter.evm.StateDB.GetBalance(scope.Contract.Address()) scope.Stack.push(balance) return nil, nil } diff --git a/core/vm/evm.go b/core/vm/evm.go index 7a95719c9e..90a9ff4c9f 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -63,9 +63,9 @@ func IsProhibited(addr common.Address) bool { type ( // CanTransferFunc is the signature of a transfer guard function - CanTransferFunc func(StateDB, common.Address, *big.Int) bool + CanTransferFunc func(StateDB, common.Address, *uint256.Int) bool // TransferFunc is the signature of a transfer function - TransferFunc func(StateDB, common.Address, common.Address, *big.Int) + TransferFunc func(StateDB, common.Address, common.Address, *uint256.Int) // GetHashFunc returns the n'th block hash in the blockchain // and is used by the BLOCKHASH EVM op code. GetHashFunc func(uint64) common.Hash @@ -254,16 +254,13 @@ func (evm *EVM) Interpreter() *EVMInterpreter { // parameters. It also handles any necessary value transfer required and takes // the necessary steps to create accounts and reverses the state in case of an // execution error or failed value transfer. -func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas uint64, value *big.Int) (ret []byte, leftOverGas uint64, err error) { +func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas uint64, value *uint256.Int) (ret []byte, leftOverGas uint64, err error) { // Fail if we're trying to execute above the call depth limit if evm.depth > int(params.CallCreateDepth) { return nil, gas, vmerrs.ErrDepth } // Fail if we're trying to transfer more than the available balance - // Note: it is not possible for a negative value to be passed in here due to the fact - // that [value] will be popped from the stack and decoded to a *big.Int, which will - // always yield a positive result. - if value.Sign() != 0 && !evm.Context.CanTransfer(evm.StateDB, caller.Address(), value) { + if !value.IsZero() && !evm.Context.CanTransfer(evm.StateDB, caller.Address(), value) { return nil, gas, vmerrs.ErrInsufficientBalance } snapshot := evm.StateDB.Snapshot() @@ -271,14 +268,14 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas debug := evm.Config.Tracer != nil if !evm.StateDB.Exist(addr) { - if !isPrecompile && evm.chainRules.IsEIP158 && value.Sign() == 0 { + if !isPrecompile && evm.chainRules.IsEIP158 && value.IsZero() { // Calling a non existing account, don't do anything, but ping the tracer if debug { if evm.depth == 0 { - evm.Config.Tracer.CaptureStart(evm, caller.Address(), addr, false, input, gas, value) + evm.Config.Tracer.CaptureStart(evm, caller.Address(), addr, false, input, gas, value.ToBig()) evm.Config.Tracer.CaptureEnd(ret, 0, nil) } else { - evm.Config.Tracer.CaptureEnter(CALL, caller.Address(), addr, input, gas, value) + evm.Config.Tracer.CaptureEnter(CALL, caller.Address(), addr, input, gas, value.ToBig()) evm.Config.Tracer.CaptureExit(ret, 0, nil) } } @@ -291,13 +288,13 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas // Capture the tracer start/end events in debug mode if debug { if evm.depth == 0 { - evm.Config.Tracer.CaptureStart(evm, caller.Address(), addr, false, input, gas, value) + evm.Config.Tracer.CaptureStart(evm, caller.Address(), addr, false, input, gas, value.ToBig()) defer func(startGas uint64) { // Lazy evaluation of the parameters evm.Config.Tracer.CaptureEnd(ret, startGas-gas, err) }(gas) } else { // Handle tracer events for entering and exiting a call frame - evm.Config.Tracer.CaptureEnter(CALL, caller.Address(), addr, input, gas, value) + evm.Config.Tracer.CaptureEnter(CALL, caller.Address(), addr, input, gas, value.ToBig()) defer func(startGas uint64) { evm.Config.Tracer.CaptureExit(ret, startGas-gas, err) }(gas) @@ -344,7 +341,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas // // CallCode differs from Call in the sense that it executes the given address' // code with the caller as context. -func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, gas uint64, value *big.Int) (ret []byte, leftOverGas uint64, err error) { +func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, gas uint64, value *uint256.Int) (ret []byte, leftOverGas uint64, err error) { // Fail if we're trying to execute above the call depth limit if evm.depth > int(params.CallCreateDepth) { return nil, gas, vmerrs.ErrDepth @@ -363,7 +360,7 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, // Invoke tracer hooks that signal entering/exiting a call frame if evm.Config.Tracer != nil { - evm.Config.Tracer.CaptureEnter(CALLCODE, caller.Address(), addr, input, gas, value) + evm.Config.Tracer.CaptureEnter(CALLCODE, caller.Address(), addr, input, gas, value.ToBig()) defer func(startGas uint64) { evm.Config.Tracer.CaptureExit(ret, startGas-gas, err) }(gas) @@ -408,7 +405,7 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by // that caller is something other than a Contract. parent := caller.(*Contract) // DELEGATECALL inherits value from parent call - evm.Config.Tracer.CaptureEnter(DELEGATECALL, caller.Address(), addr, input, gas, parent.value) + evm.Config.Tracer.CaptureEnter(DELEGATECALL, caller.Address(), addr, input, gas, parent.value.ToBig()) defer func(startGas uint64) { evm.Config.Tracer.CaptureExit(ret, startGas-gas, err) }(gas) @@ -454,7 +451,7 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte // This doesn't matter on Mainnet, where all empties are gone at the time of Byzantium, // but is the correct thing to do and matters on other networks, in tests, and potential // future scenarios - evm.StateDB.AddBalance(addr, big0) + evm.StateDB.AddBalance(addr, new(uint256.Int)) // Invoke tracer hooks that signal entering/exiting a call frame if evm.Config.Tracer != nil { @@ -473,7 +470,7 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte addrCopy := addr // Initialise a new contract and set the code that is to be used by the EVM. // The contract is a scoped environment for this execution context only. - contract := NewContract(caller, AccountRef(addrCopy), new(big.Int), gas) + contract := NewContract(caller, AccountRef(addrCopy), new(uint256.Int), gas) contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), evm.StateDB.GetCode(addrCopy)) // When an error was returned by the EVM or when setting the creation code // above we revert to the snapshot and consume any gas remaining. Additionally @@ -503,7 +500,7 @@ func (c *codeAndHash) Hash() common.Hash { } // create creates a new contract using code as deployment code. -func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, value *big.Int, address common.Address, typ OpCode) ([]byte, common.Address, uint64, error) { +func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, value *uint256.Int, address common.Address, typ OpCode) ([]byte, common.Address, uint64, error) { // Depth check execution. Fail if we're trying to execute above the // limit. if evm.depth > int(params.CallCreateDepth) { @@ -558,9 +555,9 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, if evm.Config.Tracer != nil { if evm.depth == 0 { - evm.Config.Tracer.CaptureStart(evm, caller.Address(), address, true, codeAndHash.code, gas, value) + evm.Config.Tracer.CaptureStart(evm, caller.Address(), address, true, codeAndHash.code, gas, value.ToBig()) } else { - evm.Config.Tracer.CaptureEnter(typ, caller.Address(), address, codeAndHash.code, gas, value) + evm.Config.Tracer.CaptureEnter(typ, caller.Address(), address, codeAndHash.code, gas, value.ToBig()) } } @@ -610,7 +607,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, } // Create creates a new contract using code as deployment code. -func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) { +func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *uint256.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) { contractAddr = crypto.CreateAddress(caller.Address(), evm.StateDB.GetNonce(caller.Address())) return evm.create(caller, &codeAndHash{code: code}, gas, value, contractAddr, CREATE) } @@ -619,7 +616,7 @@ func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.I // // The different between Create2 with Create is Create2 uses keccak256(0xff ++ msg.sender ++ salt ++ keccak256(init_code))[12:] // instead of the usual sender-and-nonce-hash as the address where the contract is initialized at. -func (evm *EVM) Create2(caller ContractRef, code []byte, gas uint64, endowment *big.Int, salt *uint256.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) { +func (evm *EVM) Create2(caller ContractRef, code []byte, gas uint64, endowment *uint256.Int, salt *uint256.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) { codeAndHash := &codeAndHash{code: code} contractAddr = crypto.CreateAddress2(caller.Address(), salt.Bytes32(), codeAndHash.Hash().Bytes()) return evm.create(caller, codeAndHash, gas, endowment, contractAddr, CREATE2) diff --git a/core/vm/gas_table_test.go b/core/vm/gas_table_test.go index 0102f71ec3..eaaaf5d36b 100644 --- a/core/vm/gas_table_test.go +++ b/core/vm/gas_table_test.go @@ -40,6 +40,7 @@ import ( "github.com/ava-labs/subnet-evm/vmerrs" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/holiman/uint256" ) func TestMemoryGasCost(t *testing.T) { @@ -102,12 +103,12 @@ func TestEIP2200(t *testing.T) { statedb.Finalise(true) // Push the state into the "original" slot vmctx := BlockContext{ - CanTransfer: func(StateDB, common.Address, *big.Int) bool { return true }, - Transfer: func(StateDB, common.Address, common.Address, *big.Int) {}, + CanTransfer: func(StateDB, common.Address, *uint256.Int) bool { return true }, + Transfer: func(StateDB, common.Address, common.Address, *uint256.Int) {}, } vmenv := NewEVM(vmctx, TxContext{}, statedb, params.TestChainConfig, Config{ExtraEips: []int{2200}}) - _, gas, err := vmenv.Call(AccountRef(common.Address{}), address, nil, tt.gaspool, new(big.Int)) + _, gas, err := vmenv.Call(AccountRef(common.Address{}), address, nil, tt.gaspool, new(uint256.Int)) if err != tt.failure { t.Errorf("test %d: failure mismatch: have %v, want %v", i, err, tt.failure) } @@ -152,8 +153,8 @@ func TestCreateGas(t *testing.T) { statedb.SetCode(address, hexutil.MustDecode(tt.code)) statedb.Finalise(true) vmctx := BlockContext{ - CanTransfer: func(StateDB, common.Address, *big.Int) bool { return true }, - Transfer: func(StateDB, common.Address, common.Address, *big.Int) {}, + CanTransfer: func(StateDB, common.Address, *uint256.Int) bool { return true }, + Transfer: func(StateDB, common.Address, common.Address, *uint256.Int) {}, BlockNumber: big.NewInt(0), } config := Config{} @@ -165,7 +166,7 @@ func TestCreateGas(t *testing.T) { // because it is the last fork before the activation of EIP-3860 vmenv := NewEVM(vmctx, TxContext{}, statedb, params.TestSubnetEVMChainConfig, config) var startGas = uint64(testGas) - ret, gas, err := vmenv.Call(AccountRef(common.Address{}), address, nil, startGas, new(big.Int)) + ret, gas, err := vmenv.Call(AccountRef(common.Address{}), address, nil, startGas, new(uint256.Int)) if err != nil { return false } diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 65f659aa32..eac495c0e9 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -27,6 +27,8 @@ package vm import ( + "math" + "github.com/ava-labs/subnet-evm/params" "github.com/ava-labs/subnet-evm/vmerrs" "github.com/ethereum/go-ethereum/common" @@ -270,7 +272,7 @@ func opAddress(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([] func opBalance(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { slot := scope.Stack.peek() address := common.Address(slot.Bytes20()) - slot.SetFromBig(interpreter.evm.StateDB.GetBalance(address)) + slot.Set(interpreter.evm.StateDB.GetBalance(address)) return nil, nil } @@ -285,8 +287,7 @@ func opCaller(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b } func opCallValue(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - v, _ := uint256.FromBig(scope.Contract.value) - scope.Stack.push(v) + scope.Stack.push(scope.Contract.value) return nil, nil } @@ -358,9 +359,7 @@ func opExtCodeSize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) } func opCodeSize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - l := new(uint256.Int) - l.SetUint64(uint64(len(scope.Contract.Code))) - scope.Stack.push(l) + scope.Stack.push(new(uint256.Int).SetUint64(uint64(len(scope.Contract.Code)))) return nil, nil } @@ -372,7 +371,7 @@ func opCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([ ) uint64CodeOffset, overflow := codeOffset.Uint64WithOverflow() if overflow { - uint64CodeOffset = 0xffffffffffffffff + uint64CodeOffset = math.MaxUint64 } codeCopy := getData(scope.Contract.Code, uint64CodeOffset, length.Uint64()) scope.Memory.Set(memOffset.Uint64(), length.Uint64(), codeCopy) @@ -390,7 +389,7 @@ func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ) uint64CodeOffset, overflow := codeOffset.Uint64WithOverflow() if overflow { - uint64CodeOffset = 0xffffffffffffffff + uint64CodeOffset = math.MaxUint64 } addr := common.Address(a.Bytes20()) codeCopy := getData(interpreter.evm.StateDB.GetCode(addr), uint64CodeOffset, length.Uint64()) @@ -596,13 +595,8 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b stackvalue := size scope.Contract.UseGas(gas) - //TODO: use uint256.Int instead of converting with toBig() - var bigVal = big0 - if !value.IsZero() { - bigVal = value.ToBig() - } - res, addr, returnGas, suberr := interpreter.evm.Create(scope.Contract, input, gas, bigVal) + res, addr, returnGas, suberr := interpreter.evm.Create(scope.Contract, input, gas, &value) // Push item on the stack based on the returned error. If the ruleset is // homestead we must check for CodeStoreOutOfGasError (homestead only // rule) and treat as an error, if the ruleset is frontier we must @@ -641,13 +635,8 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([] scope.Contract.UseGas(gas) // reuse size int for stackvalue stackvalue := size - //TODO: use uint256.Int instead of converting with toBig() - bigEndowment := big0 - if !endowment.IsZero() { - bigEndowment = endowment.ToBig() - } res, addr, returnGas, suberr := interpreter.evm.Create2(scope.Contract, input, gas, - bigEndowment, &salt) + &endowment, &salt) // Push item on the stack based on the returned error. if suberr != nil { stackvalue.Clear() @@ -680,16 +669,11 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byt if interpreter.readOnly && !value.IsZero() { return nil, vmerrs.ErrWriteProtection } - var bigVal = big0 - //TODO: use uint256.Int instead of converting with toBig() - // By using big0 here, we save an alloc for the most common case (non-ether-transferring contract calls), - // but it would make more sense to extend the usage of uint256.Int if !value.IsZero() { gas += params.CallStipend - bigVal = value.ToBig() } + ret, returnGas, err := interpreter.evm.Call(scope.Contract, toAddr, args, gas, &value) - ret, returnGas, err := interpreter.evm.Call(scope.Contract, toAddr, args, gas, bigVal) if err != nil { temp.Clear() } else { @@ -717,14 +701,11 @@ func opCallCode(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([ // Get arguments from the memory. args := scope.Memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64())) - //TODO: use uint256.Int instead of converting with toBig() - var bigVal = big0 if !value.IsZero() { gas += params.CallStipend - bigVal = value.ToBig() } - ret, returnGas, err := interpreter.evm.CallCode(scope.Contract, toAddr, args, gas, bigVal) + ret, returnGas, err := interpreter.evm.CallCode(scope.Contract, toAddr, args, gas, &value) if err != nil { temp.Clear() } else { @@ -828,7 +809,7 @@ func opSelfdestruct(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext interpreter.evm.StateDB.AddBalance(beneficiary.Bytes20(), balance) interpreter.evm.StateDB.SelfDestruct(scope.Contract.Address()) if tracer := interpreter.evm.Config.Tracer; tracer != nil { - tracer.CaptureEnter(SELFDESTRUCT, scope.Contract.Address(), beneficiary.Bytes20(), []byte{}, 0, balance) + tracer.CaptureEnter(SELFDESTRUCT, scope.Contract.Address(), beneficiary.Bytes20(), []byte{}, 0, balance.ToBig()) tracer.CaptureExit([]byte{}, 0, nil) } return nil, errStopToken @@ -844,7 +825,7 @@ func opSelfdestruct6780(pc *uint64, interpreter *EVMInterpreter, scope *ScopeCon interpreter.evm.StateDB.AddBalance(beneficiary.Bytes20(), balance) interpreter.evm.StateDB.Selfdestruct6780(scope.Contract.Address()) if tracer := interpreter.evm.Config.Tracer; tracer != nil { - tracer.CaptureEnter(SELFDESTRUCT, scope.Contract.Address(), beneficiary.Bytes20(), []byte{}, 0, balance) + tracer.CaptureEnter(SELFDESTRUCT, scope.Contract.Address(), beneficiary.Bytes20(), []byte{}, 0, balance.ToBig()) tracer.CaptureExit([]byte{}, 0, nil) } return nil, errStopToken diff --git a/core/vm/instructions_test.go b/core/vm/instructions_test.go index ce36b18bc8..ec7be55bd7 100644 --- a/core/vm/instructions_test.go +++ b/core/vm/instructions_test.go @@ -601,7 +601,7 @@ func TestOpTstore(t *testing.T) { caller = common.Address{} to = common.Address{1} contractRef = contractRef{caller} - contract = NewContract(contractRef, AccountRef(to), new(big.Int), 0) + contract = NewContract(contractRef, AccountRef(to), new(uint256.Int), 0) scopeContext = ScopeContext{mem, stack, contract} value = common.Hex2Bytes("abcdef00000000000000abba000000000deaf000000c0de00100000000133700") ) diff --git a/core/vm/interface.go b/core/vm/interface.go index 34b3e714da..e7355110b9 100644 --- a/core/vm/interface.go +++ b/core/vm/interface.go @@ -32,15 +32,16 @@ import ( "github.com/ava-labs/subnet-evm/core/types" "github.com/ava-labs/subnet-evm/params" "github.com/ethereum/go-ethereum/common" + "github.com/holiman/uint256" ) // StateDB is an EVM database for full state querying. type StateDB interface { CreateAccount(common.Address) - SubBalance(common.Address, *big.Int) - AddBalance(common.Address, *big.Int) - GetBalance(common.Address) *big.Int + SubBalance(common.Address, *uint256.Int) + AddBalance(common.Address, *uint256.Int) + GetBalance(common.Address) *uint256.Int GetNonce(common.Address) uint64 SetNonce(common.Address, uint64) diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 34eb46425f..d79c25dcb5 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -157,7 +157,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( ) // Don't move this deferred function, it's placed before the capturestate-deferred method, - // so that it get's executed _after_: the capturestate needs the stacks before + // so that it gets executed _after_: the capturestate needs the stacks before // they are returned to the pools defer func() { returnStack(stack) diff --git a/core/vm/interpreter_test.go b/core/vm/interpreter_test.go index 8b40bfcc84..f1eb8f72e7 100644 --- a/core/vm/interpreter_test.go +++ b/core/vm/interpreter_test.go @@ -27,7 +27,6 @@ package vm import ( - "math/big" "testing" "time" @@ -37,6 +36,7 @@ import ( "github.com/ava-labs/subnet-evm/params" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/math" + "github.com/holiman/uint256" ) var loopInterruptTests = []string{ @@ -49,7 +49,7 @@ var loopInterruptTests = []string{ func TestLoopInterrupt(t *testing.T) { address := common.BytesToAddress([]byte("contract")) vmctx := BlockContext{ - Transfer: func(StateDB, common.Address, common.Address, *big.Int) {}, + Transfer: func(StateDB, common.Address, common.Address, *uint256.Int) {}, } for i, tt := range loopInterruptTests { @@ -64,7 +64,7 @@ func TestLoopInterrupt(t *testing.T) { timeout := make(chan bool) go func(evm *EVM) { - _, _, err := evm.Call(AccountRef(common.Address{}), address, nil, math.MaxUint64, new(big.Int)) + _, _, err := evm.Call(AccountRef(common.Address{}), address, nil, math.MaxUint64, new(uint256.Int)) errChannel <- err }(evm) diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go index a97e274ccd..caa5d721e7 100644 --- a/core/vm/jump_table.go +++ b/core/vm/jump_table.go @@ -112,7 +112,7 @@ func newDurangoInstructionSet() JumpTable { // constantinople, istanbul, petersburg, subnet-evm instructions. func newSubnetEVMInstructionSet() JumpTable { instructionSet := newIstanbulInstructionSet() - enable2929(&instructionSet) + enable2929(&instructionSet) // Gas cost increases for state access opcodes https://eips.ethereum.org/EIPS/eip-2929 enable3198(&instructionSet) // Base fee opcode https://eips.ethereum.org/EIPS/eip-3198 return validate(instructionSet) } diff --git a/core/vm/jump_table_test.go b/core/vm/jump_table_test.go index 6e838337c1..5fa9a532cb 100644 --- a/core/vm/jump_table_test.go +++ b/core/vm/jump_table_test.go @@ -32,7 +32,7 @@ import ( "github.com/stretchr/testify/require" ) -// TestJumpTableCopy tests that deep copy is necessery to prevent modify shared jump table +// TestJumpTableCopy tests that deep copy is necessary to prevent modify shared jump table func TestJumpTableCopy(t *testing.T) { tbl := newDurangoInstructionSet() require.Equal(t, uint64(0), tbl[SLOAD].constantGas) diff --git a/core/vm/operations_acl.go b/core/vm/operations_acl.go index 7d8aec3741..ae952d0ff9 100644 --- a/core/vm/operations_acl.go +++ b/core/vm/operations_acl.go @@ -175,7 +175,12 @@ func makeCallVariantGasCallEIP2929(oldCalculator gasFunc) gasFunc { // outside of this function, as part of the dynamic gas, and that will make it // also become correctly reported to tracers. contract.Gas += coldCost - return gas + coldCost, nil + + var overflow bool + if gas, overflow = math.SafeAdd(gas, coldCost); overflow { + return 0, vmerrs.ErrGasUintOverflow + } + return gas, nil } } @@ -185,7 +190,7 @@ var ( gasStaticCallEIP2929 = makeCallVariantGasCallEIP2929(gasStaticCall) gasCallCodeEIP2929 = makeCallVariantGasCallEIP2929(gasCallCode) gasSelfdestructEIP2929 = makeSelfdestructGasFn(false) // Note: refunds were never enabled on Avalanche - // gasSelfdestructEIP3529 implements the changes in EIP-2539 (no refunds) + // gasSelfdestructEIP3529 implements the changes in EIP-3529 (no refunds) gasSelfdestructEIP3529 = makeSelfdestructGasFn(false) // gasSStoreEIP2929 implements gas cost for SSTORE according to EIP-2929 // @@ -202,7 +207,7 @@ var ( gasSStoreEIP2929 = makeGasSStoreFunc() ) -// makeSelfdestructGasFn can create the selfdestruct dynamic gas function for EIP-2929 and EIP-2539 +// makeSelfdestructGasFn can create the selfdestruct dynamic gas function for EIP-2929 and EIP-3529 func makeSelfdestructGasFn(refundsEnabled bool) gasFunc { gasFunc := func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { var ( diff --git a/core/vm/runtime/runtime.go b/core/vm/runtime/runtime.go index 52668a5c2c..52a3b04b01 100644 --- a/core/vm/runtime/runtime.go +++ b/core/vm/runtime/runtime.go @@ -37,6 +37,7 @@ import ( "github.com/ava-labs/subnet-evm/params" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" + "github.com/holiman/uint256" ) // Config is a basic type specifying certain configuration flags for running @@ -145,7 +146,7 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) { common.BytesToAddress([]byte("contract")), input, cfg.GasLimit, - cfg.Value, + uint256.MustFromBig(cfg.Value), ) return ret, cfg.State, err } @@ -175,7 +176,7 @@ func Create(input []byte, cfg *Config) ([]byte, common.Address, uint64, error) { sender, input, cfg.GasLimit, - cfg.Value, + uint256.MustFromBig(cfg.Value), ) return code, address, leftOverGas, err } @@ -190,7 +191,7 @@ func Call(address common.Address, input []byte, cfg *Config) ([]byte, uint64, er var ( vmenv = NewEnv(cfg) - sender = cfg.State.GetOrNewStateObject(cfg.Origin) + sender = vm.AccountRef(cfg.Origin) statedb = cfg.State rules = cfg.ChainConfig.Rules(vmenv.Context.BlockNumber, vmenv.Context.Time) ) @@ -205,7 +206,7 @@ func Call(address common.Address, input []byte, cfg *Config) ([]byte, uint64, er address, input, cfg.GasLimit, - cfg.Value, + uint256.MustFromBig(cfg.Value), ) return ret, leftOverGas, err } diff --git a/core/vm/runtime/runtime_test.go b/core/vm/runtime/runtime_test.go index 0ee9411f18..65127f24ef 100644 --- a/core/vm/runtime/runtime_test.go +++ b/core/vm/runtime/runtime_test.go @@ -48,6 +48,7 @@ import ( // force-load js tracers to trigger registration _ "github.com/ava-labs/subnet-evm/eth/tracers/js" + "github.com/holiman/uint256" ) func TestDefaults(t *testing.T) { @@ -370,12 +371,12 @@ func benchmarkNonModifyingCode(gas uint64, code []byte, name string, tracerCode //cfg.State.CreateAccount(cfg.Origin) // set the receiver's (the executing contract) code for execution. cfg.State.SetCode(destination, code) - vmenv.Call(sender, destination, nil, gas, cfg.Value) + vmenv.Call(sender, destination, nil, gas, uint256.MustFromBig(cfg.Value)) b.Run(name, func(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { - vmenv.Call(sender, destination, nil, gas, cfg.Value) + vmenv.Call(sender, destination, nil, gas, uint256.MustFromBig(cfg.Value)) } }) } diff --git a/eth/api_backend.go b/eth/api_backend.go index 92aab3fbb6..e3cba7bf92 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -38,8 +38,8 @@ import ( "github.com/ava-labs/subnet-evm/consensus/dummy" "github.com/ava-labs/subnet-evm/core" "github.com/ava-labs/subnet-evm/core/bloombits" - "github.com/ava-labs/subnet-evm/core/rawdb" "github.com/ava-labs/subnet-evm/core/state" + "github.com/ava-labs/subnet-evm/core/txpool" "github.com/ava-labs/subnet-evm/core/types" "github.com/ava-labs/subnet-evm/core/vm" "github.com/ava-labs/subnet-evm/eth/gasprice" @@ -299,7 +299,7 @@ func (b *EthAPIBackend) GetEVM(ctx context.Context, msg *core.Message, state *st } else { context = core.NewEVMBlockContext(header, b.eth.BlockChain(), nil) } - return vm.NewEVM(context, txContext, state, b.eth.blockchain.Config(), *vmConfig) + return vm.NewEVM(context, txContext, state, b.ChainConfig(), *vmConfig) } func (b *EthAPIBackend) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription { @@ -353,7 +353,7 @@ func (b *EthAPIBackend) SendTx(ctx context.Context, signedTx *types.Transaction) } func (b *EthAPIBackend) GetPoolTransactions() (types.Transactions, error) { - pending := b.eth.txPool.Pending(false) + pending := b.eth.txPool.Pending(txpool.PendingFilter{}) var txs types.Transactions for _, batch := range pending { for _, lazy := range batch { @@ -369,10 +369,14 @@ func (b *EthAPIBackend) GetPoolTransaction(hash common.Hash) *types.Transaction return b.eth.txPool.Get(hash) } -func (b *EthAPIBackend) GetTransaction(ctx context.Context, txHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64, error) { - // Note: we only index transactions during Accept, so the below check against unfinalized queries is technically redundant, but - // we keep it for defense in depth. - tx, blockHash, blockNumber, index := rawdb.ReadTransaction(b.eth.ChainDb(), txHash) +func (b *EthAPIBackend) GetTransaction(ctx context.Context, txHash common.Hash) (bool, *types.Transaction, common.Hash, uint64, uint64, error) { + lookup, tx, err := b.eth.blockchain.GetTransactionLookup(txHash) + if err != nil { + return false, nil, common.Hash{}, 0, 0, err + } + if lookup == nil || tx == nil { + return false, nil, common.Hash{}, 0, 0, nil + } // Respond as if the transaction does not exist if it is not yet in an // accepted block. We explicitly choose not to error here to avoid breaking @@ -380,12 +384,12 @@ func (b *EthAPIBackend) GetTransaction(ctx context.Context, txHash common.Hash) // does not exist). acceptedBlock := b.eth.LastAcceptedBlock() if !b.IsAllowUnfinalizedQueries() && acceptedBlock != nil && tx != nil { - if blockNumber > acceptedBlock.NumberU64() { - return nil, common.Hash{}, 0, 0, nil + if lookup.BlockIndex > acceptedBlock.NumberU64() { + return false, nil, common.Hash{}, 0, 0, nil } } - return tx, blockHash, blockNumber, index, nil + return true, tx, lookup.BlockHash, lookup.BlockIndex, lookup.Index, nil } func (b *EthAPIBackend) GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error) { diff --git a/eth/api_debug_test.go b/eth/api_debug_test.go index 4e9591e317..b4b626104c 100644 --- a/eth/api_debug_test.go +++ b/eth/api_debug_test.go @@ -29,7 +29,6 @@ package eth import ( "bytes" "fmt" - "math/big" "reflect" "strings" "testing" @@ -37,7 +36,8 @@ import ( "github.com/ava-labs/subnet-evm/core/rawdb" "github.com/ava-labs/subnet-evm/core/state" "github.com/ava-labs/subnet-evm/core/types" - "github.com/ava-labs/subnet-evm/trie" + "github.com/ava-labs/subnet-evm/triedb" + "github.com/holiman/uint256" "github.com/davecgh/go-spew/spew" @@ -76,7 +76,7 @@ func TestAccountRange(t *testing.T) { t.Parallel() var ( - statedb = state.NewDatabaseWithConfig(rawdb.NewMemoryDatabase(), &trie.Config{Preimages: true}) + statedb = state.NewDatabaseWithConfig(rawdb.NewMemoryDatabase(), &triedb.Config{Preimages: true}) sdb, _ = state.New(types.EmptyRootHash, statedb, nil) addrs = [AccountRangeMaxResults * 2]common.Address{} m = map[common.Address]bool{} @@ -86,7 +86,7 @@ func TestAccountRange(t *testing.T) { hash := common.HexToHash(fmt.Sprintf("%x", i)) addr := common.BytesToAddress(crypto.Keccak256Hash(hash.Bytes()).Bytes()) addrs[i] = addr - sdb.SetBalance(addrs[i], big.NewInt(1)) + sdb.SetBalance(addrs[i], uint256.NewInt(1)) if _, ok := m[addr]; ok { t.Fatalf("bad") } else { @@ -173,7 +173,7 @@ func TestStorageRangeAt(t *testing.T) { // Create a state where account 0x010000... has a few storage entries. var ( - db = state.NewDatabaseWithConfig(rawdb.NewMemoryDatabase(), &trie.Config{Preimages: true}) + db = state.NewDatabaseWithConfig(rawdb.NewMemoryDatabase(), &triedb.Config{Preimages: true}) sdb, _ = state.New(types.EmptyRootHash, db, nil) addr = common.Address{0x01} keys = []common.Hash{ // hashes of Keys of storage diff --git a/eth/backend.go b/eth/backend.go index 85da1b318f..b92ad99f99 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -37,7 +37,6 @@ import ( "github.com/ava-labs/avalanchego/utils/timer/mockable" "github.com/ava-labs/subnet-evm/accounts" "github.com/ava-labs/subnet-evm/consensus" - "github.com/ava-labs/subnet-evm/consensus/dummy" "github.com/ava-labs/subnet-evm/core" "github.com/ava-labs/subnet-evm/core/bloombits" "github.com/ava-labs/subnet-evm/core/rawdb" @@ -131,6 +130,7 @@ func New( chainDb ethdb.Database, settings Settings, lastAcceptedHash common.Hash, + engine consensus.Engine, clock *mockable.Clock, ) (*Ethereum, error) { if chainDb == nil { @@ -176,7 +176,7 @@ func New( chainDb: chainDb, eventMux: new(event.TypeMux), accountManager: stack.AccountManager(), - engine: dummy.NewFakerWithClock(clock), + engine: engine, closeBloomHandler: make(chan struct{}), networkID: networkID, etherbase: config.Miner.Etherbase, @@ -253,7 +253,7 @@ func New( legacyPool := legacypool.New(config.TxPool, eth.blockchain) - eth.txPool, err = txpool.New(new(big.Int).SetUint64(config.TxPool.PriceLimit), eth.blockchain, []txpool.SubPool{legacyPool}) //, blobPool}) + eth.txPool, err = txpool.New(config.TxPool.PriceLimit, eth.blockchain, []txpool.SubPool{legacyPool}) //, blobPool}) if err != nil { return nil, err } diff --git a/eth/filters/api.go b/eth/filters/api.go index eae88bc20f..0748822b97 100644 --- a/eth/filters/api.go +++ b/eth/filters/api.go @@ -170,6 +170,8 @@ func (api *FilterAPI) NewPendingTransactions(ctx context.Context, fullTx *bool) go func() { txs := make(chan []*types.Transaction, 128) pendingTxSub := api.events.SubscribePendingTxs(txs) + defer pendingTxSub.Unsubscribe() + chainConfig := api.sys.backend.ChainConfig() for { @@ -187,10 +189,8 @@ func (api *FilterAPI) NewPendingTransactions(ctx context.Context, fullTx *bool) } } case <-rpcSub.Err(): - pendingTxSub.Unsubscribe() return case <-notifier.Closed(): - pendingTxSub.Unsubscribe() return } } @@ -301,16 +301,15 @@ func (api *FilterAPI) NewHeads(ctx context.Context) (*rpc.Subscription, error) { } else { headersSub = api.events.SubscribeAcceptedHeads(headers) } + defer headersSub.Unsubscribe() for { select { case h := <-headers: notifier.Notify(rpcSub.ID, h) case <-rpcSub.Err(): - headersSub.Unsubscribe() return case <-notifier.Closed(): - headersSub.Unsubscribe() return } } @@ -346,6 +345,7 @@ func (api *FilterAPI) Logs(ctx context.Context, crit FilterCriteria) (*rpc.Subsc } go func() { + defer logsSub.Unsubscribe() for { select { case logs := <-matchedLogs: @@ -354,10 +354,8 @@ func (api *FilterAPI) Logs(ctx context.Context, crit FilterCriteria) (*rpc.Subsc notifier.Notify(rpcSub.ID, &log) } case <-rpcSub.Err(): // client send an unsubscribe request - logsSub.Unsubscribe() return case <-notifier.Closed(): // connection dropped - logsSub.Unsubscribe() return } } diff --git a/eth/filters/filter_test.go b/eth/filters/filter_test.go index 6a9c50675e..8965943bf5 100644 --- a/eth/filters/filter_test.go +++ b/eth/filters/filter_test.go @@ -42,7 +42,7 @@ import ( "github.com/ava-labs/subnet-evm/core/vm" "github.com/ava-labs/subnet-evm/params" "github.com/ava-labs/subnet-evm/rpc" - "github.com/ava-labs/subnet-evm/trie" + "github.com/ava-labs/subnet-evm/triedb" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" "github.com/stretchr/testify/require" @@ -68,7 +68,7 @@ func BenchmarkFilters(b *testing.B) { addr4 = common.BytesToAddress([]byte("random addresses please")) gspec = &core.Genesis{ - Alloc: core.GenesisAlloc{addr1: {Balance: big.NewInt(1000000)}}, + Alloc: types.GenesisAlloc{addr1: {Balance: big.NewInt(1000000)}}, BaseFee: big.NewInt(1), Config: params.TestChainConfig, } @@ -98,7 +98,7 @@ func BenchmarkFilters(b *testing.B) { // The test txs are not properly signed, can't simply create a chain // and then import blocks. TODO(rjl493456442) try to get rid of the // manual database writes. - gspec.MustCommit(db, trie.NewDatabase(db, trie.HashDefaults)) + gspec.MustCommit(db, triedb.NewDatabase(db, triedb.HashDefaults)) for i, block := range chain { rawdb.WriteBlock(db, block) @@ -176,7 +176,7 @@ func TestFilters(t *testing.T) { gspec = &core.Genesis{ Config: params.TestChainConfig, - Alloc: core.GenesisAlloc{ + Alloc: types.GenesisAlloc{ addr: {Balance: big.NewInt(0).Mul(big.NewInt(100), big.NewInt(params.Ether))}, contract: {Balance: big.NewInt(0), Code: bytecode}, contract2: {Balance: big.NewInt(0), Code: bytecode}, @@ -192,7 +192,7 @@ func TestFilters(t *testing.T) { // Hack: GenerateChainWithGenesis creates a new db. // Commit the genesis manually and use GenerateChain. - _, err = gspec.Commit(db, trie.NewDatabase(db, nil)) + _, err = gspec.Commit(db, triedb.NewDatabase(db, nil)) if err != nil { t.Fatal(err) } diff --git a/eth/gasestimator/gasestimator.go b/eth/gasestimator/gasestimator.go index acc476ff9f..c150bad8cd 100644 --- a/eth/gasestimator/gasestimator.go +++ b/eth/gasestimator/gasestimator.go @@ -82,9 +82,9 @@ func Estimate(ctx context.Context, call *core.Message, opts *Options, gasCap uin } // Recap the highest gas limit with account's available balance. if feeCap.BitLen() != 0 { - balance := opts.State.GetBalance(call.From) + balance := opts.State.GetBalance(call.From).ToBig() - available := new(big.Int).Set(balance) + available := balance if call.Value != nil { if call.Value.Cmp(available) >= 0 { return 0, nil, core.ErrInsufficientFundsForTransfer diff --git a/eth/gasprice/feehistory.go b/eth/gasprice/feehistory.go index 143adac2d4..c85e53308d 100644 --- a/eth/gasprice/feehistory.go +++ b/eth/gasprice/feehistory.go @@ -181,8 +181,8 @@ func (oracle *Oracle) FeeHistory(ctx context.Context, blocks uint64, unresolvedL if p < 0 || p > 100 { return common.Big0, nil, nil, nil, fmt.Errorf("%w: %f", errInvalidPercentile, p) } - if i > 0 && p < rewardPercentiles[i-1] { - return common.Big0, nil, nil, nil, fmt.Errorf("%w: #%d:%f > #%d:%f", errInvalidPercentile, i-1, rewardPercentiles[i-1], i, p) + if i > 0 && p <= rewardPercentiles[i-1] { + return common.Big0, nil, nil, nil, fmt.Errorf("%w: #%d:%f >= #%d:%f", errInvalidPercentile, i-1, rewardPercentiles[i-1], i, p) } } lastBlock, blocks, err := oracle.resolveBlockRange(ctx, unresolvedLastBlock, blocks) diff --git a/eth/gasprice/gasprice_test.go b/eth/gasprice/gasprice_test.go index a41c9576a5..769c968a5a 100644 --- a/eth/gasprice/gasprice_test.go +++ b/eth/gasprice/gasprice_test.go @@ -101,7 +101,7 @@ func (b *testBackend) teardown() { func newTestBackendFakerEngine(t *testing.T, config *params.ChainConfig, numBlocks int, genBlocks func(i int, b *core.BlockGen)) *testBackend { var gspec = &core.Genesis{ Config: config, - Alloc: core.GenesisAlloc{addr: core.GenesisAccount{Balance: bal}}, + Alloc: types.GenesisAlloc{addr: {Balance: bal}}, } engine := dummy.NewETHFaker() @@ -128,7 +128,7 @@ func newTestBackendFakerEngine(t *testing.T, config *params.ChainConfig, numBloc func newTestBackend(t *testing.T, config *params.ChainConfig, numBlocks int, genBlocks func(i int, b *core.BlockGen)) *testBackend { var gspec = &core.Genesis{ Config: config, - Alloc: core.GenesisAlloc{addr: core.GenesisAccount{Balance: bal}}, + Alloc: types.GenesisAlloc{addr: {Balance: bal}}, } engine := dummy.NewFaker() diff --git a/eth/state_accessor.go b/eth/state_accessor.go index aa03d5fdc7..0508e7dc56 100644 --- a/eth/state_accessor.go +++ b/eth/state_accessor.go @@ -39,6 +39,7 @@ import ( "github.com/ava-labs/subnet-evm/core/vm" "github.com/ava-labs/subnet-evm/eth/tracers" "github.com/ava-labs/subnet-evm/trie" + "github.com/ava-labs/subnet-evm/triedb" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/log" ) @@ -52,7 +53,7 @@ func (eth *Ethereum) hashState(ctx context.Context, block *types.Block, reexec u var ( current *types.Block database state.Database - triedb *trie.Database + tdb *triedb.Database report = true origin = block.NumberU64() ) @@ -78,14 +79,14 @@ func (eth *Ethereum) hashState(ctx context.Context, block *types.Block, reexec u // the internal junks created by tracing will be persisted into the disk. // TODO(rjl493456442), clean cache is disabled to prevent memory leak, // please re-enable it for better performance. - database = state.NewDatabaseWithConfig(eth.chainDb, trie.HashDefaults) + database = state.NewDatabaseWithConfig(eth.chainDb, triedb.HashDefaults) if statedb, err = state.New(block.Root(), database, nil); err == nil { log.Info("Found disk backend for state trie", "root", block.Root(), "number", block.Number()) return statedb, noopReleaser, nil } } // The optional base statedb is given, mark the start point as parent block - statedb, database, triedb, report = base, base.Database(), base.Database().TrieDB(), false + statedb, database, tdb, report = base, base.Database(), base.Database().TrieDB(), false current = eth.blockchain.GetBlock(block.ParentHash(), block.NumberU64()-1) } else { // Otherwise, try to reexec blocks until we find a state or reach our limit @@ -95,8 +96,8 @@ func (eth *Ethereum) hashState(ctx context.Context, block *types.Block, reexec u // the internal junks created by tracing will be persisted into the disk. // TODO(rjl493456442), clean cache is disabled to prevent memory leak, // please re-enable it for better performance. - triedb = trie.NewDatabase(eth.chainDb, trie.HashDefaults) - database = state.NewDatabaseWithNodeDB(eth.chainDb, triedb) + tdb = triedb.NewDatabase(eth.chainDb, triedb.HashDefaults) + database = state.NewDatabaseWithNodeDB(eth.chainDb, tdb) // If we didn't check the live database, do check state over ephemeral database, // otherwise we would rewind past a persisted block (specific corner case is @@ -171,18 +172,18 @@ func (eth *Ethereum) hashState(ctx context.Context, block *types.Block, reexec u if err != nil { return nil, nil, fmt.Errorf("state reset after block %d failed: %v", current.NumberU64(), err) } - // Note: In coreth, the state reference is held by passing true to [statedb.Commit]. + // Note: In subnet-evm, the state reference is held by passing true to [statedb.Commit]. // Drop the parent state to prevent accumulating too many nodes in memory. if parent != (common.Hash{}) { - triedb.Dereference(parent) + tdb.Dereference(parent) } parent = root } if report { - _, nodes, imgs := triedb.Size() // all memory is contained within the nodes return in hashdb + _, nodes, imgs := tdb.Size() // all memory is contained within the nodes return in hashdb log.Info("Historical state regenerated", "block", current.NumberU64(), "elapsed", time.Since(start), "nodes", nodes, "preimages", imgs) } - return statedb, func() { triedb.Dereference(block.Root()) }, nil + return statedb, func() { tdb.Dereference(block.Root()) }, nil } func (eth *Ethereum) pathState(block *types.Block) (*state.StateDB, func(), error) { diff --git a/eth/tracers/api.go b/eth/tracers/api.go index deb04c2902..1be62dacaa 100644 --- a/eth/tracers/api.go +++ b/eth/tracers/api.go @@ -90,7 +90,7 @@ type Backend interface { BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) BlockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error) BadBlocks() ([]*types.Block, []*core.BadBlockReason) - GetTransaction(ctx context.Context, txHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64, error) + GetTransaction(ctx context.Context, txHash common.Hash) (bool, *types.Transaction, common.Hash, uint64, uint64, error) RPCGasCap() uint64 ChainConfig() *params.ChainConfig Engine() consensus.Engine @@ -874,12 +874,12 @@ func containsTx(block *types.Block, hash common.Hash) bool { // TraceTransaction returns the structured logs created during the execution of EVM // and returns them as a JSON object. func (api *API) TraceTransaction(ctx context.Context, hash common.Hash, config *TraceConfig) (interface{}, error) { - tx, blockHash, blockNumber, index, err := api.backend.GetTransaction(ctx, hash) + found, _, blockHash, blockNumber, index, err := api.backend.GetTransaction(ctx, hash) if err != nil { - return nil, err + return nil, ethapi.NewTxIndexingError() } // Only mined txes are supported - if tx == nil { + if !found { return nil, errTxNotFound } // It shouldn't happen in practice. @@ -975,7 +975,7 @@ func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, bloc } } // Execute the trace - msg, err := args.ToMessage(api.backend.RPCGasCap(), block.BaseFee()) + msg, err := args.ToMessage(api.backend.RPCGasCap(), vmctx.BaseFee) if err != nil { return nil, err } diff --git a/eth/tracers/api_extra_test.go b/eth/tracers/api_extra_test.go index f5e07bfa27..06bbfa5f77 100644 --- a/eth/tracers/api_extra_test.go +++ b/eth/tracers/api_extra_test.go @@ -33,7 +33,7 @@ func TestTraceBlockPrecompileActivation(t *testing.T) { copyConfig := *params.TestChainConfig genesis := &core.Genesis{ Config: ©Config, - Alloc: core.GenesisAlloc{ + Alloc: types.GenesisAlloc{ accounts[0].addr: {Balance: big.NewInt(params.Ether)}, accounts[1].addr: {Balance: big.NewInt(params.Ether)}, accounts[2].addr: {Balance: big.NewInt(params.Ether)}, @@ -139,7 +139,7 @@ func TestTraceTransactionPrecompileActivation(t *testing.T) { copyConfig := *params.TestChainConfig genesis := &core.Genesis{ Config: ©Config, - Alloc: core.GenesisAlloc{ + Alloc: types.GenesisAlloc{ accounts[0].addr: {Balance: big.NewInt(params.Ether)}, accounts[1].addr: {Balance: big.NewInt(params.Ether)}, accounts[2].addr: {Balance: big.NewInt(params.Ether)}, @@ -203,7 +203,7 @@ func TestTraceChainPrecompileActivation(t *testing.T) { copyConfig := *params.TestChainConfig genesis := &core.Genesis{ Config: ©Config, - Alloc: core.GenesisAlloc{ + Alloc: types.GenesisAlloc{ accounts[0].addr: {Balance: big.NewInt(5 * params.Ether)}, accounts[1].addr: {Balance: big.NewInt(5 * params.Ether)}, accounts[2].addr: {Balance: big.NewInt(5 * params.Ether)}, @@ -300,7 +300,7 @@ func TestTraceCallWithOverridesStateUpgrade(t *testing.T) { copyConfig := *params.TestChainConfig genesis := &core.Genesis{ Config: ©Config, - Alloc: core.GenesisAlloc{ + Alloc: types.GenesisAlloc{ accounts[0].addr: {Balance: big.NewInt(5 * params.Ether)}, accounts[1].addr: {Balance: big.NewInt(5 * params.Ether)}, accounts[2].addr: {Balance: big.NewInt(5 * params.Ether)}, diff --git a/eth/tracers/api_test.go b/eth/tracers/api_test.go index 7b99d8e250..1f1b32492e 100644 --- a/eth/tracers/api_test.go +++ b/eth/tracers/api_test.go @@ -133,9 +133,9 @@ func (b *testBackend) BlockByNumber(ctx context.Context, number rpc.BlockNumber) func (b *testBackend) BadBlocks() ([]*types.Block, []*core.BadBlockReason) { return nil, nil } -func (b *testBackend) GetTransaction(ctx context.Context, txHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64, error) { +func (b *testBackend) GetTransaction(ctx context.Context, txHash common.Hash) (bool, *types.Transaction, common.Hash, uint64, uint64, error) { tx, hash, blockNumber, index := rawdb.ReadTransaction(b.chaindb, txHash) - return tx, hash, blockNumber, index, nil + return tx != nil, tx, hash, blockNumber, index, nil } func (b *testBackend) RPCGasCap() uint64 { @@ -227,7 +227,7 @@ func TestTraceCall(t *testing.T) { accounts := newAccounts(3) genesis := &core.Genesis{ Config: params.TestChainConfig, - Alloc: core.GenesisAlloc{ + Alloc: types.GenesisAlloc{ accounts[0].addr: {Balance: big.NewInt(params.Ether)}, accounts[1].addr: {Balance: big.NewInt(params.Ether)}, accounts[2].addr: {Balance: big.NewInt(params.Ether)}, @@ -445,7 +445,7 @@ func TestTraceTransaction(t *testing.T) { accounts := newAccounts(2) genesis := &core.Genesis{ Config: params.TestChainConfig, - Alloc: core.GenesisAlloc{ + Alloc: types.GenesisAlloc{ accounts[0].addr: {Balance: big.NewInt(params.Ether)}, accounts[1].addr: {Balance: big.NewInt(params.Ether)}, }, @@ -501,7 +501,7 @@ func TestTraceBlock(t *testing.T) { accounts := newAccounts(3) genesis := &core.Genesis{ Config: params.TestChainConfig, - Alloc: core.GenesisAlloc{ + Alloc: types.GenesisAlloc{ accounts[0].addr: {Balance: big.NewInt(params.Ether)}, accounts[1].addr: {Balance: big.NewInt(params.Ether)}, accounts[2].addr: {Balance: big.NewInt(params.Ether)}, @@ -591,7 +591,7 @@ func TestTracingWithOverrides(t *testing.T) { storageAccount := common.Address{0x13, 37} genesis := &core.Genesis{ Config: params.TestChainConfig, - Alloc: core.GenesisAlloc{ + Alloc: types.GenesisAlloc{ accounts[0].addr: {Balance: big.NewInt(params.Ether)}, accounts[1].addr: {Balance: big.NewInt(params.Ether)}, accounts[2].addr: {Balance: big.NewInt(params.Ether)}, @@ -961,7 +961,7 @@ func TestTraceChain(t *testing.T) { accounts := newAccounts(3) genesis := &core.Genesis{ Config: params.TestChainConfig, - Alloc: core.GenesisAlloc{ + Alloc: types.GenesisAlloc{ accounts[0].addr: {Balance: big.NewInt(5 * params.Ether)}, accounts[1].addr: {Balance: big.NewInt(5 * params.Ether)}, accounts[2].addr: {Balance: big.NewInt(5 * params.Ether)}, diff --git a/eth/tracers/internal/tracetest/calltrace_test.go b/eth/tracers/internal/tracetest/calltrace_test.go index 6c2aa1ab89..f2cbeed762 100644 --- a/eth/tracers/internal/tracetest/calltrace_test.go +++ b/eth/tracers/internal/tracetest/calltrace_test.go @@ -132,12 +132,7 @@ func testCallTracer(tracerName string, dirPath string, t *testing.T) { } // Configure a blockchain with the given prestate var ( - signer = types.MakeSigner(test.Genesis.Config, new(big.Int).SetUint64(uint64(test.Context.Number)), uint64(test.Context.Time)) - origin, _ = signer.Sender(tx) - txContext = vm.TxContext{ - Origin: origin, - GasPrice: tx.GasPrice(), - } + signer = types.MakeSigner(test.Genesis.Config, new(big.Int).SetUint64(uint64(test.Context.Number)), uint64(test.Context.Time)) context = vm.BlockContext{ CanTransfer: core.CanTransfer, Transfer: core.Transfer, @@ -148,19 +143,19 @@ func testCallTracer(tracerName string, dirPath string, t *testing.T) { GasLimit: uint64(test.Context.GasLimit), BaseFee: test.Genesis.BaseFee, } - triedb, _, statedb = tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc, false, rawdb.HashScheme) + state = tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc, false, rawdb.HashScheme) ) - triedb.Close() + state.Close() tracer, err := tracers.DefaultDirectory.New(tracerName, new(tracers.Context), test.TracerConfig) if err != nil { t.Fatalf("failed to create call tracer: %v", err) } - evm := vm.NewEVM(context, txContext, statedb, test.Genesis.Config, vm.Config{Tracer: tracer}) - msg, err := core.TransactionToMessage(tx, signer, nil) + msg, err := core.TransactionToMessage(tx, signer, context.BaseFee) if err != nil { t.Fatalf("failed to prepare transaction for tracing: %v", err) } + evm := vm.NewEVM(context, core.NewEVMTxContext(msg), state.StateDB, test.Genesis.Config, vm.Config{Tracer: tracer}) vmRet, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(tx.Gas())) if err != nil { t.Fatalf("failed to execute transaction: %v", err) @@ -232,10 +227,6 @@ func benchTracer(tracerName string, test *callTracerTest, b *testing.B) { b.Fatalf("failed to parse testcase input: %v", err) } signer := types.MakeSigner(test.Genesis.Config, new(big.Int).SetUint64(uint64(test.Context.Number)), uint64(test.Context.Time)) - msg, err := core.TransactionToMessage(tx, signer, nil) - if err != nil { - b.Fatalf("failed to prepare transaction for tracing: %v", err) - } origin, _ := signer.Sender(tx) txContext := vm.TxContext{ Origin: origin, @@ -250,8 +241,12 @@ func benchTracer(tracerName string, test *callTracerTest, b *testing.B) { Difficulty: (*big.Int)(test.Context.Difficulty), GasLimit: uint64(test.Context.GasLimit), } - triedb, _, statedb := tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc, false, rawdb.HashScheme) - defer triedb.Close() + msg, err := core.TransactionToMessage(tx, signer, context.BaseFee) + if err != nil { + b.Fatalf("failed to prepare transaction for tracing: %v", err) + } + state := tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc, false, rawdb.HashScheme) + defer state.Close() b.ReportAllocs() b.ResetTimer() @@ -260,8 +255,8 @@ func benchTracer(tracerName string, test *callTracerTest, b *testing.B) { if err != nil { b.Fatalf("failed to create call tracer: %v", err) } - evm := vm.NewEVM(context, txContext, statedb, test.Genesis.Config, vm.Config{Tracer: tracer}) - snap := statedb.Snapshot() + evm := vm.NewEVM(context, txContext, state.StateDB, test.Genesis.Config, vm.Config{Tracer: tracer}) + snap := state.StateDB.Snapshot() st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas())) if _, err = st.TransitionDb(); err != nil { b.Fatalf("failed to execute transaction: %v", err) @@ -269,7 +264,7 @@ func benchTracer(tracerName string, test *callTracerTest, b *testing.B) { if _, err = tracer.GetResult(); err != nil { b.Fatal(err) } - statedb.RevertToSnapshot(snap) + state.StateDB.RevertToSnapshot(snap) } } @@ -377,18 +372,18 @@ func TestInternals(t *testing.T) { }, } { t.Run(tc.name, func(t *testing.T) { - triedb, _, statedb := tests.MakePreState(rawdb.NewMemoryDatabase(), - core.GenesisAlloc{ - to: core.GenesisAccount{ + state := tests.MakePreState(rawdb.NewMemoryDatabase(), + types.GenesisAlloc{ + to: types.Account{ Code: tc.code, }, - origin: core.GenesisAccount{ + origin: types.Account{ Balance: big.NewInt(500000000000000), }, }, false, rawdb.HashScheme) - defer triedb.Close() + defer state.Close() - evm := vm.NewEVM(context, txContext, statedb, params.TestPreSubnetEVMChainConfig, vm.Config{Tracer: tc.tracer}) + evm := vm.NewEVM(context, txContext, state.StateDB, params.TestPreSubnetEVMChainConfig, vm.Config{Tracer: tc.tracer}) msg := &core.Message{ To: &to, From: origin, diff --git a/eth/tracers/internal/tracetest/flat_calltrace_test.go b/eth/tracers/internal/tracetest/flat_calltrace_test.go index 3afa5d3080..93b85735e6 100644 --- a/eth/tracers/internal/tracetest/flat_calltrace_test.go +++ b/eth/tracers/internal/tracetest/flat_calltrace_test.go @@ -96,11 +96,6 @@ func flatCallTracerTestRunner(tracerName string, filename string, dirPath string return fmt.Errorf("failed to parse testcase input: %v", err) } signer := types.MakeSigner(test.Genesis.Config, new(big.Int).SetUint64(uint64(test.Context.Number)), uint64(test.Context.Time)) - origin, _ := signer.Sender(tx) - txContext := vm.TxContext{ - Origin: origin, - GasPrice: tx.GasPrice(), - } context := vm.BlockContext{ CanTransfer: core.CanTransfer, Transfer: core.Transfer, @@ -110,20 +105,19 @@ func flatCallTracerTestRunner(tracerName string, filename string, dirPath string Difficulty: (*big.Int)(test.Context.Difficulty), GasLimit: uint64(test.Context.GasLimit), } - triedb, _, statedb := tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc, false, rawdb.HashScheme) - defer triedb.Close() + state := tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc, false, rawdb.HashScheme) + defer state.Close() // Create the tracer, the EVM environment and run it tracer, err := tracers.DefaultDirectory.New(tracerName, new(tracers.Context), test.TracerConfig) if err != nil { return fmt.Errorf("failed to create call tracer: %v", err) } - evm := vm.NewEVM(context, txContext, statedb, test.Genesis.Config, vm.Config{Tracer: tracer}) - - msg, err := core.TransactionToMessage(tx, signer, nil) + msg, err := core.TransactionToMessage(tx, signer, context.BaseFee) if err != nil { return fmt.Errorf("failed to prepare transaction for tracing: %v", err) } + evm := vm.NewEVM(context, core.NewEVMTxContext(msg), state.StateDB, test.Genesis.Config, vm.Config{Tracer: tracer}) st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas())) if _, err = st.TransitionDb(); err != nil { diff --git a/eth/tracers/internal/tracetest/prestate_test.go b/eth/tracers/internal/tracetest/prestate_test.go index 98a18aadba..ef7c5cfd20 100644 --- a/eth/tracers/internal/tracetest/prestate_test.go +++ b/eth/tracers/internal/tracetest/prestate_test.go @@ -102,12 +102,7 @@ func testPrestateDiffTracer(tracerName string, dirPath string, t *testing.T) { } // Configure a blockchain with the given prestate var ( - signer = types.MakeSigner(test.Genesis.Config, new(big.Int).SetUint64(uint64(test.Context.Number)), uint64(test.Context.Time)) - origin, _ = signer.Sender(tx) - txContext = vm.TxContext{ - Origin: origin, - GasPrice: tx.GasPrice(), - } + signer = types.MakeSigner(test.Genesis.Config, new(big.Int).SetUint64(uint64(test.Context.Number)), uint64(test.Context.Time)) context = vm.BlockContext{ CanTransfer: core.CanTransfer, Transfer: core.Transfer, @@ -118,19 +113,19 @@ func testPrestateDiffTracer(tracerName string, dirPath string, t *testing.T) { GasLimit: uint64(test.Context.GasLimit), BaseFee: test.Genesis.BaseFee, } - triedb, _, statedb = tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc, false, rawdb.HashScheme) + state = tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc, false, rawdb.HashScheme) ) - defer triedb.Close() + defer state.Close() tracer, err := tracers.DefaultDirectory.New(tracerName, new(tracers.Context), test.TracerConfig) if err != nil { t.Fatalf("failed to create call tracer: %v", err) } - evm := vm.NewEVM(context, txContext, statedb, test.Genesis.Config, vm.Config{Tracer: tracer}) - msg, err := core.TransactionToMessage(tx, signer, nil) + msg, err := core.TransactionToMessage(tx, signer, context.BaseFee) if err != nil { t.Fatalf("failed to prepare transaction for tracing: %v", err) } + evm := vm.NewEVM(context, core.NewEVMTxContext(msg), state.StateDB, test.Genesis.Config, vm.Config{Tracer: tracer}) st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas())) if _, err = st.TransitionDb(); err != nil { t.Fatalf("failed to execute transaction: %v", err) diff --git a/eth/tracers/internal/tracetest/testdata/prestate_tracer_with_diff_mode/create_failed.json b/eth/tracers/internal/tracetest/testdata/prestate_tracer_with_diff_mode/create_failed.json index 8a8786c37d..192a38c59a 100644 --- a/eth/tracers/internal/tracetest/testdata/prestate_tracer_with_diff_mode/create_failed.json +++ b/eth/tracers/internal/tracetest/testdata/prestate_tracer_with_diff_mode/create_failed.json @@ -83,11 +83,11 @@ }, "post": { "0x808b4da0be6c9512e948521452227efc619bea52": { - "balance": "0x2cd72a36dd031f089", + "balance": "0x2cd987071ba2346b6", "nonce": 1223933 }, "0x8f03f1a3f10c05e7cccf75c1fd10168e06659be7": { - "balance": "0x3807e1f151a8123ec8" + "balance": "0x3807bc244dbe20e89b" } } } diff --git a/eth/tracers/js/internal/tracers/call_tracer_legacy.js b/eth/tracers/js/internal/tracers/call_tracer_legacy.js index 451a644b91..0760bb1e3f 100644 --- a/eth/tracers/js/internal/tracers/call_tracer_legacy.js +++ b/eth/tracers/js/internal/tracers/call_tracer_legacy.js @@ -219,7 +219,7 @@ return this.finalize(result); }, - // finalize recreates a call object using the final desired field oder for json + // finalize recreates a call object using the final desired field order for json // serialization. This is a nicety feature to pass meaningfully ordered results // to users who don't interpret it, just display it. finalize: function(call) { diff --git a/eth/tracers/js/tracer_test.go b/eth/tracers/js/tracer_test.go index a1eafddb14..d2fb8a4cf3 100644 --- a/eth/tracers/js/tracer_test.go +++ b/eth/tracers/js/tracer_test.go @@ -39,6 +39,7 @@ import ( "github.com/ava-labs/subnet-evm/eth/tracers" "github.com/ava-labs/subnet-evm/params" "github.com/ethereum/go-ethereum/common" + "github.com/holiman/uint256" ) type account struct{} @@ -47,9 +48,9 @@ func (account) SubBalance(amount *big.Int) {} func (account) AddBalance(amount *big.Int) {} func (account) SetAddress(common.Address) {} func (account) Value() *big.Int { return nil } -func (account) SetBalance(*big.Int) {} +func (account) SetBalance(*uint256.Int) {} func (account) SetNonce(uint64) {} -func (account) Balance() *big.Int { return nil } +func (account) Balance() *uint256.Int { return nil } func (account) Address() common.Address { return common.Address{} } func (account) SetCode(common.Hash, []byte) {} func (account) ForEachStorage(cb func(key, value common.Hash) bool) {} @@ -58,8 +59,8 @@ type dummyStatedb struct { state.StateDB } -func (*dummyStatedb) GetRefund() uint64 { return 1337 } -func (*dummyStatedb) GetBalance(addr common.Address) *big.Int { return new(big.Int) } +func (*dummyStatedb) GetRefund() uint64 { return 1337 } +func (*dummyStatedb) GetBalance(addr common.Address) *uint256.Int { return new(uint256.Int) } type vmContext struct { blockCtx vm.BlockContext @@ -75,7 +76,7 @@ func runTrace(tracer tracers.Tracer, vmctx *vmContext, chaincfg *params.ChainCon env = vm.NewEVM(vmctx.blockCtx, vmctx.txCtx, &dummyStatedb{}, chaincfg, vm.Config{Tracer: tracer}) gasLimit uint64 = 31000 startGas uint64 = 10000 - value = big.NewInt(0) + value = uint256.NewInt(0) contract = vm.NewContract(account{}, account{}, value, startGas) ) contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x1, 0x0} @@ -84,7 +85,7 @@ func runTrace(tracer tracers.Tracer, vmctx *vmContext, chaincfg *params.ChainCon } tracer.CaptureTxStart(gasLimit) - tracer.CaptureStart(env, contract.Caller(), contract.Address(), false, []byte{}, startGas, value) + tracer.CaptureStart(env, contract.Caller(), contract.Address(), false, []byte{}, startGas, value.ToBig()) ret, err := env.Interpreter().Run(contract, []byte{}, false) tracer.CaptureEnd(ret, startGas-contract.Gas, err) // Rest gas assumes no refund @@ -192,7 +193,7 @@ func TestHaltBetweenSteps(t *testing.T) { } env := vm.NewEVM(vm.BlockContext{BlockNumber: big.NewInt(1)}, vm.TxContext{GasPrice: big.NewInt(1)}, &dummyStatedb{}, params.TestChainConfig, vm.Config{Tracer: tracer}) scope := &vm.ScopeContext{ - Contract: vm.NewContract(&account{}, &account{}, big.NewInt(0), 0), + Contract: vm.NewContract(&account{}, &account{}, uint256.NewInt(0), 0), } tracer.CaptureStart(env, common.Address{}, common.Address{}, false, []byte{}, 0, big.NewInt(0)) tracer.CaptureState(0, 0, 0, 0, scope, nil, 0, nil) @@ -282,7 +283,7 @@ func TestEnterExit(t *testing.T) { t.Fatal(err) } scope := &vm.ScopeContext{ - Contract: vm.NewContract(&account{}, &account{}, big.NewInt(0), 0), + Contract: vm.NewContract(&account{}, &account{}, uint256.NewInt(0), 0), } tracer.CaptureEnter(vm.CALL, scope.Contract.Caller(), scope.Contract.Address(), []byte{}, 1000, new(big.Int)) tracer.CaptureExit([]byte{}, 400, nil) diff --git a/eth/tracers/logger/logger_test.go b/eth/tracers/logger/logger_test.go index 7c675f3666..f859d35f9b 100644 --- a/eth/tracers/logger/logger_test.go +++ b/eth/tracers/logger/logger_test.go @@ -27,6 +27,7 @@ import ( "github.com/ava-labs/subnet-evm/core/vm" "github.com/ava-labs/subnet-evm/params" "github.com/ethereum/go-ethereum/common" + "github.com/holiman/uint256" ) type dummyContractRef struct { @@ -50,7 +51,7 @@ func TestStoreCapture(t *testing.T) { logger = NewStructLogger(nil) statedb, _ = state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) env = vm.NewEVM(vm.BlockContext{}, vm.TxContext{}, statedb, params.TestChainConfig, vm.Config{Tracer: logger}) - contract = vm.NewContract(&dummyContractRef{}, &dummyContractRef{}, new(big.Int), 100000) + contract = vm.NewContract(&dummyContractRef{}, &dummyContractRef{}, new(uint256.Int), 100000) ) contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x0, byte(vm.SSTORE)} var index common.Hash diff --git a/eth/tracers/native/call.go b/eth/tracers/native/call.go index 42ec4b74a3..d2e8a09b09 100644 --- a/eth/tracers/native/call.go +++ b/eth/tracers/native/call.go @@ -172,7 +172,7 @@ func (t *callTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, sco return } // Avoid processing nested calls when only caring about top call - if t.config.OnlyTopCall && depth > 0 { + if t.config.OnlyTopCall && depth > 1 { return } // Skip if tracing was interrupted diff --git a/eth/tracers/native/prestate.go b/eth/tracers/native/prestate.go index b623797a60..602fbc4977 100644 --- a/eth/tracers/native/prestate.go +++ b/eth/tracers/native/prestate.go @@ -205,7 +205,7 @@ func (t *prestateTracer) CaptureTxEnd(restGas uint64) { } modified := false postAccount := &account{Storage: make(map[common.Hash]common.Hash)} - newBalance := t.env.StateDB.GetBalance(addr) + newBalance := t.env.StateDB.GetBalance(addr).ToBig() newNonce := t.env.StateDB.GetNonce(addr) newCode := t.env.StateDB.GetCode(addr) @@ -289,7 +289,7 @@ func (t *prestateTracer) lookupAccount(addr common.Address) { } t.pre[addr] = &account{ - Balance: t.env.StateDB.GetBalance(addr), + Balance: t.env.StateDB.GetBalance(addr).ToBig(), Nonce: t.env.StateDB.GetNonce(addr), Code: t.env.StateDB.GetCode(addr), Storage: make(map[common.Hash]common.Hash), diff --git a/eth/tracers/tracers_test.go b/eth/tracers/tracers_test.go index aa0166d38e..b69faf00c7 100644 --- a/eth/tracers/tracers_test.go +++ b/eth/tracers/tracers_test.go @@ -71,7 +71,7 @@ func BenchmarkTransactionTrace(b *testing.B) { GasLimit: gas, BaseFee: big.NewInt(8), } - alloc := core.GenesisAlloc{} + alloc := types.GenesisAlloc{} // The code pushes 'deadbeef' into memory, then the other params, and calls CREATE2, then returns // the address loop := []byte{ @@ -79,18 +79,18 @@ func BenchmarkTransactionTrace(b *testing.B) { byte(vm.PUSH1), 0, // jumpdestination byte(vm.JUMP), } - alloc[common.HexToAddress("0x00000000000000000000000000000000deadbeef")] = core.GenesisAccount{ + alloc[common.HexToAddress("0x00000000000000000000000000000000deadbeef")] = types.Account{ Nonce: 1, Code: loop, Balance: big.NewInt(1), } - alloc[from] = core.GenesisAccount{ + alloc[from] = types.Account{ Nonce: 1, Code: []byte{}, Balance: big.NewInt(500000000000000), } - triedb, _, statedb := tests.MakePreState(rawdb.NewMemoryDatabase(), alloc, false, rawdb.HashScheme) - defer triedb.Close() + state := tests.MakePreState(rawdb.NewMemoryDatabase(), alloc, false, rawdb.HashScheme) + defer state.Close() // Create the tracer, the EVM environment and run it tracer := logger.NewStructLogger(&logger.Config{ @@ -99,8 +99,8 @@ func BenchmarkTransactionTrace(b *testing.B) { //EnableMemory: false, //EnableReturnData: false, }) - evm := vm.NewEVM(context, txContext, statedb, params.TestChainConfig, vm.Config{Tracer: tracer}) - msg, err := core.TransactionToMessage(tx, signer, nil) + evm := vm.NewEVM(context, txContext, state.StateDB, params.TestChainConfig, vm.Config{Tracer: tracer}) + msg, err := core.TransactionToMessage(tx, signer, context.BaseFee) if err != nil { b.Fatalf("failed to prepare transaction for tracing: %v", err) } @@ -108,13 +108,13 @@ func BenchmarkTransactionTrace(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { - snap := statedb.Snapshot() + snap := state.StateDB.Snapshot() st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas())) _, err = st.TransitionDb() if err != nil { b.Fatal(err) } - statedb.RevertToSnapshot(snap) + state.StateDB.RevertToSnapshot(snap) if have, want := len(tracer.StructLogs()), 244752; have != want { b.Fatalf("trace wrong, want %d steps, have %d", want, have) } @@ -134,9 +134,9 @@ func TestMemCopying(t *testing.T) { {0, 100, 0, "", 0}, // No need to pad (0 size) {100, 50, 100, "", 100}, // Should pad 100-150 {100, 50, 5, "", 5}, // Wanted range fully within memory - {100, -50, 0, "offset or size must not be negative", 0}, // Errror - {0, 1, 1024*1024 + 1, "reached limit for padding memory slice: 1048578", 0}, // Errror - {10, 0, 1024*1024 + 100, "reached limit for padding memory slice: 1048666", 0}, // Errror + {100, -50, 0, "offset or size must not be negative", 0}, // Error + {0, 1, 1024*1024 + 1, "reached limit for padding memory slice: 1048578", 0}, // Error + {10, 0, 1024*1024 + 100, "reached limit for padding memory slice: 1048666", 0}, // Error } { mem := vm.NewMemory() diff --git a/ethclient/ethclient.go b/ethclient/ethclient.go index c7f0593fc7..87d608f030 100644 --- a/ethclient/ethclient.go +++ b/ethclient/ethclient.go @@ -395,10 +395,8 @@ func (ec *client) TransactionInBlock(ctx context.Context, blockHash common.Hash, func (ec *client) TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) { var r *types.Receipt err := ec.c.CallContext(ctx, &r, "eth_getTransactionReceipt", txHash) - if err == nil { - if r == nil { - return nil, interfaces.NotFound - } + if err == nil && r == nil { + return nil, interfaces.NotFound } return r, err } diff --git a/ethclient/simulated/backend.go b/ethclient/simulated/backend.go new file mode 100644 index 0000000000..9d53ed79bd --- /dev/null +++ b/ethclient/simulated/backend.go @@ -0,0 +1,279 @@ +// Copyright 2023 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 simulated + +import ( + "errors" + "math/big" + "time" + + "github.com/ava-labs/avalanchego/utils/timer/mockable" + "github.com/ava-labs/subnet-evm/consensus/dummy" + "github.com/ava-labs/subnet-evm/constants" + "github.com/ava-labs/subnet-evm/core" + "github.com/ava-labs/subnet-evm/core/rawdb" + "github.com/ava-labs/subnet-evm/core/types" + "github.com/ava-labs/subnet-evm/eth" + "github.com/ava-labs/subnet-evm/eth/ethconfig" + "github.com/ava-labs/subnet-evm/ethclient" + "github.com/ava-labs/subnet-evm/interfaces" + "github.com/ava-labs/subnet-evm/node" + "github.com/ava-labs/subnet-evm/params" + "github.com/ava-labs/subnet-evm/rpc" + "github.com/ethereum/go-ethereum/common" +) + +var _ eth.PushGossiper = (*fakePushGossiper)(nil) + +type fakePushGossiper struct{} + +func (*fakePushGossiper) Add(*types.Transaction) {} + +// Client exposes the methods provided by the Ethereum RPC client. +type Client interface { + interfaces.BlockNumberReader + interfaces.ChainReader + interfaces.ChainStateReader + interfaces.ContractCaller + interfaces.GasEstimator + interfaces.GasPricer + interfaces.GasPricer1559 + interfaces.FeeHistoryReader + interfaces.LogFilterer + interfaces.AcceptedStateReader + interfaces.AcceptedContractCaller + interfaces.TransactionReader + interfaces.TransactionSender + interfaces.ChainIDReader +} + +// simClient wraps ethclient. This exists to prevent extracting ethclient.Client +// from the Client interface returned by Backend. +type simClient struct { + ethclient.Client +} + +// Backend is a simulated blockchain. You can use it to test your contracts or +// other code that interacts with the Ethereum chain. +type Backend struct { + eth *eth.Ethereum + client simClient + clock *mockable.Clock + server *rpc.Server +} + +// NewBackend creates a new simulated blockchain that can be used as a backend for +// contract bindings in unit tests. +// +// A simulated backend always uses chainID 1337. +func NewBackend(alloc types.GenesisAlloc, options ...func(nodeConf *node.Config, ethConf *ethconfig.Config)) *Backend { + chainConfig := *params.TestChainConfig + chainConfig.ChainID = big.NewInt(1337) + + // Create the default configurations for the outer node shell and the Ethereum + // service to mutate with the options afterwards + nodeConf := node.DefaultConfig + + ethConf := ethconfig.DefaultConfig + ethConf.Genesis = &core.Genesis{ + Config: &chainConfig, + GasLimit: chainConfig.FeeConfig.GasLimit.Uint64(), + Alloc: alloc, + } + ethConf.AllowUnfinalizedQueries = true + ethConf.Miner.Etherbase = constants.BlackholeAddr + ethConf.Miner.TestOnlyAllowDuplicateBlocks = true + ethConf.TxPool.NoLocals = true + + for _, option := range options { + option(&nodeConf, ðConf) + } + // Assemble the Ethereum stack to run the chain with + stack, err := node.New(&nodeConf) + if err != nil { + panic(err) // this should never happen + } + sim, err := newWithNode(stack, ðConf, 0) + if err != nil { + panic(err) // this should never happen + } + return sim +} + +// newWithNode sets up a simulated backend on an existing node. The provided node +// must not be started and will be started by this method. +func newWithNode(stack *node.Node, conf *eth.Config, blockPeriod uint64) (*Backend, error) { + chaindb := rawdb.NewMemoryDatabase() + clock := &mockable.Clock{} + clock.Set(time.Unix(0, 0)) + + engine := dummy.NewFakerWithModeAndClock( + dummy.Mode{ModeSkipCoinbase: true}, clock, + ) + + backend, err := eth.New( + stack, conf, &fakePushGossiper{}, chaindb, eth.Settings{}, common.Hash{}, + engine, clock, + ) + if err != nil { + return nil, err + } + server := rpc.NewServer(0) + for _, api := range backend.APIs() { + if err := server.RegisterName(api.Namespace, api.Service); err != nil { + return nil, err + } + } + return &Backend{ + eth: backend, + client: simClient{ethclient.NewClient(rpc.DialInProc(server))}, + clock: clock, + server: server, + }, nil +} + +// Close shuts down the simBackend. +// The simulated backend can't be used afterwards. +func (n *Backend) Close() error { + if n.client.Client != nil { + n.client.Close() + } + n.server.Stop() + return nil +} + +// Commit seals a block and moves the chain forward to a new empty block. +func (n *Backend) Commit(accept bool) common.Hash { + hash, err := n.buildBlock(accept, 10) + if err != nil { + panic(err) + } + return hash +} + +func (n *Backend) buildBlock(accept bool, gap uint64) (common.Hash, error) { + chain := n.eth.BlockChain() + parent := chain.CurrentBlock() + + if err := n.eth.TxPool().Sync(); err != nil { + return common.Hash{}, err + } + + n.clock.Set(time.Unix(int64(parent.Time+gap), 0)) + block, err := n.eth.Miner().GenerateBlock(nil) + if err != nil { + return common.Hash{}, err + } + if err := chain.InsertBlock(block); err != nil { + return common.Hash{}, err + } + if accept { + if err := n.acceptAncestors(block); err != nil { + return common.Hash{}, err + } + chain.DrainAcceptorQueue() + } + return block.Hash(), nil +} + +func (n *Backend) acceptAncestors(block *types.Block) error { + chain := n.eth.BlockChain() + lastAccepted := chain.LastConsensusAcceptedBlock() + + // Accept all ancestors of the block + toAccept := []*types.Block{block} + for block.ParentHash() != lastAccepted.Hash() { + block = chain.GetBlockByHash(block.ParentHash()) + toAccept = append(toAccept, block) + if block.NumberU64() < lastAccepted.NumberU64() { + return errors.New("last accepted must be an ancestor of the block to accept") + } + } + + for i := len(toAccept) - 1; i >= 0; i-- { + if err := chain.Accept(toAccept[i]); err != nil { + return err + } + } + return nil +} + +// Rollback removes all pending transactions, reverting to the last committed state. +func (n *Backend) Rollback() { + // Flush all transactions from the transaction pools + maxUint256 := new(big.Int).Sub(new(big.Int).Lsh(common.Big1, 256), common.Big1) + original := n.eth.TxPool().GasTip() + n.eth.TxPool().SetGasTip(maxUint256) + n.eth.TxPool().SetGasTip(original) +} + +// Fork creates a side-chain that can be used to simulate reorgs. +// +// This function should be called with the ancestor block where the new side +// chain should be started. Transactions (old and new) can then be applied on +// top and Commit-ed. +// +// Note, the side-chain will only become canonical (and trigger the events) when +// it becomes longer. Until then CallContract will still operate on the current +// canonical chain. +// +// There is a % chance that the side chain becomes canonical at the same length +// to simulate live network behavior. +func (n *Backend) Fork(parentHash common.Hash) error { + chain := n.eth.BlockChain() + + if chain.CurrentBlock().Hash() == parentHash { + return nil + } + + parent := chain.GetBlockByHash(parentHash) + if parent == nil { + return errors.New("parent block not found") + } + + ch := make(chan core.NewTxPoolReorgEvent, 1) + sub := n.eth.TxPool().SubscribeNewReorgEvent(ch) + defer sub.Unsubscribe() + + if err := n.eth.BlockChain().SetPreference(parent); err != nil { + return err + } + for { + select { + case reorg := <-ch: + // Wait for tx pool to reorg, then flush the tx pool + if reorg.Head.Hash() == parent.Hash() { + n.Rollback() + return nil + } + case <-time.After(2 * time.Second): + return errors.New("fork not accepted") + } + } +} + +// AdjustTime changes the block timestamp and creates a new block. +// It can only be called on empty blocks. +func (n *Backend) AdjustTime(adjustment time.Duration) error { + _, err := n.buildBlock(false, uint64(adjustment)) + return err +} + +// Client returns a client that accesses the simulated chain. +func (n *Backend) Client() Client { + return n.client +} diff --git a/ethclient/simulated/backend_test.go b/ethclient/simulated/backend_test.go new file mode 100644 index 0000000000..080bbb6960 --- /dev/null +++ b/ethclient/simulated/backend_test.go @@ -0,0 +1,318 @@ +// Copyright 2019 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 simulated + +import ( + "context" + "crypto/ecdsa" + "math/big" + "math/rand" + "testing" + "time" + + "github.com/ava-labs/subnet-evm/accounts/abi/bind" + "github.com/ava-labs/subnet-evm/core/types" + "github.com/ava-labs/subnet-evm/params" + "github.com/ava-labs/subnet-evm/rpc" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/stretchr/testify/require" +) + +var _ bind.ContractBackend = (Client)(nil) + +var ( + testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + testAddr = crypto.PubkeyToAddress(testKey.PublicKey) +) + +func simTestBackend(testAddr common.Address) *Backend { + return NewBackend( + types.GenesisAlloc{ + testAddr: {Balance: big.NewInt(10000000000000000)}, + }, + ) +} + +func newTx(sim *Backend, key *ecdsa.PrivateKey) (*types.Transaction, error) { + client := sim.Client() + + // 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)) + addr := crypto.PubkeyToAddress(key.PublicKey) + chainid, _ := client.ChainID(context.Background()) + nonce, err := client.NonceAt(context.Background(), addr, nil) + if err != nil { + return nil, err + } + tx := types.NewTx(&types.DynamicFeeTx{ + ChainID: chainid, + Nonce: nonce, + GasTipCap: big.NewInt(params.GWei), + GasFeeCap: gasPrice, + Gas: 21000, + To: &addr, + }) + return types.SignTx(tx, types.LatestSignerForChainID(chainid), key) +} + +func TestNewBackend(t *testing.T) { + sim := NewBackend(types.GenesisAlloc{}) + defer sim.Close() + + client := sim.Client() + num, err := client.BlockNumber(context.Background()) + if err != nil { + t.Fatal(err) + } + if num != 0 { + t.Fatalf("expected 0 got %v", num) + } + // Create a block + sim.Commit(true) + num, err = client.BlockNumber(context.Background()) + if err != nil { + t.Fatal(err) + } + if num != 1 { + t.Fatalf("expected 1 got %v", num) + } +} + +func TestAdjustTime(t *testing.T) { + sim := NewBackend(types.GenesisAlloc{}) + defer sim.Close() + + client := sim.Client() + block1, _ := client.BlockByNumber(context.Background(), nil) + + // Create a block + if err := sim.AdjustTime(time.Minute); err != nil { + t.Fatal(err) + } + block2, _ := client.BlockByNumber(context.Background(), nil) + prevTime := block1.Time() + newTime := block2.Time() + if newTime-prevTime != uint64(time.Minute) { + t.Errorf("adjusted time not equal to 60 seconds. prev: %v, new: %v", prevTime, newTime) + } +} + +func TestSendTransaction(t *testing.T) { + sim := simTestBackend(testAddr) + defer sim.Close() + + client := sim.Client() + ctx := context.Background() + + signedTx, err := newTx(sim, testKey) + if err != nil { + t.Errorf("could not create transaction: %v", err) + } + // send tx to simulated backend + err = client.SendTransaction(ctx, signedTx) + if err != nil { + t.Errorf("could not add tx to pending block: %v", err) + } + sim.Commit(false) + block, err := client.BlockByNumber(ctx, big.NewInt(1)) + if err != nil { + t.Errorf("could not get block at height 1: %v", err) + } + + if signedTx.Hash() != block.Transactions()[0].Hash() { + t.Errorf("did not commit sent transaction. expected hash %v got hash %v", block.Transactions()[0].Hash(), signedTx.Hash()) + } +} + +// TestFork check that the chain length after a reorg is correct. +// Steps: +// 1. Save the current block which will serve as parent for the fork. +// 2. Mine n blocks with n ∈ [0, 20]. +// 3. Assert that the chain length is n. +// 4. Fork by using the parent block as ancestor. +// 5. Mine n+1 blocks which should trigger a reorg. +// 6. Assert that the chain length is n+1. +// Since Commit() was called 2n+1 times in total, +// having a chain length of just n+1 means that a reorg occurred. +func TestFork(t *testing.T) { + t.Parallel() + testAddr := crypto.PubkeyToAddress(testKey.PublicKey) + sim := simTestBackend(testAddr) + defer sim.Close() + + client := sim.Client() + ctx := context.Background() + + // 1. + parent, _ := client.HeaderByNumber(ctx, nil) + + // 2. + n := int(rand.Int31n(21)) + for i := 0; i < n; i++ { + sim.Commit(false) + } + + // 3. + b, _ := client.BlockNumber(ctx) + if b != uint64(n) { + t.Error("wrong chain length") + } + + // 4. + sim.Fork(parent.Hash()) + + // 5. + for i := 0; i < n+1; i++ { + sim.Commit(false) + } + + // 6. + b, _ = client.BlockNumber(ctx) + if b != uint64(n+1) { + t.Error("wrong chain length") + } +} + +// TestForkResendTx checks that re-sending a TX after a fork +// is possible and does not cause a "nonce mismatch" panic. +// Steps: +// 1. Save the current block which will serve as parent for the fork. +// 2. Send a transaction. +// 3. Check that the TX is included in block 1. +// 4. Fork by using the parent block as ancestor. +// 5. Mine a block, Re-send the transaction and mine another one. +// 6. Check that the TX is now included in block 2. +func TestForkResendTx(t *testing.T) { + t.Parallel() + testAddr := crypto.PubkeyToAddress(testKey.PublicKey) + sim := simTestBackend(testAddr) + defer sim.Close() + + client := sim.Client() + ctx := context.Background() + + // 1. + parent, _ := client.HeaderByNumber(ctx, nil) + + // 2. + tx, err := newTx(sim, testKey) + if err != nil { + t.Fatalf("could not create transaction: %v", err) + } + client.SendTransaction(ctx, tx) + sim.Commit(false) + + // 3. + // Note this test is revised from upstream since we cannot get the receipt + // for a pending transaction. (Receipts only written for accepted blocks). + require := require.New(t) + pendingBlockNum := big.NewInt(int64(rpc.PendingBlockNumber)) + b1, err := client.BlockByNumber(ctx, pendingBlockNum) + require.NoError(err) + require.Len(b1.Transactions(), 1) + require.Equal(tx.Hash(), b1.Transactions()[0].Hash()) + require.Equal(uint64(1), b1.NumberU64()) + + // 4. + if err := sim.Fork(parent.Hash()); err != nil { + t.Errorf("forking: %v", err) + } + + // 5. + sim.Commit(false) + if err := client.SendTransaction(ctx, tx); err != nil { + t.Fatalf("sending transaction: %v", err) + } + sim.Commit(true) + receipt, _ := client.TransactionReceipt(ctx, tx.Hash()) + if h := receipt.BlockNumber.Uint64(); h != 2 { + t.Errorf("TX included in wrong block: %d", h) + } +} + +func TestCommitReturnValue(t *testing.T) { + t.Parallel() + testAddr := crypto.PubkeyToAddress(testKey.PublicKey) + sim := simTestBackend(testAddr) + defer sim.Close() + + client := sim.Client() + ctx := context.Background() + chainid, _ := client.ChainID(context.Background()) + + // Test if Commit returns the correct block hash + h1 := sim.Commit(false) + cur, _ := client.HeaderByNumber(ctx, nil) + if h1 != cur.Hash() { + t.Error("Commit did not return the hash of the last block.") + } + + // 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.SignTx(_tx, types.LatestSignerForChainID(chainid), testKey) + if err := client.SendTransaction(ctx, tx); err != nil { + t.Fatalf("sending transaction: %v", err) + } + + h2 := sim.Commit(false) + + // Create another block in the original chain + sim.Commit(false) + + // Fork at the first bock + if err := sim.Fork(h1); err != nil { + t.Errorf("forking: %v", err) + } + + // Test if Commit returns the correct block hash after the reorg + h2fork := sim.Commit(false) + if h2 == h2fork { + t.Error("The block in the fork and the original block are the same block!") + } + if header, err := client.HeaderByHash(ctx, h2fork); err != nil || header == nil { + t.Error("Could not retrieve the just created block (side-chain)") + } +} + +// TestAdjustTimeAfterFork ensures that after a fork, AdjustTime uses the pending fork +// block's parent rather than the canonical head's parent. +func TestAdjustTimeAfterFork(t *testing.T) { + t.Parallel() + testAddr := crypto.PubkeyToAddress(testKey.PublicKey) + sim := simTestBackend(testAddr) + defer sim.Close() + + client := sim.Client() + ctx := context.Background() + + sim.Commit(false) // h1 + h1, _ := client.HeaderByNumber(ctx, nil) + + sim.Commit(false) // h2 + sim.Fork(h1.Hash()) + sim.AdjustTime(1 * time.Second) + sim.Commit(false) + + head, _ := client.HeaderByNumber(ctx, nil) + if head.Number.Uint64() == 2 && head.ParentHash != h1.Hash() { + t.Errorf("failed to build block on fork") + } +} diff --git a/ethclient/simulated/options.go b/ethclient/simulated/options.go new file mode 100644 index 0000000000..8d5723c8d7 --- /dev/null +++ b/ethclient/simulated/options.go @@ -0,0 +1,41 @@ +// Copyright 2024 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 simulated + +import ( + "math/big" + + "github.com/ava-labs/subnet-evm/eth/ethconfig" + "github.com/ava-labs/subnet-evm/node" +) + +// WithBlockGasLimit configures the simulated backend to target a specific gas limit +// when producing blocks. +func WithBlockGasLimit(gaslimit uint64) func(nodeConf *node.Config, ethConf *ethconfig.Config) { + return func(nodeConf *node.Config, ethConf *ethconfig.Config) { + ethConf.Genesis.GasLimit = gaslimit + ethConf.Genesis.Config.FeeConfig.GasLimit = new(big.Int).SetUint64(gaslimit) + } +} + +// WithCallGasLimit configures the simulated backend to cap eth_calls to a specific +// gas limit when running client operations. +func WithCallGasLimit(gaslimit uint64) func(nodeConf *node.Config, ethConf *ethconfig.Config) { + return func(nodeConf *node.Config, ethConf *ethconfig.Config) { + ethConf.RPCGasCap = gaslimit + } +} diff --git a/ethclient/simulated/options_test.go b/ethclient/simulated/options_test.go new file mode 100644 index 0000000000..f57f215496 --- /dev/null +++ b/ethclient/simulated/options_test.go @@ -0,0 +1,74 @@ +// Copyright 2024 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 simulated + +import ( + "context" + "math/big" + "strings" + "testing" + + "github.com/ava-labs/subnet-evm/core" + "github.com/ava-labs/subnet-evm/core/types" + "github.com/ava-labs/subnet-evm/interfaces" + "github.com/ava-labs/subnet-evm/params" +) + +// Tests that the simulator starts with the initial gas limit in the genesis block, +// and that it keeps the same target value. +func TestWithBlockGasLimitOption(t *testing.T) { + // Construct a simulator, targeting a different gas limit + sim := NewBackend(types.GenesisAlloc{}, WithBlockGasLimit(12_345_678)) + defer sim.Close() + + client := sim.Client() + genesis, err := client.BlockByNumber(context.Background(), big.NewInt(0)) + if err != nil { + t.Fatalf("failed to retrieve genesis block: %v", err) + } + if genesis.GasLimit() != 12_345_678 { + t.Errorf("genesis gas limit mismatch: have %v, want %v", genesis.GasLimit(), 12_345_678) + } + // Produce a number of blocks and verify the locked in gas target + sim.Commit(false) + head, err := client.BlockByNumber(context.Background(), big.NewInt(1)) + if err != nil { + t.Fatalf("failed to retrieve head block: %v", err) + } + if head.GasLimit() != 12_345_678 { + t.Errorf("head gas limit mismatch: have %v, want %v", head.GasLimit(), 12_345_678) + } +} + +// Tests that the simulator honors the RPC call caps set by the options. +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)) + defer sim.Close() + + client := sim.Client() + _, err := client.CallContract(context.Background(), interfaces.CallMsg{ + From: testAddr, + To: &testAddr, + Gas: 21000, + }, nil) + if !strings.Contains(err.Error(), core.ErrIntrinsicGas.Error()) { + t.Fatalf("error mismatch: have %v, want %v", err, core.ErrIntrinsicGas) + } +} diff --git a/ethclient/subnetevmclient/subnet_evm_client.go b/ethclient/subnetevmclient/subnet_evm_client.go index 08cce85959..d5794c8164 100644 --- a/ethclient/subnetevmclient/subnet_evm_client.go +++ b/ethclient/subnetevmclient/subnet_evm_client.go @@ -197,6 +197,12 @@ func toCallArg(msg interfaces.CallMsg) interface{} { if msg.GasPrice != nil { arg["gasPrice"] = (*hexutil.Big)(msg.GasPrice) } + if msg.GasFeeCap != nil { + arg["maxFeePerGas"] = (*hexutil.Big)(msg.GasFeeCap) + } + if msg.GasTipCap != nil { + arg["maxPriorityFeePerGas"] = (*hexutil.Big)(msg.GasTipCap) + } if msg.AccessList != nil { arg["accessList"] = msg.AccessList } diff --git a/go.mod b/go.mod index 2e56608d7d..51fda3902c 100644 --- a/go.mod +++ b/go.mod @@ -11,8 +11,8 @@ require ( github.com/davecgh/go-spew v1.1.1 github.com/deckarep/golang-set/v2 v2.1.0 github.com/dop251/goja v0.0.0-20230806174421-c933cf95e127 - github.com/ethereum/go-ethereum v1.13.8 - github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 + github.com/ethereum/go-ethereum v1.13.14 + github.com/fjl/memsize v0.0.2 github.com/fsnotify/fsnotify v1.6.0 github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46 @@ -22,7 +22,7 @@ require ( github.com/gorilla/websocket v1.4.2 github.com/hashicorp/go-bexpr v0.1.10 github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d - github.com/holiman/billy v0.0.0-20230718173358-1c7e68d277a7 + github.com/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4 github.com/holiman/bloomfilter/v2 v2.0.3 github.com/holiman/uint256 v1.2.4 github.com/kylelemons/godebug v1.1.0 @@ -42,10 +42,10 @@ require ( github.com/urfave/cli/v2 v2.25.7 go.uber.org/goleak v1.3.0 go.uber.org/mock v0.4.0 - golang.org/x/crypto v0.21.0 + golang.org/x/crypto v0.22.0 golang.org/x/exp v0.0.0-20231127185646-65229373498e - golang.org/x/sync v0.6.0 - golang.org/x/sys v0.18.0 + golang.org/x/sync v0.7.0 + golang.org/x/sys v0.19.0 golang.org/x/text v0.14.0 golang.org/x/time v0.3.0 google.golang.org/protobuf v1.34.2 @@ -81,7 +81,7 @@ require ( github.com/getsentry/sentry-go v0.18.0 // indirect github.com/go-logr/logr v1.4.1 // indirect github.com/go-logr/stdr v1.2.2 // indirect - github.com/go-ole/go-ole v1.2.6 // indirect + github.com/go-ole/go-ole v1.3.0 // indirect github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/gogo/protobuf v1.3.2 // indirect @@ -141,9 +141,9 @@ require ( go.opentelemetry.io/proto/otlp v1.0.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.26.0 // indirect - golang.org/x/net v0.23.0 // indirect - golang.org/x/term v0.18.0 // indirect - golang.org/x/tools v0.17.0 // indirect + golang.org/x/net v0.24.0 // indirect + golang.org/x/term v0.19.0 // indirect + golang.org/x/tools v0.20.0 // indirect gonum.org/v1/gonum v0.11.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 // indirect diff --git a/go.sum b/go.sum index e4768cae01..2e295ef79a 100644 --- a/go.sum +++ b/go.sum @@ -180,12 +180,12 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7 github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= github.com/ethereum/c-kzg-4844 v0.4.0 h1:3MS1s4JtA868KpJxroZoepdV0ZKBp3u/O5HcZ7R3nlY= github.com/ethereum/c-kzg-4844 v0.4.0/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0= -github.com/ethereum/go-ethereum v1.13.8 h1:1od+thJel3tM52ZUNQwvpYOeRHlbkVFZ5S8fhi0Lgsg= -github.com/ethereum/go-ethereum v1.13.8/go.mod h1:sc48XYQxCzH3fG9BcrXCOOgQk2JfZzNAmIKnceogzsA= +github.com/ethereum/go-ethereum v1.13.14 h1:EwiY3FZP94derMCIam1iW4HFVrSgIcpsu0HwTQtm6CQ= +github.com/ethereum/go-ethereum v1.13.14/go.mod h1:TN8ZiHrdJwSe8Cb6x+p0hs5CxhJZPbqB7hHkaUXcmIU= github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= -github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 h1:FtmdgXiUlNeRsoNMFlKLDt+S+6hbjVMEW6RGQ7aUf7c= -github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= +github.com/fjl/memsize v0.0.2 h1:27txuSD9or+NZlnOWdKUxeBzTAUkWCVh+4Gf2dWFOzA= +github.com/fjl/memsize v0.0.2/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= @@ -218,8 +218,9 @@ github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ4 github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8= -github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= +github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= github.com/go-sourcemap/sourcemap v2.1.3+incompatible h1:W1iEw64niKVGogNgBN3ePyLFfuisuzeidWPMPWmECqU= github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= @@ -339,8 +340,8 @@ github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d h1:dg1dEPuW github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/holiman/billy v0.0.0-20230718173358-1c7e68d277a7 h1:3JQNjnMRil1yD0IfZKHF9GxxWKDJGj8I0IqOUol//sw= -github.com/holiman/billy v0.0.0-20230718173358-1c7e68d277a7/go.mod h1:5GuXa7vkL8u9FkFuWdVvfR5ix8hRB7DbOAaYULamFpc= +github.com/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4 h1:X4egAf/gcS1zATw6wn4Ej8vjuVGxeHdan+bRb2ebyv4= +github.com/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4/go.mod h1:5GuXa7vkL8u9FkFuWdVvfR5ix8hRB7DbOAaYULamFpc= github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA= github.com/holiman/uint256 v1.2.4 h1:jUc4Nk8fm9jZabQuqr2JzednajVmBpC+oiTiXZJEApU= @@ -645,8 +646,8 @@ golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= -golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= +golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= +golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -729,8 +730,8 @@ golang.org/x/net v0.0.0-20211008194852-3b03d305991f/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= -golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= +golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -752,8 +753,8 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= -golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -817,15 +818,16 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= -golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= -golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= +golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q= +golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -899,8 +901,8 @@ golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc= -golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps= +golang.org/x/tools v0.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY= +golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/interfaces/interfaces.go b/interfaces/interfaces.go index 1db99f4952..b234312baf 100644 --- a/interfaces/interfaces.go +++ b/interfaces/interfaces.go @@ -178,6 +178,16 @@ type GasPricer interface { SuggestGasPrice(ctx context.Context) (*big.Int, error) } +// GasPricer1559 provides access to the EIP-1559 gas price oracle. +type GasPricer1559 interface { + SuggestGasTipCap(ctx context.Context) (*big.Int, error) +} + +// FeeHistoryReader provides access to the fee history oracle. +type FeeHistoryReader interface { + FeeHistory(ctx context.Context, blockCount uint64, lastBlock *big.Int, rewardPercentiles []float64) (*FeeHistory, error) +} + // FeeHistory provides recent fee market data that consumers can use to determine // a reasonable maxPriorityFeePerGas value. type FeeHistory struct { @@ -212,3 +222,13 @@ type GasEstimator interface { type PendingStateEventer interface { SubscribePendingTransactions(ctx context.Context, ch chan<- *types.Transaction) (Subscription, error) } + +// BlockNumberReader provides access to the current block number. +type BlockNumberReader interface { + BlockNumber(ctx context.Context) (uint64, error) +} + +// ChainIDReader provides access to the chain ID. +type ChainIDReader interface { + ChainID(ctx context.Context) (*big.Int, error) +} diff --git a/internal/debug/flags.go b/internal/debug/flags.go index bf893293e1..34289ab921 100644 --- a/internal/debug/flags.go +++ b/internal/debug/flags.go @@ -176,22 +176,12 @@ var Flags = []cli.Flag{ } var ( - glogger *log.GlogHandler - logOutputFile io.WriteCloser - defaultTerminalHandler *log.TerminalHandler + glogger *log.GlogHandler + logOutputFile io.WriteCloser ) func init() { - defaultTerminalHandler = log.NewTerminalHandler(os.Stderr, false) - glogger = log.NewGlogHandler(defaultTerminalHandler) - glogger.Verbosity(log.LvlInfo) - log.SetDefault(log.NewLogger(glogger)) -} - -func ResetLogging() { - if defaultTerminalHandler != nil { - defaultTerminalHandler.ResetFieldPadding() - } + glogger = log.NewGlogHandler(log.NewTerminalHandler(os.Stderr, false)) } // Setup initializes profiling and logging based on the CLI flags. diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 00ad49e908..acd837ad33 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -55,6 +55,7 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/rlp" + "github.com/holiman/uint256" "github.com/tyler-smith/go-bip39" ) @@ -62,6 +63,8 @@ import ( // allowed to produce in order to speed up calculations. const estimateGasErrorRatio = 0.015 +var errBlobTxNotSupported = errors.New("signing blob transactions not supported") + // EthereumAPI provides an API to access Ethereum related information. type EthereumAPI struct { b Backend @@ -275,7 +278,7 @@ type PersonalAccountAPI struct { b Backend } -// NewPersonalAccountAPI create a new PersonalAccountAPI. +// NewPersonalAccountAPI creates a new PersonalAccountAPI. func NewPersonalAccountAPI(b Backend, nonceLock *AddrLocker) *PersonalAccountAPI { return &PersonalAccountAPI{ am: b.AccountManager(), @@ -440,7 +443,7 @@ func (s *PersonalAccountAPI) signTransaction(ctx context.Context, args *Transact return nil, err } // Set some sanity defaults and terminate on failure - if err := args.setDefaults(ctx, s.b); err != nil { + if err := args.setDefaults(ctx, s.b, false); err != nil { return nil, err } // Assemble the transaction and sign with the wallet @@ -459,6 +462,9 @@ func (s *PersonalAccountAPI) SendTransaction(ctx context.Context, args Transacti s.nonceLock.LockAddr(args.from()) defer s.nonceLock.UnlockAddr(args.from()) } + if args.IsEIP4844() { + return common.Hash{}, errBlobTxNotSupported + } signed, err := s.signTransaction(ctx, &args, passwd) if err != nil { log.Warn("Failed transaction send attempt", "from", args.from(), "to", args.To, "value", args.Value.ToInt(), "err", err) @@ -483,6 +489,9 @@ func (s *PersonalAccountAPI) SignTransaction(ctx context.Context, args Transacti if args.GasPrice == nil && (args.MaxFeePerGas == nil || args.MaxPriorityFeePerGas == nil) { return nil, errors.New("missing gasPrice or maxFeePerGas/maxPriorityFeePerGas") } + if args.IsEIP4844() { + return nil, errBlobTxNotSupported + } if args.Nonce == nil { return nil, errors.New("nonce not specified") } @@ -511,7 +520,7 @@ func (s *PersonalAccountAPI) SignTransaction(ctx context.Context, args Transacti // // The key used to calculate the signature is decrypted with the given password. // -// https://github.com/ethereum/go-ethereum/wiki/Management-APIs#personal_sign +// https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-personal#personal-sign func (s *PersonalAccountAPI) Sign(ctx context.Context, data hexutil.Bytes, addr common.Address, passwd string) (hexutil.Bytes, error) { // Look up the wallet containing the requested signer account := accounts.Account{Address: addr} @@ -539,7 +548,7 @@ func (s *PersonalAccountAPI) Sign(ctx context.Context, data hexutil.Bytes, addr // Note, the signature must conform to the secp256k1 curve R, S and V values, where // the V value must be 27 or 28 for legacy reasons. // -// https://github.com/ethereum/go-ethereum/wiki/Management-APIs#personal_ecRecover +// https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-personal#personal-ecrecover func (s *PersonalAccountAPI) EcRecover(ctx context.Context, data, sig hexutil.Bytes) (common.Address, error) { if len(sig) != crypto.SignatureLength { return common.Address{}, fmt.Errorf("signature must be %d bytes long", crypto.SignatureLength) @@ -632,10 +641,11 @@ func (s *BlockChainAPI) GetBalance(ctx context.Context, address common.Address, if state == nil || err != nil { return nil, err } - return (*hexutil.Big)(state.GetBalance(address)), state.Error() + b := state.GetBalance(address).ToBig() + return (*hexutil.Big)(b), state.Error() } -// Result structs for GetProof +// AccountResult structs for GetProof type AccountResult struct { Address common.Address `json:"address"` AccountProof []string `json:"accountProof"` @@ -730,10 +740,11 @@ func (s *BlockChainAPI) GetProof(ctx context.Context, address common.Address, st if err := tr.Prove(crypto.Keccak256(address.Bytes()), &accountProof); err != nil { return nil, err } + balance := statedb.GetBalance(address).ToBig() return &AccountResult{ Address: address, AccountProof: accountProof, - Balance: (*hexutil.Big)(statedb.GetBalance(address)), + Balance: (*hexutil.Big)(balance), CodeHash: codeHash, Nonce: hexutil.Uint64(statedb.GetNonce(address)), StorageHash: storageRoot, @@ -958,7 +969,8 @@ func (diff *StateOverride) Apply(state *state.StateDB) error { } // Override account balance. if account.Balance != nil { - state.SetBalance(addr, (*big.Int)(*account.Balance)) + u256Balance, _ := uint256.FromBig((*big.Int)(*account.Balance)) + state.SetBalance(addr, u256Balance) } if account.State != nil && account.StateDiff != nil { return fmt.Errorf("account %s has both 'state' and 'stateDiff'", addr.Hex()) @@ -1070,14 +1082,14 @@ func doCall(ctx context.Context, b Backend, args TransactionArgs, state *state.S defer cancel() // Get a new instance of the EVM. - msg, err := args.ToMessage(globalGasCap, header.BaseFee) - if err != nil { - return nil, err - } blockCtx := core.NewEVMBlockContext(header, NewChainContext(ctx, b), nil) if blockOverrides != nil { blockOverrides.Apply(&blockCtx) } + msg, err := args.ToMessage(globalGasCap, blockCtx.BaseFee) + if err != nil { + return nil, err + } evm := b.GetEVM(ctx, msg, state, header, &vm.Config{NoBaseFee: true}, &blockCtx) // Wait for the context to be done and cancel the evm. Even if the @@ -1202,6 +1214,7 @@ func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNr // returns error if the transaction would revert or if there are unexpected failures. The returned // value is capped by both `args.Gas` (if non-nil & non-zero) and the backend's RPCGasCap // configuration (if non-zero). +// Note: Required blob gas is not computed in this method. func (s *BlockChainAPI) EstimateGas(ctx context.Context, args TransactionArgs, blockNrOrHash *rpc.BlockNumberOrHash, overrides *StateOverride) (hexutil.Uint64, error) { bNrOrHash := rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber) if blockNrOrHash != nil { @@ -1462,7 +1475,7 @@ type accessListResult struct { // CreateAccessList creates an EIP-2930 type AccessList for the given transaction. // Reexec and BlockNrOrHash can be specified to create the accessList on top of a certain state. func (s *BlockChainAPI) CreateAccessList(ctx context.Context, args TransactionArgs, blockNrOrHash *rpc.BlockNumberOrHash) (*accessListResult, error) { - bNrOrHash := rpc.BlockNumberOrHashWithNumber(rpc.PendingBlockNumber) + bNrOrHash := rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber) if blockNrOrHash != nil { bNrOrHash = *blockNrOrHash } @@ -1486,14 +1499,9 @@ func AccessList(ctx context.Context, b Backend, blockNrOrHash rpc.BlockNumberOrH if db == nil || err != nil { return nil, 0, nil, err } - // If the gas amount is not set, default to RPC gas cap. - if args.Gas == nil { - tmp := hexutil.Uint64(b.RPCGasCap()) - args.Gas = &tmp - } // Ensure any missing fields are filled, extract the recipient and input data - if err := args.setDefaults(ctx, b); err != nil { + if err := args.setDefaults(ctx, b, true); err != nil { return nil, 0, nil, err } var to common.Address @@ -1626,51 +1634,49 @@ func (s *TransactionAPI) GetTransactionCount(ctx context.Context, address common // GetTransactionByHash returns the transaction for the given hash func (s *TransactionAPI) GetTransactionByHash(ctx context.Context, hash common.Hash) (*RPCTransaction, error) { // Try to return an already finalized transaction - tx, blockHash, blockNumber, index, err := s.b.GetTransaction(ctx, hash) - if err != nil { - return nil, err - } - if tx != nil { - header, err := s.b.HeaderByHash(ctx, blockHash) - if err != nil { - return nil, err + found, tx, blockHash, blockNumber, index, err := s.b.GetTransaction(ctx, hash) + if !found { + // No finalized transaction, try to retrieve it from the pool + if tx := s.b.GetPoolTransaction(hash); tx != nil { + estimatedBaseFee, _ := s.b.EstimateBaseFee(ctx) + return NewRPCTransaction(tx, s.b.CurrentHeader(), estimatedBaseFee, s.b.ChainConfig()), nil + } + if err == nil { + return nil, nil } - return newRPCTransaction(tx, blockHash, blockNumber, header.Time, index, header.BaseFee, s.b.ChainConfig()), nil + return nil, NewTxIndexingError() } - // No finalized transaction, try to retrieve it from the pool - if tx := s.b.GetPoolTransaction(hash); tx != nil { - estimatedBaseFee, _ := s.b.EstimateBaseFee(ctx) - return NewRPCTransaction(tx, s.b.CurrentHeader(), estimatedBaseFee, s.b.ChainConfig()), nil + header, err := s.b.HeaderByHash(ctx, blockHash) + if err != nil { + return nil, err } - - // Transaction unknown, return as such - return nil, nil + return newRPCTransaction(tx, blockHash, blockNumber, header.Time, index, header.BaseFee, s.b.ChainConfig()), nil } // GetRawTransactionByHash returns the bytes of the transaction for the given hash. func (s *TransactionAPI) GetRawTransactionByHash(ctx context.Context, hash common.Hash) (hexutil.Bytes, error) { // Retrieve a finalized transaction, or a pooled otherwise - tx, _, _, _, err := s.b.GetTransaction(ctx, hash) - if err != nil { - return nil, err - } - if tx == nil { - if tx = s.b.GetPoolTransaction(hash); tx == nil { - // Transaction not found anywhere, abort + found, tx, _, _, _, err := s.b.GetTransaction(ctx, hash) + if !found { + if tx = s.b.GetPoolTransaction(hash); tx != nil { + return tx.MarshalBinary() + } + if err == nil { return nil, nil } + return nil, NewTxIndexingError() } - // Serialize to RLP and return return tx.MarshalBinary() } // GetTransactionReceipt returns the transaction receipt for the given transaction hash. func (s *TransactionAPI) GetTransactionReceipt(ctx context.Context, hash common.Hash) (map[string]interface{}, error) { - tx, blockHash, blockNumber, index, err := s.b.GetTransaction(ctx, hash) - if tx == nil || err != nil { - // When the transaction doesn't exist, the RPC method should return JSON null - // as per specification. - return nil, nil + found, tx, blockHash, blockNumber, index, err := s.b.GetTransaction(ctx, hash) + if err != nil { + return nil, NewTxIndexingError() // transaction is not fully indexed + } + if !found { + return nil, nil // transaction is not existent or reachable } header, err := s.b.HeaderByHash(ctx, blockHash) if err != nil { @@ -1793,9 +1799,12 @@ func (s *TransactionAPI) SendTransaction(ctx context.Context, args TransactionAr s.nonceLock.LockAddr(args.from()) defer s.nonceLock.UnlockAddr(args.from()) } + if args.IsEIP4844() { + return common.Hash{}, errBlobTxNotSupported + } // Set some sanity defaults and terminate on failure - if err := args.setDefaults(ctx, s.b); err != nil { + if err := args.setDefaults(ctx, s.b, false); err != nil { return common.Hash{}, err } // Assemble the transaction and sign with the wallet @@ -1812,8 +1821,10 @@ func (s *TransactionAPI) SendTransaction(ctx context.Context, args TransactionAr // on a given unsigned transaction, and returns it to the caller for further // processing (signing + broadcast). func (s *TransactionAPI) FillTransaction(ctx context.Context, args TransactionArgs) (*SignTransactionResult, error) { + args.blobSidecarAllowed = true + // Set some sanity defaults and terminate on failure - if err := args.setDefaults(ctx, s.b); err != nil { + if err := args.setDefaults(ctx, s.b, false); err != nil { return nil, err } // Assemble the transaction and obtain rlp @@ -1876,10 +1887,13 @@ func (s *TransactionAPI) SignTransaction(ctx context.Context, args TransactionAr if args.GasPrice == nil && (args.MaxPriorityFeePerGas == nil || args.MaxFeePerGas == nil) { return nil, errors.New("missing gasPrice or maxFeePerGas/maxPriorityFeePerGas") } + if args.IsEIP4844() { + return nil, errBlobTxNotSupported + } if args.Nonce == nil { return nil, errors.New("nonce not specified") } - if err := args.setDefaults(ctx, s.b); err != nil { + if err := args.setDefaults(ctx, s.b, false); err != nil { return nil, err } // Before actually sign the transaction, ensure the transaction fee is reasonable. @@ -1929,7 +1943,7 @@ func (s *TransactionAPI) Resend(ctx context.Context, sendArgs TransactionArgs, g if sendArgs.Nonce == nil { return common.Hash{}, errors.New("missing transaction nonce in transaction spec") } - if err := sendArgs.setDefaults(ctx, s.b); err != nil { + if err := sendArgs.setDefaults(ctx, s.b, false); err != nil { return common.Hash{}, err } matchTx := sendArgs.toTransaction() @@ -2054,15 +2068,15 @@ func (api *DebugAPI) GetRawReceipts(ctx context.Context, blockNrOrHash rpc.Block // GetRawTransaction returns the bytes of the transaction for the given hash. func (s *DebugAPI) GetRawTransaction(ctx context.Context, hash common.Hash) (hexutil.Bytes, error) { // Retrieve a finalized transaction, or a pooled otherwise - tx, _, _, _, err := s.b.GetTransaction(ctx, hash) - if err != nil { - return nil, err - } - if tx == nil { - if tx = s.b.GetPoolTransaction(hash); tx == nil { - // Transaction not found anywhere, abort + found, tx, _, _, _, err := s.b.GetTransaction(ctx, hash) + if !found { + if tx = s.b.GetPoolTransaction(hash); tx != nil { + return tx.MarshalBinary() + } + if err == nil { return nil, nil } + return nil, NewTxIndexingError() } return tx.MarshalBinary() } diff --git a/internal/ethapi/api_test.go b/internal/ethapi/api_test.go index 1fd0b0da75..72b2ba8568 100644 --- a/internal/ethapi/api_test.go +++ b/internal/ethapi/api_test.go @@ -27,8 +27,10 @@ package ethapi import ( + "bytes" "context" "crypto/ecdsa" + "crypto/sha256" "encoding/json" "errors" "fmt" @@ -41,6 +43,7 @@ import ( "github.com/ava-labs/avalanchego/upgrade" "github.com/ava-labs/subnet-evm/accounts" + "github.com/ava-labs/subnet-evm/accounts/keystore" "github.com/ava-labs/subnet-evm/commontype" "github.com/ava-labs/subnet-evm/consensus" "github.com/ava-labs/subnet-evm/consensus/dummy" @@ -53,9 +56,11 @@ import ( "github.com/ava-labs/subnet-evm/internal/blocktest" "github.com/ava-labs/subnet-evm/params" "github.com/ava-labs/subnet-evm/rpc" + "github.com/ava-labs/subnet-evm/utils" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/crypto/kzg4844" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" "github.com/holiman/uint256" @@ -413,9 +418,29 @@ func allBlobTxs(addr common.Address, config *params.ChainConfig) []txData { } } +func newTestAccountManager(t *testing.T) (*accounts.Manager, accounts.Account) { + var ( + dir = t.TempDir() + am = accounts.NewManager(&accounts.Config{InsecureUnlockAllowed: true}) + b = keystore.NewKeyStore(dir, 2, 1) + testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + ) + acc, err := b.ImportECDSA(testKey, "") + if err != nil { + t.Fatalf("failed to create test account: %v", err) + } + if err := b.Unlock(acc, ""); err != nil { + t.Fatalf("failed to unlock account: %v\n", err) + } + am.AddBackend(b) + return am, acc +} + type testBackend struct { - db ethdb.Database - chain *core.BlockChain + db ethdb.Database + chain *core.BlockChain + accman *accounts.Manager + acc accounts.Account } func newTestBackend(t *testing.T, n int, gspec *core.Genesis, engine consensus.Engine, generator func(i int, b *core.BlockGen)) *testBackend { @@ -427,6 +452,8 @@ func newTestBackend(t *testing.T, n int, gspec *core.Genesis, engine consensus.E Pruning: false, // Archive mode } ) + accman, acc := newTestAccountManager(t) + gspec.Alloc[acc.Address] = types.Account{Balance: big.NewInt(params.Ether)} // Generate blocks for testing db, blocks, _, _ := core.GenerateChainWithGenesis(gspec, engine, n, 10, generator) chain, err := core.NewBlockChain(db, cacheConfig, gspec, engine, vm.Config{}, gspec.ToBlock().Hash(), false) @@ -443,7 +470,7 @@ func newTestBackend(t *testing.T, n int, gspec *core.Genesis, engine consensus.E } chain.DrainAcceptorQueue() - backend := &testBackend{db: db, chain: chain} + backend := &testBackend{db: db, chain: chain, accman: accman, acc: acc} return backend } @@ -454,7 +481,7 @@ func (b testBackend) FeeHistory(ctx context.Context, blockCount uint64, lastBloc return nil, nil, nil, nil, nil } func (b testBackend) ChainDb() ethdb.Database { return b.db } -func (b testBackend) AccountManager() *accounts.Manager { return nil } +func (b testBackend) AccountManager() *accounts.Manager { return b.accman } func (b testBackend) ExtRPCEnabled() bool { return false } func (b testBackend) RPCGasCap() uint64 { return 10000000 } func (b testBackend) RPCEVMTimeout() time.Duration { return time.Second } @@ -558,14 +585,14 @@ func (b testBackend) GetFeeConfigAt(parent *types.Header) (commontype.FeeConfig, func (b testBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error { panic("implement me") } -func (b testBackend) GetTransaction(ctx context.Context, txHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64, error) { +func (b testBackend) GetTransaction(ctx context.Context, txHash common.Hash) (bool, *types.Transaction, common.Hash, uint64, uint64, error) { tx, blockHash, blockNumber, index := rawdb.ReadTransaction(b.db, txHash) - return tx, blockHash, blockNumber, index, nil + return true, tx, blockHash, blockNumber, index, nil } func (b testBackend) GetPoolTransactions() (types.Transactions, error) { panic("implement me") } func (b testBackend) GetPoolTransaction(txHash common.Hash) *types.Transaction { panic("implement me") } func (b testBackend) GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error) { - panic("implement me") + return 0, nil } func (b testBackend) Stats() (pending int, queued int) { panic("implement me") } func (b testBackend) TxPoolContent() (map[common.Address][]*types.Transaction, map[common.Address][]*types.Transaction) { @@ -611,7 +638,7 @@ func TestEstimateGas(t *testing.T) { accounts = newAccounts(2) genesis = &core.Genesis{ Config: params.TestChainConfig, - Alloc: core.GenesisAlloc{ + Alloc: types.GenesisAlloc{ accounts[0].addr: {Balance: big.NewInt(params.Ether)}, accounts[1].addr: {Balance: big.NewInt(params.Ether)}, }, @@ -626,6 +653,7 @@ func TestEstimateGas(t *testing.T) { // 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) b.AddTx(tx) + // b.SetPoS() })) var testSuite = []struct { blockNumber rpc.BlockNumber @@ -725,6 +753,18 @@ func TestEstimateGas(t *testing.T) { expectErr: nil, want: 67595, }, + // Blobs should have no effect on gas estimate + { + blockNumber: rpc.LatestBlockNumber, + call: TransactionArgs{ + From: &accounts[0].addr, + To: &accounts[1].addr, + Value: (*hexutil.Big)(big.NewInt(1)), + BlobHashes: []common.Hash{common.Hash{0x01, 0x22}}, + BlobFeeCap: (*hexutil.Big)(big.NewInt(1)), + }, + want: 21000, + }, } for i, tc := range testSuite { result, err := api.EstimateGas(context.Background(), tc.call, &rpc.BlockNumberOrHash{BlockNumber: &tc.blockNumber}, &tc.overrides) @@ -749,13 +789,16 @@ func TestEstimateGas(t *testing.T) { } func TestCall(t *testing.T) { + // Enable BLOBHASH opcode in Cancun + cfg := *params.TestChainConfig + cfg.CancunTime = utils.NewUint64(0) t.Parallel() // Initialize test accounts var ( accounts = newAccounts(3) genesis = &core.Genesis{ - Config: params.TestChainConfig, - Alloc: core.GenesisAlloc{ + Config: &cfg, + Alloc: types.GenesisAlloc{ accounts[0].addr: {Balance: big.NewInt(params.Ether)}, accounts[1].addr: {Balance: big.NewInt(params.Ether)}, accounts[2].addr: {Balance: big.NewInt(params.Ether)}, @@ -770,6 +813,7 @@ func TestCall(t *testing.T) { // 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) b.AddTx(tx) + // b.SetPoS() })) randomAccounts := newAccounts(3) var testSuite = []struct { @@ -891,6 +935,32 @@ func TestCall(t *testing.T) { blockOverrides: BlockOverrides{Number: (*hexutil.Big)(big.NewInt(11))}, want: "0x000000000000000000000000000000000000000000000000000000000000000b", }, + // Invalid blob tx + { + blockNumber: rpc.LatestBlockNumber, + call: TransactionArgs{ + From: &accounts[1].addr, + Input: &hexutil.Bytes{0x00}, + BlobHashes: []common.Hash{}, + }, + expectErr: core.ErrBlobTxCreate, + }, + // BLOBHASH opcode + { + blockNumber: rpc.LatestBlockNumber, + call: TransactionArgs{ + From: &accounts[1].addr, + To: &randomAccounts[2].addr, + BlobHashes: []common.Hash{common.Hash{0x01, 0x22}}, + BlobFeeCap: (*hexutil.Big)(big.NewInt(1)), + }, + overrides: StateOverride{ + randomAccounts[2].addr: { + Code: hex2Bytes("60004960005260206000f3"), + }, + }, + want: "0x0122000000000000000000000000000000000000000000000000000000000000", + }, } for i, tc := range testSuite { result, err := api.Call(context.Background(), tc.call, &rpc.BlockNumberOrHash{BlockNumber: &tc.blockNumber}, &tc.overrides, &tc.blockOverrides) @@ -917,6 +987,323 @@ func TestCall(t *testing.T) { } } +func TestSignTransaction(t *testing.T) { + t.Parallel() + // Initialize test accounts + var ( + key, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a") + to = crypto.PubkeyToAddress(key.PublicKey) + genesis = &core.Genesis{ + Config: params.TestChainConfig, + Alloc: types.GenesisAlloc{}, + } + ) + b := newTestBackend(t, 1, genesis, dummy.NewCoinbaseFaker(), func(i int, b *core.BlockGen) { + // b.SetPoS() + }) + api := NewTransactionAPI(b, nil) + res, err := api.FillTransaction(context.Background(), TransactionArgs{ + From: &b.acc.Address, + To: &to, + Value: (*hexutil.Big)(big.NewInt(1)), + }) + if err != nil { + t.Fatalf("failed to fill tx defaults: %v\n", err) + } + + res, err = api.SignTransaction(context.Background(), argsFromTransaction(res.Tx, b.acc.Address)) + if err != nil { + t.Fatalf("failed to sign tx: %v\n", err) + } + tx, err := json.Marshal(res.Tx) + if err != nil { + t.Fatal(err) + } + expect := `{"type":"0x2","chainId":"0x1","nonce":"0x0","to":"0x703c4b2bd70c169f5717101caee543299fc946c7","gas":"0x5208","gasPrice":null,"maxPriorityFeePerGas":"0x0","maxFeePerGas":"0xba43b7400","value":"0x1","input":"0x","accessList":[],"v":"0x0","r":"0xa7bbf5672b6f78e934bd380aad0b2626d5337e96c12f1e755fa5522ba7a314bd","s":"0x4d661f8c7b850b7dc3ce1c8c7b443a4434a22fe3ad14cc463205e0259546f0c8","yParity":"0x0","hash":"0x0333d97cbdababb6af7cc55a6f64d47711b8e18a93d7343657508a454407a82c"}` + if !bytes.Equal(tx, []byte(expect)) { + t.Errorf("result mismatch. Have:\n%s\nWant:\n%s\n", tx, expect) + } +} + +func TestSignBlobTransaction(t *testing.T) { + t.Parallel() + // Initialize test accounts + var ( + key, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a") + to = crypto.PubkeyToAddress(key.PublicKey) + genesis = &core.Genesis{ + Config: params.TestChainConfig, + Alloc: types.GenesisAlloc{}, + } + ) + b := newTestBackend(t, 1, genesis, dummy.NewCoinbaseFaker(), func(i int, b *core.BlockGen) { + // b.SetPoS() + }) + api := NewTransactionAPI(b, nil) + res, err := api.FillTransaction(context.Background(), TransactionArgs{ + From: &b.acc.Address, + To: &to, + Value: (*hexutil.Big)(big.NewInt(1)), + BlobHashes: []common.Hash{{0x01, 0x22}}, + }) + if err != nil { + t.Fatalf("failed to fill tx defaults: %v\n", err) + } + + _, err = api.SignTransaction(context.Background(), argsFromTransaction(res.Tx, b.acc.Address)) + if err == nil { + t.Fatalf("should fail on blob transaction") + } + if !errors.Is(err, errBlobTxNotSupported) { + t.Errorf("error mismatch. Have: %v, want: %v", err, errBlobTxNotSupported) + } +} + +func TestSendBlobTransaction(t *testing.T) { + t.Parallel() + // Initialize test accounts + var ( + key, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a") + to = crypto.PubkeyToAddress(key.PublicKey) + genesis = &core.Genesis{ + Config: params.TestChainConfig, + Alloc: types.GenesisAlloc{}, + } + ) + b := newTestBackend(t, 1, genesis, dummy.NewCoinbaseFaker(), func(i int, b *core.BlockGen) { + // b.SetPoS() + }) + api := NewTransactionAPI(b, nil) + res, err := api.FillTransaction(context.Background(), TransactionArgs{ + From: &b.acc.Address, + To: &to, + Value: (*hexutil.Big)(big.NewInt(1)), + BlobHashes: []common.Hash{common.Hash{0x01, 0x22}}, + }) + if err != nil { + t.Fatalf("failed to fill tx defaults: %v\n", err) + } + + _, err = api.SendTransaction(context.Background(), argsFromTransaction(res.Tx, b.acc.Address)) + if err == nil { + t.Errorf("sending tx should have failed") + } else if !errors.Is(err, errBlobTxNotSupported) { + t.Errorf("unexpected error. Have %v, want %v\n", err, errBlobTxNotSupported) + } +} + +func TestFillBlobTransaction(t *testing.T) { + t.Parallel() + // Initialize test accounts + var ( + key, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a") + to = crypto.PubkeyToAddress(key.PublicKey) + genesis = &core.Genesis{ + Config: params.TestChainConfig, + Alloc: types.GenesisAlloc{}, + } + emptyBlob = kzg4844.Blob{} + emptyBlobCommit, _ = kzg4844.BlobToCommitment(emptyBlob) + emptyBlobProof, _ = kzg4844.ComputeBlobProof(emptyBlob, emptyBlobCommit) + emptyBlobHash common.Hash = kzg4844.CalcBlobHashV1(sha256.New(), &emptyBlobCommit) + ) + b := newTestBackend(t, 1, genesis, dummy.NewCoinbaseFaker(), func(i int, b *core.BlockGen) { + // b.SetPoS() + }) + api := NewTransactionAPI(b, nil) + type result struct { + Hashes []common.Hash + Sidecar *types.BlobTxSidecar + } + suite := []struct { + name string + args TransactionArgs + err string + want *result + }{ + { + name: "TestInvalidParamsCombination1", + args: TransactionArgs{ + From: &b.acc.Address, + To: &to, + Value: (*hexutil.Big)(big.NewInt(1)), + Blobs: []kzg4844.Blob{{}}, + Proofs: []kzg4844.Proof{{}}, + }, + err: `blob proofs provided while commitments were not`, + }, + { + name: "TestInvalidParamsCombination2", + args: TransactionArgs{ + From: &b.acc.Address, + To: &to, + Value: (*hexutil.Big)(big.NewInt(1)), + Blobs: []kzg4844.Blob{{}}, + Commitments: []kzg4844.Commitment{{}}, + }, + err: `blob commitments provided while proofs were not`, + }, + { + name: "TestInvalidParamsCount1", + args: TransactionArgs{ + From: &b.acc.Address, + To: &to, + Value: (*hexutil.Big)(big.NewInt(1)), + Blobs: []kzg4844.Blob{{}}, + Commitments: []kzg4844.Commitment{{}, {}}, + Proofs: []kzg4844.Proof{{}, {}}, + }, + err: `number of blobs and commitments mismatch (have=2, want=1)`, + }, + { + name: "TestInvalidParamsCount2", + args: TransactionArgs{ + From: &b.acc.Address, + To: &to, + Value: (*hexutil.Big)(big.NewInt(1)), + Blobs: []kzg4844.Blob{{}, {}}, + Commitments: []kzg4844.Commitment{{}, {}}, + Proofs: []kzg4844.Proof{{}}, + }, + err: `number of blobs and proofs mismatch (have=1, want=2)`, + }, + { + name: "TestInvalidProofVerification", + args: TransactionArgs{ + From: &b.acc.Address, + To: &to, + Value: (*hexutil.Big)(big.NewInt(1)), + Blobs: []kzg4844.Blob{{}, {}}, + Commitments: []kzg4844.Commitment{{}, {}}, + Proofs: []kzg4844.Proof{{}, {}}, + }, + err: `failed to verify blob proof: short buffer`, + }, + { + name: "TestGenerateBlobHashes", + args: TransactionArgs{ + From: &b.acc.Address, + To: &to, + Value: (*hexutil.Big)(big.NewInt(1)), + Blobs: []kzg4844.Blob{emptyBlob}, + Commitments: []kzg4844.Commitment{emptyBlobCommit}, + Proofs: []kzg4844.Proof{emptyBlobProof}, + }, + want: &result{ + Hashes: []common.Hash{emptyBlobHash}, + Sidecar: &types.BlobTxSidecar{ + Blobs: []kzg4844.Blob{emptyBlob}, + Commitments: []kzg4844.Commitment{emptyBlobCommit}, + Proofs: []kzg4844.Proof{emptyBlobProof}, + }, + }, + }, + { + name: "TestValidBlobHashes", + args: TransactionArgs{ + From: &b.acc.Address, + To: &to, + Value: (*hexutil.Big)(big.NewInt(1)), + BlobHashes: []common.Hash{emptyBlobHash}, + Blobs: []kzg4844.Blob{emptyBlob}, + Commitments: []kzg4844.Commitment{emptyBlobCommit}, + Proofs: []kzg4844.Proof{emptyBlobProof}, + }, + want: &result{ + Hashes: []common.Hash{emptyBlobHash}, + Sidecar: &types.BlobTxSidecar{ + Blobs: []kzg4844.Blob{emptyBlob}, + Commitments: []kzg4844.Commitment{emptyBlobCommit}, + Proofs: []kzg4844.Proof{emptyBlobProof}, + }, + }, + }, + { + name: "TestInvalidBlobHashes", + args: TransactionArgs{ + From: &b.acc.Address, + To: &to, + Value: (*hexutil.Big)(big.NewInt(1)), + BlobHashes: []common.Hash{{0x01, 0x22}}, + Blobs: []kzg4844.Blob{emptyBlob}, + Commitments: []kzg4844.Commitment{emptyBlobCommit}, + Proofs: []kzg4844.Proof{emptyBlobProof}, + }, + err: fmt.Sprintf("blob hash verification failed (have=%s, want=%s)", common.Hash{0x01, 0x22}, emptyBlobHash), + }, + { + name: "TestGenerateBlobProofs", + args: TransactionArgs{ + From: &b.acc.Address, + To: &to, + Value: (*hexutil.Big)(big.NewInt(1)), + Blobs: []kzg4844.Blob{emptyBlob}, + }, + want: &result{ + Hashes: []common.Hash{emptyBlobHash}, + Sidecar: &types.BlobTxSidecar{ + Blobs: []kzg4844.Blob{emptyBlob}, + Commitments: []kzg4844.Commitment{emptyBlobCommit}, + Proofs: []kzg4844.Proof{emptyBlobProof}, + }, + }, + }, + } + for _, tc := range suite { + t.Run(tc.name, func(t *testing.T) { + res, err := api.FillTransaction(context.Background(), tc.args) + if len(tc.err) > 0 { + if err == nil { + t.Fatalf("missing error. want: %s", tc.err) + } else if err != nil && err.Error() != tc.err { + t.Fatalf("error mismatch. want: %s, have: %s", tc.err, err.Error()) + } + return + } + if err != nil && len(tc.err) == 0 { + t.Fatalf("expected no error. have: %s", err) + } + if res == nil { + t.Fatal("result missing") + } + want, err := json.Marshal(tc.want) + if err != nil { + t.Fatalf("failed to encode expected: %v", err) + } + have, err := json.Marshal(result{Hashes: res.Tx.BlobHashes(), Sidecar: res.Tx.BlobTxSidecar()}) + if err != nil { + t.Fatalf("failed to encode computed sidecar: %v", err) + } + if !bytes.Equal(have, want) { + t.Errorf("blob sidecar mismatch. Have: %s, want: %s", have, want) + } + }) + } +} + +func argsFromTransaction(tx *types.Transaction, from common.Address) TransactionArgs { + var ( + gas = tx.Gas() + nonce = tx.Nonce() + input = tx.Data() + ) + return TransactionArgs{ + From: &from, + To: tx.To(), + Gas: (*hexutil.Uint64)(&gas), + MaxFeePerGas: (*hexutil.Big)(tx.GasFeeCap()), + MaxPriorityFeePerGas: (*hexutil.Big)(tx.GasTipCap()), + Value: (*hexutil.Big)(tx.Value()), + Nonce: (*hexutil.Uint64)(&nonce), + Input: (*hexutil.Bytes)(&input), + ChainID: (*hexutil.Big)(tx.ChainId()), + // TODO: impl accessList conversion + //AccessList: tx.AccessList(), + BlobFeeCap: (*hexutil.Big)(tx.BlobGasFeeCap()), + BlobHashes: tx.BlobHashes(), + } +} + type account struct { key *ecdsa.PrivateKey addr common.Address @@ -1162,7 +1549,7 @@ func TestRPCGetBlockOrHeader(t *testing.T) { acc2Addr = crypto.PubkeyToAddress(acc2Key.PublicKey) genesis = &core.Genesis{ Config: params.TestChainConfig, - Alloc: core.GenesisAlloc{ + Alloc: types.GenesisAlloc{ acc1Addr: {Balance: big.NewInt(params.Ether)}, acc2Addr: {Balance: big.NewInt(params.Ether)}, }, @@ -1400,6 +1787,7 @@ func TestRPCGetBlockOrHeader(t *testing.T) { func setupReceiptBackend(t *testing.T, genBlocks int) (*testBackend, []common.Hash) { config := *params.TestChainConfig + config.CancunTime = new(uint64) var ( acc1Key, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a") acc2Key, _ = crypto.HexToECDSA("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee") @@ -1411,7 +1799,7 @@ func setupReceiptBackend(t *testing.T, genBlocks int) (*testBackend, []common.Ha ExcessBlobGas: new(uint64), BlobGasUsed: new(uint64), Timestamp: uint64(upgrade.InitiallyActiveTime.Unix()), - Alloc: core.GenesisAlloc{ + Alloc: types.GenesisAlloc{ acc1Addr: {Balance: big.NewInt(params.Ether)}, acc2Addr: {Balance: big.NewInt(params.Ether)}, // // SPDX-License-Identifier: GPL-3.0 @@ -1431,14 +1819,12 @@ func setupReceiptBackend(t *testing.T, genBlocks int) (*testBackend, []common.Ha txHashes = make([]common.Hash, genBlocks) ) - // Set the terminal total difficulty in the config - // genesis.Config.TerminalTotalDifficulty = big.NewInt(0) - // genesis.Config.TerminalTotalDifficultyPassed = true backend := newTestBackend(t, genBlocks, genesis, dummy.NewCoinbaseFaker(), func(i int, b *core.BlockGen) { var ( tx *types.Transaction err error ) + // b.SetPoS() switch i { case 0: // transfer 1000wei diff --git a/internal/ethapi/backend.go b/internal/ethapi/backend.go index e80e4cad51..a5dab73a80 100644 --- a/internal/ethapi/backend.go +++ b/internal/ethapi/backend.go @@ -85,7 +85,7 @@ type Backend interface { // Transaction pool API SendTx(ctx context.Context, signedTx *types.Transaction) error - GetTransaction(ctx context.Context, txHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64, error) + GetTransaction(ctx context.Context, txHash common.Hash) (bool, *types.Transaction, common.Hash, uint64, uint64, error) GetPoolTransactions() (types.Transactions, error) GetPoolTransaction(txHash common.Hash) *types.Transaction GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error) diff --git a/internal/ethapi/errors.go b/internal/ethapi/errors.go index aedf110624..68327efb2c 100644 --- a/internal/ethapi/errors.go +++ b/internal/ethapi/errors.go @@ -65,3 +65,24 @@ func newRevertError(revert []byte) *revertError { reason: hexutil.Encode(revert), } } + +// TxIndexingError is an API error that indicates the transaction indexing is not +// fully finished yet with JSON error code and a binary data blob. +type TxIndexingError struct{} + +// NewTxIndexingError creates a TxIndexingError instance. +func NewTxIndexingError() *TxIndexingError { return &TxIndexingError{} } + +// Error implement error interface, returning the error message. +func (e *TxIndexingError) Error() string { + return "transaction indexing is in progress" +} + +// ErrorCode returns the JSON error code for a revert. +// See: https://github.com/ethereum/wiki/wiki/JSON-RPC-Error-Codes-Improvement-Proposal +func (e *TxIndexingError) ErrorCode() int { + return -32000 // to be decided +} + +// ErrorData returns the hex encoded revert reason. +func (e *TxIndexingError) ErrorData() interface{} { return "transaction indexing is in progress" } diff --git a/internal/ethapi/testdata/eth_getBlockByHash-hash-1.json b/internal/ethapi/testdata/eth_getBlockByHash-hash-1.json index a21af855b8..3835567741 100644 --- a/internal/ethapi/testdata/eth_getBlockByHash-hash-1.json +++ b/internal/ethapi/testdata/eth_getBlockByHash-hash-1.json @@ -5,17 +5,17 @@ "extraData": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "gasLimit": "0x7a1200", "gasUsed": "0x5208", - "hash": "0x0f67ad1fc8052afad4c24551748600c164091cf37e068adef76315025d3c78e7", + "hash": "0xb0a6fc2230444f86583fec57e9fa0aef3ebf36e094b5f2abeb231089697c50e0", "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "miner": "0x0000000000000000000000000000000000000000", "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", "nonce": "0x0000000000000000", "number": "0x1", - "parentHash": "0x3ead7923676a44500c46ad2192a0fc084aa42063b1703e6866f138a47fb1a9ca", + "parentHash": "0xa779a1e82fd903ae98affe3a06a0a353906130052c24969b24237adf350bd03b", "receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2", "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", "size": "0x2bb", - "stateRoot": "0x6b830601767ac4968163193facbe20123435180e325910b2c50efa21f778c697", + "stateRoot": "0xa17c770ca1e18e544a4a6d14b611cd03b505d465cc967ba5162d59ce459b553d", "timestamp": "0xa", "totalDifficulty": "0x1", "transactions": [ diff --git a/internal/ethapi/testdata/eth_getBlockByHash-hash-genesis.json b/internal/ethapi/testdata/eth_getBlockByHash-hash-genesis.json index 446f5db6ab..e8f06e2d94 100644 --- a/internal/ethapi/testdata/eth_getBlockByHash-hash-genesis.json +++ b/internal/ethapi/testdata/eth_getBlockByHash-hash-genesis.json @@ -4,7 +4,7 @@ "extraData": "0x", "gasLimit": "0x47e7c4", "gasUsed": "0x0", - "hash": "0x3ead7923676a44500c46ad2192a0fc084aa42063b1703e6866f138a47fb1a9ca", + "hash": "0xa779a1e82fd903ae98affe3a06a0a353906130052c24969b24237adf350bd03b", "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "miner": "0x0000000000000000000000000000000000000000", "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", @@ -14,7 +14,7 @@ "receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", "size": "0x201", - "stateRoot": "0xfe168c5e9584a85927212e5bea5304bb7d0d8a893453b4b2c52176a72f585ae2", + "stateRoot": "0xd883f48b83cc9c1e8389453beb4ad4e572462eec049ca4fffbe16ecefb3fe937", "timestamp": "0x0", "totalDifficulty": "0x0", "transactions": [], diff --git a/internal/ethapi/testdata/eth_getBlockByHash-hash-latest-1-fullTx.json b/internal/ethapi/testdata/eth_getBlockByHash-hash-latest-1-fullTx.json index 4d54e3f10b..8f55c154c0 100644 --- a/internal/ethapi/testdata/eth_getBlockByHash-hash-latest-1-fullTx.json +++ b/internal/ethapi/testdata/eth_getBlockByHash-hash-latest-1-fullTx.json @@ -5,22 +5,22 @@ "extraData": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "gasLimit": "0x7a1200", "gasUsed": "0x5208", - "hash": "0x0583a9d630632001771b4ecc7d62574aec3825aff47e2a680b0ea4ddb79e7365", + "hash": "0x71f4093e0536817246679d6e183d836710027189eed7d0e09f1fde11a26f499c", "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "miner": "0x0000000000000000000000000000000000000000", "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", "nonce": "0x0000000000000000", "number": "0x9", - "parentHash": "0x2fab5c6892c66668842683ced6b384c2ee83bfd6096a58f451290cabaf57a63e", + "parentHash": "0x4f8c1e68991fdb03a668c72e42c454dc911f34416035ef59fdeb83218fe4ae84", "receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2", "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", "size": "0x2bb", - "stateRoot": "0x3703d70c6443e809ce035c2a8212dbf9813f6b7d1b0f597766e9023867a852f5", + "stateRoot": "0x67e974f6435c557282cd778ae1ae16dded6e84db121d3ab27b931726bd48e2ac", "timestamp": "0x5a", "totalDifficulty": "0x9", "transactions": [ { - "blockHash": "0x0583a9d630632001771b4ecc7d62574aec3825aff47e2a680b0ea4ddb79e7365", + "blockHash": "0x71f4093e0536817246679d6e183d836710027189eed7d0e09f1fde11a26f499c", "blockNumber": "0x9", "from": "0x703c4b2bd70c169f5717101caee543299fc946c7", "gas": "0x5208", diff --git a/internal/ethapi/testdata/eth_getBlockByHash-hash-latest.json b/internal/ethapi/testdata/eth_getBlockByHash-hash-latest.json index 7917a2acc7..02d29dd90b 100644 --- a/internal/ethapi/testdata/eth_getBlockByHash-hash-latest.json +++ b/internal/ethapi/testdata/eth_getBlockByHash-hash-latest.json @@ -5,17 +5,17 @@ "extraData": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "gasLimit": "0x7a1200", "gasUsed": "0x5208", - "hash": "0x1ec39e7ec46f8df1fb31cfca53fbf71a01869af8bd8f9a1bccbffc16ffa1461d", + "hash": "0xb64852ab9008988697eb2bc3aec63b97d79c77ff81df2de60535ef31453a5979", "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "miner": "0x0000000000000000000000000000000000000000", "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", "nonce": "0x0000000000000000", "number": "0xa", - "parentHash": "0x0583a9d630632001771b4ecc7d62574aec3825aff47e2a680b0ea4ddb79e7365", + "parentHash": "0x71f4093e0536817246679d6e183d836710027189eed7d0e09f1fde11a26f499c", "receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2", "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", "size": "0x2bb", - "stateRoot": "0x7e06187d15d50badf60930290fb292ebe43e79553ad8b7d8f1b614316631def7", + "stateRoot": "0x2b8307a066fd4575aca1fa6b42ef2c3a860a9c40337ea0d647c9f25e22062e34", "timestamp": "0x64", "totalDifficulty": "0xa", "transactions": [ diff --git a/internal/ethapi/testdata/eth_getBlockByNumber-number-0.json b/internal/ethapi/testdata/eth_getBlockByNumber-number-0.json index 446f5db6ab..e8f06e2d94 100644 --- a/internal/ethapi/testdata/eth_getBlockByNumber-number-0.json +++ b/internal/ethapi/testdata/eth_getBlockByNumber-number-0.json @@ -4,7 +4,7 @@ "extraData": "0x", "gasLimit": "0x47e7c4", "gasUsed": "0x0", - "hash": "0x3ead7923676a44500c46ad2192a0fc084aa42063b1703e6866f138a47fb1a9ca", + "hash": "0xa779a1e82fd903ae98affe3a06a0a353906130052c24969b24237adf350bd03b", "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "miner": "0x0000000000000000000000000000000000000000", "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", @@ -14,7 +14,7 @@ "receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", "size": "0x201", - "stateRoot": "0xfe168c5e9584a85927212e5bea5304bb7d0d8a893453b4b2c52176a72f585ae2", + "stateRoot": "0xd883f48b83cc9c1e8389453beb4ad4e572462eec049ca4fffbe16ecefb3fe937", "timestamp": "0x0", "totalDifficulty": "0x0", "transactions": [], diff --git a/internal/ethapi/testdata/eth_getBlockByNumber-number-1.json b/internal/ethapi/testdata/eth_getBlockByNumber-number-1.json index a21af855b8..3835567741 100644 --- a/internal/ethapi/testdata/eth_getBlockByNumber-number-1.json +++ b/internal/ethapi/testdata/eth_getBlockByNumber-number-1.json @@ -5,17 +5,17 @@ "extraData": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "gasLimit": "0x7a1200", "gasUsed": "0x5208", - "hash": "0x0f67ad1fc8052afad4c24551748600c164091cf37e068adef76315025d3c78e7", + "hash": "0xb0a6fc2230444f86583fec57e9fa0aef3ebf36e094b5f2abeb231089697c50e0", "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "miner": "0x0000000000000000000000000000000000000000", "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", "nonce": "0x0000000000000000", "number": "0x1", - "parentHash": "0x3ead7923676a44500c46ad2192a0fc084aa42063b1703e6866f138a47fb1a9ca", + "parentHash": "0xa779a1e82fd903ae98affe3a06a0a353906130052c24969b24237adf350bd03b", "receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2", "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", "size": "0x2bb", - "stateRoot": "0x6b830601767ac4968163193facbe20123435180e325910b2c50efa21f778c697", + "stateRoot": "0xa17c770ca1e18e544a4a6d14b611cd03b505d465cc967ba5162d59ce459b553d", "timestamp": "0xa", "totalDifficulty": "0x1", "transactions": [ diff --git a/internal/ethapi/testdata/eth_getBlockByNumber-number-latest-1.json b/internal/ethapi/testdata/eth_getBlockByNumber-number-latest-1.json index 4d54e3f10b..8f55c154c0 100644 --- a/internal/ethapi/testdata/eth_getBlockByNumber-number-latest-1.json +++ b/internal/ethapi/testdata/eth_getBlockByNumber-number-latest-1.json @@ -5,22 +5,22 @@ "extraData": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "gasLimit": "0x7a1200", "gasUsed": "0x5208", - "hash": "0x0583a9d630632001771b4ecc7d62574aec3825aff47e2a680b0ea4ddb79e7365", + "hash": "0x71f4093e0536817246679d6e183d836710027189eed7d0e09f1fde11a26f499c", "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "miner": "0x0000000000000000000000000000000000000000", "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", "nonce": "0x0000000000000000", "number": "0x9", - "parentHash": "0x2fab5c6892c66668842683ced6b384c2ee83bfd6096a58f451290cabaf57a63e", + "parentHash": "0x4f8c1e68991fdb03a668c72e42c454dc911f34416035ef59fdeb83218fe4ae84", "receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2", "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", "size": "0x2bb", - "stateRoot": "0x3703d70c6443e809ce035c2a8212dbf9813f6b7d1b0f597766e9023867a852f5", + "stateRoot": "0x67e974f6435c557282cd778ae1ae16dded6e84db121d3ab27b931726bd48e2ac", "timestamp": "0x5a", "totalDifficulty": "0x9", "transactions": [ { - "blockHash": "0x0583a9d630632001771b4ecc7d62574aec3825aff47e2a680b0ea4ddb79e7365", + "blockHash": "0x71f4093e0536817246679d6e183d836710027189eed7d0e09f1fde11a26f499c", "blockNumber": "0x9", "from": "0x703c4b2bd70c169f5717101caee543299fc946c7", "gas": "0x5208", diff --git a/internal/ethapi/testdata/eth_getBlockByNumber-tag-latest.json b/internal/ethapi/testdata/eth_getBlockByNumber-tag-latest.json index 7917a2acc7..02d29dd90b 100644 --- a/internal/ethapi/testdata/eth_getBlockByNumber-tag-latest.json +++ b/internal/ethapi/testdata/eth_getBlockByNumber-tag-latest.json @@ -5,17 +5,17 @@ "extraData": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "gasLimit": "0x7a1200", "gasUsed": "0x5208", - "hash": "0x1ec39e7ec46f8df1fb31cfca53fbf71a01869af8bd8f9a1bccbffc16ffa1461d", + "hash": "0xb64852ab9008988697eb2bc3aec63b97d79c77ff81df2de60535ef31453a5979", "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "miner": "0x0000000000000000000000000000000000000000", "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", "nonce": "0x0000000000000000", "number": "0xa", - "parentHash": "0x0583a9d630632001771b4ecc7d62574aec3825aff47e2a680b0ea4ddb79e7365", + "parentHash": "0x71f4093e0536817246679d6e183d836710027189eed7d0e09f1fde11a26f499c", "receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2", "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", "size": "0x2bb", - "stateRoot": "0x7e06187d15d50badf60930290fb292ebe43e79553ad8b7d8f1b614316631def7", + "stateRoot": "0x2b8307a066fd4575aca1fa6b42ef2c3a860a9c40337ea0d647c9f25e22062e34", "timestamp": "0x64", "totalDifficulty": "0xa", "transactions": [ diff --git a/internal/ethapi/testdata/eth_getBlockReceipts-block-with-blob-tx.json b/internal/ethapi/testdata/eth_getBlockReceipts-block-with-blob-tx.json index 5d888be080..24afbebc14 100644 --- a/internal/ethapi/testdata/eth_getBlockReceipts-block-with-blob-tx.json +++ b/internal/ethapi/testdata/eth_getBlockReceipts-block-with-blob-tx.json @@ -2,7 +2,7 @@ { "blobGasPrice": "0x1", "blobGasUsed": "0x20000", - "blockHash": "0x264b5f62e2900dd39a6c68af3ba656cffa3fe209614ca857af1f5702c6e2ba7e", + "blockHash": "0xff73bf27fcb39258f9c4a2f7f6eb018f7029f2d5bd960a63ebb4d7ecd8044545", "blockNumber": "0x6", "contractAddress": null, "cumulativeGasUsed": "0x5208", @@ -17,4 +17,4 @@ "transactionIndex": "0x0", "type": "0x3" } -] +] \ No newline at end of file diff --git a/internal/ethapi/testdata/eth_getBlockReceipts-block-with-contract-create-tx.json b/internal/ethapi/testdata/eth_getBlockReceipts-block-with-contract-create-tx.json index 1ef955ab92..84f48f0ea5 100644 --- a/internal/ethapi/testdata/eth_getBlockReceipts-block-with-contract-create-tx.json +++ b/internal/ethapi/testdata/eth_getBlockReceipts-block-with-contract-create-tx.json @@ -1,6 +1,6 @@ [ { - "blockHash": "0x0524420ca4d3974c72883de6ccdeca2e8be7eafeac88ff03d144ed16fe78063a", + "blockHash": "0xdcba2f7c99ad0f58002737f1393578f1b72aca3270c1722d9d0fbdc2439b0484", "blockNumber": "0x2", "contractAddress": "0xae9bea628c4ce503dcfd7e305cab4e29e7476592", "cumulativeGasUsed": "0xcf50", @@ -15,4 +15,4 @@ "transactionIndex": "0x0", "type": "0x0" } -] +] \ No newline at end of file diff --git a/internal/ethapi/testdata/eth_getBlockReceipts-block-with-dynamic-fee-tx.json b/internal/ethapi/testdata/eth_getBlockReceipts-block-with-dynamic-fee-tx.json index 96ca04b629..977cbe81df 100644 --- a/internal/ethapi/testdata/eth_getBlockReceipts-block-with-dynamic-fee-tx.json +++ b/internal/ethapi/testdata/eth_getBlockReceipts-block-with-dynamic-fee-tx.json @@ -1,6 +1,6 @@ [ { - "blockHash": "0x0b7437b9229f72a563918ee8c73e9a8f5e294f9d0e17db6bf8408cdf6fbc84b7", + "blockHash": "0xfa29fee1c5195fda47b23d3ce5259e314eb7578d18b76b36068d0e321db024e1", "blockNumber": "0x4", "contractAddress": null, "cumulativeGasUsed": "0x538d", @@ -15,4 +15,4 @@ "transactionIndex": "0x0", "type": "0x2" } -] +] \ No newline at end of file diff --git a/internal/ethapi/testdata/eth_getBlockReceipts-block-with-legacy-contract-call-tx.json b/internal/ethapi/testdata/eth_getBlockReceipts-block-with-legacy-contract-call-tx.json index f63bffdf0a..78bec365a3 100644 --- a/internal/ethapi/testdata/eth_getBlockReceipts-block-with-legacy-contract-call-tx.json +++ b/internal/ethapi/testdata/eth_getBlockReceipts-block-with-legacy-contract-call-tx.json @@ -1,6 +1,6 @@ [ { - "blockHash": "0x587918ec6a2187a54418dc5334d02a53b61c39578d095f4845be8d4f25dea5b5", + "blockHash": "0xd82a2ecb764df020971408362eed2f6521c9a7ae38ab991aba7c6ddd60ba7e64", "blockNumber": "0x3", "contractAddress": null, "cumulativeGasUsed": "0x5e28", @@ -19,7 +19,7 @@ "blockNumber": "0x3", "transactionHash": "0x7366a7738f47e32f5b6d292ca064b6b66f295d3931533a3745975be1191fccdf", "transactionIndex": "0x0", - "blockHash": "0x587918ec6a2187a54418dc5334d02a53b61c39578d095f4845be8d4f25dea5b5", + "blockHash": "0xd82a2ecb764df020971408362eed2f6521c9a7ae38ab991aba7c6ddd60ba7e64", "logIndex": "0x0", "removed": false } @@ -31,4 +31,4 @@ "transactionIndex": "0x0", "type": "0x0" } -] +] \ No newline at end of file diff --git a/internal/ethapi/testdata/eth_getBlockReceipts-block-with-legacy-transfer-tx.json b/internal/ethapi/testdata/eth_getBlockReceipts-block-with-legacy-transfer-tx.json index 6a7af4ce3c..e4c599bf22 100644 --- a/internal/ethapi/testdata/eth_getBlockReceipts-block-with-legacy-transfer-tx.json +++ b/internal/ethapi/testdata/eth_getBlockReceipts-block-with-legacy-transfer-tx.json @@ -1,6 +1,6 @@ [ { - "blockHash": "0x2918b59e4b455614c1b83c0281e2b8462af47ca9726fff31789cb168793015d7", + "blockHash": "0xf9081fe79fcdfd6a743577cc42fa17bec5e6cc1ebf5807b771724bf88b454b71", "blockNumber": "0x1", "contractAddress": null, "cumulativeGasUsed": "0x5208", @@ -15,4 +15,4 @@ "transactionIndex": "0x0", "type": "0x0" } -] +] \ No newline at end of file diff --git a/internal/ethapi/testdata/eth_getBlockReceipts-tag-latest.json b/internal/ethapi/testdata/eth_getBlockReceipts-tag-latest.json index 5d888be080..24afbebc14 100644 --- a/internal/ethapi/testdata/eth_getBlockReceipts-tag-latest.json +++ b/internal/ethapi/testdata/eth_getBlockReceipts-tag-latest.json @@ -2,7 +2,7 @@ { "blobGasPrice": "0x1", "blobGasUsed": "0x20000", - "blockHash": "0x264b5f62e2900dd39a6c68af3ba656cffa3fe209614ca857af1f5702c6e2ba7e", + "blockHash": "0xff73bf27fcb39258f9c4a2f7f6eb018f7029f2d5bd960a63ebb4d7ecd8044545", "blockNumber": "0x6", "contractAddress": null, "cumulativeGasUsed": "0x5208", @@ -17,4 +17,4 @@ "transactionIndex": "0x0", "type": "0x3" } -] +] \ No newline at end of file diff --git a/internal/ethapi/testdata/eth_getHeaderByHash-hash-0.json b/internal/ethapi/testdata/eth_getHeaderByHash-hash-0.json index eced05ccfc..2d905eb44b 100644 --- a/internal/ethapi/testdata/eth_getHeaderByHash-hash-0.json +++ b/internal/ethapi/testdata/eth_getHeaderByHash-hash-0.json @@ -4,7 +4,7 @@ "extraData": "0x", "gasLimit": "0x47e7c4", "gasUsed": "0x0", - "hash": "0x3ead7923676a44500c46ad2192a0fc084aa42063b1703e6866f138a47fb1a9ca", + "hash": "0xa779a1e82fd903ae98affe3a06a0a353906130052c24969b24237adf350bd03b", "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "miner": "0x0000000000000000000000000000000000000000", "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", @@ -13,7 +13,7 @@ "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", "receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", - "stateRoot": "0xfe168c5e9584a85927212e5bea5304bb7d0d8a893453b4b2c52176a72f585ae2", + "stateRoot": "0xd883f48b83cc9c1e8389453beb4ad4e572462eec049ca4fffbe16ecefb3fe937", "timestamp": "0x0", "totalDifficulty": "0x0", "transactionsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421" diff --git a/internal/ethapi/testdata/eth_getHeaderByHash-hash-1.json b/internal/ethapi/testdata/eth_getHeaderByHash-hash-1.json index d14b952ff6..b400b4ef33 100644 --- a/internal/ethapi/testdata/eth_getHeaderByHash-hash-1.json +++ b/internal/ethapi/testdata/eth_getHeaderByHash-hash-1.json @@ -5,16 +5,16 @@ "extraData": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "gasLimit": "0x7a1200", "gasUsed": "0x5208", - "hash": "0x0f67ad1fc8052afad4c24551748600c164091cf37e068adef76315025d3c78e7", + "hash": "0xb0a6fc2230444f86583fec57e9fa0aef3ebf36e094b5f2abeb231089697c50e0", "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "miner": "0x0000000000000000000000000000000000000000", "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", "nonce": "0x0000000000000000", "number": "0x1", - "parentHash": "0x3ead7923676a44500c46ad2192a0fc084aa42063b1703e6866f138a47fb1a9ca", + "parentHash": "0xa779a1e82fd903ae98affe3a06a0a353906130052c24969b24237adf350bd03b", "receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2", "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", - "stateRoot": "0x6b830601767ac4968163193facbe20123435180e325910b2c50efa21f778c697", + "stateRoot": "0xa17c770ca1e18e544a4a6d14b611cd03b505d465cc967ba5162d59ce459b553d", "timestamp": "0xa", "totalDifficulty": "0x1", "transactionsRoot": "0x87c65a3f1a98dafe282ace11eaf88b8f31bf41fe6794d401d2f986c1af84bcd5" diff --git a/internal/ethapi/testdata/eth_getHeaderByHash-hash-latest-1.json b/internal/ethapi/testdata/eth_getHeaderByHash-hash-latest-1.json index c90f186501..ed8758d9a0 100644 --- a/internal/ethapi/testdata/eth_getHeaderByHash-hash-latest-1.json +++ b/internal/ethapi/testdata/eth_getHeaderByHash-hash-latest-1.json @@ -5,16 +5,16 @@ "extraData": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "gasLimit": "0x7a1200", "gasUsed": "0x5208", - "hash": "0x0583a9d630632001771b4ecc7d62574aec3825aff47e2a680b0ea4ddb79e7365", + "hash": "0x71f4093e0536817246679d6e183d836710027189eed7d0e09f1fde11a26f499c", "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "miner": "0x0000000000000000000000000000000000000000", "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", "nonce": "0x0000000000000000", "number": "0x9", - "parentHash": "0x2fab5c6892c66668842683ced6b384c2ee83bfd6096a58f451290cabaf57a63e", + "parentHash": "0x4f8c1e68991fdb03a668c72e42c454dc911f34416035ef59fdeb83218fe4ae84", "receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2", "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", - "stateRoot": "0x3703d70c6443e809ce035c2a8212dbf9813f6b7d1b0f597766e9023867a852f5", + "stateRoot": "0x67e974f6435c557282cd778ae1ae16dded6e84db121d3ab27b931726bd48e2ac", "timestamp": "0x5a", "totalDifficulty": "0x9", "transactionsRoot": "0xe16929d9c7efab0f962c1ed8c1295ddff42d3026779ed1318ea079ca580ee4cb" diff --git a/internal/ethapi/testdata/eth_getHeaderByHash-hash-latest.json b/internal/ethapi/testdata/eth_getHeaderByHash-hash-latest.json index ce691fa435..1ac22e8b43 100644 --- a/internal/ethapi/testdata/eth_getHeaderByHash-hash-latest.json +++ b/internal/ethapi/testdata/eth_getHeaderByHash-hash-latest.json @@ -5,16 +5,16 @@ "extraData": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "gasLimit": "0x7a1200", "gasUsed": "0x5208", - "hash": "0x1ec39e7ec46f8df1fb31cfca53fbf71a01869af8bd8f9a1bccbffc16ffa1461d", + "hash": "0xb64852ab9008988697eb2bc3aec63b97d79c77ff81df2de60535ef31453a5979", "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "miner": "0x0000000000000000000000000000000000000000", "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", "nonce": "0x0000000000000000", "number": "0xa", - "parentHash": "0x0583a9d630632001771b4ecc7d62574aec3825aff47e2a680b0ea4ddb79e7365", + "parentHash": "0x71f4093e0536817246679d6e183d836710027189eed7d0e09f1fde11a26f499c", "receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2", "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", - "stateRoot": "0x7e06187d15d50badf60930290fb292ebe43e79553ad8b7d8f1b614316631def7", + "stateRoot": "0x2b8307a066fd4575aca1fa6b42ef2c3a860a9c40337ea0d647c9f25e22062e34", "timestamp": "0x64", "totalDifficulty": "0xa", "transactionsRoot": "0x69ff8003291e1cd08f75d174f070618f7291e4540b2e33f60b3375743e3fda01" diff --git a/internal/ethapi/testdata/eth_getHeaderByNumber-number-0.json b/internal/ethapi/testdata/eth_getHeaderByNumber-number-0.json index eced05ccfc..2d905eb44b 100644 --- a/internal/ethapi/testdata/eth_getHeaderByNumber-number-0.json +++ b/internal/ethapi/testdata/eth_getHeaderByNumber-number-0.json @@ -4,7 +4,7 @@ "extraData": "0x", "gasLimit": "0x47e7c4", "gasUsed": "0x0", - "hash": "0x3ead7923676a44500c46ad2192a0fc084aa42063b1703e6866f138a47fb1a9ca", + "hash": "0xa779a1e82fd903ae98affe3a06a0a353906130052c24969b24237adf350bd03b", "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "miner": "0x0000000000000000000000000000000000000000", "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", @@ -13,7 +13,7 @@ "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", "receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", - "stateRoot": "0xfe168c5e9584a85927212e5bea5304bb7d0d8a893453b4b2c52176a72f585ae2", + "stateRoot": "0xd883f48b83cc9c1e8389453beb4ad4e572462eec049ca4fffbe16ecefb3fe937", "timestamp": "0x0", "totalDifficulty": "0x0", "transactionsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421" diff --git a/internal/ethapi/testdata/eth_getHeaderByNumber-number-1.json b/internal/ethapi/testdata/eth_getHeaderByNumber-number-1.json index d14b952ff6..b400b4ef33 100644 --- a/internal/ethapi/testdata/eth_getHeaderByNumber-number-1.json +++ b/internal/ethapi/testdata/eth_getHeaderByNumber-number-1.json @@ -5,16 +5,16 @@ "extraData": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "gasLimit": "0x7a1200", "gasUsed": "0x5208", - "hash": "0x0f67ad1fc8052afad4c24551748600c164091cf37e068adef76315025d3c78e7", + "hash": "0xb0a6fc2230444f86583fec57e9fa0aef3ebf36e094b5f2abeb231089697c50e0", "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "miner": "0x0000000000000000000000000000000000000000", "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", "nonce": "0x0000000000000000", "number": "0x1", - "parentHash": "0x3ead7923676a44500c46ad2192a0fc084aa42063b1703e6866f138a47fb1a9ca", + "parentHash": "0xa779a1e82fd903ae98affe3a06a0a353906130052c24969b24237adf350bd03b", "receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2", "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", - "stateRoot": "0x6b830601767ac4968163193facbe20123435180e325910b2c50efa21f778c697", + "stateRoot": "0xa17c770ca1e18e544a4a6d14b611cd03b505d465cc967ba5162d59ce459b553d", "timestamp": "0xa", "totalDifficulty": "0x1", "transactionsRoot": "0x87c65a3f1a98dafe282ace11eaf88b8f31bf41fe6794d401d2f986c1af84bcd5" diff --git a/internal/ethapi/testdata/eth_getHeaderByNumber-number-latest-1.json b/internal/ethapi/testdata/eth_getHeaderByNumber-number-latest-1.json index c90f186501..ed8758d9a0 100644 --- a/internal/ethapi/testdata/eth_getHeaderByNumber-number-latest-1.json +++ b/internal/ethapi/testdata/eth_getHeaderByNumber-number-latest-1.json @@ -5,16 +5,16 @@ "extraData": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "gasLimit": "0x7a1200", "gasUsed": "0x5208", - "hash": "0x0583a9d630632001771b4ecc7d62574aec3825aff47e2a680b0ea4ddb79e7365", + "hash": "0x71f4093e0536817246679d6e183d836710027189eed7d0e09f1fde11a26f499c", "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "miner": "0x0000000000000000000000000000000000000000", "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", "nonce": "0x0000000000000000", "number": "0x9", - "parentHash": "0x2fab5c6892c66668842683ced6b384c2ee83bfd6096a58f451290cabaf57a63e", + "parentHash": "0x4f8c1e68991fdb03a668c72e42c454dc911f34416035ef59fdeb83218fe4ae84", "receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2", "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", - "stateRoot": "0x3703d70c6443e809ce035c2a8212dbf9813f6b7d1b0f597766e9023867a852f5", + "stateRoot": "0x67e974f6435c557282cd778ae1ae16dded6e84db121d3ab27b931726bd48e2ac", "timestamp": "0x5a", "totalDifficulty": "0x9", "transactionsRoot": "0xe16929d9c7efab0f962c1ed8c1295ddff42d3026779ed1318ea079ca580ee4cb" diff --git a/internal/ethapi/testdata/eth_getHeaderByNumber-tag-latest.json b/internal/ethapi/testdata/eth_getHeaderByNumber-tag-latest.json index ce691fa435..1ac22e8b43 100644 --- a/internal/ethapi/testdata/eth_getHeaderByNumber-tag-latest.json +++ b/internal/ethapi/testdata/eth_getHeaderByNumber-tag-latest.json @@ -5,16 +5,16 @@ "extraData": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "gasLimit": "0x7a1200", "gasUsed": "0x5208", - "hash": "0x1ec39e7ec46f8df1fb31cfca53fbf71a01869af8bd8f9a1bccbffc16ffa1461d", + "hash": "0xb64852ab9008988697eb2bc3aec63b97d79c77ff81df2de60535ef31453a5979", "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "miner": "0x0000000000000000000000000000000000000000", "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", "nonce": "0x0000000000000000", "number": "0xa", - "parentHash": "0x0583a9d630632001771b4ecc7d62574aec3825aff47e2a680b0ea4ddb79e7365", + "parentHash": "0x71f4093e0536817246679d6e183d836710027189eed7d0e09f1fde11a26f499c", "receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2", "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", - "stateRoot": "0x7e06187d15d50badf60930290fb292ebe43e79553ad8b7d8f1b614316631def7", + "stateRoot": "0x2b8307a066fd4575aca1fa6b42ef2c3a860a9c40337ea0d647c9f25e22062e34", "timestamp": "0x64", "totalDifficulty": "0xa", "transactionsRoot": "0x69ff8003291e1cd08f75d174f070618f7291e4540b2e33f60b3375743e3fda01" diff --git a/internal/ethapi/testdata/eth_getTransactionReceipt-blob-tx.json b/internal/ethapi/testdata/eth_getTransactionReceipt-blob-tx.json index e0db84ad00..674b7e4f28 100644 --- a/internal/ethapi/testdata/eth_getTransactionReceipt-blob-tx.json +++ b/internal/ethapi/testdata/eth_getTransactionReceipt-blob-tx.json @@ -1,7 +1,7 @@ { "blobGasPrice": "0x1", "blobGasUsed": "0x20000", - "blockHash": "0x264b5f62e2900dd39a6c68af3ba656cffa3fe209614ca857af1f5702c6e2ba7e", + "blockHash": "0xff73bf27fcb39258f9c4a2f7f6eb018f7029f2d5bd960a63ebb4d7ecd8044545", "blockNumber": "0x6", "contractAddress": null, "cumulativeGasUsed": "0x5208", @@ -15,4 +15,4 @@ "transactionHash": "0x7e71344129674f4bbfdaa86313d005a96581993d93ae3a30d81b13fa25579eb2", "transactionIndex": "0x0", "type": "0x3" -} +} \ No newline at end of file diff --git a/internal/ethapi/testdata/eth_getTransactionReceipt-create-contract-tx.json b/internal/ethapi/testdata/eth_getTransactionReceipt-create-contract-tx.json index 41206a6c7a..0345b77cef 100644 --- a/internal/ethapi/testdata/eth_getTransactionReceipt-create-contract-tx.json +++ b/internal/ethapi/testdata/eth_getTransactionReceipt-create-contract-tx.json @@ -1,5 +1,5 @@ { - "blockHash": "0x0524420ca4d3974c72883de6ccdeca2e8be7eafeac88ff03d144ed16fe78063a", + "blockHash": "0xdcba2f7c99ad0f58002737f1393578f1b72aca3270c1722d9d0fbdc2439b0484", "blockNumber": "0x2", "contractAddress": "0xae9bea628c4ce503dcfd7e305cab4e29e7476592", "cumulativeGasUsed": "0xcf50", @@ -13,4 +13,4 @@ "transactionHash": "0x22aa617165f83a9f8c191c2b7724ae43eeb1249bee06c98c03c7624c21d27dc8", "transactionIndex": "0x0", "type": "0x0" -} +} \ No newline at end of file diff --git a/internal/ethapi/testdata/eth_getTransactionReceipt-create-contract-with-access-list.json b/internal/ethapi/testdata/eth_getTransactionReceipt-create-contract-with-access-list.json index 0fb927479e..2d68a68bd9 100644 --- a/internal/ethapi/testdata/eth_getTransactionReceipt-create-contract-with-access-list.json +++ b/internal/ethapi/testdata/eth_getTransactionReceipt-create-contract-with-access-list.json @@ -1,5 +1,5 @@ { - "blockHash": "0x2bf952c71ce24c68f887c12ecba6f1a36f97f528259fe0574890969e1113187a", + "blockHash": "0xea28a367715debefc3b6d9f5ed5ab5b8d3a13b956e87f05f148852d3e1e522b5", "blockNumber": "0x5", "contractAddress": "0xfdaa97661a584d977b4d3abb5370766ff5b86a18", "cumulativeGasUsed": "0xe01c", @@ -13,4 +13,4 @@ "transactionHash": "0x8afe030574f663fe5096371d6f58a6287bfb3e0c73a5050220f5775a08e7abc9", "transactionIndex": "0x0", "type": "0x1" -} +} \ No newline at end of file diff --git a/internal/ethapi/testdata/eth_getTransactionReceipt-dynamic-tx-with-logs.json b/internal/ethapi/testdata/eth_getTransactionReceipt-dynamic-tx-with-logs.json index c322944f2b..6b558601b2 100644 --- a/internal/ethapi/testdata/eth_getTransactionReceipt-dynamic-tx-with-logs.json +++ b/internal/ethapi/testdata/eth_getTransactionReceipt-dynamic-tx-with-logs.json @@ -1,5 +1,5 @@ { - "blockHash": "0x0b7437b9229f72a563918ee8c73e9a8f5e294f9d0e17db6bf8408cdf6fbc84b7", + "blockHash": "0xfa29fee1c5195fda47b23d3ce5259e314eb7578d18b76b36068d0e321db024e1", "blockNumber": "0x4", "contractAddress": null, "cumulativeGasUsed": "0x538d", @@ -13,4 +13,4 @@ "transactionHash": "0x4e1e9194ca6f9d4e1736e9e441f66104f273548ed6d91b236a5f9c2ea10fa06d", "transactionIndex": "0x0", "type": "0x2" -} +} \ No newline at end of file diff --git a/internal/ethapi/testdata/eth_getTransactionReceipt-normal-transfer-tx.json b/internal/ethapi/testdata/eth_getTransactionReceipt-normal-transfer-tx.json index be72a02737..8411f0b255 100644 --- a/internal/ethapi/testdata/eth_getTransactionReceipt-normal-transfer-tx.json +++ b/internal/ethapi/testdata/eth_getTransactionReceipt-normal-transfer-tx.json @@ -1,5 +1,5 @@ { - "blockHash": "0x2918b59e4b455614c1b83c0281e2b8462af47ca9726fff31789cb168793015d7", + "blockHash": "0xf9081fe79fcdfd6a743577cc42fa17bec5e6cc1ebf5807b771724bf88b454b71", "blockNumber": "0x1", "contractAddress": null, "cumulativeGasUsed": "0x5208", @@ -13,4 +13,4 @@ "transactionHash": "0xdf92bc7c4c0341ecbdcd2a3ca7011fe9e21df4b8553bf0c8caabe6cb4a1aee26", "transactionIndex": "0x0", "type": "0x0" -} +} \ No newline at end of file diff --git a/internal/ethapi/testdata/eth_getTransactionReceipt-with-logs.json b/internal/ethapi/testdata/eth_getTransactionReceipt-with-logs.json index 7ced85c2a2..6aebc6190b 100644 --- a/internal/ethapi/testdata/eth_getTransactionReceipt-with-logs.json +++ b/internal/ethapi/testdata/eth_getTransactionReceipt-with-logs.json @@ -1,5 +1,5 @@ { - "blockHash": "0x587918ec6a2187a54418dc5334d02a53b61c39578d095f4845be8d4f25dea5b5", + "blockHash": "0xd82a2ecb764df020971408362eed2f6521c9a7ae38ab991aba7c6ddd60ba7e64", "blockNumber": "0x3", "contractAddress": null, "cumulativeGasUsed": "0x5e28", @@ -18,7 +18,7 @@ "blockNumber": "0x3", "transactionHash": "0x7366a7738f47e32f5b6d292ca064b6b66f295d3931533a3745975be1191fccdf", "transactionIndex": "0x0", - "blockHash": "0x587918ec6a2187a54418dc5334d02a53b61c39578d095f4845be8d4f25dea5b5", + "blockHash": "0xd82a2ecb764df020971408362eed2f6521c9a7ae38ab991aba7c6ddd60ba7e64", "logIndex": "0x0", "removed": false } @@ -29,4 +29,4 @@ "transactionHash": "0x7366a7738f47e32f5b6d292ca064b6b66f295d3931533a3745975be1191fccdf", "transactionIndex": "0x0", "type": "0x0" -} +} \ No newline at end of file diff --git a/internal/ethapi/transaction_args.go b/internal/ethapi/transaction_args.go index 9dd9ede18d..3be1289947 100644 --- a/internal/ethapi/transaction_args.go +++ b/internal/ethapi/transaction_args.go @@ -29,10 +29,12 @@ package ethapi import ( "bytes" "context" + "crypto/sha256" "errors" "fmt" "math/big" + "github.com/ava-labs/subnet-evm/consensus/misc/eip4844" "github.com/ava-labs/subnet-evm/core" "github.com/ava-labs/subnet-evm/core/types" "github.com/ava-labs/subnet-evm/params" @@ -40,7 +42,13 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/math" + "github.com/ethereum/go-ethereum/crypto/kzg4844" "github.com/ethereum/go-ethereum/log" + "github.com/holiman/uint256" +) + +var ( + maxBlobsPerTransaction = params.MaxBlobGasPerBlock / params.BlobTxBlobGasPerBlob ) // TransactionArgs represents the arguments to construct a new transaction @@ -64,6 +72,18 @@ type TransactionArgs struct { // Introduced by AccessListTxType transaction. AccessList *types.AccessList `json:"accessList,omitempty"` ChainID *hexutil.Big `json:"chainId,omitempty"` + + // For BlobTxType + BlobFeeCap *hexutil.Big `json:"maxFeePerBlobGas"` + BlobHashes []common.Hash `json:"blobVersionedHashes,omitempty"` + + // For BlobTxType transactions with blob sidecar + Blobs []kzg4844.Blob `json:"blobs"` + Commitments []kzg4844.Commitment `json:"commitments"` + Proofs []kzg4844.Proof `json:"proofs"` + + // This configures whether blobs are allowed to be passed. + blobSidecarAllowed bool } // from retrieves the transaction sender address. @@ -86,10 +106,14 @@ func (args *TransactionArgs) data() []byte { } // setDefaults fills in default values for unspecified tx fields. -func (args *TransactionArgs) setDefaults(ctx context.Context, b Backend) error { +func (args *TransactionArgs) setDefaults(ctx context.Context, b Backend, skipGasEstimation bool) error { + if err := args.setBlobTxSidecar(ctx, b); err != nil { + return err + } if err := args.setFeeDefaults(ctx, b); err != nil { return err } + if args.Value == nil { args.Value = new(hexutil.Big) } @@ -103,32 +127,58 @@ func (args *TransactionArgs) setDefaults(ctx context.Context, b Backend) error { if args.Data != nil && args.Input != nil && !bytes.Equal(*args.Data, *args.Input) { return errors.New(`both "data" and "input" are set and not equal. Please use "input" to pass transaction call data`) } - if args.To == nil && len(args.data()) == 0 { - return errors.New(`contract creation without any data provided`) + + // BlobTx fields + if args.BlobHashes != nil && len(args.BlobHashes) == 0 { + return errors.New(`need at least 1 blob for a blob transaction`) } - // Estimate the gas usage if necessary. - if args.Gas == nil { - // These fields are immutable during the estimation, safe to - // pass the pointer directly. - data := args.data() - callArgs := TransactionArgs{ - From: args.From, - To: args.To, - GasPrice: args.GasPrice, - MaxFeePerGas: args.MaxFeePerGas, - MaxPriorityFeePerGas: args.MaxPriorityFeePerGas, - Value: args.Value, - Data: (*hexutil.Bytes)(&data), - AccessList: args.AccessList, + if args.BlobHashes != nil && len(args.BlobHashes) > maxBlobsPerTransaction { + return fmt.Errorf(`too many blobs in transaction (have=%d, max=%d)`, len(args.BlobHashes), maxBlobsPerTransaction) + } + + // create check + if args.To == nil { + if args.BlobHashes != nil { + return errors.New(`missing "to" in blob transaction`) } - pendingBlockNr := rpc.BlockNumberOrHashWithNumber(rpc.PendingBlockNumber) - estimated, err := DoEstimateGas(ctx, b, callArgs, pendingBlockNr, nil, b.RPCGasCap()) - if err != nil { - return err + if len(args.data()) == 0 { + return errors.New(`contract creation without any data provided`) } - args.Gas = &estimated - log.Trace("Estimate gas usage automatically", "gas", args.Gas) } + + if args.Gas == nil { + if skipGasEstimation { // Skip gas usage estimation if a precise gas limit is not critical, e.g., in non-transaction calls. + gas := hexutil.Uint64(b.RPCGasCap()) + if gas == 0 { + gas = hexutil.Uint64(math.MaxUint64 / 2) + } + args.Gas = &gas + } else { // Estimate the gas usage otherwise. + // These fields are immutable during the estimation, safe to + // pass the pointer directly. + data := args.data() + callArgs := TransactionArgs{ + From: args.From, + To: args.To, + GasPrice: args.GasPrice, + MaxFeePerGas: args.MaxFeePerGas, + MaxPriorityFeePerGas: args.MaxPriorityFeePerGas, + Value: args.Value, + Data: (*hexutil.Bytes)(&data), + AccessList: args.AccessList, + BlobFeeCap: args.BlobFeeCap, + BlobHashes: args.BlobHashes, + } + latestBlockNr := rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber) + estimated, err := DoEstimateGas(ctx, b, callArgs, latestBlockNr, nil, b.RPCGasCap()) + if err != nil { + return err + } + args.Gas = &estimated + log.Trace("Estimate gas usage automatically", "gas", args.Gas) + } + } + // If chain id is provided, ensure it matches the local chain id. Otherwise, set the local // chain id as the default. want := b.ChainConfig().ChainID @@ -150,6 +200,14 @@ type feeBackend interface { // setFeeDefaults fills in default fee values for unspecified tx fields. func (args *TransactionArgs) setFeeDefaults(ctx context.Context, b feeBackend) error { + head := b.CurrentHeader() + // Sanity check the EIP-4844 fee parameters. + if args.BlobFeeCap != nil && args.BlobFeeCap.ToInt().Sign() == 0 { + return errors.New("maxFeePerBlobGas, if specified, must be non-zero") + } + if err := args.setCancunFeeDefaults(ctx, head, b); err != nil { + return err + } // If both gasPrice and at least one of the EIP-1559 fee parameters are specified, error. if args.GasPrice != nil && (args.MaxFeePerGas != nil || args.MaxPriorityFeePerGas != nil) { return errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified") @@ -159,7 +217,6 @@ func (args *TransactionArgs) setFeeDefaults(ctx context.Context, b feeBackend) e // other tx values. See https://github.com/ethereum/go-ethereum/pull/23274 // for more information. eip1559ParamsSet := args.MaxFeePerGas != nil && args.MaxPriorityFeePerGas != nil - // Sanity check the EIP-1559 fee parameters if present. if args.GasPrice == nil && eip1559ParamsSet { if args.MaxFeePerGas.ToInt().Sign() == 0 { @@ -170,8 +227,8 @@ func (args *TransactionArgs) setFeeDefaults(ctx context.Context, b feeBackend) e } return nil // No need to set anything, user already set MaxFeePerGas and MaxPriorityFeePerGas } + // Sanity check the non-EIP-1559 fee parameters. - head := b.CurrentHeader() isLondon := b.ChainConfig().IsSubnetEVM(head.Time) if args.GasPrice != nil && !eip1559ParamsSet { // Zero gas-price is not allowed after London fork @@ -201,7 +258,26 @@ func (args *TransactionArgs) setFeeDefaults(ctx context.Context, b feeBackend) e return nil } -// setSubnetEVMFeeDefault fills in reasonable default fee values for unspecified fields. +// setCancunFeeDefaults fills in reasonable default fee values for unspecified fields. +func (args *TransactionArgs) setCancunFeeDefaults(ctx context.Context, head *types.Header, b feeBackend) error { + // Set maxFeePerBlobGas if it is missing. + if args.BlobHashes != nil && args.BlobFeeCap == nil { + var excessBlobGas uint64 + if head.ExcessBlobGas != nil { + excessBlobGas = *head.ExcessBlobGas + } + // ExcessBlobGas must be set for a Cancun block. + blobBaseFee := eip4844.CalcBlobFee(excessBlobGas) + // Set the max fee to be 2 times larger than the previous block's blob base fee. + // The additional slack allows the tx to not become invalidated if the base + // fee is rising. + val := new(big.Int).Mul(blobBaseFee, big.NewInt(2)) + args.BlobFeeCap = (*hexutil.Big)(val) + } + return nil +} + +// setSubnetEVMFeeDefaults fills in reasonable default fee values for unspecified fields. func (args *TransactionArgs) setSubnetEVMFeeDefault(ctx context.Context, head *types.Header, b feeBackend) error { // Set maxPriorityFeePerGas if it is missing. if args.MaxPriorityFeePerGas == nil { @@ -229,6 +305,81 @@ func (args *TransactionArgs) setSubnetEVMFeeDefault(ctx context.Context, head *t return nil } +// setBlobTxSidecar adds the blob tx +func (args *TransactionArgs) setBlobTxSidecar(ctx context.Context, b Backend) error { + // No blobs, we're done. + if args.Blobs == nil { + return nil + } + + // Passing blobs is not allowed in all contexts, only in specific methods. + if !args.blobSidecarAllowed { + return errors.New(`"blobs" is not supported for this RPC method`) + } + + n := len(args.Blobs) + // Assume user provides either only blobs (w/o hashes), or + // blobs together with commitments and proofs. + if args.Commitments == nil && args.Proofs != nil { + return errors.New(`blob proofs provided while commitments were not`) + } else if args.Commitments != nil && args.Proofs == nil { + return errors.New(`blob commitments provided while proofs were not`) + } + + // len(blobs) == len(commitments) == len(proofs) == len(hashes) + if args.Commitments != nil && len(args.Commitments) != n { + return fmt.Errorf("number of blobs and commitments mismatch (have=%d, want=%d)", len(args.Commitments), n) + } + if args.Proofs != nil && len(args.Proofs) != n { + return fmt.Errorf("number of blobs and proofs mismatch (have=%d, want=%d)", len(args.Proofs), n) + } + if args.BlobHashes != nil && len(args.BlobHashes) != n { + return fmt.Errorf("number of blobs and hashes mismatch (have=%d, want=%d)", len(args.BlobHashes), n) + } + + if args.Commitments == nil { + // Generate commitment and proof. + commitments := make([]kzg4844.Commitment, n) + proofs := make([]kzg4844.Proof, n) + for i, b := range args.Blobs { + c, err := kzg4844.BlobToCommitment(b) + if err != nil { + return fmt.Errorf("blobs[%d]: error computing commitment: %v", i, err) + } + commitments[i] = c + p, err := kzg4844.ComputeBlobProof(b, c) + if err != nil { + return fmt.Errorf("blobs[%d]: error computing proof: %v", i, err) + } + proofs[i] = p + } + args.Commitments = commitments + args.Proofs = proofs + } else { + for i, b := range args.Blobs { + if err := kzg4844.VerifyBlobProof(b, args.Commitments[i], args.Proofs[i]); err != nil { + return fmt.Errorf("failed to verify blob proof: %v", err) + } + } + } + + hashes := make([]common.Hash, n) + hasher := sha256.New() + for i, c := range args.Commitments { + hashes[i] = kzg4844.CalcBlobHashV1(hasher, &c) + } + if args.BlobHashes != nil { + for i, h := range hashes { + if h != args.BlobHashes[i] { + return fmt.Errorf("blob hash verification failed (have=%s, want=%s)", args.BlobHashes[i], h) + } + } + } else { + args.BlobHashes = hashes + } + return nil +} + // ToMessage converts the transaction arguments to the Message type used by the // core evm. This method is used in calls and traces that do not require a real // live transaction. @@ -253,9 +404,10 @@ func (args *TransactionArgs) ToMessage(globalGasCap uint64, baseFee *big.Int) (* gas = globalGasCap } var ( - gasPrice *big.Int - gasFeeCap *big.Int - gasTipCap *big.Int + gasPrice *big.Int + gasFeeCap *big.Int + gasTipCap *big.Int + blobFeeCap *big.Int ) if baseFee == nil { // If there's no basefee, then it must be a non-1559 execution @@ -287,6 +439,11 @@ func (args *TransactionArgs) ToMessage(globalGasCap uint64, baseFee *big.Int) (* } } } + if args.BlobFeeCap != nil { + blobFeeCap = args.BlobFeeCap.ToInt() + } else if args.BlobHashes != nil { + blobFeeCap = new(big.Int) + } value := new(big.Int) if args.Value != nil { value = args.Value.ToInt() @@ -306,6 +463,8 @@ func (args *TransactionArgs) ToMessage(globalGasCap uint64, baseFee *big.Int) (* GasTipCap: gasTipCap, Data: data, AccessList: accessList, + BlobGasFeeCap: blobFeeCap, + BlobHashes: args.BlobHashes, SkipAccountChecks: true, } return msg, nil @@ -316,6 +475,32 @@ func (args *TransactionArgs) ToMessage(globalGasCap uint64, baseFee *big.Int) (* func (args *TransactionArgs) toTransaction() *types.Transaction { var data types.TxData switch { + case args.BlobHashes != nil: + al := types.AccessList{} + if args.AccessList != nil { + al = *args.AccessList + } + data = &types.BlobTx{ + To: *args.To, + ChainID: uint256.MustFromBig((*big.Int)(args.ChainID)), + Nonce: uint64(*args.Nonce), + Gas: uint64(*args.Gas), + GasFeeCap: uint256.MustFromBig((*big.Int)(args.MaxFeePerGas)), + GasTipCap: uint256.MustFromBig((*big.Int)(args.MaxPriorityFeePerGas)), + Value: uint256.MustFromBig((*big.Int)(args.Value)), + Data: args.data(), + AccessList: al, + BlobHashes: args.BlobHashes, + BlobFeeCap: uint256.MustFromBig((*big.Int)(args.BlobFeeCap)), + } + if args.Blobs != nil { + data.(*types.BlobTx).Sidecar = &types.BlobTxSidecar{ + Blobs: args.Blobs, + Commitments: args.Commitments, + Proofs: args.Proofs, + } + } + case args.MaxFeePerGas != nil: al := types.AccessList{} if args.AccessList != nil { @@ -332,6 +517,7 @@ func (args *TransactionArgs) toTransaction() *types.Transaction { Data: args.data(), AccessList: al, } + case args.AccessList != nil: data = &types.AccessListTx{ To: args.To, @@ -343,6 +529,7 @@ func (args *TransactionArgs) toTransaction() *types.Transaction { Data: args.data(), AccessList: *args.AccessList, } + default: data = &types.LegacyTx{ To: args.To, @@ -356,8 +543,7 @@ func (args *TransactionArgs) toTransaction() *types.Transaction { return types.NewTx(data) } -// ToTransaction converts the arguments to a transaction. -// This assumes that setDefaults has been called. -func (args *TransactionArgs) ToTransaction() *types.Transaction { - return args.toTransaction() +// IsEIP4844 returns an indicator if the args contains EIP4844 fields. +func (args *TransactionArgs) IsEIP4844() bool { + return args.BlobHashes != nil || args.BlobFeeCap != nil } diff --git a/internal/ethapi/transaction_args_test.go b/internal/ethapi/transaction_args_test.go index 98bdde2edf..a439553909 100644 --- a/internal/ethapi/transaction_args_test.go +++ b/internal/ethapi/transaction_args_test.go @@ -45,11 +45,11 @@ var _ feeBackend = &backendMock{} // TestSetFeeDefaults tests the logic for filling in default fee values works as expected. func TestSetFeeDefaults(t *testing.T) { type test struct { - name string - isLondon bool - in *TransactionArgs - want *TransactionArgs - err error + name string + fork string // options: legacy, london, cancun + in *TransactionArgs + want *TransactionArgs + err error } var ( @@ -64,28 +64,28 @@ func TestSetFeeDefaults(t *testing.T) { // Legacy txs { "legacy tx pre-London", - false, + "legacy", &TransactionArgs{}, &TransactionArgs{GasPrice: fortytwo}, nil, }, { "legacy tx pre-London with zero price", - false, + "legacy", &TransactionArgs{GasPrice: zero}, &TransactionArgs{GasPrice: zero}, nil, }, { "legacy tx post-London, explicit gas price", - true, + "london", &TransactionArgs{GasPrice: fortytwo}, &TransactionArgs{GasPrice: fortytwo}, nil, }, { "legacy tx post-London with zero price", - true, + "london", &TransactionArgs{GasPrice: zero}, nil, errors.New("gasPrice must be non-zero after london fork"), @@ -94,35 +94,35 @@ func TestSetFeeDefaults(t *testing.T) { // Access list txs { "access list tx pre-London", - false, + "legacy", &TransactionArgs{AccessList: al}, &TransactionArgs{AccessList: al, GasPrice: fortytwo}, nil, }, { "access list tx post-London, explicit gas price", - false, + "legacy", &TransactionArgs{AccessList: al, GasPrice: fortytwo}, &TransactionArgs{AccessList: al, GasPrice: fortytwo}, nil, }, { "access list tx post-London", - true, + "london", &TransactionArgs{AccessList: al}, &TransactionArgs{AccessList: al, MaxFeePerGas: maxFee, MaxPriorityFeePerGas: fortytwo}, nil, }, { "access list tx post-London, only max fee", - true, + "london", &TransactionArgs{AccessList: al, MaxFeePerGas: maxFee}, &TransactionArgs{AccessList: al, MaxFeePerGas: maxFee, MaxPriorityFeePerGas: fortytwo}, nil, }, { "access list tx post-London, only priority fee", - true, + "london", &TransactionArgs{AccessList: al, MaxFeePerGas: maxFee}, &TransactionArgs{AccessList: al, MaxFeePerGas: maxFee, MaxPriorityFeePerGas: fortytwo}, nil, @@ -131,56 +131,56 @@ func TestSetFeeDefaults(t *testing.T) { // Dynamic fee txs { "dynamic tx post-London", - true, + "london", &TransactionArgs{}, &TransactionArgs{MaxFeePerGas: maxFee, MaxPriorityFeePerGas: fortytwo}, nil, }, { "dynamic tx post-London, only max fee", - true, + "london", &TransactionArgs{MaxFeePerGas: maxFee}, &TransactionArgs{MaxFeePerGas: maxFee, MaxPriorityFeePerGas: fortytwo}, nil, }, { "dynamic tx post-London, only priority fee", - true, + "london", &TransactionArgs{MaxFeePerGas: maxFee}, &TransactionArgs{MaxFeePerGas: maxFee, MaxPriorityFeePerGas: fortytwo}, nil, }, { "dynamic fee tx pre-London, maxFee set", - false, + "legacy", &TransactionArgs{MaxFeePerGas: maxFee}, nil, errors.New("maxFeePerGas and maxPriorityFeePerGas are not valid before London is active"), }, { "dynamic fee tx pre-London, priorityFee set", - false, + "legacy", &TransactionArgs{MaxPriorityFeePerGas: fortytwo}, nil, errors.New("maxFeePerGas and maxPriorityFeePerGas are not valid before London is active"), }, { "dynamic fee tx, maxFee < priorityFee", - true, + "london", &TransactionArgs{MaxFeePerGas: maxFee, MaxPriorityFeePerGas: (*hexutil.Big)(big.NewInt(1000))}, nil, errors.New("maxFeePerGas (0x3e) < maxPriorityFeePerGas (0x3e8)"), }, { "dynamic fee tx, maxFee < priorityFee while setting default", - true, + "london", &TransactionArgs{MaxFeePerGas: (*hexutil.Big)(big.NewInt(7))}, nil, errors.New("maxFeePerGas (0x7) < maxPriorityFeePerGas (0x2a)"), }, { "dynamic fee tx post-London, explicit gas price", - true, + "london", &TransactionArgs{MaxFeePerGas: zero, MaxPriorityFeePerGas: zero}, nil, errors.New("maxFeePerGas must be non-zero"), @@ -189,41 +189,66 @@ func TestSetFeeDefaults(t *testing.T) { // Misc { "set all fee parameters", - false, + "legacy", &TransactionArgs{GasPrice: fortytwo, MaxFeePerGas: maxFee, MaxPriorityFeePerGas: fortytwo}, nil, errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified"), }, { "set gas price and maxPriorityFee", - false, + "legacy", &TransactionArgs{GasPrice: fortytwo, MaxPriorityFeePerGas: fortytwo}, nil, errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified"), }, { "set gas price and maxFee", - true, + "london", &TransactionArgs{GasPrice: fortytwo, MaxFeePerGas: maxFee}, nil, errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified"), }, + // EIP-4844 + { + "set gas price and maxFee for blob transaction", + "cancun", + &TransactionArgs{GasPrice: fortytwo, MaxFeePerGas: maxFee, BlobHashes: []common.Hash{}}, + nil, + errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified"), + }, + { + "fill maxFeePerBlobGas", + "cancun", + &TransactionArgs{BlobHashes: []common.Hash{}}, + &TransactionArgs{BlobHashes: []common.Hash{}, BlobFeeCap: (*hexutil.Big)(big.NewInt(4)), MaxFeePerGas: maxFee, MaxPriorityFeePerGas: fortytwo}, + nil, + }, + { + "fill maxFeePerBlobGas when dynamic fees are set", + "cancun", + &TransactionArgs{BlobHashes: []common.Hash{}, MaxFeePerGas: maxFee, MaxPriorityFeePerGas: fortytwo}, + &TransactionArgs{BlobHashes: []common.Hash{}, BlobFeeCap: (*hexutil.Big)(big.NewInt(4)), MaxFeePerGas: maxFee, MaxPriorityFeePerGas: fortytwo}, + nil, + }, } ctx := context.Background() for i, test := range tests { - if test.isLondon { - b.activateLondon() - } else { - b.deactivateLondon() + if err := b.setFork(test.fork); err != nil { + t.Fatalf("failed to set fork: %v", err) } got := test.in err := got.setFeeDefaults(ctx, b) - if err != nil && err.Error() == test.err.Error() { - // Test threw expected error. + if err != nil { + if test.err == nil { + t.Fatalf("test %d (%s): unexpected error: %s", i, test.name, err) + } else if err.Error() != test.err.Error() { + t.Fatalf("test %d (%s): unexpected error: (got: %s, want: %s)", i, test.name, err, test.err) + } + // Matching error. continue - } else if err != nil { - t.Fatalf("test %d (%s): unexpected error: %s", i, test.name, err) + } else if test.err != nil { + t.Fatalf("test %d (%s): expected error: %s", i, test.name, test.err) } if !reflect.DeepEqual(got, test.want) { t.Fatalf("test %d (%s): did not fill defaults as expected: (got: %v, want: %v)", i, test.name, got, test.want) @@ -237,6 +262,7 @@ type backendMock struct { } func newBackendMock() *backendMock { + var cancunTime uint64 = 600 config := ¶ms.ChainConfig{ ChainID: big.NewInt(42), HomesteadBlock: big.NewInt(0), @@ -249,8 +275,9 @@ func newBackendMock() *backendMock { IstanbulBlock: big.NewInt(0), MuirGlacierBlock: big.NewInt(0), NetworkUpgrades: params.NetworkUpgrades{ - SubnetEVMTimestamp: utils.NewUint64(1000), + SubnetEVMTimestamp: utils.NewUint64(100), }, + CancunTime: &cancunTime, } return &backendMock{ current: &types.Header{ @@ -266,13 +293,23 @@ func newBackendMock() *backendMock { } } -func (b *backendMock) activateLondon() { - b.current.Time = uint64(1100) +func (b *backendMock) setFork(fork string) error { + if fork == "legacy" { + b.current.Time = uint64(90) // Before SubnetEVMTimestamp + } else if fork == "london" { + b.current.Time = uint64(110) // After SubnetEVMTimestamp + } else if fork == "cancun" { + b.current.Number = big.NewInt(1100) + b.current.Time = 700 + // Blob base fee will be 2 + excess := uint64(2314058) + b.current.ExcessBlobGas = &excess + } else { + return errors.New("invalid fork") + } + return nil } -func (b *backendMock) deactivateLondon() { - b.current.Time = uint64(900) -} func (b *backendMock) SuggestGasTipCap(ctx context.Context) (*big.Int, error) { return big.NewInt(42), nil } diff --git a/internal/flags/flags.go b/internal/flags/flags.go index ed3a14bcf4..167a485ab7 100644 --- a/internal/flags/flags.go +++ b/internal/flags/flags.go @@ -266,7 +266,8 @@ type BigFlag struct { Hidden bool HasBeenSet bool - Value *big.Int + Value *big.Int + defaultValue *big.Int Aliases []string EnvVars []string @@ -279,6 +280,10 @@ func (f *BigFlag) IsSet() bool { return f.HasBeenSet } func (f *BigFlag) String() string { return cli.FlagStringer(f) } func (f *BigFlag) Apply(set *flag.FlagSet) error { + // Set default value so that environment wont be able to overwrite it + if f.Value != nil { + f.defaultValue = new(big.Int).Set(f.Value) + } for _, envVar := range f.EnvVars { envVar = strings.TrimSpace(envVar) if value, found := syscall.Getenv(envVar); found { @@ -293,7 +298,6 @@ func (f *BigFlag) Apply(set *flag.FlagSet) error { f.Value = new(big.Int) set.Var((*bigValue)(f.Value), f.Name, f.Usage) }) - return nil } @@ -320,7 +324,7 @@ func (f *BigFlag) GetDefaultText() string { if f.DefaultText != "" { return f.DefaultText } - return f.GetValue() + return f.defaultValue.String() } // bigValue turns *big.Int into a flag.Value diff --git a/internal/flags/helpers.go b/internal/flags/helpers.go index 0724e4ccee..dc54193ec9 100644 --- a/internal/flags/helpers.go +++ b/internal/flags/helpers.go @@ -51,7 +51,7 @@ func NewApp(usage string) *cli.App { app.EnableBashCompletion = true app.Version = params.VersionWithCommit(git.Commit, git.Date) app.Usage = usage - app.Copyright = "Copyright 2013-2023 The go-ethereum Authors" + app.Copyright = "Copyright 2013-2024 The go-ethereum Authors" app.Before = func(ctx *cli.Context) error { MigrateGlobalFlags(ctx) return nil @@ -125,7 +125,7 @@ func doMigrateFlags(ctx *cli.Context) { for _, parent := range ctx.Lineage()[1:] { if parent.IsSet(name) { // When iterating across the lineage, we will be served both - // the 'canon' and alias formats of all commmands. In most cases, + // the 'canon' and alias formats of all commands. In most cases, // it's fine to set it in the ctx multiple times (one for each // name), however, the Slice-flags are not fine. // The slice-flags accumulate, so if we set it once as diff --git a/log/logger.go b/log/logger.go index b3e60d6451..a3e78748a9 100644 --- a/log/logger.go +++ b/log/logger.go @@ -106,7 +106,7 @@ func LevelAlignedString(l slog.Level) string { } } -// LevelString returns a 5-character string containing the name of a Lvl. +// LevelString returns a string containing the name of a Lvl. func LevelString(l slog.Level) string { switch l { case LevelTrace: @@ -118,7 +118,7 @@ func LevelString(l slog.Level) string { case slog.LevelWarn: return "warn" case slog.LevelError: - return "eror" + return "error" case LevelCrit: return "crit" default: diff --git a/metrics/counter.go b/metrics/counter.go index cb81599c21..dbe8e16a90 100644 --- a/metrics/counter.go +++ b/metrics/counter.go @@ -8,7 +8,7 @@ type CounterSnapshot interface { Count() int64 } -// Counters hold an int64 value that can be incremented and decremented. +// Counter hold an int64 value that can be incremented and decremented. type Counter interface { Clear() Dec(int64) diff --git a/metrics/gauge.go b/metrics/gauge.go index 68f8f11abc..5933df3107 100644 --- a/metrics/gauge.go +++ b/metrics/gauge.go @@ -2,12 +2,12 @@ package metrics import "sync/atomic" -// gaugeSnapshot contains a readonly int64. +// GaugeSnapshot contains a readonly int64. type GaugeSnapshot interface { Value() int64 } -// Gauges hold an int64 value that can be set arbitrarily. +// Gauge holds an int64 value that can be set arbitrarily. type Gauge interface { Snapshot() GaugeSnapshot Update(int64) @@ -74,7 +74,7 @@ func (g *StandardGauge) Update(v int64) { g.value.Store(v) } -// Update updates the gauge's value if v is larger then the current valie. +// Update updates the gauge's value if v is larger then the current value. func (g *StandardGauge) UpdateIfGt(v int64) { for { exist := g.value.Load() diff --git a/metrics/gauge_float64.go b/metrics/gauge_float64.go index 967f2bc60e..c1c3c6b6e6 100644 --- a/metrics/gauge_float64.go +++ b/metrics/gauge_float64.go @@ -48,7 +48,7 @@ type gaugeFloat64Snapshot float64 // Value returns the value at the time the snapshot was taken. func (g gaugeFloat64Snapshot) Value() float64 { return float64(g) } -// NilGauge is a no-op Gauge. +// NilGaugeFloat64 is a no-op Gauge. type NilGaugeFloat64 struct{} func (NilGaugeFloat64) Snapshot() GaugeFloat64Snapshot { return NilGaugeFloat64{} } diff --git a/metrics/gauge_info.go b/metrics/gauge_info.go index c44b2d85f3..0010edc324 100644 --- a/metrics/gauge_info.go +++ b/metrics/gauge_info.go @@ -9,7 +9,7 @@ type GaugeInfoSnapshot interface { Value() GaugeInfoValue } -// GaugeInfos hold a GaugeInfoValue value that can be set arbitrarily. +// GaugeInfo holds a GaugeInfoValue value that can be set arbitrarily. type GaugeInfo interface { Update(GaugeInfoValue) Snapshot() GaugeInfoSnapshot diff --git a/metrics/healthcheck.go b/metrics/healthcheck.go index f1ae31e34a..adcd15ab58 100644 --- a/metrics/healthcheck.go +++ b/metrics/healthcheck.go @@ -1,6 +1,6 @@ package metrics -// Healthchecks hold an error value describing an arbitrary up/down status. +// Healthcheck holds an error value describing an arbitrary up/down status. type Healthcheck interface { Check() Error() error diff --git a/metrics/histogram.go b/metrics/histogram.go index 44de588bc1..10259a2463 100644 --- a/metrics/histogram.go +++ b/metrics/histogram.go @@ -4,7 +4,7 @@ type HistogramSnapshot interface { SampleSnapshot } -// Histograms calculate distribution statistics from a series of int64 values. +// Histogram calculates distribution statistics from a series of int64 values. type Histogram interface { Clear() Update(int64) diff --git a/miner/miner.go b/miner/miner.go index 14e5ba8d75..a325bff0d2 100644 --- a/miner/miner.go +++ b/miner/miner.go @@ -47,7 +47,8 @@ type Backend interface { // Config is the configuration parameters of mining. type Config struct { - Etherbase common.Address `toml:",omitempty"` // Public address for block mining rewards + Etherbase common.Address `toml:",omitempty"` // Public address for block mining rewards + TestOnlyAllowDuplicateBlocks bool // Allow mining of duplicate blocks (used in tests only) } type Miner struct { diff --git a/miner/ordering.go b/miner/ordering.go index 70a2a42eb6..40bc68d032 100644 --- a/miner/ordering.go +++ b/miner/ordering.go @@ -33,26 +33,29 @@ import ( "github.com/ava-labs/subnet-evm/core/txpool" "github.com/ava-labs/subnet-evm/core/types" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/math" + "github.com/holiman/uint256" ) // txWithMinerFee wraps a transaction with its gas price or effective miner gasTipCap type txWithMinerFee struct { tx *txpool.LazyTransaction from common.Address - fees *big.Int + fees *uint256.Int } // newTxWithMinerFee creates a wrapped transaction, calculating the effective // miner gasTipCap if a base fee is provided. // Returns error in case of a negative effective miner gasTipCap. -func newTxWithMinerFee(tx *txpool.LazyTransaction, from common.Address, baseFee *big.Int) (*txWithMinerFee, error) { - tip := new(big.Int).Set(tx.GasTipCap) +func newTxWithMinerFee(tx *txpool.LazyTransaction, from common.Address, baseFee *uint256.Int) (*txWithMinerFee, error) { + tip := new(uint256.Int).Set(tx.GasTipCap) if baseFee != nil { if tx.GasFeeCap.Cmp(baseFee) < 0 { return nil, types.ErrGasFeeCapTooLow } - tip = math.BigMin(tx.GasTipCap, new(big.Int).Sub(tx.GasFeeCap, baseFee)) + tip = new(uint256.Int).Sub(tx.GasFeeCap, baseFee) + if tip.Gt(tx.GasTipCap) { + tip = tx.GasTipCap + } } return &txWithMinerFee{ tx: tx, @@ -97,7 +100,7 @@ type transactionsByPriceAndNonce struct { txs map[common.Address][]*txpool.LazyTransaction // Per account nonce-sorted list of transactions heads txByPriceAndTime // Next transaction for each unique account (price heap) signer types.Signer // Signer for the set of transactions - baseFee *big.Int // Current base fee + baseFee *uint256.Int // Current base fee } // newTransactionsByPriceAndNonce creates a transaction set that can retrieve @@ -106,10 +109,15 @@ type transactionsByPriceAndNonce struct { // Note, the input map is reowned so the caller should not interact any more with // if after providing it to the constructor. func newTransactionsByPriceAndNonce(signer types.Signer, txs map[common.Address][]*txpool.LazyTransaction, baseFee *big.Int) *transactionsByPriceAndNonce { + // Convert the basefee from header format to uint256 format + var baseFeeUint *uint256.Int + if baseFee != nil { + baseFeeUint = uint256.MustFromBig(baseFee) + } // Initialize a price and received time based heap with the head transactions heads := make(txByPriceAndTime, 0, len(txs)) for from, accTxs := range txs { - wrapped, err := newTxWithMinerFee(accTxs[0], from, baseFee) + wrapped, err := newTxWithMinerFee(accTxs[0], from, baseFeeUint) if err != nil { delete(txs, from) continue @@ -124,16 +132,16 @@ func newTransactionsByPriceAndNonce(signer types.Signer, txs map[common.Address] txs: txs, heads: heads, signer: signer, - baseFee: baseFee, + baseFee: baseFeeUint, } } // Peek returns the next transaction by price. -func (t *transactionsByPriceAndNonce) Peek() *txpool.LazyTransaction { +func (t *transactionsByPriceAndNonce) Peek() (*txpool.LazyTransaction, *uint256.Int) { if len(t.heads) == 0 { - return nil + return nil, nil } - return t.heads[0].tx + return t.heads[0].tx, t.heads[0].fees } // Shift replaces the current best head with the next one from the same account. @@ -155,3 +163,14 @@ func (t *transactionsByPriceAndNonce) Shift() { func (t *transactionsByPriceAndNonce) Pop() { heap.Pop(&t.heads) } + +// Empty returns if the price heap is empty. It can be used to check it simpler +// than calling peek and checking for nil return. +func (t *transactionsByPriceAndNonce) Empty() bool { + return len(t.heads) == 0 +} + +// Clear removes the entire content of the heap. +func (t *transactionsByPriceAndNonce) Clear() { + t.heads, t.txs = nil, nil +} diff --git a/miner/ordering_test.go b/miner/ordering_test.go index 9bee7cf700..2773affe22 100644 --- a/miner/ordering_test.go +++ b/miner/ordering_test.go @@ -37,6 +37,7 @@ import ( "github.com/ava-labs/subnet-evm/core/types" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" + "github.com/holiman/uint256" ) func TestTransactionPriceNonceSortLegacy(t *testing.T) { @@ -102,8 +103,8 @@ func testTransactionPriceNonceSort(t *testing.T, baseFee *big.Int) { Hash: tx.Hash(), Tx: tx, Time: tx.Time(), - GasFeeCap: tx.GasFeeCap(), - GasTipCap: tx.GasTipCap(), + GasFeeCap: uint256.MustFromBig(tx.GasFeeCap()), + GasTipCap: uint256.MustFromBig(tx.GasTipCap()), Gas: tx.Gas(), BlobGas: tx.BlobGas(), }) @@ -114,7 +115,7 @@ func testTransactionPriceNonceSort(t *testing.T, baseFee *big.Int) { txset := newTransactionsByPriceAndNonce(signer, groups, baseFee) txs := types.Transactions{} - for tx := txset.Peek(); tx != nil; tx = txset.Peek() { + for tx, _ := txset.Peek(); tx != nil; tx, _ = txset.Peek() { txs = append(txs, tx.Tx) txset.Shift() } @@ -170,8 +171,8 @@ func TestTransactionTimeSort(t *testing.T) { Hash: tx.Hash(), Tx: tx, Time: tx.Time(), - GasFeeCap: tx.GasFeeCap(), - GasTipCap: tx.GasTipCap(), + GasFeeCap: uint256.MustFromBig(tx.GasFeeCap()), + GasTipCap: uint256.MustFromBig(tx.GasTipCap()), Gas: tx.Gas(), BlobGas: tx.BlobGas(), }) @@ -180,7 +181,7 @@ func TestTransactionTimeSort(t *testing.T) { txset := newTransactionsByPriceAndNonce(signer, groups, nil) txs := types.Transactions{} - for tx := txset.Peek(); tx != nil; tx = txset.Peek() { + for tx, _ := txset.Peek(); tx != nil; tx, _ = txset.Peek() { txs = append(txs, tx.Tx) txset.Shift() } diff --git a/miner/worker.go b/miner/worker.go index 7c2f7d2e23..360d30d55d 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -52,6 +52,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/log" + "github.com/holiman/uint256" ) const ( @@ -232,25 +233,47 @@ func (w *worker) commitNewWork(predicateContext *precompileconfig.PredicateConte return nil, err } - pending := w.eth.TxPool().PendingWithBaseFee(true, header.BaseFee) + // Retrieve the pending transactions pre-filtered by the 1559/4844 dynamic fees + filter := txpool.PendingFilter{ + MinTip: uint256.MustFromBig(w.eth.TxPool().GasTip()), + } + if env.header.BaseFee != nil { + filter.BaseFee = uint256.MustFromBig(env.header.BaseFee) + } + if env.header.ExcessBlobGas != nil { + filter.BlobFee = uint256.MustFromBig(eip4844.CalcBlobFee(*env.header.ExcessBlobGas)) + } + filter.OnlyPlainTxs, filter.OnlyBlobTxs = true, false + pendingPlainTxs := w.eth.TxPool().Pending(filter) + + filter.OnlyPlainTxs, filter.OnlyBlobTxs = false, true + pendingBlobTxs := w.eth.TxPool().Pending(filter) // Split the pending transactions into locals and remotes. - localTxs, remoteTxs := make(map[common.Address][]*txpool.LazyTransaction), pending + localPlainTxs, remotePlainTxs := make(map[common.Address][]*txpool.LazyTransaction), pendingPlainTxs + localBlobTxs, remoteBlobTxs := make(map[common.Address][]*txpool.LazyTransaction), pendingBlobTxs for _, account := range w.eth.TxPool().Locals() { - if txs := remoteTxs[account]; len(txs) > 0 { - delete(remoteTxs, account) - localTxs[account] = txs + if txs := remotePlainTxs[account]; len(txs) > 0 { + delete(remotePlainTxs, account) + localPlainTxs[account] = txs + } + if txs := remoteBlobTxs[account]; len(txs) > 0 { + delete(remoteBlobTxs, account) + localBlobTxs[account] = txs } } - // Fill the block with all available pending transactions. - if len(localTxs) > 0 { - txs := newTransactionsByPriceAndNonce(env.signer, localTxs, header.BaseFee) - w.commitTransactions(env, txs, header.Coinbase) + if len(localPlainTxs) > 0 || len(localBlobTxs) > 0 { + plainTxs := newTransactionsByPriceAndNonce(env.signer, localPlainTxs, env.header.BaseFee) + blobTxs := newTransactionsByPriceAndNonce(env.signer, localBlobTxs, env.header.BaseFee) + + w.commitTransactions(env, plainTxs, blobTxs, env.header.Coinbase) } - if len(remoteTxs) > 0 { - txs := newTransactionsByPriceAndNonce(env.signer, remoteTxs, header.BaseFee) - w.commitTransactions(env, txs, header.Coinbase) + if len(remotePlainTxs) > 0 || len(remoteBlobTxs) > 0 { + plainTxs := newTransactionsByPriceAndNonce(env.signer, remotePlainTxs, env.header.BaseFee) + blobTxs := newTransactionsByPriceAndNonce(env.signer, remoteBlobTxs, env.header.BaseFee) + + w.commitTransactions(env, plainTxs, blobTxs, env.header.Coinbase) } return w.commit(env) @@ -343,15 +366,47 @@ func (w *worker) applyTransaction(env *environment, tx *types.Transaction, coinb return receipt, err } -func (w *worker) commitTransactions(env *environment, txs *transactionsByPriceAndNonce, coinbase common.Address) { +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) 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 { + 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 { + log.Trace("Not enough blob space for further blob transactions") + blobTxs.Clear() + // Fall though to pick up any plain txs + } // Retrieve the next transaction and abort if all done. - ltx := txs.Peek() + var ( + ltx *txpool.LazyTransaction + txs *transactionsByPriceAndNonce + ) + pltx, ptip := plainTxs.Peek() + bltx, btip := blobTxs.Peek() + + switch { + case pltx == nil: + txs, ltx = blobTxs, bltx + case bltx == nil: + txs, ltx = plainTxs, pltx + default: + if ptip.Lt(btip) { + txs, ltx = blobTxs, bltx + } else { + txs, ltx = plainTxs, pltx + } + } if ltx == nil { break } @@ -438,7 +493,7 @@ func (w *worker) commit(env *environment) (*types.Block, error) { func (w *worker) handleResult(env *environment, block *types.Block, createdAt time.Time, unfinishedReceipts []*types.Receipt) (*types.Block, error) { // Short circuit when receiving duplicate result caused by resubmitting. - if w.chain.HasBlock(block.Hash(), block.NumberU64()) { + if !w.config.TestOnlyAllowDuplicateBlocks && w.chain.HasBlock(block.Hash(), block.NumberU64()) { return nil, fmt.Errorf("produced duplicate block (Hash: %s, Number %d)", block.Hash(), block.NumberU64()) } // Different block could share same sealhash, deep copy here to prevent write-write conflict. diff --git a/node/defaults.go b/node/defaults.go new file mode 100644 index 0000000000..b19ac3a861 --- /dev/null +++ b/node/defaults.go @@ -0,0 +1,22 @@ +// Copyright 2016 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 node + +// DefaultConfig contains reasonable default settings. +var DefaultConfig = Config{ + BatchRequestLimit: 1000, + BatchResponseMaxSize: 25 * 1000 * 1000, +} diff --git a/params/config.go b/params/config.go index 70cc2159ba..5d5799b92b 100644 --- a/params/config.go +++ b/params/config.go @@ -236,7 +236,8 @@ func (c *ChainConfig) Description() string { } banner += "Hard forks (timestamp based):\n" - banner += fmt.Sprintf(" - Cancun Timestamp: @%-10v (https://github.com/ava-labs/avalanchego/releases/tag/v1.12.0)\n", ptrToString(c.CancunTime)) + banner += fmt.Sprintf(" - Cancun Timestamp: @%-10v (https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/cancun.md)\n", ptrToString(c.CancunTime)) + banner += fmt.Sprintf(" - Verkle Timestamp: @%-10v", ptrToString(c.VerkleTime)) banner += "Avalanche Upgrades (timestamp based):\n" banner += c.NetworkUpgrades.Description() @@ -628,6 +629,7 @@ type EthRules struct { IsHomestead, IsEIP150, IsEIP155, IsEIP158 bool IsByzantium, IsConstantinople, IsPetersburg, IsIstanbul bool IsCancun bool + IsVerkle bool } // Rules wraps ChainConfig and is merely syntactic sugar or can be used for functions @@ -675,6 +677,7 @@ func (c *ChainConfig) rules(num *big.Int, timestamp uint64) Rules { IsPetersburg: c.IsPetersburg(num), IsIstanbul: c.IsIstanbul(num), IsCancun: c.IsCancun(num, timestamp), + IsVerkle: c.IsVerkle(num, timestamp), }, } } diff --git a/params/protocol_params.go b/params/protocol_params.go index 6e4b96f8e7..0f85e6cd2f 100644 --- a/params/protocol_params.go +++ b/params/protocol_params.go @@ -172,7 +172,6 @@ const ( BlobTxBytesPerFieldElement = 32 // Size in bytes of a field element BlobTxFieldElementsPerBlob = 4096 // Number of field elements stored in a single data blob - BlobTxHashVersion = 0x01 // Version byte of the commitment hash 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 diff --git a/params/version.go b/params/version.go index be03098ff4..f27e094e03 100644 --- a/params/version.go +++ b/params/version.go @@ -33,7 +33,7 @@ import ( const ( VersionMajor = 1 // Major version component of the current release VersionMinor = 13 // Minor version component of the current release - VersionPatch = 8 // Patch version component of the current release + VersionPatch = 14 // Patch version component of the current release VersionMeta = "stable" // Version metadata to append to the version string ) diff --git a/peer/network.go b/peer/network.go index 13e3cc6220..8f1919c032 100644 --- a/peer/network.go +++ b/peer/network.go @@ -123,6 +123,11 @@ func NewNetwork(p2pNetwork *p2p.Network, appSender common.AppSender, codec codec // Returns the ID of the chosen peer, and an error if the request could not // be sent to a peer with the desired [minVersion]. func (n *network) SendAppRequestAny(ctx context.Context, minVersion *version.Application, request []byte, handler message.ResponseHandler) (ids.NodeID, error) { + // If the context was cancelled, we can skip sending this request. + if err := ctx.Err(); err != nil { + return ids.EmptyNodeID, err + } + // Take a slot from total [activeAppRequests] and block until a slot becomes available. if err := n.activeAppRequests.Acquire(ctx, 1); err != nil { return ids.EmptyNodeID, errAcquiringSemaphore @@ -144,6 +149,11 @@ func (n *network) SendAppRequest(ctx context.Context, nodeID ids.NodeID, request return fmt.Errorf("cannot send request to empty nodeID, nodeID=%s, requestLen=%d", nodeID, len(request)) } + // If the context was cancelled, we can skip sending this request. + if err := ctx.Err(); err != nil { + return err + } + // Take a slot from total [activeAppRequests] and block until a slot becomes available. if err := n.activeAppRequests.Acquire(ctx, 1); err != nil { return errAcquiringSemaphore diff --git a/plugin/evm/block_builder.go b/plugin/evm/block_builder.go index cfeb2385ec..cdd5e63889 100644 --- a/plugin/evm/block_builder.go +++ b/plugin/evm/block_builder.go @@ -11,6 +11,7 @@ import ( "github.com/ava-labs/subnet-evm/core" "github.com/ava-labs/subnet-evm/core/txpool" "github.com/ava-labs/subnet-evm/params" + "github.com/holiman/uint256" "github.com/ava-labs/avalanchego/snow" commonEng "github.com/ava-labs/avalanchego/snow/engine/common" @@ -98,7 +99,9 @@ func (b *blockBuilder) handleGenerateBlock() { // needToBuild returns true if there are outstanding transactions to be issued // into a block. func (b *blockBuilder) needToBuild() bool { - size := b.txPool.PendingSize(true) + size := b.txPool.PendingSize(txpool.PendingFilter{ + MinTip: uint256.MustFromBig(b.txPool.GasTip()), + }) return size > 0 } diff --git a/plugin/evm/config.go b/plugin/evm/config.go index 47b342537c..9f59775ba9 100644 --- a/plugin/evm/config.go +++ b/plugin/evm/config.go @@ -222,6 +222,9 @@ type Config struct { // Note: only supports AddressedCall payloads as defined here: // https://github.com/ava-labs/avalanchego/tree/7623ffd4be915a5185c9ed5e11fa9be15a6e1f00/vms/platformvm/warp/payload#addressedcall WarpOffChainMessages []hexutil.Bytes `json:"warp-off-chain-messages"` + + // RPC settings + HttpBodyLimit uint64 `json:"http-body-limit"` } // EthAPIs returns an array of strings representing the Eth APIs that should be enabled diff --git a/plugin/evm/gossip.go b/plugin/evm/gossip.go index 05270f7a65..f10c63bb43 100644 --- a/plugin/evm/gossip.go +++ b/plugin/evm/gossip.go @@ -137,7 +137,7 @@ func (g *GossipEthTxPool) Subscribe(ctx context.Context) { return case pendingTxs := <-g.pendingTxs: g.lock.Lock() - optimalElements := (g.mempool.PendingSize(false) + len(pendingTxs.Txs)) * txGossipBloomChurnMultiplier + optimalElements := (g.mempool.PendingSize(txpool.PendingFilter{}) + len(pendingTxs.Txs)) * txGossipBloomChurnMultiplier for _, pendingTx := range pendingTxs.Txs { tx := &GossipEthTx{Tx: pendingTx} g.bloom.Add(tx) diff --git a/plugin/evm/gossip_test.go b/plugin/evm/gossip_test.go index 61772063d4..551d3a8075 100644 --- a/plugin/evm/gossip_test.go +++ b/plugin/evm/gossip_test.go @@ -96,14 +96,14 @@ func setupPoolWithConfig(t *testing.T, config *params.ChainConfig, fundedAddress gspec := &core.Genesis{ Config: config, - Alloc: core.GenesisAlloc{fundedAddress: core.GenesisAccount{Balance: big.NewInt(1000000000000000000)}}, + Alloc: types.GenesisAlloc{fundedAddress: {Balance: big.NewInt(1000000000000000000)}}, } chain, err := core.NewBlockChain(diskdb, core.DefaultCacheConfig, gspec, engine, vm.Config{}, common.Hash{}, false) require.NoError(t, err) testTxPoolConfig := legacypool.DefaultConfig legacyPool := legacypool.New(testTxPoolConfig, chain) - txPool, err := txpool.New(new(big.Int).SetUint64(testTxPoolConfig.PriceLimit), chain, []txpool.SubPool{legacyPool}) + txPool, err := txpool.New(testTxPoolConfig.PriceLimit, chain, []txpool.SubPool{legacyPool}) require.NoError(t, err) return txPool diff --git a/plugin/evm/gossiper_eth_gossiping_test.go b/plugin/evm/gossiper_eth_gossiping_test.go index 8eb140c3a8..6e4e685ed0 100644 --- a/plugin/evm/gossiper_eth_gossiping_test.go +++ b/plugin/evm/gossiper_eth_gossiping_test.go @@ -34,9 +34,9 @@ func fundAddressByGenesis(addrs []common.Address) (string, error) { Difficulty: common.Big0, GasLimit: params.TestChainConfig.FeeConfig.GasLimit.Uint64(), } - funds := make(map[common.Address]core.GenesisAccount) + funds := make(map[common.Address]types.Account) for _, addr := range addrs { - funds[addr] = core.GenesisAccount{ + funds[addr] = types.Account{ Balance: balance, } } diff --git a/plugin/evm/log_test.go b/plugin/evm/log_test.go new file mode 100644 index 0000000000..eb37e7a9e9 --- /dev/null +++ b/plugin/evm/log_test.go @@ -0,0 +1,19 @@ +// (c) 2024, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package evm + +import ( + "os" + "testing" + + "github.com/ethereum/go-ethereum/log" + "github.com/stretchr/testify/require" +) + +func TestInitLogger(t *testing.T) { + require := require.New(t) + _, err := InitLogger("alias", "info", true, os.Stderr) + require.NoError(err) + log.Info("test") +} diff --git a/plugin/evm/network_handler.go b/plugin/evm/network_handler.go index 2e46477cc0..e54d40e676 100644 --- a/plugin/evm/network_handler.go +++ b/plugin/evm/network_handler.go @@ -12,7 +12,7 @@ import ( "github.com/ava-labs/subnet-evm/plugin/evm/message" syncHandlers "github.com/ava-labs/subnet-evm/sync/handlers" syncStats "github.com/ava-labs/subnet-evm/sync/handlers/stats" - "github.com/ava-labs/subnet-evm/trie" + "github.com/ava-labs/subnet-evm/triedb" "github.com/ava-labs/subnet-evm/warp" warpHandlers "github.com/ava-labs/subnet-evm/warp/handlers" "github.com/ethereum/go-ethereum/ethdb" @@ -31,7 +31,7 @@ type networkHandler struct { func newNetworkHandler( provider syncHandlers.SyncDataProvider, diskDB ethdb.KeyValueReader, - evmTrieDB *trie.Database, + evmTrieDB *triedb.Database, warpBackend warp.Backend, networkCodec codec.Manager, ) message.RequestHandler { diff --git a/plugin/evm/static_service_test.go b/plugin/evm/static_service_test.go index 626f8f30d3..6ac264543d 100644 --- a/plugin/evm/static_service_test.go +++ b/plugin/evm/static_service_test.go @@ -10,6 +10,7 @@ import ( "github.com/ava-labs/avalanchego/utils/formatting" "github.com/ava-labs/subnet-evm/core" + "github.com/ava-labs/subnet-evm/core/types" "github.com/ava-labs/subnet-evm/params" "github.com/stretchr/testify/assert" ) @@ -25,9 +26,9 @@ func TestBuildGenesis(t *testing.T) { } // add test allocs - testAlloc := core.GenesisAlloc{ - testEthAddrs[0]: core.GenesisAccount{Balance: genesisBalance}, - testEthAddrs[1]: core.GenesisAccount{Balance: genesisBalance}, + testAlloc := types.GenesisAlloc{ + testEthAddrs[0]: {Balance: genesisBalance}, + testEthAddrs[1]: {Balance: genesisBalance}, } genesis.Alloc = testAlloc genesis.Config.FeeConfig = params.DefaultFeeConfig @@ -63,9 +64,9 @@ func TestDecodeGenesis(t *testing.T) { } // add test allocs - testAlloc := core.GenesisAlloc{ - testEthAddrs[0]: core.GenesisAccount{Balance: genesisBalance}, - testEthAddrs[1]: core.GenesisAccount{Balance: genesisBalance}, + testAlloc := types.GenesisAlloc{ + testEthAddrs[0]: {Balance: genesisBalance}, + testEthAddrs[1]: {Balance: genesisBalance}, } genesis.Alloc = testAlloc genesis.Config.FeeConfig = params.DefaultFeeConfig diff --git a/plugin/evm/syncervm_test.go b/plugin/evm/syncervm_test.go index 64306ef66c..5a5b260a2d 100644 --- a/plugin/evm/syncervm_test.go +++ b/plugin/evm/syncervm_test.go @@ -36,6 +36,7 @@ import ( statesyncclient "github.com/ava-labs/subnet-evm/sync/client" "github.com/ava-labs/subnet-evm/sync/statesync" "github.com/ava-labs/subnet-evm/trie" + "github.com/ava-labs/subnet-evm/triedb" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/log" @@ -295,7 +296,7 @@ func createSyncServerAndClientVMs(t *testing.T, test syncTest, numBlocks int) *s }, nil) // make some accounts - trieDB := trie.NewDatabase(serverVM.chaindb, nil) + trieDB := triedb.NewDatabase(serverVM.chaindb, nil) root, accounts := statesync.FillAccountsWithOverlappingStorage(t, trieDB, types.EmptyRootHash, 1000, 16) // patch serverVM's lastAcceptedBlock to have the new root diff --git a/plugin/evm/vm.go b/plugin/evm/vm.go index 3e49bcc94d..376a8a12c7 100644 --- a/plugin/evm/vm.go +++ b/plugin/evm/vm.go @@ -21,6 +21,7 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/ava-labs/subnet-evm/commontype" + "github.com/ava-labs/subnet-evm/consensus/dummy" "github.com/ava-labs/subnet-evm/constants" "github.com/ava-labs/subnet-evm/core" "github.com/ava-labs/subnet-evm/core/rawdb" @@ -35,13 +36,13 @@ import ( "github.com/ava-labs/subnet-evm/params" "github.com/ava-labs/subnet-evm/peer" "github.com/ava-labs/subnet-evm/plugin/evm/message" - "github.com/ava-labs/subnet-evm/trie/triedb/hashdb" + "github.com/ava-labs/subnet-evm/triedb" + "github.com/ava-labs/subnet-evm/triedb/hashdb" warpcontract "github.com/ava-labs/subnet-evm/precompile/contracts/warp" "github.com/ava-labs/subnet-evm/rpc" statesyncclient "github.com/ava-labs/subnet-evm/sync/client" "github.com/ava-labs/subnet-evm/sync/client/stats" - "github.com/ava-labs/subnet-evm/trie" "github.com/ava-labs/subnet-evm/warp" "github.com/ava-labs/subnet-evm/warp/handlers" @@ -547,6 +548,7 @@ func (vm *VM) initializeChain(lastAcceptedHash common.Hash, ethConfig ethconfig. vm.chaindb, vm.config.EthBackendSettings(), lastAcceptedHash, + dummy.NewFakerWithClock(&vm.clock), &vm.clock, ) if err != nil { @@ -798,9 +800,9 @@ func (vm *VM) setAppRequestHandlers() { // Create separate EVM TrieDB (read only) for serving leafs requests. // We create a separate TrieDB here, so that it has a separate cache from the one // used by the node when processing blocks. - evmTrieDB := trie.NewDatabase( + evmTrieDB := triedb.NewDatabase( vm.chaindb, - &trie.Config{ + &triedb.Config{ HashDB: &hashdb.Config{ CleanCacheSize: vm.config.StateSyncServerTrieCache * units.MiB, }, @@ -992,6 +994,10 @@ func newHandler(name string, service interface{}) (http.Handler, error) { // CreateHandlers makes new http handlers that can handle API calls func (vm *VM) CreateHandlers(context.Context) (map[string]http.Handler, error) { handler := rpc.NewServer(vm.config.APIMaxDuration.Duration) + if vm.config.HttpBodyLimit > 0 { + handler.SetHTTPBodyLimit(int(vm.config.HttpBodyLimit)) + } + enabledAPIs := vm.config.EthAPIs() if err := attachEthService(handler, vm.eth.APIs(), enabledAPIs); err != nil { return nil, err @@ -1040,6 +1046,9 @@ func (vm *VM) CreateHandlers(context.Context) (map[string]http.Handler, error) { // CreateStaticHandlers makes new http handlers that can handle API calls func (vm *VM) CreateStaticHandlers(context.Context) (map[string]http.Handler, error) { handler := rpc.NewServer(0) + if vm.config.HttpBodyLimit > 0 { + handler.SetHTTPBodyLimit(int(vm.config.HttpBodyLimit)) + } if err := handler.RegisterName("static", &StaticService{}); err != nil { return nil, err } diff --git a/plugin/evm/vm_test.go b/plugin/evm/vm_test.go index 6818b773bf..934a999684 100644 --- a/plugin/evm/vm_test.go +++ b/plugin/evm/vm_test.go @@ -2707,7 +2707,7 @@ func TestAllowFeeRecipientEnabled(t *testing.T) { } balance := blkState.GetBalance(etherBase) - require.Equal(t, 1, balance.Cmp(common.Big0)) + require.Equal(t, 1, balance.Cmp(common.U2560)) } func TestRewardManagerPrecompileSetRewardAddress(t *testing.T) { @@ -2797,7 +2797,7 @@ func TestRewardManagerPrecompileSetRewardAddress(t *testing.T) { require.NoError(t, err) balance := blkState.GetBalance(testAddr) - require.Equal(t, 1, balance.Cmp(common.Big0)) + require.Equal(t, 1, balance.Cmp(common.U2560)) // Test Case: Disable reward manager // This should revert back to enabling fee recipients @@ -2935,7 +2935,7 @@ func TestRewardManagerPrecompileAllowFeeRecipients(t *testing.T) { require.NoError(t, err) balance := blkState.GetBalance(etherBase) - require.Equal(t, 1, balance.Cmp(common.Big0)) + require.Equal(t, 1, balance.Cmp(common.U2560)) // Test Case: Disable reward manager // This should revert back to burning fees diff --git a/plugin/evm/vm_upgrade_bytes_test.go b/plugin/evm/vm_upgrade_bytes_test.go index 1960077647..9907f98531 100644 --- a/plugin/evm/vm_upgrade_bytes_test.go +++ b/plugin/evm/vm_upgrade_bytes_test.go @@ -28,6 +28,7 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/crypto" + "github.com/holiman/uint256" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -285,7 +286,7 @@ func TestVMStateUpgrade(t *testing.T) { // Verify the new account doesn't exist yet genesisState, err := vm.blockChain.State() require.NoError(t, err) - require.Equal(t, common.Big0, genesisState.GetBalance(newAccount)) + require.Equal(t, common.U2560, genesisState.GetBalance(newAccount)) // Advance the chain to the upgrade time vm.clock.Set(upgradeTimestamp) @@ -308,9 +309,11 @@ func TestVMStateUpgrade(t *testing.T) { require.NoError(t, err) // Existing account - expectedGenesisAccountBalance := new(big.Int).Add( - genesisAccount.Balance, - (*big.Int)(genesisAccountUpgrade.BalanceChange), + expectedGenesisAccountBalance := uint256.MustFromBig( + new(big.Int).Add( + genesisAccount.Balance, + (*big.Int)(genesisAccountUpgrade.BalanceChange), + ), ) require.Equal(t, state.GetBalance(testEthAddrs[0]), expectedGenesisAccountBalance) require.Equal(t, state.GetState(testEthAddrs[0], storageKey), genesisAccountUpgrade.Storage[storageKey]) @@ -319,8 +322,8 @@ func TestVMStateUpgrade(t *testing.T) { require.Equal(t, state.GetNonce(testEthAddrs[0]), genesisAccount.Nonce) // Nonce should be preserved since it was non-zero // New account - expectedNewAccountBalance := newAccountUpgrade.BalanceChange - require.Equal(t, state.GetBalance(newAccount), (*big.Int)(expectedNewAccountBalance)) + expectedNewAccountBalance := uint256.MustFromBig((*big.Int)(newAccountUpgrade.BalanceChange)) + require.Equal(t, state.GetBalance(newAccount), expectedNewAccountBalance) require.Equal(t, state.GetCode(newAccount), upgradedCode) require.Equal(t, state.GetCodeHash(newAccount), crypto.Keccak256Hash(upgradedCode)) require.Equal(t, state.GetNonce(newAccount), uint64(1)) // Nonce should be set to 1 when code is set if nonce was 0 diff --git a/precompile/contract/interfaces.go b/precompile/contract/interfaces.go index 5ac6baa486..7bc5d750a5 100644 --- a/precompile/contract/interfaces.go +++ b/precompile/contract/interfaces.go @@ -10,6 +10,7 @@ import ( "github.com/ava-labs/avalanchego/snow" "github.com/ava-labs/subnet-evm/precompile/precompileconfig" "github.com/ethereum/go-ethereum/common" + "github.com/holiman/uint256" ) // StatefulPrecompiledContract is the interface for executing a precompiled contract @@ -26,8 +27,8 @@ type StateDB interface { SetNonce(common.Address, uint64) GetNonce(common.Address) uint64 - GetBalance(common.Address) *big.Int - AddBalance(common.Address, *big.Int) + GetBalance(common.Address) *uint256.Int + AddBalance(common.Address, *uint256.Int) CreateAccount(common.Address) Exist(common.Address) bool diff --git a/precompile/contract/mocks.go b/precompile/contract/mocks.go index 6510d2d738..86943cf6e4 100644 --- a/precompile/contract/mocks.go +++ b/precompile/contract/mocks.go @@ -16,6 +16,7 @@ import ( snow "github.com/ava-labs/avalanchego/snow" precompileconfig "github.com/ava-labs/subnet-evm/precompile/precompileconfig" common "github.com/ethereum/go-ethereum/common" + uint256 "github.com/holiman/uint256" gomock "go.uber.org/mock/gomock" ) @@ -187,7 +188,7 @@ func (m *MockStateDB) EXPECT() *MockStateDBMockRecorder { } // AddBalance mocks base method. -func (m *MockStateDB) AddBalance(arg0 common.Address, arg1 *big.Int) { +func (m *MockStateDB) AddBalance(arg0 common.Address, arg1 *uint256.Int) { m.ctrl.T.Helper() m.ctrl.Call(m, "AddBalance", arg0, arg1) } @@ -237,10 +238,10 @@ func (mr *MockStateDBMockRecorder) Exist(arg0 any) *gomock.Call { } // GetBalance mocks base method. -func (m *MockStateDB) GetBalance(arg0 common.Address) *big.Int { +func (m *MockStateDB) GetBalance(arg0 common.Address) *uint256.Int { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetBalance", arg0) - ret0, _ := ret[0].(*big.Int) + ret0, _ := ret[0].(*uint256.Int) return ret0 } diff --git a/precompile/contracts/nativeminter/contract.go b/precompile/contracts/nativeminter/contract.go index 2e578fe2cc..4ca751ccf2 100644 --- a/precompile/contracts/nativeminter/contract.go +++ b/precompile/contracts/nativeminter/contract.go @@ -13,6 +13,7 @@ import ( "github.com/ava-labs/subnet-evm/precompile/contract" "github.com/ava-labs/subnet-evm/vmerrs" "github.com/ethereum/go-ethereum/common" + "github.com/holiman/uint256" ) const ( @@ -117,7 +118,8 @@ func mintNativeCoin(accessibleState contract.AccessibleState, caller common.Addr stateDB.CreateAccount(to) } - stateDB.AddBalance(to, amount) + amountU256, _ := uint256.FromBig(amount) + stateDB.AddBalance(to, amountU256) // Return an empty output and the remaining gas return []byte{}, remainingGas, nil } diff --git a/precompile/contracts/nativeminter/contract_test.go b/precompile/contracts/nativeminter/contract_test.go index 0881918516..6a6cf3574b 100644 --- a/precompile/contracts/nativeminter/contract_test.go +++ b/precompile/contracts/nativeminter/contract_test.go @@ -15,6 +15,7 @@ import ( "github.com/ava-labs/subnet-evm/vmerrs" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/math" + "github.com/holiman/uint256" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" ) @@ -47,7 +48,8 @@ var ( ReadOnly: false, ExpectedRes: []byte{}, AfterHook: func(t testing.TB, stateDB contract.StateDB) { - require.Equal(t, common.Big1, stateDB.GetBalance(allowlist.TestEnabledAddr), "expected minted funds") + expected := uint256.MustFromBig(common.Big1) + require.Equal(t, expected, stateDB.GetBalance(allowlist.TestEnabledAddr), "expected minted funds") logsTopics, logsData := stateDB.GetLogData() assertNativeCoinMintedEvent(t, logsTopics, logsData, allowlist.TestEnabledAddr, allowlist.TestEnabledAddr, common.Big1) @@ -62,7 +64,8 @@ var ( }, }, AfterHook: func(t testing.TB, stateDB contract.StateDB) { - require.Equal(t, common.Big2, stateDB.GetBalance(allowlist.TestEnabledAddr), "expected minted funds") + expected := uint256.MustFromBig(common.Big2) + require.Equal(t, expected, stateDB.GetBalance(allowlist.TestEnabledAddr), "expected minted funds") }, }, "calling mintNativeCoin from Manager should succeed": { @@ -78,7 +81,8 @@ var ( ReadOnly: false, ExpectedRes: []byte{}, AfterHook: func(t testing.TB, stateDB contract.StateDB) { - require.Equal(t, common.Big1, stateDB.GetBalance(allowlist.TestEnabledAddr), "expected minted funds") + expected := uint256.MustFromBig(common.Big1) + require.Equal(t, expected, stateDB.GetBalance(allowlist.TestEnabledAddr), "expected minted funds") logsTopics, logsData := stateDB.GetLogData() assertNativeCoinMintedEvent(t, logsTopics, logsData, allowlist.TestManagerAddr, allowlist.TestEnabledAddr, common.Big1) @@ -97,7 +101,8 @@ var ( ReadOnly: false, ExpectedRes: []byte{}, AfterHook: func(t testing.TB, stateDB contract.StateDB) { - require.Equal(t, common.Big1, stateDB.GetBalance(allowlist.TestAdminAddr), "expected minted funds") + expected := uint256.MustFromBig(common.Big1) + require.Equal(t, expected, stateDB.GetBalance(allowlist.TestAdminAddr), "expected minted funds") logsTopics, logsData := stateDB.GetLogData() assertNativeCoinMintedEvent(t, logsTopics, logsData, allowlist.TestAdminAddr, allowlist.TestAdminAddr, common.Big1) @@ -116,7 +121,8 @@ var ( ReadOnly: false, ExpectedRes: []byte{}, AfterHook: func(t testing.TB, stateDB contract.StateDB) { - require.Equal(t, math.MaxBig256, stateDB.GetBalance(allowlist.TestAdminAddr), "expected minted funds") + expected := uint256.MustFromBig(math.MaxBig256) + require.Equal(t, expected, stateDB.GetBalance(allowlist.TestAdminAddr), "expected minted funds") logsTopics, logsData := stateDB.GetLogData() assertNativeCoinMintedEvent(t, logsTopics, logsData, allowlist.TestAdminAddr, allowlist.TestAdminAddr, math.MaxBig256) @@ -239,7 +245,8 @@ var ( SuppliedGas: MintGasCost + NativeCoinMintedEventGasCost, ReadOnly: false, AfterHook: func(t testing.TB, state contract.StateDB) { - require.Equal(t, common.Big1, state.GetBalance(allowlist.TestEnabledAddr), "expected minted funds") + expected := uint256.MustFromBig(common.Big1) + require.Equal(t, expected, state.GetBalance(allowlist.TestEnabledAddr), "expected minted funds") logsTopics, logsData := state.GetLogData() assertNativeCoinMintedEvent(t, logsTopics, logsData, allowlist.TestEnabledAddr, allowlist.TestEnabledAddr, common.Big1) @@ -261,7 +268,8 @@ func assertNativeCoinMintedEvent(t testing.TB, logsData [][]byte, expectedSender common.Address, expectedRecipient common.Address, - expectedAmount *big.Int) { + expectedAmount *big.Int, +) { require.Len(t, logsTopics, 1) require.Len(t, logsData, 1) topics := logsTopics[0] diff --git a/precompile/contracts/nativeminter/module.go b/precompile/contracts/nativeminter/module.go index ce62cee149..02529e2432 100644 --- a/precompile/contracts/nativeminter/module.go +++ b/precompile/contracts/nativeminter/module.go @@ -11,6 +11,7 @@ import ( "github.com/ava-labs/subnet-evm/precompile/modules" "github.com/ava-labs/subnet-evm/precompile/precompileconfig" "github.com/ethereum/go-ethereum/common" + "github.com/holiman/uint256" ) var _ contract.Configurator = &configurator{} @@ -52,8 +53,9 @@ func (*configurator) Configure(chainConfig precompileconfig.ChainConfig, cfg pre } for to, amount := range config.InitialMint { if amount != nil { - bigIntAmount := (*big.Int)(amount) - state.AddBalance(to, bigIntAmount) + amountBig := (*big.Int)(amount) + amountU256, _ := uint256.FromBig(amountBig) + state.AddBalance(to, amountU256) } } diff --git a/rpc/http.go b/rpc/http.go index a3ff1eac7f..b8670a9df8 100644 --- a/rpc/http.go +++ b/rpc/http.go @@ -43,8 +43,8 @@ import ( ) const ( - maxRequestContentLength = 1024 * 1024 * 5 - contentType = "application/json" + defaultBodyLimit = 5 * 1024 * 1024 + contentType = "application/json" ) // https://www.jsonrpc.org/historical/json-rpc-over-http.html#id13 @@ -267,8 +267,8 @@ type httpServerConn struct { r *http.Request } -func newHTTPServerConn(r *http.Request, w http.ResponseWriter) ServerCodec { - body := io.LimitReader(r.Body, maxRequestContentLength) +func (s *Server) newHTTPServerConn(r *http.Request, w http.ResponseWriter) ServerCodec { + body := io.LimitReader(r.Body, int64(s.httpBodyLimit)) conn := &httpServerConn{Reader: body, Writer: w, r: r} encoder := func(v any, isErrorResponse bool) error { @@ -326,7 +326,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) return } - if code, err := validateRequest(r); err != nil { + if code, err := s.validateRequest(r); err != nil { http.Error(w, err.Error(), code) return } @@ -344,19 +344,19 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { // until EOF, writes the response to w, and orders the server to process a // single request. w.Header().Set("content-type", contentType) - codec := newHTTPServerConn(r, w) + codec := s.newHTTPServerConn(r, w) defer codec.close() s.serveSingleRequest(ctx, codec) } // validateRequest returns a non-zero response code and error message if the // request is invalid. -func validateRequest(r *http.Request) (int, error) { +func (s *Server) validateRequest(r *http.Request) (int, error) { if r.Method == http.MethodPut || r.Method == http.MethodDelete { return http.StatusMethodNotAllowed, errors.New("method not allowed") } - if r.ContentLength > maxRequestContentLength { - err := fmt.Errorf("content length too large (%d>%d)", r.ContentLength, maxRequestContentLength) + if r.ContentLength > int64(s.httpBodyLimit) { + err := fmt.Errorf("content length too large (%d>%d)", r.ContentLength, s.httpBodyLimit) return http.StatusRequestEntityTooLarge, err } // Allow OPTIONS (regardless of content-type) diff --git a/rpc/http_test.go b/rpc/http_test.go index d5d4ad1158..17e5617058 100644 --- a/rpc/http_test.go +++ b/rpc/http_test.go @@ -50,11 +50,13 @@ func confirmStatusCode(t *testing.T, got, want int) { func confirmRequestValidationCode(t *testing.T, method, contentType, body string, expectedStatusCode int) { t.Helper() + + s := NewServer(0) request := httptest.NewRequest(method, "http://url.com", strings.NewReader(body)) if len(contentType) > 0 { request.Header.Set("Content-Type", contentType) } - code, err := validateRequest(request) + code, err := s.validateRequest(request) if code == 0 { if err != nil { t.Errorf("validation: got error %v, expected nil", err) @@ -74,7 +76,7 @@ func TestHTTPErrorResponseWithPut(t *testing.T) { } func TestHTTPErrorResponseWithMaxContentLength(t *testing.T) { - body := make([]rune, maxRequestContentLength+1) + body := make([]rune, defaultBodyLimit+1) confirmRequestValidationCode(t, http.MethodPost, contentType, string(body), http.StatusRequestEntityTooLarge) } @@ -114,7 +116,7 @@ func TestHTTPResponseWithEmptyGet(t *testing.T) { // This checks that maxRequestContentLength is not applied to the response of a request. func TestHTTPRespBodyUnlimited(t *testing.T) { - const respLength = maxRequestContentLength * 3 + const respLength = defaultBodyLimit * 3 s := NewServer(0) defer s.Stop() diff --git a/rpc/server.go b/rpc/server.go index a993fbe96e..054bd2de92 100644 --- a/rpc/server.go +++ b/rpc/server.go @@ -62,6 +62,7 @@ type Server struct { run atomic.Bool batchItemLimit int batchResponseLimit int + httpBodyLimit int } // NewServer creates a new server instance with no registered handlers. @@ -74,6 +75,7 @@ func NewServer(maximumDuration time.Duration) *Server { idgen: randomIDGenerator(), codecs: make(map[ServerCodec]struct{}), maximumDuration: maximumDuration, + httpBodyLimit: defaultBodyLimit, } server.run.Store(true) // Register the default service providing meta information about the RPC service such @@ -94,6 +96,13 @@ func (s *Server) SetBatchLimits(itemLimit, maxResponseSize int) { s.batchResponseLimit = maxResponseSize } +// SetHTTPBodyLimit sets the size limit for HTTP requests. +// +// This method should be called before processing any requests via ServeHTTP. +func (s *Server) SetHTTPBodyLimit(limit int) { + s.httpBodyLimit = limit +} + // RegisterName creates a service for the given receiver type under the given name. When no // methods on the given receiver match the criteria to be either a RPC method or a // subscription an error is returned. Otherwise a new service is created and added to the diff --git a/rpc/types.go b/rpc/types.go index 7b0d717dad..b2f5b98528 100644 --- a/rpc/types.go +++ b/rpc/types.go @@ -29,6 +29,7 @@ package rpc import ( "context" "encoding/json" + "errors" "fmt" "math" "strings" @@ -115,7 +116,7 @@ func (bn *BlockNumber) UnmarshalJSON(data []byte) error { return err } if blckNum > math.MaxInt64 { - return fmt.Errorf("block number larger than int64") + return errors.New("block number larger than int64") } *bn = BlockNumber(blckNum) return nil @@ -174,7 +175,7 @@ func (bnh *BlockNumberOrHash) UnmarshalJSON(data []byte) error { err := json.Unmarshal(data, &e) if err == nil { if e.BlockNumber != nil && e.BlockHash != nil { - return fmt.Errorf("cannot specify both BlockHash and BlockNumber, choose one or the other") + return errors.New("cannot specify both BlockHash and BlockNumber, choose one or the other") } bnh.BlockNumber = e.BlockNumber bnh.BlockHash = e.BlockHash @@ -223,7 +224,7 @@ func (bnh *BlockNumberOrHash) UnmarshalJSON(data []byte) error { return err } if blckNum > math.MaxInt64 { - return fmt.Errorf("blocknumber too high") + return errors.New("blocknumber too high") } bn := BlockNumber(blckNum) bnh.BlockNumber = &bn diff --git a/rpc/websocket_test.go b/rpc/websocket_test.go index ae7203e58f..42a6518310 100644 --- a/rpc/websocket_test.go +++ b/rpc/websocket_test.go @@ -108,7 +108,7 @@ func TestWebsocketLargeCall(t *testing.T) { // This call sends slightly less than the limit and should work. var result echoResult - arg := strings.Repeat("x", maxRequestContentLength-200) + arg := strings.Repeat("x", defaultBodyLimit-200) if err := client.Call(&result, "test_echo", arg, 1); err != nil { t.Fatalf("valid call didn't work: %v", err) } @@ -117,7 +117,7 @@ func TestWebsocketLargeCall(t *testing.T) { } // This call sends twice the allowed size and shouldn't work. - arg = strings.Repeat("x", maxRequestContentLength*2) + arg = strings.Repeat("x", defaultBodyLimit*2) err = client.Call(&result, "test_echo", arg) if err == nil { t.Fatal("no error for too large call") diff --git a/signer/core/apitypes/types.go b/signer/core/apitypes/types.go index 64e75c9ed4..89b621d329 100644 --- a/signer/core/apitypes/types.go +++ b/signer/core/apitypes/types.go @@ -61,7 +61,7 @@ func (vs *ValidationMessages) Info(msg string) { vs.Messages = append(vs.Messages, ValidationInfo{INFO, msg}) } -// getWarnings returns an error with all messages of type WARN of above, or nil if no warnings were present +// GetWarnings returns an error with all messages of type WARN of above, or nil if no warnings were present func (v *ValidationMessages) GetWarnings() error { var messages []string for _, msg := range v.Messages { diff --git a/stateupgrade/interfaces.go b/stateupgrade/interfaces.go index f667980487..a12351ecc9 100644 --- a/stateupgrade/interfaces.go +++ b/stateupgrade/interfaces.go @@ -7,13 +7,14 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" + "github.com/holiman/uint256" ) // StateDB is the interface for accessing EVM state in state upgrades type StateDB interface { SetState(common.Address, common.Hash, common.Hash) SetCode(common.Address, []byte) - AddBalance(common.Address, *big.Int) + AddBalance(common.Address, *uint256.Int) GetNonce(common.Address) uint64 SetNonce(common.Address, uint64) diff --git a/stateupgrade/state_upgrade.go b/stateupgrade/state_upgrade.go index 682e7cd493..65a1f2dc69 100644 --- a/stateupgrade/state_upgrade.go +++ b/stateupgrade/state_upgrade.go @@ -8,6 +8,7 @@ import ( "github.com/ava-labs/subnet-evm/params" "github.com/ethereum/go-ethereum/common" + "github.com/holiman/uint256" ) // Configure applies the state upgrade to the state. @@ -29,7 +30,8 @@ func upgradeAccount(account common.Address, upgrade params.StateUpgradeAccount, } if upgrade.BalanceChange != nil { - state.AddBalance(account, (*big.Int)(upgrade.BalanceChange)) + balanceChange, _ := uint256.FromBig((*big.Int)(upgrade.BalanceChange)) + state.AddBalance(account, balanceChange) } if len(upgrade.Code) != 0 { // if the nonce is 0, set the nonce to 1 as we would when deploying a contract at diff --git a/sync/client/client_test.go b/sync/client/client_test.go index 0ec9cafb77..1e65286085 100644 --- a/sync/client/client_test.go +++ b/sync/client/client_test.go @@ -25,7 +25,7 @@ import ( "github.com/ava-labs/subnet-evm/sync/handlers" handlerstats "github.com/ava-labs/subnet-evm/sync/handlers/stats" "github.com/ava-labs/subnet-evm/sync/syncutils" - "github.com/ava-labs/subnet-evm/trie" + "github.com/ava-labs/subnet-evm/triedb" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" ) @@ -143,7 +143,7 @@ func TestGetBlocks(t *testing.T) { Config: params.TestChainConfig, } memdb := rawdb.NewMemoryDatabase() - tdb := trie.NewDatabase(memdb, nil) + tdb := triedb.NewDatabase(memdb, nil) genesis := gspec.MustCommit(memdb, tdb) engine := dummy.NewETHFaker() numBlocks := 110 @@ -411,7 +411,7 @@ func TestGetLeafs(t *testing.T) { const leafsLimit = 1024 - trieDB := trie.NewDatabase(rawdb.NewMemoryDatabase(), nil) + trieDB := triedb.NewDatabase(rawdb.NewMemoryDatabase(), nil) largeTrieRoot, largeTrieKeys, _ := syncutils.GenerateTrie(t, trieDB, 100_000, common.HashLength) smallTrieRoot, _, _ := syncutils.GenerateTrie(t, trieDB, leafsLimit, common.HashLength) @@ -782,7 +782,7 @@ func TestGetLeafs(t *testing.T) { func TestGetLeafsRetries(t *testing.T) { rand.Seed(1) - trieDB := trie.NewDatabase(rawdb.NewMemoryDatabase(), nil) + trieDB := triedb.NewDatabase(rawdb.NewMemoryDatabase(), nil) root, _, _ := syncutils.GenerateTrie(t, trieDB, 100_000, common.HashLength) handler := handlers.NewLeafsRequestHandler(trieDB, nil, message.Codec, handlerstats.NewNoopHandlerStats()) diff --git a/sync/handlers/block_request_test.go b/sync/handlers/block_request_test.go index 968f71a309..dfc467c188 100644 --- a/sync/handlers/block_request_test.go +++ b/sync/handlers/block_request_test.go @@ -17,7 +17,7 @@ import ( "github.com/ava-labs/subnet-evm/params" "github.com/ava-labs/subnet-evm/plugin/evm/message" "github.com/ava-labs/subnet-evm/sync/handlers/stats" - "github.com/ava-labs/subnet-evm/trie" + "github.com/ava-labs/subnet-evm/triedb" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/rlp" @@ -106,7 +106,7 @@ func TestBlockRequestHandler(t *testing.T) { Config: params.TestChainConfig, } memdb := rawdb.NewMemoryDatabase() - tdb := trie.NewDatabase(memdb, nil) + tdb := triedb.NewDatabase(memdb, nil) genesis := gspec.MustCommit(memdb, tdb) engine := dummy.NewETHFaker() blocks, _, err := core.GenerateChain(params.TestChainConfig, genesis, engine, memdb, 96, 0, func(i int, b *core.BlockGen) {}) @@ -159,12 +159,12 @@ func TestBlockRequestHandlerLargeBlocks(t *testing.T) { funds = big.NewInt(1000000000000000000) gspec = &core.Genesis{ Config: ¶ms.ChainConfig{HomesteadBlock: new(big.Int)}, - Alloc: core.GenesisAlloc{addr1: {Balance: funds}}, + Alloc: types.GenesisAlloc{addr1: {Balance: funds}}, } signer = types.LatestSigner(gspec.Config) ) memdb := rawdb.NewMemoryDatabase() - tdb := trie.NewDatabase(memdb, nil) + tdb := triedb.NewDatabase(memdb, nil) genesis := gspec.MustCommit(memdb, tdb) engine := dummy.NewETHFaker() blocks, _, err := core.GenerateChain(gspec.Config, genesis, engine, memdb, 96, 0, func(i int, b *core.BlockGen) { @@ -218,7 +218,7 @@ func TestBlockRequestHandlerCtxExpires(t *testing.T) { Config: params.TestChainConfig, } memdb := rawdb.NewMemoryDatabase() - tdb := trie.NewDatabase(memdb, nil) + tdb := triedb.NewDatabase(memdb, nil) genesis := gspec.MustCommit(memdb, tdb) engine := dummy.NewETHFaker() blocks, _, err := core.GenerateChain(params.TestChainConfig, genesis, engine, memdb, 11, 0, func(i int, b *core.BlockGen) {}) diff --git a/sync/handlers/leafs_request.go b/sync/handlers/leafs_request.go index bc4abc809d..5222091649 100644 --- a/sync/handlers/leafs_request.go +++ b/sync/handlers/leafs_request.go @@ -17,6 +17,7 @@ import ( "github.com/ava-labs/subnet-evm/sync/handlers/stats" "github.com/ava-labs/subnet-evm/sync/syncutils" "github.com/ava-labs/subnet-evm/trie" + "github.com/ava-labs/subnet-evm/triedb" "github.com/ava-labs/subnet-evm/utils" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/ethdb" @@ -41,14 +42,14 @@ const ( // LeafsRequestHandler is a peer.RequestHandler for types.LeafsRequest // serving requested trie data type LeafsRequestHandler struct { - trieDB *trie.Database + trieDB *triedb.Database snapshotProvider SnapshotProvider codec codec.Manager stats stats.LeafsRequestHandlerStats pool sync.Pool } -func NewLeafsRequestHandler(trieDB *trie.Database, snapshotProvider SnapshotProvider, codec codec.Manager, syncerStats stats.LeafsRequestHandlerStats) *LeafsRequestHandler { +func NewLeafsRequestHandler(trieDB *triedb.Database, snapshotProvider SnapshotProvider, codec codec.Manager, syncerStats stats.LeafsRequestHandlerStats) *LeafsRequestHandler { return &LeafsRequestHandler{ trieDB: trieDB, snapshotProvider: snapshotProvider, diff --git a/sync/handlers/leafs_request_test.go b/sync/handlers/leafs_request_test.go index d73aa31ad8..7c97c8afa1 100644 --- a/sync/handlers/leafs_request_test.go +++ b/sync/handlers/leafs_request_test.go @@ -17,6 +17,7 @@ import ( "github.com/ava-labs/subnet-evm/sync/handlers/stats" "github.com/ava-labs/subnet-evm/sync/syncutils" "github.com/ava-labs/subnet-evm/trie" + "github.com/ava-labs/subnet-evm/triedb" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" @@ -27,7 +28,7 @@ func TestLeafsRequestHandler_OnLeafsRequest(t *testing.T) { rand.Seed(1) mockHandlerStats := &stats.MockHandlerStats{} memdb := rawdb.NewMemoryDatabase() - trieDB := trie.NewDatabase(memdb, nil) + trieDB := triedb.NewDatabase(memdb, nil) corruptedTrieRoot, _, _ := syncutils.GenerateTrie(t, trieDB, 100, common.HashLength) tr, err := trie.New(trie.TrieID(corruptedTrieRoot), trieDB) diff --git a/sync/statesync/state_syncer.go b/sync/statesync/state_syncer.go index 0cb7c33b54..873d81e000 100644 --- a/sync/statesync/state_syncer.go +++ b/sync/statesync/state_syncer.go @@ -10,7 +10,7 @@ import ( "github.com/ava-labs/subnet-evm/core/state/snapshot" syncclient "github.com/ava-labs/subnet-evm/sync/client" - "github.com/ava-labs/subnet-evm/trie" + "github.com/ava-labs/subnet-evm/triedb" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/ethdb" "golang.org/x/sync/errgroup" @@ -37,7 +37,7 @@ type StateSyncerConfig struct { type stateSync struct { db ethdb.Database // database we are syncing root common.Hash // root of the EVM state we are syncing to - trieDB *trie.Database // trieDB on top of db we are syncing. used to restore any existing tries. + trieDB *triedb.Database // trieDB on top of db we are syncing. used to restore any existing tries. snapshot snapshot.Snapshot // used to access the database we are syncing as a snapshot. batchSize int // write batches when they reach this size client syncclient.Client // used to contact peers over the network @@ -68,7 +68,7 @@ func NewStateSyncer(config *StateSyncerConfig) (*stateSync, error) { db: config.DB, client: config.Client, root: config.Root, - trieDB: trie.NewDatabase(config.DB, nil), + trieDB: triedb.NewDatabase(config.DB, nil), snapshot: snapshot.NewDiskLayer(config.DB), stats: newTrieSyncStats(), triesInProgress: make(map[common.Hash]*trieToSync), diff --git a/sync/statesync/sync_test.go b/sync/statesync/sync_test.go index 5d098d0856..1fe066e142 100644 --- a/sync/statesync/sync_test.go +++ b/sync/statesync/sync_test.go @@ -22,6 +22,7 @@ import ( handlerstats "github.com/ava-labs/subnet-evm/sync/handlers/stats" "github.com/ava-labs/subnet-evm/sync/syncutils" "github.com/ava-labs/subnet-evm/trie" + "github.com/ava-labs/subnet-evm/triedb" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" @@ -35,7 +36,7 @@ var errInterrupted = errors.New("interrupted sync") type syncTest struct { ctx context.Context - prepareForTest func(t *testing.T) (clientDB ethdb.Database, serverDB ethdb.Database, serverTrieDB *trie.Database, syncRoot common.Hash) + prepareForTest func(t *testing.T) (clientDB ethdb.Database, serverDB ethdb.Database, serverTrieDB *triedb.Database, syncRoot common.Hash) expectedError error GetLeafsIntercept func(message.LeafsRequest, message.LeafsResponse) (message.LeafsResponse, error) GetCodeIntercept func([]common.Hash, [][]byte) ([][]byte, error) @@ -74,7 +75,7 @@ func testSync(t *testing.T, test syncTest) { return } - assertDBConsistency(t, root, clientDB, serverTrieDB, trie.NewDatabase(clientDB, nil)) + assertDBConsistency(t, root, clientDB, serverTrieDB, triedb.NewDatabase(clientDB, nil)) } // testSyncResumes tests a series of syncTests work as expected, invoking a callback function after each @@ -118,17 +119,17 @@ func TestSimpleSyncCases(t *testing.T) { ) tests := map[string]syncTest{ "accounts": { - prepareForTest: func(t *testing.T) (ethdb.Database, ethdb.Database, *trie.Database, common.Hash) { + prepareForTest: func(t *testing.T) (ethdb.Database, ethdb.Database, *triedb.Database, common.Hash) { serverDB := rawdb.NewMemoryDatabase() - serverTrieDB := trie.NewDatabase(serverDB, nil) + serverTrieDB := triedb.NewDatabase(serverDB, nil) root, _ := syncutils.FillAccounts(t, serverTrieDB, common.Hash{}, numAccounts, nil) return rawdb.NewMemoryDatabase(), serverDB, serverTrieDB, root }, }, "accounts with code": { - prepareForTest: func(t *testing.T) (ethdb.Database, ethdb.Database, *trie.Database, common.Hash) { + prepareForTest: func(t *testing.T) (ethdb.Database, ethdb.Database, *triedb.Database, common.Hash) { serverDB := rawdb.NewMemoryDatabase() - serverTrieDB := trie.NewDatabase(serverDB, nil) + serverTrieDB := triedb.NewDatabase(serverDB, nil) root, _ := syncutils.FillAccounts(t, serverTrieDB, common.Hash{}, numAccounts, func(t *testing.T, index int, account types.StateAccount) types.StateAccount { if index%3 == 0 { codeBytes := make([]byte, 256) @@ -147,17 +148,17 @@ func TestSimpleSyncCases(t *testing.T) { }, }, "accounts with code and storage": { - prepareForTest: func(t *testing.T) (ethdb.Database, ethdb.Database, *trie.Database, common.Hash) { + prepareForTest: func(t *testing.T) (ethdb.Database, ethdb.Database, *triedb.Database, common.Hash) { serverDB := rawdb.NewMemoryDatabase() - serverTrieDB := trie.NewDatabase(serverDB, nil) + serverTrieDB := triedb.NewDatabase(serverDB, nil) root := fillAccountsWithStorage(t, serverDB, serverTrieDB, common.Hash{}, numAccounts) return rawdb.NewMemoryDatabase(), serverDB, serverTrieDB, root }, }, "accounts with storage": { - prepareForTest: func(t *testing.T) (ethdb.Database, ethdb.Database, *trie.Database, common.Hash) { + prepareForTest: func(t *testing.T) (ethdb.Database, ethdb.Database, *triedb.Database, common.Hash) { serverDB := rawdb.NewMemoryDatabase() - serverTrieDB := trie.NewDatabase(serverDB, nil) + serverTrieDB := triedb.NewDatabase(serverDB, nil) root, _ := syncutils.FillAccounts(t, serverTrieDB, common.Hash{}, numAccounts, func(t *testing.T, i int, account types.StateAccount) types.StateAccount { if i%5 == 0 { account.Root, _, _ = syncutils.GenerateTrie(t, serverTrieDB, 16, common.HashLength) @@ -169,17 +170,17 @@ func TestSimpleSyncCases(t *testing.T) { }, }, "accounts with overlapping storage": { - prepareForTest: func(t *testing.T) (ethdb.Database, ethdb.Database, *trie.Database, common.Hash) { + prepareForTest: func(t *testing.T) (ethdb.Database, ethdb.Database, *triedb.Database, common.Hash) { serverDB := rawdb.NewMemoryDatabase() - serverTrieDB := trie.NewDatabase(serverDB, nil) + serverTrieDB := triedb.NewDatabase(serverDB, nil) root, _ := FillAccountsWithOverlappingStorage(t, serverTrieDB, common.Hash{}, numAccounts, 3) return rawdb.NewMemoryDatabase(), serverDB, serverTrieDB, root }, }, "failed to fetch leafs": { - prepareForTest: func(t *testing.T) (ethdb.Database, ethdb.Database, *trie.Database, common.Hash) { + prepareForTest: func(t *testing.T) (ethdb.Database, ethdb.Database, *triedb.Database, common.Hash) { serverDB := rawdb.NewMemoryDatabase() - serverTrieDB := trie.NewDatabase(serverDB, nil) + serverTrieDB := triedb.NewDatabase(serverDB, nil) root, _ := syncutils.FillAccounts(t, serverTrieDB, common.Hash{}, numAccountsSmall, nil) return rawdb.NewMemoryDatabase(), serverDB, serverTrieDB, root }, @@ -189,9 +190,9 @@ func TestSimpleSyncCases(t *testing.T) { expectedError: clientErr, }, "failed to fetch code": { - prepareForTest: func(t *testing.T) (ethdb.Database, ethdb.Database, *trie.Database, common.Hash) { + prepareForTest: func(t *testing.T) (ethdb.Database, ethdb.Database, *triedb.Database, common.Hash) { serverDB := rawdb.NewMemoryDatabase() - serverTrieDB := trie.NewDatabase(serverDB, nil) + serverTrieDB := triedb.NewDatabase(serverDB, nil) root := fillAccountsWithStorage(t, serverDB, serverTrieDB, common.Hash{}, numAccountsSmall) return rawdb.NewMemoryDatabase(), serverDB, serverTrieDB, root }, @@ -211,14 +212,14 @@ func TestSimpleSyncCases(t *testing.T) { func TestCancelSync(t *testing.T) { serverDB := rawdb.NewMemoryDatabase() - serverTrieDB := trie.NewDatabase(serverDB, nil) + serverTrieDB := triedb.NewDatabase(serverDB, nil) // Create trie with 2000 accounts (more than one leaf request) root := fillAccountsWithStorage(t, serverDB, serverTrieDB, common.Hash{}, 2000) ctx, cancel := context.WithCancel(context.Background()) defer cancel() testSync(t, syncTest{ ctx: ctx, - prepareForTest: func(t *testing.T) (ethdb.Database, ethdb.Database, *trie.Database, common.Hash) { + prepareForTest: func(t *testing.T) (ethdb.Database, ethdb.Database, *triedb.Database, common.Hash) { return rawdb.NewMemoryDatabase(), serverDB, serverTrieDB, root }, expectedError: context.Canceled, @@ -252,7 +253,7 @@ func (i *interruptLeafsIntercept) getLeafsIntercept(request message.LeafsRequest func TestResumeSyncAccountsTrieInterrupted(t *testing.T) { serverDB := rawdb.NewMemoryDatabase() - serverTrieDB := trie.NewDatabase(serverDB, nil) + serverTrieDB := triedb.NewDatabase(serverDB, nil) root, _ := FillAccountsWithOverlappingStorage(t, serverTrieDB, common.Hash{}, 2000, 3) clientDB := rawdb.NewMemoryDatabase() intercept := &interruptLeafsIntercept{ @@ -260,7 +261,7 @@ func TestResumeSyncAccountsTrieInterrupted(t *testing.T) { interruptAfter: 1, } testSync(t, syncTest{ - prepareForTest: func(t *testing.T) (ethdb.Database, ethdb.Database, *trie.Database, common.Hash) { + prepareForTest: func(t *testing.T) (ethdb.Database, ethdb.Database, *triedb.Database, common.Hash) { return clientDB, serverDB, serverTrieDB, root }, expectedError: errInterrupted, @@ -270,7 +271,7 @@ func TestResumeSyncAccountsTrieInterrupted(t *testing.T) { assert.EqualValues(t, 2, intercept.numRequests) testSync(t, syncTest{ - prepareForTest: func(t *testing.T) (ethdb.Database, ethdb.Database, *trie.Database, common.Hash) { + prepareForTest: func(t *testing.T) (ethdb.Database, ethdb.Database, *triedb.Database, common.Hash) { return clientDB, serverDB, serverTrieDB, root }, }) @@ -278,7 +279,7 @@ func TestResumeSyncAccountsTrieInterrupted(t *testing.T) { func TestResumeSyncLargeStorageTrieInterrupted(t *testing.T) { serverDB := rawdb.NewMemoryDatabase() - serverTrieDB := trie.NewDatabase(serverDB, nil) + serverTrieDB := triedb.NewDatabase(serverDB, nil) largeStorageRoot, _, _ := syncutils.GenerateTrie(t, serverTrieDB, 2000, common.HashLength) root, _ := syncutils.FillAccounts(t, serverTrieDB, common.Hash{}, 2000, func(t *testing.T, index int, account types.StateAccount) types.StateAccount { @@ -294,7 +295,7 @@ func TestResumeSyncLargeStorageTrieInterrupted(t *testing.T) { interruptAfter: 1, } testSync(t, syncTest{ - prepareForTest: func(t *testing.T) (ethdb.Database, ethdb.Database, *trie.Database, common.Hash) { + prepareForTest: func(t *testing.T) (ethdb.Database, ethdb.Database, *triedb.Database, common.Hash) { return clientDB, serverDB, serverTrieDB, root }, expectedError: errInterrupted, @@ -302,7 +303,7 @@ func TestResumeSyncLargeStorageTrieInterrupted(t *testing.T) { }) testSync(t, syncTest{ - prepareForTest: func(t *testing.T) (ethdb.Database, ethdb.Database, *trie.Database, common.Hash) { + prepareForTest: func(t *testing.T) (ethdb.Database, ethdb.Database, *triedb.Database, common.Hash) { return clientDB, serverDB, serverTrieDB, root }, }) @@ -310,7 +311,7 @@ func TestResumeSyncLargeStorageTrieInterrupted(t *testing.T) { func TestResumeSyncToNewRootAfterLargeStorageTrieInterrupted(t *testing.T) { serverDB := rawdb.NewMemoryDatabase() - serverTrieDB := trie.NewDatabase(serverDB, nil) + serverTrieDB := triedb.NewDatabase(serverDB, nil) largeStorageRoot1, _, _ := syncutils.GenerateTrie(t, serverTrieDB, 2000, common.HashLength) largeStorageRoot2, _, _ := syncutils.GenerateTrie(t, serverTrieDB, 2000, common.HashLength) @@ -333,7 +334,7 @@ func TestResumeSyncToNewRootAfterLargeStorageTrieInterrupted(t *testing.T) { interruptAfter: 1, } testSync(t, syncTest{ - prepareForTest: func(t *testing.T) (ethdb.Database, ethdb.Database, *trie.Database, common.Hash) { + prepareForTest: func(t *testing.T) (ethdb.Database, ethdb.Database, *triedb.Database, common.Hash) { return clientDB, serverDB, serverTrieDB, root1 }, expectedError: errInterrupted, @@ -343,7 +344,7 @@ func TestResumeSyncToNewRootAfterLargeStorageTrieInterrupted(t *testing.T) { <-snapshot.WipeSnapshot(clientDB, false) testSync(t, syncTest{ - prepareForTest: func(t *testing.T) (ethdb.Database, ethdb.Database, *trie.Database, common.Hash) { + prepareForTest: func(t *testing.T) (ethdb.Database, ethdb.Database, *triedb.Database, common.Hash) { return clientDB, serverDB, serverTrieDB, root2 }, }) @@ -351,7 +352,7 @@ func TestResumeSyncToNewRootAfterLargeStorageTrieInterrupted(t *testing.T) { func TestResumeSyncLargeStorageTrieWithConsecutiveDuplicatesInterrupted(t *testing.T) { serverDB := rawdb.NewMemoryDatabase() - serverTrieDB := trie.NewDatabase(serverDB, nil) + serverTrieDB := triedb.NewDatabase(serverDB, nil) largeStorageRoot, _, _ := syncutils.GenerateTrie(t, serverTrieDB, 2000, common.HashLength) root, _ := syncutils.FillAccounts(t, serverTrieDB, common.Hash{}, 100, func(t *testing.T, index int, account types.StateAccount) types.StateAccount { @@ -367,7 +368,7 @@ func TestResumeSyncLargeStorageTrieWithConsecutiveDuplicatesInterrupted(t *testi interruptAfter: 1, } testSync(t, syncTest{ - prepareForTest: func(t *testing.T) (ethdb.Database, ethdb.Database, *trie.Database, common.Hash) { + prepareForTest: func(t *testing.T) (ethdb.Database, ethdb.Database, *triedb.Database, common.Hash) { return clientDB, serverDB, serverTrieDB, root }, expectedError: errInterrupted, @@ -375,7 +376,7 @@ func TestResumeSyncLargeStorageTrieWithConsecutiveDuplicatesInterrupted(t *testi }) testSync(t, syncTest{ - prepareForTest: func(t *testing.T) (ethdb.Database, ethdb.Database, *trie.Database, common.Hash) { + prepareForTest: func(t *testing.T) (ethdb.Database, ethdb.Database, *triedb.Database, common.Hash) { return clientDB, serverDB, serverTrieDB, root }, }) @@ -383,7 +384,7 @@ func TestResumeSyncLargeStorageTrieWithConsecutiveDuplicatesInterrupted(t *testi func TestResumeSyncLargeStorageTrieWithSpreadOutDuplicatesInterrupted(t *testing.T) { serverDB := rawdb.NewMemoryDatabase() - serverTrieDB := trie.NewDatabase(serverDB, nil) + serverTrieDB := triedb.NewDatabase(serverDB, nil) largeStorageRoot, _, _ := syncutils.GenerateTrie(t, serverTrieDB, 2000, common.HashLength) root, _ := syncutils.FillAccounts(t, serverTrieDB, common.Hash{}, 100, func(t *testing.T, index int, account types.StateAccount) types.StateAccount { @@ -398,7 +399,7 @@ func TestResumeSyncLargeStorageTrieWithSpreadOutDuplicatesInterrupted(t *testing interruptAfter: 1, } testSync(t, syncTest{ - prepareForTest: func(t *testing.T) (ethdb.Database, ethdb.Database, *trie.Database, common.Hash) { + prepareForTest: func(t *testing.T) (ethdb.Database, ethdb.Database, *triedb.Database, common.Hash) { return clientDB, serverDB, serverTrieDB, root }, expectedError: errInterrupted, @@ -406,7 +407,7 @@ func TestResumeSyncLargeStorageTrieWithSpreadOutDuplicatesInterrupted(t *testing }) testSync(t, syncTest{ - prepareForTest: func(t *testing.T) (ethdb.Database, ethdb.Database, *trie.Database, common.Hash) { + prepareForTest: func(t *testing.T) (ethdb.Database, ethdb.Database, *triedb.Database, common.Hash) { return clientDB, serverDB, serverTrieDB, root }, }) @@ -436,7 +437,7 @@ func TestResyncNewRootAfterDeletes(t *testing.T) { }, "delete intermediate storage nodes": { deleteBetweenSyncs: func(t *testing.T, root common.Hash, clientDB ethdb.Database) { - clientTrieDB := trie.NewDatabase(clientDB, nil) + clientTrieDB := triedb.NewDatabase(clientDB, nil) tr, err := trie.New(trie.TrieID(root), clientTrieDB) if err != nil { t.Fatal(err) @@ -482,7 +483,7 @@ func TestResyncNewRootAfterDeletes(t *testing.T) { }, "delete intermediate account trie nodes": { deleteBetweenSyncs: func(t *testing.T, root common.Hash, clientDB ethdb.Database) { - clientTrieDB := trie.NewDatabase(clientDB, nil) + clientTrieDB := triedb.NewDatabase(clientDB, nil) tr, err := trie.New(trie.TrieID(root), clientTrieDB) if err != nil { t.Fatal(err) @@ -501,7 +502,7 @@ func testSyncerSyncsToNewRoot(t *testing.T, deleteBetweenSyncs func(*testing.T, rand.Seed(1) clientDB := rawdb.NewMemoryDatabase() serverDB := rawdb.NewMemoryDatabase() - serverTrieDB := trie.NewDatabase(serverDB, nil) + serverTrieDB := triedb.NewDatabase(serverDB, nil) root1, _ := FillAccountsWithOverlappingStorage(t, serverTrieDB, common.Hash{}, 1000, 3) root2, _ := FillAccountsWithOverlappingStorage(t, serverTrieDB, root1, 1000, 3) @@ -510,12 +511,12 @@ func testSyncerSyncsToNewRoot(t *testing.T, deleteBetweenSyncs func(*testing.T, testSyncResumes(t, []syncTest{ { - prepareForTest: func(t *testing.T) (ethdb.Database, ethdb.Database, *trie.Database, common.Hash) { + prepareForTest: func(t *testing.T) (ethdb.Database, ethdb.Database, *triedb.Database, common.Hash) { return clientDB, serverDB, serverTrieDB, root1 }, }, { - prepareForTest: func(t *testing.T) (ethdb.Database, ethdb.Database, *trie.Database, common.Hash) { + prepareForTest: func(t *testing.T) (ethdb.Database, ethdb.Database, *triedb.Database, common.Hash) { return clientDB, serverDB, serverTrieDB, root2 }, }, diff --git a/sync/statesync/test_sync.go b/sync/statesync/test_sync.go index a319e34c08..f3e32797fe 100644 --- a/sync/statesync/test_sync.go +++ b/sync/statesync/test_sync.go @@ -12,7 +12,7 @@ import ( "github.com/ava-labs/subnet-evm/core/rawdb" "github.com/ava-labs/subnet-evm/core/types" "github.com/ava-labs/subnet-evm/sync/syncutils" - "github.com/ava-labs/subnet-evm/trie" + "github.com/ava-labs/subnet-evm/triedb" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" @@ -23,7 +23,7 @@ import ( // assertDBConsistency checks [serverTrieDB] and [clientTrieDB] have the same EVM state trie at [root], // and that [clientTrieDB.DiskDB] has corresponding account & snapshot values. // Also verifies any code referenced by the EVM state is present in [clientTrieDB] and the hash is correct. -func assertDBConsistency(t testing.TB, root common.Hash, clientDB ethdb.Database, serverTrieDB, clientTrieDB *trie.Database) { +func assertDBConsistency(t testing.TB, root common.Hash, clientDB ethdb.Database, serverTrieDB, clientTrieDB *triedb.Database) { numSnapshotAccounts := 0 accountIt := rawdb.IterateAccountSnapshots(clientDB) defer accountIt.Release() @@ -88,7 +88,7 @@ func assertDBConsistency(t testing.TB, root common.Hash, clientDB ethdb.Database assert.Equal(t, trieAccountLeaves, numSnapshotAccounts) } -func fillAccountsWithStorage(t *testing.T, serverDB ethdb.Database, serverTrieDB *trie.Database, root common.Hash, numAccounts int) common.Hash { +func fillAccountsWithStorage(t *testing.T, serverDB ethdb.Database, serverTrieDB *triedb.Database, root common.Hash, numAccounts int) common.Hash { newRoot, _ := syncutils.FillAccounts(t, serverTrieDB, root, numAccounts, func(t *testing.T, index int, account types.StateAccount) types.StateAccount { codeBytes := make([]byte, 256) _, err := rand.Read(codeBytes) @@ -115,7 +115,7 @@ func fillAccountsWithStorage(t *testing.T, serverDB ethdb.Database, serverTrieDB // - One has a uniquely generated storage trie, // returns the new trie root and a map of funded keys to StateAccount structs. func FillAccountsWithOverlappingStorage( - t *testing.T, trieDB *trie.Database, root common.Hash, numAccounts int, numOverlappingStorageRoots int, + t *testing.T, trieDB *triedb.Database, root common.Hash, numAccounts int, numOverlappingStorageRoots int, ) (common.Hash, map[*keystore.Key]*types.StateAccount) { storageRoots := make([]common.Hash, 0, numOverlappingStorageRoots) for i := 0; i < numOverlappingStorageRoots; i++ { diff --git a/sync/syncutils/test_trie.go b/sync/syncutils/test_trie.go index 23b603ce73..a02ce03216 100644 --- a/sync/syncutils/test_trie.go +++ b/sync/syncutils/test_trie.go @@ -6,7 +6,6 @@ package syncutils import ( cryptoRand "crypto/rand" "encoding/binary" - "math/big" "math/rand" "testing" @@ -15,10 +14,12 @@ import ( "github.com/ava-labs/subnet-evm/core/types" "github.com/ava-labs/subnet-evm/trie" "github.com/ava-labs/subnet-evm/trie/trienode" + "github.com/ava-labs/subnet-evm/triedb" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/rlp" + "github.com/holiman/uint256" "github.com/stretchr/testify/assert" ) @@ -26,7 +27,7 @@ import ( // Returns the root of the generated trie, the slice of keys inserted into the trie in lexicographical // order, and the slice of corresponding values. // GenerateTrie reads from [rand] and the caller should call rand.Seed(n) for deterministic results -func GenerateTrie(t *testing.T, trieDB *trie.Database, numKeys int, keySize int) (common.Hash, [][]byte, [][]byte) { +func GenerateTrie(t *testing.T, trieDB *triedb.Database, numKeys int, keySize int) (common.Hash, [][]byte, [][]byte) { if keySize < wrappers.LongLen+1 { t.Fatal("key size must be at least 9 bytes (8 bytes for uint64 and 1 random byte)") } @@ -36,7 +37,7 @@ func GenerateTrie(t *testing.T, trieDB *trie.Database, numKeys int, keySize int) // FillTrie fills a given trie with [numKeys] number of keys, each of size [keySize] // returns inserted keys and values // FillTrie reads from [rand] and the caller should call rand.Seed(n) for deterministic results -func FillTrie(t *testing.T, start, numKeys int, keySize int, trieDB *trie.Database, root common.Hash) (common.Hash, [][]byte, [][]byte) { +func FillTrie(t *testing.T, start, numKeys int, keySize int, trieDB *triedb.Database, root common.Hash) (common.Hash, [][]byte, [][]byte) { testTrie, err := trie.New(trie.TrieID(root), trieDB) if err != nil { t.Fatalf("error creating trie: %v", err) @@ -75,7 +76,7 @@ func FillTrie(t *testing.T, start, numKeys int, keySize int, trieDB *trie.Databa // AssertTrieConsistency ensures given trieDB [a] and [b] both have the same // non-empty trie at [root]. (all key/value pairs must be equal) -func AssertTrieConsistency(t testing.TB, root common.Hash, a, b *trie.Database, onLeaf func(key, val []byte) error) { +func AssertTrieConsistency(t testing.TB, root common.Hash, a, b *triedb.Database, onLeaf func(key, val []byte) error) { trieA, err := trie.New(trie.TrieID(root), a) if err != nil { t.Fatalf("error creating trieA, root=%s, err=%v", root, err) @@ -144,12 +145,12 @@ func CorruptTrie(t *testing.T, diskdb ethdb.Batcher, tr *trie.Trie, n int) { // [onAccount] is called if non-nil (so the caller can modify the account before it is stored in the secure trie). // returns the new trie root and a map of funded keys to StateAccount structs. func FillAccounts( - t *testing.T, trieDB *trie.Database, root common.Hash, numAccounts int, + t *testing.T, trieDB *triedb.Database, root common.Hash, numAccounts int, onAccount func(*testing.T, int, types.StateAccount) types.StateAccount, ) (common.Hash, map[*keystore.Key]*types.StateAccount) { var ( - minBalance = big.NewInt(3000000000000000000) - randBalance = big.NewInt(1000000000000000000) + minBalance = uint256.NewInt(3000000000000000000) + randBalance = uint256.NewInt(1000000000000000000) maxNonce = 10 accounts = make(map[*keystore.Key]*types.StateAccount, numAccounts) ) @@ -162,7 +163,7 @@ func FillAccounts( for i := 0; i < numAccounts; i++ { acc := types.StateAccount{ Nonce: uint64(rand.Intn(maxNonce)), - Balance: new(big.Int).Add(minBalance, randBalance), + Balance: new(uint256.Int).Add(minBalance, randBalance), CodeHash: types.EmptyCodeHash[:], Root: types.EmptyRootHash, } diff --git a/tests/gen_stenv.go b/tests/gen_stenv.go new file mode 100644 index 0000000000..9bcdb5d162 --- /dev/null +++ b/tests/gen_stenv.go @@ -0,0 +1,86 @@ +// Code generated by github.com/fjl/gencodec. DO NOT EDIT. + +package tests + +import ( + "encoding/json" + "errors" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/math" +) + +var _ = (*stEnvMarshaling)(nil) + +// MarshalJSON marshals as JSON. +func (s stEnv) MarshalJSON() ([]byte, error) { + type stEnv struct { + Coinbase common.UnprefixedAddress `json:"currentCoinbase" gencodec:"required"` + Difficulty *math.HexOrDecimal256 `json:"currentDifficulty" gencodec:"required"` + Random *math.HexOrDecimal256 `json:"currentRandom" gencodec:"optional"` + GasLimit math.HexOrDecimal64 `json:"currentGasLimit" gencodec:"required"` + Number math.HexOrDecimal64 `json:"currentNumber" gencodec:"required"` + Timestamp math.HexOrDecimal64 `json:"currentTimestamp" gencodec:"required"` + BaseFee *math.HexOrDecimal256 `json:"currentBaseFee" gencodec:"optional"` + ExcessBlobGas *math.HexOrDecimal64 `json:"currentExcessBlobGas" gencodec:"optional"` + } + var enc stEnv + enc.Coinbase = common.UnprefixedAddress(s.Coinbase) + enc.Difficulty = (*math.HexOrDecimal256)(s.Difficulty) + enc.Random = (*math.HexOrDecimal256)(s.Random) + enc.GasLimit = math.HexOrDecimal64(s.GasLimit) + enc.Number = math.HexOrDecimal64(s.Number) + enc.Timestamp = math.HexOrDecimal64(s.Timestamp) + enc.BaseFee = (*math.HexOrDecimal256)(s.BaseFee) + enc.ExcessBlobGas = (*math.HexOrDecimal64)(s.ExcessBlobGas) + return json.Marshal(&enc) +} + +// UnmarshalJSON unmarshals from JSON. +func (s *stEnv) UnmarshalJSON(input []byte) error { + type stEnv struct { + Coinbase *common.UnprefixedAddress `json:"currentCoinbase" gencodec:"required"` + Difficulty *math.HexOrDecimal256 `json:"currentDifficulty" gencodec:"required"` + Random *math.HexOrDecimal256 `json:"currentRandom" gencodec:"optional"` + GasLimit *math.HexOrDecimal64 `json:"currentGasLimit" gencodec:"required"` + Number *math.HexOrDecimal64 `json:"currentNumber" gencodec:"required"` + Timestamp *math.HexOrDecimal64 `json:"currentTimestamp" gencodec:"required"` + BaseFee *math.HexOrDecimal256 `json:"currentBaseFee" gencodec:"optional"` + ExcessBlobGas *math.HexOrDecimal64 `json:"currentExcessBlobGas" gencodec:"optional"` + } + var dec stEnv + if err := json.Unmarshal(input, &dec); err != nil { + return err + } + if dec.Coinbase == nil { + return errors.New("missing required field 'currentCoinbase' for stEnv") + } + s.Coinbase = common.Address(*dec.Coinbase) + if dec.Difficulty == nil { + return errors.New("missing required field 'currentDifficulty' for stEnv") + } + s.Difficulty = (*big.Int)(dec.Difficulty) + if dec.Random != nil { + s.Random = (*big.Int)(dec.Random) + } + if dec.GasLimit == nil { + return errors.New("missing required field 'currentGasLimit' for stEnv") + } + s.GasLimit = uint64(*dec.GasLimit) + if dec.Number == nil { + return errors.New("missing required field 'currentNumber' for stEnv") + } + s.Number = uint64(*dec.Number) + if dec.Timestamp == nil { + return errors.New("missing required field 'currentTimestamp' for stEnv") + } + s.Timestamp = uint64(*dec.Timestamp) + if dec.BaseFee != nil { + s.BaseFee = (*big.Int)(dec.BaseFee) + } + if dec.ExcessBlobGas != nil { + s.ExcessBlobGas = (*uint64)(dec.ExcessBlobGas) + } + return nil +} diff --git a/tests/state_test_util.go b/tests/state_test_util.go index a31ea88a95..7ba439accc 100644 --- a/tests/state_test_util.go +++ b/tests/state_test_util.go @@ -35,6 +35,7 @@ import ( "strconv" "strings" + "github.com/ava-labs/subnet-evm/consensus/misc/eip4844" "github.com/ava-labs/subnet-evm/core" "github.com/ava-labs/subnet-evm/core/rawdb" "github.com/ava-labs/subnet-evm/core/state" @@ -42,15 +43,16 @@ import ( "github.com/ava-labs/subnet-evm/core/types" "github.com/ava-labs/subnet-evm/core/vm" "github.com/ava-labs/subnet-evm/params" - "github.com/ava-labs/subnet-evm/trie" - "github.com/ava-labs/subnet-evm/trie/triedb/hashdb" - "github.com/ava-labs/subnet-evm/trie/triedb/pathdb" + "github.com/ava-labs/subnet-evm/triedb" + "github.com/ava-labs/subnet-evm/triedb/hashdb" + "github.com/ava-labs/subnet-evm/triedb/pathdb" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/rlp" + "github.com/holiman/uint256" "golang.org/x/crypto/sha3" ) @@ -72,7 +74,7 @@ func (t *StateTest) UnmarshalJSON(in []byte) error { type stJSON struct { Env stEnv `json:"env"` - Pre core.GenesisAlloc `json:"pre"` + Pre types.GenesisAlloc `json:"pre"` Tx stTransaction `json:"transaction"` Out hexutil.Bytes `json:"out"` Post map[string][]stPostState `json:"post"` @@ -92,13 +94,25 @@ type stPostState struct { //go:generate go run github.com/fjl/gencodec -type stEnv -field-override stEnvMarshaling -out gen_stenv.go type stEnv struct { - Coinbase common.Address `json:"currentCoinbase" gencodec:"required"` - Difficulty *big.Int `json:"currentDifficulty" gencodec:"required"` - Random *big.Int `json:"currentRandom" gencodec:"optional"` - GasLimit uint64 `json:"currentGasLimit" gencodec:"required"` - Number uint64 `json:"currentNumber" gencodec:"required"` - Timestamp uint64 `json:"currentTimestamp" gencodec:"required"` - BaseFee *big.Int `json:"currentBaseFee" gencodec:"optional"` + Coinbase common.Address `json:"currentCoinbase" gencodec:"required"` + Difficulty *big.Int `json:"currentDifficulty" gencodec:"required"` + Random *big.Int `json:"currentRandom" gencodec:"optional"` + GasLimit uint64 `json:"currentGasLimit" gencodec:"required"` + Number uint64 `json:"currentNumber" gencodec:"required"` + Timestamp uint64 `json:"currentTimestamp" gencodec:"required"` + BaseFee *big.Int `json:"currentBaseFee" gencodec:"optional"` + ExcessBlobGas *uint64 `json:"currentExcessBlobGas" gencodec:"optional"` +} + +type stEnvMarshaling struct { + Coinbase common.UnprefixedAddress + Difficulty *math.HexOrDecimal256 + Random *math.HexOrDecimal256 + GasLimit math.HexOrDecimal64 + Number math.HexOrDecimal64 + Timestamp math.HexOrDecimal64 + BaseFee *math.HexOrDecimal256 + ExcessBlobGas *math.HexOrDecimal64 } //go:generate go run github.com/fjl/gencodec -type stTransaction -field-override stTransactionMarshaling -out gen_sttransaction.go @@ -203,20 +217,14 @@ func (t *StateTest) checkError(subtest StateSubtest, err error) error { } // Run executes a specific subtest and verifies the post-state and logs -func (t *StateTest) Run(subtest StateSubtest, vmconfig vm.Config, snapshotter bool, scheme string, postCheck func(err error, snaps *snapshot.Tree, state *state.StateDB)) (result error) { - triedb, snaps, statedb, root, err := t.RunNoVerify(subtest, vmconfig, snapshotter, scheme) - +func (t *StateTest) Run(subtest StateSubtest, vmconfig vm.Config, snapshotter bool, scheme string, postCheck func(err error, st *StateTestState)) (result error) { + st, root, err := t.RunNoVerify(subtest, vmconfig, snapshotter, scheme) // Invoke the callback at the end of function for further analysis. defer func() { - postCheck(result, snaps, statedb) - - if triedb != nil { - triedb.Close() - } - if snaps != nil { - snaps.Release() - } + postCheck(result, &st) + st.Close() }() + checkedErr := t.checkError(subtest, err) if checkedErr != nil { return checkedErr @@ -233,23 +241,24 @@ func (t *StateTest) Run(subtest StateSubtest, vmconfig vm.Config, snapshotter bo if root != common.Hash(post.Root) { return fmt.Errorf("post state root mismatch: got %x, want %x", root, post.Root) } - if logs := rlpHash(statedb.Logs()); logs != common.Hash(post.Logs) { + if logs := rlpHash(st.StateDB.Logs()); logs != common.Hash(post.Logs) { return fmt.Errorf("post state logs hash mismatch: got %x, want %x", logs, post.Logs) } - statedb, _ = state.New(root, statedb.Database(), snaps) + st.StateDB, _ = state.New(root, st.StateDB.Database(), st.Snapshots) return nil } -// RunNoVerify runs a specific subtest and returns the statedb and post-state root -func (t *StateTest) RunNoVerify(subtest StateSubtest, vmconfig vm.Config, snapshotter bool, scheme string) (*trie.Database, *snapshot.Tree, *state.StateDB, common.Hash, error) { +// RunNoVerify runs a specific subtest and returns the statedb and post-state root. +// Remember to call state.Close after verifying the test result! +func (t *StateTest) RunNoVerify(subtest StateSubtest, vmconfig vm.Config, snapshotter bool, scheme string) (state StateTestState, root common.Hash, err error) { config, eips, err := GetChainConfig(subtest.Fork) if err != nil { - return nil, nil, nil, common.Hash{}, UnsupportedForkError{subtest.Fork} + return state, common.Hash{}, UnsupportedForkError{subtest.Fork} } vmconfig.ExtraEips = eips block := t.genesis(config).ToBlock() - triedb, snaps, statedb := MakePreState(rawdb.NewMemoryDatabase(), t.json.Pre, snapshotter, scheme) + state = MakePreState(rawdb.NewMemoryDatabase(), t.json.Pre, snapshotter, scheme) var baseFee *big.Int if config.IsSubnetEVM(0) { @@ -263,8 +272,18 @@ func (t *StateTest) RunNoVerify(subtest StateSubtest, vmconfig vm.Config, snapsh post := t.json.Post[subtest.Fork][subtest.Index] msg, err := t.json.Tx.toMessage(post, baseFee) if err != nil { - triedb.Close() - return nil, nil, nil, common.Hash{}, err + return state, common.Hash{}, err + } + + { // Blob transactions may be present after the Cancun fork. + // In production, + // - the header is verified against the max in eip4844.go:VerifyEIP4844Header + // - the block body is verified against the header in block_validator.go:ValidateBody + // Here, we just do this shortcut smaller fix, since state tests do not + // utilize those codepaths + if len(msg.BlobHashes)*params.BlobTxBlobGasPerBlob > params.MaxBlobGasPerBlock { + return state, common.Hash{}, errors.New("blob gas exceeds maximum") + } } // Try to recover tx with current signer @@ -272,13 +291,10 @@ func (t *StateTest) RunNoVerify(subtest StateSubtest, vmconfig vm.Config, snapsh var ttx types.Transaction err := ttx.UnmarshalBinary(post.TxBytes) if err != nil { - triedb.Close() - return nil, nil, nil, common.Hash{}, err + return state, common.Hash{}, err } - if _, err := types.Sender(types.LatestSigner(config), &ttx); err != nil { - triedb.Close() - return nil, nil, nil, common.Hash{}, err + return state, common.Hash{}, err } } @@ -290,60 +306,29 @@ func (t *StateTest) RunNoVerify(subtest StateSubtest, vmconfig vm.Config, snapsh if config.IsSubnetEVM(0) && t.json.Env.Random != nil { context.Difficulty = big.NewInt(0) } - evm := vm.NewEVM(context, txContext, statedb, config, vmconfig) + if config.IsCancun(new(big.Int), block.Time()) && t.json.Env.ExcessBlobGas != nil { + context.BlobBaseFee = eip4844.CalcBlobFee(*t.json.Env.ExcessBlobGas) + } + evm := vm.NewEVM(context, txContext, state.StateDB, config, vmconfig) // Execute the message. - snapshot := statedb.Snapshot() + snapshot := state.StateDB.Snapshot() gaspool := new(core.GasPool) gaspool.AddGas(block.GasLimit()) _, err = core.ApplyMessage(evm, msg, gaspool) if err != nil { - statedb.RevertToSnapshot(snapshot) + state.StateDB.RevertToSnapshot(snapshot) } // Add 0-value mining reward. This only makes a difference in the cases // where // - the coinbase self-destructed, or // - there are only 'bad' transactions, which aren't executed. In those cases, // the coinbase gets no txfee, so isn't created, and thus needs to be touched - statedb.AddBalance(block.Coinbase(), new(big.Int)) - // Commit block - root, _ := statedb.Commit(block.NumberU64(), config.IsEIP158(block.Number()), false) - return triedb, snaps, statedb, root, err -} + state.StateDB.AddBalance(block.Coinbase(), new(uint256.Int)) -func MakePreState(db ethdb.Database, accounts core.GenesisAlloc, snapshotter bool, scheme string) (*trie.Database, *snapshot.Tree, *state.StateDB) { - tconf := &trie.Config{Preimages: true} - if scheme == rawdb.HashScheme { - tconf.HashDB = hashdb.Defaults - } else { - tconf.PathDB = pathdb.Defaults - } - triedb := trie.NewDatabase(db, tconf) - sdb := state.NewDatabaseWithNodeDB(db, triedb) - statedb, _ := state.New(types.EmptyRootHash, sdb, nil) - for addr, a := range accounts { - statedb.SetCode(addr, a.Code) - statedb.SetNonce(addr, a.Nonce) - statedb.SetBalance(addr, a.Balance) - for k, v := range a.Storage { - statedb.SetState(addr, k, v) - } - } - // Commit and re-open to start with a clean state. - root, _ := statedb.Commit(0, false, false) - - var snaps *snapshot.Tree - if snapshotter { - snapconfig := snapshot.Config{ - CacheSize: 1, - NoBuild: false, - AsyncBuild: false, - SkipVerify: true, - } - snaps, _ = snapshot.New(snapconfig, db, triedb, common.Hash{}, root) - } - statedb, _ = state.New(root, sdb, snaps) - return triedb, snaps, statedb + // Commit state mutations into database. + root, _ = state.StateDB.Commit(block.NumberU64(), config.IsEIP158(block.Number()), false) + return state, root, err } func (t *StateTest) genesis(config *params.ChainConfig) *core.Genesis { @@ -462,3 +447,61 @@ func rlpHash(x interface{}) (h common.Hash) { func vmTestBlockHash(n uint64) common.Hash { return common.BytesToHash(crypto.Keccak256([]byte(big.NewInt(int64(n)).String()))) } + +// StateTestState groups all the state database objects together for use in tests. +type StateTestState struct { + StateDB *state.StateDB + TrieDB *triedb.Database + Snapshots *snapshot.Tree +} + +// MakePreState creates a state containing the given allocation. +func MakePreState(db ethdb.Database, accounts types.GenesisAlloc, snapshotter bool, scheme string) StateTestState { + tconf := &triedb.Config{Preimages: true} + if scheme == rawdb.HashScheme { + tconf.HashDB = hashdb.Defaults + } else { + tconf.PathDB = pathdb.Defaults + } + triedb := triedb.NewDatabase(db, tconf) + sdb := state.NewDatabaseWithNodeDB(db, triedb) + statedb, _ := state.New(types.EmptyRootHash, sdb, nil) + for addr, a := range accounts { + statedb.SetCode(addr, a.Code) + statedb.SetNonce(addr, a.Nonce) + statedb.SetBalance(addr, uint256.MustFromBig(a.Balance)) + for k, v := range a.Storage { + statedb.SetState(addr, k, v) + } + } + // Commit and re-open to start with a clean state. + root, _ := statedb.Commit(0, false, false) + + // If snapshot is requested, initialize the snapshotter and use it in state. + var snaps *snapshot.Tree + if snapshotter { + snapconfig := snapshot.Config{ + CacheSize: 1, + NoBuild: false, + AsyncBuild: false, + SkipVerify: true, + } + snaps, _ = snapshot.New(snapconfig, db, triedb, common.Hash{}, root) + } + statedb, _ = state.New(root, sdb, snaps) + return StateTestState{statedb, triedb, snaps} +} + +// Close should be called when the state is no longer needed, ie. after running the test. +func (st *StateTestState) Close() { + if st.TrieDB != nil { + st.TrieDB.Close() + st.TrieDB = nil + } + if st.Snapshots != nil { + // Need to call Disable here to quit the snapshot generator goroutine. + st.Snapshots.AbortGeneration() + st.Snapshots.Release() + st.Snapshots = nil + } +} diff --git a/trie/committer.go b/trie/committer.go index 1ce9ccf33d..97d7ff6f6f 100644 --- a/trie/committer.go +++ b/trie/committer.go @@ -164,12 +164,12 @@ func (c *committer) store(path []byte, n node) node { return hash } -// mptResolver the children resolver in merkle-patricia-tree. -type mptResolver struct{} +// MerkleResolver the children resolver in merkle-patricia-tree. +type MerkleResolver struct{} // ForEach implements childResolver, decodes the provided node and // traverses the children inside. -func (resolver mptResolver) ForEach(node []byte, onChild func(common.Hash)) { +func (resolver MerkleResolver) ForEach(node []byte, onChild func(common.Hash)) { forGatherChildren(mustDecodeNodeUnsafe(nil, node), onChild) } diff --git a/trie/database_test.go b/trie/database_test.go index fc5598df6a..d844eb4a37 100644 --- a/trie/database_test.go +++ b/trie/database_test.go @@ -28,23 +28,135 @@ package trie import ( "github.com/ava-labs/subnet-evm/core/rawdb" - "github.com/ava-labs/subnet-evm/trie/triedb/hashdb" - "github.com/ava-labs/subnet-evm/trie/triedb/pathdb" + "github.com/ava-labs/subnet-evm/core/types" + "github.com/ava-labs/subnet-evm/trie/trienode" + "github.com/ava-labs/subnet-evm/triedb/database" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/ethdb" ) -// newTestDatabase initializes the trie database with specified scheme. -func newTestDatabase(diskdb ethdb.Database, scheme string) *Database { - config := &Config{Preimages: false} - if scheme == rawdb.HashScheme { - config.HashDB = &hashdb.Config{ - CleanCacheSize: 0, - } // disable clean cache - } else { - config.PathDB = &pathdb.Config{ - CleanCacheSize: 0, - DirtyCacheSize: 0, - } // disable clean/dirty cache - } - return NewDatabase(diskdb, config) +// testReader implements database.Reader interface, providing function to +// access trie nodes. +type testReader struct { + db ethdb.Database + scheme string + nodes []*trienode.MergedNodeSet // sorted from new to old +} + +// Node implements database.Reader interface, retrieving trie node with +// all available cached layers. +func (r *testReader) Node(owner common.Hash, path []byte, hash common.Hash) ([]byte, error) { + // Check the node presence with the cached layer, from latest to oldest. + for _, nodes := range r.nodes { + if _, ok := nodes.Sets[owner]; !ok { + continue + } + n, ok := nodes.Sets[owner].Nodes[string(path)] + if !ok { + continue + } + if n.IsDeleted() || n.Hash != hash { + return nil, &MissingNodeError{Owner: owner, Path: path, NodeHash: hash} + } + return n.Blob, nil + } + // Check the node presence in database. + return rawdb.ReadTrieNode(r.db, owner, path, hash, r.scheme), nil +} + +// testDb implements database.Database interface, using for testing purpose. +type testDb struct { + disk ethdb.Database + root common.Hash + scheme string + nodes map[common.Hash]*trienode.MergedNodeSet + parents map[common.Hash]common.Hash +} + +func newTestDatabase(diskdb ethdb.Database, scheme string) *testDb { + return &testDb{ + disk: diskdb, + root: types.EmptyRootHash, + scheme: scheme, + nodes: make(map[common.Hash]*trienode.MergedNodeSet), + parents: make(map[common.Hash]common.Hash), + } +} + +func (db *testDb) Reader(stateRoot common.Hash) (database.Reader, error) { + nodes, _ := db.dirties(stateRoot, true) + return &testReader{db: db.disk, scheme: db.scheme, nodes: nodes}, nil +} + +func (db *testDb) Preimage(hash common.Hash) []byte { + return rawdb.ReadPreimage(db.disk, hash) +} + +func (db *testDb) InsertPreimage(preimages map[common.Hash][]byte) { + rawdb.WritePreimages(db.disk, preimages) +} + +func (db *testDb) Scheme() string { return db.scheme } + +func (db *testDb) Update(root common.Hash, parent common.Hash, nodes *trienode.MergedNodeSet) error { + if root == parent { + return nil + } + if _, ok := db.nodes[root]; ok { + return nil + } + db.parents[root] = parent + db.nodes[root] = nodes + return nil +} + +func (db *testDb) dirties(root common.Hash, topToBottom bool) ([]*trienode.MergedNodeSet, []common.Hash) { + var ( + pending []*trienode.MergedNodeSet + roots []common.Hash + ) + for { + if root == db.root { + break + } + nodes, ok := db.nodes[root] + if !ok { + break + } + if topToBottom { + pending = append(pending, nodes) + roots = append(roots, root) + } else { + pending = append([]*trienode.MergedNodeSet{nodes}, pending...) + roots = append([]common.Hash{root}, roots...) + } + root = db.parents[root] + } + return pending, roots +} + +func (db *testDb) Commit(root common.Hash) error { + if root == db.root { + return nil + } + pending, roots := db.dirties(root, false) + for i, nodes := range pending { + for owner, set := range nodes.Sets { + if owner == (common.Hash{}) { + continue + } + set.ForEachWithOrder(func(path string, n *trienode.Node) { + rawdb.WriteTrieNode(db.disk, owner, []byte(path), n.Hash, n.Blob, db.scheme) + }) + } + nodes.Sets[common.Hash{}].ForEachWithOrder(func(path string, n *trienode.Node) { + rawdb.WriteTrieNode(db.disk, common.Hash{}, []byte(path), n.Hash, n.Blob, db.scheme) + }) + db.root = roots[i] + } + for _, root := range roots { + delete(db.nodes, root) + delete(db.parents, root) + } + return nil } diff --git a/trie/iterator_test.go b/trie/iterator_test.go index 2ba4c33e21..a329e27f6e 100644 --- a/trie/iterator_test.go +++ b/trie/iterator_test.go @@ -40,7 +40,7 @@ import ( ) func TestEmptyIterator(t *testing.T) { - trie := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase(), nil)) + trie := NewEmpty(newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme)) iter := trie.MustNodeIterator(nil) seen := make(map[string]struct{}) @@ -53,7 +53,7 @@ func TestEmptyIterator(t *testing.T) { } func TestIterator(t *testing.T) { - db := NewDatabase(rawdb.NewMemoryDatabase(), nil) + db := newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme) trie := NewEmpty(db) vals := []struct{ k, v string }{ {"do", "verb"}, @@ -70,7 +70,7 @@ func TestIterator(t *testing.T) { trie.MustUpdate([]byte(val.k), []byte(val.v)) } root, nodes, _ := trie.Commit(false) - db.Update(root, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodes), nil) + db.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes)) trie, _ = New(TrieID(root), db) found := make(map[string]string) @@ -96,7 +96,7 @@ func (k *kv) cmp(other *kv) int { } func TestIteratorLargeData(t *testing.T) { - trie := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase(), nil)) + trie := NewEmpty(newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme)) vals := make(map[string]*kv) for i := byte(0); i < 255; i++ { @@ -215,7 +215,7 @@ var testdata2 = []kvs{ } func TestIteratorSeek(t *testing.T) { - trie := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase(), nil)) + trie := NewEmpty(newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme)) for _, val := range testdata1 { trie.MustUpdate([]byte(val.k), []byte(val.v)) } @@ -256,22 +256,22 @@ func checkIteratorOrder(want []kvs, it *Iterator) error { } func TestDifferenceIterator(t *testing.T) { - dba := NewDatabase(rawdb.NewMemoryDatabase(), nil) + dba := newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme) triea := NewEmpty(dba) for _, val := range testdata1 { triea.MustUpdate([]byte(val.k), []byte(val.v)) } rootA, nodesA, _ := triea.Commit(false) - dba.Update(rootA, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodesA), nil) + dba.Update(rootA, types.EmptyRootHash, trienode.NewWithNodeSet(nodesA)) triea, _ = New(TrieID(rootA), dba) - dbb := NewDatabase(rawdb.NewMemoryDatabase(), nil) + dbb := newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme) trieb := NewEmpty(dbb) for _, val := range testdata2 { trieb.MustUpdate([]byte(val.k), []byte(val.v)) } rootB, nodesB, _ := trieb.Commit(false) - dbb.Update(rootB, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodesB), nil) + dbb.Update(rootB, types.EmptyRootHash, trienode.NewWithNodeSet(nodesB)) trieb, _ = New(TrieID(rootB), dbb) found := make(map[string]string) @@ -298,22 +298,22 @@ func TestDifferenceIterator(t *testing.T) { } func TestUnionIterator(t *testing.T) { - dba := NewDatabase(rawdb.NewMemoryDatabase(), nil) + dba := newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme) triea := NewEmpty(dba) for _, val := range testdata1 { triea.MustUpdate([]byte(val.k), []byte(val.v)) } rootA, nodesA, _ := triea.Commit(false) - dba.Update(rootA, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodesA), nil) + dba.Update(rootA, types.EmptyRootHash, trienode.NewWithNodeSet(nodesA)) triea, _ = New(TrieID(rootA), dba) - dbb := NewDatabase(rawdb.NewMemoryDatabase(), nil) + dbb := newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme) trieb := NewEmpty(dbb) for _, val := range testdata2 { trieb.MustUpdate([]byte(val.k), []byte(val.v)) } rootB, nodesB, _ := trieb.Commit(false) - dbb.Update(rootB, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodesB), nil) + dbb.Update(rootB, types.EmptyRootHash, trienode.NewWithNodeSet(nodesB)) trieb, _ = New(TrieID(rootB), dbb) di, _ := NewUnionIterator([]NodeIterator{triea.MustNodeIterator(nil), trieb.MustNodeIterator(nil)}) @@ -351,7 +351,8 @@ func TestUnionIterator(t *testing.T) { } func TestIteratorNoDups(t *testing.T) { - tr := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase(), nil)) + db := newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme) + tr := NewEmpty(db) for _, val := range testdata1 { tr.MustUpdate([]byte(val.k), []byte(val.v)) } @@ -375,9 +376,9 @@ func testIteratorContinueAfterError(t *testing.T, memonly bool, scheme string) { tr.MustUpdate([]byte(val.k), []byte(val.v)) } root, nodes, _ := tr.Commit(false) - tdb.Update(root, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodes), nil) + tdb.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes)) if !memonly { - tdb.Commit(root, false) + tdb.Commit(root) } tr, _ = New(TrieID(root), tdb) wantNodeCount := checkIteratorNoDups(t, tr.MustNodeIterator(nil), nil) @@ -491,9 +492,9 @@ func testIteratorContinueAfterSeekError(t *testing.T, memonly bool, scheme strin break } } - triedb.Update(root, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodes), nil) + triedb.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes)) if !memonly { - triedb.Commit(root, false) + triedb.Commit(root) } var ( barNodeBlob []byte @@ -565,8 +566,8 @@ func testIteratorNodeBlob(t *testing.T, scheme string) { trie.MustUpdate([]byte(val.k), []byte(val.v)) } root, nodes, _ := trie.Commit(false) - triedb.Update(root, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodes), nil) - triedb.Commit(root, false) + triedb.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes)) + triedb.Commit(root) var found = make(map[common.Hash][]byte) trie, _ = New(TrieID(root), triedb) diff --git a/trie/proof.go b/trie/proof.go index df78f1926e..4bedbf0bc6 100644 --- a/trie/proof.go +++ b/trie/proof.go @@ -399,7 +399,7 @@ func unset(parent node, child node, key []byte, pos int, removeLeft bool) error } else { if bytes.Compare(cld.Key, key[pos:]) > 0 { // The key of fork shortnode is greater than the - // path(it belongs to the range), unset the entrie + // path(it belongs to the range), unset the entries // branch. The parent must be a fullnode. fn := parent.(*fullNode) fn.Children[key[pos-1]] = nil diff --git a/trie/proof_test.go b/trie/proof_test.go index 3191174667..91a5c64ac0 100644 --- a/trie/proof_test.go +++ b/trie/proof_test.go @@ -104,7 +104,7 @@ func TestProof(t *testing.T) { } func TestOneElementProof(t *testing.T) { - trie := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase(), nil)) + trie := NewEmpty(newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme)) updateString(trie, "k", "v") for i, prover := range makeProvers(trie) { proof := prover([]byte("k")) @@ -155,7 +155,7 @@ func TestBadProof(t *testing.T) { // Tests that missing keys can also be proven. The test explicitly uses a single // entry trie and checks for missing keys both before and after the single entry. func TestMissingKeyProof(t *testing.T) { - trie := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase(), nil)) + trie := NewEmpty(newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme)) updateString(trie, "k", "v") for i, key := range []string{"a", "j", "l", "z"} { @@ -353,7 +353,7 @@ func TestOneElementRangeProof(t *testing.T) { } // Test the mini trie with only a single element. - tinyTrie := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase(), nil)) + tinyTrie := NewEmpty(newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme)) entry := &kv{randBytes(32), randBytes(20), false} tinyTrie.MustUpdate(entry.k, entry.v) @@ -424,7 +424,7 @@ func TestAllElementsProof(t *testing.T) { // TestSingleSideRangeProof tests the range starts from zero. func TestSingleSideRangeProof(t *testing.T) { for i := 0; i < 64; i++ { - trie := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase(), nil)) + trie := NewEmpty(newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme)) var entries []*kv for i := 0; i < 4096; i++ { value := &kv{randBytes(32), randBytes(20), false} @@ -530,7 +530,7 @@ func TestBadRangeProof(t *testing.T) { // TestGappedRangeProof focuses on the small trie with embedded nodes. // If the gapped node is embedded in the trie, it should be detected too. func TestGappedRangeProof(t *testing.T) { - trie := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase(), nil)) + trie := NewEmpty(newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme)) var entries []*kv // Sorted entries for i := byte(0); i < 10; i++ { value := &kv{common.LeftPadBytes([]byte{i}, 32), []byte{i}, false} @@ -602,7 +602,7 @@ func TestSameSideProofs(t *testing.T) { } func TestHasRightElement(t *testing.T) { - trie := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase(), nil)) + trie := NewEmpty(newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme)) var entries []*kv for i := 0; i < 4096; i++ { value := &kv{randBytes(32), randBytes(20), false} @@ -944,7 +944,7 @@ func benchmarkVerifyRangeNoProof(b *testing.B, size int) { } func randomTrie(n int) (*Trie, map[string]*kv) { - trie := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase(), nil)) + trie := NewEmpty(newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme)) vals := make(map[string]*kv) for i := byte(0); i < 100; i++ { value := &kv{common.LeftPadBytes([]byte{i}, 32), []byte{i}, false} @@ -963,7 +963,7 @@ func randomTrie(n int) (*Trie, map[string]*kv) { } func nonRandomTrie(n int) (*Trie, map[string]*kv) { - trie := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase(), nil)) + trie := NewEmpty(newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme)) vals := make(map[string]*kv) max := uint64(0xffffffffffffffff) for i := uint64(0); i < uint64(n); i++ { @@ -988,7 +988,7 @@ func TestRangeProofKeysWithSharedPrefix(t *testing.T) { common.Hex2Bytes("02"), common.Hex2Bytes("03"), } - trie := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase(), nil)) + trie := NewEmpty(newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme)) for i, key := range keys { trie.MustUpdate(key, vals[i]) } diff --git a/trie/secure_trie.go b/trie/secure_trie.go index ef29bb8404..bcf983e0ee 100644 --- a/trie/secure_trie.go +++ b/trie/secure_trie.go @@ -29,6 +29,7 @@ package trie import ( "github.com/ava-labs/subnet-evm/core/types" "github.com/ava-labs/subnet-evm/trie/trienode" + "github.com/ava-labs/subnet-evm/triedb/database" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/rlp" ) @@ -39,7 +40,7 @@ type SecureTrie = StateTrie // NewSecure creates a new StateTrie. // Deprecated: use NewStateTrie. -func NewSecure(stateRoot common.Hash, owner common.Hash, root common.Hash, db *Database) (*SecureTrie, error) { +func NewSecure(stateRoot common.Hash, owner common.Hash, root common.Hash, db database.Database) (*SecureTrie, error) { id := &ID{ StateRoot: stateRoot, Owner: owner, @@ -60,7 +61,7 @@ func NewSecure(stateRoot common.Hash, owner common.Hash, root common.Hash, db *D // StateTrie is not safe for concurrent use. type StateTrie struct { trie Trie - preimages *preimageStore + db database.Database hashKeyBuf [common.HashLength]byte secKeyCache map[string][]byte secKeyCacheOwner *StateTrie // Pointer to self, replace the key cache on mismatch @@ -71,7 +72,7 @@ type StateTrie struct { // If root is the zero hash or the sha3 hash of an empty string, the // trie is initially empty. Otherwise, New will panic if db is nil // and returns MissingNodeError if the root node cannot be found. -func NewStateTrie(id *ID, db *Database) (*StateTrie, error) { +func NewStateTrie(id *ID, db database.Database) (*StateTrie, error) { if db == nil { panic("trie.NewStateTrie called without a database") } @@ -79,7 +80,7 @@ func NewStateTrie(id *ID, db *Database) (*StateTrie, error) { if err != nil { return nil, err } - return &StateTrie{trie: *trie, preimages: db.preimages}, nil + return &StateTrie{trie: *trie, db: db}, nil } // MustGet returns the value for key stored in the trie. @@ -220,10 +221,7 @@ func (t *StateTrie) GetKey(shaKey []byte) []byte { if key, ok := t.getSecKeyCache()[string(shaKey)]; ok { return key } - if t.preimages == nil { - return nil - } - return t.preimages.preimage(common.BytesToHash(shaKey)) + return t.db.Preimage(common.BytesToHash(shaKey)) } // Commit collects all dirty nodes in the trie and replaces them with the @@ -236,13 +234,11 @@ func (t *StateTrie) GetKey(shaKey []byte) []byte { func (t *StateTrie) Commit(collectLeaf bool) (common.Hash, *trienode.NodeSet, error) { // Write all the pre-images to the actual disk database if len(t.getSecKeyCache()) > 0 { - if t.preimages != nil { - preimages := make(map[common.Hash][]byte) - for hk, key := range t.secKeyCache { - preimages[common.BytesToHash([]byte(hk))] = key - } - t.preimages.insertPreimage(preimages) + preimages := make(map[common.Hash][]byte) + for hk, key := range t.secKeyCache { + preimages[common.BytesToHash([]byte(hk))] = key } + t.db.InsertPreimage(preimages) t.secKeyCache = make(map[string][]byte) } // Commit the trie and return its modified nodeset. @@ -259,7 +255,7 @@ func (t *StateTrie) Hash() common.Hash { func (t *StateTrie) Copy() *StateTrie { return &StateTrie{ trie: *t.trie.Copy(), - preimages: t.preimages, + db: t.db, secKeyCache: t.secKeyCache, } } diff --git a/trie/secure_trie_test.go b/trie/secure_trie_test.go index fab78d19bd..137ecf1074 100644 --- a/trie/secure_trie_test.go +++ b/trie/secure_trie_test.go @@ -41,14 +41,14 @@ import ( ) func newEmptySecure() *StateTrie { - trie, _ := NewStateTrie(TrieID(types.EmptyRootHash), NewDatabase(rawdb.NewMemoryDatabase(), nil)) + trie, _ := NewStateTrie(TrieID(types.EmptyRootHash), newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme)) return trie } // makeTestStateTrie creates a large enough secure trie for testing. -func makeTestStateTrie() (*Database, *StateTrie, map[string][]byte) { +func makeTestStateTrie() (*testDb, *StateTrie, map[string][]byte) { // Create an empty trie - triedb := NewDatabase(rawdb.NewMemoryDatabase(), nil) + triedb := newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme) trie, _ := NewStateTrie(TrieID(types.EmptyRootHash), triedb) // Fill it with some arbitrary data @@ -71,7 +71,7 @@ func makeTestStateTrie() (*Database, *StateTrie, map[string][]byte) { } } root, nodes, _ := trie.Commit(false) - if err := triedb.Update(root, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodes), nil); err != nil { + if err := triedb.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes)); err != nil { panic(fmt.Errorf("failed to commit db %v", err)) } // Re-create the trie based on the new state diff --git a/trie/stacktrie_fuzzer_test.go b/trie/stacktrie_fuzzer_test.go index e9014df0d9..379e18026d 100644 --- a/trie/stacktrie_fuzzer_test.go +++ b/trie/stacktrie_fuzzer_test.go @@ -42,10 +42,10 @@ func fuzz(data []byte, debugging bool) { var ( input = bytes.NewReader(data) spongeA = &spongeDb{sponge: sha3.NewLegacyKeccak256()} - dbA = NewDatabase(rawdb.NewDatabase(spongeA), nil) + dbA = newTestDatabase(rawdb.NewDatabase(spongeA), rawdb.HashScheme) trieA = NewEmpty(dbA) spongeB = &spongeDb{sponge: sha3.NewLegacyKeccak256()} - dbB = NewDatabase(rawdb.NewDatabase(spongeB), nil) + dbB = newTestDatabase(rawdb.NewDatabase(spongeB), rawdb.HashScheme) options = NewStackTrieOptions().WithWriter(func(path []byte, hash common.Hash, blob []byte) { rawdb.WriteTrieNode(spongeB, common.Hash{}, path, hash, blob, dbB.Scheme()) @@ -87,10 +87,10 @@ func fuzz(data []byte, debugging bool) { panic(err) } if nodes != nil { - dbA.Update(rootA, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodes), nil) + dbA.Update(rootA, types.EmptyRootHash, trienode.NewWithNodeSet(nodes)) } // Flush memdb -> disk (sponge) - dbA.Commit(rootA, false) + dbA.Commit(rootA) // Stacktrie requires sorted insertion slices.SortFunc(vals, (*kv).cmp) diff --git a/trie/stacktrie_test.go b/trie/stacktrie_test.go index 3d7bbc79f9..f9db27ef0e 100644 --- a/trie/stacktrie_test.go +++ b/trie/stacktrie_test.go @@ -233,7 +233,7 @@ func TestStackTrieInsertAndHash(t *testing.T) { func TestSizeBug(t *testing.T) { st := NewStackTrie(nil) - nt := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase(), nil)) + nt := NewEmpty(newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme)) leaf := common.FromHex("290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563") value := common.FromHex("94cf40d0d2b44f2b66e07cace1372ca42b73cf21a3") @@ -248,7 +248,7 @@ func TestSizeBug(t *testing.T) { func TestEmptyBug(t *testing.T) { st := NewStackTrie(nil) - nt := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase(), nil)) + nt := NewEmpty(newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme)) //leaf := common.FromHex("290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563") //value := common.FromHex("94cf40d0d2b44f2b66e07cace1372ca42b73cf21a3") @@ -274,7 +274,7 @@ func TestEmptyBug(t *testing.T) { func TestValLength56(t *testing.T) { st := NewStackTrie(nil) - nt := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase(), nil)) + nt := NewEmpty(newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme)) //leaf := common.FromHex("290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563") //value := common.FromHex("94cf40d0d2b44f2b66e07cace1372ca42b73cf21a3") @@ -299,7 +299,7 @@ func TestValLength56(t *testing.T) { // which causes a lot of node-within-node. This case was found via fuzzing. func TestUpdateSmallNodes(t *testing.T) { st := NewStackTrie(nil) - nt := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase(), nil)) + nt := NewEmpty(newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme)) kvs := []struct { K string V string @@ -327,7 +327,7 @@ func TestUpdateSmallNodes(t *testing.T) { func TestUpdateVariableKeys(t *testing.T) { t.SkipNow() st := NewStackTrie(nil) - nt := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase(), nil)) + nt := NewEmpty(newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme)) kvs := []struct { K string V string diff --git a/trie/sync_test.go b/trie/sync_test.go index e899ba5458..e0838a2059 100644 --- a/trie/sync_test.go +++ b/trie/sync_test.go @@ -37,7 +37,7 @@ import ( ) // makeTestTrie create a sample test trie to test node-wise reconstruction. -func makeTestTrie(scheme string) (ethdb.Database, *Database, *StateTrie, map[string][]byte) { +func makeTestTrie(scheme string) (ethdb.Database, *testDb, *StateTrie, map[string][]byte) { // Create an empty trie db := rawdb.NewMemoryDatabase() triedb := newTestDatabase(db, scheme) @@ -63,10 +63,10 @@ func makeTestTrie(scheme string) (ethdb.Database, *Database, *StateTrie, map[str } } root, nodes, _ := trie.Commit(false) - if err := triedb.Update(root, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodes), nil); err != nil { + if err := triedb.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes)); err != nil { panic(fmt.Errorf("failed to commit db %v", err)) } - if err := triedb.Commit(root, false); err != nil { + if err := triedb.Commit(root); err != nil { panic(err) } // Re-create the trie based on the new state diff --git a/trie/tracer_test.go b/trie/tracer_test.go index f3406a0250..b3735dd1ff 100644 --- a/trie/tracer_test.go +++ b/trie/tracer_test.go @@ -61,7 +61,7 @@ func TestTrieTracer(t *testing.T) { // Tests if the trie diffs are tracked correctly. Tracer should capture // all non-leaf dirty nodes, no matter the node is embedded or not. func testTrieTracer(t *testing.T, vals []struct{ k, v string }) { - db := NewDatabase(rawdb.NewMemoryDatabase(), nil) + db := newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme) trie := NewEmpty(db) // Determine all new nodes are tracked @@ -71,7 +71,7 @@ func testTrieTracer(t *testing.T, vals []struct{ k, v string }) { insertSet := copySet(trie.tracer.inserts) // copy before commit deleteSet := copySet(trie.tracer.deletes) // copy before commit root, nodes, _ := trie.Commit(false) - db.Update(root, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodes), nil) + db.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes)) seen := setKeys(iterNodes(db, root)) if !compareSet(insertSet, seen) { @@ -104,7 +104,8 @@ func TestTrieTracerNoop(t *testing.T) { } func testTrieTracerNoop(t *testing.T, vals []struct{ k, v string }) { - trie := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase(), nil)) + db := newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme) + trie := NewEmpty(db) for _, val := range vals { trie.MustUpdate([]byte(val.k), []byte(val.v)) } @@ -128,7 +129,7 @@ func TestAccessList(t *testing.T) { func testAccessList(t *testing.T, vals []struct{ k, v string }) { var ( - db = NewDatabase(rawdb.NewMemoryDatabase(), nil) + db = newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme) trie = NewEmpty(db) orig = trie.Copy() ) @@ -137,7 +138,7 @@ func testAccessList(t *testing.T, vals []struct{ k, v string }) { trie.MustUpdate([]byte(val.k), []byte(val.v)) } root, nodes, _ := trie.Commit(false) - db.Update(root, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodes), nil) + db.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes)) trie, _ = New(TrieID(root), db) if err := verifyAccessList(orig, trie, nodes); err != nil { @@ -152,7 +153,7 @@ func testAccessList(t *testing.T, vals []struct{ k, v string }) { trie.MustUpdate([]byte(val.k), randBytes(32)) } root, nodes, _ = trie.Commit(false) - db.Update(root, parent, 0, trienode.NewWithNodeSet(nodes), nil) + db.Update(root, parent, trienode.NewWithNodeSet(nodes)) trie, _ = New(TrieID(root), db) if err := verifyAccessList(orig, trie, nodes); err != nil { @@ -170,7 +171,7 @@ func testAccessList(t *testing.T, vals []struct{ k, v string }) { trie.MustUpdate(key, randBytes(32)) } root, nodes, _ = trie.Commit(false) - db.Update(root, parent, 0, trienode.NewWithNodeSet(nodes), nil) + db.Update(root, parent, trienode.NewWithNodeSet(nodes)) trie, _ = New(TrieID(root), db) if err := verifyAccessList(orig, trie, nodes); err != nil { @@ -185,7 +186,7 @@ func testAccessList(t *testing.T, vals []struct{ k, v string }) { trie.MustUpdate([]byte(key), nil) } root, nodes, _ = trie.Commit(false) - db.Update(root, parent, 0, trienode.NewWithNodeSet(nodes), nil) + db.Update(root, parent, trienode.NewWithNodeSet(nodes)) trie, _ = New(TrieID(root), db) if err := verifyAccessList(orig, trie, nodes); err != nil { @@ -200,7 +201,7 @@ func testAccessList(t *testing.T, vals []struct{ k, v string }) { trie.MustUpdate([]byte(val.k), nil) } root, nodes, _ = trie.Commit(false) - db.Update(root, parent, 0, trienode.NewWithNodeSet(nodes), nil) + db.Update(root, parent, trienode.NewWithNodeSet(nodes)) trie, _ = New(TrieID(root), db) if err := verifyAccessList(orig, trie, nodes); err != nil { @@ -211,7 +212,7 @@ func testAccessList(t *testing.T, vals []struct{ k, v string }) { // Tests origin values won't be tracked in Iterator or Prover func TestAccessListLeak(t *testing.T) { var ( - db = NewDatabase(rawdb.NewMemoryDatabase(), nil) + db = newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme) trie = NewEmpty(db) ) // Create trie from scratch @@ -219,7 +220,7 @@ func TestAccessListLeak(t *testing.T) { trie.MustUpdate([]byte(val.k), []byte(val.v)) } root, nodes, _ := trie.Commit(false) - db.Update(root, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodes), nil) + db.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes)) var cases = []struct { op func(tr *Trie) @@ -262,14 +263,14 @@ func TestAccessListLeak(t *testing.T) { // in its parent due to the smaller size of the original tree node. func TestTinyTree(t *testing.T) { var ( - db = NewDatabase(rawdb.NewMemoryDatabase(), nil) + db = newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme) trie = NewEmpty(db) ) for _, val := range tiny { trie.MustUpdate([]byte(val.k), randBytes(32)) } root, set, _ := trie.Commit(false) - db.Update(root, types.EmptyRootHash, 0, trienode.NewWithNodeSet(set), nil) + db.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(set)) parent := root trie, _ = New(TrieID(root), db) @@ -278,7 +279,7 @@ func TestTinyTree(t *testing.T) { trie.MustUpdate([]byte(val.k), []byte(val.v)) } root, set, _ = trie.Commit(false) - db.Update(root, parent, 0, trienode.NewWithNodeSet(set), nil) + db.Update(root, parent, trienode.NewWithNodeSet(set)) trie, _ = New(TrieID(root), db) if err := verifyAccessList(orig, trie, set); err != nil { @@ -312,7 +313,7 @@ func forNodes(tr *Trie) map[string][]byte { return nodes } -func iterNodes(db *Database, root common.Hash) map[string][]byte { +func iterNodes(db *testDb, root common.Hash) map[string][]byte { tr, _ := New(TrieID(root), db) return forNodes(tr) } diff --git a/trie/trie.go b/trie/trie.go index 168f2b9730..4370f22f20 100644 --- a/trie/trie.go +++ b/trie/trie.go @@ -34,6 +34,7 @@ import ( "github.com/ava-labs/subnet-evm/core/types" "github.com/ava-labs/subnet-evm/trie/trienode" + "github.com/ava-labs/subnet-evm/triedb/database" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/log" ) @@ -89,7 +90,7 @@ func (t *Trie) Copy() *Trie { // zero hash or the sha3 hash of an empty string, then trie is initially // empty, otherwise, the root node must be present in database or returns // a MissingNodeError if not. -func New(id *ID, db *Database) (*Trie, error) { +func New(id *ID, db database.Database) (*Trie, error) { reader, err := newTrieReader(id.StateRoot, id.Owner, db) if err != nil { return nil, err @@ -110,7 +111,7 @@ func New(id *ID, db *Database) (*Trie, error) { } // NewEmpty is a shortcut to create empty tree. It's mostly used in tests. -func NewEmpty(db *Database) *Trie { +func NewEmpty(db database.Database) *Trie { tr, _ := New(TrieID(types.EmptyRootHash), db) return tr } diff --git a/trie/trie_reader.go b/trie/trie_reader.go index b43f8cec29..74c5c2a25c 100644 --- a/trie/trie_reader.go +++ b/trie/trie_reader.go @@ -29,33 +29,21 @@ package trie import ( "github.com/ava-labs/subnet-evm/core/types" "github.com/ava-labs/subnet-evm/trie/triestate" + "github.com/ava-labs/subnet-evm/triedb/database" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/log" ) -// Reader wraps the Node method of a backing trie store. -type Reader interface { - // Node retrieves the trie node blob with the provided trie identifier, node path and - // the corresponding node hash. No error will be returned if the node is not found. - // - // When looking up nodes in the account trie, 'owner' is the zero hash. For contract - // storage trie nodes, 'owner' is the hash of the account address that containing the - // storage. - // - // TODO(rjl493456442): remove the 'hash' parameter, it's redundant in PBSS. - Node(owner common.Hash, path []byte, hash common.Hash) ([]byte, error) -} - // trieReader is a wrapper of the underlying node reader. It's not safe // for concurrent usage. type trieReader struct { owner common.Hash - reader Reader + reader database.Reader banned map[string]struct{} // Marker to prevent node from being accessed, for tests } // newTrieReader initializes the trie reader with the given node reader. -func newTrieReader(stateRoot, owner common.Hash, db *Database) (*trieReader, error) { +func newTrieReader(stateRoot, owner common.Hash, db database.Database) (*trieReader, error) { if stateRoot == (common.Hash{}) || stateRoot == types.EmptyRootHash { if stateRoot == (common.Hash{}) { log.Error("Zero state root hash!") @@ -95,17 +83,22 @@ func (r *trieReader) node(path []byte, hash common.Hash) ([]byte, error) { return blob, nil } -// trieLoader implements triestate.TrieLoader for constructing tries. -type trieLoader struct { - db *Database +// MerkleLoader implements triestate.TrieLoader for constructing tries. +type MerkleLoader struct { + db database.Database +} + +// NewMerkleLoader creates the merkle trie loader. +func NewMerkleLoader(db database.Database) *MerkleLoader { + return &MerkleLoader{db: db} } // OpenTrie opens the main account trie. -func (l *trieLoader) OpenTrie(root common.Hash) (triestate.Trie, error) { +func (l *MerkleLoader) OpenTrie(root common.Hash) (triestate.Trie, error) { return New(TrieID(root), l.db) } // OpenStorageTrie opens the storage trie of an account. -func (l *trieLoader) OpenStorageTrie(stateRoot common.Hash, addrHash, root common.Hash) (triestate.Trie, error) { +func (l *MerkleLoader) OpenStorageTrie(stateRoot common.Hash, addrHash, root common.Hash) (triestate.Trie, error) { return New(StorageTrieID(stateRoot, addrHash, root), l.db) } diff --git a/trie/trie_test.go b/trie/trie_test.go index c714e41de3..edcf805525 100644 --- a/trie/trie_test.go +++ b/trie/trie_test.go @@ -33,9 +33,9 @@ import ( "fmt" "hash" "io" - "math/big" "math/rand" "reflect" + "sort" "testing" "testing/quick" @@ -47,6 +47,7 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/rlp" + "github.com/holiman/uint256" "github.com/stretchr/testify/require" "golang.org/x/crypto/sha3" ) @@ -57,7 +58,7 @@ func init() { } func TestEmptyTrie(t *testing.T) { - trie := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase(), nil)) + trie := NewEmpty(newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme)) res := trie.Hash() exp := types.EmptyRootHash if res != exp { @@ -66,7 +67,7 @@ func TestEmptyTrie(t *testing.T) { } func TestNull(t *testing.T) { - trie := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase(), nil)) + trie := NewEmpty(newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme)) key := make([]byte, 32) value := []byte("test") trie.MustUpdate(key, value) @@ -106,10 +107,10 @@ func testMissingNode(t *testing.T, memonly bool, scheme string) { updateString(trie, "120000", "qwerqwerqwerqwerqwerqwerqwerqwer") updateString(trie, "123456", "asdfasdfasdfasdfasdfasdfasdfasdf") root, nodes, _ := trie.Commit(false) - triedb.Update(root, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodes), nil) + triedb.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes)) if !memonly { - require.NoError(t, triedb.Commit(root, false)) + require.NoError(t, triedb.Commit(root)) } trie, _ = New(TrieID(root), triedb) @@ -178,7 +179,7 @@ func testMissingNode(t *testing.T, memonly bool, scheme string) { } func TestInsert(t *testing.T) { - trie := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase(), nil)) + trie := NewEmpty(newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme)) updateString(trie, "doe", "reindeer") updateString(trie, "dog", "puppy") @@ -190,7 +191,7 @@ func TestInsert(t *testing.T) { t.Errorf("case 1: exp %x got %x", exp, root) } - trie = NewEmpty(NewDatabase(rawdb.NewMemoryDatabase(), nil)) + trie = NewEmpty(newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme)) updateString(trie, "A", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") exp = common.HexToHash("d23786fb4a010da3ce639d66d5e904a11dbc02746d1ce25029e53290cabf28ab") @@ -201,7 +202,7 @@ func TestInsert(t *testing.T) { } func TestGet(t *testing.T) { - db := NewDatabase(rawdb.NewMemoryDatabase(), nil) + db := newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme) trie := NewEmpty(db) updateString(trie, "doe", "reindeer") updateString(trie, "dog", "puppy") @@ -220,13 +221,14 @@ func TestGet(t *testing.T) { return } root, nodes, _ := trie.Commit(false) - db.Update(root, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodes), nil) + db.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes)) trie, _ = New(TrieID(root), db) } } func TestDelete(t *testing.T) { - trie := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase(), nil)) + db := newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme) + trie := NewEmpty(db) vals := []struct{ k, v string }{ {"do", "verb"}, {"ether", "wookiedoo"}, @@ -253,7 +255,7 @@ func TestDelete(t *testing.T) { } func TestEmptyValues(t *testing.T) { - trie := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase(), nil)) + trie := NewEmpty(newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme)) vals := []struct{ k, v string }{ {"do", "verb"}, @@ -277,7 +279,7 @@ func TestEmptyValues(t *testing.T) { } func TestReplication(t *testing.T) { - db := NewDatabase(rawdb.NewMemoryDatabase(), nil) + db := newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme) trie := NewEmpty(db) vals := []struct{ k, v string }{ {"do", "verb"}, @@ -292,7 +294,7 @@ func TestReplication(t *testing.T) { updateString(trie, val.k, val.v) } root, nodes, _ := trie.Commit(false) - db.Update(root, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodes), nil) + db.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes)) // create a new trie on top of the database and check that lookups work. trie2, err := New(TrieID(root), db) @@ -311,7 +313,7 @@ func TestReplication(t *testing.T) { // recreate the trie after commit if nodes != nil { - db.Update(hash, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodes), nil) + db.Update(hash, types.EmptyRootHash, trienode.NewWithNodeSet(nodes)) } trie2, err = New(TrieID(hash), db) if err != nil { @@ -338,13 +340,13 @@ func TestReplication(t *testing.T) { } func TestLargeValue(t *testing.T) { - trie := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase(), nil)) + trie := NewEmpty(newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme)) trie.MustUpdate([]byte("key1"), []byte{99, 99, 99, 99}) trie.MustUpdate([]byte("key2"), bytes.Repeat([]byte{1}, 32)) trie.Hash() } -// TestRandomCases tests som cases that were found via random fuzzing +// TestRandomCases tests some cases that were found via random fuzzing func TestRandomCases(t *testing.T) { var rt = []randTestStep{ {op: 6, key: common.Hex2Bytes(""), value: common.Hex2Bytes("")}, // step 0 @@ -541,7 +543,7 @@ func runRandTest(rt randTest) error { case opCommit: root, nodes, _ := tr.Commit(true) if nodes != nil { - triedb.Update(root, origin, 0, trienode.NewWithNodeSet(nodes), nil) + triedb.Update(root, origin, trienode.NewWithNodeSet(nodes)) } newtr, err := New(TrieID(root), triedb) if err != nil { @@ -642,7 +644,7 @@ func BenchmarkUpdateLE(b *testing.B) { benchUpdate(b, binary.LittleEndian) } const benchElemCount = 20000 func benchGet(b *testing.B) { - triedb := NewDatabase(rawdb.NewMemoryDatabase(), nil) + triedb := newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme) trie := NewEmpty(triedb) k := make([]byte, 32) for i := 0; i < benchElemCount; i++ { @@ -661,7 +663,7 @@ func benchGet(b *testing.B) { } func benchUpdate(b *testing.B, e binary.ByteOrder) *Trie { - trie := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase(), nil)) + trie := NewEmpty(newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme)) k := make([]byte, 32) b.ReportAllocs() for i := 0; i < b.N; i++ { @@ -693,7 +695,7 @@ func BenchmarkHash(b *testing.B) { // entries, then adding N more. addresses, accounts := makeAccounts(2 * b.N) // Insert the accounts into the trie and hash it - trie := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase(), nil)) + trie := NewEmpty(newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme)) i := 0 for ; i < len(addresses)/2; i++ { trie.MustUpdate(crypto.Keccak256(addresses[i][:]), accounts[i]) @@ -708,13 +710,6 @@ func BenchmarkHash(b *testing.B) { trie.Hash() } -type account struct { - Nonce uint64 - Balance *big.Int - Root common.Hash - CodeHash []byte -} - // Benchmarks the trie Commit following a Hash. Since the trie caches the result of any operation, // we cannot use b.N as the number of hashing rounds, since all rounds apart from // the first one will be NOOP. As such, we'll use b.N as the number of account to @@ -731,7 +726,7 @@ func BenchmarkCommitAfterHash(b *testing.B) { func benchmarkCommitAfterHash(b *testing.B, collectLeaf bool) { // Make the random benchmark deterministic addresses, accounts := makeAccounts(b.N) - trie := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase(), nil)) + trie := NewEmpty(newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme)) for i := 0; i < len(addresses); i++ { trie.MustUpdate(crypto.Keccak256(addresses[i][:]), accounts[i]) } @@ -745,7 +740,7 @@ func benchmarkCommitAfterHash(b *testing.B, collectLeaf bool) { func TestTinyTrie(t *testing.T) { // Create a realistic account trie to hash _, accounts := makeAccounts(5) - trie := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase(), nil)) + trie := NewEmpty(newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme)) trie.MustUpdate(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000001337"), accounts[3]) if exp, root := common.HexToHash("8c6a85a4d9fda98feff88450299e574e5378e32391f75a055d470ac0653f1005"), trie.Hash(); exp != root { t.Errorf("1: got %x, exp %x", root, exp) @@ -758,7 +753,7 @@ func TestTinyTrie(t *testing.T) { if exp, root := common.HexToHash("0608c1d1dc3905fa22204c7a0e43644831c3b6d3def0f274be623a948197e64a"), trie.Hash(); exp != root { t.Errorf("3: got %x, exp %x", root, exp) } - checktr := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase(), nil)) + checktr := NewEmpty(newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme)) it := NewIterator(trie.MustNodeIterator(nil)) for it.Next() { checktr.MustUpdate(it.Key, it.Value) @@ -771,7 +766,7 @@ func TestTinyTrie(t *testing.T) { func TestCommitAfterHash(t *testing.T) { // Create a realistic account trie to hash addresses, accounts := makeAccounts(1000) - trie := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase(), nil)) + trie := NewEmpty(newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme)) for i := 0; i < len(addresses); i++ { trie.MustUpdate(crypto.Keccak256(addresses[i][:]), accounts[i]) } @@ -813,8 +808,8 @@ func makeAccounts(size int) (addresses [][20]byte, accounts [][]byte) { numBytes := random.Uint32() % 33 // [0, 32] bytes balanceBytes := make([]byte, numBytes) random.Read(balanceBytes) - balance := new(big.Int).SetBytes(balanceBytes) - data, _ := rlp.EncodeToBytes(&account{Nonce: nonce, Balance: balance, Root: root, CodeHash: code}) + balance := new(uint256.Int).SetBytes(balanceBytes) + data, _ := rlp.EncodeToBytes(&types.StateAccount{Nonce: nonce, Balance: balance, Root: root, CodeHash: code}) accounts[i] = data } return addresses, accounts @@ -825,6 +820,8 @@ type spongeDb struct { sponge hash.Hash id string journal []string + keys []string + values map[string]string } func (s *spongeDb) Has(key []byte) (bool, error) { panic("implement me") } @@ -848,12 +845,27 @@ func (s *spongeDb) Put(key []byte, value []byte) error { valbrief = valbrief[:8] } s.journal = append(s.journal, fmt.Sprintf("%v: PUT([%x...], [%d bytes] %x...)\n", s.id, keybrief, len(value), valbrief)) - s.sponge.Write(key) - s.sponge.Write(value) + + if s.values == nil { + s.sponge.Write(key) + s.sponge.Write(value) + } else { + s.keys = append(s.keys, string(key)) + s.values[string(key)] = string(value) + } return nil } func (s *spongeDb) NewIterator(prefix []byte, start []byte) ethdb.Iterator { panic("implement me") } +func (s *spongeDb) Flush() { + // Bottom-up, the longest path first + sort.Sort(sort.Reverse(sort.StringSlice(s.keys))) + for _, key := range s.keys { + s.sponge.Write([]byte(key)) + s.sponge.Write([]byte(s.values[key])) + } +} + // spongeBatch is a dummy batch which immediately writes to the underlying spongedb type spongeBatch struct { db *spongeDb @@ -878,14 +890,14 @@ func TestCommitSequence(t *testing.T) { count int expWriteSeqHash []byte }{ - {20, common.FromHex("873c78df73d60e59d4a2bcf3716e8bfe14554549fea2fc147cb54129382a8066")}, - {200, common.FromHex("ba03d891bb15408c940eea5ee3d54d419595102648d02774a0268d892add9c8e")}, - {2000, common.FromHex("f7a184f20df01c94f09537401d11e68d97ad0c00115233107f51b9c287ce60c7")}, + {20, common.FromHex("330b0afae2853d96b9f015791fbe0fb7f239bf65f335f16dfc04b76c7536276d")}, + {200, common.FromHex("5162b3735c06b5d606b043a3ee8adbdbbb408543f4966bca9dcc63da82684eeb")}, + {2000, common.FromHex("4574cd8e6b17f3fe8ad89140d1d0bf4f1bd7a87a8ac3fb623b33550544c77635")}, } { addresses, accounts := makeAccounts(tc.count) // This spongeDb is used to check the sequence of disk-db-writes s := &spongeDb{sponge: sha3.NewLegacyKeccak256()} - db := NewDatabase(rawdb.NewDatabase(s), nil) + db := newTestDatabase(rawdb.NewDatabase(s), rawdb.HashScheme) trie := NewEmpty(db) // Fill the trie with elements for i := 0; i < tc.count; i++ { @@ -893,9 +905,9 @@ func TestCommitSequence(t *testing.T) { } // Flush trie -> database root, nodes, _ := trie.Commit(false) - db.Update(root, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodes), nil) + db.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes)) // Flush memdb -> disk (sponge) - db.Commit(root, false) + db.Commit(root) if got, exp := s.sponge.Sum(nil), tc.expWriteSeqHash; !bytes.Equal(got, exp) { t.Errorf("test %d, disk write sequence wrong:\ngot %x exp %x\n", i, got, exp) } @@ -909,14 +921,14 @@ func TestCommitSequenceRandomBlobs(t *testing.T) { count int expWriteSeqHash []byte }{ - {20, common.FromHex("8e4a01548551d139fa9e833ebc4e66fc1ba40a4b9b7259d80db32cff7b64ebbc")}, - {200, common.FromHex("6869b4e7b95f3097a19ddb30ff735f922b915314047e041614df06958fc50554")}, - {2000, common.FromHex("444200e6f4e2df49f77752f629a96ccf7445d4698c164f962bbd85a0526ef424")}, + {20, common.FromHex("8016650c7a50cf88485fd06cde52d634a89711051107f00d21fae98234f2f13d")}, + {200, common.FromHex("dde92ca9812e068e6982d04b40846dc65a61a9fd4996fc0f55f2fde172a8e13c")}, + {2000, common.FromHex("ab553a7f9aff82e3929c382908e30ef7dd17a332933e92ba3fe873fc661ef382")}, } { prng := rand.New(rand.NewSource(int64(i))) // This spongeDb is used to check the sequence of disk-db-writes s := &spongeDb{sponge: sha3.NewLegacyKeccak256()} - db := NewDatabase(rawdb.NewDatabase(s), nil) + db := newTestDatabase(rawdb.NewDatabase(s), rawdb.HashScheme) trie := NewEmpty(db) // Fill the trie with elements for i := 0; i < tc.count; i++ { @@ -934,9 +946,9 @@ func TestCommitSequenceRandomBlobs(t *testing.T) { } // Flush trie -> database root, nodes, _ := trie.Commit(false) - db.Update(root, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodes), nil) + db.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes)) // Flush memdb -> disk (sponge) - db.Commit(root, false) + db.Commit(root) if got, exp := s.sponge.Sum(nil), tc.expWriteSeqHash; !bytes.Equal(got, exp) { t.Fatalf("test %d, disk write sequence wrong:\ngot %x exp %x\n", i, got, exp) } @@ -947,17 +959,26 @@ func TestCommitSequenceStackTrie(t *testing.T) { for count := 1; count < 200; count++ { prng := rand.New(rand.NewSource(int64(count))) // This spongeDb is used to check the sequence of disk-db-writes - s := &spongeDb{sponge: sha3.NewLegacyKeccak256(), id: "a"} - db := NewDatabase(rawdb.NewDatabase(s), nil) + s := &spongeDb{ + sponge: sha3.NewLegacyKeccak256(), + id: "a", + values: make(map[string]string), + } + db := newTestDatabase(rawdb.NewDatabase(s), rawdb.HashScheme) trie := NewEmpty(db) - // Another sponge is used for the stacktrie commits - stackTrieSponge := &spongeDb{sponge: sha3.NewLegacyKeccak256(), id: "b"} + // Another sponge is used for the stacktrie commits + stackTrieSponge := &spongeDb{ + sponge: sha3.NewLegacyKeccak256(), + id: "b", + values: make(map[string]string), + } options := NewStackTrieOptions() options = options.WithWriter(func(path []byte, hash common.Hash, blob []byte) { rawdb.WriteTrieNode(stackTrieSponge, common.Hash{}, path, hash, blob, db.Scheme()) }) stTrie := NewStackTrie(options) + // Fill the trie with elements for i := 0; i < count; i++ { // For the stack trie, we need to do inserts in proper order @@ -977,13 +998,16 @@ func TestCommitSequenceStackTrie(t *testing.T) { // Flush trie -> database root, nodes, _ := trie.Commit(false) // Flush memdb -> disk (sponge) - db.Update(root, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodes), nil) - db.Commit(root, false) + db.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes)) + db.Commit(root) + s.Flush() + // And flush stacktrie -> disk stRoot := stTrie.Commit() if stRoot != root { t.Fatalf("root wrong, got %x exp %x", stRoot, root) } + stackTrieSponge.Flush() if got, exp := stackTrieSponge.sponge.Sum(nil), s.sponge.Sum(nil); !bytes.Equal(got, exp) { // Show the journal t.Logf("Expected:") @@ -1006,34 +1030,47 @@ func TestCommitSequenceStackTrie(t *testing.T) { // that even a small trie which contains a leaf will have an extension making it // not fit into 32 bytes, rlp-encoded. However, it's still the correct thing to do. func TestCommitSequenceSmallRoot(t *testing.T) { - s := &spongeDb{sponge: sha3.NewLegacyKeccak256(), id: "a"} - db := NewDatabase(rawdb.NewDatabase(s), nil) + s := &spongeDb{ + sponge: sha3.NewLegacyKeccak256(), + id: "a", + values: make(map[string]string), + } + db := newTestDatabase(rawdb.NewDatabase(s), rawdb.HashScheme) trie := NewEmpty(db) - // Another sponge is used for the stacktrie commits - stackTrieSponge := &spongeDb{sponge: sha3.NewLegacyKeccak256(), id: "b"} + // Another sponge is used for the stacktrie commits + stackTrieSponge := &spongeDb{ + sponge: sha3.NewLegacyKeccak256(), + id: "b", + values: make(map[string]string), + } options := NewStackTrieOptions() options = options.WithWriter(func(path []byte, hash common.Hash, blob []byte) { rawdb.WriteTrieNode(stackTrieSponge, common.Hash{}, path, hash, blob, db.Scheme()) }) stTrie := NewStackTrie(options) + // Add a single small-element to the trie(s) key := make([]byte, 5) key[0] = 1 trie.Update(key, []byte{0x1}) stTrie.Update(key, []byte{0x1}) + // Flush trie -> database root, nodes, _ := trie.Commit(false) // Flush memdb -> disk (sponge) - db.Update(root, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodes), nil) - db.Commit(root, false) + db.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes)) + db.Commit(root) + // And flush stacktrie -> disk stRoot := stTrie.Commit() if stRoot != root { t.Fatalf("root wrong, got %x exp %x", stRoot, root) } - t.Logf("root: %x\n", stRoot) + + s.Flush() + stackTrieSponge.Flush() if got, exp := stackTrieSponge.sponge.Sum(nil), s.sponge.Sum(nil); !bytes.Equal(got, exp) { t.Fatalf("test, disk write sequence wrong:\ngot %x exp %x\n", got, exp) } @@ -1084,7 +1121,7 @@ func BenchmarkHashFixedSize(b *testing.B) { func benchmarkHashFixedSize(b *testing.B, addresses [][20]byte, accounts [][]byte) { b.ReportAllocs() - trie := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase(), nil)) + trie := NewEmpty(newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme)) for i := 0; i < len(addresses); i++ { trie.MustUpdate(crypto.Keccak256(addresses[i][:]), accounts[i]) } @@ -1135,7 +1172,7 @@ func BenchmarkCommitAfterHashFixedSize(b *testing.B) { func benchmarkCommitAfterHashFixedSize(b *testing.B, addresses [][20]byte, accounts [][]byte) { b.ReportAllocs() - trie := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase(), nil)) + trie := NewEmpty(newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme)) for i := 0; i < len(addresses); i++ { trie.MustUpdate(crypto.Keccak256(addresses[i][:]), accounts[i]) } @@ -1146,60 +1183,6 @@ func benchmarkCommitAfterHashFixedSize(b *testing.B, addresses [][20]byte, accou b.StopTimer() } -func BenchmarkDerefRootFixedSize(b *testing.B) { - b.Run("10", func(b *testing.B) { - b.StopTimer() - acc, add := makeAccounts(20) - for i := 0; i < b.N; i++ { - benchmarkDerefRootFixedSize(b, acc, add) - } - }) - b.Run("100", func(b *testing.B) { - b.StopTimer() - acc, add := makeAccounts(100) - for i := 0; i < b.N; i++ { - benchmarkDerefRootFixedSize(b, acc, add) - } - }) - - b.Run("1K", func(b *testing.B) { - b.StopTimer() - acc, add := makeAccounts(1000) - for i := 0; i < b.N; i++ { - benchmarkDerefRootFixedSize(b, acc, add) - } - }) - b.Run("10K", func(b *testing.B) { - b.StopTimer() - acc, add := makeAccounts(10000) - for i := 0; i < b.N; i++ { - benchmarkDerefRootFixedSize(b, acc, add) - } - }) - b.Run("100K", func(b *testing.B) { - b.StopTimer() - acc, add := makeAccounts(100000) - for i := 0; i < b.N; i++ { - benchmarkDerefRootFixedSize(b, acc, add) - } - }) -} - -func benchmarkDerefRootFixedSize(b *testing.B, addresses [][20]byte, accounts [][]byte) { - b.ReportAllocs() - triedb := NewDatabase(rawdb.NewMemoryDatabase(), nil) - trie := NewEmpty(triedb) - for i := 0; i < len(addresses); i++ { - trie.MustUpdate(crypto.Keccak256(addresses[i][:]), accounts[i]) - } - h := trie.Hash() - root, nodes, _ := trie.Commit(false) - triedb.Update(root, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodes), nil) - b.StartTimer() - triedb.Dereference(h) - b.StopTimer() -} - func getString(trie *Trie, k string) []byte { return trie.MustGet([]byte(k)) } diff --git a/trie/verkle.go b/trie/verkle.go index a32f8810b2..e6f60a8e85 100644 --- a/trie/verkle.go +++ b/trie/verkle.go @@ -20,11 +20,11 @@ import ( "encoding/binary" "errors" "fmt" - "math/big" "github.com/ava-labs/subnet-evm/core/types" "github.com/ava-labs/subnet-evm/trie/trienode" "github.com/ava-labs/subnet-evm/trie/utils" + "github.com/ava-labs/subnet-evm/triedb/database" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/ethdb" "github.com/gballet/go-verkle" @@ -40,13 +40,12 @@ var ( // interface so that Verkle trees can be reused verbatim. type VerkleTrie struct { root verkle.VerkleNode - db *Database cache *utils.PointCache reader *trieReader } // NewVerkleTrie constructs a verkle tree based on the specified root hash. -func NewVerkleTrie(root common.Hash, db *Database, cache *utils.PointCache) (*VerkleTrie, error) { +func NewVerkleTrie(root common.Hash, db database.Database, cache *utils.PointCache) (*VerkleTrie, error) { reader, err := newTrieReader(root, common.Hash{}, db) if err != nil { return nil, err @@ -65,7 +64,6 @@ func NewVerkleTrie(root common.Hash, db *Database, cache *utils.PointCache) (*Ve } return &VerkleTrie{ root: node, - db: db, cache: cache, reader: reader, }, nil @@ -108,7 +106,7 @@ func (t *VerkleTrie) GetAccount(addr common.Address) (*types.StateAccount, error for i := 0; i < len(balance)/2; i++ { balance[len(balance)-i-1], balance[i] = balance[i], balance[len(balance)-i-1] } - acc.Balance = new(big.Int).SetBytes(balance[:]) + acc.Balance = new(uint256.Int).SetBytes32(balance[:]) // Decode codehash acc.CodeHash = values[utils.CodeKeccakLeafKey] @@ -262,7 +260,6 @@ func (t *VerkleTrie) Prove(key []byte, proofDb ethdb.KeyValueWriter) error { func (t *VerkleTrie) Copy() *VerkleTrie { return &VerkleTrie{ root: t.root.Copy(), - db: t.db, cache: t.cache, reader: t.reader, } diff --git a/trie/verkle_test.go b/trie/verkle_test.go index 559e68d888..e491a4bbb3 100644 --- a/trie/verkle_test.go +++ b/trie/verkle_test.go @@ -18,27 +18,26 @@ package trie import ( "bytes" - "math/big" "reflect" "testing" "github.com/ava-labs/subnet-evm/core/rawdb" "github.com/ava-labs/subnet-evm/core/types" - "github.com/ava-labs/subnet-evm/trie/triedb/pathdb" "github.com/ava-labs/subnet-evm/trie/utils" "github.com/ethereum/go-ethereum/common" + "github.com/holiman/uint256" ) var ( accounts = map[common.Address]*types.StateAccount{ {1}: { Nonce: 100, - Balance: big.NewInt(100), + Balance: uint256.NewInt(100), CodeHash: common.Hash{0x1}.Bytes(), }, {2}: { Nonce: 200, - Balance: big.NewInt(200), + Balance: uint256.NewInt(200), CodeHash: common.Hash{0x2}.Bytes(), }, } @@ -57,12 +56,7 @@ var ( ) func TestVerkleTreeReadWrite(t *testing.T) { - db := NewDatabase(rawdb.NewMemoryDatabase(), &Config{ - IsVerkle: true, - PathDB: pathdb.Defaults, - }) - defer db.Close() - + db := newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.PathScheme) tr, _ := NewVerkleTrie(types.EmptyVerkleHash, db, utils.NewPointCache(100)) for addr, acct := range accounts { diff --git a/trie/database.go b/triedb/database.go similarity index 91% rename from trie/database.go rename to triedb/database.go index 62550facc7..5383b57540 100644 --- a/trie/database.go +++ b/triedb/database.go @@ -14,15 +14,17 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -package trie +package triedb import ( "errors" - "github.com/ava-labs/subnet-evm/trie/triedb/hashdb" - "github.com/ava-labs/subnet-evm/trie/triedb/pathdb" + "github.com/ava-labs/subnet-evm/trie" "github.com/ava-labs/subnet-evm/trie/trienode" "github.com/ava-labs/subnet-evm/trie/triestate" + "github.com/ava-labs/subnet-evm/triedb/database" + "github.com/ava-labs/subnet-evm/triedb/hashdb" + "github.com/ava-labs/subnet-evm/triedb/pathdb" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/log" @@ -108,14 +110,21 @@ func NewDatabase(diskdb ethdb.Database, config *Config) *Database { if config.PathDB != nil { db.backend = pathdb.New(diskdb, config.PathDB) } else { - db.backend = hashdb.New(diskdb, config.HashDB, mptResolver{}) + var resolver hashdb.ChildResolver + if config.IsVerkle { + // TODO define verkle resolver + log.Crit("Verkle node resolver is not defined") + } else { + resolver = trie.MerkleResolver{} + } + db.backend = hashdb.New(diskdb, config.HashDB, resolver) } return db } // Reader returns a reader for accessing all trie nodes with provided state root. // An error will be returned if the requested state is not available. -func (db *Database) Reader(blockRoot common.Hash) (Reader, error) { +func (db *Database) Reader(blockRoot common.Hash) (database.Reader, error) { switch b := db.backend.(type) { case *hashdb.Database: return b.Reader(blockRoot) @@ -201,8 +210,7 @@ func (db *Database) WritePreimages() { } } -// Preimage retrieves a cached trie node pre-image from memory. If it cannot be -// found cached, the method queries the persistent database for the content. +// Preimage retrieves a cached trie node pre-image from preimage store. func (db *Database) Preimage(hash common.Hash) []byte { if db.preimages == nil { return nil @@ -210,6 +218,14 @@ func (db *Database) Preimage(hash common.Hash) []byte { return db.preimages.preimage(hash) } +// InsertPreimage writes pre-images of trie node to the preimage store. +func (db *Database) InsertPreimage(preimages map[common.Hash][]byte) { + if db.preimages == nil { + return + } + db.preimages.insertPreimage(preimages) +} + // Cap iteratively flushes old but still referenced trie nodes until the total // memory usage goes below the given threshold. The held pre-images accumulated // up to this point will be flushed in case the size exceeds the threshold. @@ -260,7 +276,14 @@ func (db *Database) Recover(target common.Hash) error { if !ok { return errors.New("not supported") } - return pdb.Recover(target, &trieLoader{db: db}) + var loader triestate.TrieLoader + if db.config.IsVerkle { + // TODO define verkle loader + log.Crit("Verkle loader is not defined") + } else { + loader = trie.NewMerkleLoader(db) + } + return pdb.Recover(target, loader) } // Recoverable returns the indicator if the specified state is enabled to be diff --git a/triedb/database/database.go b/triedb/database/database.go new file mode 100644 index 0000000000..18a8f454e2 --- /dev/null +++ b/triedb/database/database.go @@ -0,0 +1,48 @@ +// Copyright 2024 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 database + +import ( + "github.com/ethereum/go-ethereum/common" +) + +// Reader wraps the Node method of a backing trie reader. +type Reader interface { + // Node retrieves the trie node blob with the provided trie identifier, + // node path and the corresponding node hash. No error will be returned + // if the node is not found. + Node(owner common.Hash, path []byte, hash common.Hash) ([]byte, error) +} + +// PreimageStore wraps the methods of a backing store for reading and writing +// trie node preimages. +type PreimageStore interface { + // Preimage retrieves the preimage of the specified hash. + Preimage(hash common.Hash) []byte + + // InsertPreimage commits a set of preimages along with their hashes. + InsertPreimage(preimages map[common.Hash][]byte) +} + +// Database wraps the methods of a backing trie store. +type Database interface { + PreimageStore + + // Reader returns a node reader associated with the specific state. + // An error will be returned if the specified state is not available. + Reader(stateRoot common.Hash) (Reader, error) +} diff --git a/trie/triedb/hashdb/database.go b/triedb/hashdb/database.go similarity index 100% rename from trie/triedb/hashdb/database.go rename to triedb/hashdb/database.go diff --git a/trie/triedb/pathdb/database.go b/triedb/pathdb/database.go similarity index 100% rename from trie/triedb/pathdb/database.go rename to triedb/pathdb/database.go diff --git a/trie/triedb/pathdb/database_test.go b/triedb/pathdb/database_test.go similarity index 99% rename from trie/triedb/pathdb/database_test.go rename to triedb/pathdb/database_test.go index 7abfcb09f9..b452ac2a44 100644 --- a/trie/triedb/pathdb/database_test.go +++ b/triedb/pathdb/database_test.go @@ -30,7 +30,6 @@ import ( "bytes" "errors" "fmt" - "math/big" "math/rand" "testing" @@ -42,6 +41,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/rlp" + "github.com/holiman/uint256" "github.com/stretchr/testify/require" ) @@ -64,7 +64,7 @@ func updateTrie(addrHash common.Hash, root common.Hash, dirties, cleans map[comm func generateAccount(storageRoot common.Hash) types.StateAccount { return types.StateAccount{ Nonce: uint64(rand.Intn(100)), - Balance: big.NewInt(rand.Int63()), + Balance: uint256.NewInt(rand.Uint64()), CodeHash: testutil.RandBytes(32), Root: storageRoot, } diff --git a/trie/triedb/pathdb/difflayer.go b/triedb/pathdb/difflayer.go similarity index 100% rename from trie/triedb/pathdb/difflayer.go rename to triedb/pathdb/difflayer.go diff --git a/trie/triedb/pathdb/difflayer_test.go b/triedb/pathdb/difflayer_test.go similarity index 100% rename from trie/triedb/pathdb/difflayer_test.go rename to triedb/pathdb/difflayer_test.go diff --git a/trie/triedb/pathdb/disklayer.go b/triedb/pathdb/disklayer.go similarity index 100% rename from trie/triedb/pathdb/disklayer.go rename to triedb/pathdb/disklayer.go diff --git a/trie/triedb/pathdb/errors.go b/triedb/pathdb/errors.go similarity index 100% rename from trie/triedb/pathdb/errors.go rename to triedb/pathdb/errors.go diff --git a/trie/triedb/pathdb/history.go b/triedb/pathdb/history.go similarity index 100% rename from trie/triedb/pathdb/history.go rename to triedb/pathdb/history.go diff --git a/trie/triedb/pathdb/history_test.go b/triedb/pathdb/history_test.go similarity index 100% rename from trie/triedb/pathdb/history_test.go rename to triedb/pathdb/history_test.go diff --git a/trie/triedb/pathdb/journal.go b/triedb/pathdb/journal.go similarity index 100% rename from trie/triedb/pathdb/journal.go rename to triedb/pathdb/journal.go diff --git a/trie/triedb/pathdb/layertree.go b/triedb/pathdb/layertree.go similarity index 100% rename from trie/triedb/pathdb/layertree.go rename to triedb/pathdb/layertree.go diff --git a/trie/triedb/pathdb/metrics.go b/triedb/pathdb/metrics.go similarity index 100% rename from trie/triedb/pathdb/metrics.go rename to triedb/pathdb/metrics.go diff --git a/trie/triedb/pathdb/nodebuffer.go b/triedb/pathdb/nodebuffer.go similarity index 100% rename from trie/triedb/pathdb/nodebuffer.go rename to triedb/pathdb/nodebuffer.go diff --git a/trie/triedb/pathdb/testutils.go b/triedb/pathdb/testutils.go similarity index 100% rename from trie/triedb/pathdb/testutils.go rename to triedb/pathdb/testutils.go diff --git a/trie/preimages.go b/triedb/preimages.go similarity index 99% rename from trie/preimages.go rename to triedb/preimages.go index e8977e2a57..be337a1fc5 100644 --- a/trie/preimages.go +++ b/triedb/preimages.go @@ -24,7 +24,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -package trie +package triedb import ( "sync" From 9f9532a3d531c3e44bd623007aae6614d18f1602 Mon Sep 17 00:00:00 2001 From: Ceyhun Onur Date: Wed, 4 Sep 2024 12:08:47 +0300 Subject: [PATCH 5/9] Bump avago rc (#1330) * bump avago to rc * go mod tidy * update versions --- README.md | 39 +------------------------------------ compatibility.json | 42 ++-------------------------------------- go.mod | 4 ++-- go.sum | 8 ++++---- plugin/evm/version.go | 2 +- scripts/known_flakes.txt | 4 +++- scripts/versions.sh | 2 +- 7 files changed, 14 insertions(+), 87 deletions(-) diff --git a/README.md b/README.md index 4fb70c2095..a5571a937c 100644 --- a/README.md +++ b/README.md @@ -18,44 +18,6 @@ The Subnet EVM runs in a separate process from the main AvalancheGo process and ### AvalancheGo Compatibility ```text -[v0.1.0] AvalancheGo@v1.7.0-v1.7.4 (Protocol Version: 9) -[v0.1.1-v0.1.2] AvalancheGo@v1.7.5-v1.7.6 (Protocol Version: 10) -[v0.2.0] AvalancheGo@v1.7.7-v1.7.9 (Protocol Version: 11) -[v0.2.1] AvalancheGo@v1.7.10 (Protocol Version: 12) -[v0.2.2] AvalancheGo@v1.7.11-v1.7.12 (Protocol Version: 14) -[v0.2.3] AvalancheGo@v1.7.13-v1.7.16 (Protocol Version: 15) -[v0.2.4] AvalancheGo@v1.7.13-v1.7.16 (Protocol Version: 15) -[v0.2.5] AvalancheGo@v1.7.13-v1.7.16 (Protocol Version: 15) -[v0.2.6] AvalancheGo@v1.7.13-v1.7.16 (Protocol Version: 15) -[v0.2.7] AvalancheGo@v1.7.13-v1.7.16 (Protocol Version: 15) -[v0.2.8] AvalancheGo@v1.7.13-v1.7.18 (Protocol Version: 15) -[v0.2.9] AvalancheGo@v1.7.13-v1.7.18 (Protocol Version: 15) -[v0.3.0] AvalancheGo@v1.8.0-v1.8.6 (Protocol Version: 16) -[v0.4.0] AvalancheGo@v1.9.0 (Protocol Version: 17) -[v0.4.1] AvalancheGo@v1.9.1 (Protocol Version: 18) -[v0.4.2] AvalancheGo@v1.9.1 (Protocol Version: 18) -[v0.4.3] AvalancheGo@v1.9.2-v1.9.3 (Protocol Version: 19) -[v0.4.4] AvalancheGo@v1.9.2-v1.9.3 (Protocol Version: 19) -[v0.4.5] AvalancheGo@v1.9.4 (Protocol Version: 20) -[v0.4.6] AvalancheGo@v1.9.4 (Protocol Version: 20) -[v0.4.7] AvalancheGo@v1.9.5 (Protocol Version: 21) -[v0.4.8] AvalancheGo@v1.9.6-v1.9.8 (Protocol Version: 22) -[v0.4.9] AvalancheGo@v1.9.9 (Protocol Version: 23) -[v0.4.10] AvalancheGo@v1.9.9 (Protocol Version: 23) -[v0.4.11] AvalancheGo@v1.9.10-v1.9.16 (Protocol Version: 24) -[v0.4.12] AvalancheGo@v1.9.10-v1.9.16 (Protocol Version: 24) -[v0.5.0] AvalancheGo@v1.10.0 (Protocol Version: 25) -[v0.5.1] AvalancheGo@v1.10.1-v1.10.4 (Protocol Version: 26) -[v0.5.2] AvalancheGo@v1.10.1-v1.10.4 (Protocol Version: 26) -[v0.5.3] AvalancheGo@v1.10.5-v1.10.8 (Protocol Version: 27) -[v0.5.4] AvalancheGo@v1.10.9-v1.10.12 (Protocol Version: 28) -[v0.5.5] AvalancheGo@v1.10.9-v1.10.12 (Protocol Version: 28) -[v0.5.6] AvalancheGo@v1.10.9-v1.10.12 (Protocol Version: 28) -[v0.5.7] AvalancheGo@v1.10.13-v1.10.14 (Protocol Version: 29) -[v0.5.8] AvalancheGo@v1.10.13-v1.10.14 (Protocol Version: 29) -[v0.5.9] AvalancheGo@v1.10.15-v1.10.17 (Protocol Version: 30) -[v0.5.10] AvalancheGo@v1.10.15-v1.10.17 (Protocol Version: 30) -[v0.5.11] AvalancheGo@v1.10.18-v1.10.19 (Protocol Version: 31) [v0.6.0] AvalancheGo@v1.11.0-v1.11.1 (Protocol Version: 33) [v0.6.1] AvalancheGo@v1.11.0-v1.11.1 (Protocol Version: 33) [v0.6.2] AvalancheGo@v1.11.2 (Protocol Version: 34) @@ -65,6 +27,7 @@ The Subnet EVM runs in a separate process from the main AvalancheGo process and [v0.6.6] AvalancheGo@v1.11.3-v1.11.9 (Protocol Version: 35) [v0.6.7] AvalancheGo@v1.11.3-v1.11.9 (Protocol Version: 35) [v0.6.8] AvalancheGo@v1.11.10 (Protocol Version: 36) +[v0.6.9] AvalancheGo@v1.11.11 (Protocol Version: 37) ``` ## API diff --git a/compatibility.json b/compatibility.json index 372773a46b..9e150a8646 100644 --- a/compatibility.json +++ b/compatibility.json @@ -1,5 +1,6 @@ { "rpcChainVMProtocolVersion": { + "v0.6.9": 37, "v0.6.8": 36, "v0.6.7": 35, "v0.6.6": 35, @@ -8,45 +9,6 @@ "v0.6.3": 35, "v0.6.2": 34, "v0.6.1": 33, - "v0.6.0": 33, - "v0.5.11": 31, - "v0.5.10": 30, - "v0.5.9": 30, - "v0.5.8": 29, - "v0.5.7": 29, - "v0.5.6": 28, - "v0.5.5": 28, - "v0.5.4": 28, - "v0.5.3": 27, - "v0.5.2": 26, - "v0.5.1": 26, - "v0.5.0": 25, - "v0.4.12": 24, - "v0.4.11": 24, - "v0.4.10": 23, - "v0.4.9": 23, - "v0.4.8": 22, - "v0.4.7": 21, - "v0.4.6": 20, - "v0.4.5": 20, - "v0.4.4": 19, - "v0.4.3": 19, - "v0.4.2": 18, - "v0.4.1": 18, - "v0.4.0": 17, - "v0.3.0": 16, - "v0.2.9": 15, - "v0.2.8": 15, - "v0.2.7": 15, - "v0.2.6": 15, - "v0.2.5": 15, - "v0.2.4": 15, - "v0.2.3": 15, - "v0.2.2": 14, - "v0.2.1": 12, - "v0.2.0": 11, - "v0.1.2": 10, - "v0.1.1": 10, - "v0.1.0": 9 + "v0.6.0": 33 } } \ No newline at end of file diff --git a/go.mod b/go.mod index 51fda3902c..cc35468149 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.21.12 require ( github.com/VictoriaMetrics/fastcache v1.12.1 github.com/antithesishq/antithesis-sdk-go v0.3.8 - github.com/ava-labs/avalanchego v1.11.11-monitoring-url.0.20240827194054-0117ab96791c + github.com/ava-labs/avalanchego v1.11.11 github.com/cespare/cp v0.1.0 github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233 github.com/davecgh/go-spew v1.1.1 @@ -55,7 +55,7 @@ require ( require ( github.com/DataDog/zstd v1.5.2 // indirect github.com/NYTimes/gziphandler v1.1.1 // indirect - github.com/ava-labs/coreth v0.13.8-fixed-genesis-upgrade.0.20240813194342-7635a96aa180 // indirect + github.com/ava-labs/coreth v0.13.8-fixed-genesis-upgrade.0.20240815193440-a96bc921e732 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bits-and-blooms/bitset v1.10.0 // indirect github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect diff --git a/go.sum b/go.sum index 2e295ef79a..32359d6de1 100644 --- a/go.sum +++ b/go.sum @@ -58,10 +58,10 @@ github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax github.com/antithesishq/antithesis-sdk-go v0.3.8 h1:OvGoHxIcOXFJLyn9IJQ5DzByZ3YVAWNBc394ObzDRb8= github.com/antithesishq/antithesis-sdk-go v0.3.8/go.mod h1:IUpT2DPAKh6i/YhSbt6Gl3v2yvUZjmKncl7U91fup7E= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= -github.com/ava-labs/avalanchego v1.11.11-monitoring-url.0.20240827194054-0117ab96791c h1:WAbq2clkTkoxELkCePFe7sqwOvD32SZPzoWtciUmzQI= -github.com/ava-labs/avalanchego v1.11.11-monitoring-url.0.20240827194054-0117ab96791c/go.mod h1:UkyrRDXK2E15Lq2abyae2Pt+JsWvgsg1pe0/AtoMyAM= -github.com/ava-labs/coreth v0.13.8-fixed-genesis-upgrade.0.20240813194342-7635a96aa180 h1:6aIHp7wbyGVYdhHVQUbG7BEcbCMEQ5SYopPPJyipyvk= -github.com/ava-labs/coreth v0.13.8-fixed-genesis-upgrade.0.20240813194342-7635a96aa180/go.mod h1:/wNBVq7J7wlC2Kbov7kk6LV5xZvau7VF9zwTVOeyAjY= +github.com/ava-labs/avalanchego v1.11.11 h1:MIQq8xRavRj4ZXHA4G+aMiymig7SOScGOG1SApmMvBc= +github.com/ava-labs/avalanchego v1.11.11/go.mod h1:yFx3V31Jy9NFa8GZlgGnwiVf8KGjeF2+Uc99l9Scd/8= +github.com/ava-labs/coreth v0.13.8-fixed-genesis-upgrade.0.20240815193440-a96bc921e732 h1:wlhGJbmb7s3bU2QWtxKjscGjfHknQiq+cVhhUjONsB8= +github.com/ava-labs/coreth v0.13.8-fixed-genesis-upgrade.0.20240815193440-a96bc921e732/go.mod h1:RkQLaQ961Xe/sUb3ycn4Qi18vPPuEetTqDf2eDcquAs= github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= diff --git a/plugin/evm/version.go b/plugin/evm/version.go index 149ab0c482..236797f3e7 100644 --- a/plugin/evm/version.go +++ b/plugin/evm/version.go @@ -11,7 +11,7 @@ var ( // GitCommit is set by the build script GitCommit string // Version is the version of Subnet EVM - Version string = "v0.6.8" + Version string = "v0.6.9" ) func init() { diff --git a/scripts/known_flakes.txt b/scripts/known_flakes.txt index c035e08c25..fdbafaccf4 100644 --- a/scripts/known_flakes.txt +++ b/scripts/known_flakes.txt @@ -7,4 +7,6 @@ TestResyncNewRootAfterDeletes TestTransactionSkipIndexing TestVMShutdownWhileSyncing TestWalletNotifications -TestWebsocketLargeRead \ No newline at end of file +TestWebsocketLargeRead +TestChainIndexerWithChildren +TestWalletNotifications \ No newline at end of file diff --git a/scripts/versions.sh b/scripts/versions.sh index faee809b7c..e76d3bbad6 100644 --- a/scripts/versions.sh +++ b/scripts/versions.sh @@ -4,7 +4,7 @@ # shellcheck disable=SC2034 # Don't export them as they're used in the context of other calls -AVALANCHE_VERSION=${AVALANCHE_VERSION:-'0117ab96'} +AVALANCHE_VERSION=${AVALANCHE_VERSION:-'v1.11.11'} GINKGO_VERSION=${GINKGO_VERSION:-'v2.2.0'} # This won't be used, but it's here to make code syncs easier From a42c7c1518d4cf7f8220ff0c5b946f055e8f3b34 Mon Sep 17 00:00:00 2001 From: Ceyhun Onur Date: Wed, 4 Sep 2024 17:38:22 +0300 Subject: [PATCH 6/9] add new line after timestamp hardforks (#1331) --- params/config.go | 1 + 1 file changed, 1 insertion(+) diff --git a/params/config.go b/params/config.go index 5d5799b92b..bf313a8380 100644 --- a/params/config.go +++ b/params/config.go @@ -238,6 +238,7 @@ func (c *ChainConfig) Description() string { banner += "Hard forks (timestamp based):\n" banner += fmt.Sprintf(" - Cancun Timestamp: @%-10v (https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/cancun.md)\n", ptrToString(c.CancunTime)) banner += fmt.Sprintf(" - Verkle Timestamp: @%-10v", ptrToString(c.VerkleTime)) + banner += "\n" banner += "Avalanche Upgrades (timestamp based):\n" banner += c.NetworkUpgrades.Description() From 19fe7b9e2205dc8adc984ea2444d4f75ffc7ece7 Mon Sep 17 00:00:00 2001 From: Darioush Jalali Date: Thu, 5 Sep 2024 04:31:17 -0700 Subject: [PATCH 7/9] nit: peer/network.go: remove refs to cross chain in comments (#1333) --- peer/network.go | 2 +- peer/stats/stats.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/peer/network.go b/peer/network.go index 8f1919c032..7494db4656 100644 --- a/peer/network.go +++ b/peer/network.go @@ -303,7 +303,7 @@ func (n *network) AppRequestFailed(ctx context.Context, nodeID ids.NodeID, reque } // calculateTimeUntilDeadline calculates the time until deadline and drops it if we missed he deadline to response. -// This function updates metrics for both app requests and cross chain requests. +// This function updates metrics for app requests. // This is called by [AppRequest]. func calculateTimeUntilDeadline(deadline time.Time, stats stats.RequestHandlerStats) (time.Time, error) { // calculate how much time is left until the deadline diff --git a/peer/stats/stats.go b/peer/stats/stats.go index 7f82f887d3..57f3b15597 100644 --- a/peer/stats/stats.go +++ b/peer/stats/stats.go @@ -9,7 +9,7 @@ import ( "github.com/ava-labs/subnet-evm/metrics" ) -// RequestHandlerStats provides the interface for metrics for both app requests and cross chain requests. +// RequestHandlerStats provides the interface for metrics for app requests. type RequestHandlerStats interface { UpdateTimeUntilDeadline(duration time.Duration) IncDeadlineDroppedRequest() From 400862c752d9e385bbeb29e7d268576e80f00a5e Mon Sep 17 00:00:00 2001 From: Darioush Jalali Date: Thu, 5 Sep 2024 04:53:47 -0700 Subject: [PATCH 8/9] update known_flakes.txt (#1332) --- scripts/known_flakes.txt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/scripts/known_flakes.txt b/scripts/known_flakes.txt index fdbafaccf4..7f8f65990d 100644 --- a/scripts/known_flakes.txt +++ b/scripts/known_flakes.txt @@ -1,4 +1,6 @@ +TestChainIndexerWithChildren TestClientCancelWebsocket +TestExampleOpenTSB TestExpDecaySampleNanosecondRegression TestGolangBindings TestMempoolEthTxsAppGossipHandling @@ -7,6 +9,5 @@ TestResyncNewRootAfterDeletes TestTransactionSkipIndexing TestVMShutdownWhileSyncing TestWalletNotifications -TestWebsocketLargeRead -TestChainIndexerWithChildren -TestWalletNotifications \ No newline at end of file +TestWalletNotifications +TestWebsocketLargeRead \ No newline at end of file From 6fd1328d25ca90e41e73a2425deddcf78742abee Mon Sep 17 00:00:00 2001 From: Ceyhun Onur Date: Mon, 9 Sep 2024 19:33:11 +0300 Subject: [PATCH 9/9] Coreth nits (#1336) * ensure blob gas is zero in cancun * txlookup limit cleanup * use full faker * use full faker in api tests --------- Co-authored-by: Darioush Jalali --- consensus/dummy/consensus.go | 3 +++ core/state_processor_test.go | 3 ++- eth/ethconfig/config.go | 2 +- internal/ethapi/api_test.go | 2 +- plugin/evm/config_test.go | 27 +++++++++++++++++---------- 5 files changed, 24 insertions(+), 13 deletions(-) diff --git a/consensus/dummy/consensus.go b/consensus/dummy/consensus.go index 49c6253beb..1ab9444f53 100644 --- a/consensus/dummy/consensus.go +++ b/consensus/dummy/consensus.go @@ -262,6 +262,9 @@ func (eng *DummyEngine) verifyHeader(chain consensus.ChainHeaderReader, header * if err := eip4844.VerifyEIP4844Header(parent, header); err != nil { return err } + if *header.BlobGasUsed > 0 { // VerifyEIP4844Header ensures BlobGasUsed is non-nil + return fmt.Errorf("blobs not enabled on avalanche networks: used %d blob gas, expected 0", *header.BlobGasUsed) + } } return nil } diff --git a/core/state_processor_test.go b/core/state_processor_test.go index f2e0a770d2..cf165bea7f 100644 --- a/core/state_processor_test.go +++ b/core/state_processor_test.go @@ -121,7 +121,8 @@ func TestStateProcessorErrors(t *testing.T) { }, GasLimit: params.TestChainConfig.FeeConfig.GasLimit.Uint64(), } - blockchain, _ = NewBlockChain(db, DefaultCacheConfig, gspec, dummy.NewCoinbaseFaker(), vm.Config{}, common.Hash{}, false) + // 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{} ) diff --git a/eth/ethconfig/config.go b/eth/ethconfig/config.go index 0c451d6241..d8549b26af 100644 --- a/eth/ethconfig/config.go +++ b/eth/ethconfig/config.go @@ -171,6 +171,6 @@ type Config struct { // SkipTxIndexing skips indexing transactions. // This is useful for validators that don't need to index transactions. - // TxLookupLimit can be still used to control unindexing old transactions. + // TransactionHistory can be still used to control unindexing old transactions. SkipTxIndexing bool } diff --git a/internal/ethapi/api_test.go b/internal/ethapi/api_test.go index 72b2ba8568..78c9448b3b 100644 --- a/internal/ethapi/api_test.go +++ b/internal/ethapi/api_test.go @@ -1819,7 +1819,7 @@ func setupReceiptBackend(t *testing.T, genBlocks int) (*testBackend, []common.Ha txHashes = make([]common.Hash, genBlocks) ) - backend := newTestBackend(t, genBlocks, genesis, dummy.NewCoinbaseFaker(), func(i int, b *core.BlockGen) { + backend := newTestBackend(t, genBlocks, genesis, dummy.NewFullFaker(), func(i int, b *core.BlockGen) { var ( tx *types.Transaction err error diff --git a/plugin/evm/config_test.go b/plugin/evm/config_test.go index 2a69ea68d0..f5deb297ff 100644 --- a/plugin/evm/config_test.go +++ b/plugin/evm/config_test.go @@ -72,33 +72,39 @@ func TestUnmarshalConfig(t *testing.T) { false, }, { - "empty tx lookup limit", + "empty transaction history ", []byte(`{}`), - Config{TxLookupLimit: 0}, + Config{TransactionHistory: 0}, false, }, { - "zero tx lookup limit", - []byte(`{"tx-lookup-limit": 0}`), + "zero transaction history", + []byte(`{"transaction-history": 0}`), func() Config { - return Config{TxLookupLimit: 0} + return Config{TransactionHistory: 0} }(), false, }, { - "1 tx lookup limit", - []byte(`{"tx-lookup-limit": 1}`), + "1 transaction history", + []byte(`{"transaction-history": 1}`), func() Config { - return Config{TxLookupLimit: 1} + return Config{TransactionHistory: 1} }(), false, }, { - "-1 tx lookup limit", - []byte(`{"tx-lookup-limit": -1}`), + "-1 transaction history", + []byte(`{"transaction-history": -1}`), Config{}, true, }, + { + "deprecated tx lookup limit", + []byte(`{"tx-lookup-limit": 1}`), + Config{TransactionHistory: 1, TxLookupLimit: 1}, + false, + }, { "allow unprotected tx hashes", []byte(`{"allow-unprotected-tx-hashes": ["0x803351deb6d745e91545a6a3e1c0ea3e9a6a02a1a4193b70edfcd2f40f71a01c"]}`), @@ -115,6 +121,7 @@ func TestUnmarshalConfig(t *testing.T) { assert.Error(t, err) } else { assert.NoError(t, err) + tmp.Deprecate() assert.Equal(t, tt.expected, tmp) } })