diff --git a/core/blockchain.go b/core/blockchain.go index d279b0e6d4..efe2f01e7a 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -331,6 +331,7 @@ func NewBlockChain( } // Open trie database with provided config triedb := trie.NewDatabase(db, cacheConfig.triedbConfig()) + triedb.EnableRefCounting() // Setup the genesis block, commit the provided genesis specification // to database if the genesis block is not present yet, or load the @@ -1239,9 +1240,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 @@ -1793,9 +1794,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] @@ -1936,7 +1937,6 @@ 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{}) { triedb.Dereference(previousRoot) } diff --git a/core/chain_makers.go b/core/chain_makers.go index 599591182c..bf46a0e6ca 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -297,7 +297,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 bb8de14c80..862861a694 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -346,7 +346,7 @@ func (g *Genesis) toBlock(db ethdb.Database, triedb *trie.Database) *types.Block } } - 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/state/state_test.go b/core/state/state_test.go index ad096e9cbf..5fdca04479 100644 --- a/core/state/state_test.go +++ b/core/state/state_test.go @@ -70,7 +70,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{} diff --git a/core/state/statedb.go b/core/state/statedb.go index 6d49444cfb..060f80fd38 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -1262,14 +1262,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 @@ -1279,7 +1279,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) @@ -1388,14 +1388,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 7187d574ec..38c11c0e7b 100644 --- a/core/state/statedb_fuzz_test.go +++ b/core/state/statedb_fuzz_test.go @@ -232,7 +232,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 19f1a5198f..e8871d1598 100644 --- a/core/state/statedb_test.go +++ b/core/state/statedb_test.go @@ -126,7 +126,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) } @@ -134,7 +134,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) } @@ -542,7 +542,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() @@ -630,7 +630,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(big.NewInt(42)) != 0 { t.Fatalf("state post-commit balance mismatch: have %v, want %v", balance, 42) @@ -744,7 +744,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(big.NewInt(0)) != 0 { t.Fatalf("unexpected balance: have %v", balance) @@ -778,7 +778,7 @@ func TestDeleteCreateRevert(t *testing.T) { addr := common.BytesToAddress([]byte("so")) state.SetBalance(addr, big.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 @@ -790,7 +790,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 { @@ -833,7 +833,7 @@ func testMissingTrieNodes(t *testing.T, scheme string) { a2 := common.BytesToAddress([]byte("another")) state.SetBalance(a2, big.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 triedb.Commit(root, false) @@ -857,7 +857,7 @@ func testMissingTrieNodes(t *testing.T, scheme string) { } // Modify the state state.SetBalance(addr, big.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) } @@ -1043,7 +1043,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(big.Int)) @@ -1100,14 +1100,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 @@ -1115,7 +1115,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) @@ -1124,7 +1124,7 @@ func TestMultiCoinSnapshot(t *testing.T) { stateDB, _ = New(root, sdb, snapTree) stateDB.AddBalance(addr, big.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) } @@ -1146,7 +1146,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) } @@ -1206,7 +1206,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) } @@ -1285,7 +1285,7 @@ func TestResetObject(t *testing.T) { state.CreateAccount(addr) state.SetBalance(addr, big.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) @@ -1316,7 +1316,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 22eda7fa8d..1aa36bf211 100644 --- a/core/state/sync_test.go +++ b/core/state/sync_test.go @@ -75,7 +75,7 @@ func makeTestState(scheme string) (ethdb.Database, Database, *trie.Database, com } 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/txpool/blobpool/blobpool_test.go b/core/txpool/blobpool/blobpool_test.go index ecd15f828f..1f4e7274dc 100644 --- a/core/txpool/blobpool/blobpool_test.go +++ b/core/txpool/blobpool/blobpool_test.go @@ -549,7 +549,7 @@ func TestOpenDrops(t *testing.T) { statedb.AddBalance(crypto.PubkeyToAddress(exceeder.PublicKey), big.NewInt(1000000)) statedb.AddBalance(crypto.PubkeyToAddress(overdrafter.PublicKey), big.NewInt(1000000)) statedb.AddBalance(crypto.PubkeyToAddress(overcapper.PublicKey), big.NewInt(10000000)) - statedb.Commit(0, true, false) + statedb.Commit(0, true) chain := &testBlockChain{ config: testChainConfig, @@ -664,7 +664,7 @@ func TestOpenIndex(t *testing.T) { // Create a blob pool out of the pre-seeded data statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewDatabase(memorydb.New())), nil) statedb.AddBalance(addr, big.NewInt(1_000_000_000)) - statedb.Commit(0, true, false) + statedb.Commit(0, true) chain := &testBlockChain{ config: testChainConfig, @@ -767,7 +767,7 @@ func TestOpenHeap(t *testing.T) { statedb.AddBalance(addr1, big.NewInt(1_000_000_000)) statedb.AddBalance(addr2, big.NewInt(1_000_000_000)) statedb.AddBalance(addr3, big.NewInt(1_000_000_000)) - statedb.Commit(0, true, false) + statedb.Commit(0, true) chain := &testBlockChain{ config: testChainConfig, @@ -848,7 +848,7 @@ func TestOpenCap(t *testing.T) { statedb.AddBalance(addr1, big.NewInt(params.Ether)) statedb.AddBalance(addr2, big.NewInt(params.Ether)) statedb.AddBalance(addr3, big.NewInt(params.Ether)) - statedb.Commit(0, true, false) + statedb.Commit(0, true) chain := &testBlockChain{ config: testChainConfig, @@ -1248,7 +1248,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 diff --git a/eth/api_debug_test.go b/eth/api_debug_test.go index c67284fb6c..399ba6830e 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 666972d48e..0a55f33c22 100644 --- a/eth/state_accessor.go +++ b/eth/state_accessor.go @@ -142,6 +142,7 @@ func (eth *Ethereum) hashState(ctx context.Context, block *types.Block, reexec u logged time.Time parent common.Hash ) + triedb.EnableRefCounting() for current.NumberU64() < origin { if err := ctx.Err(); err != nil { return nil, nil, err @@ -162,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) diff --git a/tests/state_test_util.go b/tests/state_test_util.go index 29e934efe5..b64fe51679 100644 --- a/tests/state_test_util.go +++ b/tests/state_test_util.go @@ -58,7 +58,7 @@ func MakePreState(db ethdb.Database, accounts core.GenesisAlloc, snapshotter boo } } // Commit and re-open to start with a clean state. - root, _ := statedb.Commit(0, false, false) + root, _ := statedb.Commit(0, false) var snaps *snapshot.Tree if snapshotter { diff --git a/trie/database.go b/trie/database.go index a3bd3ecc65..7c7ca4d243 100644 --- a/trie/database.go +++ b/trie/database.go @@ -80,10 +80,11 @@ type backend interface { // types of node backend as an entrypoint. It's responsible for all interactions // relevant with trie nodes and node preimages. type Database struct { - config *Config // Configuration for trie database - diskdb ethdb.Database // Persistent database to store the snapshot - preimages *preimageStore // The store for caching preimages - backend backend // The backend for managing trie nodes + config *Config // Configuration for trie database + diskdb ethdb.Database // Persistent database to store the snapshot + preimages *preimageStore // The store for caching preimages + backend backend // The backend for managing trie nodes + refCounting bool // True if Update should increment the root's reference count } // NewDatabase initializes the trie database with default settings, note @@ -133,23 +134,20 @@ func (db *Database) Reader(blockRoot common.Hash) (Reader, error) { // The passed in maps(nodes, states) will be retained to avoid copying everything. // Therefore, these maps must not be changed afterwards. func (db *Database) Update(root common.Hash, parent common.Hash, block uint64, nodes *trienode.MergedNodeSet, states *triestate.Set) error { - if db.preimages != nil { - db.preimages.commit(false) - } - 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 { + if ok && db.refCounting { return hdb.UpdateAndReferenceRoot(root, parent, block, nodes, states) } return db.backend.Update(root, parent, block, nodes, states) } +func (db *Database) EnableRefCounting() { + db.refCounting = true +} + // 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.