Files
reth/prototype-option-branch-node-masks.md
yongkangc 35430830c6 fix: correct store_in_db_trie logic and remove leftover TrieMasks usage
- Fix parallel trie store_in_db_trie: was setting true when masks were
  present regardless of whether they were empty. Now correctly checks
  that at least one mask is non-empty.
- Remove leftover TrieMasks struct usage in proof_v2 test
2026-01-03 04:50:08 +00:00

4.3 KiB

Prototype: Replace TrieMasks with Option<BranchNodeMasks> in ProofTrieNode

Summary

This prototype explores replacing TrieMasks with Option<BranchNodeMasks> in ProofTrieNode and related APIs, as suggested after PR #20664.

Current State

// TrieMasks - each field is independently optional
pub struct TrieMasks {
    pub hash_mask: Option<TrieMask>,  // 4 bytes
    pub tree_mask: Option<TrieMask>,  // 4 bytes
}  // Total: 8 bytes

// BranchNodeMasks - both fields always present together
pub struct BranchNodeMasks {
    pub hash_mask: TrieMask,  // 2 bytes
    pub tree_mask: TrieMask,  // 2 bytes
}  // Total: 4 bytes

// Option<BranchNodeMasks> = 6 bytes

Key Findings

1. Size Reduction

  • TrieMasks: 8 bytes
  • Option<BranchNodeMasks>: 6 bytes
  • Savings: 25% reduction (2 bytes per instance)

2. Code Simplification

The prototype shows -27 net lines across 5 files:

File Insertions Deletions Net
common/src/trie.rs +2 -1 +1
sparse/src/provider.rs +4 -4 0
sparse/src/state.rs +16 -23 -7
sparse/src/traits.rs +3 -3 0
sparse/src/trie.rs +18 -33 -15
Total +40 -67 -27

3. Semantic Correctness

Analysis confirms that both masks always come together:

  • When read from database, both masks are fetched together
  • Production code never sets one mask without the other
  • Independent mask setting (hash_mask: Some(..), tree_mask: None) only appears in tests

4. Simplification Examples

Before:

if masks.tree_mask.is_some() || masks.hash_mask.is_some() {
    self.branch_node_masks.insert(
        path,
        BranchNodeMasks {
            tree_mask: masks.tree_mask.unwrap_or_default(),
            hash_mask: masks.hash_mask.unwrap_or_default(),
        },
    );
}

After:

if let Some(branch_masks) = masks {
    self.branch_node_masks.insert(path, branch_masks);
}

Before:

store_in_db_trie: Some(
    masks.hash_mask.is_some_and(|mask| !mask.is_empty()) ||
        masks.tree_mask.is_some_and(|mask| !mask.is_empty()),
),

After:

store_in_db_trie: Some(masks.is_some_and(|m| {
    !m.hash_mask.is_empty() || !m.tree_mask.is_empty()
})),

5. Full Scope of Changes

Location Count Notes
sparse-parallel/src/trie.rs 70 Mostly tests
sparse/src/trie.rs 16 All tests after migration
proof_v2/mod.rs 8 Production code
proof_v2/node.rs 5 Production code
configured_sparse_trie.rs 3 Production code
common/src/trie.rs 2 Struct definition
common/src/lib.rs 1 Export

6. Additional Changes Required

  • RevealedNode in provider.rs should also change from two Option<TrieMask> fields to single Option<BranchNodeMasks> (done in prototype)
  • Tests need updates to use Some(BranchNodeMasks { .. }) instead of TrieMasks { hash_mask: Some(..), tree_mask: None }
  • proof_v2 module needs similar updates
  • Parallel sparse trie needs same changes as serial version

Recommendation

Proceed with this refactor because:

  1. Memory reduction: 25% smaller per instance
  2. Code simplification: -27 lines, cleaner patterns
  3. Semantic correctness: Reflects actual usage (masks come together)
  4. Type safety: Impossible to create invalid state with one mask missing

Test Migration Strategy

For tests that currently set masks independently:

// Before
TrieMasks { hash_mask: Some(TrieMask::new(0b01)), tree_mask: None }

// After - use default for the other mask
Some(BranchNodeMasks { hash_mask: TrieMask::new(0b01), tree_mask: TrieMask::default() })

Files Changed in Prototype

crates/trie/common/src/trie.rs       (ProofTrieNode definition)
crates/trie/sparse/src/provider.rs   (RevealedNode definition)
crates/trie/sparse/src/state.rs      (State trie operations)
crates/trie/sparse/src/traits.rs     (Trait signatures)
crates/trie/sparse/src/trie.rs       (Serial sparse trie impl)

Outstanding Work

  1. Update tests in sparse/src/trie.rs
  2. Update sparse-parallel/src/trie.rs (same changes)
  3. Update proof_v2 module
  4. Update configured_sparse_trie.rs
  5. Run full test suite and benchmarks
  6. Consider removing TrieMasks struct entirely if no longer needed