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