This commit is contained in:
Jared Wasinger 2024-02-19 20:03:20 -08:00
parent 9100e2e566
commit 095a65e2b7
2 changed files with 32 additions and 69 deletions

View file

@ -30,6 +30,7 @@ import (
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/rpc"
"github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie"
"github.com/holiman/uint256"
) )
// Proof-of-stake protocol constants. // 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) beacon.ethone.Finalize(chain, header, state, txs, uncles, nil)
return 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. // No block reward which is issued by consensus layer instead.
} }

View file

@ -62,6 +62,7 @@ type StateDB struct {
db Database db Database
prefetcher *triePrefetcher prefetcher *triePrefetcher
trie Trie trie Trie
witness *Witness
hasher crypto.KeccakState hasher crypto.KeccakState
snaps *snapshot.Tree // Nil if snapshot is not available snaps *snapshot.Tree // Nil if snapshot is not available
snap snapshot.Snapshot // Nil if snapshot is not available snap snapshot.Snapshot // Nil if snapshot is not available
@ -138,9 +139,6 @@ type StateDB struct {
// Testing hooks // Testing hooks
onCommit func(states *triestate.Set) // Hook invoked when commit is performed 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 // 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. // commit phase, most of the needed data is already hot.
func (s *StateDB) StartPrefetcher(namespace string) { func (s *StateDB) StartPrefetcher(namespace string) {
if s.prefetcher != nil { if s.prefetcher != nil {
s.prefetcher.wait()
s.prefetcher.close() s.prefetcher.close()
s.prefetcher = nil s.prefetcher = nil
} }
if s.readPrefetcher != nil {
s.readPrefetcher.wait()
s.readPrefetcher.close()
s.readPrefetcher = nil
}
if s.snap != nil { if s.snap != nil {
s.prefetcher = newTriePrefetcher(s.db, s.originalRoot, namespace) 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. // from the gathered metrics.
func (s *StateDB) StopPrefetcher() { func (s *StateDB) StopPrefetcher() {
if s.prefetcher != nil { if s.prefetcher != nil {
s.prefetcher.wait()
s.prefetcher.close() s.prefetcher.close()
s.prefetcher = nil 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. // 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) s.SnapshotAccountReads += time.Since(start)
} }
if err == nil { 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 { if acc == nil {
return nil return nil
} }
@ -634,15 +622,9 @@ func (s *StateDB) getDeletedStateObject(addr common.Address) *stateObject {
return nil return nil
} }
} }
// Insert into the live set // Insert into the live set
obj := newObject(s, addr, data) obj := newObject(s, addr, data)
s.setStateObject(obj) 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 return obj
} }
@ -719,7 +701,10 @@ func (s *StateDB) CreateAccount(addr common.Address) {
} }
// Copy creates a deep, independent copy of the state. // 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 { func (s *StateDB) Copy() *StateDB {
// Copy all the basic fields, initialize the memory ones // Copy all the basic fields, initialize the memory ones
state := &StateDB{ state := &StateDB{
@ -816,12 +801,6 @@ func (s *StateDB) Copy() *StateDB {
state.accessList = s.accessList.Copy() state.accessList = s.accessList.Copy()
state.transientStorage = s.transientStorage.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 return state
} }
@ -909,7 +888,7 @@ func (s *StateDB) collectReadStorageAccessLists() {
for _, obj := range s.stateObjects { for _, obj := range s.stateObjects {
// load read storage slots from the finished trie in the prefetcher as these continue to be prefetched // load read storage slots from the finished trie in the prefetcher as these continue to be prefetched
// until commit. // until commit.
tr := s.readPrefetcher.trie(obj.addrHash, obj.data.Root) tr := s.prefetcher.trie(obj.addrHash, obj.data.Root)
if tr == nil { if tr == nil {
continue continue
} }
@ -921,9 +900,9 @@ func (s *StateDB) collectReadStorageAccessLists() {
} }
func (s *StateDB) collectReadAccountsAccessLists() { func (s *StateDB) collectReadAccountsAccessLists() {
tr := s.readPrefetcher.trie(common.Hash{}, s.originalRoot) tr := s.prefetcher.trie(common.Hash{}, s.originalRoot)
if tr == nil { if tr == nil {
// TODO: ensure this case is b/c of empty block // empty block
return return
} }
accessList := tr.AccessList() accessList := tr.AccessList()
@ -942,21 +921,14 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
prefetcher := s.prefetcher prefetcher := s.prefetcher
if s.prefetcher != nil { if s.prefetcher != nil {
defer func() { defer func() {
// TODO: need to wait for read accounts to be resolved in prefetcher main trie?
s.prefetcher.wait()
s.prefetcher.close() s.prefetcher.close()
if s.witness != nil {
s.collectReadStorageAccessLists()
s.collectReadAccountsAccessLists()
}
s.prefetcher = nil 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 // Although naively it makes sense to retrieve the account trie and then do
// the contract storage and account updates sequentially, that short circuits // the contract storage and account updates sequentially, that short circuits
// the account prefetcher. Instead, let's process all the storage updates // 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)) usedAddrs := make([][]byte, 0, len(s.stateObjectsPending))
// perform updates before deletions. In the case where a full node // Perform updates before deletions. This is needed for stateless execution in MPT:
// has two children, one of them selfdestructs and makes the recipient //
// another non-existing sibling, applying deletion before update would // Take value nodes 1, 2 who share the same full node parent 3 and have no other siblings.
// result in the unecessary premature collapse of the full node into a short node // 1 self-destructs specifying a non-existing recipient account which would be a child of 3.
// for the untouched third sibling. // 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 var deletedObjects []*stateObject
for addr := range s.stateObjectsPending { for addr := range s.stateObjectsPending {
if obj := s.stateObjects[addr]; !obj.deleted { if obj := s.stateObjects[addr]; !obj.deleted {
@ -1250,22 +1223,6 @@ func (s *StateDB) Witness() *Witness {
return s.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. // Commit writes the state to the underlying in-memory trie database.
// Once the state is committed, tries cached in stateDB (including account // Once the state is committed, tries cached in stateDB (including account
// trie, storage tries) will no longer be functional. A new state instance // 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 // 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. // 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. // 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 { for addr := range s.stateObjects {
obj := s.stateObjects[addr] obj := s.stateObjects[addr]
if _, ok := s.stateObjectsDirty[addr]; ok && !obj.deleted { 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 continue
} }
if obj.trie != nil { if obj.trie != nil {