Skip to content

Commit

Permalink
trie: refactor trie node
Browse files Browse the repository at this point in the history
  • Loading branch information
Francesco4203 committed Sep 27, 2024
1 parent 1bb3b3f commit 01ec38a
Show file tree
Hide file tree
Showing 11 changed files with 279 additions and 231 deletions.
48 changes: 47 additions & 1 deletion core/rawdb/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"encoding/binary"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/metrics"
)

Expand Down Expand Up @@ -103,7 +104,7 @@ var (
internalTxsPrefix = []byte("itxs") // internalTxsPrefix + block hash -> internal transactions
dirtyAccountsKey = []byte("dacc") // dirtyAccountsPrefix + block hash -> dirty accounts

// Path-based trie node scheme.
// Path-based storage scheme of merkle patricia trie.
trieNodeAccountPrefix = []byte("A") // trieNodeAccountPrefix + hexPath -> trie node
trieNodeStoragePrefix = []byte("O") // trieNodeStoragePrefix + accountHash + hexPath -> trie node

Expand Down Expand Up @@ -250,3 +251,48 @@ func storageTrieNodeKey(accountHash common.Hash, path []byte) []byte {
func snapshotConsortiumKey(hash common.Hash) []byte {
return append(snapshotConsortiumPrefix, hash.Bytes()...)
}

// IsLegacyTrieNode reports whether a provided database entry is a legacy trie
// node. The characteristics of legacy trie node are:
// - the key length is 32 bytes
// - the key is the hash of val
func IsLegacyTrieNode(key []byte, val []byte) bool {
if len(key) != common.HashLength {
return false
}
return bytes.Equal(key, crypto.Keccak256(val))
}

// IsAccountTrieNode reports whether a provided database entry is an account
// trie node in path-based state scheme.
func IsAccountTrieNode(key []byte) (bool, []byte) {
if !bytes.HasPrefix(key, trieNodeAccountPrefix) {
return false, nil
}
// The remaining key should only consist a hex node path
// whose length is in the range 0 to 64 (64 is excluded
// since leaves are always wrapped with shortNode).
if len(key) >= len(trieNodeAccountPrefix)+common.HashLength*2 {
return false, nil
}
return true, key[len(trieNodeAccountPrefix):]
}

// IsStorageTrieNode reports whether a provided database entry is a storage
// trie node in path-based state scheme.
func IsStorageTrieNode(key []byte) (bool, common.Hash, []byte) {
if !bytes.HasPrefix(key, trieNodeStoragePrefix) {
return false, common.Hash{}, nil
}
// The remaining key consists of 2 parts:
// - 32 bytes account hash
// - hex node path whose length is in the range 0 to 64
if len(key) < len(trieNodeStoragePrefix)+common.HashLength {
return false, common.Hash{}, nil
}
if len(key) >= len(trieNodeStoragePrefix)+common.HashLength+common.HashLength*2 {
return false, common.Hash{}, nil
}
accountHash := common.BytesToHash(key[len(trieNodeStoragePrefix) : len(trieNodeStoragePrefix)+common.HashLength])
return true, accountHash, key[len(trieNodeStoragePrefix)+common.HashLength:]
}
3 changes: 2 additions & 1 deletion core/state/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/trie"
"github.com/ethereum/go-ethereum/trie/trienode"
lru "github.com/hashicorp/golang-lru/v2"
)

Expand Down Expand Up @@ -92,7 +93,7 @@ type Trie interface {
// corresponding node hash. All collected nodes(including dirty leaves if
// collectLeaf is true) will be encapsulated into a nodeset for return.
// The returned nodeset can be nil if the trie is clean(nothing to commit).
Commit(collectLeaf bool) (common.Hash, *trie.NodeSet, error)
Commit(collectLeaf bool) (common.Hash, *trienode.NodeSet, error)

// NodeIterator returns an iterator that returns nodes of the trie. Iteration
// starts at the key after the given start key.
Expand Down
6 changes: 4 additions & 2 deletions core/state/snapshot/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,14 @@ import (
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/metrics"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie"
"github.com/ethereum/go-ethereum/trie/trienode"
)

var (
Expand Down Expand Up @@ -438,9 +440,9 @@ func (dl *diskLayer) generateRange(trieID *trie.ID, prefix []byte, kind string,
}
root, nodes, _ := snapTrie.Commit(false)
if nodes != nil {
snapTrieDb.Update(trie.NewWithNodeSet(nodes))
snapTrieDb.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes))
}
snapTrieDb.Commit(root, false, nil)
snapTrieDb.Commit(root, false)
}
tr := result.tr
if tr == nil {
Expand Down
4 changes: 2 additions & 2 deletions core/state/state_object.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import (
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/metrics"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie"
"github.com/ethereum/go-ethereum/trie/trienode"
)

var emptyCodeHash = crypto.Keccak256(nil)
Expand Down Expand Up @@ -394,7 +394,7 @@ func (s *stateObject) updateRoot(db Database) {

// commitTrie submits the storage changes into the storage trie and re-computes
// the root. Besides, all trie changes will be collected in a nodeset and returned.
func (s *stateObject) commitTrie(db Database) (*trie.NodeSet, error) {
func (s *stateObject) commitTrie(db Database) (*trienode.NodeSet, error) {
// If nothing changed, don't bother with hashing anything
if s.updateTrie(db) == nil {
return nil, nil
Expand Down
5 changes: 3 additions & 2 deletions core/state/statedb.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import (
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie"
"github.com/ethereum/go-ethereum/trie/trienode"
)

type revision struct {
Expand Down Expand Up @@ -978,7 +979,7 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (common.Hash, error) {
accountTrieNodesDeleted int
storageTrieNodesUpdated int
storageTrieNodesDeleted int
nodes = trie.NewMergedNodeSet()
nodes = trienode.NewMergedNodeSet()
)
codeWriter := s.db.TrieDB().DiskDB().NewBatch()
for addr := range s.stateObjectsDirty {
Expand Down Expand Up @@ -1082,7 +1083,7 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (common.Hash, error) {
}
if root != origin {
start := time.Now()
if err := s.db.TrieDB().Update(nodes); err != nil {
if err := s.db.TrieDB().Update(root, origin, nodes); err != nil {
return common.Hash{}, err
}
s.originalRoot = root
Expand Down
32 changes: 12 additions & 20 deletions trie/committer.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,20 +23,14 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie/trienode"
"golang.org/x/crypto/sha3"
)

// leafChanSize is the size of the leafCh. It's a pretty arbitrary number, to allow
// some parallelism but not incur too much memory overhead.
const leafChanSize = 200

// leaf represents a trie leaf value
type leaf struct {
blob []byte // raw blob of leaf
parent common.Hash // the hash of parent node
path []byte // the path from the root node
}

// committer is a type used for the trie Commit operation. The committer will
// capture all dirty nodes during the commit process and keep them cached in
// insertion order.
Expand All @@ -45,7 +39,7 @@ type committer struct {
sha crypto.KeccakState

owner common.Hash // TODO: same as nodes.owner, consider removing
nodes *NodeSet
nodes *trienode.NodeSet
tracer *tracer
collectLeaf bool
}
Expand All @@ -61,7 +55,7 @@ var committerPool = sync.Pool{
}

// newCommitter creates a new committer or picks one from the pool.
func newCommitter(nodes *NodeSet, tracer *tracer, collectLeaf bool) *committer {
func newCommitter(nodes *trienode.NodeSet, tracer *tracer, collectLeaf bool) *committer {
return &committer{
nodes: nodes,
tracer: tracer,
Expand All @@ -70,7 +64,7 @@ func newCommitter(nodes *NodeSet, tracer *tracer, collectLeaf bool) *committer {
}

// Commit collapses a node down into a hash node and inserts it into the database
func (c *committer) Commit(n node) (hashNode, *NodeSet, error) {
func (c *committer) Commit(n node) (hashNode, *trienode.NodeSet, error) {
h, err := c.commit(nil, n)
if err != nil {
return nil, nil, err
Expand Down Expand Up @@ -176,7 +170,7 @@ func (c *committer) store(path []byte, n node) node {
// deleted only if the node was existent in database before.
prev, ok := c.tracer.accessList[string(path)]
if ok {
c.nodes.addNode(path, &nodeWithPrev{&memoryNode{}, prev})
c.nodes.AddNode(path, trienode.NewWithPrev(common.Hash{}, nil, prev))
}
return n
}
Expand All @@ -185,24 +179,22 @@ func (c *committer) store(path []byte, n node) node {
var (
nhash = common.BytesToHash(hash)
blob, _ = rlp.EncodeToBytes(n)
node = &nodeWithPrev{
&memoryNode{
nhash,
blob,
},
node = trienode.NewWithPrev(
nhash,
blob,
c.tracer.accessList[string(path)],
}
)
)

// Collect the dirty node to nodeset for return.
c.nodes.addNode(path, node)
c.nodes.AddNode(path, node)
// Collect the corresponding leaf node if it's required. We don't check
// full node since it's impossible to store value in fullNode. The key
// length of leaves should be exactly same.
if c.collectLeaf {
if sn, ok := n.(*shortNode); ok {
if val, ok := sn.Val.(valueNode); ok {
c.nodes.addLeaf(&leaf{blob: val, parent: nhash})
c.nodes.AddLeaf(nhash, val)
}
}
}
Expand All @@ -214,7 +206,7 @@ type mptResolver struct{}

// ForEach implements childResolver, decodes the provided node and
// traverses the children inside.
func (resolver mptResolver) forEach(node []byte, onChild func(common.Hash)) {
func (resolver mptResolver) ForEach(node []byte, onChild func(common.Hash)) {
forGatherChildren(mustDecodeNode(nil, node), onChild)
}

Expand Down
Loading

0 comments on commit 01ec38a

Please sign in to comment.