Skip to content

Commit

Permalink
trie: relocate state execution logic into pathdb package (ethereum#29861
Browse files Browse the repository at this point in the history
)
  • Loading branch information
rjl493456442 committed Jun 27, 2024
1 parent 269e80b commit 045b971
Show file tree
Hide file tree
Showing 10 changed files with 259 additions and 445 deletions.
29 changes: 26 additions & 3 deletions trie/secure_trie.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,16 @@ import (
"github.com/ethereum/go-ethereum/triedb/database"
)

// preimageStore wraps the methods of a backing store for reading and writing
// trie node preimages.
type preimageStore interface {
// Preimage retrieves the preimage of the specified hash.
Preimage(hash common.Hash) []byte

// InsertPreimage commits a set of preimages along with their hashes.
InsertPreimage(preimages map[common.Hash][]byte)
}

// SecureTrie is the old name of StateTrie.
// Deprecated: use StateTrie.
type SecureTrie = StateTrie
Expand Down Expand Up @@ -52,6 +62,7 @@ func NewSecure(stateRoot common.Hash, owner common.Hash, root common.Hash, db da
type StateTrie struct {
trie Trie
db database.Database
preimages preimageStore
hashKeyBuf [common.HashLength]byte
secKeyCache map[string][]byte
secKeyCacheOwner *StateTrie // Pointer to self, replace the key cache on mismatch
Expand All @@ -70,7 +81,14 @@ func NewStateTrie(id *ID, db database.Database) (*StateTrie, error) {
if err != nil {
return nil, err
}
return &StateTrie{trie: *trie, db: db}, nil
tr := &StateTrie{trie: *trie, db: db}

// link the preimage store if it's supported
preimages, ok := db.(preimageStore)
if ok {
tr.preimages = preimages
}
return tr, nil
}

// MustGet returns the value for key stored in the trie.
Expand Down Expand Up @@ -211,7 +229,10 @@ func (t *StateTrie) GetKey(shaKey []byte) []byte {
if key, ok := t.getSecKeyCache()[string(shaKey)]; ok {
return key
}
return t.db.Preimage(common.BytesToHash(shaKey))
if t.preimages == nil {
return nil
}
return t.preimages.Preimage(common.BytesToHash(shaKey))
}

// Witness returns a set containing all trie nodes that have been accessed.
Expand All @@ -233,7 +254,9 @@ func (t *StateTrie) Commit(collectLeaf bool) (common.Hash, *trienode.NodeSet) {
for hk, key := range t.secKeyCache {
preimages[common.BytesToHash([]byte(hk))] = key
}
t.db.InsertPreimage(preimages)
if t.preimages != nil {
t.preimages.InsertPreimage(preimages)
}
t.secKeyCache = make(map[string][]byte)
}
// Commit the trie and return its modified nodeset.
Expand Down
21 changes: 0 additions & 21 deletions trie/trie_reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/trie/triestate"
"github.com/ethereum/go-ethereum/triedb/database"
)

Expand Down Expand Up @@ -72,23 +71,3 @@ func (r *trieReader) node(path []byte, hash common.Hash) ([]byte, error) {
}
return blob, nil
}

// MerkleLoader implements triestate.TrieLoader for constructing tries.
type MerkleLoader struct {
db database.Database
}

// NewMerkleLoader creates the merkle trie loader.
func NewMerkleLoader(db database.Database) *MerkleLoader {
return &MerkleLoader{db: db}
}

// OpenTrie opens the main account trie.
func (l *MerkleLoader) OpenTrie(root common.Hash) (triestate.Trie, error) {
return New(TrieID(root), l.db)
}

// OpenStorageTrie opens the storage trie of an account.
func (l *MerkleLoader) OpenStorageTrie(stateRoot common.Hash, addrHash, root common.Hash) (triestate.Trie, error) {
return New(StorageTrieID(stateRoot, addrHash, root), l.db)
}
212 changes: 1 addition & 211 deletions trie/triestate/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,43 +16,7 @@

package triestate

import (
"errors"
"fmt"
"sync"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie/trienode"
)

// Trie is an Ethereum state trie, can be implemented by Ethereum Merkle Patricia
// tree or Verkle tree.
type Trie interface {
// Get returns the value for key stored in the trie.
Get(key []byte) ([]byte, error)

// Update associates key with value in the trie.
Update(key, value []byte) error

// Delete removes any existing value for key from the trie.
Delete(key []byte) error

// Commit the trie and returns a set of dirty nodes generated along with
// the new root hash.
Commit(collectLeaf bool) (common.Hash, *trienode.NodeSet)
}

// TrieLoader wraps functions to load tries.
type TrieLoader interface {
// OpenTrie opens the main account trie.
OpenTrie(root common.Hash) (Trie, error)

// OpenStorageTrie opens the storage trie of an account.
OpenStorageTrie(stateRoot common.Hash, addrHash, root common.Hash) (Trie, error)
}
import "github.com/ethereum/go-ethereum/common"

// Set represents a collection of mutated states during a state transition.
// The value refers to the original content of state before the transition
Expand Down Expand Up @@ -87,177 +51,3 @@ func (s *Set) Size() common.StorageSize {
}
return s.size
}

// context wraps all fields for executing state diffs.
type context struct {
prevRoot common.Hash
postRoot common.Hash
accounts map[common.Address][]byte
storages map[common.Address]map[common.Hash][]byte
accountTrie Trie
nodes *trienode.MergedNodeSet
}

// Apply traverses the provided state diffs, apply them in the associated
// post-state and return the generated dirty trie nodes. The state can be
// loaded via the provided trie loader.
func Apply(prevRoot common.Hash, postRoot common.Hash, accounts map[common.Address][]byte, storages map[common.Address]map[common.Hash][]byte, loader TrieLoader) (map[common.Hash]map[string]*trienode.Node, error) {
tr, err := loader.OpenTrie(postRoot)
if err != nil {
return nil, err
}
ctx := &context{
prevRoot: prevRoot,
postRoot: postRoot,
accounts: accounts,
storages: storages,
accountTrie: tr,
nodes: trienode.NewMergedNodeSet(),
}
for addr, account := range accounts {
var err error
if len(account) == 0 {
err = deleteAccount(ctx, loader, addr)
} else {
err = updateAccount(ctx, loader, addr)
}
if err != nil {
return nil, fmt.Errorf("failed to revert state, err: %w", err)
}
}
root, result := tr.Commit(false)
if root != prevRoot {
return nil, fmt.Errorf("failed to revert state, want %#x, got %#x", prevRoot, root)
}
if err := ctx.nodes.Merge(result); err != nil {
return nil, err
}
return ctx.nodes.Flatten(), nil
}

// updateAccount the account was present in prev-state, and may or may not
// existent in post-state. Apply the reverse diff and verify if the storage
// root matches the one in prev-state account.
func updateAccount(ctx *context, loader TrieLoader, addr common.Address) error {
// The account was present in prev-state, decode it from the
// 'slim-rlp' format bytes.
h := newHasher()
defer h.release()

addrHash := h.hash(addr.Bytes())
prev, err := types.FullAccount(ctx.accounts[addr])
if err != nil {
return err
}
// The account may or may not existent in post-state, try to
// load it and decode if it's found.
blob, err := ctx.accountTrie.Get(addrHash.Bytes())
if err != nil {
return err
}
post := types.NewEmptyStateAccount()
if len(blob) != 0 {
if err := rlp.DecodeBytes(blob, &post); err != nil {
return err
}
}
// Apply all storage changes into the post-state storage trie.
st, err := loader.OpenStorageTrie(ctx.postRoot, addrHash, post.Root)
if err != nil {
return err
}
for key, val := range ctx.storages[addr] {
var err error
if len(val) == 0 {
err = st.Delete(key.Bytes())
} else {
err = st.Update(key.Bytes(), val)
}
if err != nil {
return err
}
}
root, result := st.Commit(false)
if root != prev.Root {
return errors.New("failed to reset storage trie")
}
// The returned set can be nil if storage trie is not changed
// at all.
if result != nil {
if err := ctx.nodes.Merge(result); err != nil {
return err
}
}
// Write the prev-state account into the main trie
full, err := rlp.EncodeToBytes(prev)
if err != nil {
return err
}
return ctx.accountTrie.Update(addrHash.Bytes(), full)
}

// deleteAccount the account was not present in prev-state, and is expected
// to be existent in post-state. Apply the reverse diff and verify if the
// account and storage is wiped out correctly.
func deleteAccount(ctx *context, loader TrieLoader, addr common.Address) error {
// The account must be existent in post-state, load the account.
h := newHasher()
defer h.release()

addrHash := h.hash(addr.Bytes())
blob, err := ctx.accountTrie.Get(addrHash.Bytes())
if err != nil {
return err
}
if len(blob) == 0 {
return fmt.Errorf("account is non-existent %#x", addrHash)
}
var post types.StateAccount
if err := rlp.DecodeBytes(blob, &post); err != nil {
return err
}
st, err := loader.OpenStorageTrie(ctx.postRoot, addrHash, post.Root)
if err != nil {
return err
}
for key, val := range ctx.storages[addr] {
if len(val) != 0 {
return errors.New("expect storage deletion")
}
if err := st.Delete(key.Bytes()); err != nil {
return err
}
}
root, result := st.Commit(false)
if root != types.EmptyRootHash {
return errors.New("failed to clear storage trie")
}
// The returned set can be nil if storage trie is not changed
// at all.
if result != nil {
if err := ctx.nodes.Merge(result); err != nil {
return err
}
}
// Delete the post-state account from the main trie.
return ctx.accountTrie.Delete(addrHash.Bytes())
}

// hasher is used to compute the sha256 hash of the provided data.
type hasher struct{ sha crypto.KeccakState }

var hasherPool = sync.Pool{
New: func() interface{} { return &hasher{sha: crypto.NewKeccakState()} },
}

func newHasher() *hasher {
return hasherPool.Get().(*hasher)
}

func (h *hasher) hash(data []byte) common.Hash {
return crypto.HashData(h.sha, data)
}

func (h *hasher) release() {
hasherPool.Put(h)
}
9 changes: 1 addition & 8 deletions triedb/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -264,14 +264,7 @@ func (db *Database) Recover(target common.Hash) error {
if !ok {
return errors.New("not supported")
}
var loader triestate.TrieLoader
if db.config.IsVerkle {
// TODO define verkle loader
log.Crit("Verkle loader is not defined")
} else {
loader = trie.NewMerkleLoader(db)
}
return pdb.Recover(target, loader)
return pdb.Recover(target)
}

// Recoverable returns the indicator if the specified state is enabled to be
Expand Down
16 changes: 1 addition & 15 deletions triedb/database/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,7 @@

package database

import (
"github.com/ethereum/go-ethereum/common"
)
import "github.com/ethereum/go-ethereum/common"

// Reader wraps the Node method of a backing trie reader.
type Reader interface {
Expand All @@ -31,20 +29,8 @@ type Reader interface {
Node(owner common.Hash, path []byte, hash common.Hash) ([]byte, error)
}

// PreimageStore wraps the methods of a backing store for reading and writing
// trie node preimages.
type PreimageStore interface {
// Preimage retrieves the preimage of the specified hash.
Preimage(hash common.Hash) []byte

// InsertPreimage commits a set of preimages along with their hashes.
InsertPreimage(preimages map[common.Hash][]byte)
}

// Database wraps the methods of a backing trie store.
type Database interface {
PreimageStore

// Reader returns a node reader associated with the specific state.
// An error will be returned if the specified state is not available.
Reader(stateRoot common.Hash) (Reader, error)
Expand Down
4 changes: 2 additions & 2 deletions triedb/pathdb/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,7 @@ func (db *Database) Enable(root common.Hash) error {
// Recover rollbacks the database to a specified historical point.
// The state is supported as the rollback destination only if it's
// canonical state and the corresponding trie histories are existent.
func (db *Database) Recover(root common.Hash, loader triestate.TrieLoader) error {
func (db *Database) Recover(root common.Hash) error {
db.lock.Lock()
defer db.lock.Unlock()

Expand All @@ -371,7 +371,7 @@ func (db *Database) Recover(root common.Hash, loader triestate.TrieLoader) error
if err != nil {
return err
}
dl, err = dl.revert(h, loader)
dl, err = dl.revert(h)
if err != nil {
return err
}
Expand Down
Loading

0 comments on commit 045b971

Please sign in to comment.