diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 8200c0597f..7751a8146f 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -3,3 +3,7 @@ ## How this works ## How this was tested + +## Need to be documented? + +## Need to update RELEASES.md? diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fbf1601ee1..e7bf047ead 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,7 +31,7 @@ jobs: token: ${{ secrets.AVALANCHE_PAT }} - uses: actions/setup-go@v5 with: - go-version: "~1.21.12" + go-version: "~1.22.8" check-latest: true - name: change avalanchego dep if: ${{ github.event_name == 'workflow_dispatch' }} @@ -73,7 +73,7 @@ jobs: token: ${{ secrets.AVALANCHE_PAT }} - uses: actions/setup-go@v5 with: - go-version: "~1.21.12" + go-version: "~1.22.8" check-latest: true - name: change avalanchego dep if: ${{ github.event_name == 'workflow_dispatch' }} @@ -112,7 +112,7 @@ jobs: token: ${{ secrets.AVALANCHE_PAT }} - uses: actions/setup-go@v5 with: - go-version: "~1.21.12" + go-version: "~1.22.8" check-latest: true - name: Run e2e tests run: E2E_SERIAL=1 ./scripts/tests.e2e.sh diff --git a/.github/workflows/sync-subnet-evm-branch.yml b/.github/workflows/sync-subnet-evm-branch.yml index d51be8df89..97e8554ef8 100644 --- a/.github/workflows/sync-subnet-evm-branch.yml +++ b/.github/workflows/sync-subnet-evm-branch.yml @@ -1,11 +1,11 @@ -name: "Sync Subnet EVM Branch" +name: "Sync Subnet EVM Branch" on: workflow_dispatch: - inputs: - remoteBranch: - description: "Subnet EVM Branch" - required: true - default: "master" + inputs: + remoteBranch: + description: "Subnet EVM Branch" + required: true + default: "master" jobs: sync_branch: @@ -16,5 +16,5 @@ jobs: - uses: actions/setup-go@v5 with: - go-version: "~1.21.12" - check-latest: true \ No newline at end of file + go-version: "~1.22.8" + check-latest: true diff --git a/Dockerfile b/Dockerfile index 1807b17666..1b370e61ed 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # ============= Compilation Stage ================ -FROM golang:1.21.12-bullseye AS builder +FROM golang:1.22.8-bullseye AS builder ARG AVALANCHE_VERSION @@ -17,7 +17,7 @@ WORKDIR $GOPATH/src/github.com/ava-labs/avalanchego RUN go mod download # Replace the coreth dependency RUN go mod edit -replace github.com/ava-labs/coreth=../coreth -RUN go mod download && go mod tidy -compat=1.21 +RUN go mod download && go mod tidy -compat=1.22 # Build the AvalancheGo binary with local version of coreth. RUN ./scripts/build_avalanche.sh diff --git a/README.md b/README.md index 69a11a6c43..40cd3a8407 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [Avalanche](https://docs.avax.network/intro) is a network composed of multiple blockchains. Each blockchain is an instance of a Virtual Machine (VM), much like an object in an object-oriented language is an instance of a class. That is, the VM defines the behavior of the blockchain. -Coreth (from core Ethereum) is the [Virtual Machine (VM)](https://docs.avax.network/learn/avalanche/virtual-machines) that defines the Contract Chain (C-Chain). +Coreth (from core Ethereum) is the [Virtual Machine (VM)](https://docs.avax.network/learn/virtual-machines) that defines the Contract Chain (C-Chain). This chain implements the Ethereum Virtual Machine and supports Solidity smart contracts as well as most other Ethereum client functionality. ## Building diff --git a/accounts/abi/bind/bind_test.go b/accounts/abi/bind/bind_test.go index 7f82c5dd51..a54919722c 100644 --- a/accounts/abi/bind/bind_test.go +++ b/accounts/abi/bind/bind_test.go @@ -2179,7 +2179,7 @@ func golangBindings(t *testing.T, overload bool) { if out, err := replacer.CombinedOutput(); err != nil { t.Fatalf("failed to replace binding test dependency to current source tree: %v\n%s", err, out) } - tidier := exec.Command(gocmd, "mod", "tidy", "-compat=1.21") + tidier := exec.Command(gocmd, "mod", "tidy", "-compat=1.22") tidier.Dir = pkg if out, err := tidier.CombinedOutput(); err != nil { t.Fatalf("failed to tidy Go module file: %v\n%s", err, out) diff --git a/accounts/abi/bind/util_test.go b/accounts/abi/bind/util_test.go index d5754c603a..3eddeec0b1 100644 --- a/accounts/abi/bind/util_test.go +++ b/accounts/abi/bind/util_test.go @@ -125,10 +125,12 @@ func TestWaitDeployedCornerCases(t *testing.T) { // 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.LatestSigner(params.TestChainConfig), testKey) + tx, _ = types.SignTx(tx, types.LatestSignerForChainID(big.NewInt(1337)), testKey) ctx, cancel := context.WithCancel(context.Background()) defer cancel() - backend.Client().SendTransaction(ctx, tx) + if err := backend.Client().SendTransaction(ctx, tx); err != nil { + t.Fatalf("Failed to send transaction: %s", err) + } backend.Commit(true) notContractCreation := errors.New("tx is not contract creation") if _, err := bind.WaitDeployed(ctx, backend.Client(), tx); err.Error() != notContractCreation.Error() { @@ -137,7 +139,7 @@ func TestWaitDeployedCornerCases(t *testing.T) { // Create a transaction that is not mined. tx = types.NewContractCreation(1, big.NewInt(0), 3000000, gasPrice, common.FromHex(code)) - tx, _ = types.SignTx(tx, types.LatestSigner(params.TestChainConfig), testKey) + tx, _ = types.SignTx(tx, types.LatestSignerForChainID(big.NewInt(1337)), testKey) go func() { contextCanceled := errors.New("context canceled") @@ -146,6 +148,8 @@ func TestWaitDeployedCornerCases(t *testing.T) { } }() - backend.Client().SendTransaction(ctx, tx) + if err := backend.Client().SendTransaction(ctx, tx); err != nil { + t.Fatalf("Failed to send transaction: %s", err) + } cancel() } diff --git a/core/blockchain.go b/core/blockchain.go index 2dcd2738a6..db3a49bdb0 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -177,10 +177,11 @@ type CacheConfig struct { // triedbConfig derives the configures for trie database. func (c *CacheConfig) triedbConfig() *triedb.Config { config := &triedb.Config{Preimages: c.Preimages} - if c.StateScheme == rawdb.HashScheme { + if c.StateScheme == rawdb.HashScheme || c.StateScheme == "" { config.HashDB = &hashdb.Config{ - CleanCacheSize: c.TrieCleanLimit * 1024 * 1024, - StatsPrefix: trieCleanCacheStatsNamespace, + CleanCacheSize: c.TrieCleanLimit * 1024 * 1024, + StatsPrefix: trieCleanCacheStatsNamespace, + ReferenceRootAtomicallyOnUpdate: true, } } if c.StateScheme == rawdb.PathScheme { @@ -1161,9 +1162,9 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types. // diff layer for the block. var err error if bc.snaps == nil { - _, err = state.Commit(block.NumberU64(), bc.chainConfig.IsEIP158(block.Number()), true) + _, err = state.Commit(block.NumberU64(), bc.chainConfig.IsEIP158(block.Number())) } else { - _, err = state.CommitWithSnap(block.NumberU64(), bc.chainConfig.IsEIP158(block.Number()), bc.snaps, block.Hash(), block.ParentHash(), true) + _, err = state.CommitWithSnap(block.NumberU64(), bc.chainConfig.IsEIP158(block.Number()), bc.snaps, block.Hash(), block.ParentHash()) } if err != nil { return err @@ -1695,9 +1696,9 @@ func (bc *BlockChain) reprocessBlock(parent *types.Block, current *types.Block) // If snapshots are enabled, call CommitWithSnaps to explicitly create a snapshot // diff layer for the block. if bc.snaps == nil { - return statedb.Commit(current.NumberU64(), bc.chainConfig.IsEIP158(current.Number()), false) + return statedb.Commit(current.NumberU64(), bc.chainConfig.IsEIP158(current.Number())) } - return statedb.CommitWithSnap(current.NumberU64(), bc.chainConfig.IsEIP158(current.Number()), bc.snaps, current.Hash(), current.ParentHash(), false) + return statedb.CommitWithSnap(current.NumberU64(), bc.chainConfig.IsEIP158(current.Number()), bc.snaps, current.Hash(), current.ParentHash()) } // initSnapshot instantiates a Snapshot instance and adds it to [bc] @@ -1838,8 +1839,7 @@ func (bc *BlockChain) reprocessState(current *types.Block, reexec uint64) error // Flatten snapshot if initialized, holding a reference to the state root until the next block // is processed. if err := bc.flattenSnapshot(func() error { - triedb.Reference(root, common.Hash{}) - if previousRoot != (common.Hash{}) { + if previousRoot != (common.Hash{}) && previousRoot != root { triedb.Dereference(previousRoot) } previousRoot = root diff --git a/core/blockchain_test.go b/core/blockchain_test.go index 1c51be82da..601cb12f02 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -708,6 +708,9 @@ func TestCreateThenDeletePreByzantium(t *testing.T) { config.PetersburgBlock = nil config.IstanbulBlock = nil config.MuirGlacierBlock = nil + config.BerlinBlock = nil + config.LondonBlock = nil + testCreateThenDelete(t, &config) } func TestCreateThenDeletePostByzantium(t *testing.T) { diff --git a/core/chain_makers.go b/core/chain_makers.go index f8831ade81..d5ffd53074 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -298,7 +298,7 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse } // Write state changes to db - root, err := statedb.Commit(b.header.Number.Uint64(), config.IsEIP158(b.header.Number), false) + root, err := statedb.Commit(b.header.Number.Uint64(), config.IsEIP158(b.header.Number)) if err != nil { panic(fmt.Sprintf("state write error: %v", err)) } diff --git a/core/genesis.go b/core/genesis.go index 0a69436296..07d3ba072d 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -171,6 +171,7 @@ func SetupGenesisBlock( rawdb.WriteChainConfig(db, stored, newcfg) return newcfg, stored, nil } + storedcfg.SetEthUpgrades() storedData, _ := json.Marshal(storedcfg) // Check config compatibility and write the config. Compatibility errors // are returned to the caller unless we're already at block zero. @@ -298,7 +299,7 @@ func (g *Genesis) toBlock(db ethdb.Database, triedb *triedb.Database) *types.Blo } } - statedb.Commit(0, false, false) + statedb.Commit(0, false) // Commit newly generated states into disk if it's not empty. if root != types.EmptyRootHash { if err := triedb.Commit(root, true); err != nil { diff --git a/core/genesis_extra_test.go b/core/genesis_extra_test.go new file mode 100644 index 0000000000..25008653e0 --- /dev/null +++ b/core/genesis_extra_test.go @@ -0,0 +1,90 @@ +// (c) 2019-2021, 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 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package core + +import ( + "math/big" + "testing" + "time" + + "github.com/ava-labs/coreth/core/rawdb" + "github.com/ava-labs/coreth/core/types" + "github.com/ava-labs/coreth/params" + "github.com/ava-labs/coreth/triedb" + "github.com/ava-labs/coreth/utils" + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/require" +) + +func TestGenesisEthUpgrades(t *testing.T) { + db := rawdb.NewMemoryDatabase() + preEthUpgrades := ¶ms.ChainConfig{ + ChainID: big.NewInt(43114), // Specifically refers to mainnet for this UT + HomesteadBlock: big.NewInt(0), + DAOForkBlock: nil, + DAOForkSupport: false, + EIP150Block: big.NewInt(0), + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), + ConstantinopleBlock: big.NewInt(0), + PetersburgBlock: big.NewInt(0), + IstanbulBlock: big.NewInt(0), + MuirGlacierBlock: big.NewInt(0), + NetworkUpgrades: params.NetworkUpgrades{ + ApricotPhase1BlockTimestamp: utils.NewUint64(0), + ApricotPhase2BlockTimestamp: utils.NewUint64(0), + }, + } + + tdb := triedb.NewDatabase(db, triedb.HashDefaults) + config := *preEthUpgrades + // Set this up once, just to get the genesis hash + _, genHash, err := SetupGenesisBlock(db, tdb, &Genesis{Config: &config}, common.Hash{}, false) + require.NoError(t, err) + + // Write the configuration back to the db as it would be in prior versions + rawdb.WriteChainConfig(db, genHash, preEthUpgrades) + + // Make some other block + block := types.NewBlock( + &types.Header{ + Number: big.NewInt(1640340), // Berlin activation on mainnet + Difficulty: big.NewInt(1), + ParentHash: genHash, + Time: uint64(time.Now().Unix()), + }, + nil, nil, nil, nil, + ) + rawdb.WriteBlock(db, block) + + // We should still be able to re-initialize + config = *preEthUpgrades + config.SetEthUpgrades() // New versions will set additional fields eg, LondonBlock + _, _, err = SetupGenesisBlock(db, tdb, &Genesis{Config: &config}, block.Hash(), false) + require.NoError(t, err) +} diff --git a/core/genesis_test.go b/core/genesis_test.go index 35adbab77f..6050a06f84 100644 --- a/core/genesis_test.go +++ b/core/genesis_test.go @@ -301,6 +301,9 @@ func TestVerkleGenesisCommit(t *testing.T) { PetersburgBlock: big.NewInt(0), IstanbulBlock: big.NewInt(0), MuirGlacierBlock: big.NewInt(0), + BerlinBlock: big.NewInt(0), + LondonBlock: big.NewInt(0), + ShanghaiTime: &verkleTime, CancunTime: &verkleTime, VerkleTime: &verkleTime, } diff --git a/core/state/state_test.go b/core/state/state_test.go index 0d6f7d6445..3d6dadf209 100644 --- a/core/state/state_test.go +++ b/core/state/state_test.go @@ -68,7 +68,7 @@ func TestDump(t *testing.T) { // write some of them to the trie s.state.updateStateObject(obj1) s.state.updateStateObject(obj2) - root, _ := s.state.Commit(0, false, false) + root, _ := s.state.Commit(0, false) // check that DumpToCollector contains the state objects that are in trie s.state, _ = New(root, tdb, nil) @@ -130,7 +130,7 @@ func TestIterativeDump(t *testing.T) { // write some of them to the trie s.state.updateStateObject(obj1) s.state.updateStateObject(obj2) - root, _ := s.state.Commit(0, false, false) + root, _ := s.state.Commit(0, false) s.state, _ = New(root, tdb, nil) b := &bytes.Buffer{} @@ -156,7 +156,7 @@ func TestNull(t *testing.T) { var value common.Hash s.state.SetState(address, common.Hash{}, value) - s.state.Commit(0, false, false) + s.state.Commit(0, false) if value := s.state.GetState(address, common.Hash{}); value != (common.Hash{}) { t.Errorf("expected empty current value, got %x", value) @@ -228,7 +228,7 @@ func TestSnapshot2(t *testing.T) { so0.deleted = false state.setStateObject(so0) - root, _ := state.Commit(0, false, false) + root, _ := state.Commit(0, false) state, _ = New(root, state.db, nil) // and one with deleted == true diff --git a/core/state/statedb.go b/core/state/statedb.go index 9eda070321..3096589b6e 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -1263,14 +1263,14 @@ func (s *StateDB) handleDestruction(nodes *trienode.MergedNodeSet) (map[common.A } // Commit writes the state to the underlying in-memory trie database. -func (s *StateDB) Commit(block uint64, deleteEmptyObjects bool, referenceRoot bool) (common.Hash, error) { - return s.commit(block, deleteEmptyObjects, nil, common.Hash{}, common.Hash{}, referenceRoot) +func (s *StateDB) Commit(block uint64, deleteEmptyObjects bool) (common.Hash, error) { + return s.commit(block, deleteEmptyObjects, nil, common.Hash{}, common.Hash{}) } // CommitWithSnap writes the state to the underlying in-memory trie database and // generates a snapshot layer for the newly committed state. -func (s *StateDB) CommitWithSnap(block uint64, deleteEmptyObjects bool, snaps *snapshot.Tree, blockHash, parentHash common.Hash, referenceRoot bool) (common.Hash, error) { - return s.commit(block, deleteEmptyObjects, snaps, blockHash, parentHash, referenceRoot) +func (s *StateDB) CommitWithSnap(block uint64, deleteEmptyObjects bool, snaps *snapshot.Tree, blockHash, parentHash common.Hash) (common.Hash, error) { + return s.commit(block, deleteEmptyObjects, snaps, blockHash, parentHash) } // Once the state is committed, tries cached in stateDB (including account @@ -1280,7 +1280,7 @@ func (s *StateDB) CommitWithSnap(block uint64, deleteEmptyObjects bool, snaps *s // // The associated block number of the state transition is also provided // for more chain context. -func (s *StateDB) commit(block uint64, deleteEmptyObjects bool, snaps *snapshot.Tree, blockHash, parentHash common.Hash, referenceRoot bool) (common.Hash, error) { +func (s *StateDB) commit(block uint64, deleteEmptyObjects bool, snaps *snapshot.Tree, blockHash, parentHash common.Hash) (common.Hash, error) { // Short circuit in case any database failure occurred earlier. if s.dbErr != nil { return common.Hash{}, fmt.Errorf("commit aborted due to earlier error: %v", s.dbErr) @@ -1389,14 +1389,8 @@ func (s *StateDB) commit(block uint64, deleteEmptyObjects bool, snaps *snapshot. if root != origin { start := time.Now() set := triestate.New(s.accountsOrigin, s.storagesOrigin, incomplete) - if referenceRoot { - if err := s.db.TrieDB().UpdateAndReferenceRoot(root, origin, block, nodes, set); err != nil { - return common.Hash{}, err - } - } else { - if err := s.db.TrieDB().Update(root, origin, block, nodes, set); err != nil { - return common.Hash{}, err - } + if err := s.db.TrieDB().Update(root, origin, block, nodes, set); err != nil { + return common.Hash{}, err } s.originalRoot = root if metrics.EnabledExpensive { diff --git a/core/state/statedb_fuzz_test.go b/core/state/statedb_fuzz_test.go index f694cb2a0f..9cbbffa870 100644 --- a/core/state/statedb_fuzz_test.go +++ b/core/state/statedb_fuzz_test.go @@ -233,7 +233,7 @@ func (test *stateTest) run() bool { } else { state.IntermediateRoot(true) // call intermediateRoot at the transaction boundary } - nroot, err := state.Commit(0, true, false) // call commit at the block boundary + nroot, err := state.Commit(0, true) // call commit at the block boundary if err != nil { panic(err) } diff --git a/core/state/statedb_test.go b/core/state/statedb_test.go index 7ae230fbed..88799e3c3e 100644 --- a/core/state/statedb_test.go +++ b/core/state/statedb_test.go @@ -127,7 +127,7 @@ func TestIntermediateLeaks(t *testing.T) { } // Commit and cross check the databases. - transRoot, err := transState.Commit(0, false, false) + transRoot, err := transState.Commit(0, false) if err != nil { t.Fatalf("failed to commit transition state: %v", err) } @@ -135,7 +135,7 @@ func TestIntermediateLeaks(t *testing.T) { t.Errorf("can not commit trie %v to persistent database", transRoot.Hex()) } - finalRoot, err := finalState.Commit(0, false, false) + finalRoot, err := finalState.Commit(0, false) if err != nil { t.Fatalf("failed to commit final state: %v", err) } @@ -543,7 +543,7 @@ func (test *snapshotTest) checkEqual(state, checkstate *StateDB) error { func TestTouchDelete(t *testing.T) { s := newStateEnv() s.state.getOrNewStateObject(common.Address{}) - root, _ := s.state.Commit(0, false, false) + root, _ := s.state.Commit(0, false) s.state, _ = NewWithSnapshot(root, s.state.db, s.state.snap) snapshot := s.state.Snapshot() @@ -631,7 +631,7 @@ func TestCopyCommitCopy(t *testing.T) { t.Fatalf("second copy committed storage slot mismatch: have %x, want %x", val, sval) } // Commit state, ensure states can be loaded from disk - root, _ := state.Commit(0, false, false) + root, _ := state.Commit(0, false) state, _ = New(root, tdb, nil) if balance := state.GetBalance(addr); balance.Cmp(uint256.NewInt(42)) != 0 { t.Fatalf("state post-commit balance mismatch: have %v, want %v", balance, 42) @@ -745,7 +745,7 @@ func TestCommitCopy(t *testing.T) { t.Fatalf("initial committed storage slot mismatch: have %x, want %x", val, common.Hash{}) } // Copy the committed state database, the copied one is not functional. - state.Commit(0, true, false) + state.Commit(0, true) copied := state.Copy() if balance := copied.GetBalance(addr); balance.Cmp(uint256.NewInt(0)) != 0 { t.Fatalf("unexpected balance: have %v", balance) @@ -779,7 +779,7 @@ func TestDeleteCreateRevert(t *testing.T) { addr := common.BytesToAddress([]byte("so")) state.SetBalance(addr, uint256.NewInt(1)) - root, _ := state.Commit(0, false, false) + root, _ := state.Commit(0, false) state, _ = NewWithSnapshot(root, state.db, state.snap) // Simulate self-destructing in one transaction, then create-reverting in another @@ -791,7 +791,7 @@ func TestDeleteCreateRevert(t *testing.T) { state.RevertToSnapshot(id) // Commit the entire state and make sure we don't crash and have the correct state - root, _ = state.Commit(0, true, false) + root, _ = state.Commit(0, true) state, _ = NewWithSnapshot(root, state.db, state.snap) if state.getStateObject(addr) != nil { @@ -834,7 +834,7 @@ func testMissingTrieNodes(t *testing.T, scheme string) { a2 := common.BytesToAddress([]byte("another")) state.SetBalance(a2, uint256.NewInt(100)) state.SetCode(a2, []byte{1, 2, 4}) - root, _ = state.Commit(0, false, false) + root, _ = state.Commit(0, false) t.Logf("root: %x", root) // force-flush tdb.Commit(root, false) @@ -858,7 +858,7 @@ func testMissingTrieNodes(t *testing.T, scheme string) { } // Modify the state state.SetBalance(addr, uint256.NewInt(2)) - root, err := state.Commit(0, false, false) + root, err := state.Commit(0, false) if err == nil { t.Fatalf("expected error, got root :%x", root) } @@ -1044,7 +1044,7 @@ func TestMultiCoinOperations(t *testing.T) { assetID := common.Hash{2} s.state.getOrNewStateObject(addr) - root, _ := s.state.Commit(0, false, false) + root, _ := s.state.Commit(0, false) s.state, _ = NewWithSnapshot(root, s.state.db, s.state.snap) s.state.AddBalance(addr, new(uint256.Int)) @@ -1101,14 +1101,14 @@ func TestMultiCoinSnapshot(t *testing.T) { assertBalances(10, 0, 0) // Commit and get the new root - root, _ = stateDB.Commit(0, false, false) + root, _ = stateDB.Commit(0, false) assertBalances(10, 0, 0) // Create a new state from the latest root, add a multicoin balance, and // commit it to the tree. stateDB, _ = New(root, sdb, snapTree) stateDB.AddBalanceMultiCoin(addr, assetID1, big.NewInt(10)) - root, _ = stateDB.Commit(0, false, false) + root, _ = stateDB.Commit(0, false) assertBalances(10, 10, 0) // Add more layers than the cap and ensure the balances and layers are correct @@ -1116,7 +1116,7 @@ func TestMultiCoinSnapshot(t *testing.T) { stateDB, _ = New(root, sdb, snapTree) stateDB.AddBalanceMultiCoin(addr, assetID1, big.NewInt(1)) stateDB.AddBalanceMultiCoin(addr, assetID2, big.NewInt(2)) - root, _ = stateDB.Commit(0, false, false) + root, _ = stateDB.Commit(0, false) } assertBalances(10, 266, 512) @@ -1125,7 +1125,7 @@ func TestMultiCoinSnapshot(t *testing.T) { stateDB, _ = New(root, sdb, snapTree) stateDB.AddBalance(addr, uint256.NewInt(1)) stateDB.AddBalanceMultiCoin(addr, assetID1, big.NewInt(1)) - root, _ = stateDB.Commit(0, false, false) + root, _ = stateDB.Commit(0, false) stateDB, _ = New(root, sdb, snapTree) assertBalances(11, 267, 512) } @@ -1147,7 +1147,7 @@ func TestGenerateMultiCoinAccounts(t *testing.T) { t.Fatal(err) } stateDB.SetBalanceMultiCoin(addr, assetID, assetBalance) - root, err := stateDB.Commit(0, false, false) + root, err := stateDB.Commit(0, false) if err != nil { t.Fatal(err) } @@ -1207,7 +1207,7 @@ func TestFlushOrderDataLoss(t *testing.T) { state.SetState(common.Address{a}, common.Hash{a, s}, common.Hash{a, s}) } } - root, err := state.Commit(0, false, false) + root, err := state.Commit(0, false) if err != nil { t.Fatalf("failed to commit state trie: %v", err) } @@ -1286,7 +1286,7 @@ func TestResetObject(t *testing.T) { state.CreateAccount(addr) 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) + root, _ := state.CommitWithSnap(0, true, snaps, common.Hash{}, common.Hash{}) // Ensure the original account is wiped properly snap := snaps.Snapshot(root) @@ -1317,7 +1317,7 @@ func TestDeleteStorage(t *testing.T) { value := common.Hash(uint256.NewInt(uint64(10 * i)).Bytes32()) state.SetState(addr, slot, value) } - root, _ := state.CommitWithSnap(0, true, snaps, common.Hash{}, common.Hash{}, false) + root, _ := state.CommitWithSnap(0, true, snaps, common.Hash{}, common.Hash{}) // Init phase done, create two states, one with snap and one without fastState, _ := New(root, db, snaps) slowState, _ := New(root, db, nil) diff --git a/core/state/sync_test.go b/core/state/sync_test.go index 5b1c9e3866..17444dfe89 100644 --- a/core/state/sync_test.go +++ b/core/state/sync_test.go @@ -76,7 +76,7 @@ func makeTestState(scheme string) (ethdb.Database, Database, *triedb.Database, c } accounts = append(accounts, acc) } - root, _ := state.Commit(0, false, false) + root, _ := state.Commit(0, false) // Return the generated state return db, sdb, nodeDb, root, accounts diff --git a/core/state_processor_test.go b/core/state_processor_test.go index 028f49479a..5392ad4427 100644 --- a/core/state_processor_test.go +++ b/core/state_processor_test.go @@ -56,6 +56,7 @@ func u64(val uint64) *uint64 { return &val } func TestStateProcessorErrors(t *testing.T) { cpcfg := *params.TestChainConfig config := &cpcfg + config.ShanghaiTime = u64(0) config.CancunTime = u64(0) var ( diff --git a/core/state_transition_test.go b/core/state_transition_test.go index 8aed625bc1..929072b0d8 100644 --- a/core/state_transition_test.go +++ b/core/state_transition_test.go @@ -142,7 +142,7 @@ func TestNativeAssetContractCall(t *testing.T) { makeTx(1, contractAddr, common.Big0, 100_000, big.NewInt(params.LaunchMinGasPrice), nil), // No input data is necessary, since this will hit the contract's fallback function. } - phase6Tests := map[string]stateTransitionTest{ + tests := map[string]stateTransitionTest{ "phase5": { config: params.TestApricotPhase5Config, txs: txs, @@ -167,9 +167,21 @@ func TestNativeAssetContractCall(t *testing.T) { gasUsed: []uint64{132091, 21618}, want: "", }, + "durango": { + config: params.TestDurangoChainConfig, + txs: txs, + gasUsed: []uint64{132117, 21618}, + want: "", + }, + "etna": { + config: params.TestEtnaChainConfig, + txs: txs, + gasUsed: []uint64{132117, 21618}, + want: "", + }, } - for name, stTest := range phase6Tests { + for name, stTest := range tests { t.Run(name, func(t *testing.T) { executeStateTransitionTest(t, stTest) }) diff --git a/core/test_blockchain.go b/core/test_blockchain.go index 41d5657b92..9c22ff0338 100644 --- a/core/test_blockchain.go +++ b/core/test_blockchain.go @@ -1502,7 +1502,7 @@ func checkTxIndicesHelper(t *testing.T, expectedTail *uint64, indexedFrom uint64 require.EventuallyWithTf(t, func(c *assert.CollectT) { stored = *rawdb.ReadTxIndexTail(db) - require.Equalf(t, tailValue, stored, "expected tail to be %d, found %d", tailValue, stored) + assert.Equalf(c, tailValue, stored, "expected tail to be %d, found %d", tailValue, stored) }, 30*time.Second, 500*time.Millisecond, "expected tail to be %d eventually", tailValue) } diff --git a/core/txpool/blobpool/blobpool_test.go b/core/txpool/blobpool/blobpool_test.go index 5290153bba..0e993ca9dc 100644 --- a/core/txpool/blobpool/blobpool_test.go +++ b/core/txpool/blobpool/blobpool_test.go @@ -584,7 +584,7 @@ func TestOpenDrops(t *testing.T) { 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) + statedb.Commit(0, true) chain := &testBlockChain{ config: testChainConfig, @@ -703,7 +703,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, uint256.NewInt(1_000_000_000)) - statedb.Commit(0, true, false) + statedb.Commit(0, true) chain := &testBlockChain{ config: testChainConfig, @@ -806,7 +806,7 @@ func TestOpenHeap(t *testing.T) { 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) + statedb.Commit(0, true) chain := &testBlockChain{ config: testChainConfig, @@ -887,7 +887,7 @@ func TestOpenCap(t *testing.T) { 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) + statedb.Commit(0, true) chain := &testBlockChain{ config: testChainConfig, @@ -1305,7 +1305,7 @@ func TestAdd(t *testing.T) { store.Put(blob) } } - statedb.Commit(0, true, false) + statedb.Commit(0, true) store.Close() // Create a blob pool out of the pre-seeded dats @@ -1378,7 +1378,7 @@ func benchmarkPoolPending(b *testing.B, datacap uint64) { statedb.AddBalance(addr, uint256.NewInt(1_000_000_000)) pool.add(tx) } - statedb.Commit(0, true, false) + statedb.Commit(0, true) defer pool.Close() // Benchmark assembling the pending diff --git a/core/vm/contracts.go b/core/vm/contracts.go index 77d0ecc808..b14dea6b2a 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -172,6 +172,9 @@ var PrecompiledContractsCancun = map[common.Address]contract.StatefulPrecompiled common.BytesToAddress([]byte{8}): newWrappedPrecompiledContract(&bn256PairingIstanbul{}), common.BytesToAddress([]byte{9}): newWrappedPrecompiledContract(&blake2F{}), common.BytesToAddress([]byte{0x0a}): newWrappedPrecompiledContract(&kzgPointEvaluation{}), + genesisContractAddr: &deprecatedContract{}, + NativeAssetBalanceAddr: &deprecatedContract{}, + NativeAssetCallAddr: &deprecatedContract{}, } // PrecompiledContractsBLS contains the set of pre-compiled Ethereum diff --git a/core/vm/eips.go b/core/vm/eips.go index 71748718f9..b573705dc2 100644 --- a/core/vm/eips.go +++ b/core/vm/eips.go @@ -169,12 +169,6 @@ func enable2929(jt *JumpTable) { func enableAP1(jt *JumpTable) { jt[SSTORE].dynamicGas = gasSStoreAP1 jt[SELFDESTRUCT].dynamicGas = gasSelfdestructAP1 - jt[CALLEX].dynamicGas = gasCallExpertAP1 -} - -func enableAP2(jt *JumpTable) { - jt[BALANCEMC] = &operation{execute: opUndefined, maxStack: maxStack(0, 0)} - jt[CALLEX] = &operation{execute: opUndefined, maxStack: maxStack(0, 0)} } // enable3198 applies EIP-3198 (BASEFEE Opcode) diff --git a/core/vm/evm.go b/core/vm/evm.go index 9c1193927f..0eb7ec12df 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -29,7 +29,6 @@ package vm import ( "math/big" "sync/atomic" - "time" "github.com/ava-labs/avalanchego/snow" "github.com/ava-labs/coreth/constants" @@ -346,84 +345,6 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas return ret, gas, err } -// This allows the user transfer balance of a specified coinId in addition to a normal Call(). -func (evm *EVM) CallExpert(caller ContractRef, addr common.Address, input []byte, gas uint64, value *uint256.Int, coinID common.Hash, value2 *big.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) { - return nil, gas, vmerrs.ErrInsufficientBalance - } - - if value2.Sign() != 0 && !evm.Context.CanTransferMC(evm.StateDB, caller.Address(), addr, coinID, value2) { - return nil, gas, vmerrs.ErrInsufficientBalance - } - - snapshot := evm.StateDB.Snapshot() - //p, isPrecompile := evm.precompile(addr) - - if !evm.StateDB.Exist(addr) { - //if !isPrecompile && evm.chainRules.IsEIP158 && value.Sign() == 0 { - // // Calling a non existing account, don't do anything, but ping the tracer - // if evm.Config.Debug && evm.depth == 0 { - // evm.Config.Tracer.CaptureStart(evm, caller.Address(), addr, false, input, gas, value) - // evm.Config.Tracer.CaptureEnd(ret, 0, 0, nil) - // } - // return nil, gas, nil - //} - evm.StateDB.CreateAccount(addr) - } - evm.Context.Transfer(evm.StateDB, caller.Address(), addr, value) - evm.Context.TransferMultiCoin(evm.StateDB, caller.Address(), addr, coinID, value2) - - // Capture the tracer start/end events in debug mode - debug := evm.Config.Tracer != nil - if debug && evm.depth == 0 { - evm.Config.Tracer.CaptureStart(evm, caller.Address(), addr, false, input, gas, value.ToBig()) - defer func(startGas uint64, startTime time.Time) { // Lazy evaluation of the parameters - evm.Config.Tracer.CaptureEnd(ret, startGas-gas, err) - }(gas, time.Now()) - } - - //if isPrecompile { - // ret, gas, err = RunPrecompiledContract(p, input, gas) - //} else { - // 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. - code := evm.StateDB.GetCode(addr) - if len(code) == 0 { - ret, err = nil, nil // gas is unchanged - } else { - addrCopy := addr - // If the account has no code, we can abort here - // The depth-check is already done, and precompiles handled above - contract := NewContract(caller, AccountRef(addrCopy), value, gas) - contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), code) - ret, err = evm.interpreter.Run(contract, input, false) - gas = contract.Gas - } - //} - // 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 - // when we're in homestead this also counts for code storage gas errors. - if err != nil { - evm.StateDB.RevertToSnapshot(snapshot) - if err != vmerrs.ErrExecutionReverted { - gas = 0 - } - // TODO: consider clearing up unused snapshots: - //} else { - // evm.StateDB.DiscardSnapshot(snapshot) - } - return ret, gas, err -} - // CallCode executes the contract associated with the addr with the given input // as 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 diff --git a/core/vm/gas_table.go b/core/vm/gas_table.go index f97d0fd1ba..68a2ae2d58 100644 --- a/core/vm/gas_table.go +++ b/core/vm/gas_table.go @@ -444,45 +444,6 @@ func gasCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize return gas, nil } -func gasCallExpertAP1(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { - var ( - gas uint64 - transfersValue = !stack.Back(2).IsZero() - multiCoinTransfersValue = !stack.Back(4).IsZero() - address = common.Address(stack.Back(1).Bytes20()) - ) - if evm.chainRules.IsEIP158 { - if (transfersValue || multiCoinTransfersValue) && evm.StateDB.Empty(address) { - gas += params.CallNewAccountGas - } - } else if !evm.StateDB.Exist(address) { - gas += params.CallNewAccountGas - } - if transfersValue { - gas += params.CallValueTransferGas - } - if multiCoinTransfersValue { - gas += params.CallValueTransferGas - } - memoryGas, err := memoryGasCost(mem, memorySize) - if err != nil { - return 0, err - } - var overflow bool - if gas, overflow = math.SafeAdd(gas, memoryGas); overflow { - return 0, vmerrs.ErrGasUintOverflow - } - - evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas, gas, stack.Back(0)) - if err != nil { - return 0, err - } - if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow { - return 0, vmerrs.ErrGasUintOverflow - } - return gas, nil -} - func gasCallCode(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { memoryGas, err := memoryGasCost(mem, memorySize) if err != nil { diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 912293f367..72047c260d 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -27,7 +27,6 @@ package vm import ( - "errors" "math" "github.com/ava-labs/coreth/params" @@ -277,17 +276,6 @@ func opBalance(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([] return nil, nil } -func opBalanceMultiCoin(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - addr, cid := scope.Stack.pop(), scope.Stack.pop() - res, err := uint256.FromBig(interpreter.evm.StateDB.GetBalanceMultiCoin( - common.BigToAddress(addr.ToBig()), common.BigToHash(cid.ToBig()))) - if err { - return nil, errors.New("balance overflow") - } - scope.Stack.push(res) - return nil, nil -} - func opOrigin(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { scope.Stack.push(new(uint256.Int).SetBytes(interpreter.evm.Origin.Bytes())) return nil, nil @@ -701,49 +689,6 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byt return ret, nil } -// Note: opCallExpert was de-activated in ApricotPhase2. -func opCallExpert(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - stack := scope.Stack - // Pop gas. The actual gas in interpreter.evm.callGasTemp. - // We can use this as a temporary value - temp := stack.pop() - gas := interpreter.evm.callGasTemp - // Pop other call parameters. - addr, value, cid, value2, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop() - toAddr := common.Address(addr.Bytes20()) - coinID := common.BigToHash(cid.ToBig()) - // Get the arguments from the memory. - args := scope.Memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64())) - - // Note: this code fails to check that value2 is zero, which was a bug when CALLEX was active. - // The CALLEX opcode was de-activated in ApricotPhase2 resolving this issue. - if interpreter.readOnly && !value.IsZero() { - return nil, vmerrs.ErrWriteProtection - } - - var bigVal2 = 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 !value2.IsZero() { - bigVal2 = value2.ToBig() - } - - ret, returnGas, err := interpreter.evm.CallExpert(scope.Contract, toAddr, args, gas, &value, coinID, bigVal2) - if err != nil { - temp.Clear() - } else { - temp.SetOne() - } - stack.push(&temp) - if err == nil || err == vmerrs.ErrExecutionReverted { - scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) - } - scope.Contract.Gas += returnGas - - interpreter.returnData = ret - return ret, nil -} func opCallCode(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { // Pop gas. The actual gas is in interpreter.evm.callGasTemp. stack := scope.Stack diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 3c60b10e9a..5d495f0af6 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -34,11 +34,6 @@ import ( "github.com/ethereum/go-ethereum/log" ) -var BuiltinAddr = common.Address{ - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -} - // Config are the configuration options for the Interpreter type Config struct { Tracer EVMLogger // Opcode logger @@ -121,18 +116,6 @@ func NewEVMInterpreter(evm *EVM) *EVMInterpreter { // considered a revert-and-consume-all-gas operation except for // ErrExecutionReverted which means revert-and-keep-gas-left. func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (ret []byte, err error) { - // Deprecate special handling of [BuiltinAddr] as of ApricotPhase2. - // In ApricotPhase2, the contract deployed in the genesis is overridden by a deprecated precompiled - // contract which will return an error immediately if its ever called. Therefore, this function should - // never be called after ApricotPhase2 with [BuiltinAddr] as the contract address. - if !in.evm.chainRules.IsApricotPhase2 && contract.Address() == BuiltinAddr { - self := AccountRef(contract.Caller()) - if _, ok := contract.caller.(*Contract); ok { - contract = contract.AsDelegate() - } - contract.self = self - } - // Increment the call depth which is restricted to 1024 in.evm.depth++ defer func() { in.evm.depth-- }() diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go index d1784c94a3..f642f1cb8a 100644 --- a/core/vm/jump_table.go +++ b/core/vm/jump_table.go @@ -125,7 +125,6 @@ func newApricotPhase2InstructionSet() JumpTable { instructionSet := newApricotPhase1InstructionSet() enable2929(&instructionSet) - enableAP2(&instructionSet) return validate(instructionSet) } @@ -243,7 +242,6 @@ func newTangerineWhistleInstructionSet() JumpTable { instructionSet[SLOAD].constantGas = params.SloadGasEIP150 instructionSet[EXTCODECOPY].constantGas = params.ExtcodeCopyBaseEIP150 instructionSet[CALL].constantGas = params.CallGasEIP150 - instructionSet[CALLEX].constantGas = params.CallGasEIP150 instructionSet[CALLCODE].constantGas = params.CallGasEIP150 instructionSet[DELEGATECALL].constantGas = params.CallGasEIP150 return validate(instructionSet) @@ -426,12 +424,6 @@ func newFrontierInstructionSet() JumpTable { minStack: minStack(1, 1), maxStack: maxStack(1, 1), }, - BALANCEMC: { - execute: opBalanceMultiCoin, - constantGas: params.BalanceGasFrontier, - minStack: minStack(2, 1), - maxStack: maxStack(2, 1), - }, ORIGIN: { execute: opOrigin, constantGas: GasQuickStep, @@ -1053,14 +1045,6 @@ func newFrontierInstructionSet() JumpTable { maxStack: maxStack(7, 1), memorySize: memoryCall, }, - CALLEX: { - execute: opCallExpert, - constantGas: params.CallGasFrontier, - dynamicGas: gasCall, - minStack: minStack(9, 1), - maxStack: maxStack(9, 1), - memorySize: memoryCallExpert, - }, CALLCODE: { execute: opCallCode, constantGas: params.CallGasFrontier, diff --git a/core/vm/memory_table.go b/core/vm/memory_table.go index 8d0d80406c..276e1bd2a3 100644 --- a/core/vm/memory_table.go +++ b/core/vm/memory_table.go @@ -89,21 +89,6 @@ func memoryCall(stack *Stack) (uint64, bool) { return y, false } -func memoryCallExpert(stack *Stack) (uint64, bool) { - x, overflow := calcMemSize64(stack.Back(7), stack.Back(8)) - if overflow { - return 0, true - } - y, overflow := calcMemSize64(stack.Back(5), stack.Back(6)) - if overflow { - return 0, true - } - if x > y { - return x, false - } - return y, false -} - func memoryDelegateCall(stack *Stack) (uint64, bool) { x, overflow := calcMemSize64(stack.Back(4), stack.Back(5)) if overflow { diff --git a/core/vm/opcodes.go b/core/vm/opcodes.go index a32119a0f4..c4e99b0669 100644 --- a/core/vm/opcodes.go +++ b/core/vm/opcodes.go @@ -217,11 +217,6 @@ const ( LOG4 ) -const ( - BALANCEMC = 0xcd - CALLEX = 0xcf -) - // 0xf0 range - closures. const ( CREATE OpCode = 0xf0 @@ -274,7 +269,6 @@ var opCodeToString = [256]string{ // 0x30 range - closure state. ADDRESS: "ADDRESS", BALANCE: "BALANCE", - BALANCEMC: "BALANCEMC", ORIGIN: "ORIGIN", CALLER: "CALLER", CALLVALUE: "CALLVALUE", @@ -401,7 +395,6 @@ var opCodeToString = [256]string{ // 0xf0 range - closures. CREATE: "CREATE", CALL: "CALL", - CALLEX: "CALLEX", RETURN: "RETURN", CALLCODE: "CALLCODE", DELEGATECALL: "DELEGATECALL", @@ -449,7 +442,6 @@ var stringToOp = map[string]OpCode{ "KECCAK256": KECCAK256, "ADDRESS": ADDRESS, "BALANCE": BALANCE, - "BALANCEMC": BALANCEMC, "ORIGIN": ORIGIN, "CALLER": CALLER, "CALLVALUE": CALLVALUE, @@ -565,7 +557,6 @@ var stringToOp = map[string]OpCode{ "CREATE": CREATE, "CREATE2": CREATE2, "CALL": CALL, - "CALLEX": CALLEX, "RETURN": RETURN, "CALLCODE": CALLCODE, "REVERT": REVERT, diff --git a/eth/api_debug_test.go b/eth/api_debug_test.go index 5ff163664e..a0fa2eaa5a 100644 --- a/eth/api_debug_test.go +++ b/eth/api_debug_test.go @@ -93,7 +93,7 @@ func TestAccountRange(t *testing.T) { m[addr] = true } } - root, _ := sdb.Commit(0, true, false) + root, _ := sdb.Commit(0, true) sdb, _ = state.New(root, statedb, nil) trie, err := statedb.OpenTrie(root) @@ -151,7 +151,7 @@ func TestEmptyAccountRange(t *testing.T) { st, _ = state.New(types.EmptyRootHash, statedb, nil) ) // Commit(although nothing to flush) and re-init the statedb - st.Commit(0, true, false) + st.Commit(0, true) st, _ = state.New(types.EmptyRootHash, statedb, nil) results := st.RawDump(&state.DumpConfig{ @@ -197,7 +197,7 @@ func TestStorageRangeAt(t *testing.T) { for _, entry := range storage { sdb.SetState(addr, *entry.Key, entry.Value) } - root, _ := sdb.Commit(0, false, false) + root, _ := sdb.Commit(0, false) sdb, _ = state.New(root, db, nil) // Check a few combinations of limit and start/end. diff --git a/eth/state_accessor.go b/eth/state_accessor.go index 15b14b559a..d5f87ce2ef 100644 --- a/eth/state_accessor.go +++ b/eth/state_accessor.go @@ -163,7 +163,7 @@ func (eth *Ethereum) hashState(ctx context.Context, block *types.Block, reexec u return nil, nil, fmt.Errorf("processing block %d failed: %v", current.NumberU64(), err) } // Finalize the state so any modifications are written to the trie - root, err := statedb.Commit(current.NumberU64(), eth.blockchain.Config().IsEIP158(current.Number()), true) + root, err := statedb.Commit(current.NumberU64(), eth.blockchain.Config().IsEIP158(current.Number())) if err != nil { return nil, nil, fmt.Errorf("stateAtBlock commit failed, number %d root %v: %w", current.NumberU64(), current.Root().Hex(), err) @@ -172,8 +172,9 @@ 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 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. + // Hold the state reference and also drop the parent state + // to prevent accumulating too many nodes in memory. + tdb.Reference(root, common.Hash{}) if parent != (common.Hash{}) { tdb.Dereference(parent) } diff --git a/eth/tracers/internal/tracetest/prestate_test.go b/eth/tracers/internal/tracetest/prestate_test.go index 36d8e35619..7f659d6f71 100644 --- a/eth/tracers/internal/tracetest/prestate_test.go +++ b/eth/tracers/internal/tracetest/prestate_test.go @@ -74,10 +74,6 @@ func TestPrestateWithDiffModeTracer(t *testing.T) { testPrestateDiffTracer("prestateTracer", "prestate_tracer_with_diff_mode", t) } -func TestPrestateWithDiffModeANTTracer(t *testing.T) { - testPrestateDiffTracer("prestateTracer", "prestate_tracer_ant", t) -} - func testPrestateDiffTracer(tracerName string, dirPath string, t *testing.T) { files, err := os.ReadDir(filepath.Join("testdata", dirPath)) if err != nil { @@ -108,16 +104,14 @@ func testPrestateDiffTracer(tracerName string, dirPath string, t *testing.T) { var ( signer = types.MakeSigner(test.Genesis.Config, new(big.Int).SetUint64(uint64(test.Context.Number)), uint64(test.Context.Time)) context = vm.BlockContext{ - CanTransfer: core.CanTransfer, - CanTransferMC: core.CanTransferMC, - Transfer: core.Transfer, - TransferMultiCoin: core.TransferMultiCoin, - Coinbase: test.Context.Miner, - BlockNumber: new(big.Int).SetUint64(uint64(test.Context.Number)), - Time: uint64(test.Context.Time), - Difficulty: (*big.Int)(test.Context.Difficulty), - GasLimit: uint64(test.Context.GasLimit), - BaseFee: test.Genesis.BaseFee, + CanTransfer: core.CanTransfer, + Transfer: core.Transfer, + Coinbase: test.Context.Miner, + BlockNumber: new(big.Int).SetUint64(uint64(test.Context.Number)), + Time: uint64(test.Context.Time), + Difficulty: (*big.Int)(test.Context.Difficulty), + GasLimit: uint64(test.Context.GasLimit), + BaseFee: test.Genesis.BaseFee, } state = tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc, false, rawdb.HashScheme) ) diff --git a/go.mod b/go.mod index 6f242937fc..97ac0c6033 100644 --- a/go.mod +++ b/go.mod @@ -1,10 +1,10 @@ module github.com/ava-labs/coreth -go 1.21.12 +go 1.22.8 require ( github.com/VictoriaMetrics/fastcache v1.12.1 - github.com/ava-labs/avalanchego v1.11.11 + github.com/ava-labs/avalanchego v1.11.13-rc.0 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 @@ -16,7 +16,7 @@ require ( github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46 github.com/google/uuid v1.6.0 github.com/gorilla/rpc v1.2.0 - github.com/gorilla/websocket v1.4.2 + github.com/gorilla/websocket v1.5.0 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-20240216141850-2abb0c79d3c4 @@ -33,16 +33,16 @@ require ( github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.12.0 github.com/status-im/keycard-go v0.2.0 - github.com/stretchr/testify v1.8.4 + github.com/stretchr/testify v1.9.0 github.com/tyler-smith/go-bip39 v1.1.0 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.22.0 + golang.org/x/crypto v0.26.0 golang.org/x/exp v0.0.0-20231127185646-65229373498e - golang.org/x/sync v0.7.0 - golang.org/x/sys v0.19.0 - golang.org/x/text v0.14.0 + golang.org/x/sync v0.8.0 + golang.org/x/sys v0.24.0 + golang.org/x/text v0.17.0 golang.org/x/time v0.3.0 google.golang.org/protobuf v1.34.2 gopkg.in/natefinch/lumberjack.v2 v2.0.0 @@ -55,7 +55,7 @@ require ( github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect github.com/btcsuite/btcd/btcutil v1.1.3 // indirect github.com/cenkalti/backoff/v4 v4.2.1 // indirect - github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cockroachdb/errors v1.9.1 // indirect github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect github.com/cockroachdb/pebble v0.0.0-20230928194634-aa077af62593 // indirect @@ -99,12 +99,12 @@ require ( github.com/prometheus/common v0.42.0 // indirect github.com/prometheus/procfs v0.10.1 // indirect github.com/rivo/uniseg v0.2.0 // indirect - github.com/rogpeppe/go-internal v1.10.0 // indirect + github.com/rogpeppe/go-internal v1.12.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/spf13/afero v1.8.2 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/subosito/gotenv v1.3.0 // indirect - github.com/supranational/blst v0.3.11 // indirect + github.com/supranational/blst v0.3.13 // indirect github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a // indirect github.com/tklauser/go-sysconf v0.3.12 // indirect github.com/tklauser/numcpus v0.6.1 // indirect @@ -120,12 +120,12 @@ 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.24.0 // indirect - golang.org/x/term v0.19.0 // indirect + golang.org/x/net v0.28.0 // indirect + golang.org/x/term v0.23.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 - google.golang.org/grpc v1.62.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240604185151-ef581f913117 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240827150818-7e3bb234dfed // indirect + google.golang.org/grpc v1.66.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index de26108af3..88d27d9e50 100644 --- a/go.sum +++ b/go.sum @@ -54,8 +54,8 @@ github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= -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/avalanchego v1.11.13-rc.0 h1:yxhZbLlZ+29wR6W29rz0QryZxk35iyMNr1IhX9NPFCU= +github.com/ava-labs/avalanchego v1.11.13-rc.0/go.mod h1:86tO6F1FT8emclUwdQ2WCwAtAerqjm5A4IbV6XxNUyM= 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= @@ -90,8 +90,9 @@ github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyY github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/cp v0.1.0 h1:SE+dxFebS7Iik5LK0tsi1k9ZCxEaFX4AjQmoyA+1dJk= github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= -github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/logex v1.2.0/go.mod h1:9+9sk7u7pGNWYMkh0hdiL++6OeibzJccyQU4p4MedaY= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= @@ -219,8 +220,8 @@ github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69 github.com/gogo/status v1.1.0/go.mod h1:BFv9nrluPLmrS0EmGVvLaPNmRosr9KapBYd5/hpY1WM= github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/glog v1.2.0 h1:uCdmnmatrKCgMBlM4rMuJZWOkPDqdbZPnrMXDY4gI68= -github.com/golang/glog v1.2.0/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= +github.com/golang/glog v1.2.1 h1:OptwRhECazUx5ix5TTWC3EZhsZEHWcYWY4FQHTIubm4= +github.com/golang/glog v1.2.1/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -299,8 +300,8 @@ github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORR github.com/gorilla/rpc v1.2.0 h1:WvvdC2lNeT1SP32zrIce5l0ECBfbAlmrmSBsuc57wfk= github.com/gorilla/rpc v1.2.0/go.mod h1:V4h9r+4sF5HnzqbwIez0fKSpANP0zlYd3qR7p36jkTQ= github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= -github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= +github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms= @@ -467,8 +468,8 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= -github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= @@ -510,12 +511,12 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.3.0 h1:mjC+YW8QpAdXibNi+vNWgzmgBH4+5l5dCXv8cNysBLI= github.com/subosito/gotenv v1.3.0/go.mod h1:YzJjq/33h7nrwdY+iHMhEOEEbW0ovIz0tB6t6PwAXzs= -github.com/supranational/blst v0.3.11 h1:LyU6FolezeWAhvQk0k6O/d49jqgO52MSDDfYgbeoEm4= -github.com/supranational/blst v0.3.11/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= +github.com/supranational/blst v0.3.13 h1:AYeSxdOMacwu7FBmpfloBz5pbFXDmJL33RuwnKtmTjk= +github.com/supranational/blst v0.3.13/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a h1:1ur3QoCqvE5fl+nylMaIr9PVV1w343YRDtsy+Rwu7XI= github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48= @@ -600,8 +601,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.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= -golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= +golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= +golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= 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= @@ -684,8 +685,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.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= -golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= +golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= +golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= 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= @@ -707,8 +708,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.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= -golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.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= @@ -775,12 +776,12 @@ 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.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= -golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= +golang.org/x/sys v0.24.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.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q= -golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk= +golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU= +golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk= 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= @@ -791,8 +792,8 @@ golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= +golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -925,12 +926,10 @@ google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= -google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80 h1:KAeGQVN3M9nD0/bQXnr/ClcEMJ968gUXJQ9pwfSynuQ= -google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80/go.mod h1:cc8bqMqtv9gMOr0zHg2Vzff5ULhhL2IXP4sbcn32Dro= -google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80 h1:Lj5rbfG876hIAYFjqiJnPHfhXbv+nzTWfm04Fg/XSVU= -google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80/go.mod h1:4jWUdICTdgc3Ibxmr8nAJiiLHwQBY0UI0XZcEMaFKaA= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 h1:AjyfHzEPEFp/NpvfN5g+KDla3EMojjhRVZc1i7cj+oM= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80/go.mod h1:PAREbraiVEVGVdTZsVWjSbbTtSyGbAgIIvni8a8CD5s= +google.golang.org/genproto/googleapis/api v0.0.0-20240604185151-ef581f913117 h1:+rdxYoE3E5htTEWIe15GlN6IfvbURM//Jt0mmkmm6ZU= +google.golang.org/genproto/googleapis/api v0.0.0-20240604185151-ef581f913117/go.mod h1:OimBR/bc1wPO9iV4NC2bpyjy3VnAwZh5EBPQdtaE5oo= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240827150818-7e3bb234dfed h1:J6izYgfBXAI3xTKLgxzTmUltdYaLsuBxFCgDHWJ/eXg= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240827150818-7e3bb234dfed/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= @@ -949,8 +948,8 @@ google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.62.0 h1:HQKZ/fa1bXkX1oFOvSjmZEUL8wLSaZTjCcLAlmZRtdk= -google.golang.org/grpc v1.62.0/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE= +google.golang.org/grpc v1.66.0 h1:DibZuoBznOxbDQxRINckZcUvnCEvrW9pcWIE2yF9r1c= +google.golang.org/grpc v1.66.0/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= diff --git a/internal/ethapi/api_test.go b/internal/ethapi/api_test.go index d4dde119a3..87a7176026 100644 --- a/internal/ethapi/api_test.go +++ b/internal/ethapi/api_test.go @@ -786,6 +786,7 @@ func TestEstimateGas(t *testing.T) { func TestCall(t *testing.T) { // Enable BLOBHASH opcode in Cancun cfg := *params.TestChainConfig + cfg.ShanghaiTime = utils.NewUint64(0) cfg.CancunTime = utils.NewUint64(0) t.Parallel() // Initialize test accounts @@ -1788,6 +1789,7 @@ func TestRPCGetBlockOrHeader(t *testing.T) { func setupReceiptBackend(t *testing.T, genBlocks int) (*testBackend, []common.Hash) { config := *params.TestChainConfig + config.ShanghaiTime = new(uint64) config.CancunTime = new(uint64) var ( acc1Key, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a") diff --git a/miner/worker.go b/miner/worker.go index 5bd709ebb9..ec2bd60fca 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -293,6 +293,7 @@ func (w *worker) commitTransaction(env *environment, tx *types.Transaction, coin } env.txs = append(env.txs, tx) env.receipts = append(env.receipts, receipt) + env.size += tx.Size() return receipt.Logs, nil } diff --git a/multicoin.bin b/multicoin.bin deleted file mode 100644 index 5b0110f133..0000000000 --- a/multicoin.bin +++ /dev/null @@ -1,4 +0,0 @@ - -======= multicoin.sol:MultiCoin ======= -Binary of the runtime part: -0x7300000000000000000000000000000000000000003014608060405260043610603d5760003560e01c80631e010439146042578063b6510bb314606e575b600080fd5b605c60048036036020811015605657600080fd5b503560b1565b60408051918252519081900360200190f35b818015607957600080fd5b5060af60048036036080811015608e57600080fd5b506001600160a01b03813516906020810135906040810135906060013560b6565b005b30cd90565b836001600160a01b031681836108fc8690811502906040516000604051808303818888878c8acf9550505050505015801560f4573d6000803e3d6000fd5b505050505056fea26469706673582212201eebce970fe3f5cb96bf8ac6ba5f5c133fc2908ae3dcd51082cfee8f583429d064736f6c634300060a0033 diff --git a/multicoin.sol b/multicoin.sol deleted file mode 100644 index 97d9cdd334..0000000000 --- a/multicoin.sol +++ /dev/null @@ -1,11 +0,0 @@ -pragma solidity >=0.6.0; - -library MultiCoin { - function transfer(address payable recipient, uint256 amount, uint256 coinid, uint256 amount2) public { - recipient.transferex(amount, coinid, amount2); - } - - function getBalance(uint256 coinid) public view returns (uint256) { - return address(this).balancemc(coinid); - } -} diff --git a/params/config.go b/params/config.go index aa6e3406f4..c8211685d4 100644 --- a/params/config.go +++ b/params/config.go @@ -28,7 +28,6 @@ package params import ( "encoding/json" - "errors" "fmt" "math/big" @@ -46,8 +45,6 @@ var ( AvalancheFujiChainID = big.NewInt(43113) // AvalancheLocalChainID ... AvalancheLocalChainID = big.NewInt(43112) - - errNonGenesisForkByHeight = errors.New("coreth only supports forking by height at the genesis block") ) var ( @@ -65,6 +62,8 @@ var ( PetersburgBlock: big.NewInt(0), IstanbulBlock: big.NewInt(0), MuirGlacierBlock: big.NewInt(0), + BerlinBlock: big.NewInt(0), + LondonBlock: big.NewInt(0), NetworkUpgrades: NetworkUpgrades{ ApricotPhase1BlockTimestamp: utils.NewUint64(0), ApricotPhase2BlockTimestamp: utils.NewUint64(0), @@ -155,6 +154,7 @@ var ( PetersburgBlock: big.NewInt(0), IstanbulBlock: big.NewInt(0), MuirGlacierBlock: big.NewInt(0), + BerlinBlock: big.NewInt(0), NetworkUpgrades: NetworkUpgrades{ ApricotPhase1BlockTimestamp: utils.NewUint64(0), ApricotPhase2BlockTimestamp: utils.NewUint64(0), @@ -185,6 +185,8 @@ var ( PetersburgBlock: big.NewInt(0), IstanbulBlock: big.NewInt(0), MuirGlacierBlock: big.NewInt(0), + BerlinBlock: big.NewInt(0), + LondonBlock: big.NewInt(0), NetworkUpgrades: NetworkUpgrades{ ApricotPhase1BlockTimestamp: utils.NewUint64(0), ApricotPhase2BlockTimestamp: utils.NewUint64(0), @@ -215,6 +217,8 @@ var ( PetersburgBlock: big.NewInt(0), IstanbulBlock: big.NewInt(0), MuirGlacierBlock: big.NewInt(0), + BerlinBlock: big.NewInt(0), + LondonBlock: big.NewInt(0), NetworkUpgrades: NetworkUpgrades{ ApricotPhase1BlockTimestamp: utils.NewUint64(0), ApricotPhase2BlockTimestamp: utils.NewUint64(0), @@ -245,6 +249,8 @@ var ( PetersburgBlock: big.NewInt(0), IstanbulBlock: big.NewInt(0), MuirGlacierBlock: big.NewInt(0), + BerlinBlock: big.NewInt(0), + LondonBlock: big.NewInt(0), NetworkUpgrades: NetworkUpgrades{ ApricotPhase1BlockTimestamp: utils.NewUint64(0), ApricotPhase2BlockTimestamp: utils.NewUint64(0), @@ -275,6 +281,8 @@ var ( PetersburgBlock: big.NewInt(0), IstanbulBlock: big.NewInt(0), MuirGlacierBlock: big.NewInt(0), + BerlinBlock: big.NewInt(0), + LondonBlock: big.NewInt(0), NetworkUpgrades: NetworkUpgrades{ ApricotPhase1BlockTimestamp: utils.NewUint64(0), ApricotPhase2BlockTimestamp: utils.NewUint64(0), @@ -305,6 +313,8 @@ var ( PetersburgBlock: big.NewInt(0), IstanbulBlock: big.NewInt(0), MuirGlacierBlock: big.NewInt(0), + BerlinBlock: big.NewInt(0), + LondonBlock: big.NewInt(0), NetworkUpgrades: NetworkUpgrades{ ApricotPhase1BlockTimestamp: utils.NewUint64(0), ApricotPhase2BlockTimestamp: utils.NewUint64(0), @@ -335,6 +345,8 @@ var ( PetersburgBlock: big.NewInt(0), IstanbulBlock: big.NewInt(0), MuirGlacierBlock: big.NewInt(0), + BerlinBlock: big.NewInt(0), + LondonBlock: big.NewInt(0), NetworkUpgrades: NetworkUpgrades{ ApricotPhase1BlockTimestamp: utils.NewUint64(0), ApricotPhase2BlockTimestamp: utils.NewUint64(0), @@ -365,6 +377,8 @@ var ( PetersburgBlock: big.NewInt(0), IstanbulBlock: big.NewInt(0), MuirGlacierBlock: big.NewInt(0), + BerlinBlock: big.NewInt(0), + LondonBlock: big.NewInt(0), NetworkUpgrades: NetworkUpgrades{ ApricotPhase1BlockTimestamp: utils.NewUint64(0), ApricotPhase2BlockTimestamp: utils.NewUint64(0), @@ -395,6 +409,8 @@ var ( PetersburgBlock: big.NewInt(0), IstanbulBlock: big.NewInt(0), MuirGlacierBlock: big.NewInt(0), + BerlinBlock: big.NewInt(0), + LondonBlock: big.NewInt(0), NetworkUpgrades: NetworkUpgrades{ ApricotPhase1BlockTimestamp: utils.NewUint64(0), ApricotPhase2BlockTimestamp: utils.NewUint64(0), @@ -425,6 +441,9 @@ var ( PetersburgBlock: big.NewInt(0), IstanbulBlock: big.NewInt(0), MuirGlacierBlock: big.NewInt(0), + BerlinBlock: big.NewInt(0), + LondonBlock: big.NewInt(0), + ShanghaiTime: utils.NewUint64(0), NetworkUpgrades: NetworkUpgrades{ ApricotPhase1BlockTimestamp: utils.NewUint64(0), ApricotPhase2BlockTimestamp: utils.NewUint64(0), @@ -455,6 +474,10 @@ var ( PetersburgBlock: big.NewInt(0), IstanbulBlock: big.NewInt(0), MuirGlacierBlock: big.NewInt(0), + BerlinBlock: big.NewInt(0), + LondonBlock: big.NewInt(0), + ShanghaiTime: utils.NewUint64(0), + CancunTime: utils.NewUint64(0), NetworkUpgrades: NetworkUpgrades{ ApricotPhase1BlockTimestamp: utils.NewUint64(0), ApricotPhase2BlockTimestamp: utils.NewUint64(0), @@ -497,11 +520,14 @@ type ChainConfig struct { PetersburgBlock *big.Int `json:"petersburgBlock,omitempty"` // Petersburg switch block (nil = same as Constantinople) IstanbulBlock *big.Int `json:"istanbulBlock,omitempty"` // Istanbul switch block (nil = no fork, 0 = already on istanbul) MuirGlacierBlock *big.Int `json:"muirGlacierBlock,omitempty"` // Eip-2384 (bomb delay) switch block (nil = no fork, 0 = already activated) + BerlinBlock *big.Int `json:"berlinBlock,omitempty"` // Berlin switch block (nil = no fork, 0 = already on berlin) + LondonBlock *big.Int `json:"londonBlock,omitempty"` // London switch block (nil = no fork, 0 = already on london) // Fork scheduling was switched from blocks to timestamps here - CancunTime *uint64 `json:"cancunTime,omitempty"` // Cancun switch time (nil = no fork, 0 = already activated) - VerkleTime *uint64 `json:"verkleTime,omitempty"` // Verkle switch time (nil = no fork, 0 = already on verkle) + ShanghaiTime *uint64 `json:"shanghaiTime,omitempty"` // Shanghai switch time (nil = no fork, 0 = already on shanghai) + CancunTime *uint64 `json:"cancunTime,omitempty"` // Cancun switch time (nil = no fork, 0 = already activated) + VerkleTime *uint64 `json:"verkleTime,omitempty"` // Verkle switch time (nil = no fork, 0 = already on verkle) NetworkUpgrades // Config for timestamps that enable network upgrades. Skip encoding/decoding directly into ChainConfig. @@ -606,14 +632,29 @@ func (c *ChainConfig) IsIstanbul(num *big.Int) bool { return isBlockForked(c.IstanbulBlock, num) } -// IsCancun returns whether time is either equal to the Cancun fork time or greater. +// IsBerlin returns whether num is either equal to the Berlin fork block or greater. +func (c *ChainConfig) IsBerlin(num *big.Int) bool { + return isBlockForked(c.BerlinBlock, num) +} + +// IsLondon returns whether num is either equal to the London fork block or greater. +func (c *ChainConfig) IsLondon(num *big.Int) bool { + return isBlockForked(c.LondonBlock, num) +} + +// IsShanghai returns whether time is either equal to the Shanghai fork time or greater. +func (c *ChainConfig) IsShanghai(num *big.Int, time uint64) bool { + return c.IsLondon(num) && isTimestampForked(c.ShanghaiTime, time) +} + +// IsCancun returns whether num is either equal to the Cancun fork time or greater. func (c *ChainConfig) IsCancun(num *big.Int, time uint64) bool { - return isTimestampForked(c.CancunTime, time) + return c.IsLondon(num) && isTimestampForked(c.CancunTime, time) } -// IsVerkle returns whether time is either equal to the Verkle fork time or greater. +// IsVerkle returns whether num is either equal to the Verkle fork time or greater. func (c *ChainConfig) IsVerkle(num *big.Int, time uint64) bool { - return isTimestampForked(c.VerkleTime, time) + return c.IsLondon(num) && isTimestampForked(c.VerkleTime, time) } // CheckCompatible checks whether scheduled fork transitions have been imported @@ -662,6 +703,9 @@ func (c *ChainConfig) CheckConfigForkOrder() error { {name: "petersburgBlock", block: c.PetersburgBlock}, {name: "istanbulBlock", block: c.IstanbulBlock}, {name: "muirGlacierBlock", block: c.MuirGlacierBlock, optional: true}, + {name: "berlinBlock", block: c.BerlinBlock}, + {name: "londonBlock", block: c.LondonBlock}, + {name: "shanghaiTime", timestamp: c.ShanghaiTime}, {name: "cancunTime", timestamp: c.CancunTime, optional: true}, {name: "verkleTime", timestamp: c.VerkleTime, optional: true}, } @@ -691,9 +735,6 @@ func (c *ChainConfig) CheckConfigForkOrder() error { func checkForks(forks []fork, blockFork bool) error { lastFork := fork{} for _, cur := range forks { - if blockFork && cur.block != nil && common.Big0.Cmp(cur.block) != 0 { - return errNonGenesisForkByHeight - } if lastFork.name != "" { switch { // Non-optional forks must all be present in the chain config up to the last defined fork @@ -775,6 +816,15 @@ func (c *ChainConfig) checkCompatible(newcfg *ChainConfig, headNumber *big.Int, if isForkBlockIncompatible(c.MuirGlacierBlock, newcfg.MuirGlacierBlock, headNumber) { return newBlockCompatError("Muir Glacier fork block", c.MuirGlacierBlock, newcfg.MuirGlacierBlock) } + if isForkBlockIncompatible(c.BerlinBlock, newcfg.BerlinBlock, headNumber) { + return newBlockCompatError("Berlin fork block", c.BerlinBlock, newcfg.BerlinBlock) + } + if isForkBlockIncompatible(c.LondonBlock, newcfg.LondonBlock, headNumber) { + return newBlockCompatError("London fork block", c.LondonBlock, newcfg.LondonBlock) + } + if isForkTimestampIncompatible(c.ShanghaiTime, newcfg.ShanghaiTime, headTimestamp) { + return newTimestampCompatError("Shanghai fork timestamp", c.ShanghaiTime, newcfg.ShanghaiTime) + } if isForkTimestampIncompatible(c.CancunTime, newcfg.CancunTime, headTimestamp) { return newTimestampCompatError("Cancun fork timestamp", c.CancunTime, newcfg.CancunTime) } diff --git a/params/config_extra.go b/params/config_extra.go index 36fc2c98ba..abe6ed7bb0 100644 --- a/params/config_extra.go +++ b/params/config_extra.go @@ -40,6 +40,27 @@ type AvalancheContext struct { // code in place of their Ethereum counterparts. The original Ethereum names // should be restored for maintainability. func (c *ChainConfig) SetEthUpgrades() { + if c.ChainID != nil && AvalancheFujiChainID.Cmp(c.ChainID) == 0 { + c.BerlinBlock = big.NewInt(184985) // https://testnet.snowtrace.io/block/184985?chainid=43113, AP2 activation block + c.LondonBlock = big.NewInt(805078) // https://testnet.snowtrace.io/block/805078?chainid=43113, AP3 activation block + } else if c.ChainID != nil && AvalancheMainnetChainID.Cmp(c.ChainID) == 0 { + c.BerlinBlock = big.NewInt(1640340) // https://snowtrace.io/block/1640340?chainid=43114, AP2 activation block + c.LondonBlock = big.NewInt(3308552) // https://snowtrace.io/block/3308552?chainid=43114, AP3 activation block + } else { + // In testing or local networks, we only support enabling Berlin and London prior + // to the initially active time. This is likely to correspond to an intended block + // number of 0 as well. + initiallyActive := uint64(upgrade.InitiallyActiveTime.Unix()) + if c.ApricotPhase2BlockTimestamp != nil && *c.ApricotPhase2BlockTimestamp <= initiallyActive && c.BerlinBlock == nil { + c.BerlinBlock = big.NewInt(0) + } + if c.ApricotPhase3BlockTimestamp != nil && *c.ApricotPhase3BlockTimestamp <= initiallyActive && c.LondonBlock == nil { + c.LondonBlock = big.NewInt(0) + } + } + if c.DurangoBlockTimestamp != nil { + c.ShanghaiTime = utils.NewUint64(*c.DurangoBlockTimestamp) + } if c.EtnaTimestamp != nil { c.CancunTime = utils.NewUint64(*c.EtnaTimestamp) } @@ -158,7 +179,7 @@ func (c *ChainConfig) ToWithUpgradesJSON() *ChainConfigWithUpgradesJSON { } func GetChainConfig(agoUpgrade upgrade.Config, chainID *big.Int) *ChainConfig { - return &ChainConfig{ + c := &ChainConfig{ ChainID: chainID, HomesteadBlock: big.NewInt(0), DAOForkBlock: big.NewInt(0), @@ -173,6 +194,7 @@ func GetChainConfig(agoUpgrade upgrade.Config, chainID *big.Int) *ChainConfig { MuirGlacierBlock: big.NewInt(0), NetworkUpgrades: getNetworkUpgrades(agoUpgrade), } + return c } func (r *Rules) PredicatersExist() bool { diff --git a/peer/network.go b/peer/network.go index 32c2b63bf6..7739e279bc 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 856ab8847a..165537bae7 100644 --- a/peer/stats/stats.go +++ b/peer/stats/stats.go @@ -9,7 +9,7 @@ import ( "github.com/ava-labs/coreth/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() diff --git a/plugin/evm/block.go b/plugin/evm/block.go index 4b6ac6824a..a8d9084464 100644 --- a/plugin/evm/block.go +++ b/plugin/evm/block.go @@ -148,12 +148,9 @@ func (b *Block) Accept(context.Context) error { // Call Accept for relevant precompile logs. Note we do this prior to // calling Accept on the blockChain so any side effects (eg warp signatures) - // take place before the accepted log is emitted to subscribers. Use of the - // sharedMemoryWriter ensures shared memory requests generated by - // precompiles are committed atomically with the vm's lastAcceptedKey. + // take place before the accepted log is emitted to subscribers. rules := b.vm.chainConfig.Rules(b.ethBlock.Number(), b.ethBlock.Timestamp()) - sharedMemoryWriter := NewSharedMemoryWriter() - if err := b.handlePrecompileAccept(rules, sharedMemoryWriter); err != nil { + if err := b.handlePrecompileAccept(rules); err != nil { return err } if err := vm.blockChain.Accept(b.ethBlock); err != nil { @@ -177,23 +174,20 @@ func (b *Block) Accept(context.Context) error { return err } // Get pending operations on the vm's versionDB so we can apply them atomically - // with the shared memory requests. + // with the shared memory changes. vdbBatch, err := b.vm.db.CommitBatch() if err != nil { return fmt.Errorf("could not create commit batch processing block[%s]: %w", b.ID(), err) } - // Apply any shared memory requests that accumulated from processing the logs - // of the accepted block (generated by precompiles) atomically with other pending - // changes to the vm's versionDB. - return atomicState.Accept(vdbBatch, sharedMemoryWriter.requests) + // Apply any shared memory changes atomically with other pending changes to + // the vm's versionDB. + return atomicState.Accept(vdbBatch, nil) } // handlePrecompileAccept calls Accept on any logs generated with an active precompile address that implements // contract.Accepter -// This function assumes that the Accept function will ONLY operate on state maintained in the VM's versiondb. -// This ensures that any DB operations are performed atomically with marking the block as accepted. -func (b *Block) handlePrecompileAccept(rules params.Rules, sharedMemoryWriter *sharedMemoryWriter) error { +func (b *Block) handlePrecompileAccept(rules params.Rules) error { // Short circuit early if there are no precompile accepters to execute if len(rules.AccepterPrecompiles) == 0 { return nil @@ -207,9 +201,8 @@ func (b *Block) handlePrecompileAccept(rules params.Rules, sharedMemoryWriter *s return fmt.Errorf("failed to fetch receipts for accepted block with non-empty root hash (%s) (Block: %s, Height: %d)", b.ethBlock.ReceiptHash(), b.ethBlock.Hash(), b.ethBlock.NumberU64()) } acceptCtx := &precompileconfig.AcceptContext{ - SnowCtx: b.vm.ctx, - SharedMemory: sharedMemoryWriter, - Warp: b.vm.warpBackend, + SnowCtx: b.vm.ctx, + Warp: b.vm.warpBackend, } for _, receipt := range receipts { for logIdx, log := range receipt.Logs { @@ -334,7 +327,7 @@ func (b *Block) verify(predicateContext *precompileconfig.PredicateContext, writ // If the chain is still bootstrapping, we can assume that all blocks we are verifying have // been accepted by the network (so the predicate was validated by the network when the // block was originally verified). - if b.vm.bootstrapped { + if b.vm.bootstrapped.Get() { if err := b.verifyPredicates(predicateContext); err != nil { return fmt.Errorf("failed to verify predicates: %w", err) } @@ -405,7 +398,7 @@ func (b *Block) verifyUTXOsPresent() error { return nil } - if !b.vm.bootstrapped { + if !b.vm.bootstrapped.Get() { return nil } diff --git a/plugin/evm/gossip_test.go b/plugin/evm/gossip_test.go index 2db67a6f4b..8ed7aee3cf 100644 --- a/plugin/evm/gossip_test.go +++ b/plugin/evm/gossip_test.go @@ -189,7 +189,7 @@ func TestGossipSubscribe(t *testing.T) { defer gossipTxPool.lock.RUnlock() for i, tx := range ethTxs { - require.Truef(gossipTxPool.bloom.Has(&GossipEthTx{Tx: tx}), "expected tx[%d] to be in bloom filter", i) + assert.Truef(c, gossipTxPool.bloom.Has(&GossipEthTx{Tx: tx}), "expected tx[%d] to be in bloom filter", i) } }, 30*time.Second, diff --git a/plugin/evm/import_tx.go b/plugin/evm/import_tx.go index b67b834673..b447a717ee 100644 --- a/plugin/evm/import_tx.go +++ b/plugin/evm/import_tx.go @@ -221,7 +221,7 @@ func (utx *UnsignedImportTx) SemanticVerify( return fmt.Errorf("import tx contained mismatched number of inputs/credentials (%d vs. %d)", len(utx.ImportedInputs), len(stx.Creds)) } - if !vm.bootstrapped { + if !vm.bootstrapped.Get() { // Allow for force committing during bootstrapping return nil } diff --git a/plugin/evm/prestate_tracer_test.go b/plugin/evm/prestate_tracer_test.go new file mode 100644 index 0000000000..097155a8ae --- /dev/null +++ b/plugin/evm/prestate_tracer_test.go @@ -0,0 +1,129 @@ +// (c) 2024, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package evm + +import ( + "encoding/json" + "math/big" + "os" + "path/filepath" + "strings" + "testing" + "unicode" + + "github.com/ava-labs/coreth/core" + "github.com/ava-labs/coreth/core/rawdb" + "github.com/ava-labs/coreth/core/types" + "github.com/ava-labs/coreth/core/vm" + "github.com/ava-labs/coreth/eth/tracers" + "github.com/ava-labs/coreth/tests" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/math" +) + +func TestPrestateWithDiffModeANTTracer(t *testing.T) { + testPrestateDiffTracer("prestateTracer", "prestate_tracer_ant", t) +} + +// testPrestateDiffTracer is adapted from the original testPrestateDiffTracer in +// eth/tracers/internal/tracetest/prestate_test.go. +func testPrestateDiffTracer(tracerName string, dirPath string, t *testing.T) { + files, err := os.ReadDir(filepath.Join("testdata", dirPath)) + if err != nil { + t.Fatalf("failed to retrieve tracer test suite: %v", err) + } + for _, file := range files { + if !strings.HasSuffix(file.Name(), ".json") { + continue + } + file := file // capture range variable + t.Run(camel(strings.TrimSuffix(file.Name(), ".json")), func(t *testing.T) { + t.Parallel() + + var ( + test = new(testcase) + tx = new(types.Transaction) + ) + // Call tracer test found, read if from disk + if blob, err := os.ReadFile(filepath.Join("testdata", dirPath, file.Name())); err != nil { + t.Fatalf("failed to read testcase: %v", err) + } else if err := json.Unmarshal(blob, test); err != nil { + t.Fatalf("failed to parse testcase: %v", err) + } + if err := tx.UnmarshalBinary(common.FromHex(test.Input)); err != nil { + t.Fatalf("failed to parse testcase input: %v", err) + } + // 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)) + context = vm.BlockContext{ + CanTransfer: core.CanTransfer, + CanTransferMC: core.CanTransferMC, + Transfer: core.Transfer, + TransferMultiCoin: core.TransferMultiCoin, + Coinbase: test.Context.Miner, + BlockNumber: new(big.Int).SetUint64(uint64(test.Context.Number)), + Time: uint64(test.Context.Time), + Difficulty: (*big.Int)(test.Context.Difficulty), + GasLimit: uint64(test.Context.GasLimit), + BaseFee: test.Genesis.BaseFee, + } + state = tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc, false, rawdb.HashScheme) + ) + 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) + } + 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) + } + // Retrieve the trace result and compare against the expected + res, err := tracer.GetResult() + if err != nil { + t.Fatalf("failed to retrieve trace result: %v", err) + } + want, err := json.Marshal(test.Result) + if err != nil { + t.Fatalf("failed to marshal test: %v", err) + } + if string(want) != string(res) { + t.Fatalf("trace mismatch\n have: %v\n want: %v\n", string(res), string(want)) + } + }) + } +} + +// testcase defines a single test to check the stateDiff tracer against. +type testcase struct { + Genesis *core.Genesis `json:"genesis"` + Context *callContext `json:"context"` + Input string `json:"input"` + TracerConfig json.RawMessage `json:"tracerConfig"` + Result interface{} `json:"result"` +} + +type callContext struct { + Number math.HexOrDecimal64 `json:"number"` + Difficulty *math.HexOrDecimal256 `json:"difficulty"` + Time math.HexOrDecimal64 `json:"timestamp"` + GasLimit math.HexOrDecimal64 `json:"gasLimit"` + Miner common.Address `json:"miner"` +} + +// camel converts a snake cased input string into a camel cased output. +func camel(str string) string { + pieces := strings.Split(str, "_") + for i := 1; i < len(pieces); i++ { + pieces[i] = string(unicode.ToUpper(rune(pieces[i][0]))) + pieces[i][1:] + } + return strings.Join(pieces, "") +} diff --git a/plugin/evm/shared_memory_writer.go b/plugin/evm/shared_memory_writer.go deleted file mode 100644 index 7e6de6f862..0000000000 --- a/plugin/evm/shared_memory_writer.go +++ /dev/null @@ -1,37 +0,0 @@ -// (c) 2023, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package evm - -import ( - "github.com/ava-labs/avalanchego/chains/atomic" - "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/coreth/precompile/precompileconfig" -) - -var _ precompileconfig.SharedMemoryWriter = &sharedMemoryWriter{} - -type sharedMemoryWriter struct { - requests map[ids.ID]*atomic.Requests -} - -func NewSharedMemoryWriter() *sharedMemoryWriter { - return &sharedMemoryWriter{ - requests: make(map[ids.ID]*atomic.Requests), - } -} - -func (s *sharedMemoryWriter) AddSharedMemoryRequests(chainID ids.ID, requests *atomic.Requests) { - mergeAtomicOpsToMap(s.requests, chainID, requests) -} - -// mergeAtomicOps merges atomic ops for [chainID] represented by [requests] -// to the [output] map provided. -func mergeAtomicOpsToMap(output map[ids.ID]*atomic.Requests, chainID ids.ID, requests *atomic.Requests) { - if request, exists := output[chainID]; exists { - request.PutRequests = append(request.PutRequests, requests.PutRequests...) - request.RemoveRequests = append(request.RemoveRequests, requests.RemoveRequests...) - } else { - output[chainID] = requests - } -} diff --git a/plugin/evm/syncervm_test.go b/plugin/evm/syncervm_test.go index 3ecfcb517c..bba4663153 100644 --- a/plugin/evm/syncervm_test.go +++ b/plugin/evm/syncervm_test.go @@ -554,7 +554,7 @@ func testSyncerVM(t *testing.T, vmSetup *syncVMSetup, test syncTest) { // check we can transition to [NormalOp] state and continue to process blocks. require.NoError(syncerVM.SetState(context.Background(), snow.NormalOp)) - require.True(syncerVM.bootstrapped) + require.True(syncerVM.bootstrapped.Get()) // check atomic memory was synced properly syncerSharedMemories := newSharedMemories(syncerAtomicMemory, syncerVM.ctx.ChainID, syncerVM.ctx.XChainID) diff --git a/eth/tracers/internal/tracetest/testdata/prestate_tracer_ant/sload.json b/plugin/evm/testdata/prestate_tracer_ant/sload.json similarity index 100% rename from eth/tracers/internal/tracetest/testdata/prestate_tracer_ant/sload.json rename to plugin/evm/testdata/prestate_tracer_ant/sload.json diff --git a/plugin/evm/tx.go b/plugin/evm/tx.go index 00f7de4bce..9361f71976 100644 --- a/plugin/evm/tx.go +++ b/plugin/evm/tx.go @@ -288,3 +288,14 @@ func mergeAtomicOps(txs []*Tx) (map[ids.ID]*atomic.Requests, error) { } return output, nil } + +// mergeAtomicOps merges atomic ops for [chainID] represented by [requests] +// to the [output] map provided. +func mergeAtomicOpsToMap(output map[ids.ID]*atomic.Requests, chainID ids.ID, requests *atomic.Requests) { + if request, exists := output[chainID]; exists { + request.PutRequests = append(request.PutRequests, requests.PutRequests...) + request.RemoveRequests = append(request.RemoveRequests, requests.RemoveRequests...) + } else { + output[chainID] = requests + } +} diff --git a/plugin/evm/tx_gossip_test.go b/plugin/evm/tx_gossip_test.go index 6024747180..af544e9415 100644 --- a/plugin/evm/tx_gossip_test.go +++ b/plugin/evm/tx_gossip_test.go @@ -21,7 +21,6 @@ import ( "github.com/ava-labs/avalanchego/snow/engine/common" "github.com/ava-labs/avalanchego/snow/engine/enginetest" "github.com/ava-labs/avalanchego/snow/validators" - "github.com/ava-labs/avalanchego/snow/validators/validatorstest" agoUtils "github.com/ava-labs/avalanchego/utils" "github.com/ava-labs/avalanchego/utils/crypto/secp256k1" "github.com/ava-labs/avalanchego/utils/logging" @@ -43,7 +42,7 @@ func TestEthTxGossip(t *testing.T) { require := require.New(t) ctx := context.Background() snowCtx := utils.TestSnowContext() - validatorState := &validatorstest.State{} + validatorState := utils.NewTestValidatorState() snowCtx.ValidatorState = validatorState pk, err := secp256k1.NewPrivateKey() @@ -169,15 +168,10 @@ func TestAtomicTxGossip(t *testing.T) { ctx := context.Background() snowCtx := utils.TestSnowContext() snowCtx.AVAXAssetID = ids.GenerateTestID() - snowCtx.XChainID = ids.GenerateTestID() - validatorState := &validatorstest.State{ - GetSubnetIDF: func(context.Context, ids.ID) (ids.ID, error) { - return ids.Empty, nil - }, - } + validatorState := utils.NewTestValidatorState() snowCtx.ValidatorState = validatorState memory := atomic.NewMemory(memdb.New()) - snowCtx.SharedMemory = memory.NewSharedMemory(ids.Empty) + snowCtx.SharedMemory = memory.NewSharedMemory(snowCtx.ChainID) pk, err := secp256k1.NewPrivateKey() require.NoError(err) @@ -308,14 +302,6 @@ func TestEthTxPushGossipOutbound(t *testing.T) { require := require.New(t) ctx := context.Background() snowCtx := utils.TestSnowContext() - snowCtx.ValidatorState = &validatorstest.State{ - GetCurrentHeightF: func(context.Context) (uint64, error) { - return 0, nil - }, - GetValidatorSetF: func(context.Context, uint64, ids.ID) (map[ids.NodeID]*validators.GetValidatorOutput, error) { - return nil, nil - }, - } sender := &enginetest.SenderStub{ SentAppGossip: make(chan []byte, 1), } @@ -440,15 +426,10 @@ func TestAtomicTxPushGossipOutbound(t *testing.T) { ctx := context.Background() snowCtx := utils.TestSnowContext() snowCtx.AVAXAssetID = ids.GenerateTestID() - snowCtx.XChainID = ids.GenerateTestID() - validatorState := &validatorstest.State{ - GetSubnetIDF: func(context.Context, ids.ID) (ids.ID, error) { - return ids.Empty, nil - }, - } + validatorState := utils.NewTestValidatorState() snowCtx.ValidatorState = validatorState memory := atomic.NewMemory(memdb.New()) - snowCtx.SharedMemory = memory.NewSharedMemory(ids.Empty) + snowCtx.SharedMemory = memory.NewSharedMemory(snowCtx.ChainID) pk, err := secp256k1.NewPrivateKey() require.NoError(err) @@ -518,15 +499,10 @@ func TestAtomicTxPushGossipInbound(t *testing.T) { ctx := context.Background() snowCtx := utils.TestSnowContext() snowCtx.AVAXAssetID = ids.GenerateTestID() - snowCtx.XChainID = ids.GenerateTestID() - validatorState := &validatorstest.State{ - GetSubnetIDF: func(context.Context, ids.ID) (ids.ID, error) { - return ids.Empty, nil - }, - } + validatorState := utils.NewTestValidatorState() snowCtx.ValidatorState = validatorState memory := atomic.NewMemory(memdb.New()) - snowCtx.SharedMemory = memory.NewSharedMemory(ids.Empty) + snowCtx.SharedMemory = memory.NewSharedMemory(snowCtx.ChainID) pk, err := secp256k1.NewPrivateKey() require.NoError(err) diff --git a/plugin/evm/version.go b/plugin/evm/version.go index f3fd851d70..6151bbbe8e 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 Coreth - Version string = "v0.13.8" + Version string = "v0.13.9" ) func init() { diff --git a/plugin/evm/vm.go b/plugin/evm/vm.go index e113252e05..50606b0af7 100644 --- a/plugin/evm/vm.go +++ b/plugin/evm/vm.go @@ -17,7 +17,9 @@ import ( "sync" "time" + "github.com/ava-labs/avalanchego/cache/metercacher" "github.com/ava-labs/avalanchego/network/p2p" + "github.com/ava-labs/avalanchego/network/p2p/acp118" "github.com/ava-labs/avalanchego/network/p2p/gossip" "github.com/ava-labs/avalanchego/upgrade" avalanchegoConstants "github.com/ava-labs/avalanchego/utils/constants" @@ -49,7 +51,6 @@ import ( statesyncclient "github.com/ava-labs/coreth/sync/client" "github.com/ava-labs/coreth/sync/client/stats" "github.com/ava-labs/coreth/warp" - "github.com/ava-labs/coreth/warp/handlers" // Force-load tracer engine to trigger registration // @@ -316,7 +317,7 @@ type VM struct { // Metrics sdkMetrics *prometheus.Registry - bootstrapped bool + bootstrapped avalancheUtils.Atomic[bool] IsPlugin bool logger CorethLogger @@ -336,6 +337,10 @@ type VM struct { atomicTxGossipHandler p2p.Handler atomicTxPushGossiper *gossip.PushGossiper[*GossipAtomicTx] atomicTxPullGossiper gossip.Gossiper + + chainAlias string + // RPC handlers (should be stopped before closing chaindb) + rpcHandlers []interface{ Stop() } } // CodecRegistry implements the secp256k1fx interface @@ -392,13 +397,14 @@ func (vm *VM) Initialize( // fallback to ChainID string instead of erroring alias = vm.ctx.ChainID.String() } + vm.chainAlias = alias var writer io.Writer = vm.ctx.Log if vm.IsPlugin { writer = originalStderr } - corethLogger, err := InitLogger(alias, vm.config.LogLevel, vm.config.LogJSONFormat, writer) + corethLogger, err := InitLogger(vm.chainAlias, vm.config.LogLevel, vm.config.LogJSONFormat, writer) if err != nil { return fmt.Errorf("failed to initialize logger due to: %w ", err) } @@ -419,16 +425,15 @@ func (vm *VM) Initialize( vm.toEngine = toEngine vm.shutdownChan = make(chan struct{}, 1) - // Use NewNested rather than New so that the structure of the database - // remains the same regardless of the provided baseDB type. - vm.chaindb = rawdb.NewDatabase(Database{prefixdb.NewNested(ethDBPrefix, db)}) - vm.db = versiondb.New(db) - vm.acceptedBlockDB = prefixdb.New(acceptedPrefix, vm.db) - vm.metadataDB = prefixdb.New(metadataPrefix, vm.db) - // Note warpDB is not part of versiondb because it is not necessary - // that warp signatures are committed to the database atomically with - // the last accepted block. - vm.warpDB = prefixdb.New(warpPrefix, db) + + if err := vm.initializeMetrics(); err != nil { + return fmt.Errorf("failed to initialize metrics: %w", err) + } + + // Initialize the database + if err := vm.initializeDBs(db); err != nil { + return fmt.Errorf("failed to initialize databases: %w", err) + } if vm.config.InspectDatabase { start := time.Now() @@ -571,10 +576,6 @@ func (vm *VM) Initialize( vm.codec = Codec - if err := vm.initializeMetrics(); err != nil { - return err - } - // TODO: read size from settings vm.mempool, err = NewMempool(chainCtx, vm.sdkMetrics, defaultMempoolSize, vm.verifyTxAtTip) if err != nil { @@ -600,26 +601,31 @@ func (vm *VM) Initialize( for i, hexMsg := range vm.config.WarpOffChainMessages { offchainWarpMessages[i] = []byte(hexMsg) } + warpSignatureCache := &cache.LRU[ids.ID, []byte]{Size: warpSignatureCacheSize} + meteredCache, err := metercacher.New("warp_signature_cache", vm.sdkMetrics, warpSignatureCache) + if err != nil { + return fmt.Errorf("failed to create warp signature cache: %w", err) + } + + // clear warpdb on initialization if config enabled + if vm.config.PruneWarpDB { + if err := database.Clear(vm.warpDB, ethdb.IdealBatchSize); err != nil { + return fmt.Errorf("failed to prune warpDB: %w", err) + } + } + vm.warpBackend, err = warp.NewBackend( vm.ctx.NetworkID, vm.ctx.ChainID, vm.ctx.WarpSigner, vm, vm.warpDB, - warpSignatureCacheSize, + meteredCache, offchainWarpMessages, ) if err != nil { return err } - - // clear warpdb on initialization if config enabled - if vm.config.PruneWarpDB { - if err := vm.warpBackend.Clear(); err != nil { - return fmt.Errorf("failed to prune warpDB: %w", err) - } - } - if err := vm.initializeChain(lastAcceptedHash); err != nil { return err } @@ -660,7 +666,17 @@ func (vm *VM) Initialize( return err } - vm.initializeHandlers() + // Add p2p warp message warpHandler + warpHandler := acp118.NewCachedHandler(meteredCache, vm.warpBackend, vm.ctx.WarpSigner) + vm.Network.AddHandler(p2p.SignatureRequestHandlerID, warpHandler) + + vm.setAppRequestHandlers() + + vm.StateSyncServer = NewStateSyncServer(&stateSyncServerConfig{ + Chain: vm.blockChain, + AtomicTrie: vm.atomicTrie, + SyncableInterval: vm.config.StateSyncCommitInterval, + }) return vm.initializeStateSyncClient(lastAcceptedHeight) } @@ -799,21 +815,6 @@ func (vm *VM) initializeStateSyncClient(lastAcceptedHeight uint64) error { return nil } -// initializeHandlers should be called after [vm.chain] is initialized. -func (vm *VM) initializeHandlers() { - vm.StateSyncServer = NewStateSyncServer(&stateSyncServerConfig{ - Chain: vm.blockChain, - AtomicTrie: vm.atomicTrie, - SyncableInterval: vm.config.StateSyncCommitInterval, - }) - - // Add p2p warp message warpHandler - warpHandler := handlers.NewSignatureRequestHandlerP2P(vm.warpBackend, vm.networkCodec) - vm.Network.AddHandler(p2p.SignatureRequestHandlerID, warpHandler) - - vm.setAppRequestHandlers() -} - func (vm *VM) initChainState(lastAcceptedBlock *types.Block) error { block, err := vm.newBlock(lastAcceptedBlock) if err != nil { @@ -1076,33 +1077,48 @@ func (vm *VM) onExtraStateChange(block *types.Block, state *state.StateDB) (*big func (vm *VM) SetState(_ context.Context, state snow.State) error { switch state { case snow.StateSyncing: - vm.bootstrapped = false + vm.bootstrapped.Set(false) return nil case snow.Bootstrapping: - vm.bootstrapped = false - if err := vm.StateSyncClient.Error(); err != nil { - return err - } - // After starting bootstrapping, do not attempt to resume a previous state sync. - if err := vm.StateSyncClient.ClearOngoingSummary(); err != nil { - return err - } - // Ensure snapshots are initialized before bootstrapping (i.e., if state sync is skipped). - // Note calling this function has no effect if snapshots are already initialized. - vm.blockChain.InitializeSnapshots() - return vm.fx.Bootstrapping() + return vm.onBootstrapStarted() case snow.NormalOp: - // Initialize goroutines related to block building once we enter normal operation as there is no need to handle mempool gossip before this point. - if err := vm.initBlockBuilding(); err != nil { - return fmt.Errorf("failed to initialize block building: %w", err) - } - vm.bootstrapped = true - return vm.fx.Bootstrapped() + return vm.onNormalOperationsStarted() default: return snow.ErrUnknownState } } +// onBootstrapStarted marks this VM as bootstrapping +func (vm *VM) onBootstrapStarted() error { + vm.bootstrapped.Set(false) + if err := vm.StateSyncClient.Error(); err != nil { + return err + } + // After starting bootstrapping, do not attempt to resume a previous state sync. + if err := vm.StateSyncClient.ClearOngoingSummary(); err != nil { + return err + } + // Ensure snapshots are initialized before bootstrapping (i.e., if state sync is skipped). + // Note calling this function has no effect if snapshots are already initialized. + vm.blockChain.InitializeSnapshots() + + return vm.fx.Bootstrapping() +} + +// onNormalOperationsStarted marks this VM as bootstrapped +func (vm *VM) onNormalOperationsStarted() error { + if vm.bootstrapped.Get() { + return nil + } + vm.bootstrapped.Set(true) + if err := vm.fx.Bootstrapped(); err != nil { + return err + } + // Initialize goroutines related to block building + // once we enter normal operation as there is no need to handle mempool gossip before this point. + return vm.initBlockBuilding() +} + // initBlockBuilding starts goroutines to manage block building func (vm *VM) initBlockBuilding() error { ctx, cancel := context.WithCancel(context.TODO()) @@ -1116,7 +1132,7 @@ func (vm *VM) initBlockBuilding() error { } ethTxPool, err := NewGossipEthTxPool(vm.txPool, vm.sdkMetrics) if err != nil { - return err + return fmt.Errorf("failed to initialize gossip eth tx pool: %w", err) } vm.shutdownWg.Add(1) go func() { @@ -1199,7 +1215,7 @@ func (vm *VM) initBlockBuilding() error { } if err := vm.Network.AddHandler(p2p.TxGossipHandlerID, vm.ethTxGossipHandler); err != nil { - return err + return fmt.Errorf("failed to add eth tx gossip handler: %w", err) } if vm.atomicTxGossipHandler == nil { @@ -1216,7 +1232,7 @@ func (vm *VM) initBlockBuilding() error { } if err := vm.Network.AddHandler(p2p.AtomicTxGossipHandlerID, vm.atomicTxGossipHandler); err != nil { - return err + return fmt.Errorf("failed to add atomic tx gossip handler: %w", err) } if vm.ethTxPullGossiper == nil { @@ -1279,8 +1295,8 @@ func (vm *VM) initBlockBuilding() error { // setAppRequestHandlers sets the request handlers for the VM to serve state sync // requests. 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 + // Create standalone EVM TrieDB (read only) for serving leafs requests. + // We create a standalone TrieDB here, so that it has a standalone cache from the one // used by the node when processing blocks. evmTrieDB := triedb.NewDatabase( vm.chaindb, @@ -1314,6 +1330,10 @@ func (vm *VM) Shutdown(context.Context) error { log.Error("error stopping state syncer", "err", err) } close(vm.shutdownChan) + // Stop RPC handlers before eth.Stop which will close the database + for _, handler := range vm.rpcHandlers { + handler.Stop() + } vm.eth.Stop() vm.shutdownWg.Wait() return nil @@ -1500,10 +1520,6 @@ func (vm *VM) CreateHandlers(context.Context) (map[string]http.Handler, error) { return nil, err } - primaryAlias, err := vm.ctx.BCLookup.PrimaryAlias(vm.ctx.ChainID) - if err != nil { - return nil, fmt.Errorf("failed to get primary alias for chain due to %w", err) - } apis := make(map[string]http.Handler) avaxAPI, err := newHandler("avax", &AvaxAPI{vm}) if err != nil { @@ -1513,7 +1529,7 @@ func (vm *VM) CreateHandlers(context.Context) (map[string]http.Handler, error) { apis[avaxEndpoint] = avaxAPI if vm.config.AdminAPIEnabled { - adminAPI, err := newHandler("admin", NewAdminService(vm, os.ExpandEnv(fmt.Sprintf("%s_coreth_performance_%s", vm.config.AdminAPIDir, primaryAlias)))) + adminAPI, err := newHandler("admin", NewAdminService(vm, os.ExpandEnv(fmt.Sprintf("%s_coreth_performance_%s", vm.config.AdminAPIDir, vm.chainAlias)))) if err != nil { return nil, fmt.Errorf("failed to register service for admin API due to %w", err) } @@ -1544,9 +1560,26 @@ func (vm *VM) CreateHandlers(context.Context) (map[string]http.Handler, error) { vm.config.WSCPUMaxStored.Duration, ) + vm.rpcHandlers = append(vm.rpcHandlers, handler) return apis, nil } +// initializeDBs initializes the databases used by the VM. +// coreth always uses the avalanchego provided database. +func (vm *VM) initializeDBs(db database.Database) error { + // Use NewNested rather than New so that the structure of the database + // remains the same regardless of the provided baseDB type. + vm.chaindb = rawdb.NewDatabase(Database{prefixdb.NewNested(ethDBPrefix, db)}) + vm.db = versiondb.New(db) + vm.acceptedBlockDB = prefixdb.New(acceptedPrefix, vm.db) + vm.metadataDB = prefixdb.New(metadataPrefix, vm.db) + // Note warpDB is not part of versiondb because it is not necessary + // that warp signatures are committed to the database atomically with + // the last accepted block. + vm.warpDB = prefixdb.New(warpPrefix, db) + return nil +} + // 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) @@ -1557,6 +1590,7 @@ func (vm *VM) CreateStaticHandlers(context.Context) (map[string]http.Handler, er return nil, err } + vm.rpcHandlers = append(vm.rpcHandlers, handler) return map[string]http.Handler{ "/rpc": handler, }, nil diff --git a/plugin/evm/vm_test.go b/plugin/evm/vm_test.go index bcc1caa168..3d60b23356 100644 --- a/plugin/evm/vm_test.go +++ b/plugin/evm/vm_test.go @@ -110,6 +110,7 @@ var ( activateCancun = func(cfg *params.ChainConfig) *params.ChainConfig { cpy := *cfg + cpy.ShanghaiTime = utils.NewUint64(0) cpy.CancunTime = utils.NewUint64(0) return &cpy } diff --git a/plugin/evm/vm_warp_test.go b/plugin/evm/vm_warp_test.go index ec333e4132..7893588ac2 100644 --- a/plugin/evm/vm_warp_test.go +++ b/plugin/evm/vm_warp_test.go @@ -14,6 +14,7 @@ import ( "github.com/ava-labs/avalanchego/ids" commonEng "github.com/ava-labs/avalanchego/snow/engine/common" + "github.com/ava-labs/avalanchego/snow/engine/enginetest" "github.com/ava-labs/avalanchego/snow/engine/snowman/block" "github.com/ava-labs/avalanchego/snow/validators" "github.com/ava-labs/avalanchego/snow/validators/validatorstest" @@ -31,9 +32,10 @@ import ( "github.com/ava-labs/coreth/params" "github.com/ava-labs/coreth/plugin/evm/message" "github.com/ava-labs/coreth/precompile/contract" - "github.com/ava-labs/coreth/precompile/contracts/warp" + warpcontract "github.com/ava-labs/coreth/precompile/contracts/warp" "github.com/ava-labs/coreth/predicate" "github.com/ava-labs/coreth/utils" + "github.com/ava-labs/coreth/warp" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" "github.com/stretchr/testify/require" @@ -74,7 +76,7 @@ func TestSendWarpMessage(t *testing.T) { payloadData := avagoUtils.RandomBytes(100) - warpSendMessageInput, err := warp.PackSendWarpMessage(payloadData) + warpSendMessageInput, err := warpcontract.PackSendWarpMessage(payloadData) require.NoError(err) addressedPayload, err := payload.NewAddressedCall( testEthAddrs[0].Bytes(), @@ -89,7 +91,7 @@ func TestSendWarpMessage(t *testing.T) { require.NoError(err) // Submit a transaction to trigger sending a warp message - tx0 := types.NewTransaction(uint64(0), warp.ContractAddress, big.NewInt(1), 100_000, big.NewInt(params.LaunchMinGasPrice), warpSendMessageInput) + tx0 := types.NewTransaction(uint64(0), warpcontract.ContractAddress, big.NewInt(1), 100_000, big.NewInt(params.LaunchMinGasPrice), warpSendMessageInput) signedTx0, err := types.SignTx(tx0, types.LatestSignerForChainID(vm.chainConfig.ChainID), testKeys[0].ToECDSA()) require.NoError(err) @@ -110,20 +112,19 @@ func TestSendWarpMessage(t *testing.T) { require.Len(receipts[0].Logs, 1) expectedTopics := []common.Hash{ - warp.WarpABI.Events["SendWarpMessage"].ID, + warpcontract.WarpABI.Events["SendWarpMessage"].ID, common.BytesToHash(testEthAddrs[0].Bytes()), common.Hash(expectedUnsignedMessage.ID()), } require.Equal(expectedTopics, receipts[0].Logs[0].Topics) logData := receipts[0].Logs[0].Data - unsignedMessage, err := warp.UnpackSendWarpEventDataToMessage(logData) + unsignedMessage, err := warpcontract.UnpackSendWarpEventDataToMessage(logData) require.NoError(err) - unsignedMessageID := unsignedMessage.ID() // Verify the signature cannot be fetched before the block is accepted - _, err = vm.warpBackend.GetMessageSignature(unsignedMessageID) + _, err = vm.warpBackend.GetMessageSignature(context.TODO(), unsignedMessage) require.Error(err) - _, err = vm.warpBackend.GetBlockSignature(blk.ID()) + _, err = vm.warpBackend.GetBlockSignature(context.TODO(), blk.ID()) require.Error(err) require.NoError(vm.SetPreference(context.Background(), blk.ID())) @@ -131,7 +132,7 @@ func TestSendWarpMessage(t *testing.T) { vm.blockChain.DrainAcceptorQueue() // Verify the message signature after accepting the block. - rawSignatureBytes, err := vm.warpBackend.GetMessageSignature(unsignedMessageID) + rawSignatureBytes, err := vm.warpBackend.GetMessageSignature(context.TODO(), unsignedMessage) require.NoError(err) blsSignature, err := bls.SignatureFromBytes(rawSignatureBytes[:]) require.NoError(err) @@ -148,7 +149,7 @@ func TestSendWarpMessage(t *testing.T) { require.True(bls.Verify(vm.ctx.PublicKey, blsSignature, unsignedMessage.Bytes())) // Verify the blockID will now be signed by the backend and produces a valid signature. - rawSignatureBytes, err = vm.warpBackend.GetBlockSignature(blk.ID()) + rawSignatureBytes, err = vm.warpBackend.GetBlockSignature(context.TODO(), blk.ID()) require.NoError(err) blsSignature, err = bls.SignatureFromBytes(rawSignatureBytes[:]) require.NoError(err) @@ -341,7 +342,7 @@ func testWarpVMTransaction(t *testing.T, unsignedMessage *avalancheWarp.Unsigned common.Big0, txPayload, types.AccessList{}, - warp.ContractAddress, + warpcontract.ContractAddress, signedMessage.Bytes(), ), types.LatestSignerForChainID(vm.chainConfig.ChainID), @@ -410,15 +411,15 @@ func TestReceiveWarpMessage(t *testing.T) { // enable warp at the default genesis time enableTime := upgrade.InitiallyActiveTime - enableConfig := warp.NewDefaultConfig(utils.TimeToNewUint64(enableTime)) + enableConfig := warpcontract.NewDefaultConfig(utils.TimeToNewUint64(enableTime)) // disable warp so we can re-enable it with RequirePrimaryNetworkSigners disableTime := upgrade.InitiallyActiveTime.Add(10 * time.Second) - disableConfig := warp.NewDisableConfig(utils.TimeToNewUint64(disableTime)) + disableConfig := warpcontract.NewDisableConfig(utils.TimeToNewUint64(disableTime)) // re-enable warp with RequirePrimaryNetworkSigners reEnableTime := disableTime.Add(10 * time.Second) - reEnableConfig := warp.NewConfig( + reEnableConfig := warpcontract.NewConfig( utils.TimeToNewUint64(reEnableTime), 0, // QuorumNumerator true, // RequirePrimaryNetworkSigners @@ -458,7 +459,7 @@ func TestReceiveWarpMessage(t *testing.T) { }, { name: "C-Chain message should be signed by subnet without RequirePrimaryNetworkSigners", - sourceChainID: testCChainID, + sourceChainID: vm.ctx.CChainID, msgFrom: fromPrimary, useSigners: signersSubnet, blockTime: upgrade.InitiallyActiveTime.Add(2 * blockGap), @@ -481,7 +482,7 @@ func TestReceiveWarpMessage(t *testing.T) { }, { name: "C-Chain message should be signed by primary with RequirePrimaryNetworkSigners (impacted)", - sourceChainID: testCChainID, + sourceChainID: vm.ctx.CChainID, msgFrom: fromPrimary, useSigners: signersPrimary, blockTime: reEnableTime.Add(2 * blockGap), @@ -607,20 +608,20 @@ func testReceiveWarpMessage( ) require.NoError(err) - getWarpMsgInput, err := warp.PackGetVerifiedWarpMessage(0) + getWarpMsgInput, err := warpcontract.PackGetVerifiedWarpMessage(0) require.NoError(err) getVerifiedWarpMessageTx, err := types.SignTx( predicate.NewPredicateTx( vm.chainConfig.ChainID, vm.txPool.Nonce(testEthAddrs[0]), - &warp.Module.Address, + &warpcontract.Module.Address, 1_000_000, big.NewInt(225*params.GWei), big.NewInt(params.GWei), common.Big0, getWarpMsgInput, types.AccessList{}, - warp.ContractAddress, + warpcontract.ContractAddress, signedMessage.Bytes(), ), types.LatestSignerForChainID(vm.chainConfig.ChainID), @@ -653,7 +654,7 @@ func testReceiveWarpMessage( // An empty bitset indicates success. txResultsBytes := results.GetResults( getVerifiedWarpMessageTx.Hash(), - warp.ContractAddress, + warpcontract.ContractAddress, ) bitset := set.BitsFromBytes(txResultsBytes) require.Zero(bitset.Len()) // Empty bitset indicates success @@ -686,8 +687,8 @@ func testReceiveWarpMessage( verifiedMessageTxReceipt := verifiedMessageReceipts[0] require.Equal(types.ReceiptStatusSuccessful, verifiedMessageTxReceipt.Status) - expectedOutput, err := warp.PackGetVerifiedWarpMessageOutput(warp.GetVerifiedWarpMessageOutput{ - Message: warp.WarpMessage{ + expectedOutput, err := warpcontract.PackGetVerifiedWarpMessageOutput(warpcontract.GetVerifiedWarpMessageOutput{ + Message: warpcontract.WarpMessage{ SourceChainID: common.Hash(sourceChainID), OriginSenderAddress: testEthAddrs[0], Payload: payloadData, @@ -728,8 +729,10 @@ func TestMessageSignatureRequestsToVM(t *testing.T) { // Add the known message and get its signature to confirm. err = vm.warpBackend.AddMessage(warpMessage) require.NoError(t, err) - signature, err := vm.warpBackend.GetMessageSignature(warpMessage.ID()) + signature, err := vm.warpBackend.GetMessageSignature(context.TODO(), warpMessage) require.NoError(t, err) + var knownSignature [bls.SignatureLen]byte + copy(knownSignature[:], signature) tests := map[string]struct { messageID ids.ID @@ -737,7 +740,7 @@ func TestMessageSignatureRequestsToVM(t *testing.T) { }{ "known": { messageID: warpMessage.ID(), - expectedResponse: signature, + expectedResponse: knownSignature, }, "unknown": { messageID: ids.GenerateTestID(), @@ -784,8 +787,10 @@ func TestBlockSignatureRequestsToVM(t *testing.T) { lastAcceptedID, err := vm.LastAccepted(context.Background()) require.NoError(t, err) - signature, err := vm.warpBackend.GetBlockSignature(lastAcceptedID) + signature, err := vm.warpBackend.GetBlockSignature(context.TODO(), lastAcceptedID) require.NoError(t, err) + var knownSignature [bls.SignatureLen]byte + copy(knownSignature[:], signature) tests := map[string]struct { blockID ids.ID @@ -793,7 +798,7 @@ func TestBlockSignatureRequestsToVM(t *testing.T) { }{ "known": { blockID: lastAcceptedID, - expectedResponse: signature, + expectedResponse: knownSignature, }, "unknown": { blockID: ids.GenerateTestID(), @@ -828,3 +833,61 @@ func TestBlockSignatureRequestsToVM(t *testing.T) { }) } } + +func TestClearWarpDB(t *testing.T) { + ctx, db, genesisBytes, issuer, _ := setupGenesis(t, genesisJSONLatest) + vm := &VM{} + err := vm.Initialize(context.Background(), ctx, db, genesisBytes, []byte{}, []byte{}, issuer, []*commonEng.Fx{}, &enginetest.Sender{}) + require.NoError(t, err) + + // use multiple messages to test that all messages get cleared + payloads := [][]byte{[]byte("test1"), []byte("test2"), []byte("test3"), []byte("test4"), []byte("test5")} + messages := []*avalancheWarp.UnsignedMessage{} + + // add all messages + for _, payload := range payloads { + unsignedMsg, err := avalancheWarp.NewUnsignedMessage(vm.ctx.NetworkID, vm.ctx.ChainID, payload) + require.NoError(t, err) + err = vm.warpBackend.AddMessage(unsignedMsg) + require.NoError(t, err) + // ensure that the message was added + _, err = vm.warpBackend.GetMessageSignature(context.TODO(), unsignedMsg) + require.NoError(t, err) + messages = append(messages, unsignedMsg) + } + + require.NoError(t, vm.Shutdown(context.Background())) + + // Restart VM with the same database default should not prune the warp db + vm = &VM{} + // we need new context since the previous one has registered metrics. + ctx, _, _, _, _ = setupGenesis(t, genesisJSONLatest) + err = vm.Initialize(context.Background(), ctx, db, genesisBytes, []byte{}, []byte{}, issuer, []*commonEng.Fx{}, &enginetest.Sender{}) + require.NoError(t, err) + + // check messages are still present + for _, message := range messages { + bytes, err := vm.warpBackend.GetMessageSignature(context.TODO(), message) + require.NoError(t, err) + require.NotEmpty(t, bytes) + } + + require.NoError(t, vm.Shutdown(context.Background())) + + // restart the VM with pruning enabled + vm = &VM{} + config := `{"prune-warp-db-enabled": true}` + ctx, _, _, _, _ = setupGenesis(t, genesisJSONLatest) + err = vm.Initialize(context.Background(), ctx, db, genesisBytes, []byte{}, []byte(config), issuer, []*commonEng.Fx{}, &enginetest.Sender{}) + require.NoError(t, err) + + it := vm.warpDB.NewIterator() + require.False(t, it.Next()) + it.Release() + + // ensure all messages have been deleted + for _, message := range messages { + _, err := vm.warpBackend.GetMessageSignature(context.TODO(), message) + require.ErrorIs(t, err, &commonEng.AppError{Code: warp.ParseErrCode}) + } +} diff --git a/precompile/precompileconfig/config.go b/precompile/precompileconfig/config.go index 43365e3bf9..eb741cbc55 100644 --- a/precompile/precompileconfig/config.go +++ b/precompile/precompileconfig/config.go @@ -5,8 +5,6 @@ package precompileconfig import ( - "github.com/ava-labs/avalanchego/chains/atomic" - "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/snow" "github.com/ava-labs/avalanchego/snow/engine/snowman/block" "github.com/ava-labs/avalanchego/vms/platformvm/warp" @@ -53,21 +51,14 @@ type Predicater interface { VerifyPredicate(predicateContext *PredicateContext, predicateBytes []byte) error } -// SharedMemoryWriter defines an interface to allow a precompile's Accepter to write operations -// into shared memory to be committed atomically on block accept. -type SharedMemoryWriter interface { - AddSharedMemoryRequests(chainID ids.ID, requests *atomic.Requests) -} - type WarpMessageWriter interface { AddMessage(unsignedMessage *warp.UnsignedMessage) error } // AcceptContext defines the context passed in to a precompileconfig's Accepter type AcceptContext struct { - SnowCtx *snow.Context - SharedMemory SharedMemoryWriter - Warp WarpMessageWriter + SnowCtx *snow.Context + Warp WarpMessageWriter } // Accepter is an optional interface for StatefulPrecompiledContracts to implement. diff --git a/scripts/build_test.sh b/scripts/build_test.sh index 460ebcd564..c3fb241349 100755 --- a/scripts/build_test.sh +++ b/scripts/build_test.sh @@ -41,7 +41,7 @@ do unexpected_failures=$( # First grep pattern corresponds to test failures, second pattern corresponds to test panics due to timeouts (grep "^--- FAIL" test.out | awk '{print $3}' || grep -E '^\s+Test.+ \(' test.out | awk '{print $1}') | - sort -u | comm -23 - ./scripts/known_flakes.txt + sort -u | comm -23 - <(sed 's/\r$//' ./scripts/known_flakes.txt) ) if [ -n "${unexpected_failures}" ]; then echo "Unexpected test failures: ${unexpected_failures}" diff --git a/scripts/known_flakes.txt b/scripts/known_flakes.txt index 05c41794ba..b28b4e710c 100644 --- a/scripts/known_flakes.txt +++ b/scripts/known_flakes.txt @@ -7,5 +7,6 @@ TestResumeSyncAccountsTrieInterrupted TestResyncNewRootAfterDeletes TestTransactionSkipIndexing TestVMShutdownWhileSyncing +TestWaitDeployedCornerCases TestWalletNotifications TestWebsocketLargeRead \ No newline at end of file diff --git a/scripts/versions.sh b/scripts/versions.sh index 4154fe89c0..225b0592d4 100644 --- a/scripts/versions.sh +++ b/scripts/versions.sh @@ -6,4 +6,4 @@ set -euo pipefail # Don't export them as they're used in the context of other calls -AVALANCHE_VERSION=${AVALANCHE_VERSION:-'v1.11.11'} +AVALANCHE_VERSION=${AVALANCHE_VERSION:-'v1.11.13-rc.0'} diff --git a/tests/init.go b/tests/init.go index aae979bbe0..eb410ee388 100644 --- a/tests/init.go +++ b/tests/init.go @@ -190,6 +190,7 @@ var Forks = map[string]*params.ChainConfig{ PetersburgBlock: big.NewInt(0), IstanbulBlock: big.NewInt(0), MuirGlacierBlock: big.NewInt(0), + BerlinBlock: big.NewInt(0), NetworkUpgrades: params.NetworkUpgrades{ ApricotPhase1BlockTimestamp: utils.NewUint64(0), ApricotPhase2BlockTimestamp: utils.NewUint64(0), @@ -206,6 +207,8 @@ var Forks = map[string]*params.ChainConfig{ PetersburgBlock: big.NewInt(0), IstanbulBlock: big.NewInt(0), MuirGlacierBlock: big.NewInt(0), + BerlinBlock: big.NewInt(0), + LondonBlock: big.NewInt(0), NetworkUpgrades: params.NetworkUpgrades{ ApricotPhase1BlockTimestamp: utils.NewUint64(0), ApricotPhase2BlockTimestamp: utils.NewUint64(0), @@ -223,6 +226,8 @@ var Forks = map[string]*params.ChainConfig{ PetersburgBlock: big.NewInt(0), IstanbulBlock: big.NewInt(0), MuirGlacierBlock: big.NewInt(0), + BerlinBlock: big.NewInt(0), + LondonBlock: big.NewInt(0), NetworkUpgrades: params.NetworkUpgrades{ ApricotPhase1BlockTimestamp: utils.NewUint64(0), ApricotPhase2BlockTimestamp: utils.NewUint64(0), @@ -240,6 +245,8 @@ var Forks = map[string]*params.ChainConfig{ ConstantinopleBlock: big.NewInt(0), PetersburgBlock: big.NewInt(0), IstanbulBlock: big.NewInt(0), + BerlinBlock: big.NewInt(0), + LondonBlock: big.NewInt(0), NetworkUpgrades: params.NetworkUpgrades{ ApricotPhase1BlockTimestamp: utils.NewUint64(0), ApricotPhase2BlockTimestamp: utils.NewUint64(0), @@ -258,6 +265,8 @@ var Forks = map[string]*params.ChainConfig{ ConstantinopleBlock: big.NewInt(0), PetersburgBlock: big.NewInt(0), IstanbulBlock: big.NewInt(0), + BerlinBlock: big.NewInt(0), + LondonBlock: big.NewInt(0), NetworkUpgrades: params.NetworkUpgrades{ ApricotPhase1BlockTimestamp: utils.NewUint64(0), ApricotPhase2BlockTimestamp: utils.NewUint64(0), @@ -277,6 +286,8 @@ var Forks = map[string]*params.ChainConfig{ ConstantinopleBlock: big.NewInt(0), PetersburgBlock: big.NewInt(0), IstanbulBlock: big.NewInt(0), + BerlinBlock: big.NewInt(0), + LondonBlock: big.NewInt(0), NetworkUpgrades: params.NetworkUpgrades{ ApricotPhase1BlockTimestamp: utils.NewUint64(0), ApricotPhase2BlockTimestamp: utils.NewUint64(0), @@ -297,6 +308,8 @@ var Forks = map[string]*params.ChainConfig{ ConstantinopleBlock: big.NewInt(0), PetersburgBlock: big.NewInt(0), IstanbulBlock: big.NewInt(0), + BerlinBlock: big.NewInt(0), + LondonBlock: big.NewInt(0), NetworkUpgrades: params.NetworkUpgrades{ ApricotPhase1BlockTimestamp: utils.NewUint64(0), ApricotPhase2BlockTimestamp: utils.NewUint64(0), @@ -318,6 +331,8 @@ var Forks = map[string]*params.ChainConfig{ ConstantinopleBlock: big.NewInt(0), PetersburgBlock: big.NewInt(0), IstanbulBlock: big.NewInt(0), + BerlinBlock: big.NewInt(0), + LondonBlock: big.NewInt(0), CancunTime: utils.NewUint64(0), NetworkUpgrades: params.NetworkUpgrades{ ApricotPhase1BlockTimestamp: utils.NewUint64(0), diff --git a/tests/state_test_util.go b/tests/state_test_util.go index a919bbb725..dc3dcb1ea4 100644 --- a/tests/state_test_util.go +++ b/tests/state_test_util.go @@ -66,7 +66,7 @@ func MakePreState(db ethdb.Database, accounts types.GenesisAlloc, snapshotter bo } } // Commit and re-open to start with a clean state. - root, _ := statedb.Commit(0, false, false) + root, _ := statedb.Commit(0, false) // If snapshot is requested, initialize the snapshotter and use it in state. var snaps *snapshot.Tree diff --git a/triedb/database.go b/triedb/database.go index 295c723bbc..7421b74cf0 100644 --- a/triedb/database.go +++ b/triedb/database.go @@ -148,17 +148,6 @@ func (db *Database) Update(root common.Hash, parent common.Hash, block uint64, n return db.backend.Update(root, parent, block, nodes, states) } -func (db *Database) UpdateAndReferenceRoot(root common.Hash, parent common.Hash, block uint64, nodes *trienode.MergedNodeSet, states *triestate.Set) error { - if db.preimages != nil { - db.preimages.commit(false) - } - hdb, ok := db.backend.(*hashdb.Database) - if ok { - return hdb.UpdateAndReferenceRoot(root, parent, block, nodes, states) - } - return db.backend.Update(root, parent, block, nodes, states) -} - // Commit iterates over all the children of a particular node, writes them out // to disk. As a side effect, all pre-images accumulated up to this point are // also written. diff --git a/triedb/hashdb/database.go b/triedb/hashdb/database.go index f5c55b3198..11efee0fb8 100644 --- a/triedb/hashdb/database.go +++ b/triedb/hashdb/database.go @@ -97,8 +97,9 @@ type cache interface { // Config contains the settings for database. type Config struct { - CleanCacheSize int // Maximum memory allowance (in bytes) for caching clean nodes - StatsPrefix string // Prefix for cache stats (disabled if empty) + CleanCacheSize int // Maximum memory allowance (in bytes) for caching clean nodes + StatsPrefix string // Prefix for cache stats (disabled if empty) + ReferenceRootAtomicallyOnUpdate bool // Whether to reference the root node on update } // Defaults is the default setting for database if it's not specified. @@ -137,6 +138,8 @@ type Database struct { childrenSize common.StorageSize // Storage size of the external children tracking lock sync.RWMutex + + referenceRoot bool } // cachedNode is all the information we know about a single cached trie node @@ -174,10 +177,11 @@ func New(diskdb ethdb.Database, config *Config, resolver ChildResolver) *Databas cleans = utils.NewMeteredCache(config.CleanCacheSize, config.StatsPrefix, cacheStatsUpdateFrequency) } return &Database{ - diskdb: diskdb, - resolver: resolver, - cleans: cleans, - dirties: make(map[common.Hash]*cachedNode), + diskdb: diskdb, + resolver: resolver, + cleans: cleans, + dirties: make(map[common.Hash]*cachedNode), + referenceRoot: config.ReferenceRootAtomicallyOnUpdate, } } @@ -627,6 +631,8 @@ func (db *Database) Initialized(genesisRoot common.Hash) bool { // Update inserts the dirty nodes in provided nodeset into database and link the // account trie with multiple storage tries if necessary. +// If ReferenceRootAtomicallyOnUpdate was enabled in the config, it will also add a reference from +// the root to the metaroot while holding the db's lock. func (db *Database) Update(root common.Hash, parent common.Hash, block uint64, nodes *trienode.MergedNodeSet, states *triestate.Set) error { // Ensure the parent state is present and signal a warning if not. if parent != types.EmptyRootHash { @@ -637,26 +643,13 @@ func (db *Database) Update(root common.Hash, parent common.Hash, block uint64, n db.lock.Lock() defer db.lock.Unlock() - return db.update(root, parent, nodes) -} - -// UpdateAndReferenceRoot inserts the dirty nodes in provided nodeset into -// database and links the account trie with multiple storage tries if necessary, -// then adds a reference [from] root to the metaroot while holding the db's lock. -func (db *Database) UpdateAndReferenceRoot(root common.Hash, parent common.Hash, block uint64, nodes *trienode.MergedNodeSet, states *triestate.Set) error { - // Ensure the parent state is present and signal a warning if not. - if parent != types.EmptyRootHash { - if blob, _ := db.node(parent); len(blob) == 0 { - log.Error("parent state is not present") - } - } - db.lock.Lock() - defer db.lock.Unlock() - if err := db.update(root, parent, nodes); err != nil { return err } - db.reference(root, common.Hash{}) + + if db.referenceRoot { + db.reference(root, common.Hash{}) + } return nil } diff --git a/utils/snow.go b/utils/snow.go index 77368f0750..e46e2ac7b1 100644 --- a/utils/snow.go +++ b/utils/snow.go @@ -4,12 +4,24 @@ package utils import ( + "context" + "errors" + "github.com/ava-labs/avalanchego/api/metrics" "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/snow" + "github.com/ava-labs/avalanchego/snow/validators" "github.com/ava-labs/avalanchego/snow/validators/validatorstest" + "github.com/ava-labs/avalanchego/utils/constants" "github.com/ava-labs/avalanchego/utils/crypto/bls" "github.com/ava-labs/avalanchego/utils/logging" + "github.com/ava-labs/avalanchego/vms/platformvm/warp" +) + +var ( + testCChainID = ids.ID{'c', 'c', 'h', 'a', 'i', 'n', 't', 'e', 's', 't'} + testXChainID = ids.ID{'t', 'e', 's', 't', 'x'} + testChainID = ids.ID{'t', 'e', 's', 't', 'c', 'h', 'a', 'i', 'n'} ) func TestSnowContext() *snow.Context { @@ -18,16 +30,49 @@ func TestSnowContext() *snow.Context { panic(err) } pk := bls.PublicFromSecretKey(sk) - return &snow.Context{ - NetworkID: 0, + networkID := constants.UnitTestID + chainID := testChainID + + ctx := &snow.Context{ + NetworkID: networkID, SubnetID: ids.Empty, - ChainID: ids.Empty, - NodeID: ids.EmptyNodeID, + ChainID: chainID, + NodeID: ids.GenerateTestNodeID(), + XChainID: testXChainID, + CChainID: testCChainID, PublicKey: pk, + WarpSigner: warp.NewSigner(sk, networkID, chainID), Log: logging.NoLog{}, BCLookup: ids.NewAliaser(), Metrics: metrics.NewPrefixGatherer(), ChainDataDir: "", - ValidatorState: &validatorstest.State{}, + ValidatorState: NewTestValidatorState(), + } + + return ctx +} + +func NewTestValidatorState() *validatorstest.State { + return &validatorstest.State{ + GetCurrentHeightF: func(context.Context) (uint64, error) { + return 0, nil + }, + GetSubnetIDF: func(_ context.Context, chainID ids.ID) (ids.ID, error) { + subnetID, ok := map[ids.ID]ids.ID{ + constants.PlatformChainID: constants.PrimaryNetworkID, + testXChainID: constants.PrimaryNetworkID, + testCChainID: constants.PrimaryNetworkID, + }[chainID] + if !ok { + return ids.Empty, errors.New("unknown chain") + } + return subnetID, nil + }, + GetValidatorSetF: func(context.Context, uint64, ids.ID) (map[ids.NodeID]*validators.GetValidatorOutput, error) { + return map[ids.NodeID]*validators.GetValidatorOutput{}, nil + }, + GetCurrentValidatorSetF: func(context.Context, ids.ID) (map[ids.ID]*validators.GetCurrentValidatorOutput, uint64, error) { + return map[ids.ID]*validators.GetCurrentValidatorOutput{}, 0, nil + }, } } diff --git a/warp/backend.go b/warp/backend.go index 7e7377ad57..6e1f6a9553 100644 --- a/warp/backend.go +++ b/warp/backend.go @@ -11,20 +11,19 @@ import ( "github.com/ava-labs/avalanchego/cache" "github.com/ava-labs/avalanchego/database" "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/network/p2p/acp118" "github.com/ava-labs/avalanchego/snow/consensus/snowman" - "github.com/ava-labs/avalanchego/utils/crypto/bls" avalancheWarp "github.com/ava-labs/avalanchego/vms/platformvm/warp" "github.com/ava-labs/avalanchego/vms/platformvm/warp/payload" - "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/log" ) var ( _ Backend = &backend{} errParsingOffChainMessage = errors.New("failed to parse off-chain message") -) -const batchSize = ethdb.IdealBatchSize + messageCacheSize = 500 +) type BlockClient interface { GetAcceptedBlock(ctx context.Context, blockID ids.ID) (snowman.Block, error) @@ -36,19 +35,18 @@ type Backend interface { // AddMessage signs [unsignedMessage] and adds it to the warp backend database AddMessage(unsignedMessage *avalancheWarp.UnsignedMessage) error - // GetMessageSignature returns the signature of the requested message hash. - GetMessageSignature(messageID ids.ID) ([bls.SignatureLen]byte, error) + // GetMessageSignature validates the message and returns the signature of the requested message. + GetMessageSignature(ctx context.Context, message *avalancheWarp.UnsignedMessage) ([]byte, error) - // GetBlockSignature returns the signature of the requested message hash. - GetBlockSignature(blockID ids.ID) ([bls.SignatureLen]byte, error) + // GetBlockSignature returns the signature of a hash payload containing blockID if it's the ID of an accepted block. + GetBlockSignature(ctx context.Context, blockID ids.ID) ([]byte, error) // GetMessage retrieves the [unsignedMessage] from the warp backend database if available - // TODO: After E-Upgrade, the backend no longer needs to store the mapping from messageHash + // TODO: After Etna, the backend no longer needs to store the mapping from messageHash // to unsignedMessage (and this method can be removed). GetMessage(messageHash ids.ID) (*avalancheWarp.UnsignedMessage, error) - // Clear clears the entire db - Clear() error + acp118.Verifier } // backend implements Backend, keeps track of warp messages, and generates message signatures. @@ -58,10 +56,10 @@ type backend struct { db database.Database warpSigner avalancheWarp.Signer blockClient BlockClient - messageSignatureCache *cache.LRU[ids.ID, [bls.SignatureLen]byte] - blockSignatureCache *cache.LRU[ids.ID, [bls.SignatureLen]byte] + signatureCache cache.Cacher[ids.ID, []byte] messageCache *cache.LRU[ids.ID, *avalancheWarp.UnsignedMessage] offchainAddressedCallMsgs map[ids.ID]*avalancheWarp.UnsignedMessage + stats *verifierStats } // NewBackend creates a new Backend, and initializes the signature cache and message tracking database. @@ -71,7 +69,7 @@ func NewBackend( warpSigner avalancheWarp.Signer, blockClient BlockClient, db database.Database, - cacheSize int, + signatureCache cache.Cacher[ids.ID, []byte], offchainMessages [][]byte, ) (Backend, error) { b := &backend{ @@ -80,9 +78,9 @@ func NewBackend( db: db, warpSigner: warpSigner, blockClient: blockClient, - messageSignatureCache: &cache.LRU[ids.ID, [bls.SignatureLen]byte]{Size: cacheSize}, - blockSignatureCache: &cache.LRU[ids.ID, [bls.SignatureLen]byte]{Size: cacheSize}, - messageCache: &cache.LRU[ids.ID, *avalancheWarp.UnsignedMessage]{Size: cacheSize}, + signatureCache: signatureCache, + messageCache: &cache.LRU[ids.ID, *avalancheWarp.UnsignedMessage]{Size: messageCacheSize}, + stats: newVerifierStats(), offchainAddressedCallMsgs: make(map[ids.ID]*avalancheWarp.UnsignedMessage), } return b, b.initOffChainMessages(offchainMessages) @@ -113,15 +111,9 @@ func (b *backend) initOffChainMessages(offchainMessages [][]byte) error { return nil } -func (b *backend) Clear() error { - b.messageSignatureCache.Flush() - b.blockSignatureCache.Flush() - b.messageCache.Flush() - return database.Clear(b.db, batchSize) -} - func (b *backend) AddMessage(unsignedMessage *avalancheWarp.UnsignedMessage) error { messageID := unsignedMessage.ID() + log.Debug("Adding warp message to backend", "messageID", messageID) // In the case when a node restarts, and possibly changes its bls key, the cache gets emptied but the database does not. // So to avoid having incorrect signatures saved in the database after a bls key change, we save the full message in the database. @@ -130,68 +122,52 @@ func (b *backend) AddMessage(unsignedMessage *avalancheWarp.UnsignedMessage) err return fmt.Errorf("failed to put warp signature in db: %w", err) } - var signature [bls.SignatureLen]byte - sig, err := b.warpSigner.Sign(unsignedMessage) - if err != nil { + if _, err := b.signMessage(unsignedMessage); err != nil { return fmt.Errorf("failed to sign warp message: %w", err) } - - copy(signature[:], sig) - b.messageSignatureCache.Put(messageID, signature) - log.Debug("Adding warp message to backend", "messageID", messageID) return nil } -func (b *backend) GetMessageSignature(messageID ids.ID) ([bls.SignatureLen]byte, error) { +func (b *backend) GetMessageSignature(ctx context.Context, unsignedMessage *avalancheWarp.UnsignedMessage) ([]byte, error) { + messageID := unsignedMessage.ID() + log.Debug("Getting warp message from backend", "messageID", messageID) - if sig, ok := b.messageSignatureCache.Get(messageID); ok { + if sig, ok := b.signatureCache.Get(messageID); ok { return sig, nil } - unsignedMessage, err := b.GetMessage(messageID) - if err != nil { - return [bls.SignatureLen]byte{}, fmt.Errorf("failed to get warp message %s from db: %w", messageID.String(), err) + if err := b.Verify(ctx, unsignedMessage, nil); err != nil { + return nil, fmt.Errorf("failed to validate warp message: %w", err) } - - var signature [bls.SignatureLen]byte - sig, err := b.warpSigner.Sign(unsignedMessage) - if err != nil { - return [bls.SignatureLen]byte{}, fmt.Errorf("failed to sign warp message: %w", err) - } - - copy(signature[:], sig) - b.messageSignatureCache.Put(messageID, signature) - return signature, nil + return b.signMessage(unsignedMessage) } -func (b *backend) GetBlockSignature(blockID ids.ID) ([bls.SignatureLen]byte, error) { +func (b *backend) GetBlockSignature(ctx context.Context, blockID ids.ID) ([]byte, error) { log.Debug("Getting block from backend", "blockID", blockID) - if sig, ok := b.blockSignatureCache.Get(blockID); ok { - return sig, nil - } - _, err := b.blockClient.GetAcceptedBlock(context.TODO(), blockID) - if err != nil { - return [bls.SignatureLen]byte{}, fmt.Errorf("failed to get block %s: %w", blockID, err) - } - - var signature [bls.SignatureLen]byte blockHashPayload, err := payload.NewHash(blockID) if err != nil { - return [bls.SignatureLen]byte{}, fmt.Errorf("failed to create new block hash payload: %w", err) + return nil, fmt.Errorf("failed to create new block hash payload: %w", err) } + unsignedMessage, err := avalancheWarp.NewUnsignedMessage(b.networkID, b.sourceChainID, blockHashPayload.Bytes()) if err != nil { - return [bls.SignatureLen]byte{}, fmt.Errorf("failed to create new unsigned warp message: %w", err) + return nil, fmt.Errorf("failed to create new unsigned warp message: %w", err) } - sig, err := b.warpSigner.Sign(unsignedMessage) - if err != nil { - return [bls.SignatureLen]byte{}, fmt.Errorf("failed to sign warp message: %w", err) + + if sig, ok := b.signatureCache.Get(unsignedMessage.ID()); ok { + return sig, nil + } + + if err := b.verifyBlockMessage(ctx, blockHashPayload); err != nil { + return nil, fmt.Errorf("failed to validate block message: %w", err) } - copy(signature[:], sig) - b.blockSignatureCache.Put(blockID, signature) - return signature, nil + sig, err := b.signMessage(unsignedMessage) + if err != nil { + return nil, fmt.Errorf("failed to sign block message: %w", err) + } + return sig, nil } func (b *backend) GetMessage(messageID ids.ID) (*avalancheWarp.UnsignedMessage, error) { @@ -215,3 +191,13 @@ func (b *backend) GetMessage(messageID ids.ID) (*avalancheWarp.UnsignedMessage, return unsignedMessage, nil } + +func (b *backend) signMessage(unsignedMessage *avalancheWarp.UnsignedMessage) ([]byte, error) { + sig, err := b.warpSigner.Sign(unsignedMessage) + if err != nil { + return nil, fmt.Errorf("failed to sign warp message: %w", err) + } + + b.signatureCache.Put(unsignedMessage.ID(), sig) + return sig, nil +} diff --git a/warp/backend_test.go b/warp/backend_test.go index 7b2208a2d4..5a0a64a0c2 100644 --- a/warp/backend_test.go +++ b/warp/backend_test.go @@ -4,13 +4,14 @@ package warp import ( + "context" "testing" + "github.com/ava-labs/avalanchego/cache" "github.com/ava-labs/avalanchego/database/memdb" "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/utils" "github.com/ava-labs/avalanchego/utils/crypto/bls" - "github.com/ava-labs/avalanchego/utils/hashing" avalancheWarp "github.com/ava-labs/avalanchego/vms/platformvm/warp" "github.com/ava-labs/avalanchego/vms/platformvm/warp/payload" "github.com/ava-labs/coreth/warp/warptest" @@ -36,57 +37,14 @@ func init() { } } -func TestClearDB(t *testing.T) { - db := memdb.New() - - sk, err := bls.NewSecretKey() - require.NoError(t, err) - warpSigner := avalancheWarp.NewSigner(sk, networkID, sourceChainID) - backendIntf, err := NewBackend(networkID, sourceChainID, warpSigner, nil, db, 500, nil) - require.NoError(t, err) - backend, ok := backendIntf.(*backend) - require.True(t, ok) - - // use multiple messages to test that all messages get cleared - payloads := [][]byte{[]byte("test1"), []byte("test2"), []byte("test3"), []byte("test4"), []byte("test5")} - messageIDs := []ids.ID{} - - // add all messages - for _, payload := range payloads { - unsignedMsg, err := avalancheWarp.NewUnsignedMessage(networkID, sourceChainID, payload) - require.NoError(t, err) - messageID := hashing.ComputeHash256Array(unsignedMsg.Bytes()) - messageIDs = append(messageIDs, messageID) - err = backend.AddMessage(unsignedMsg) - require.NoError(t, err) - // ensure that the message was added - _, err = backend.GetMessageSignature(messageID) - require.NoError(t, err) - } - - err = backend.Clear() - require.NoError(t, err) - require.Zero(t, backend.messageCache.Len()) - require.Zero(t, backend.messageSignatureCache.Len()) - require.Zero(t, backend.blockSignatureCache.Len()) - it := db.NewIterator() - defer it.Release() - require.False(t, it.Next()) - - // ensure all messages have been deleted - for _, messageID := range messageIDs { - _, err := backend.GetMessageSignature(messageID) - require.ErrorContains(t, err, "failed to get warp message") - } -} - func TestAddAndGetValidMessage(t *testing.T) { db := memdb.New() sk, err := bls.NewSecretKey() require.NoError(t, err) warpSigner := avalancheWarp.NewSigner(sk, networkID, sourceChainID) - backend, err := NewBackend(networkID, sourceChainID, warpSigner, nil, db, 500, nil) + messageSignatureCache := &cache.LRU[ids.ID, []byte]{Size: 500} + backend, err := NewBackend(networkID, sourceChainID, warpSigner, nil, db, messageSignatureCache, nil) require.NoError(t, err) // Add testUnsignedMessage to the warp backend @@ -94,8 +52,7 @@ func TestAddAndGetValidMessage(t *testing.T) { require.NoError(t, err) // Verify that a signature is returned successfully, and compare to expected signature. - messageID := testUnsignedMessage.ID() - signature, err := backend.GetMessageSignature(messageID) + signature, err := backend.GetMessageSignature(context.TODO(), testUnsignedMessage) require.NoError(t, err) expectedSig, err := warpSigner.Sign(testUnsignedMessage) @@ -109,12 +66,12 @@ func TestAddAndGetUnknownMessage(t *testing.T) { sk, err := bls.NewSecretKey() require.NoError(t, err) warpSigner := avalancheWarp.NewSigner(sk, networkID, sourceChainID) - backend, err := NewBackend(networkID, sourceChainID, warpSigner, nil, db, 500, nil) + messageSignatureCache := &cache.LRU[ids.ID, []byte]{Size: 500} + backend, err := NewBackend(networkID, sourceChainID, warpSigner, nil, db, messageSignatureCache, nil) require.NoError(t, err) // Try getting a signature for a message that was not added. - messageID := testUnsignedMessage.ID() - _, err = backend.GetMessageSignature(messageID) + _, err = backend.GetMessageSignature(context.TODO(), testUnsignedMessage) require.Error(t, err) } @@ -128,7 +85,8 @@ func TestGetBlockSignature(t *testing.T) { sk, err := bls.NewSecretKey() require.NoError(err) warpSigner := avalancheWarp.NewSigner(sk, networkID, sourceChainID) - backend, err := NewBackend(networkID, sourceChainID, warpSigner, blockClient, db, 500, nil) + messageSignatureCache := &cache.LRU[ids.ID, []byte]{Size: 500} + backend, err := NewBackend(networkID, sourceChainID, warpSigner, blockClient, db, messageSignatureCache, nil) require.NoError(err) blockHashPayload, err := payload.NewHash(blkID) @@ -138,11 +96,11 @@ func TestGetBlockSignature(t *testing.T) { expectedSig, err := warpSigner.Sign(unsignedMessage) require.NoError(err) - signature, err := backend.GetBlockSignature(blkID) + signature, err := backend.GetBlockSignature(context.TODO(), blkID) require.NoError(err) require.Equal(expectedSig, signature[:]) - _, err = backend.GetBlockSignature(ids.GenerateTestID()) + _, err = backend.GetBlockSignature(context.TODO(), ids.GenerateTestID()) require.Error(err) } @@ -154,7 +112,8 @@ func TestZeroSizedCache(t *testing.T) { warpSigner := avalancheWarp.NewSigner(sk, networkID, sourceChainID) // Verify zero sized cache works normally, because the lru cache will be initialized to size 1 for any size parameter <= 0. - backend, err := NewBackend(networkID, sourceChainID, warpSigner, nil, db, 0, nil) + messageSignatureCache := &cache.LRU[ids.ID, []byte]{Size: 0} + backend, err := NewBackend(networkID, sourceChainID, warpSigner, nil, db, messageSignatureCache, nil) require.NoError(t, err) // Add testUnsignedMessage to the warp backend @@ -162,8 +121,7 @@ func TestZeroSizedCache(t *testing.T) { require.NoError(t, err) // Verify that a signature is returned successfully, and compare to expected signature. - messageID := testUnsignedMessage.ID() - signature, err := backend.GetMessageSignature(messageID) + signature, err := backend.GetMessageSignature(context.TODO(), testUnsignedMessage) require.NoError(t, err) expectedSig, err := warpSigner.Sign(testUnsignedMessage) @@ -192,7 +150,7 @@ func TestOffChainMessages(t *testing.T) { require.NoError(err) require.Equal(testUnsignedMessage.Bytes(), msg.Bytes()) - signature, err := b.GetMessageSignature(testUnsignedMessage.ID()) + signature, err := b.GetMessageSignature(context.TODO(), testUnsignedMessage) require.NoError(err) expectedSignatureBytes, err := warpSigner.Sign(msg) require.NoError(err) @@ -208,7 +166,8 @@ func TestOffChainMessages(t *testing.T) { require := require.New(t) db := memdb.New() - backend, err := NewBackend(networkID, sourceChainID, warpSigner, nil, db, 0, test.offchainMessages) + messageSignatureCache := &cache.LRU[ids.ID, []byte]{Size: 0} + backend, err := NewBackend(networkID, sourceChainID, warpSigner, nil, db, messageSignatureCache, test.offchainMessages) require.ErrorIs(err, test.err) if test.check != nil { test.check(require, backend) diff --git a/warp/handlers/signature_request.go b/warp/handlers/signature_request.go index 8a8b4e4e1e..bb9c2c7855 100644 --- a/warp/handlers/signature_request.go +++ b/warp/handlers/signature_request.go @@ -16,7 +16,7 @@ import ( ) // SignatureRequestHandler serves warp signature requests. It is a peer.RequestHandler for message.MessageSignatureRequest. -// TODO: After E-Upgrade, this handler can be removed and SignatureRequestHandlerP2P is sufficient. +// TODO: After Etna, this handler can be removed and SignatureRequestHandlerP2P is sufficient. type SignatureRequestHandler struct { backend warp.Backend codec codec.Manager @@ -45,13 +45,20 @@ func (s *SignatureRequestHandler) OnMessageSignatureRequest(ctx context.Context, s.stats.UpdateMessageSignatureRequestTime(time.Since(startTime)) }() - signature, err := s.backend.GetMessageSignature(signatureRequest.MessageID) + var signature [bls.SignatureLen]byte + unsignedMessage, err := s.backend.GetMessage(signatureRequest.MessageID) if err != nil { - log.Debug("Unknown warp signature requested", "messageID", signatureRequest.MessageID) + log.Debug("Unknown warp message requested", "messageID", signatureRequest.MessageID) s.stats.IncMessageSignatureMiss() - signature = [bls.SignatureLen]byte{} } else { - s.stats.IncMessageSignatureHit() + sig, err := s.backend.GetMessageSignature(ctx, unsignedMessage) + if err != nil { + log.Debug("Unknown warp signature requested", "messageID", signatureRequest.MessageID) + s.stats.IncMessageSignatureMiss() + } else { + s.stats.IncMessageSignatureHit() + copy(signature[:], sig) + } } response := message.SignatureResponse{Signature: signature} @@ -73,13 +80,14 @@ func (s *SignatureRequestHandler) OnBlockSignatureRequest(ctx context.Context, n s.stats.UpdateBlockSignatureRequestTime(time.Since(startTime)) }() - signature, err := s.backend.GetBlockSignature(request.BlockID) + var signature [bls.SignatureLen]byte + sig, err := s.backend.GetBlockSignature(ctx, request.BlockID) if err != nil { log.Debug("Unknown warp signature requested", "blockID", request.BlockID) s.stats.IncBlockSignatureMiss() - signature = [bls.SignatureLen]byte{} } else { s.stats.IncBlockSignatureHit() + copy(signature[:], sig) } response := message.SignatureResponse{Signature: signature} diff --git a/warp/handlers/signature_request_p2p.go b/warp/handlers/signature_request_p2p.go deleted file mode 100644 index 47fe2d0908..0000000000 --- a/warp/handlers/signature_request_p2p.go +++ /dev/null @@ -1,151 +0,0 @@ -// (c) 2024, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package handlers - -import ( - "context" - "fmt" - "time" - - "github.com/ava-labs/avalanchego/codec" - "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/avalanchego/network/p2p" - "github.com/ava-labs/avalanchego/proto/pb/sdk" - "github.com/ava-labs/avalanchego/snow/engine/common" - "github.com/ava-labs/avalanchego/utils/crypto/bls" - avalancheWarp "github.com/ava-labs/avalanchego/vms/platformvm/warp" - "github.com/ava-labs/avalanchego/vms/platformvm/warp/payload" - "github.com/ava-labs/coreth/warp" - "google.golang.org/protobuf/proto" -) - -var _ p2p.Handler = (*SignatureRequestHandlerP2P)(nil) - -const ( - ErrFailedToParse = iota - ErrFailedToGetSig - ErrFailedToMarshal -) - -// SignatureRequestHandlerP2P serves warp signature requests using the p2p -// framework from avalanchego. It is a peer.RequestHandler for -// message.MessageSignatureRequest. -type SignatureRequestHandlerP2P struct { - backend warp.Backend - codec codec.Manager - stats *handlerStats -} - -func NewSignatureRequestHandlerP2P(backend warp.Backend, codec codec.Manager) *SignatureRequestHandlerP2P { - return &SignatureRequestHandlerP2P{ - backend: backend, - codec: codec, - stats: newStats(), - } -} - -func (s *SignatureRequestHandlerP2P) AppRequest( - ctx context.Context, - nodeID ids.NodeID, - deadline time.Time, - requestBytes []byte, -) ([]byte, *common.AppError) { - // Per ACP-118, the requestBytes are the serialized form of - // sdk.SignatureRequest. - req := new(sdk.SignatureRequest) - if err := proto.Unmarshal(requestBytes, req); err != nil { - return nil, &common.AppError{ - Code: ErrFailedToParse, - Message: "failed to unmarshal request: " + err.Error(), - } - } - - unsignedMessage, err := avalancheWarp.ParseUnsignedMessage(req.Message) - if err != nil { - return nil, &common.AppError{ - Code: ErrFailedToParse, - Message: "failed to parse unsigned message: " + err.Error(), - } - } - parsed, err := payload.Parse(unsignedMessage.Payload) - if err != nil { - return nil, &common.AppError{ - Code: ErrFailedToParse, - Message: "failed to parse payload: " + err.Error(), - } - } - - var sig [bls.SignatureLen]byte - switch p := parsed.(type) { - case *payload.AddressedCall: - // Note we pass the unsigned message ID to GetMessageSignature since - // that is what the backend expects. - // However, we verify the types and format of the payload to ensure - // the message conforms to the ACP-118 spec. - sig, err = s.GetMessageSignature(unsignedMessage.ID()) - if err != nil { - s.stats.IncMessageSignatureMiss() - } else { - s.stats.IncMessageSignatureHit() - } - case *payload.Hash: - sig, err = s.GetBlockSignature(p.Hash) - if err != nil { - s.stats.IncBlockSignatureMiss() - } else { - s.stats.IncBlockSignatureHit() - } - default: - return nil, &common.AppError{ - Code: ErrFailedToParse, - Message: fmt.Sprintf("unknown payload type: %T", p), - } - } - if err != nil { - return nil, &common.AppError{ - Code: ErrFailedToGetSig, - Message: "failed to get signature: " + err.Error(), - } - } - - // Per ACP-118, the responseBytes are the serialized form of - // sdk.SignatureResponse. - resp := &sdk.SignatureResponse{Signature: sig[:]} - respBytes, err := proto.Marshal(resp) - if err != nil { - return nil, &common.AppError{ - Code: ErrFailedToMarshal, - Message: "failed to marshal response: " + err.Error(), - } - } - return respBytes, nil -} - -func (s *SignatureRequestHandlerP2P) GetMessageSignature(messageID ids.ID) ([bls.SignatureLen]byte, error) { - startTime := time.Now() - s.stats.IncMessageSignatureRequest() - - // Always report signature request time - defer func() { - s.stats.UpdateMessageSignatureRequestTime(time.Since(startTime)) - }() - - return s.backend.GetMessageSignature(messageID) -} - -func (s *SignatureRequestHandlerP2P) GetBlockSignature(blockID ids.ID) ([bls.SignatureLen]byte, error) { - startTime := time.Now() - s.stats.IncBlockSignatureRequest() - - // Always report signature request time - defer func() { - s.stats.UpdateBlockSignatureRequestTime(time.Since(startTime)) - }() - - return s.backend.GetBlockSignature(blockID) -} - -func (s *SignatureRequestHandlerP2P) AppGossip( - ctx context.Context, nodeID ids.NodeID, gossipBytes []byte) { -} diff --git a/warp/handlers/signature_request_p2p_test.go b/warp/handlers/signature_request_p2p_test.go deleted file mode 100644 index 36d6507771..0000000000 --- a/warp/handlers/signature_request_p2p_test.go +++ /dev/null @@ -1,232 +0,0 @@ -// (c) 2024, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package handlers - -import ( - "context" - "testing" - "time" - - "github.com/ava-labs/avalanchego/database/memdb" - "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/avalanchego/proto/pb/sdk" - "github.com/ava-labs/avalanchego/snow/engine/common" - "github.com/ava-labs/avalanchego/utils/crypto/bls" - avalancheWarp "github.com/ava-labs/avalanchego/vms/platformvm/warp" - "github.com/ava-labs/avalanchego/vms/platformvm/warp/payload" - "github.com/ava-labs/coreth/plugin/evm/message" - "github.com/ava-labs/coreth/utils" - "github.com/ava-labs/coreth/warp" - "github.com/ava-labs/coreth/warp/warptest" - "github.com/stretchr/testify/require" - "google.golang.org/protobuf/proto" -) - -func TestMessageSignatureHandlerP2P(t *testing.T) { - database := memdb.New() - snowCtx := utils.TestSnowContext() - blsSecretKey, err := bls.NewSecretKey() - require.NoError(t, err) - warpSigner := avalancheWarp.NewSigner(blsSecretKey, snowCtx.NetworkID, snowCtx.ChainID) - - addressedPayload, err := payload.NewAddressedCall([]byte{1, 2, 3}, []byte{1, 2, 3}) - require.NoError(t, err) - offchainMessage, err := avalancheWarp.NewUnsignedMessage(snowCtx.NetworkID, snowCtx.ChainID, addressedPayload.Bytes()) - require.NoError(t, err) - - backend, err := warp.NewBackend(snowCtx.NetworkID, snowCtx.ChainID, warpSigner, warptest.EmptyBlockClient, database, 100, [][]byte{offchainMessage.Bytes()}) - require.NoError(t, err) - - offchainPayload, err := payload.NewAddressedCall([]byte{0, 0, 0}, []byte("test")) - require.NoError(t, err) - msg, err := avalancheWarp.NewUnsignedMessage(snowCtx.NetworkID, snowCtx.ChainID, offchainPayload.Bytes()) - require.NoError(t, err) - messageID := msg.ID() - require.NoError(t, backend.AddMessage(msg)) - signature, err := backend.GetMessageSignature(messageID) - require.NoError(t, err) - offchainSignature, err := backend.GetMessageSignature(offchainMessage.ID()) - require.NoError(t, err) - - unknownPayload, err := payload.NewAddressedCall([]byte{0, 0, 0}, []byte("unknown message")) - require.NoError(t, err) - unknownMessage, err := avalancheWarp.NewUnsignedMessage(snowCtx.NetworkID, snowCtx.ChainID, unknownPayload.Bytes()) - require.NoError(t, err) - - tests := map[string]struct { - setup func() (request sdk.SignatureRequest, expectedResponse []byte) - verifyStats func(t *testing.T, stats *handlerStats) - err error - }{ - "known message": { - setup: func() (request sdk.SignatureRequest, expectedResponse []byte) { - return sdk.SignatureRequest{Message: msg.Bytes()}, signature[:] - }, - verifyStats: func(t *testing.T, stats *handlerStats) { - require.EqualValues(t, 1, stats.messageSignatureRequest.Snapshot().Count()) - require.EqualValues(t, 1, stats.messageSignatureHit.Snapshot().Count()) - require.EqualValues(t, 0, stats.messageSignatureMiss.Snapshot().Count()) - require.EqualValues(t, 0, stats.blockSignatureRequest.Snapshot().Count()) - require.EqualValues(t, 0, stats.blockSignatureHit.Snapshot().Count()) - require.EqualValues(t, 0, stats.blockSignatureMiss.Snapshot().Count()) - }, - }, - "offchain message": { - setup: func() (request sdk.SignatureRequest, expectedResponse []byte) { - return sdk.SignatureRequest{Message: offchainMessage.Bytes()}, offchainSignature[:] - }, - verifyStats: func(t *testing.T, stats *handlerStats) { - require.EqualValues(t, 1, stats.messageSignatureRequest.Snapshot().Count()) - require.EqualValues(t, 1, stats.messageSignatureHit.Snapshot().Count()) - require.EqualValues(t, 0, stats.messageSignatureMiss.Snapshot().Count()) - require.EqualValues(t, 0, stats.blockSignatureRequest.Snapshot().Count()) - require.EqualValues(t, 0, stats.blockSignatureHit.Snapshot().Count()) - require.EqualValues(t, 0, stats.blockSignatureMiss.Snapshot().Count()) - }, - }, - "unknown message": { - setup: func() (request sdk.SignatureRequest, expectedResponse []byte) { - return sdk.SignatureRequest{Message: unknownMessage.Bytes()}, nil - }, - verifyStats: func(t *testing.T, stats *handlerStats) { - require.EqualValues(t, 1, stats.messageSignatureRequest.Snapshot().Count()) - require.EqualValues(t, 0, stats.messageSignatureHit.Snapshot().Count()) - require.EqualValues(t, 1, stats.messageSignatureMiss.Snapshot().Count()) - require.EqualValues(t, 0, stats.blockSignatureRequest.Snapshot().Count()) - require.EqualValues(t, 0, stats.blockSignatureHit.Snapshot().Count()) - require.EqualValues(t, 0, stats.blockSignatureMiss.Snapshot().Count()) - }, - err: &common.AppError{Code: ErrFailedToGetSig}, - }, - } - - for name, test := range tests { - t.Run(name, func(t *testing.T) { - handler := NewSignatureRequestHandlerP2P(backend, message.Codec) - handler.stats.Clear() - - request, expectedResponse := test.setup() - requestBytes, err := proto.Marshal(&request) - require.NoError(t, err) - responseBytes, appErr := handler.AppRequest(context.Background(), ids.GenerateTestNodeID(), time.Time{}, requestBytes) - if test.err != nil { - require.ErrorIs(t, appErr, test.err) - } else { - require.Nil(t, appErr) - } - - test.verifyStats(t, handler.stats) - - // If the expected response is empty, assert that the handler returns an empty response and return early. - if len(expectedResponse) == 0 { - require.Len(t, responseBytes, 0, "expected response to be empty") - return - } - var response sdk.SignatureResponse - err = proto.Unmarshal(responseBytes, &response) - require.NoError(t, err, "error unmarshalling SignatureResponse") - - require.Equal(t, expectedResponse, response.Signature) - }) - } -} - -func TestBlockSignatureHandlerP2P(t *testing.T) { - database := memdb.New() - snowCtx := utils.TestSnowContext() - blsSecretKey, err := bls.NewSecretKey() - require.NoError(t, err) - - warpSigner := avalancheWarp.NewSigner(blsSecretKey, snowCtx.NetworkID, snowCtx.ChainID) - blkID := ids.GenerateTestID() - blockClient := warptest.MakeBlockClient(blkID) - backend, err := warp.NewBackend( - snowCtx.NetworkID, - snowCtx.ChainID, - warpSigner, - blockClient, - database, - 100, - nil, - ) - require.NoError(t, err) - - signature, err := backend.GetBlockSignature(blkID) - require.NoError(t, err) - unknownBlockID := ids.GenerateTestID() - - toMessageBytes := func(id ids.ID) []byte { - idPayload, err := payload.NewHash(id) - require.NoError(t, err) - - msg, err := avalancheWarp.NewUnsignedMessage(snowCtx.NetworkID, snowCtx.ChainID, idPayload.Bytes()) - require.NoError(t, err) - - return msg.Bytes() - } - - tests := map[string]struct { - setup func() (request sdk.SignatureRequest, expectedResponse []byte) - verifyStats func(t *testing.T, stats *handlerStats) - err error - }{ - "known block": { - setup: func() (request sdk.SignatureRequest, expectedResponse []byte) { - return sdk.SignatureRequest{Message: toMessageBytes(blkID)}, signature[:] - }, - verifyStats: func(t *testing.T, stats *handlerStats) { - require.EqualValues(t, 0, stats.messageSignatureRequest.Snapshot().Count()) - require.EqualValues(t, 0, stats.messageSignatureHit.Snapshot().Count()) - require.EqualValues(t, 0, stats.messageSignatureMiss.Snapshot().Count()) - require.EqualValues(t, 1, stats.blockSignatureRequest.Snapshot().Count()) - require.EqualValues(t, 1, stats.blockSignatureHit.Snapshot().Count()) - require.EqualValues(t, 0, stats.blockSignatureMiss.Snapshot().Count()) - }, - }, - "unknown block": { - setup: func() (request sdk.SignatureRequest, expectedResponse []byte) { - return sdk.SignatureRequest{Message: toMessageBytes(unknownBlockID)}, nil - }, - verifyStats: func(t *testing.T, stats *handlerStats) { - require.EqualValues(t, 0, stats.messageSignatureRequest.Snapshot().Count()) - require.EqualValues(t, 0, stats.messageSignatureHit.Snapshot().Count()) - require.EqualValues(t, 0, stats.messageSignatureMiss.Snapshot().Count()) - require.EqualValues(t, 1, stats.blockSignatureRequest.Snapshot().Count()) - require.EqualValues(t, 0, stats.blockSignatureHit.Snapshot().Count()) - require.EqualValues(t, 1, stats.blockSignatureMiss.Snapshot().Count()) - }, - err: &common.AppError{Code: ErrFailedToGetSig}, - }, - } - - for name, test := range tests { - t.Run(name, func(t *testing.T) { - handler := NewSignatureRequestHandlerP2P(backend, message.Codec) - handler.stats.Clear() - - request, expectedResponse := test.setup() - requestBytes, err := proto.Marshal(&request) - require.NoError(t, err) - responseBytes, appErr := handler.AppRequest(context.Background(), ids.GenerateTestNodeID(), time.Time{}, requestBytes) - if test.err != nil { - require.ErrorIs(t, appErr, test.err) - } else { - require.Nil(t, appErr) - } - - test.verifyStats(t, handler.stats) - - // If the expected response is empty, assert that the handler returns an empty response and return early. - if len(expectedResponse) == 0 { - require.Len(t, responseBytes, 0, "expected response to be empty") - return - } - var response sdk.SignatureResponse - err = proto.Unmarshal(responseBytes, &response) - require.NoError(t, err, "error unmarshalling SignatureResponse") - - require.Equal(t, expectedResponse, response.Signature) - }) - } -} diff --git a/warp/handlers/signature_request_test.go b/warp/handlers/signature_request_test.go index b50a1b519b..dd2271988b 100644 --- a/warp/handlers/signature_request_test.go +++ b/warp/handlers/signature_request_test.go @@ -7,6 +7,7 @@ import ( "context" "testing" + "github.com/ava-labs/avalanchego/cache" "github.com/ava-labs/avalanchego/database/memdb" "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/utils/crypto/bls" @@ -31,16 +32,17 @@ func TestMessageSignatureHandler(t *testing.T) { offchainMessage, err := avalancheWarp.NewUnsignedMessage(snowCtx.NetworkID, snowCtx.ChainID, addressedPayload.Bytes()) require.NoError(t, err) - backend, err := warp.NewBackend(snowCtx.NetworkID, snowCtx.ChainID, warpSigner, warptest.EmptyBlockClient, database, 100, [][]byte{offchainMessage.Bytes()}) + messageSignatureCache := &cache.LRU[ids.ID, []byte]{Size: 100} + backend, err := warp.NewBackend(snowCtx.NetworkID, snowCtx.ChainID, warpSigner, warptest.EmptyBlockClient, database, messageSignatureCache, [][]byte{offchainMessage.Bytes()}) require.NoError(t, err) msg, err := avalancheWarp.NewUnsignedMessage(snowCtx.NetworkID, snowCtx.ChainID, []byte("test")) require.NoError(t, err) messageID := msg.ID() require.NoError(t, backend.AddMessage(msg)) - signature, err := backend.GetMessageSignature(messageID) + signature, err := backend.GetMessageSignature(context.TODO(), msg) require.NoError(t, err) - offchainSignature, err := backend.GetMessageSignature(offchainMessage.ID()) + offchainSignature, err := backend.GetMessageSignature(context.TODO(), offchainMessage) require.NoError(t, err) unknownMessageID := ids.GenerateTestID() @@ -101,7 +103,6 @@ func TestMessageSignatureHandler(t *testing.T) { for name, test := range tests { t.Run(name, func(t *testing.T) { handler := NewSignatureRequestHandler(backend, message.Codec) - handler.stats.Clear() request, expectedResponse := test.setup() responseBytes, err := handler.OnMessageSignatureRequest(context.Background(), ids.GenerateTestNodeID(), 1, request) @@ -132,18 +133,19 @@ func TestBlockSignatureHandler(t *testing.T) { warpSigner := avalancheWarp.NewSigner(blsSecretKey, snowCtx.NetworkID, snowCtx.ChainID) blkID := ids.GenerateTestID() blockClient := warptest.MakeBlockClient(blkID) + messageSignatureCache := &cache.LRU[ids.ID, []byte]{Size: 100} backend, err := warp.NewBackend( snowCtx.NetworkID, snowCtx.ChainID, warpSigner, blockClient, database, - 100, + messageSignatureCache, nil, ) require.NoError(t, err) - signature, err := backend.GetBlockSignature(blkID) + signature, err := backend.GetBlockSignature(context.TODO(), blkID) require.NoError(t, err) unknownMessageID := ids.GenerateTestID() @@ -188,7 +190,6 @@ func TestBlockSignatureHandler(t *testing.T) { for name, test := range tests { t.Run(name, func(t *testing.T) { handler := NewSignatureRequestHandler(backend, message.Codec) - handler.stats.Clear() request, expectedResponse := test.setup() responseBytes, err := handler.OnBlockSignatureRequest(context.Background(), ids.GenerateTestNodeID(), 1, request) diff --git a/warp/handlers/stats.go b/warp/handlers/stats.go index 9e2ea373aa..ef3f31ae9a 100644 --- a/warp/handlers/stats.go +++ b/warp/handlers/stats.go @@ -24,14 +24,14 @@ type handlerStats struct { func newStats() *handlerStats { return &handlerStats{ - messageSignatureRequest: metrics.GetOrRegisterCounter("message_signature_request_count", nil), - messageSignatureHit: metrics.GetOrRegisterCounter("message_signature_request_hit", nil), - messageSignatureMiss: metrics.GetOrRegisterCounter("message_signature_request_miss", nil), - messageSignatureRequestDuration: metrics.GetOrRegisterGauge("message_signature_request_duration", nil), - blockSignatureRequest: metrics.GetOrRegisterCounter("block_signature_request_count", nil), - blockSignatureHit: metrics.GetOrRegisterCounter("block_signature_request_hit", nil), - blockSignatureMiss: metrics.GetOrRegisterCounter("block_signature_request_miss", nil), - blockSignatureRequestDuration: metrics.GetOrRegisterGauge("block_signature_request_duration", nil), + messageSignatureRequest: metrics.NewRegisteredCounter("message_signature_request_count", nil), + messageSignatureHit: metrics.NewRegisteredCounter("message_signature_request_hit", nil), + messageSignatureMiss: metrics.NewRegisteredCounter("message_signature_request_miss", nil), + messageSignatureRequestDuration: metrics.NewRegisteredGauge("message_signature_request_duration", nil), + blockSignatureRequest: metrics.NewRegisteredCounter("block_signature_request_count", nil), + blockSignatureHit: metrics.NewRegisteredCounter("block_signature_request_hit", nil), + blockSignatureMiss: metrics.NewRegisteredCounter("block_signature_request_miss", nil), + blockSignatureRequestDuration: metrics.NewRegisteredGauge("block_signature_request_duration", nil), } } @@ -47,13 +47,3 @@ func (h *handlerStats) IncBlockSignatureMiss() { h.blockSignatureMiss.Inc(1) func (h *handlerStats) UpdateBlockSignatureRequestTime(duration time.Duration) { h.blockSignatureRequestDuration.Inc(int64(duration)) } -func (h *handlerStats) Clear() { - h.messageSignatureRequest.Clear() - h.messageSignatureHit.Clear() - h.messageSignatureMiss.Clear() - h.messageSignatureRequestDuration.Update(0) - h.blockSignatureRequest.Clear() - h.blockSignatureHit.Clear() - h.blockSignatureMiss.Clear() - h.blockSignatureRequestDuration.Update(0) -} diff --git a/warp/service.go b/warp/service.go index 8b92da80db..610fc85a91 100644 --- a/warp/service.go +++ b/warp/service.go @@ -54,7 +54,11 @@ func (a *API) GetMessage(ctx context.Context, messageID ids.ID) (hexutil.Bytes, // GetMessageSignature returns the BLS signature associated with a messageID. func (a *API) GetMessageSignature(ctx context.Context, messageID ids.ID) (hexutil.Bytes, error) { - signature, err := a.backend.GetMessageSignature(messageID) + unsignedMessage, err := a.backend.GetMessage(messageID) + if err != nil { + return nil, fmt.Errorf("failed to get message %s with error %w", messageID, err) + } + signature, err := a.backend.GetMessageSignature(ctx, unsignedMessage) if err != nil { return nil, fmt.Errorf("failed to get signature for message %s with error %w", messageID, err) } @@ -63,7 +67,7 @@ func (a *API) GetMessageSignature(ctx context.Context, messageID ids.ID) (hexuti // GetBlockSignature returns the BLS signature associated with a blockID. func (a *API) GetBlockSignature(ctx context.Context, blockID ids.ID) (hexutil.Bytes, error) { - signature, err := a.backend.GetBlockSignature(blockID) + signature, err := a.backend.GetBlockSignature(ctx, blockID) if err != nil { return nil, fmt.Errorf("failed to get signature for block %s with error %w", blockID, err) } diff --git a/warp/verifier_backend.go b/warp/verifier_backend.go new file mode 100644 index 0000000000..c70563c585 --- /dev/null +++ b/warp/verifier_backend.go @@ -0,0 +1,64 @@ +// (c) 2024, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package warp + +import ( + "context" + "fmt" + + "github.com/ava-labs/avalanchego/snow/engine/common" + avalancheWarp "github.com/ava-labs/avalanchego/vms/platformvm/warp" + "github.com/ava-labs/avalanchego/vms/platformvm/warp/payload" +) + +const ( + ParseErrCode = iota + 1 + VerifyErrCode +) + +// Verify verifies the signature of the message +// It also implements the acp118.Verifier interface +func (b *backend) Verify(ctx context.Context, unsignedMessage *avalancheWarp.UnsignedMessage, _ []byte) *common.AppError { + messageID := unsignedMessage.ID() + // Known on-chain messages should be signed + if _, err := b.GetMessage(messageID); err == nil { + return nil + } + + parsed, err := payload.Parse(unsignedMessage.Payload) + if err != nil { + b.stats.IncMessageParseFail() + return &common.AppError{ + Code: ParseErrCode, + Message: "failed to parse payload: " + err.Error(), + } + } + + switch p := parsed.(type) { + case *payload.Hash: + return b.verifyBlockMessage(ctx, p) + default: + b.stats.IncMessageParseFail() + return &common.AppError{ + Code: ParseErrCode, + Message: fmt.Sprintf("unknown payload type: %T", p), + } + } +} + +// verifyBlockMessage returns nil if blockHashPayload contains the ID +// of an accepted block indicating it should be signed by the VM. +func (b *backend) verifyBlockMessage(ctx context.Context, blockHashPayload *payload.Hash) *common.AppError { + blockID := blockHashPayload.Hash + _, err := b.blockClient.GetAcceptedBlock(ctx, blockID) + if err != nil { + b.stats.IncBlockSignatureValidationFail() + return &common.AppError{ + Code: VerifyErrCode, + Message: fmt.Sprintf("failed to get block %s: %s", blockID, err.Error()), + } + } + + return nil +} diff --git a/warp/verifier_backend_test.go b/warp/verifier_backend_test.go new file mode 100644 index 0000000000..5ea3a37e0f --- /dev/null +++ b/warp/verifier_backend_test.go @@ -0,0 +1,255 @@ +// (c) 2024, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package warp + +import ( + "context" + "testing" + "time" + + "github.com/ava-labs/avalanchego/cache" + "github.com/ava-labs/avalanchego/database/memdb" + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/network/p2p/acp118" + "github.com/ava-labs/avalanchego/proto/pb/sdk" + "github.com/ava-labs/avalanchego/snow/engine/common" + "github.com/ava-labs/avalanchego/utils/crypto/bls" + avalancheWarp "github.com/ava-labs/avalanchego/vms/platformvm/warp" + "github.com/ava-labs/avalanchego/vms/platformvm/warp/payload" + "github.com/ava-labs/coreth/utils" + "github.com/ava-labs/coreth/warp/warptest" + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/proto" +) + +func TestAddressedCallSignatures(t *testing.T) { + database := memdb.New() + snowCtx := utils.TestSnowContext() + blsSecretKey, err := bls.NewSecretKey() + require.NoError(t, err) + warpSigner := avalancheWarp.NewSigner(blsSecretKey, snowCtx.NetworkID, snowCtx.ChainID) + + offChainPayload, err := payload.NewAddressedCall([]byte{1, 2, 3}, []byte{1, 2, 3}) + require.NoError(t, err) + offchainMessage, err := avalancheWarp.NewUnsignedMessage(snowCtx.NetworkID, snowCtx.ChainID, offChainPayload.Bytes()) + require.NoError(t, err) + offchainSignature, err := warpSigner.Sign(offchainMessage) + require.NoError(t, err) + + tests := map[string]struct { + setup func(backend Backend) (request []byte, expectedResponse []byte) + verifyStats func(t *testing.T, stats *verifierStats) + err error + }{ + "known message": { + setup: func(backend Backend) (request []byte, expectedResponse []byte) { + knownPayload, err := payload.NewAddressedCall([]byte{0, 0, 0}, []byte("test")) + require.NoError(t, err) + msg, err := avalancheWarp.NewUnsignedMessage(snowCtx.NetworkID, snowCtx.ChainID, knownPayload.Bytes()) + require.NoError(t, err) + signature, err := warpSigner.Sign(msg) + require.NoError(t, err) + + backend.AddMessage(msg) + return msg.Bytes(), signature[:] + }, + verifyStats: func(t *testing.T, stats *verifierStats) { + require.EqualValues(t, 0, stats.messageParseFail.Snapshot().Count()) + require.EqualValues(t, 0, stats.blockSignatureValidationFail.Snapshot().Count()) + }, + }, + "offchain message": { + setup: func(_ Backend) (request []byte, expectedResponse []byte) { + return offchainMessage.Bytes(), offchainSignature[:] + }, + verifyStats: func(t *testing.T, stats *verifierStats) { + require.EqualValues(t, 0, stats.messageParseFail.Snapshot().Count()) + require.EqualValues(t, 0, stats.blockSignatureValidationFail.Snapshot().Count()) + }, + }, + "unknown message": { + setup: func(_ Backend) (request []byte, expectedResponse []byte) { + unknownPayload, err := payload.NewAddressedCall([]byte{0, 0, 0}, []byte("unknown message")) + require.NoError(t, err) + unknownMessage, err := avalancheWarp.NewUnsignedMessage(snowCtx.NetworkID, snowCtx.ChainID, unknownPayload.Bytes()) + require.NoError(t, err) + return unknownMessage.Bytes(), nil + }, + verifyStats: func(t *testing.T, stats *verifierStats) { + require.EqualValues(t, 1, stats.messageParseFail.Snapshot().Count()) + require.EqualValues(t, 0, stats.blockSignatureValidationFail.Snapshot().Count()) + }, + err: &common.AppError{Code: ParseErrCode}, + }, + } + + for name, test := range tests { + for _, withCache := range []bool{true, false} { + if withCache { + name += "_with_cache" + } else { + name += "_no_cache" + } + t.Run(name, func(t *testing.T) { + var sigCache cache.Cacher[ids.ID, []byte] + if withCache { + sigCache = &cache.LRU[ids.ID, []byte]{Size: 100} + } else { + sigCache = &cache.Empty[ids.ID, []byte]{} + } + warpBackend, err := NewBackend(snowCtx.NetworkID, snowCtx.ChainID, warpSigner, warptest.EmptyBlockClient, database, sigCache, [][]byte{offchainMessage.Bytes()}) + require.NoError(t, err) + handler := acp118.NewCachedHandler(sigCache, warpBackend, warpSigner) + + requestBytes, expectedResponse := test.setup(warpBackend) + protoMsg := &sdk.SignatureRequest{Message: requestBytes} + protoBytes, err := proto.Marshal(protoMsg) + require.NoError(t, err) + responseBytes, appErr := handler.AppRequest(context.Background(), ids.GenerateTestNodeID(), time.Time{}, protoBytes) + if test.err != nil { + require.Error(t, appErr) + require.ErrorIs(t, appErr, test.err) + } else { + require.Nil(t, appErr) + } + + test.verifyStats(t, warpBackend.(*backend).stats) + + // If the expected response is empty, assert that the handler returns an empty response and return early. + if len(expectedResponse) == 0 { + require.Len(t, responseBytes, 0, "expected response to be empty") + return + } + // check cache is populated + if withCache { + require.NotZero(t, warpBackend.(*backend).signatureCache.Len()) + } else { + require.Zero(t, warpBackend.(*backend).signatureCache.Len()) + } + response := &sdk.SignatureResponse{} + require.NoError(t, proto.Unmarshal(responseBytes, response)) + require.NoError(t, err, "error unmarshalling SignatureResponse") + + require.Equal(t, expectedResponse, response.Signature) + }) + } + } +} + +func TestBlockSignatures(t *testing.T) { + database := memdb.New() + snowCtx := utils.TestSnowContext() + blsSecretKey, err := bls.NewSecretKey() + require.NoError(t, err) + + warpSigner := avalancheWarp.NewSigner(blsSecretKey, snowCtx.NetworkID, snowCtx.ChainID) + knownBlkID := ids.GenerateTestID() + blockClient := warptest.MakeBlockClient(knownBlkID) + + toMessageBytes := func(id ids.ID) []byte { + idPayload, err := payload.NewHash(id) + if err != nil { + panic(err) + } + + msg, err := avalancheWarp.NewUnsignedMessage(snowCtx.NetworkID, snowCtx.ChainID, idPayload.Bytes()) + if err != nil { + panic(err) + } + + return msg.Bytes() + } + + tests := map[string]struct { + setup func() (request []byte, expectedResponse []byte) + verifyStats func(t *testing.T, stats *verifierStats) + err error + }{ + "known block": { + setup: func() (request []byte, expectedResponse []byte) { + hashPayload, err := payload.NewHash(knownBlkID) + require.NoError(t, err) + unsignedMessage, err := avalancheWarp.NewUnsignedMessage(snowCtx.NetworkID, snowCtx.ChainID, hashPayload.Bytes()) + require.NoError(t, err) + signature, err := warpSigner.Sign(unsignedMessage) + require.NoError(t, err) + return toMessageBytes(knownBlkID), signature[:] + }, + verifyStats: func(t *testing.T, stats *verifierStats) { + require.EqualValues(t, 0, stats.blockSignatureValidationFail.Snapshot().Count()) + require.EqualValues(t, 0, stats.messageParseFail.Snapshot().Count()) + }, + }, + "unknown block": { + setup: func() (request []byte, expectedResponse []byte) { + unknownBlockID := ids.GenerateTestID() + return toMessageBytes(unknownBlockID), nil + }, + verifyStats: func(t *testing.T, stats *verifierStats) { + require.EqualValues(t, 1, stats.blockSignatureValidationFail.Snapshot().Count()) + require.EqualValues(t, 0, stats.messageParseFail.Snapshot().Count()) + }, + err: &common.AppError{Code: VerifyErrCode}, + }, + } + + for name, test := range tests { + for _, withCache := range []bool{true, false} { + if withCache { + name += "_with_cache" + } else { + name += "_no_cache" + } + t.Run(name, func(t *testing.T) { + var sigCache cache.Cacher[ids.ID, []byte] + if withCache { + sigCache = &cache.LRU[ids.ID, []byte]{Size: 100} + } else { + sigCache = &cache.Empty[ids.ID, []byte]{} + } + warpBackend, err := NewBackend( + snowCtx.NetworkID, + snowCtx.ChainID, + warpSigner, + blockClient, + database, + sigCache, + nil, + ) + require.NoError(t, err) + handler := acp118.NewCachedHandler(sigCache, warpBackend, warpSigner) + + requestBytes, expectedResponse := test.setup() + protoMsg := &sdk.SignatureRequest{Message: requestBytes} + protoBytes, err := proto.Marshal(protoMsg) + require.NoError(t, err) + responseBytes, appErr := handler.AppRequest(context.Background(), ids.GenerateTestNodeID(), time.Time{}, protoBytes) + if test.err != nil { + require.NotNil(t, appErr) + require.ErrorIs(t, test.err, appErr) + } else { + require.Nil(t, appErr) + } + + test.verifyStats(t, warpBackend.(*backend).stats) + + // If the expected response is empty, assert that the handler returns an empty response and return early. + if len(expectedResponse) == 0 { + require.Len(t, responseBytes, 0, "expected response to be empty") + return + } + // check cache is populated + if withCache { + require.NotZero(t, warpBackend.(*backend).signatureCache.Len()) + } else { + require.Zero(t, warpBackend.(*backend).signatureCache.Len()) + } + var response sdk.SignatureResponse + err = proto.Unmarshal(responseBytes, &response) + require.NoError(t, err, "error unmarshalling SignatureResponse") + require.Equal(t, expectedResponse, response.Signature) + }) + } + } +} diff --git a/warp/verifier_stats.go b/warp/verifier_stats.go new file mode 100644 index 0000000000..3ee90312d9 --- /dev/null +++ b/warp/verifier_stats.go @@ -0,0 +1,29 @@ +// (c) 2023, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package warp + +import ( + "github.com/ava-labs/coreth/metrics" +) + +type verifierStats struct { + messageParseFail metrics.Counter + // BlockRequest metrics + blockSignatureValidationFail metrics.Counter +} + +func newVerifierStats() *verifierStats { + return &verifierStats{ + messageParseFail: metrics.NewRegisteredCounter("message_parse_fail", nil), + blockSignatureValidationFail: metrics.NewRegisteredCounter("block_signature_validation_fail", nil), + } +} + +func (h *verifierStats) IncBlockSignatureValidationFail() { + h.blockSignatureValidationFail.Inc(1) +} + +func (h *verifierStats) IncMessageParseFail() { + h.messageParseFail.Inc(1) +}