Skip to content

core, consensus/beacon: defer trie resolution until necessary #31725

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion consensus/beacon/consensus.go
Original file line number Diff line number Diff line change
Expand Up @@ -392,8 +392,12 @@ func (beacon *Beacon) FinalizeAndAssemble(chain consensus.ChainHeaderReader, hea
if err != nil {
return nil, fmt.Errorf("error opening pre-state tree root: %w", err)
}
postTrie := state.GetTrie()
if postTrie == nil {
return nil, errors.New("post-state tree is not available")
}
vktPreTrie, okpre := preTrie.(*trie.VerkleTrie)
vktPostTrie, okpost := state.GetTrie().(*trie.VerkleTrie)
vktPostTrie, okpost := postTrie.(*trie.VerkleTrie)

// The witness is only attached iff both parent and current block are
// using verkle tree.
Expand Down
2 changes: 1 addition & 1 deletion core/chain_makers.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ func (b *BlockGen) addTx(bc *BlockChain, vmConfig vm.Config, tx *types.Transacti
}
// Merge the tx-local access event into the "block-local" one, in order to collect
// all values, so that the witness can be built.
if b.statedb.GetTrie().IsVerkle() {
if b.statedb.Database().TrieDB().IsVerkle() {
b.statedb.AccessEvents().Merge(evm.AccessEvents)
}
b.txs = append(b.txs, tx)
Expand Down
23 changes: 16 additions & 7 deletions core/state/dump.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,9 @@ func (d iterativeDump) OnRoot(root common.Hash) {

// DumpToCollector iterates the state according to the given options and inserts
// the items into a collector for aggregation or serialization.
//
// The state iterator is still trie-based and can be converted to snapshot-based
// once the state snapshot is fully integrated into database. TODO(rjl493456442).
func (s *StateDB) DumpToCollector(c DumpCollector, conf *DumpConfig) (nextKey []byte) {
// Sanitize the input to allow nil configs
if conf == nil {
Expand All @@ -123,15 +126,20 @@ func (s *StateDB) DumpToCollector(c DumpCollector, conf *DumpConfig) (nextKey []
start = time.Now()
logged = time.Now()
)
log.Info("Trie dumping started", "root", s.trie.Hash())
c.OnRoot(s.trie.Hash())
log.Info("Trie dumping started", "root", s.originalRoot)
c.OnRoot(s.originalRoot)

trieIt, err := s.trie.NodeIterator(conf.Start)
tr, err := s.db.OpenTrie(s.originalRoot)
if err != nil {
return nil
}
trieIt, err := tr.NodeIterator(conf.Start)
if err != nil {
log.Error("Trie dumping error", "err", err)
return nil
}
it := trie.NewIterator(trieIt)

for it.Next() {
var data types.StateAccount
if err := rlp.DecodeBytes(it.Value, &data); err != nil {
Expand All @@ -147,7 +155,7 @@ func (s *StateDB) DumpToCollector(c DumpCollector, conf *DumpConfig) (nextKey []
}
address *common.Address
addr common.Address
addrBytes = s.trie.GetKey(it.Key)
addrBytes = tr.GetKey(it.Key)
)
if addrBytes == nil {
missingPreimages++
Expand All @@ -165,12 +173,13 @@ func (s *StateDB) DumpToCollector(c DumpCollector, conf *DumpConfig) (nextKey []
}
if !conf.SkipStorage {
account.Storage = make(map[common.Hash]string)
tr, err := obj.getTrie()

storageTr, err := s.db.OpenStorageTrie(s.originalRoot, addr, obj.Root(), tr)
if err != nil {
log.Error("Failed to load storage trie", "err", err)
continue
}
trieIt, err := tr.NodeIterator(nil)
trieIt, err := storageTr.NodeIterator(nil)
if err != nil {
log.Error("Failed to create trie iterator", "err", err)
continue
Expand All @@ -182,7 +191,7 @@ func (s *StateDB) DumpToCollector(c DumpCollector, conf *DumpConfig) (nextKey []
log.Error("Failed to decode the value returned by iterator", "error", err)
continue
}
account.Storage[common.BytesToHash(s.trie.GetKey(storageIt.Key))] = common.Bytes2Hex(content)
account.Storage[common.BytesToHash(storageTr.GetKey(storageIt.Key))] = common.Bytes2Hex(content)
}
}
c.OnAccount(address, account)
Expand Down
16 changes: 12 additions & 4 deletions core/state/iterator.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import (
// required in order to resolve the contract address.
type nodeIterator struct {
state *StateDB // State being iterated
tr Trie // Primary account trie for traversal

stateIt trie.NodeIterator // Primary iterator for the global state trie
dataIt trie.NodeIterator // Secondary iterator for the data trie of a contract
Expand Down Expand Up @@ -75,13 +76,20 @@ func (it *nodeIterator) step() error {
if it.state == nil {
return nil
}
if it.tr == nil {
tr, err := it.state.db.OpenTrie(it.state.originalRoot)
if err != nil {
return err
}
it.tr = tr
}
// Initialize the iterator if we've just started
var err error
if it.stateIt == nil {
it.stateIt, err = it.state.trie.NodeIterator(nil)
stateIt, err := it.tr.NodeIterator(nil)
if err != nil {
return err
}
it.stateIt = stateIt
}
// If we had data nodes previously, we surely have at least state nodes
if it.dataIt != nil {
Expand Down Expand Up @@ -116,14 +124,14 @@ func (it *nodeIterator) step() error {
return err
}
// Lookup the preimage of account hash
preimage := it.state.trie.GetKey(it.stateIt.LeafKey())
preimage := it.tr.GetKey(it.stateIt.LeafKey())
if preimage == nil {
return errors.New("account address is not available")
}
address := common.BytesToAddress(preimage)

// Traverse the storage slots belong to the account
dataTrie, err := it.state.db.OpenStorageTrie(it.state.originalRoot, address, account.Root, it.state.trie)
dataTrie, err := it.state.db.OpenStorageTrie(it.state.originalRoot, address, account.Root, it.tr)
if err != nil {
return err
}
Expand Down
1 change: 1 addition & 0 deletions core/state/state_object.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ func (s *stateObject) touch() {
// subsequent reads to expand the same trie instead of reloading from disk.
func (s *stateObject) getTrie() (Trie, error) {
if s.trie == nil {
// Assumes the primary account trie is already loaded
tr, err := s.db.db.OpenStorageTrie(s.db.originalRoot, s.address, s.data.Root, s.db.trie)
if err != nil {
return nil, err
Expand Down
4 changes: 0 additions & 4 deletions core/state/state_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,6 @@ func TestDump(t *testing.T) {
obj3.SetBalance(uint256.NewInt(44))

// write some of them to the trie
s.state.updateStateObject(obj1)
s.state.updateStateObject(obj2)
root, _ := s.state.Commit(0, false, false)

// check that DumpToCollector contains the state objects that are in trie
Expand Down Expand Up @@ -114,8 +112,6 @@ func TestIterativeDump(t *testing.T) {
obj4.AddBalance(uint256.NewInt(1337))

// write some of them to the trie
s.state.updateStateObject(obj1)
s.state.updateStateObject(obj2)
root, _ := s.state.Commit(0, false, false)
s.state, _ = New(root, tdb)

Expand Down
25 changes: 18 additions & 7 deletions core/state/statedb.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,8 @@ func (m *mutation) isDelete() bool {
type StateDB struct {
db Database
prefetcher *triePrefetcher
trie Trie
reader Reader
trie Trie // it's resolved on first access

// originalRoot is the pre-state root, before any changes were made.
// It will be updated when the Commit is called.
Expand Down Expand Up @@ -159,17 +159,12 @@ type StateDB struct {

// New creates a new state from a given trie.
func New(root common.Hash, db Database) (*StateDB, error) {
tr, err := db.OpenTrie(root)
if err != nil {
return nil, err
}
reader, err := db.Reader(root)
if err != nil {
return nil, err
}
sdb := &StateDB{
db: db,
trie: tr,
originalRoot: root,
reader: reader,
stateObjects: make(map[common.Address]*stateObject),
Expand Down Expand Up @@ -653,7 +648,6 @@ func (s *StateDB) Copy() *StateDB {
reader, _ := s.db.Reader(s.originalRoot) // impossible to fail
state := &StateDB{
db: s.db,
trie: mustCopyTrie(s.trie),
reader: reader,
originalRoot: s.originalRoot,
stateObjects: make(map[common.Address]*stateObject, len(s.stateObjects)),
Expand All @@ -677,6 +671,9 @@ func (s *StateDB) Copy() *StateDB {
transientStorage: s.transientStorage.Copy(),
journal: s.journal.copy(),
}
if s.trie != nil {
state.trie = mustCopyTrie(s.trie)
}
if s.witness != nil {
state.witness = s.witness.Copy()
}
Expand Down Expand Up @@ -772,6 +769,20 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
// Finalise all the dirty storage states and write them into the tries
s.Finalise(deleteEmptyObjects)

// Initialize the trie if it's not constructed yet. If the prefetch
// is enabled, the trie constructed below will be replaced by the
// prefetched one.
//
// This operation must be done before state object storage hashing,
// as it assumes the main trie is already loaded.
if s.trie == nil {
tr, err := s.db.OpenTrie(s.originalRoot)
if err != nil {
s.setError(err)
return common.Hash{}
}
s.trie = tr
}
// If there was a trie prefetcher operating, terminate it async so that the
// individual storage tries can be updated as soon as the disk load finishes.
if s.prefetcher != nil {
Expand Down
11 changes: 1 addition & 10 deletions core/state/statedb_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,6 @@ func TestCopy(t *testing.T) {
for i := byte(0); i < 255; i++ {
obj := orig.getOrNewStateObject(common.BytesToAddress([]byte{i}))
obj.AddBalance(uint256.NewInt(uint64(i)))
orig.updateStateObject(obj)
}
orig.Finalise(false)

Expand All @@ -190,10 +189,6 @@ func TestCopy(t *testing.T) {
origObj.AddBalance(uint256.NewInt(2 * uint64(i)))
copyObj.AddBalance(uint256.NewInt(3 * uint64(i)))
ccopyObj.AddBalance(uint256.NewInt(4 * uint64(i)))

orig.updateStateObject(origObj)
copy.updateStateObject(copyObj)
ccopy.updateStateObject(copyObj)
}

// Finalise the changes on all concurrently
Expand Down Expand Up @@ -238,7 +233,6 @@ func TestCopyWithDirtyJournal(t *testing.T) {
obj := orig.getOrNewStateObject(common.BytesToAddress([]byte{i}))
obj.AddBalance(uint256.NewInt(uint64(i)))
obj.data.Root = common.HexToHash("0xdeadbeef")
orig.updateStateObject(obj)
}
root, _ := orig.Commit(0, true, false)
orig, _ = New(root, db)
Expand All @@ -248,8 +242,6 @@ func TestCopyWithDirtyJournal(t *testing.T) {
obj := orig.getOrNewStateObject(common.BytesToAddress([]byte{i}))
amount := uint256.NewInt(uint64(i))
obj.SetBalance(new(uint256.Int).Sub(obj.Balance(), amount))

orig.updateStateObject(obj)
}
cpy := orig.Copy()

Expand Down Expand Up @@ -284,7 +276,6 @@ func TestCopyObjectState(t *testing.T) {
obj := orig.getOrNewStateObject(common.BytesToAddress([]byte{i}))
obj.AddBalance(uint256.NewInt(uint64(i)))
obj.data.Root = common.HexToHash("0xdeadbeef")
orig.updateStateObject(obj)
}
orig.Finalise(true)
cpy := orig.Copy()
Expand Down Expand Up @@ -573,7 +564,7 @@ func forEachStorage(s *StateDB, addr common.Address, cb func(key, value common.H
)

for it.Next() {
key := common.BytesToHash(s.trie.GetKey(it.Key))
key := common.BytesToHash(tr.GetKey(it.Key))
visited[key] = true
if value, dirty := so.dirtyStorage[key]; dirty {
if !cb(key, value) {
Expand Down
3 changes: 1 addition & 2 deletions core/state_processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,10 +161,9 @@ func ApplyTransactionWithEVM(msg *Message, gp *GasPool, statedb *state.StateDB,

// Merge the tx-local access event into the "block-local" one, in order to collect
// all values, so that the witness can be built.
if statedb.GetTrie().IsVerkle() {
if statedb.Database().TrieDB().IsVerkle() {
statedb.AccessEvents().Merge(evm.AccessEvents)
}

return MakeReceipt(evm, result, statedb, blockNumber, blockHash, tx, *usedGas, root), nil
}

Expand Down