core, trie, triedb: group 2^N binary trie nodes in serialization

This commit is contained in:
Guillaume Ballet 2026-04-22 16:33:14 +02:00
parent b0ead5e17b
commit f99258ad36
No known key found for this signature in database
10 changed files with 65 additions and 28 deletions

View file

@ -455,7 +455,7 @@ func BinKeys(ctx *cli.Context) error {
db := triedb.NewDatabase(rawdb.NewMemoryDatabase(), triedb.UBTDefaults)
defer db.Close()
bt, err := genBinTrieFromAlloc(alloc, db)
bt, err := genBinTrieFromAlloc(alloc, db, triedb.UBTDefaults.BinTrieGroupDepth)
if err != nil {
return fmt.Errorf("error generating bt: %w", err)
}
@ -499,7 +499,7 @@ func BinTrieRoot(ctx *cli.Context) error {
db := triedb.NewDatabase(rawdb.NewMemoryDatabase(), triedb.UBTDefaults)
defer db.Close()
bt, err := genBinTrieFromAlloc(alloc, db)
bt, err := genBinTrieFromAlloc(alloc, db, triedb.UBTDefaults.BinTrieGroupDepth)
if err != nil {
return fmt.Errorf("error generating bt: %w", err)
}
@ -509,8 +509,8 @@ func BinTrieRoot(ctx *cli.Context) error {
}
// TODO(@CPerezz): Should this go to `bintrie` module?
func genBinTrieFromAlloc(alloc core.GenesisAlloc, db database.NodeDatabase) (*bintrie.BinaryTrie, error) {
bt, err := bintrie.NewBinaryTrie(types.EmptyBinaryHash, db)
func genBinTrieFromAlloc(alloc core.GenesisAlloc, db database.NodeDatabase, groupDepth int) (*bintrie.BinaryTrie, error) {
bt, err := bintrie.NewBinaryTrie(types.EmptyBinaryHash, db, groupDepth)
if err != nil {
return nil, err
}

View file

@ -95,6 +95,7 @@ var (
utils.StateHistoryFlag,
utils.TrienodeHistoryFlag,
utils.TrienodeHistoryFullValueCheckpointFlag,
utils.BinTrieGroupDepthFlag,
utils.LightKDFFlag,
utils.EthRequiredBlocksFlag,
utils.LegacyWhitelistFlag, // deprecated

View file

@ -297,6 +297,12 @@ var (
Value: ethconfig.Defaults.EnableStateSizeTracking,
Category: flags.StateCategory,
}
BinTrieGroupDepthFlag = &cli.IntFlag{
Name: "bintrie.groupdepth",
Usage: "Number of levels per serialized group in binary trie (1-8, default 8). Lower values create smaller groups with more nodes.",
Value: 8,
Category: flags.StateCategory,
}
StateHistoryFlag = &cli.Uint64Flag{
Name: "history.state",
Usage: "Number of recent blocks to retain state history for, only relevant in state.scheme=path (default = 90,000 blocks, 0 = entire chain)",
@ -1815,7 +1821,10 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
cfg.TrienodeHistory = ctx.Int64(TrienodeHistoryFlag.Name)
}
if ctx.IsSet(TrienodeHistoryFullValueCheckpointFlag.Name) {
cfg.NodeFullValueCheckpoint = uint32(ctx.Uint(TrienodeHistoryFullValueCheckpointFlag.Name))
cfg.TrienodeHistory = ctx.Int64(TrienodeHistoryFullValueCheckpointFlag.Name)
}
if ctx.IsSet(BinTrieGroupDepthFlag.Name) {
cfg.BinTrieGroupDepth = ctx.Int(BinTrieGroupDepthFlag.Name)
}
if ctx.IsSet(StateSchemeFlag.Name) {
cfg.StateScheme = ctx.String(StateSchemeFlag.Name)
@ -2433,6 +2442,7 @@ func MakeChain(ctx *cli.Context, stack *node.Node, readonly bool) (*core.BlockCh
StateHistory: ctx.Uint64(StateHistoryFlag.Name),
TrienodeHistory: ctx.Int64(TrienodeHistoryFlag.Name),
NodeFullValueCheckpoint: uint32(ctx.Uint(TrienodeHistoryFullValueCheckpointFlag.Name)),
BinTrieGroupDepth: ctx.Int(BinTrieGroupDepthFlag.Name),
// Disable transaction indexing/unindexing.
TxLookupLimit: -1,

View file

@ -170,9 +170,10 @@ type BlockChainConfig struct {
TrieNoAsyncFlush bool // Whether the asynchronous buffer flushing is disallowed
TrieJournalDirectory string // Directory path to the journal used for persisting trie data across node restarts
Preimages bool // Whether to store preimage of trie key to the disk
StateScheme string // Scheme used to store ethereum states and merkle tree nodes on top
ArchiveMode bool // Whether to enable the archive mode
Preimages bool // Whether to store preimage of trie key to the disk
StateScheme string // Scheme used to store ethereum states and merkle tree nodes on top
ArchiveMode bool // Whether to enable the archive mode
BinTrieGroupDepth int // Number of levels per serialized group in binary trie (1-8)
// Number of blocks from the chain head for which state histories are retained.
// If set to 0, all state histories across the entire chain will be retained;
@ -258,10 +259,11 @@ func (cfg BlockChainConfig) WithNoAsyncFlush(on bool) *BlockChainConfig {
}
// triedbConfig derives the configures for trie database.
func (cfg *BlockChainConfig) triedbConfig(isUBT bool) *triedb.Config {
func (cfg *BlockChainConfig) triedbConfig(isVerkle bool) *triedb.Config {
config := &triedb.Config{
Preimages: cfg.Preimages,
IsUBT: isUBT,
Preimages: cfg.Preimages,
IsVerkle: isVerkle,
BinTrieGroupDepth: cfg.BinTrieGroupDepth,
}
if cfg.StateScheme == rawdb.HashScheme {
config.HashDB = &hashdb.Config{

View file

@ -237,6 +237,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
StateHistory: config.StateHistory,
TrienodeHistory: config.TrienodeHistory,
NodeFullValueCheckpoint: config.NodeFullValueCheckpoint,
BinTrieGroupDepth: config.BinTrieGroupDepth,
StateScheme: scheme,
HistoryPolicy: histPolicy,
TxLookupLimit: int64(min(config.TransactionHistory, math.MaxInt64)),

View file

@ -59,6 +59,7 @@ var Defaults = Config{
StateHistory: pathdb.Defaults.StateHistory,
TrienodeHistory: pathdb.Defaults.TrienodeHistory,
NodeFullValueCheckpoint: pathdb.Defaults.FullValueCheckpoint,
BinTrieGroupDepth: 8, // byte-aligned groups by default
DatabaseCache: 2048,
TrieCleanCache: 614,
TrieDirtyCache: 1024,
@ -125,6 +126,11 @@ type Config struct {
// consistent with persistent state.
StateScheme string `toml:",omitempty"`
// BinTrieGroupDepth is the number of levels per serialized group in binary trie.
// Valid values are 1-8, with 8 being the default (byte-aligned groups).
// Lower values create smaller groups with more nodes.
BinTrieGroupDepth int `toml:",omitempty"`
// RequiredBlocks is a set of block number -> hash mappings which must be in the
// canonical chain of all remote peers. Setting the option makes geth verify the
// presence of these blocks for every new peer connection.

View file

@ -34,6 +34,7 @@ func (c Config) MarshalTOML() (interface{}, error) {
TrienodeHistory int64 `toml:",omitempty"`
NodeFullValueCheckpoint uint32 `toml:",omitempty"`
StateScheme string `toml:",omitempty"`
BinTrieGroupDepth int `toml:",omitempty"`
RequiredBlocks map[uint64]common.Hash `toml:"-"`
SlowBlockThreshold time.Duration `toml:",omitempty"`
SkipBcVersionCheck bool `toml:"-"`
@ -87,6 +88,7 @@ func (c Config) MarshalTOML() (interface{}, error) {
enc.TrienodeHistory = c.TrienodeHistory
enc.NodeFullValueCheckpoint = c.NodeFullValueCheckpoint
enc.StateScheme = c.StateScheme
enc.BinTrieGroupDepth = c.BinTrieGroupDepth
enc.RequiredBlocks = c.RequiredBlocks
enc.SlowBlockThreshold = c.SlowBlockThreshold
enc.SkipBcVersionCheck = c.SkipBcVersionCheck
@ -144,6 +146,7 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error {
TrienodeHistory *int64 `toml:",omitempty"`
NodeFullValueCheckpoint *uint32 `toml:",omitempty"`
StateScheme *string `toml:",omitempty"`
BinTrieGroupDepth *int `toml:",omitempty"`
RequiredBlocks map[uint64]common.Hash `toml:"-"`
SlowBlockThreshold *time.Duration `toml:",omitempty"`
SkipBcVersionCheck *bool `toml:"-"`
@ -234,6 +237,9 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error {
if dec.StateScheme != nil {
c.StateScheme = *dec.StateScheme
}
if dec.BinTrieGroupDepth != nil {
c.BinTrieGroupDepth = *dec.BinTrieGroupDepth
}
if dec.RequiredBlocks != nil {
c.RequiredBlocks = dec.RequiredBlocks
}

View file

@ -27,6 +27,8 @@ const (
NodeTypeBytes = 1 // Size of node type prefix in serialization
HashSize = 32 // Size of a hash in bytes
StemBitmapSize = 32 // Size of the bitmap in a stem node (256 values = 32 bytes)
MaxGroupDepth = 8
)
const (

View file

@ -107,9 +107,10 @@ func ChunkifyCode(code []byte) ChunkedCode {
// BinaryTrie is the implementation of https://eips.ethereum.org/EIPS/eip-7864.
type BinaryTrie struct {
store *nodeStore
reader *trie.Reader
tracer *trie.PrevalueTracer
root BinaryNode
reader *trie.Reader
tracer *trie.PrevalueTracer
groupDepth int // Number of levels per serialized group (1-8, default 8)
}
// ToDot converts the binary trie to a DOT language representation. Useful for debugging.
@ -119,15 +120,20 @@ func (t *BinaryTrie) ToDot() string {
}
// NewBinaryTrie creates a new binary trie.
func NewBinaryTrie(root common.Hash, db database.NodeDatabase) (*BinaryTrie, error) {
// groupDepth specifies the number of levels per serialized group (1-8).
func NewBinaryTrie(root common.Hash, db database.NodeDatabase, groupDepth int) (*BinaryTrie, error) {
if groupDepth < 1 || groupDepth > MaxGroupDepth {
groupDepth = MaxGroupDepth // Default to 8
}
reader, err := trie.NewReader(root, common.Hash{}, db)
if err != nil {
return nil, err
}
t := &BinaryTrie{
store: newNodeStore(),
reader: reader,
tracer: trie.NewPrevalueTracer(),
root: NewBinaryNode(),
reader: reader,
tracer: trie.NewPrevalueTracer(),
groupDepth: groupDepth,
}
// Parse the root node if it's not empty
if root != types.EmptyBinaryHash && root != types.EmptyRootHash {
@ -341,9 +347,10 @@ func (t *BinaryTrie) Prove(key []byte, proofDb ethdb.KeyValueWriter) error {
// Copy creates a deep copy of the trie.
func (t *BinaryTrie) Copy() *BinaryTrie {
return &BinaryTrie{
store: t.store.Copy(),
reader: t.reader,
tracer: t.tracer.Copy(),
root: t.root.Copy(),
reader: t.reader,
tracer: t.tracer.Copy(),
groupDepth: t.groupDepth,
}
}

View file

@ -31,10 +31,11 @@ import (
// Config defines all necessary options for database.
type Config struct {
Preimages bool // Flag whether the preimage of node key is recorded
IsUBT bool // Flag whether the db is holding a verkle tree
HashDB *hashdb.Config // Configs for hash-based scheme
PathDB *pathdb.Config // Configs for experimental path-based scheme
Preimages bool // Flag whether the preimage of node key is recorded
IsVerkle bool // Flag whether the db is holding a verkle tree
BinTrieGroupDepth int // Number of levels per serialized group in binary trie (1-8, default 8)
HashDB *hashdb.Config // Configs for hash-based scheme
PathDB *pathdb.Config // Configs for experimental path-based scheme
}
// HashDefaults represents a config for using hash-based scheme with
@ -48,9 +49,10 @@ var HashDefaults = &Config{
// UBTDefaults represents a config for holding verkle trie data
// using path-based scheme with default settings.
var UBTDefaults = &Config{
Preimages: false,
IsUBT: true,
PathDB: pathdb.Defaults,
Preimages: false,
IsUBT: true,
BinTrieGroupDepth: 8, // Default to byte-aligned groups
PathDB: pathdb.Defaults,
}
// backend defines the methods needed to access/update trie nodes in different