Skip to content
Merged
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
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)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe get and put or insert or something. tr.preimages.Preimage() and tr.preimages.InsertPreimage() feel kinda cumbersome

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unfortunately this two methods are implemented by the triedb. Get or Put is too generic for triedb.
I can try to improve the interface implementation a bit in a following PR.

}

// 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