diff --git a/consensus/beacon/consensus.go b/consensus/beacon/consensus.go index 457e57fda8..a350e383a2 100644 --- a/consensus/beacon/consensus.go +++ b/consensus/beacon/consensus.go @@ -30,6 +30,7 @@ import ( "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/trie" + "github.com/holiman/uint256" ) // Proof-of-stake protocol constants. @@ -352,7 +353,13 @@ func (beacon *Beacon) Finalize(chain consensus.ChainHeaderReader, header *types. beacon.ethone.Finalize(chain, header, state, txs, uncles, nil) return } - state.ApplyWithdrawals(withdrawals) + // Withdrawals processing. + for _, w := range withdrawals { + // Convert amount from gwei to wei. + amount := new(uint256.Int).SetUint64(w.Amount) + amount = amount.Mul(amount, uint256.NewInt(params.GWei)) + state.AddBalance(w.Address, amount) + } // No block reward which is issued by consensus layer instead. } diff --git a/core/state/statedb.go b/core/state/statedb.go index 53d194aba5..bb0f053ade 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -62,6 +62,7 @@ type StateDB struct { db Database prefetcher *triePrefetcher trie Trie + witness *Witness hasher crypto.KeccakState snaps *snapshot.Tree // Nil if snapshot is not available snap snapshot.Snapshot // Nil if snapshot is not available @@ -138,9 +139,6 @@ type StateDB struct { // Testing hooks onCommit func(states *triestate.Set) // Hook invoked when commit is performed - - witness *Witness - readPrefetcher *triePrefetcher } // NewWithWitnessRecording creates a new state from a given trie. The state is configured to construct a stateless @@ -191,20 +189,11 @@ func New(root common.Hash, db Database, snaps *snapshot.Tree) (*StateDB, error) // commit phase, most of the needed data is already hot. func (s *StateDB) StartPrefetcher(namespace string) { if s.prefetcher != nil { - s.prefetcher.wait() s.prefetcher.close() s.prefetcher = nil } - if s.readPrefetcher != nil { - s.readPrefetcher.wait() - s.readPrefetcher.close() - s.readPrefetcher = nil - } if s.snap != nil { s.prefetcher = newTriePrefetcher(s.db, s.originalRoot, namespace) - if s.witness != nil { - s.readPrefetcher = newTriePrefetcher(s.db, s.originalRoot, namespace) - } } } @@ -212,15 +201,9 @@ func (s *StateDB) StartPrefetcher(namespace string) { // from the gathered metrics. func (s *StateDB) StopPrefetcher() { if s.prefetcher != nil { - s.prefetcher.wait() s.prefetcher.close() s.prefetcher = nil } - if s.readPrefetcher != nil { - s.readPrefetcher.wait() - s.readPrefetcher.close() - s.readPrefetcher = nil - } } // setError remembers the first non-nil error it is called with. @@ -601,6 +584,11 @@ func (s *StateDB) getDeletedStateObject(addr common.Address) *stateObject { s.SnapshotAccountReads += time.Since(start) } if err == nil { + // If witness building is enabled, prefetch any trie paths loaded directly + // via the snapshots + if s.prefetcher != nil && s.witness != nil { + s.prefetcher.prefetch(common.Hash{}, s.originalRoot, common.Address{}, [][]byte{addr[:]}) + } if acc == nil { return nil } @@ -634,15 +622,9 @@ func (s *StateDB) getDeletedStateObject(addr common.Address) *stateObject { return nil } } - // Insert into the live set obj := newObject(s, addr, data) s.setStateObject(obj) - if s.witness != nil && s.snap != nil { - // when building witness with snap enabled, prefetch all read accounts to later be collected and - // included in the witness - s.readPrefetcher.prefetch(common.Hash{}, s.originalRoot, common.Address{}, [][]byte{addr[:]}) - } return obj } @@ -719,7 +701,10 @@ func (s *StateDB) CreateAccount(addr common.Address) { } // Copy creates a deep, independent copy of the state. -// Snapshots of the copied state cannot be applied to the copy. +// +// Note: +// - Snapshots of the copied state cannot be applied to the copy. +// - Pre-fetchers will not be copied nor active in the copy. func (s *StateDB) Copy() *StateDB { // Copy all the basic fields, initialize the memory ones state := &StateDB{ @@ -816,12 +801,6 @@ func (s *StateDB) Copy() *StateDB { state.accessList = s.accessList.Copy() state.transientStorage = s.transientStorage.Copy() - // If there's a prefetcher running, make an inactive copy of it that can - // only access data but does not actively preload (since the user will not - // know that they need to explicitly terminate an active copy). - if s.prefetcher != nil { - state.prefetcher = s.prefetcher.copy() - } return state } @@ -909,7 +888,7 @@ func (s *StateDB) collectReadStorageAccessLists() { for _, obj := range s.stateObjects { // load read storage slots from the finished trie in the prefetcher as these continue to be prefetched // until commit. - tr := s.readPrefetcher.trie(obj.addrHash, obj.data.Root) + tr := s.prefetcher.trie(obj.addrHash, obj.data.Root) if tr == nil { continue } @@ -921,9 +900,9 @@ func (s *StateDB) collectReadStorageAccessLists() { } func (s *StateDB) collectReadAccountsAccessLists() { - tr := s.readPrefetcher.trie(common.Hash{}, s.originalRoot) + tr := s.prefetcher.trie(common.Hash{}, s.originalRoot) if tr == nil { - // TODO: ensure this case is b/c of empty block + // empty block return } accessList := tr.AccessList() @@ -942,21 +921,14 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash { prefetcher := s.prefetcher if s.prefetcher != nil { defer func() { - // TODO: need to wait for read accounts to be resolved in prefetcher main trie? - s.prefetcher.wait() s.prefetcher.close() + if s.witness != nil { + s.collectReadStorageAccessLists() + s.collectReadAccountsAccessLists() + } s.prefetcher = nil }() } - if s.readPrefetcher != nil { - // TODO: move read prefetcher logic into Commit? - s.readPrefetcher.wait() - s.collectReadStorageAccessLists() - s.collectReadAccountsAccessLists() - s.readPrefetcher.close() - s.readPrefetcher = nil - } - // Although naively it makes sense to retrieve the account trie and then do // the contract storage and account updates sequentially, that short circuits // the account prefetcher. Instead, let's process all the storage updates @@ -978,11 +950,12 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash { } usedAddrs := make([][]byte, 0, len(s.stateObjectsPending)) - // perform updates before deletions. In the case where a full node - // has two children, one of them selfdestructs and makes the recipient - // another non-existing sibling, applying deletion before update would - // result in the unecessary premature collapse of the full node into a short node - // for the untouched third sibling. + // Perform updates before deletions. This is needed for stateless execution in MPT: + // + // Take value nodes 1, 2 who share the same full node parent 3 and have no other siblings. + // 1 self-destructs specifying a non-existing recipient account which would be a child of 3. + // If the deletion of 1 happens before the account-creating balance transfer, 3 will temporarily + // be collapsed to a short node on 2, requiring 2 to be unecessarily resolved. var deletedObjects []*stateObject for addr := range s.stateObjectsPending { if obj := s.stateObjects[addr]; !obj.deleted { @@ -1250,22 +1223,6 @@ func (s *StateDB) Witness() *Witness { return s.witness } -// ApplyWithdrawals credits the balance of each account that is the recipient -// of a withdrawal. -func (s *StateDB) ApplyWithdrawals(withdrawals types.Withdrawals) { - for _, w := range withdrawals { - // Convert amount from gwei to wei. - amount := new(uint256.Int).SetUint64(w.Amount) - amount = amount.Mul(amount, uint256.NewInt(params.GWei)) - s.AddBalance(w.Address, amount) - } - - if s.witness != nil { - al := s.trie.AccessList() - s.witness.addAccessList(common.Hash{}, al) - } -} - // Commit writes the state to the underlying in-memory trie database. // Once the state is committed, tries cached in stateDB (including account // trie, storage tries) will no longer be functional. A new state instance @@ -1308,11 +1265,10 @@ func (s *StateDB) Commit(block uint64, deleteEmptyObjects bool) (common.Hash, er // computation of a new trie root hash where a value has been deleted could // collapse a parent branch node + sibling into a full node for the sibling. // In this case, the sibling would only be read during root hash computation. - // TODO: verify the above assertion is the case. for addr := range s.stateObjects { obj := s.stateObjects[addr] if _, ok := s.stateObjectsDirty[addr]; ok && !obj.deleted { - // collect dirty object access witness if/when we commit them + // collect dirty object access witness when we commit them continue } if obj.trie != nil {