mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-19 18:32:23 +00:00
core,trie: use direct get for statedb
This commit is contained in:
parent
462e2bf71b
commit
f9fb58ee0c
18 changed files with 307 additions and 55 deletions
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
//
|
||||
|
|
|
|||
|
|
@ -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{}{}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
11
trie/trie.go
11
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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
LatestAccounts: latestAccount,
|
||||
LatestStorages: latestStorages,
|
||||
DestructSet: destructSet,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
@ -61,30 +71,33 @@ func newNodeBuffer(limit int, nodes map[common.Hash]map[string]*trienode.Node, l
|
|||
return &nodebuffer{
|
||||
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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
Loading…
Reference in a new issue