From f9fb58ee0cbbce5a521374b4619348a6ad4f344b Mon Sep 17 00:00:00 2001 From: Fynn Date: Sat, 11 May 2024 14:48:42 +0800 Subject: [PATCH] core,trie: use direct get for statedb --- core/rawdb/accessors_trie.go | 4 +- core/state/database.go | 4 +- core/state/state_object.go | 2 +- core/state/statedb.go | 16 +++- core/state/trie_prefetcher.go | 4 +- trie/secure_trie.go | 24 +++++- trie/tracer.go | 13 ++- trie/trie.go | 11 +++ trie/trie_test.go | 34 +++++--- trie/triestate/state.go | 10 ++- trie/verkle.go | 4 +- triedb/database/database.go | 6 ++ triedb/hashdb/database.go | 8 ++ triedb/pathdb/database.go | 2 +- triedb/pathdb/journal.go | 149 +++++++++++++++++++++++++++++++++- triedb/pathdb/layertree.go | 15 ++++ triedb/pathdb/nodebuffer.go | 47 +++++++---- triedb/pathdb/reader.go | 9 ++ 18 files changed, 307 insertions(+), 55 deletions(-) diff --git a/core/rawdb/accessors_trie.go b/core/rawdb/accessors_trie.go index 540cdcba5d..35295fe6b6 100644 --- a/core/rawdb/accessors_trie.go +++ b/core/rawdb/accessors_trie.go @@ -99,8 +99,8 @@ func DeleteAccountTrieNode(db ethdb.KeyValueWriter, path []byte) { func EncodeNibbles(bytes []byte) []byte { nibbles := make([]byte, len(bytes)*2) for i, b := range bytes { - nibbles[i*2] = b >> 4 // 取字节高4位 - nibbles[i*2+1] = b & 0x0F // 取字节低4位 + nibbles[i*2] = b >> 4 + nibbles[i*2+1] = b & 0x0F } return nibbles } diff --git a/core/state/database.go b/core/state/database.go index 188ecf0c86..43210a16ef 100644 --- a/core/state/database.go +++ b/core/state/database.go @@ -85,12 +85,12 @@ type Trie interface { // the trie, nil will be returned. If the trie is corrupted(e.g. some nodes // are missing or the account blob is incorrect for decoding), an error will // be returned. - GetAccount(address common.Address) (*types.StateAccount, error) + GetAccount(address common.Address, direct bool) (*types.StateAccount, error) // GetStorage returns the value for key stored in the trie. The value bytes // must not be modified by the caller. If a node was not found in the database, // a trie.MissingNodeError is returned. - GetStorage(addr common.Address, key []byte) ([]byte, error) + GetStorage(addr common.Address, key []byte, direct bool) ([]byte, error) // UpdateAccount abstracts an account write to the trie. It encodes the // provided account object with associated algorithm and then updates it diff --git a/core/state/state_object.go b/core/state/state_object.go index d75ba01376..720db2ad77 100644 --- a/core/state/state_object.go +++ b/core/state/state_object.go @@ -202,7 +202,7 @@ func (s *stateObject) GetCommittedState(key common.Hash) common.Hash { s.db.setError(err) return common.Hash{} } - val, err := tr.GetStorage(s.address, key.Bytes()) + val, err := tr.GetStorage(s.address, key.Bytes(), true) s.db.StorageReads += time.Since(start) if err != nil { diff --git a/core/state/statedb.go b/core/state/statedb.go index ac37d4ceeb..38d30a5dbf 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -623,7 +623,7 @@ func (s *StateDB) getStateObject(addr common.Address) *stateObject { if data == nil { start := time.Now() var err error - data, err = s.trie.GetAccount(addr) + data, err = s.trie.GetAccount(addr, true) s.AccountReads += time.Since(start) if err != nil { @@ -1292,7 +1292,7 @@ func (s *StateDB) Commit(block uint64, deleteEmptyObjects bool) (common.Hash, er } if root != origin { start = time.Now() - set := triestate.New(s.accountsOrigin, s.storagesOrigin) + set := triestate.New(s.accountsOrigin, s.storagesOrigin, s.mustConvertSlmAccount(s.accounts), s.storages, s.convertAccountSet(s.stateObjectsDestruct)) if err := s.db.TrieDB().Update(root, origin, block, nodes, set); err != nil { return common.Hash{}, err } @@ -1313,6 +1313,18 @@ func (s *StateDB) Commit(block uint64, deleteEmptyObjects bool) (common.Hash, er return root, nil } +func (s *StateDB) mustConvertSlmAccount(slmAccounts map[common.Hash][]byte) map[common.Hash][]byte { + ret := make(map[common.Hash][]byte) + for h, acc := range slmAccounts { + fullAccount, err := types.FullAccountRLP(acc) + if err != nil { + panic(fmt.Sprintf("mustConvertSlmAccount, acc: %v", common.Bytes2Hex(acc))) + } + ret[h] = fullAccount + } + return ret +} + // Prepare handles the preparatory steps for executing a state transition with. // This method must be invoked before state transition. // diff --git a/core/state/trie_prefetcher.go b/core/state/trie_prefetcher.go index c2a49417d4..84f81430d8 100644 --- a/core/state/trie_prefetcher.go +++ b/core/state/trie_prefetcher.go @@ -344,9 +344,9 @@ func (sf *subfetcher) loop() { sf.dups++ } else { if len(task) == common.AddressLength { - sf.trie.GetAccount(common.BytesToAddress(task)) + sf.trie.GetAccount(common.BytesToAddress(task), false) } else { - sf.trie.GetStorage(sf.addr, task) + sf.trie.GetStorage(sf.addr, task, false) } sf.seen[string(task)] = struct{}{} } diff --git a/trie/secure_trie.go b/trie/secure_trie.go index efd4dfb5d3..3c04793a43 100644 --- a/trie/secure_trie.go +++ b/trie/secure_trie.go @@ -86,8 +86,16 @@ func (t *StateTrie) MustGet(key []byte) []byte { // and slot key. The value bytes must not be modified by the caller. // If the specified storage slot is not in the trie, nil will be returned. // If a trie node is not found in the database, a MissingNodeError is returned. -func (t *StateTrie) GetStorage(_ common.Address, key []byte) ([]byte, error) { - enc, err := t.trie.Get(t.hashKey(key)) +func (t *StateTrie) GetStorage(_ common.Address, key []byte, direct bool) ([]byte, error) { + var ( + enc []byte + err error + ) + if direct { + enc, err = t.trie.GetDirectly(t.hashKey(key)) + } else { + enc, err = t.trie.Get(t.hashKey(key)) + } if err != nil || len(enc) == 0 { return nil, err } @@ -98,8 +106,16 @@ func (t *StateTrie) GetStorage(_ common.Address, key []byte) ([]byte, error) { // GetAccount attempts to retrieve an account with provided account address. // If the specified account is not in the trie, nil will be returned. // If a trie node is not found in the database, a MissingNodeError is returned. -func (t *StateTrie) GetAccount(address common.Address) (*types.StateAccount, error) { - res, err := t.trie.Get(t.hashKey(address.Bytes())) +func (t *StateTrie) GetAccount(address common.Address, direct bool) (*types.StateAccount, error) { + var ( + res []byte + err error + ) + if direct { + res, err = t.trie.GetDirectly(t.hashKey(address.Bytes())) + } else { + res, err = t.trie.Get(t.hashKey(address.Bytes())) + } if res == nil || err != nil { return nil, err } diff --git a/trie/tracer.go b/trie/tracer.go index 90b9666f0b..c11d42e5e3 100644 --- a/trie/tracer.go +++ b/trie/tracer.go @@ -112,10 +112,15 @@ func (t *tracer) deletedNodes() []string { // It's possible a few deleted nodes were embedded // in their parent before, the deletions can be no // effect by deleting nothing, filter them out. - _, ok := t.accessList[path] - if !ok { - continue - } + + // Note: In order to read the account/storage + // directly from pathdb, redundant storage is made + // for the embedded node in committer.store, so it + // cannot be filtered out here. + // _, ok := t.accessList[path] + // if !ok { + // continue + // } paths = append(paths, path) } return paths diff --git a/trie/trie.go b/trie/trie.go index 12764e18d1..de57a9686c 100644 --- a/trie/trie.go +++ b/trie/trie.go @@ -153,6 +153,17 @@ func (t *Trie) Get(key []byte) ([]byte, error) { return value, err } +func (t *Trie) GetDirectly(key []byte) ([]byte, error) { + if t.reader.reader == nil { + return nil, nil + } + if t.owner == (common.Hash{}) { + return t.reader.reader.Account(common.BytesToHash(key)) + } else { + return t.reader.reader.Storage(t.owner, common.BytesToHash(key)) + } +} + func (t *Trie) get(origNode node, key []byte, pos int) (value []byte, newnode node, didResolve bool, err error) { switch n := (origNode).(type) { case nil: diff --git a/trie/trie_test.go b/trie/trie_test.go index da60a7423d..4c20fccb6b 100644 --- a/trie/trie_test.go +++ b/trie/trie_test.go @@ -447,9 +447,9 @@ func verifyAccessList(old *Trie, new *Trie, set *trienode.NodeSet) error { if !ok || n.IsDeleted() { return errors.New("expect new node") } - //if len(n.Prev) > 0 { + // if len(n.Prev) > 0 { // return errors.New("unexpected origin value") - //} + // } } // Check deletion set for path := range deletes { @@ -457,12 +457,12 @@ func verifyAccessList(old *Trie, new *Trie, set *trienode.NodeSet) error { if !ok || !n.IsDeleted() { return errors.New("expect deleted node") } - //if len(n.Prev) == 0 { + // if len(n.Prev) == 0 { // return errors.New("expect origin value") - //} - //if !bytes.Equal(n.Prev, blob) { + // } + // if !bytes.Equal(n.Prev, blob) { // return errors.New("invalid origin value") - //} + // } } // Check update set for path := range updates { @@ -470,12 +470,12 @@ func verifyAccessList(old *Trie, new *Trie, set *trienode.NodeSet) error { if !ok || n.IsDeleted() { return errors.New("expect updated node") } - //if len(n.Prev) == 0 { + // if len(n.Prev) == 0 { // return errors.New("expect origin value") - //} - //if !bytes.Equal(n.Prev, blob) { + // } + // if !bytes.Equal(n.Prev, blob) { // return errors.New("invalid origin value") - //} + // } } return nil } @@ -696,7 +696,7 @@ func BenchmarkHash(b *testing.B) { } b.ResetTimer() b.ReportAllocs() - //trie.hashRoot(nil, nil) + // trie.hashRoot(nil, nil) trie.Hash() } @@ -793,7 +793,7 @@ func makeAccounts(size int) (addresses [][20]byte, accounts [][]byte) { ) // The big.Rand function is not deterministic with regards to 64 vs 32 bit systems, // and will consume different amount of data from the rand source. - //balance = new(big.Int).Rand(random, new(big.Int).Exp(common.Big2, common.Big256, nil)) + // balance = new(big.Int).Rand(random, new(big.Int).Exp(common.Big2, common.Big256, nil)) // Therefore, we instead just read via byte buffer numBytes := random.Uint32() % 33 // [0, 32] bytes balanceBytes := make([]byte, numBytes) @@ -814,6 +814,11 @@ type spongeDb struct { values map[string]string } +func (s *spongeDb) DeleteRange(start, end []byte) error { + // TODO implement me + panic("implement me") +} + func (s *spongeDb) Has(key []byte) (bool, error) { panic("implement me") } func (s *spongeDb) Get(key []byte) ([]byte, error) { return nil, errors.New("no such elem") } func (s *spongeDb) Delete(key []byte) error { panic("implement me") } @@ -861,6 +866,11 @@ type spongeBatch struct { db *spongeDb } +func (b *spongeBatch) DeleteRange(start, end []byte) error { + // TODO implement me + panic("implement me") +} + func (b *spongeBatch) Put(key, value []byte) error { b.db.Put(key, value) return nil diff --git a/trie/triestate/state.go b/trie/triestate/state.go index 4fc7d444d0..cae4de2384 100644 --- a/trie/triestate/state.go +++ b/trie/triestate/state.go @@ -68,10 +68,14 @@ type Set struct { } // New constructs the state set with provided data. -func New(accounts map[common.Address][]byte, storages map[common.Address]map[common.Hash][]byte) *Set { +func New(accounts map[common.Address][]byte, storages map[common.Address]map[common.Hash][]byte, + latestAccount map[common.Hash][]byte, latestStorages map[common.Hash]map[common.Hash][]byte, destructSet map[common.Hash]struct{}) *Set { return &Set{ - Accounts: accounts, - Storages: storages, + Accounts: accounts, + Storages: storages, + LatestAccounts: latestAccount, + LatestStorages: latestStorages, + DestructSet: destructSet, } } diff --git a/trie/verkle.go b/trie/verkle.go index 01d813d9ec..4b42e5b3bf 100644 --- a/trie/verkle.go +++ b/trie/verkle.go @@ -78,7 +78,7 @@ func (t *VerkleTrie) GetKey(key []byte) []byte { // GetAccount implements state.Trie, retrieving the account with the specified // account address. If the specified account is not in the verkle tree, nil will // be returned. If the tree is corrupted, an error will be returned. -func (t *VerkleTrie) GetAccount(addr common.Address) (*types.StateAccount, error) { +func (t *VerkleTrie) GetAccount(addr common.Address, _ bool) (*types.StateAccount, error) { var ( acc = &types.StateAccount{} values [][]byte @@ -118,7 +118,7 @@ func (t *VerkleTrie) GetAccount(addr common.Address) (*types.StateAccount, error // GetStorage implements state.Trie, retrieving the storage slot with the specified // account address and storage key. If the specified slot is not in the verkle tree, // nil will be returned. If the tree is corrupted, an error will be returned. -func (t *VerkleTrie) GetStorage(addr common.Address, key []byte) ([]byte, error) { +func (t *VerkleTrie) GetStorage(addr common.Address, key []byte, _ bool) ([]byte, error) { k := utils.StorageSlotKeyWithEvaluatedAddress(t.cache.Get(addr.Bytes()), key) val, err := t.root.Get(k, t.nodeResolver) if err != nil { diff --git a/triedb/database/database.go b/triedb/database/database.go index f11c7e9bbd..95ef79e039 100644 --- a/triedb/database/database.go +++ b/triedb/database/database.go @@ -29,6 +29,12 @@ type Reader interface { // Don't modify the returned byte slice since it's not deep-copied and // still be referenced by database. Node(owner common.Hash, path []byte, hash common.Hash) ([]byte, error) + + // Account retrieves the account with the provided account hash, + Account(hash common.Hash) ([]byte, error) + + // Storage retrieves the storage key-value with the provided account hash, + Storage(accountHash, storageHash common.Hash) ([]byte, error) } // PreimageStore wraps the methods of a backing store for reading and writing diff --git a/triedb/hashdb/database.go b/triedb/hashdb/database.go index 367bd13741..f0cd21d3fc 100644 --- a/triedb/hashdb/database.go +++ b/triedb/hashdb/database.go @@ -642,6 +642,14 @@ type reader struct { db *Database } +func (reader reader) Account(hash common.Hash) ([]byte, error) { + panic("Not Supported") +} + +func (reader reader) Storage(accountHash, storageHash common.Hash) ([]byte, error) { + panic("Not Supported") +} + // Node retrieves the trie node with the given node hash. No error will be // returned if the node is not found. func (reader *reader) Node(owner common.Hash, path []byte, hash common.Hash) ([]byte, error) { diff --git a/triedb/pathdb/database.go b/triedb/pathdb/database.go index b3f3302bd2..1cfffca53c 100644 --- a/triedb/pathdb/database.go +++ b/triedb/pathdb/database.go @@ -340,7 +340,7 @@ func (db *Database) Enable(root common.Hash) error { } // Re-construct a new disk layer backed by persistent state // with **empty clean cache and node buffer**. - db.tree.reset(newDiskLayer(root, 0, db, nil, newNodeBuffer(db.bufferSize, nil, 0))) + db.tree.reset(newDiskLayer(root, 0, db, nil, newNodeBuffer(db.bufferSize, nil, nil, nil, nil, 0))) // Re-enable the database as the final step. db.waitSync = false diff --git a/triedb/pathdb/journal.go b/triedb/pathdb/journal.go index 1740ec5935..70d3b3c3e8 100644 --- a/triedb/pathdb/journal.go +++ b/triedb/pathdb/journal.go @@ -75,6 +75,24 @@ type journalStorage struct { Slots [][]byte } +// journalDestruct is an account deletion entry in a diffLayer's disk journal. +type journalDestruct struct { + Hash common.Hash +} + +// journalAccount is an account entry in a diffLayer's disk journal. +type journalLatestAccount struct { + Hash common.Hash + Blob []byte +} + +// journalStorage is an account's storage map in a diffLayer's disk journal. +type journalLatestStorage struct { + Hash common.Hash + Keys []common.Hash + Vals [][]byte +} + // loadJournal tries to parse the layer journal from the disk. func (db *Database) loadJournal(diskRoot common.Hash) (layer, error) { journal := rawdb.ReadTrieJournal(db.diskdb) @@ -136,7 +154,7 @@ func (db *Database) loadLayers() layer { log.Info("Failed to load journal, discard it", "err", err) } // Return single layer with persistent state. - return newDiskLayer(root, rawdb.ReadPersistentStateID(db.diskdb), db, nil, newNodeBuffer(db.bufferSize, nil, 0)) + return newDiskLayer(root, rawdb.ReadPersistentStateID(db.diskdb), db, nil, newNodeBuffer(db.bufferSize, nil, nil, nil, nil, 0)) } // loadDiskLayer reads the binary blob from the layer journal, reconstructing @@ -175,8 +193,42 @@ func (db *Database) loadDiskLayer(r *rlp.Stream) (layer, error) { } nodes[entry.Owner] = subset } + + // Resolve latest states + var ( + jdestructSet []journalDestruct + jlatestAccounts []journalLatestAccount + jlatestStorage []journalLatestStorage + + latestAccounts = make(map[common.Hash][]byte) + latestStorages = make(map[common.Hash]map[common.Hash][]byte) + destructSet = make(map[common.Hash]struct{}) + ) + if err := r.Decode(&jdestructSet); err != nil { + return nil, fmt.Errorf("load destrctSet: %v", err) + } + for _, entry := range jdestructSet { + destructSet[entry.Hash] = struct{}{} + } + + if err := r.Decode(&jlatestAccounts); err != nil { + return nil, fmt.Errorf("load latest accounts: %v", err) + } + for _, entry := range jlatestAccounts { + latestAccounts[entry.Hash] = entry.Blob + } + + if err := r.Decode(&jlatestStorage); err != nil { + return nil, fmt.Errorf("load latest accounts: %v", err) + } + for _, entry := range jlatestStorage { + latestStorages[entry.Hash] = make(map[common.Hash][]byte) + for i, key := range entry.Keys { + latestStorages[entry.Hash][key] = entry.Vals[i] + } + } // Calculate the internal state transitions by id difference. - base := newDiskLayer(root, id, db, nil, newNodeBuffer(db.bufferSize, nodes, id-stored)) + base := newDiskLayer(root, id, db, nil, newNodeBuffer(db.bufferSize, nodes, latestAccounts, latestStorages, destructSet, id-stored)) return base, nil } @@ -219,6 +271,14 @@ func (db *Database) loadDiffLayer(parent layer, r *rlp.Stream) (layer, error) { jstorages []journalStorage accounts = make(map[common.Address][]byte) storages = make(map[common.Address]map[common.Hash][]byte) + + jdestructSet []journalDestruct + jlatestAccounts []journalLatestAccount + jlatestStorage []journalLatestStorage + + latestAccounts = make(map[common.Hash][]byte) + latestStorages = make(map[common.Hash]map[common.Hash][]byte) + destructSet = make(map[common.Hash]struct{}) ) if err := r.Decode(&jaccounts); err != nil { return nil, fmt.Errorf("load diff accounts: %v", err) @@ -240,7 +300,30 @@ func (db *Database) loadDiffLayer(parent layer, r *rlp.Stream) (layer, error) { } storages[entry.Account] = set } - return db.loadDiffLayer(newDiffLayer(parent, root, parent.stateID()+1, block, nodes, triestate.New(accounts, storages)), r) + if err := r.Decode(&jdestructSet); err != nil { + return nil, fmt.Errorf("load destrctSet: %v", err) + } + for _, entry := range jdestructSet { + destructSet[entry.Hash] = struct{}{} + } + + if err := r.Decode(&jlatestAccounts); err != nil { + return nil, fmt.Errorf("load latest accounts: %v", err) + } + for _, entry := range jlatestAccounts { + latestAccounts[entry.Hash] = entry.Blob + } + + if err := r.Decode(&jlatestStorage); err != nil { + return nil, fmt.Errorf("load latest accounts: %v", err) + } + for _, entry := range jlatestStorage { + latestStorages[entry.Hash] = make(map[common.Hash][]byte) + for i, key := range entry.Keys { + latestStorages[entry.Hash][key] = entry.Vals[i] + } + } + return db.loadDiffLayer(newDiffLayer(parent, root, parent.stateID()+1, block, nodes, triestate.New(accounts, storages, latestAccounts, latestStorages, destructSet)), r) } // journal implements the layer interface, marshaling the un-flushed trie nodes @@ -273,6 +356,36 @@ func (dl *diskLayer) journal(w io.Writer) error { if err := rlp.Encode(w, nodes); err != nil { return err } + + // Step four Write latest accounts/storages/destructSet into buffer + destructs := make([]journalDestruct, 0, len(dl.buffer.destructSet)) + for hash := range dl.buffer.destructSet { + destructs = append(destructs, journalDestruct{Hash: hash}) + } + if err := rlp.Encode(w, destructs); err != nil { + return err + } + + latestAccounts := make([]journalLatestAccount, 0, len(dl.buffer.latestAccounts)) + for hash, blob := range dl.buffer.latestAccounts { + latestAccounts = append(latestAccounts, journalLatestAccount{Hash: hash, Blob: blob}) + } + if err := rlp.Encode(w, latestAccounts); err != nil { + return err + } + latestStorage := make([]journalLatestStorage, 0, len(dl.buffer.latestStorages)) + for hash, slots := range dl.buffer.latestStorages { + keys := make([]common.Hash, 0, len(slots)) + vals := make([][]byte, 0, len(slots)) + for key, val := range slots { + keys = append(keys, key) + vals = append(vals, val) + } + latestStorage = append(latestStorage, journalLatestStorage{Hash: hash, Keys: keys, Vals: vals}) + } + if err := rlp.Encode(w, latestStorage); err != nil { + return err + } log.Debug("Journaled pathdb disk layer", "root", dl.root, "nodes", len(dl.buffer.nodes)) return nil } @@ -327,6 +440,36 @@ func (dl *diffLayer) journal(w io.Writer) error { if err := rlp.Encode(w, storage); err != nil { return err } + + // Write latest accounts/storages/destructSet into buffer + destructs := make([]journalDestruct, 0, len(dl.states.DestructSet)) + for hash := range dl.states.DestructSet { + destructs = append(destructs, journalDestruct{Hash: hash}) + } + if err := rlp.Encode(w, destructs); err != nil { + return err + } + + latestAccounts := make([]journalLatestAccount, 0, len(dl.states.LatestAccounts)) + for hash, blob := range dl.states.LatestAccounts { + latestAccounts = append(latestAccounts, journalLatestAccount{Hash: hash, Blob: blob}) + } + if err := rlp.Encode(w, latestAccounts); err != nil { + return err + } + latestStorage := make([]journalLatestStorage, 0, len(dl.states.LatestStorages)) + for hash, slots := range dl.states.LatestStorages { + keys := make([]common.Hash, 0, len(slots)) + vals := make([][]byte, 0, len(slots)) + for key, val := range slots { + keys = append(keys, key) + vals = append(vals, val) + } + latestStorage = append(latestStorage, journalLatestStorage{Hash: hash, Keys: keys, Vals: vals}) + } + if err := rlp.Encode(w, latestStorage); err != nil { + return err + } log.Debug("Journaled pathdb diff layer", "root", dl.root, "parent", dl.parent.rootHash(), "id", dl.stateID(), "block", dl.block, "nodes", len(dl.nodes)) return nil } diff --git a/triedb/pathdb/layertree.go b/triedb/pathdb/layertree.go index d314779910..5d9611375a 100644 --- a/triedb/pathdb/layertree.go +++ b/triedb/pathdb/layertree.go @@ -145,6 +145,7 @@ func (tree *layerTree) cap(root common.Hash, layers int) error { return nil } } + var persisted *diskLayer // We're out of layers, flatten anything below, stopping if it's the disk or if // the memory limit is not yet exceeded. switch parent := diff.parentLayer().(type) { @@ -163,6 +164,7 @@ func (tree *layerTree) cap(root common.Hash, layers int) error { } tree.layers[base.rootHash()] = base diff.parent = base + persisted = base.(*diskLayer) diff.lock.Unlock() @@ -190,6 +192,19 @@ func (tree *layerTree) cap(root common.Hash, layers int) error { remove(root) } } + // If the disk layer was modified, regenerate all the cumulative blooms + if persisted != nil { + var rebloom func(root common.Hash) + rebloom = func(root common.Hash) { + if diff, ok := tree.layers[root].(*diffLayer); ok { + diff.rebloom(persisted) + } + for _, child := range children[root] { + rebloom(child) + } + } + rebloom(persisted.root) + } return nil } diff --git a/triedb/pathdb/nodebuffer.go b/triedb/pathdb/nodebuffer.go index 873ffacf82..264cef9fcb 100644 --- a/triedb/pathdb/nodebuffer.go +++ b/triedb/pathdb/nodebuffer.go @@ -42,16 +42,26 @@ type nodebuffer struct { nodes map[common.Hash]map[string]*trienode.Node // The dirty node set, mapped by owner and path // latest account and storage - LatestAccounts map[common.Hash][]byte - LatestStorages map[common.Hash]map[common.Hash][]byte - DestructSet map[common.Hash]struct{} + latestAccounts map[common.Hash][]byte + latestStorages map[common.Hash]map[common.Hash][]byte + destructSet map[common.Hash]struct{} } // newNodeBuffer initializes the node buffer with the provided nodes. -func newNodeBuffer(limit int, nodes map[common.Hash]map[string]*trienode.Node, layers uint64) *nodebuffer { +func newNodeBuffer(limit int, nodes map[common.Hash]map[string]*trienode.Node, + latestAccounts map[common.Hash][]byte, latestStorages map[common.Hash]map[common.Hash][]byte, destructSet map[common.Hash]struct{}, layers uint64) *nodebuffer { if nodes == nil { nodes = make(map[common.Hash]map[string]*trienode.Node) } + if latestAccounts == nil { + latestAccounts = make(map[common.Hash][]byte) + } + if latestStorages == nil { + latestStorages = make(map[common.Hash]map[common.Hash][]byte) + } + if destructSet == nil { + destructSet = make(map[common.Hash]struct{}) + } var size uint64 for _, subset := range nodes { for path, n := range subset { @@ -59,32 +69,35 @@ func newNodeBuffer(limit int, nodes map[common.Hash]map[string]*trienode.Node, l } } return &nodebuffer{ - layers: layers, - nodes: nodes, - size: size, - limit: uint64(limit), + layers: layers, + nodes: nodes, + latestAccounts: latestAccounts, + latestStorages: latestStorages, + destructSet: destructSet, + size: size, + limit: uint64(limit), } } func (b *nodebuffer) account(hash common.Hash) ([]byte, bool) { - if data, ok := b.LatestAccounts[hash]; ok { + if data, ok := b.latestAccounts[hash]; ok { return data, true } - if _, ok := b.DestructSet[hash]; ok { + if _, ok := b.destructSet[hash]; ok { return nil, true } return nil, false } func (b *nodebuffer) storage(accountHash, storageHash common.Hash) ([]byte, bool) { - if storage, ok := b.LatestStorages[accountHash]; ok { + if storage, ok := b.latestStorages[accountHash]; ok { if data, ok := storage[storageHash]; ok { return data, true } } - if _, ok := b.DestructSet[accountHash]; ok { + if _, ok := b.destructSet[accountHash]; ok { return nil, true } return nil, false @@ -260,17 +273,17 @@ func (b *nodebuffer) flush(db ethdb.KeyValueStore, clean *cleanCache, id uint64, batch = b.allocBatch(db) ) // delete all kv for destructSet first to keep latest for disk nodes - for h, _ := range b.DestructSet { + for h, _ := range b.destructSet { rawdb.DeleteStorageTrie(batch, h) clean.plainStates.Set(h.Bytes(), nil) } var wg sync.WaitGroup - if len(b.DestructSet) != 0 { + if len(b.destructSet) != 0 { wg.Add(1) go func() { st := time.Now() nums := 0 - for h := range b.DestructSet { + for h := range b.destructSet { // delete from the clean cache it := rawdb.IterateStorageTrieNodes(db, h) for it.Next() { @@ -287,10 +300,10 @@ func (b *nodebuffer) flush(db ethdb.KeyValueStore, clean *cleanCache, id uint64, it.Release() } log.Info("handle deletion of plain storage", "elapsed", time.Since(st).String(), "deleted nums", nums) - for h, acc := range b.LatestAccounts { + for h, acc := range b.latestAccounts { clean.plainStates.Set(h.Bytes(), types.FullToSlimAccountRLP(acc)) } - for h, storages := range b.LatestStorages { + for h, storages := range b.latestStorages { for k, v := range storages { clean.plainStates.Set(append(h.Bytes(), k.Bytes()...), v) } diff --git a/triedb/pathdb/reader.go b/triedb/pathdb/reader.go index 54dc98a543..2b84310dbe 100644 --- a/triedb/pathdb/reader.go +++ b/triedb/pathdb/reader.go @@ -52,6 +52,15 @@ type reader struct { noHashCheck bool } +func (r *reader) Account(hash common.Hash) ([]byte, error) { + return r.layer.Account(hash) + +} + +func (r *reader) Storage(accountHash, storageHash common.Hash) ([]byte, error) { + return r.layer.Storage(accountHash, storageHash) +} + // Node implements database.Reader interface, retrieving the node with specified // node info. Don't modify the returned byte slice since it's not deep-copied // and still be referenced by database.