# Prototype: Replace `TrieMasks` with `Option` in `ProofTrieNode` ## Summary This prototype explores replacing `TrieMasks` with `Option` in `ProofTrieNode` and related APIs, as suggested after PR #20664. ## Current State ```rust // TrieMasks - each field is independently optional pub struct TrieMasks { pub hash_mask: Option, // 4 bytes pub tree_mask: Option, // 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 = 6 bytes ``` ## Key Findings ### 1. Size Reduction - `TrieMasks`: 8 bytes - `Option`: 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:** ```rust 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:** ```rust if let Some(branch_masks) = masks { self.branch_node_masks.insert(path, branch_masks); } ``` **Before:** ```rust 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:** ```rust 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` fields to single `Option` (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: ```rust // 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