Skip to content

Commit

Permalink
core, trie/triedb/pathdb: remove hash checking in pathdb
Browse files Browse the repository at this point in the history
  • Loading branch information
rjl493456442 committed Aug 25, 2023
1 parent 60ec41c commit 186bb05
Show file tree
Hide file tree
Showing 14 changed files with 162 additions and 197 deletions.
43 changes: 15 additions & 28 deletions core/rawdb/accessors_trie.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,16 +65,10 @@ func (h *hasher) release() {
hasherPool.Put(h)
}

// ReadAccountTrieNode retrieves the account trie node and the associated node
// hash with the specified node path.
func ReadAccountTrieNode(db ethdb.KeyValueReader, path []byte) ([]byte, common.Hash) {
data, err := db.Get(accountTrieNodeKey(path))
if err != nil {
return nil, common.Hash{}
}
h := newHasher()
defer h.release()
return data, h.hash(data)
// ReadAccountTrieNode retrieves the account trie node with the specified node path.
func ReadAccountTrieNode(db ethdb.KeyValueReader, path []byte) []byte {
data, _ := db.Get(accountTrieNodeKey(path))
return data
}

// HasAccountTrieNode checks the account trie node presence with the specified
Expand Down Expand Up @@ -103,16 +97,10 @@ func DeleteAccountTrieNode(db ethdb.KeyValueWriter, path []byte) {
}
}

// ReadStorageTrieNode retrieves the storage trie node and the associated node
// hash with the specified node path.
func ReadStorageTrieNode(db ethdb.KeyValueReader, accountHash common.Hash, path []byte) ([]byte, common.Hash) {
data, err := db.Get(storageTrieNodeKey(accountHash, path))
if err != nil {
return nil, common.Hash{}
}
h := newHasher()
defer h.release()
return data, h.hash(data)
// ReadStorageTrieNode retrieves the storage trie node with the specified node path.
func ReadStorageTrieNode(db ethdb.KeyValueReader, accountHash common.Hash, path []byte) []byte {
data, _ := db.Get(storageTrieNodeKey(accountHash, path))
return data
}

// HasStorageTrieNode checks the storage trie node presence with the provided
Expand Down Expand Up @@ -200,16 +188,15 @@ func ReadTrieNode(db ethdb.KeyValueReader, owner common.Hash, path []byte, hash
case HashScheme:
return ReadLegacyTrieNode(db, hash)
case PathScheme:
var (
blob []byte
nHash common.Hash
)
var blob []byte
if owner == (common.Hash{}) {
blob, nHash = ReadAccountTrieNode(db, path)
blob = ReadAccountTrieNode(db, path)
} else {
blob, nHash = ReadStorageTrieNode(db, owner, path)
blob = ReadStorageTrieNode(db, owner, path)
}
if nHash != hash {
h := newHasher()
defer h.release()
if h.hash(blob) != hash {
return nil
}
return blob
Expand Down Expand Up @@ -268,7 +255,7 @@ func DeleteTrieNode(db ethdb.KeyValueWriter, owner common.Hash, path []byte, has
// if the state is not present in database.
func ReadStateScheme(db ethdb.Reader) string {
// Check if state in path-based scheme is present
blob, _ := ReadAccountTrieNode(db, nil)
blob := ReadAccountTrieNode(db, nil)
if len(blob) != 0 {
return PathScheme
}
Expand Down
13 changes: 13 additions & 0 deletions trie/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,19 @@ import (
"github.com/ethereum/go-ethereum/trie/triestate"
)

// Reader wraps the Node method of a backing trie store.
type Reader interface {
// Node retrieves the trie node blob with the provided trie identifier, node path and
// the corresponding node hash. No error will be returned if the node is not found.
//
// When looking up nodes in the account trie, 'owner' is the zero hash. For contract
// storage trie nodes, 'owner' is the hash of the account address that containing the
// storage.
//
// TODO(rjl493456442): remove the 'hash' parameter, it's redundant in PBSS.
Node(owner common.Hash, path []byte, hash common.Hash) ([]byte, error)
}

// Config defines all necessary options for database.
type Config struct {
Preimages bool // Flag whether the preimage of node key is recorded
Expand Down
13 changes: 0 additions & 13 deletions trie/trie_reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,19 +23,6 @@ import (
"github.com/ethereum/go-ethereum/trie/triestate"
)

// Reader wraps the Node method of a backing trie store.
type Reader interface {
// Node retrieves the trie node blob with the provided trie identifier, node path and
// the corresponding node hash. No error will be returned if the node is not found.
//
// When looking up nodes in the account trie, 'owner' is the zero hash. For contract
// storage trie nodes, 'owner' is the hash of the account address that containing the
// storage.
//
// TODO(rjl493456442): remove the 'hash' parameter, it's redundant in PBSS.
Node(owner common.Hash, path []byte, hash common.Hash) ([]byte, error)
}

// trieReader is a wrapper of the underlying node reader. It's not safe
// for concurrent usage.
type trieReader struct {
Expand Down
34 changes: 29 additions & 5 deletions trie/triedb/pathdb/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"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/params"
Expand Down Expand Up @@ -61,7 +62,7 @@ type layer interface {
// if the read operation exits abnormally. For example, if the layer is already
// stale, or the associated state is regarded as corrupted. Notably, no error
// will be returned if the requested node is not found in database.
Node(owner common.Hash, path []byte, hash common.Hash) ([]byte, error)
Node(owner common.Hash, path []byte) ([]byte, error)

// rootHash returns the root hash for which this layer was made.
rootHash() common.Hash
Expand All @@ -76,7 +77,7 @@ type layer interface {
// the provided dirty trie nodes along with the state change set.
//
// Note, the maps are retained by the method to avoid copying everything.
update(root common.Hash, id uint64, block uint64, nodes map[common.Hash]map[string]*trienode.Node, states *triestate.Set) *diffLayer
update(root common.Hash, id uint64, block uint64, nodes map[common.Hash]map[string][]byte, states *triestate.Set) *diffLayer

// journal commits an entire diff hierarchy to disk into a single journal entry.
// This is meant to be used during shutdown to persist the layer without
Expand Down Expand Up @@ -183,13 +184,35 @@ func New(diskdb ethdb.Database, config *Config) *Database {
return db
}

// reader is a state reader of Database which implements the Reader interface.
type reader struct {
l layer
}

func (r *reader) Node(owner common.Hash, path []byte, hash common.Hash) ([]byte, error) {
blob, err := r.l.Node(owner, path)
if err != nil {
return nil, err
}
// Note, in verkle tree, the hash checking can be ignored/skipped
// if slim node format is used. TODO(rjl493456442) remove it for
// verkle branch.
h := newHasher()
defer h.release()

if got := h.hash(blob); got != hash {
return nil, fmt.Errorf("unexpected node: (%x %v), %x!=%x", owner, path, hash, got)
}
return blob, nil
}

// Reader retrieves a layer belonging to the given state root.
func (db *Database) Reader(root common.Hash) (layer, error) {
func (db *Database) Reader(root common.Hash) (*reader, error) {
l := db.tree.get(root)
if l == nil {
return nil, fmt.Errorf("state %#x is not available", root)
}
return l, nil
return &reader{l: l}, nil
}

// Update adds a new layer into the tree, if that can be linked to an existing
Expand Down Expand Up @@ -256,7 +279,8 @@ func (db *Database) Reset(root common.Hash) error {
} else {
// Ensure the requested state is existent before any
// action is applied.
_, hash := rawdb.ReadAccountTrieNode(db.diskdb, nil)
blob := rawdb.ReadAccountTrieNode(db.diskdb, nil)
hash := crypto.Keccak256Hash(blob)
if hash != root {
return fmt.Errorf("state is mismatched, local: %x, target: %x", hash, root)
}
Expand Down
4 changes: 3 additions & 1 deletion trie/triedb/pathdb/database_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -530,7 +530,9 @@ func TestCorruptedJournal(t *testing.T) {
t.Errorf("Failed to journal, err: %v", err)
}
tester.db.Close()
_, root := rawdb.ReadAccountTrieNode(tester.db.diskdb, nil)

rootBlob := rawdb.ReadAccountTrieNode(tester.db.diskdb, nil)
root := crypto.Keccak256Hash(rootBlob)

// Mutate the journal in disk, it should be regarded as invalid
blob := rawdb.ReadTrieJournal(tester.db.diskdb)
Expand Down
42 changes: 17 additions & 25 deletions trie/triedb/pathdb/difflayer.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import (

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/trie/trienode"
"github.com/ethereum/go-ethereum/trie/triestate"
)

Expand All @@ -33,19 +32,19 @@ import (
// made to the state, that have not yet graduated into a semi-immutable state.
type diffLayer struct {
// Immutables
root common.Hash // Root hash to which this layer diff belongs to
id uint64 // Corresponding state id
block uint64 // Associated block number
nodes map[common.Hash]map[string]*trienode.Node // Cached trie nodes indexed by owner and path
states *triestate.Set // Associated state change set for building history
memory uint64 // Approximate guess as to how much memory we use
root common.Hash // Root hash to which this layer diff belongs to
id uint64 // Corresponding state id
block uint64 // Associated block number
nodes map[common.Hash]map[string][]byte // Cached trie nodes indexed by owner and path
states *triestate.Set // Associated state change set for building history
memory uint64 // Approximate guess as to how much memory we use

parent layer // Parent layer modified by this one, never nil, **can be changed**
lock sync.RWMutex // Lock used to protect parent
}

// newDiffLayer creates a new diff layer on top of an existing layer.
func newDiffLayer(parent layer, root common.Hash, id uint64, block uint64, nodes map[common.Hash]map[string]*trienode.Node, states *triestate.Set) *diffLayer {
func newDiffLayer(parent layer, root common.Hash, id uint64, block uint64, nodes map[common.Hash]map[string][]byte, states *triestate.Set) *diffLayer {
var (
size int64
count int
Expand All @@ -60,11 +59,11 @@ func newDiffLayer(parent layer, root common.Hash, id uint64, block uint64, nodes
}
for _, subset := range nodes {
for path, n := range subset {
dl.memory += uint64(n.Size() + len(path))
size += int64(len(n.Blob) + len(path))
size += int64(len(n) + len(path))
}
count += len(subset)
}
dl.memory = uint64(size)
if states != nil {
dl.memory += uint64(states.Size())
}
Expand Down Expand Up @@ -98,7 +97,7 @@ func (dl *diffLayer) parentLayer() layer {
// node retrieves the node with provided node information. It's the internal
// version of Node function with additional accessed layer tracked. No error
// will be returned if node is not found.
func (dl *diffLayer) node(owner common.Hash, path []byte, hash common.Hash, depth int) ([]byte, error) {
func (dl *diffLayer) node(owner common.Hash, path []byte, depth int) ([]byte, error) {
// Hold the lock, ensure the parent won't be changed during the
// state accessing.
dl.lock.RLock()
Expand All @@ -109,36 +108,29 @@ func (dl *diffLayer) node(owner common.Hash, path []byte, hash common.Hash, dept
if ok {
n, ok := subset[string(path)]
if ok {
// If the trie node is not hash matched, or marked as removed,
// bubble up an error here. It shouldn't happen at all.
if n.Hash != hash {
dirtyFalseMeter.Mark(1)
log.Error("Unexpected trie node in diff layer", "owner", owner, "path", path, "expect", hash, "got", n.Hash)
return nil, newUnexpectedNodeError("diff", hash, n.Hash, owner, path)
}
dirtyHitMeter.Mark(1)
dirtyNodeHitDepthHist.Update(int64(depth))
dirtyReadMeter.Mark(int64(len(n.Blob)))
return n.Blob, nil
dirtyReadMeter.Mark(int64(len(n)))
return n, nil
}
}
// Trie node unknown to this layer, resolve from parent
if diff, ok := dl.parent.(*diffLayer); ok {
return diff.node(owner, path, hash, depth+1)
return diff.node(owner, path, depth+1)
}
// Failed to resolve through diff layers, fallback to disk layer
return dl.parent.Node(owner, path, hash)
return dl.parent.Node(owner, path)
}

// Node implements the layer interface, retrieving the trie node blob with the
// provided node information. No error will be returned if the node is not found.
func (dl *diffLayer) Node(owner common.Hash, path []byte, hash common.Hash) ([]byte, error) {
return dl.node(owner, path, hash, 0)
func (dl *diffLayer) Node(owner common.Hash, path []byte) ([]byte, error) {
return dl.node(owner, path, 0)
}

// update implements the layer interface, creating a new layer on top of the
// existing layer tree with the specified data items.
func (dl *diffLayer) update(root common.Hash, id uint64, block uint64, nodes map[common.Hash]map[string]*trienode.Node, states *triestate.Set) *diffLayer {
func (dl *diffLayer) update(root common.Hash, id uint64, block uint64, nodes map[common.Hash]map[string][]byte, states *triestate.Set) *diffLayer {
return newDiffLayer(dl, root, id, block, nodes, states)
}

Expand Down
31 changes: 14 additions & 17 deletions trie/triedb/pathdb/difflayer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/trie/testutil"
"github.com/ethereum/go-ethereum/trie/trienode"
)

func emptyLayer() *diskLayer {
Expand Down Expand Up @@ -57,23 +56,21 @@ func BenchmarkSearch1Layer(b *testing.B) { benchmarkSearch(b, 127, 128) }
func benchmarkSearch(b *testing.B, depth int, total int) {
var (
npath []byte
nhash common.Hash
nblob []byte
)
// First, we set up 128 diff layers, with 3K items each
fill := func(parent layer, index int) *diffLayer {
nodes := make(map[common.Hash]map[string]*trienode.Node)
nodes[common.Hash{}] = make(map[string]*trienode.Node)
nodes := make(map[common.Hash]map[string][]byte)
nodes[common.Hash{}] = make(map[string][]byte)
for i := 0; i < 3000; i++ {
var (
path = testutil.RandBytes(32)
node = testutil.RandomNode()
blob = testutil.RandBytes(100)
)
nodes[common.Hash{}][string(path)] = trienode.New(node.Hash, node.Blob)
nodes[common.Hash{}][string(path)] = blob
if npath == nil && depth == index {
npath = common.CopyBytes(path)
nblob = common.CopyBytes(node.Blob)
nhash = node.Hash
nblob = common.CopyBytes(blob)
}
}
return newDiffLayer(parent, common.Hash{}, 0, 0, nodes, nil)
Expand All @@ -90,7 +87,7 @@ func benchmarkSearch(b *testing.B, depth int, total int) {
err error
)
for i := 0; i < b.N; i++ {
have, err = layer.Node(common.Hash{}, npath, nhash)
have, err = layer.Node(common.Hash{}, npath)
if err != nil {
b.Fatal(err)
}
Expand All @@ -108,14 +105,14 @@ func benchmarkSearch(b *testing.B, depth int, total int) {
func BenchmarkPersist(b *testing.B) {
// First, we set up 128 diff layers, with 3K items each
fill := func(parent layer) *diffLayer {
nodes := make(map[common.Hash]map[string]*trienode.Node)
nodes[common.Hash{}] = make(map[string]*trienode.Node)
nodes := make(map[common.Hash]map[string][]byte)
nodes[common.Hash{}] = make(map[string][]byte)
for i := 0; i < 3000; i++ {
var (
path = testutil.RandBytes(32)
node = testutil.RandomNode()
blob = testutil.RandBytes(100)
)
nodes[common.Hash{}][string(path)] = trienode.New(node.Hash, node.Blob)
nodes[common.Hash{}][string(path)] = blob
}
return newDiffLayer(parent, common.Hash{}, 0, 0, nodes, nil)
}
Expand Down Expand Up @@ -145,14 +142,14 @@ func BenchmarkJournal(b *testing.B) {

// First, we set up 128 diff layers, with 3K items each
fill := func(parent layer) *diffLayer {
nodes := make(map[common.Hash]map[string]*trienode.Node)
nodes[common.Hash{}] = make(map[string]*trienode.Node)
nodes := make(map[common.Hash]map[string][]byte)
nodes[common.Hash{}] = make(map[string][]byte)
for i := 0; i < 3000; i++ {
var (
path = testutil.RandBytes(32)
node = testutil.RandomNode()
blob = testutil.RandBytes(100)
)
nodes[common.Hash{}][string(path)] = trienode.New(node.Hash, node.Blob)
nodes[common.Hash{}][string(path)] = blob
}
// TODO(rjl493456442) a non-nil state set is expected.
return newDiffLayer(parent, common.Hash{}, 0, 0, nodes, nil)
Expand Down
Loading

0 comments on commit 186bb05

Please sign in to comment.