zebra_state/service/check/
nullifier.rs1use std::{collections::HashMap, sync::Arc};
4
5use tracing::trace;
6use zebra_chain::transaction::Transaction;
7
8use crate::{
9 error::DuplicateNullifierError,
10 service::{
11 finalized_state::ZebraDb,
12 non_finalized_state::{Chain, SpendingTransactionId},
13 },
14 SemanticallyVerifiedBlock, ValidateContextError,
15};
16
17#[allow(unused_imports)]
19use crate::service;
20
21#[tracing::instrument(skip(semantically_verified, finalized_state))]
37pub(crate) fn no_duplicates_in_finalized_chain(
38 semantically_verified: &SemanticallyVerifiedBlock,
39 finalized_state: &ZebraDb,
40) -> Result<(), ValidateContextError> {
41 for nullifier in semantically_verified.block.sprout_nullifiers() {
42 if finalized_state.contains_sprout_nullifier(nullifier) {
43 Err(nullifier.duplicate_nullifier_error(true))?;
44 }
45 }
46
47 for nullifier in semantically_verified.block.sapling_nullifiers() {
48 if finalized_state.contains_sapling_nullifier(nullifier) {
49 Err(nullifier.duplicate_nullifier_error(true))?;
50 }
51 }
52
53 for nullifier in semantically_verified.block.orchard_nullifiers() {
54 if finalized_state.contains_orchard_nullifier(nullifier) {
55 Err(nullifier.duplicate_nullifier_error(true))?;
56 }
57 }
58
59 for nullifier in semantically_verified.block.ironwood_nullifiers() {
60 if finalized_state.contains_ironwood_nullifier(&nullifier) {
61 Err(nullifier.duplicate_nullifier_error(true))?;
62 }
63 }
64
65 Ok(())
66}
67
68fn find_duplicate_nullifier<NullifierT, FinalizedStateContainsFn, NonFinalizedStateContainsFn>(
76 revealed_nullifiers: impl IntoIterator<Item = NullifierT>,
77 finalized_chain_contains: FinalizedStateContainsFn,
78 non_finalized_chain_contains: Option<NonFinalizedStateContainsFn>,
79) -> Result<(), ValidateContextError>
80where
81 NullifierT: DuplicateNullifierError + Copy,
82 FinalizedStateContainsFn: Fn(NullifierT) -> bool,
83 NonFinalizedStateContainsFn: Fn(NullifierT) -> bool,
84{
85 for nullifier in revealed_nullifiers {
86 if let Some(true) = non_finalized_chain_contains.as_ref().map(|f| f(nullifier)) {
87 Err(nullifier.duplicate_nullifier_error(false))?
88 } else if finalized_chain_contains(nullifier) {
89 Err(nullifier.duplicate_nullifier_error(true))?
90 }
91 }
92
93 Ok(())
94}
95
96#[tracing::instrument(skip_all)]
109pub(crate) fn tx_no_duplicates_in_chain(
110 finalized_chain: &ZebraDb,
111 non_finalized_chain: Option<&Arc<Chain>>,
112 transaction: &Arc<Transaction>,
113) -> Result<(), ValidateContextError> {
114 find_duplicate_nullifier(
115 transaction.sprout_nullifiers().copied(),
116 |nullifier| finalized_chain.contains_sprout_nullifier(&nullifier),
117 non_finalized_chain
118 .map(|chain| move |nullifier| chain.sprout_nullifiers.contains_key(&nullifier)),
119 )?;
120
121 find_duplicate_nullifier(
122 transaction.sapling_nullifiers().copied(),
123 |nullifier| finalized_chain.contains_sapling_nullifier(&nullifier),
124 non_finalized_chain
125 .map(|chain| move |nullifier| chain.sapling_nullifiers.contains_key(&nullifier)),
126 )?;
127
128 find_duplicate_nullifier(
129 transaction.orchard_nullifiers().copied(),
130 |nullifier| finalized_chain.contains_orchard_nullifier(&nullifier),
131 non_finalized_chain
132 .map(|chain| move |nullifier| chain.orchard_nullifiers.contains_key(&nullifier)),
133 )?;
134
135 find_duplicate_nullifier(
137 transaction.ironwood_nullifiers(),
138 |nullifier| finalized_chain.contains_ironwood_nullifier(&nullifier),
139 non_finalized_chain
140 .map(|chain| move |nullifier| chain.ironwood_nullifiers.contains_key(&nullifier)),
141 )?;
142
143 Ok(())
144}
145
146#[tracing::instrument(skip(chain_nullifiers, shielded_data_nullifiers))]
174pub(crate) fn add_to_non_finalized_chain_unique<NullifierT>(
175 chain_nullifiers: &mut HashMap<NullifierT, SpendingTransactionId>,
176 shielded_data_nullifiers: impl IntoIterator<Item = NullifierT>,
177 revealing_tx_id: SpendingTransactionId,
178) -> Result<(), ValidateContextError>
179where
180 NullifierT: DuplicateNullifierError + Copy + std::fmt::Debug + Eq + std::hash::Hash,
181{
182 for nullifier in shielded_data_nullifiers.into_iter() {
183 trace!(?nullifier, "adding nullifier");
184
185 if chain_nullifiers
187 .insert(nullifier, revealing_tx_id)
188 .is_some()
189 {
190 Err(nullifier.duplicate_nullifier_error(false))?;
191 }
192 }
193
194 Ok(())
195}
196
197#[tracing::instrument(skip(chain_nullifiers, shielded_data_nullifiers))]
221pub(crate) fn remove_from_non_finalized_chain<NullifierT>(
222 chain_nullifiers: &mut HashMap<NullifierT, SpendingTransactionId>,
223 shielded_data_nullifiers: impl IntoIterator<Item = NullifierT>,
224) where
225 NullifierT: std::fmt::Debug + Eq + std::hash::Hash,
226{
227 for nullifier in shielded_data_nullifiers.into_iter() {
228 trace!(?nullifier, "removing nullifier");
229
230 assert!(
231 chain_nullifiers.remove(&nullifier).is_some(),
232 "nullifier must be present if block was added to chain"
233 );
234 }
235}