Skip to content

Perf/shielded pool incremental merkle frontier#79

Merged
nol4lej merged 5 commits into
mainfrom
perf/shielded-pool-incremental-merkle-frontier
Apr 30, 2026
Merged

Perf/shielded pool incremental merkle frontier#79
nol4lej merged 5 commits into
mainfrom
perf/shielded-pool-incremental-merkle-frontier

Conversation

@nol4lej
Copy link
Copy Markdown
Member

@nol4lej nol4lej commented Apr 29, 2026

No description provided.

nol4lej and others added 5 commits April 29, 2026 16:25
… frontier

insert_leaf formerly called get_all_leaves() and recomputed the full
Poseidon Merkle root from scratch on every insert (O(n) reads,
O(n log n) hashes). At 10k commitments this meant ~10k storage reads
per shield or private_transfer extrinsic.

Replace with an incremental frontier algorithm: persist a 20-slot array
(MerkleTreeFrontier storage item) that stores the last left-sibling at
each tree level. Each insert now takes exactly 20 hashes and 1 storage
read/write, regardless of tree size. Complexity is O(depth) = O(20) —
constant at any scale.

Changes:
- lib.rs: add MerkleTreeFrontier<T> StorageValue<_, [[u8;32]; 20]>
- storage.rs: add get_frontier / set_frontier to MerkleRepository
- merkle.rs: rewrite MerkleTreeService::insert_leaf to use frontier;
  remove compute_poseidon_merkle_root (now unused); fix old_root in
  MerkleRootUpdated event (was always [0u8;32], now carries real value)
- genesis.rs: initialize MerkleTreeFrontier to all-zeros at genesis

Add 5 tests:
- incremental_root_matches_batch_root_after_single_insert
- incremental_root_matches_batch_root_after_multiple_inserts
- incremental_proof_verifies_against_incremental_root
- merkle_root_updated_event_carries_correct_old_root
- frontier_survives_storage_round_trip_across_separate_calls
…hardening and tests

Merkle tree performance:
- Replace full O(n) root recomputation with an incremental frontier; insertion
  is now O(depth) instead of O(n leaves).
- Add CommitmentToLeafIndex reverse storage map; commitment → leaf position
  lookup is now O(1) instead of a linear scan.

Pool statistics:
- Track total commitments inserted and total nullifiers spent in dedicated
  storage items updated on every operation.
- Expose nullifier_count in fc-rpc-v2 PoolStatsResponse as an O(1) read
  from the new counter, replacing the previous O(n) key-page scan.

RPC cleanup:
- Remove shieldedPool_scanEvents from the legacy RPC trait and server;
  the endpoint always returned an empty list and has been superseded by
  the privacy_* v2 endpoints.

Type safety:
- Add debug_assert_eq in EncryptedMemo::nonce(), ciphertext(), and tag()
  to catch buffer-size invariant violations at test time.

Testing:
- Add integration tests for Flujo A + Flujo B mixed batch disclosure,
  covering both successful mixing and policy-missing failure paths.
- Add integration tests for the claim_shielded ZK verification path;
  extend MockZkVerifier with a sentinel byte (0x00) to simulate Ok(false).
- Add unit tests for the validate_unsigned fee floor anti-spam guard;
  introduce mock_set_min_relay_fee to control the minimum per test.
@nol4lej nol4lej merged commit 39fbade into main Apr 30, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant