go-ethereum/triedb/nomtdb/reader.go
weiihann 53fd00926f triedb/nomtdb, trie/nomttrie: add Phase 6 geth integration for NOMT
Wire the NOMT binary merkle trie engine into geth's triedb/state
framework. This adds two new packages:

- triedb/nomtdb: backend implementing triedb.backend interface, manages
  flat state persistence in ethdb and delegates trie ops to nomt/db
- trie/nomttrie: NomtTrie implementing state.Trie, accumulates LeafOps
  during block execution and flushes to NOMT engine on Hash()/Commit()

Key design choices:
- Single flat keyspace: accounts use keccak256(addr), storage uses
  keccak256(keccak256(addr) || keccak256(slot)) as 256-bit trie paths
- OpenStorageTrie returns the account trie itself (no separate tries)
- Flat state (account/storage values) stored in ethdb with prefixed keys
- NOMT trie stores only hashes; reads delegate to ethdb flat state

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 17:36:57 +08:00

84 lines
2.6 KiB
Go

package nomtdb
import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/nomt/db"
"github.com/ethereum/go-ethereum/rlp"
)
// Key prefixes for NOMT flat state in ethdb.
const (
nomtAccountPrefix byte = 0x01
nomtStoragePrefix byte = 0x02
)
// NomtAccountKey returns the ethdb key for an account's flat state.
// Format: 0x01 || accountHash (32 bytes)
func NomtAccountKey(accountHash common.Hash) []byte {
key := make([]byte, 1+common.HashLength)
key[0] = nomtAccountPrefix
copy(key[1:], accountHash[:])
return key
}
// NomtStorageKey returns the ethdb key for a storage slot's flat state.
// Format: 0x02 || accountHash (32 bytes) || slotHash (32 bytes)
func NomtStorageKey(accountHash, slotHash common.Hash) []byte {
key := make([]byte, 1+2*common.HashLength)
key[0] = nomtStoragePrefix
copy(key[1:], accountHash[:])
copy(key[1+common.HashLength:], slotHash[:])
return key
}
// nodeReader implements database.NodeReader backed by the NOMT page store.
//
// In NOMT, trie data is stored as pages (126 nodes each), not individual nodes.
// This reader extracts individual nodes from pages for compatibility with
// geth's node-oriented interfaces.
type nodeReader struct {
nomt *db.DB
}
// Node retrieves a trie node blob. For NOMT, this is a compatibility shim —
// the NomtTrie accesses pages directly rather than going through NodeReader.
func (r *nodeReader) Node(owner common.Hash, path []byte, hash common.Hash) ([]byte, error) {
// NOMT trie operations go through the page-based engine directly.
// This reader exists to satisfy the interface; the NomtTrie doesn't
// use it for normal trie operations.
return nil, nil
}
// stateReader implements database.StateReader backed by NOMT's flat state
// stored in geth's ethdb.
type stateReader struct {
diskdb ethdb.Database
}
// Account retrieves an account from NOMT's flat state storage.
func (r *stateReader) Account(hash common.Hash) (*types.SlimAccount, error) {
data, err := r.diskdb.Get(NomtAccountKey(hash))
if err != nil {
// Key not found is not an error — return nil account.
return nil, nil
}
if len(data) == 0 {
return nil, nil
}
account := new(types.SlimAccount)
if err := rlp.DecodeBytes(data, account); err != nil {
return nil, err
}
return account, nil
}
// Storage retrieves a storage slot from NOMT's flat state storage.
func (r *stateReader) Storage(accountHash, storageHash common.Hash) ([]byte, error) {
data, err := r.diskdb.Get(NomtStorageKey(accountHash, storageHash))
if err != nil {
return nil, nil
}
return data, nil
}