diff --git a/CHANGELOG.md b/CHANGELOG.md index f7ab38e17..e888d5efd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,7 @@ - [BREAKING] Renamed `SubmitProvenBatch` RPC endpoint to `SubmitProvenTxBatch` ([#2094](https://github.com/0xMiden/node/pull/2094)). - Fixed block producer mempool panic when selecting transactions that depend on notes created by pruned committed transactions ([#2097](https://github.com/0xMiden/node/pull/2097)). - Implemented filtering based on the network account note script root allowlist in ntx-builder ([#2042](https://github.com/0xMiden/node/issues/2042)). +- Avoided a duplicate pass over block nullifiers while computing store tree mutations ([#1548](https://github.com/0xMiden/node/issues/1548)). - [BREAKING] Renamed `ExplorerStatusDetails` fields in the network monitor's `/status` payload from `number_of_*` to `total_*` (`total_transactions`, `total_nullifiers`, `total_notes`, `total_account_updates`). The values now represent network-wide cumulative totals from the explorer's `overviewStats` query instead of last-block counts. - [BREAKING] Removed `--wallet-filepath` / `--counter-filepath` flags and the `MIDEN_MONITOR_WALLET_FILEPATH` / `MIDEN_MONITOR_COUNTER_FILEPATH` env vars from the network monitor. The monitor now keeps wallet and counter accounts fully in memory and regenerates them on every startup; the dashboard's counter value resets to zero on restart. diff --git a/crates/store/src/state/apply_block.rs b/crates/store/src/state/apply_block.rs index 2cf12a1b1..dee2f48a1 100644 --- a/crates/store/src/state/apply_block.rs +++ b/crates/store/src/state/apply_block.rs @@ -221,12 +221,18 @@ impl State { self.with_inner_read_blocking(|inner| { let block_num = header.block_num(); - // nullifiers can be produced only once - let duplicate_nullifiers: Vec<_> = body + // Nullifiers can be produced only once. Build the mutation input in the same pass so + // large blocks do not need to traverse the nullifier list twice. + let mut duplicate_nullifiers = Vec::new(); + let nullifier_mutations: Vec<_> = body .created_nullifiers() .iter() - .filter(|&nullifier| inner.nullifier_tree.get_block_num(nullifier).is_some()) - .copied() + .map(|nullifier| { + if inner.nullifier_tree.get_block_num(nullifier).is_some() { + duplicate_nullifiers.push(*nullifier); + } + (*nullifier, block_num) + }) .collect(); if !duplicate_nullifiers.is_empty() { return Err(InvalidBlockError::DuplicatedNullifiers(duplicate_nullifiers).into()); @@ -241,9 +247,7 @@ impl State { // compute update for nullifier tree let nullifier_tree_update = inner .nullifier_tree - .compute_mutations( - body.created_nullifiers().iter().map(|nullifier| (*nullifier, block_num)), - ) + .compute_mutations(nullifier_mutations) .map_err(InvalidBlockError::NewBlockNullifierAlreadySpent)?; if nullifier_tree_update.as_mutation_set().root() != header.nullifier_root() {