core/rawdb: add BinTrieStemPrefix and stem accessors

Reserve a new top-level key-value namespace for bintrie flat-state
entries: BinTrieStemPrefix = "X" (chosen to be free at the top level so
unwrapped-db callers do not collide with blockBodyPrefix "b"). In
practice pathdb nests this namespace under VerklePrefix "v" via
rawdb.NewTable, so the on-disk key is effectively "v"+"X"+stem.

A stem is the 31-byte common prefix of the 32-byte tree key defined in
EIP-7864. The stored value is a packed blob containing the populated
(offset, value) pairs at that stem; BasicData (offset 0), CodeHash
(offset 1), header storage (offsets 64-127), code chunks (offsets
128-255) and main-storage slots can all be extracted from the same
blob by a single Pebble read, matching the on-trie StemNode grouping.

Add Read/Write/DeleteBinTrieStem alongside the existing snapshot
accessors, and a private binTrieStemKey helper in schema.go. No
callers yet — the bintrieFlatCodec in the next commit consumes them.
Matches the same style as ReadAccountSnapshot et al.; the codec and
integration tests downstream will exercise them.
This commit is contained in:
CPerezz 2026-04-07 19:02:29 +02:00
parent 0fb4d9226b
commit a2f00cb291
No known key found for this signature in database
GPG key ID: 62045F34B97177DD
2 changed files with 58 additions and 0 deletions

View file

@ -112,6 +112,34 @@ func DeleteStorageSnapshot(db ethdb.KeyValueWriter, accountHash, storageHash com
}
}
// ReadBinTrieStem retrieves the flat-state stem blob for the given 31-byte
// stem. Returns nil if no entry exists under this stem.
//
// The stem blob is a packed representation of the (offset, value) pairs at
// that stem in the binary trie; callers must decode it to extract any
// specific offset. See trie/bintrie and EIP-7864 for the on-trie layout.
func ReadBinTrieStem(db ethdb.KeyValueReader, stem []byte) []byte {
data, _ := db.Get(binTrieStemKey(stem))
return data
}
// WriteBinTrieStem stores the flat-state stem blob for the given 31-byte
// stem. The blob is written verbatim; encoding/decoding is the caller's
// responsibility.
func WriteBinTrieStem(db ethdb.KeyValueWriter, stem []byte, blob []byte) {
if err := db.Put(binTrieStemKey(stem), blob); err != nil {
log.Crit("Failed to store bintrie stem", "err", err)
}
}
// DeleteBinTrieStem removes the flat-state stem blob entry for the given
// 31-byte stem.
func DeleteBinTrieStem(db ethdb.KeyValueWriter, stem []byte) {
if err := db.Delete(binTrieStemKey(stem)); err != nil {
log.Crit("Failed to delete bintrie stem", "err", err)
}
}
// IterateStorageSnapshots returns an iterator for walking the entire storage
// space of a specific account.
func IterateStorageSnapshots(db ethdb.Iteratee, accountHash common.Hash) ethdb.Iterator {

View file

@ -126,6 +126,20 @@ var (
TrieNodeStoragePrefix = []byte("O") // TrieNodeStoragePrefix + accountHash + hexPath -> trie node
stateIDPrefix = []byte("L") // stateIDPrefix + state root -> state id
// Binary-trie flat-state scheme. A stem is 31 bytes per EIP-7864 (the
// common prefix of the 32-byte tree key); the stored value is a packed
// blob containing the subset of 256 offset values that are populated
// for this stem (layout: 32-byte bitmap of present offsets, followed
// by N 32-byte values in offset order).
//
// Note: bintrie pathdb wraps the disk database in a table keyed by
// VerklePrefix ("v"), so this prefix is effectively nested inside "v"
// when used by pathdb. It is defined as a distinct top-level byte
// ("X") to prevent accidental collisions with other top-level
// namespaces (e.g. blockBodyPrefix "b") when the codec is ever used
// against an unwrapped database.
BinTrieStemPrefix = []byte("X") // BinTrieStemPrefix + stem(31B) -> stem blob
// State history indexing within path-based storage scheme
StateHistoryIndexPrefix = []byte("m") // The global prefix of state history index data
StateHistoryAccountMetadataPrefix = []byte("ma") // StateHistoryAccountMetadataPrefix + account address hash => account metadata
@ -297,6 +311,22 @@ func storageTrieNodeKey(accountHash common.Hash, path []byte) []byte {
return buf
}
// binTrieStemKey = BinTrieStemPrefix + stem (31 bytes).
//
// A bintrie stem is the common 31-byte prefix of the 32-byte tree key (see
// EIP-7864). The stem blob stored under this key holds the packed set of
// (offset, value) pairs at that stem, from which BasicData (offset 0),
// CodeHash (offset 1), header storage (offsets 64-127), code chunks
// (offsets 128-255) and main-storage slots can be extracted.
func binTrieStemKey(stem []byte) []byte {
// Callers always pass a 31-byte stem. We allocate the exact size to
// avoid accidental aliasing with backing storage.
buf := make([]byte, len(BinTrieStemPrefix)+len(stem))
n := copy(buf, BinTrieStemPrefix)
copy(buf[n:], stem)
return buf
}
// 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