From a2f00cb291ba3c12fc9fdd0975e21f32e1fec4ff Mon Sep 17 00:00:00 2001 From: CPerezz Date: Tue, 7 Apr 2026 19:02:29 +0200 Subject: [PATCH] core/rawdb: add BinTrieStemPrefix and stem accessors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- core/rawdb/accessors_snapshot.go | 28 ++++++++++++++++++++++++++++ core/rawdb/schema.go | 30 ++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/core/rawdb/accessors_snapshot.go b/core/rawdb/accessors_snapshot.go index 5cea581fcd..2ae227c07a 100644 --- a/core/rawdb/accessors_snapshot.go +++ b/core/rawdb/accessors_snapshot.go @@ -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 { diff --git a/core/rawdb/schema.go b/core/rawdb/schema.go index 54c76143b4..abc0db24d7 100644 --- a/core/rawdb/schema.go +++ b/core/rawdb/schema.go @@ -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