mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-18 02:40:45 +00:00
parameterize the page size
This commit is contained in:
parent
3b154187ed
commit
353982a2c1
22 changed files with 220 additions and 100 deletions
|
|
@ -455,7 +455,7 @@ func BinKeys(ctx *cli.Context) error {
|
||||||
db := triedb.NewDatabase(rawdb.NewMemoryDatabase(), triedb.VerkleDefaults)
|
db := triedb.NewDatabase(rawdb.NewMemoryDatabase(), triedb.VerkleDefaults)
|
||||||
defer db.Close()
|
defer db.Close()
|
||||||
|
|
||||||
bt, err := genBinTrieFromAlloc(alloc, db)
|
bt, err := genBinTrieFromAlloc(alloc, db, db.BinTrieGroupDepth())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error generating bt: %w", err)
|
return fmt.Errorf("error generating bt: %w", err)
|
||||||
}
|
}
|
||||||
|
|
@ -499,7 +499,7 @@ func BinTrieRoot(ctx *cli.Context) error {
|
||||||
db := triedb.NewDatabase(rawdb.NewMemoryDatabase(), triedb.VerkleDefaults)
|
db := triedb.NewDatabase(rawdb.NewMemoryDatabase(), triedb.VerkleDefaults)
|
||||||
defer db.Close()
|
defer db.Close()
|
||||||
|
|
||||||
bt, err := genBinTrieFromAlloc(alloc, db)
|
bt, err := genBinTrieFromAlloc(alloc, db, db.BinTrieGroupDepth())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error generating bt: %w", err)
|
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?
|
// TODO(@CPerezz): Should this go to `bintrie` module?
|
||||||
func genBinTrieFromAlloc(alloc core.GenesisAlloc, db database.NodeDatabase) (*bintrie.BinaryTrie, error) {
|
func genBinTrieFromAlloc(alloc core.GenesisAlloc, db database.NodeDatabase, groupDepth int) (*bintrie.BinaryTrie, error) {
|
||||||
bt, err := bintrie.NewBinaryTrie(types.EmptyBinaryHash, db)
|
bt, err := bintrie.NewBinaryTrie(types.EmptyBinaryHash, db, groupDepth)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -293,6 +293,12 @@ var (
|
||||||
Value: ethconfig.Defaults.EnableStateSizeTracking,
|
Value: ethconfig.Defaults.EnableStateSizeTracking,
|
||||||
Category: flags.StateCategory,
|
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{
|
StateHistoryFlag = &cli.Uint64Flag{
|
||||||
Name: "history.state",
|
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)",
|
Usage: "Number of recent blocks to retain state history for, only relevant in state.scheme=path (default = 90,000 blocks, 0 = entire chain)",
|
||||||
|
|
@ -1808,9 +1814,6 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
|
||||||
if ctx.IsSet(TrienodeHistoryFlag.Name) {
|
if ctx.IsSet(TrienodeHistoryFlag.Name) {
|
||||||
cfg.TrienodeHistory = ctx.Int64(TrienodeHistoryFlag.Name)
|
cfg.TrienodeHistory = ctx.Int64(TrienodeHistoryFlag.Name)
|
||||||
}
|
}
|
||||||
if ctx.IsSet(TrienodeHistoryFullValueCheckpointFlag.Name) {
|
|
||||||
cfg.NodeFullValueCheckpoint = uint32(ctx.Uint(TrienodeHistoryFullValueCheckpointFlag.Name))
|
|
||||||
}
|
|
||||||
if ctx.IsSet(StateSchemeFlag.Name) {
|
if ctx.IsSet(StateSchemeFlag.Name) {
|
||||||
cfg.StateScheme = ctx.String(StateSchemeFlag.Name)
|
cfg.StateScheme = ctx.String(StateSchemeFlag.Name)
|
||||||
}
|
}
|
||||||
|
|
@ -2427,6 +2430,7 @@ func MakeChain(ctx *cli.Context, stack *node.Node, readonly bool) (*core.BlockCh
|
||||||
StateHistory: ctx.Uint64(StateHistoryFlag.Name),
|
StateHistory: ctx.Uint64(StateHistoryFlag.Name),
|
||||||
TrienodeHistory: ctx.Int64(TrienodeHistoryFlag.Name),
|
TrienodeHistory: ctx.Int64(TrienodeHistoryFlag.Name),
|
||||||
NodeFullValueCheckpoint: uint32(ctx.Uint(TrienodeHistoryFullValueCheckpointFlag.Name)),
|
NodeFullValueCheckpoint: uint32(ctx.Uint(TrienodeHistoryFullValueCheckpointFlag.Name)),
|
||||||
|
BinTrieGroupDepth: ctx.Int(BinTrieGroupDepthFlag.Name),
|
||||||
|
|
||||||
// Disable transaction indexing/unindexing.
|
// Disable transaction indexing/unindexing.
|
||||||
TxLookupLimit: -1,
|
TxLookupLimit: -1,
|
||||||
|
|
|
||||||
|
|
@ -170,9 +170,10 @@ type BlockChainConfig struct {
|
||||||
TrieNoAsyncFlush bool // Whether the asynchronous buffer flushing is disallowed
|
TrieNoAsyncFlush bool // Whether the asynchronous buffer flushing is disallowed
|
||||||
TrieJournalDirectory string // Directory path to the journal used for persisting trie data across node restarts
|
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
|
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
|
StateScheme string // Scheme used to store ethereum states and merkle tree nodes on top
|
||||||
ArchiveMode bool // Whether to enable the archive mode
|
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.
|
// 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;
|
// If set to 0, all state histories across the entire chain will be retained;
|
||||||
|
|
@ -261,8 +262,9 @@ func (cfg BlockChainConfig) WithNoAsyncFlush(on bool) *BlockChainConfig {
|
||||||
// triedbConfig derives the configures for trie database.
|
// triedbConfig derives the configures for trie database.
|
||||||
func (cfg *BlockChainConfig) triedbConfig(isVerkle bool) *triedb.Config {
|
func (cfg *BlockChainConfig) triedbConfig(isVerkle bool) *triedb.Config {
|
||||||
config := &triedb.Config{
|
config := &triedb.Config{
|
||||||
Preimages: cfg.Preimages,
|
Preimages: cfg.Preimages,
|
||||||
IsVerkle: isVerkle,
|
IsVerkle: isVerkle,
|
||||||
|
BinTrieGroupDepth: cfg.BinTrieGroupDepth,
|
||||||
}
|
}
|
||||||
if cfg.StateScheme == rawdb.HashScheme {
|
if cfg.StateScheme == rawdb.HashScheme {
|
||||||
config.HashDB = &hashdb.Config{
|
config.HashDB = &hashdb.Config{
|
||||||
|
|
|
||||||
|
|
@ -244,7 +244,7 @@ func (db *CachingDB) OpenTrie(root common.Hash) (Trie, error) {
|
||||||
if ts.Transitioned() {
|
if ts.Transitioned() {
|
||||||
// Use BinaryTrie instead of VerkleTrie when IsVerkle is set
|
// Use BinaryTrie instead of VerkleTrie when IsVerkle is set
|
||||||
// (IsVerkle actually means Binary Trie mode in this codebase)
|
// (IsVerkle actually means Binary Trie mode in this codebase)
|
||||||
return bintrie.NewBinaryTrie(root, db.triedb)
|
return bintrie.NewBinaryTrie(root, db.triedb, db.triedb.BinTrieGroupDepth())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tr, err := trie.NewStateTrie(trie.StateTrieID(root), db.triedb)
|
tr, err := trie.NewStateTrie(trie.StateTrieID(root), db.triedb)
|
||||||
|
|
|
||||||
|
|
@ -176,7 +176,7 @@ func newTrieReader(root common.Hash, db *triedb.Database) (*trieReader, error) {
|
||||||
tr, err = trie.NewStateTrie(trie.StateTrieID(root), db)
|
tr, err = trie.NewStateTrie(trie.StateTrieID(root), db)
|
||||||
} else {
|
} else {
|
||||||
// When IsVerkle() is true, create a BinaryTrie wrapped in TransitionTrie
|
// When IsVerkle() is true, create a BinaryTrie wrapped in TransitionTrie
|
||||||
binTrie, binErr := bintrie.NewBinaryTrie(root, db)
|
binTrie, binErr := bintrie.NewBinaryTrie(root, db, db.BinTrieGroupDepth())
|
||||||
if binErr != nil {
|
if binErr != nil {
|
||||||
return nil, binErr
|
return nil, binErr
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -232,6 +232,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
|
||||||
StateHistory: config.StateHistory,
|
StateHistory: config.StateHistory,
|
||||||
TrienodeHistory: config.TrienodeHistory,
|
TrienodeHistory: config.TrienodeHistory,
|
||||||
NodeFullValueCheckpoint: config.NodeFullValueCheckpoint,
|
NodeFullValueCheckpoint: config.NodeFullValueCheckpoint,
|
||||||
|
BinTrieGroupDepth: config.BinTrieGroupDepth,
|
||||||
StateScheme: scheme,
|
StateScheme: scheme,
|
||||||
ChainHistoryMode: config.HistoryMode,
|
ChainHistoryMode: config.HistoryMode,
|
||||||
TxLookupLimit: int64(min(config.TransactionHistory, math.MaxInt64)),
|
TxLookupLimit: int64(min(config.TransactionHistory, math.MaxInt64)),
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,7 @@ var Defaults = Config{
|
||||||
StateHistory: pathdb.Defaults.StateHistory,
|
StateHistory: pathdb.Defaults.StateHistory,
|
||||||
TrienodeHistory: pathdb.Defaults.TrienodeHistory,
|
TrienodeHistory: pathdb.Defaults.TrienodeHistory,
|
||||||
NodeFullValueCheckpoint: pathdb.Defaults.FullValueCheckpoint,
|
NodeFullValueCheckpoint: pathdb.Defaults.FullValueCheckpoint,
|
||||||
|
BinTrieGroupDepth: 8, // byte-aligned groups by default
|
||||||
DatabaseCache: 2048,
|
DatabaseCache: 2048,
|
||||||
TrieCleanCache: 614,
|
TrieCleanCache: 614,
|
||||||
TrieDirtyCache: 1024,
|
TrieDirtyCache: 1024,
|
||||||
|
|
@ -125,6 +126,11 @@ type Config struct {
|
||||||
// consistent with persistent state.
|
// consistent with persistent state.
|
||||||
StateScheme string `toml:",omitempty"`
|
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
|
// 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
|
// canonical chain of all remote peers. Setting the option makes geth verify the
|
||||||
// presence of these blocks for every new peer connection.
|
// presence of these blocks for every new peer connection.
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ func (c Config) MarshalTOML() (interface{}, error) {
|
||||||
TrienodeHistory int64 `toml:",omitempty"`
|
TrienodeHistory int64 `toml:",omitempty"`
|
||||||
NodeFullValueCheckpoint uint32 `toml:",omitempty"`
|
NodeFullValueCheckpoint uint32 `toml:",omitempty"`
|
||||||
StateScheme string `toml:",omitempty"`
|
StateScheme string `toml:",omitempty"`
|
||||||
|
BinTrieGroupDepth int `toml:",omitempty"`
|
||||||
RequiredBlocks map[uint64]common.Hash `toml:"-"`
|
RequiredBlocks map[uint64]common.Hash `toml:"-"`
|
||||||
SlowBlockThreshold time.Duration `toml:",omitempty"`
|
SlowBlockThreshold time.Duration `toml:",omitempty"`
|
||||||
SkipBcVersionCheck bool `toml:"-"`
|
SkipBcVersionCheck bool `toml:"-"`
|
||||||
|
|
@ -87,6 +88,7 @@ func (c Config) MarshalTOML() (interface{}, error) {
|
||||||
enc.TrienodeHistory = c.TrienodeHistory
|
enc.TrienodeHistory = c.TrienodeHistory
|
||||||
enc.NodeFullValueCheckpoint = c.NodeFullValueCheckpoint
|
enc.NodeFullValueCheckpoint = c.NodeFullValueCheckpoint
|
||||||
enc.StateScheme = c.StateScheme
|
enc.StateScheme = c.StateScheme
|
||||||
|
enc.BinTrieGroupDepth = c.BinTrieGroupDepth
|
||||||
enc.RequiredBlocks = c.RequiredBlocks
|
enc.RequiredBlocks = c.RequiredBlocks
|
||||||
enc.SlowBlockThreshold = c.SlowBlockThreshold
|
enc.SlowBlockThreshold = c.SlowBlockThreshold
|
||||||
enc.SkipBcVersionCheck = c.SkipBcVersionCheck
|
enc.SkipBcVersionCheck = c.SkipBcVersionCheck
|
||||||
|
|
@ -144,6 +146,7 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error {
|
||||||
TrienodeHistory *int64 `toml:",omitempty"`
|
TrienodeHistory *int64 `toml:",omitempty"`
|
||||||
NodeFullValueCheckpoint *uint32 `toml:",omitempty"`
|
NodeFullValueCheckpoint *uint32 `toml:",omitempty"`
|
||||||
StateScheme *string `toml:",omitempty"`
|
StateScheme *string `toml:",omitempty"`
|
||||||
|
BinTrieGroupDepth *int `toml:",omitempty"`
|
||||||
RequiredBlocks map[uint64]common.Hash `toml:"-"`
|
RequiredBlocks map[uint64]common.Hash `toml:"-"`
|
||||||
SlowBlockThreshold *time.Duration `toml:",omitempty"`
|
SlowBlockThreshold *time.Duration `toml:",omitempty"`
|
||||||
SkipBcVersionCheck *bool `toml:"-"`
|
SkipBcVersionCheck *bool `toml:"-"`
|
||||||
|
|
@ -234,6 +237,9 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error {
|
||||||
if dec.StateScheme != nil {
|
if dec.StateScheme != nil {
|
||||||
c.StateScheme = *dec.StateScheme
|
c.StateScheme = *dec.StateScheme
|
||||||
}
|
}
|
||||||
|
if dec.BinTrieGroupDepth != nil {
|
||||||
|
c.BinTrieGroupDepth = *dec.BinTrieGroupDepth
|
||||||
|
}
|
||||||
if dec.RequiredBlocks != nil {
|
if dec.RequiredBlocks != nil {
|
||||||
c.RequiredBlocks = dec.RequiredBlocks
|
c.RequiredBlocks = dec.RequiredBlocks
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,21 +31,30 @@ type (
|
||||||
var zero [32]byte
|
var zero [32]byte
|
||||||
|
|
||||||
const (
|
const (
|
||||||
StemNodeWidth = 256 // Number of child per leaf node
|
StemNodeWidth = 256 // Number of child per leaf node
|
||||||
StemSize = 31 // Number of bytes to travel before reaching a group of leaves
|
StemSize = 31 // Number of bytes to travel before reaching a group of leaves
|
||||||
NodeTypeBytes = 1 // Size of node type prefix in serialization
|
NodeTypeBytes = 1 // Size of node type prefix in serialization
|
||||||
HashSize = 32 // Size of a hash in bytes
|
HashSize = 32 // Size of a hash in bytes
|
||||||
BitmapSize = 32 // Size of the bitmap in a stem node
|
StemBitmapSize = 32 // Size of the bitmap in a stem node (256 values = 32 bytes)
|
||||||
|
|
||||||
// GroupDepth is the number of levels in a grouped subtree serialization.
|
// MaxGroupDepth is the maximum allowed group depth for InternalNode serialization.
|
||||||
// Groups are byte-aligned (depth % 8 == 0). This may become configurable later.
|
// Valid group depths are 1-8, where depth N means 2^N bottom-layer positions.
|
||||||
// Serialization format for InternalNode groups:
|
// Serialization format for InternalNode groups:
|
||||||
// [1 byte type] [1 byte group depth (1-8)] [32 byte bitmap] [N × 32 byte hashes]
|
// [1 byte type] [1 byte group depth (1-8)] [variable bitmap] [N × 32 byte hashes]
|
||||||
// The bitmap has 2^groupDepth bits, indicating which bottom-layer children are present.
|
// The bitmap has 2^groupDepth bits (BitmapSizeForDepth bytes), indicating which
|
||||||
// Only present children's hashes are stored, in order.
|
// bottom-layer children are present. Only present children's hashes are stored.
|
||||||
GroupDepth = 8
|
MaxGroupDepth = 8
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// BitmapSizeForDepth returns the bitmap size in bytes for a given group depth.
|
||||||
|
// For depths 1-3, returns 1 byte. For depths 4-8, returns 2^(depth-3) bytes.
|
||||||
|
func BitmapSizeForDepth(groupDepth int) int {
|
||||||
|
if groupDepth <= 3 {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return 1 << (groupDepth - 3)
|
||||||
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
nodeTypeStem = iota + 1 // Stem node, contains a stem and a bitmap of values
|
nodeTypeStem = iota + 1 // Stem node, contains a stem and a bitmap of values
|
||||||
nodeTypeInternal
|
nodeTypeInternal
|
||||||
|
|
@ -59,7 +68,7 @@ type BinaryNode interface {
|
||||||
Hash() common.Hash
|
Hash() common.Hash
|
||||||
GetValuesAtStem([]byte, NodeResolverFn) ([][]byte, error)
|
GetValuesAtStem([]byte, NodeResolverFn) ([][]byte, error)
|
||||||
InsertValuesAtStem([]byte, [][]byte, NodeResolverFn, int) (BinaryNode, error)
|
InsertValuesAtStem([]byte, [][]byte, NodeResolverFn, int) (BinaryNode, error)
|
||||||
CollectNodes([]byte, NodeFlushFn) error
|
CollectNodes([]byte, NodeFlushFn, int) error // groupDepth parameter for serialization
|
||||||
|
|
||||||
toDot(parent, path string) string
|
toDot(parent, path string) string
|
||||||
GetHeight() int
|
GetHeight() int
|
||||||
|
|
@ -106,25 +115,28 @@ func serializeSubtree(node BinaryNode, remainingDepth int, position int, bitmap
|
||||||
}
|
}
|
||||||
|
|
||||||
// SerializeNode serializes a binary trie node into a byte slice.
|
// SerializeNode serializes a binary trie node into a byte slice.
|
||||||
func SerializeNode(node BinaryNode) []byte {
|
// groupDepth specifies how many levels to include in an InternalNode group (1-8).
|
||||||
|
func SerializeNode(node BinaryNode, groupDepth int) []byte {
|
||||||
|
if groupDepth < 1 || groupDepth > MaxGroupDepth {
|
||||||
|
panic("groupDepth must be between 1 and 8")
|
||||||
|
}
|
||||||
switch n := (node).(type) {
|
switch n := (node).(type) {
|
||||||
case *InternalNode:
|
case *InternalNode:
|
||||||
// InternalNode group: 1 byte type + 1 byte group depth + 32 byte bitmap + N×32 byte hashes
|
// InternalNode group: 1 byte type + 1 byte group depth + variable bitmap + N×32 byte hashes
|
||||||
groupDepth := GroupDepth
|
bitmapSize := BitmapSizeForDepth(groupDepth)
|
||||||
|
bitmap := make([]byte, bitmapSize)
|
||||||
var bitmap [BitmapSize]byte
|
|
||||||
var hashes []common.Hash
|
var hashes []common.Hash
|
||||||
|
|
||||||
serializeSubtree(n, groupDepth, 0, bitmap[:], &hashes)
|
serializeSubtree(n, groupDepth, 0, bitmap, &hashes)
|
||||||
|
|
||||||
// Build serialized output
|
// Build serialized output
|
||||||
serializedLen := NodeTypeBytes + 1 + BitmapSize + len(hashes)*HashSize
|
serializedLen := NodeTypeBytes + 1 + bitmapSize + len(hashes)*HashSize
|
||||||
serialized := make([]byte, serializedLen)
|
serialized := make([]byte, serializedLen)
|
||||||
serialized[0] = nodeTypeInternal
|
serialized[0] = nodeTypeInternal
|
||||||
serialized[1] = byte(groupDepth)
|
serialized[1] = byte(groupDepth)
|
||||||
copy(serialized[2:2+BitmapSize], bitmap[:])
|
copy(serialized[2:2+bitmapSize], bitmap)
|
||||||
|
|
||||||
offset := NodeTypeBytes + 1 + BitmapSize
|
offset := NodeTypeBytes + 1 + bitmapSize
|
||||||
for _, h := range hashes {
|
for _, h := range hashes {
|
||||||
copy(serialized[offset:offset+HashSize], h.Bytes())
|
copy(serialized[offset:offset+HashSize], h.Bytes())
|
||||||
offset += HashSize
|
offset += HashSize
|
||||||
|
|
@ -223,16 +235,20 @@ func deserializeNode(serialized []byte, depth int, hn common.Hash, mustRecompute
|
||||||
|
|
||||||
switch serialized[0] {
|
switch serialized[0] {
|
||||||
case nodeTypeInternal:
|
case nodeTypeInternal:
|
||||||
// Grouped format: 1 byte type + 1 byte group depth + 32 byte bitmap + N×32 byte hashes
|
// Grouped format: 1 byte type + 1 byte group depth + variable bitmap + N×32 byte hashes
|
||||||
if len(serialized) < NodeTypeBytes+1+BitmapSize {
|
if len(serialized) < NodeTypeBytes+1 {
|
||||||
return nil, invalidSerializedLength
|
return nil, invalidSerializedLength
|
||||||
}
|
}
|
||||||
groupDepth := int(serialized[1])
|
groupDepth := int(serialized[1])
|
||||||
if groupDepth < 1 || groupDepth > GroupDepth {
|
if groupDepth < 1 || groupDepth > MaxGroupDepth {
|
||||||
return nil, errors.New("invalid group depth")
|
return nil, errors.New("invalid group depth")
|
||||||
}
|
}
|
||||||
bitmap := serialized[2 : 2+BitmapSize]
|
bitmapSize := BitmapSizeForDepth(groupDepth)
|
||||||
hashData := serialized[2+BitmapSize:]
|
if len(serialized) < NodeTypeBytes+1+bitmapSize {
|
||||||
|
return nil, invalidSerializedLength
|
||||||
|
}
|
||||||
|
bitmap := serialized[2 : 2+bitmapSize]
|
||||||
|
hashData := serialized[2+bitmapSize:]
|
||||||
|
|
||||||
// Count present children from bitmap
|
// Count present children from bitmap
|
||||||
hashIdx := 0
|
hashIdx := 0
|
||||||
|
|
|
||||||
|
|
@ -37,27 +37,28 @@ func TestSerializeDeserializeInternalNode(t *testing.T) {
|
||||||
right: HashedNode(rightHash),
|
right: HashedNode(rightHash),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Serialize the node
|
// Serialize the node with default group depth of 8
|
||||||
serialized := SerializeNode(node)
|
serialized := SerializeNode(node, MaxGroupDepth)
|
||||||
|
|
||||||
// Check the serialized format: type byte + group depth byte + 32 byte bitmap + N*32 byte hashes
|
// Check the serialized format: type byte + group depth byte + 32 byte bitmap + N*32 byte hashes
|
||||||
if serialized[0] != nodeTypeInternal {
|
if serialized[0] != nodeTypeInternal {
|
||||||
t.Errorf("Expected type byte to be %d, got %d", nodeTypeInternal, serialized[0])
|
t.Errorf("Expected type byte to be %d, got %d", nodeTypeInternal, serialized[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
if serialized[1] != GroupDepth {
|
if serialized[1] != MaxGroupDepth {
|
||||||
t.Errorf("Expected group depth to be %d, got %d", GroupDepth, serialized[1])
|
t.Errorf("Expected group depth to be %d, got %d", MaxGroupDepth, serialized[1])
|
||||||
}
|
}
|
||||||
|
|
||||||
// Expected length: 1 (type) + 1 (group depth) + 32 (bitmap) + 2*32 (two hashes) = 98 bytes
|
// Expected length: 1 (type) + 1 (group depth) + 32 (bitmap) + 2*32 (two hashes) = 98 bytes
|
||||||
expectedLen := NodeTypeBytes + 1 + BitmapSize + 2*HashSize
|
bitmapSize := BitmapSizeForDepth(MaxGroupDepth)
|
||||||
|
expectedLen := NodeTypeBytes + 1 + bitmapSize + 2*HashSize
|
||||||
if len(serialized) != expectedLen {
|
if len(serialized) != expectedLen {
|
||||||
t.Errorf("Expected serialized length to be %d, got %d", expectedLen, len(serialized))
|
t.Errorf("Expected serialized length to be %d, got %d", expectedLen, len(serialized))
|
||||||
}
|
}
|
||||||
|
|
||||||
// The left child (HashedNode) terminates at remainingDepth=7, so it's placed at position 0<<7 = 0
|
// The left child (HashedNode) terminates at remainingDepth=7, so it's placed at position 0<<7 = 0
|
||||||
// The right child (HashedNode) terminates at remainingDepth=7, so it's placed at position 1<<7 = 128
|
// The right child (HashedNode) terminates at remainingDepth=7, so it's placed at position 1<<7 = 128
|
||||||
bitmap := serialized[2 : 2+BitmapSize]
|
bitmap := serialized[2 : 2+bitmapSize]
|
||||||
if bitmap[0]&0x80 == 0 { // bit 0 (MSB of byte 0)
|
if bitmap[0]&0x80 == 0 { // bit 0 (MSB of byte 0)
|
||||||
t.Error("Expected bit 0 to be set in bitmap (left child)")
|
t.Error("Expected bit 0 to be set in bitmap (left child)")
|
||||||
}
|
}
|
||||||
|
|
@ -135,8 +136,8 @@ func TestSerializeDeserializeStemNode(t *testing.T) {
|
||||||
depth: 10,
|
depth: 10,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Serialize the node
|
// Serialize the node (groupDepth doesn't affect StemNode serialization)
|
||||||
serialized := SerializeNode(node)
|
serialized := SerializeNode(node, MaxGroupDepth)
|
||||||
|
|
||||||
// Check the serialized format
|
// Check the serialized format
|
||||||
if serialized[0] != nodeTypeStem {
|
if serialized[0] != nodeTypeStem {
|
||||||
|
|
@ -214,8 +215,8 @@ func TestDeserializeInvalidType(t *testing.T) {
|
||||||
|
|
||||||
// TestDeserializeInvalidLength tests deserialization with invalid data length
|
// TestDeserializeInvalidLength tests deserialization with invalid data length
|
||||||
func TestDeserializeInvalidLength(t *testing.T) {
|
func TestDeserializeInvalidLength(t *testing.T) {
|
||||||
// InternalNode with type byte 1 but wrong length
|
// InternalNode with valid type byte and group depth but too short for bitmap
|
||||||
invalidData := []byte{nodeTypeInternal, 0, 0} // Too short for internal node
|
invalidData := []byte{nodeTypeInternal, 8, 0, 0} // Too short for bitmap (needs 32 bytes)
|
||||||
|
|
||||||
_, err := DeserializeNode(invalidData, 0)
|
_, err := DeserializeNode(invalidData, 0)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,7 @@ func (e Empty) InsertValuesAtStem(key []byte, values [][]byte, _ NodeResolverFn,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e Empty) CollectNodes(_ []byte, _ NodeFlushFn) error {
|
func (e Empty) CollectNodes(_ []byte, _ NodeFlushFn, _ int) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -186,7 +186,7 @@ func TestEmptyCollectNodes(t *testing.T) {
|
||||||
collected = append(collected, n)
|
collected = append(collected, n)
|
||||||
}
|
}
|
||||||
|
|
||||||
err := node.CollectNodes([]byte{0, 1, 0}, flushFn)
|
err := node.CollectNodes([]byte{0, 1, 0}, flushFn, MaxGroupDepth)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Unexpected error: %v", err)
|
t.Fatalf("Unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,11 +18,12 @@ func TestGroupedSerializationDebug(t *testing.T) {
|
||||||
right: HashedNode(rightHash),
|
right: HashedNode(rightHash),
|
||||||
}
|
}
|
||||||
|
|
||||||
serialized := SerializeNode(node)
|
serialized := SerializeNode(node, MaxGroupDepth)
|
||||||
t.Logf("Serialized length: %d", len(serialized))
|
t.Logf("Serialized length: %d", len(serialized))
|
||||||
t.Logf("Type: %d, GroupDepth: %d", serialized[0], serialized[1])
|
t.Logf("Type: %d, GroupDepth: %d", serialized[0], serialized[1])
|
||||||
|
|
||||||
bitmap := serialized[2 : 2+BitmapSize]
|
bitmapSize := BitmapSizeForDepth(MaxGroupDepth)
|
||||||
|
bitmap := serialized[2 : 2+bitmapSize]
|
||||||
t.Logf("Bitmap: %x", bitmap)
|
t.Logf("Bitmap: %x", bitmap)
|
||||||
|
|
||||||
// Count and show set bits
|
// Count and show set bits
|
||||||
|
|
@ -69,12 +70,13 @@ func TestFullDepth8Tree(t *testing.T) {
|
||||||
// Build a full 8-level tree
|
// Build a full 8-level tree
|
||||||
root := buildFullTree(0, 8)
|
root := buildFullTree(0, 8)
|
||||||
|
|
||||||
serialized := SerializeNode(root)
|
serialized := SerializeNode(root, MaxGroupDepth)
|
||||||
t.Logf("Full tree serialized length: %d", len(serialized))
|
t.Logf("Full tree serialized length: %d", len(serialized))
|
||||||
t.Logf("Expected: 1 + 1 + 32 + 256*32 = %d", 1+1+32+256*32)
|
t.Logf("Expected: 1 + 1 + 32 + 256*32 = %d", 1+1+32+256*32)
|
||||||
|
|
||||||
// Count set bits in bitmap
|
// Count set bits in bitmap
|
||||||
bitmap := serialized[2 : 2+BitmapSize]
|
bitmapSize := BitmapSizeForDepth(MaxGroupDepth)
|
||||||
|
bitmap := serialized[2 : 2+bitmapSize]
|
||||||
count := 0
|
count := 0
|
||||||
for i := 0; i < 256; i++ {
|
for i := 0; i < 256; i++ {
|
||||||
if bitmap[i/8]>>(7-(i%8))&1 == 1 {
|
if bitmap[i/8]>>(7-(i%8))&1 == 1 {
|
||||||
|
|
@ -146,7 +148,7 @@ func TestRoundTripPreservesHashes(t *testing.T) {
|
||||||
|
|
||||||
root := buildTreeWithHashes(0, 8, 0, hashes)
|
root := buildTreeWithHashes(0, 8, 0, hashes)
|
||||||
|
|
||||||
serialized := SerializeNode(root)
|
serialized := SerializeNode(root, MaxGroupDepth)
|
||||||
deserialized, err := DeserializeNode(serialized, 0)
|
deserialized, err := DeserializeNode(serialized, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Error: %v", err)
|
t.Fatalf("Error: %v", err)
|
||||||
|
|
@ -205,9 +207,9 @@ func TestCollectNodesGrouping(t *testing.T) {
|
||||||
}{pathCopy, node})
|
}{pathCopy, node})
|
||||||
|
|
||||||
// Serialize and store by hash
|
// Serialize and store by hash
|
||||||
serialized := SerializeNode(node)
|
serialized := SerializeNode(node, MaxGroupDepth)
|
||||||
serializedNodes[node.Hash()] = serialized
|
serializedNodes[node.Hash()] = serialized
|
||||||
})
|
}, MaxGroupDepth)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("CollectNodes failed: %v", err)
|
t.Fatalf("CollectNodes failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -362,3 +364,64 @@ func buildDeepTreeUnique(depth, maxDepth, position int) BinaryNode {
|
||||||
right: buildDeepTreeUnique(depth+1, maxDepth, position*2+1),
|
right: buildDeepTreeUnique(depth+1, maxDepth, position*2+1),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestVariableGroupDepth tests serialization with different group depths (1-8)
|
||||||
|
func TestVariableGroupDepth(t *testing.T) {
|
||||||
|
for groupDepth := 1; groupDepth <= MaxGroupDepth; groupDepth++ {
|
||||||
|
t.Run(fmt.Sprintf("groupDepth=%d", groupDepth), func(t *testing.T) {
|
||||||
|
// Build a tree with depth equal to groupDepth * 2 (two full groups)
|
||||||
|
treeDepth := groupDepth * 2
|
||||||
|
root := buildDeepTreeUnique(0, treeDepth, 0)
|
||||||
|
originalHash := root.Hash()
|
||||||
|
|
||||||
|
// Serialize with this group depth
|
||||||
|
serialized := SerializeNode(root, groupDepth)
|
||||||
|
|
||||||
|
// Verify header
|
||||||
|
if serialized[0] != nodeTypeInternal {
|
||||||
|
t.Errorf("Expected type byte %d, got %d", nodeTypeInternal, serialized[0])
|
||||||
|
}
|
||||||
|
if int(serialized[1]) != groupDepth {
|
||||||
|
t.Errorf("Expected group depth %d, got %d", groupDepth, serialized[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify bitmap size
|
||||||
|
expectedBitmapSize := BitmapSizeForDepth(groupDepth)
|
||||||
|
expectedMinLen := 1 + 1 + expectedBitmapSize // type + depth + bitmap
|
||||||
|
if len(serialized) < expectedMinLen {
|
||||||
|
t.Errorf("Serialized data too short: got %d, expected at least %d", len(serialized), expectedMinLen)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deserialize and verify hash matches
|
||||||
|
deserialized, err := DeserializeNode(serialized, 0)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("DeserializeNode failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if deserialized.Hash() != originalHash {
|
||||||
|
t.Errorf("Hash mismatch after round-trip: expected %x, got %x", originalHash, deserialized.Hash())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect nodes and verify correct grouping
|
||||||
|
var collectedDepths []int
|
||||||
|
err = root.CollectNodes(nil, func(path []byte, node BinaryNode) {
|
||||||
|
if in, ok := node.(*InternalNode); ok {
|
||||||
|
collectedDepths = append(collectedDepths, in.depth)
|
||||||
|
}
|
||||||
|
}, groupDepth)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("CollectNodes failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify all collected nodes are at group boundaries
|
||||||
|
for _, depth := range collectedDepths {
|
||||||
|
if depth%groupDepth != 0 {
|
||||||
|
t.Errorf("Collected node at depth %d, but groupDepth is %d (not a boundary)", depth, groupDepth)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("groupDepth=%d: serialized=%d bytes, collected=%d nodes at depths %v",
|
||||||
|
groupDepth, len(serialized), len(collectedDepths), collectedDepths)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -80,7 +80,7 @@ func (h HashedNode) toDot(parent string, path string) string {
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h HashedNode) CollectNodes([]byte, NodeFlushFn) error {
|
func (h HashedNode) CollectNodes([]byte, NodeFlushFn, int) error {
|
||||||
// HashedNodes are already persisted in the database and don't need to be collected.
|
// HashedNodes are already persisted in the database and don't need to be collected.
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -135,7 +135,7 @@ func TestHashedNodeInsertValuesAtStem(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Serialize the node
|
// Serialize the node
|
||||||
serialized := SerializeNode(originalNode)
|
serialized := SerializeNode(originalNode, MaxGroupDepth)
|
||||||
|
|
||||||
// Create a mock resolver that returns the serialized node
|
// Create a mock resolver that returns the serialized node
|
||||||
validResolver := func(path []byte, hash common.Hash) ([]byte, error) {
|
validResolver := func(path []byte, hash common.Hash) ([]byte, error) {
|
||||||
|
|
|
||||||
|
|
@ -197,15 +197,18 @@ func (bt *InternalNode) InsertValuesAtStem(stem []byte, values [][]byte, resolve
|
||||||
return bt, err
|
return bt, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// CollectNodes collects all child nodes at group boundaries (every GroupDepth levels),
|
// CollectNodes collects all child nodes at group boundaries (every groupDepth levels),
|
||||||
// and flushes them into the provided node collector. Each flush serializes an 8-level
|
// and flushes them into the provided node collector. Each flush serializes a groupDepth-level
|
||||||
// subtree group. Nodes within a group are not flushed individually.
|
// subtree group. Nodes within a group are not flushed individually.
|
||||||
func (bt *InternalNode) CollectNodes(path []byte, flushfn NodeFlushFn) error {
|
func (bt *InternalNode) CollectNodes(path []byte, flushfn NodeFlushFn, groupDepth int) error {
|
||||||
// Only flush at group boundaries (depth % GroupDepth == 0)
|
if groupDepth < 1 || groupDepth > MaxGroupDepth {
|
||||||
if bt.depth%GroupDepth == 0 {
|
return errors.New("groupDepth must be between 1 and 8")
|
||||||
|
}
|
||||||
|
// Only flush at group boundaries (depth % groupDepth == 0)
|
||||||
|
if bt.depth%groupDepth == 0 {
|
||||||
// We're at a group boundary - first collect any nodes in deeper groups,
|
// We're at a group boundary - first collect any nodes in deeper groups,
|
||||||
// then flush this group
|
// then flush this group
|
||||||
if err := bt.collectChildGroups(path, flushfn, GroupDepth-1); err != nil {
|
if err := bt.collectChildGroups(path, flushfn, groupDepth, groupDepth-1); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
flushfn(path, bt)
|
flushfn(path, bt)
|
||||||
|
|
@ -213,22 +216,22 @@ func (bt *InternalNode) CollectNodes(path []byte, flushfn NodeFlushFn) error {
|
||||||
}
|
}
|
||||||
// Not at a group boundary - this shouldn't happen if we're called correctly from root
|
// Not at a group boundary - this shouldn't happen if we're called correctly from root
|
||||||
// but handle it by continuing to traverse
|
// but handle it by continuing to traverse
|
||||||
return bt.collectChildGroups(path, flushfn, GroupDepth-(bt.depth%GroupDepth)-1)
|
return bt.collectChildGroups(path, flushfn, groupDepth, groupDepth-(bt.depth%groupDepth)-1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// collectChildGroups traverses within a group to find and collect nodes in the next group.
|
// collectChildGroups traverses within a group to find and collect nodes in the next group.
|
||||||
// remainingLevels is how many more levels below the current node until we reach the group boundary.
|
// remainingLevels is how many more levels below the current node until we reach the group boundary.
|
||||||
// When remainingLevels=0, the current node's children are at the next group boundary.
|
// When remainingLevels=0, the current node's children are at the next group boundary.
|
||||||
func (bt *InternalNode) collectChildGroups(path []byte, flushfn NodeFlushFn, remainingLevels int) error {
|
func (bt *InternalNode) collectChildGroups(path []byte, flushfn NodeFlushFn, groupDepth int, remainingLevels int) error {
|
||||||
if remainingLevels == 0 {
|
if remainingLevels == 0 {
|
||||||
// Current node is at depth (groupBoundary - 1), its children are at the next group boundary
|
// Current node is at depth (groupBoundary - 1), its children are at the next group boundary
|
||||||
if bt.left != nil {
|
if bt.left != nil {
|
||||||
if err := bt.left.CollectNodes(appendBit(path, 0), flushfn); err != nil {
|
if err := bt.left.CollectNodes(appendBit(path, 0), flushfn, groupDepth); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if bt.right != nil {
|
if bt.right != nil {
|
||||||
if err := bt.right.CollectNodes(appendBit(path, 1), flushfn); err != nil {
|
if err := bt.right.CollectNodes(appendBit(path, 1), flushfn, groupDepth); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -239,12 +242,12 @@ func (bt *InternalNode) collectChildGroups(path []byte, flushfn NodeFlushFn, rem
|
||||||
if bt.left != nil {
|
if bt.left != nil {
|
||||||
switch n := bt.left.(type) {
|
switch n := bt.left.(type) {
|
||||||
case *InternalNode:
|
case *InternalNode:
|
||||||
if err := n.collectChildGroups(appendBit(path, 0), flushfn, remainingLevels-1); err != nil {
|
if err := n.collectChildGroups(appendBit(path, 0), flushfn, groupDepth, remainingLevels-1); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
// StemNode, HashedNode, or Empty - they handle their own collection
|
// StemNode, HashedNode, or Empty - they handle their own collection
|
||||||
if err := bt.left.CollectNodes(appendBit(path, 0), flushfn); err != nil {
|
if err := bt.left.CollectNodes(appendBit(path, 0), flushfn, groupDepth); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -252,12 +255,12 @@ func (bt *InternalNode) collectChildGroups(path []byte, flushfn NodeFlushFn, rem
|
||||||
if bt.right != nil {
|
if bt.right != nil {
|
||||||
switch n := bt.right.(type) {
|
switch n := bt.right.(type) {
|
||||||
case *InternalNode:
|
case *InternalNode:
|
||||||
if err := n.collectChildGroups(appendBit(path, 1), flushfn, remainingLevels-1); err != nil {
|
if err := n.collectChildGroups(appendBit(path, 1), flushfn, groupDepth, remainingLevels-1); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
// StemNode, HashedNode, or Empty - they handle their own collection
|
// StemNode, HashedNode, or Empty - they handle their own collection
|
||||||
if err := bt.right.CollectNodes(appendBit(path, 1), flushfn); err != nil {
|
if err := bt.right.CollectNodes(appendBit(path, 1), flushfn, groupDepth); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -95,7 +95,7 @@ func TestInternalNodeGetWithResolver(t *testing.T) {
|
||||||
Values: values[:],
|
Values: values[:],
|
||||||
depth: 1,
|
depth: 1,
|
||||||
}
|
}
|
||||||
return SerializeNode(stemNode), nil
|
return SerializeNode(stemNode, MaxGroupDepth), nil
|
||||||
}
|
}
|
||||||
return nil, errors.New("node not found")
|
return nil, errors.New("node not found")
|
||||||
}
|
}
|
||||||
|
|
@ -381,7 +381,7 @@ func TestInternalNodeCollectNodes(t *testing.T) {
|
||||||
collectedNodes = append(collectedNodes, n)
|
collectedNodes = append(collectedNodes, n)
|
||||||
}
|
}
|
||||||
|
|
||||||
err := node.CollectNodes([]byte{1}, flushFn)
|
err := node.CollectNodes([]byte{1}, flushFn, MaxGroupDepth)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to collect nodes: %v", err)
|
t.Fatalf("Failed to collect nodes: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -184,7 +184,7 @@ func (it *binaryNodeIterator) Path() []byte {
|
||||||
|
|
||||||
// NodeBlob returns the serialized bytes of the current node.
|
// NodeBlob returns the serialized bytes of the current node.
|
||||||
func (it *binaryNodeIterator) NodeBlob() []byte {
|
func (it *binaryNodeIterator) NodeBlob() []byte {
|
||||||
return SerializeNode(it.current)
|
return SerializeNode(it.current, it.trie.groupDepth)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Leaf returns true iff the current node is a leaf node.
|
// Leaf returns true iff the current node is a leaf node.
|
||||||
|
|
|
||||||
|
|
@ -147,8 +147,8 @@ func (bt *StemNode) Hash() common.Hash {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CollectNodes collects all child nodes at a given path, and flushes it
|
// CollectNodes collects all child nodes at a given path, and flushes it
|
||||||
// into the provided node collector.
|
// into the provided node collector. groupDepth is ignored for StemNodes.
|
||||||
func (bt *StemNode) CollectNodes(path []byte, flush NodeFlushFn) error {
|
func (bt *StemNode) CollectNodes(path []byte, flush NodeFlushFn, groupDepth int) error {
|
||||||
flush(path, bt)
|
flush(path, bt)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -348,7 +348,7 @@ func TestStemNodeCollectNodes(t *testing.T) {
|
||||||
collectedNodes = append(collectedNodes, n)
|
collectedNodes = append(collectedNodes, n)
|
||||||
}
|
}
|
||||||
|
|
||||||
err := node.CollectNodes([]byte{0, 1, 0}, flushFn)
|
err := node.CollectNodes([]byte{0, 1, 0}, flushFn, MaxGroupDepth)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to collect nodes: %v", err)
|
t.Fatalf("Failed to collect nodes: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -115,9 +115,10 @@ func NewBinaryNode() BinaryNode {
|
||||||
|
|
||||||
// BinaryTrie is the implementation of https://eips.ethereum.org/EIPS/eip-7864.
|
// BinaryTrie is the implementation of https://eips.ethereum.org/EIPS/eip-7864.
|
||||||
type BinaryTrie struct {
|
type BinaryTrie struct {
|
||||||
root BinaryNode
|
root BinaryNode
|
||||||
reader *trie.Reader
|
reader *trie.Reader
|
||||||
tracer *trie.PrevalueTracer
|
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.
|
// ToDot converts the binary trie to a DOT language representation. Useful for debugging.
|
||||||
|
|
@ -127,15 +128,20 @@ func (t *BinaryTrie) ToDot() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewBinaryTrie creates a new binary trie.
|
// 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)
|
reader, err := trie.NewReader(root, common.Hash{}, db)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
t := &BinaryTrie{
|
t := &BinaryTrie{
|
||||||
root: NewBinaryNode(),
|
root: NewBinaryNode(),
|
||||||
reader: reader,
|
reader: reader,
|
||||||
tracer: trie.NewPrevalueTracer(),
|
tracer: trie.NewPrevalueTracer(),
|
||||||
|
groupDepth: groupDepth,
|
||||||
}
|
}
|
||||||
// Parse the root node if it's not empty
|
// Parse the root node if it's not empty
|
||||||
if root != types.EmptyBinaryHash && root != types.EmptyRootHash {
|
if root != types.EmptyBinaryHash && root != types.EmptyRootHash {
|
||||||
|
|
@ -325,9 +331,9 @@ func (t *BinaryTrie) Commit(_ bool) (common.Hash, *trienode.NodeSet) {
|
||||||
|
|
||||||
// The root can be any type of BinaryNode (InternalNode, StemNode, etc.)
|
// The root can be any type of BinaryNode (InternalNode, StemNode, etc.)
|
||||||
err := t.root.CollectNodes(nil, func(path []byte, node BinaryNode) {
|
err := t.root.CollectNodes(nil, func(path []byte, node BinaryNode) {
|
||||||
serialized := SerializeNode(node)
|
serialized := SerializeNode(node, t.groupDepth)
|
||||||
nodeset.AddNode(path, trienode.NewNodeWithPrev(node.Hash(), serialized, t.tracer.Get(path)))
|
nodeset.AddNode(path, trienode.NewNodeWithPrev(node.Hash(), serialized, t.tracer.Get(path)))
|
||||||
})
|
}, t.groupDepth)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(fmt.Errorf("CollectNodes failed: %v", err))
|
panic(fmt.Errorf("CollectNodes failed: %v", err))
|
||||||
}
|
}
|
||||||
|
|
@ -355,9 +361,10 @@ func (t *BinaryTrie) Prove(key []byte, proofDb ethdb.KeyValueWriter) error {
|
||||||
// Copy creates a deep copy of the trie.
|
// Copy creates a deep copy of the trie.
|
||||||
func (t *BinaryTrie) Copy() *BinaryTrie {
|
func (t *BinaryTrie) Copy() *BinaryTrie {
|
||||||
return &BinaryTrie{
|
return &BinaryTrie{
|
||||||
root: t.root.Copy(),
|
root: t.root.Copy(),
|
||||||
reader: t.reader,
|
reader: t.reader,
|
||||||
tracer: t.tracer.Copy(),
|
tracer: t.tracer.Copy(),
|
||||||
|
groupDepth: t.groupDepth,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,10 +31,11 @@ import (
|
||||||
|
|
||||||
// Config defines all necessary options for database.
|
// Config defines all necessary options for database.
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Preimages bool // Flag whether the preimage of node key is recorded
|
Preimages bool // Flag whether the preimage of node key is recorded
|
||||||
IsVerkle bool // Flag whether the db is holding a verkle tree
|
IsVerkle bool // Flag whether the db is holding a verkle tree
|
||||||
HashDB *hashdb.Config // Configs for hash-based scheme
|
BinTrieGroupDepth int // Number of levels per serialized group in binary trie (1-8, default 8)
|
||||||
PathDB *pathdb.Config // Configs for experimental path-based scheme
|
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
|
// HashDefaults represents a config for using hash-based scheme with
|
||||||
|
|
@ -48,9 +49,10 @@ var HashDefaults = &Config{
|
||||||
// VerkleDefaults represents a config for holding verkle trie data
|
// VerkleDefaults represents a config for holding verkle trie data
|
||||||
// using path-based scheme with default settings.
|
// using path-based scheme with default settings.
|
||||||
var VerkleDefaults = &Config{
|
var VerkleDefaults = &Config{
|
||||||
Preimages: false,
|
Preimages: false,
|
||||||
IsVerkle: true,
|
IsVerkle: true,
|
||||||
PathDB: pathdb.Defaults,
|
BinTrieGroupDepth: 8, // Default to byte-aligned groups
|
||||||
|
PathDB: pathdb.Defaults,
|
||||||
}
|
}
|
||||||
|
|
||||||
// backend defines the methods needed to access/update trie nodes in different
|
// backend defines the methods needed to access/update trie nodes in different
|
||||||
|
|
@ -380,6 +382,15 @@ func (db *Database) IsVerkle() bool {
|
||||||
return db.config.IsVerkle
|
return db.config.IsVerkle
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BinTrieGroupDepth returns the group depth for binary trie serialization (1-8).
|
||||||
|
// Returns 8 as default if not configured.
|
||||||
|
func (db *Database) BinTrieGroupDepth() int {
|
||||||
|
if db.config.BinTrieGroupDepth < 1 || db.config.BinTrieGroupDepth > 8 {
|
||||||
|
return 8 // Default
|
||||||
|
}
|
||||||
|
return db.config.BinTrieGroupDepth
|
||||||
|
}
|
||||||
|
|
||||||
// Disk returns the underlying disk database.
|
// Disk returns the underlying disk database.
|
||||||
func (db *Database) Disk() ethdb.Database {
|
func (db *Database) Disk() ethdb.Database {
|
||||||
return db.disk
|
return db.disk
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue