zebra_state/service/check/anchors.rs
1//! Checks for whether cited anchors are previously-computed note commitment
2//! tree roots.
3
4use std::{collections::HashMap, sync::Arc};
5
6use rayon::prelude::*;
7
8use zebra_chain::{
9 block::{Block, Height},
10 sprout,
11 transaction::{Hash as TransactionHash, Transaction, UnminedTx},
12};
13
14use crate::{
15 service::{finalized_state::ZebraDb, non_finalized_state::Chain},
16 SemanticallyVerifiedBlock, ValidateContextError,
17};
18
19/// Checks the final Sapling and Orchard anchors specified by `transaction`
20///
21/// This method checks for anchors computed from the final treestate of each block in
22/// the `parent_chain` or `finalized_state`.
23#[tracing::instrument(skip(finalized_state, parent_chain, transaction))]
24fn sapling_orchard_anchors_refer_to_final_treestates(
25 finalized_state: &ZebraDb,
26 parent_chain: Option<&Arc<Chain>>,
27 transaction: &Arc<Transaction>,
28 transaction_hash: TransactionHash,
29 tx_index_in_block: Option<usize>,
30 height: Option<Height>,
31) -> Result<(), ValidateContextError> {
32 // Sapling Spends
33 //
34 // MUST refer to some earlier block’s final Sapling treestate.
35 //
36 // # Consensus
37 //
38 // > The anchor of each Spend description MUST refer to some earlier
39 // > block’s final Sapling treestate. The anchor is encoded separately
40 // > in each Spend description for v4 transactions, or encoded once and
41 // > shared between all Spend descriptions in a v5 transaction.
42 //
43 // <https://zips.z.cash/protocol/protocol.pdf#spendsandoutputs>
44 //
45 // This rule is also implemented in
46 // [`zebra_chain::sapling::shielded_data`].
47 //
48 // The "earlier treestate" check is implemented here.
49 for (anchor_index_in_tx, anchor) in transaction.sapling_anchors().enumerate() {
50 tracing::debug!(
51 ?anchor,
52 ?anchor_index_in_tx,
53 ?tx_index_in_block,
54 ?height,
55 "observed sapling anchor",
56 );
57
58 if !parent_chain
59 .map(|chain| chain.sapling_anchors.contains(&anchor))
60 .unwrap_or(false)
61 && !finalized_state.contains_sapling_anchor(&anchor)
62 {
63 return Err(ValidateContextError::UnknownSaplingAnchor {
64 anchor,
65 height,
66 tx_index_in_block,
67 transaction_hash,
68 });
69 }
70
71 tracing::debug!(
72 ?anchor,
73 ?anchor_index_in_tx,
74 ?tx_index_in_block,
75 ?height,
76 "validated sapling anchor",
77 );
78 }
79
80 // Orchard Actions
81 //
82 // MUST refer to some earlier block’s final Orchard treestate.
83 //
84 // # Consensus
85 //
86 // > The anchorOrchard field of the transaction, whenever it exists
87 // > (i.e. when there are any Action descriptions), MUST refer to some
88 // > earlier block’s final Orchard treestate.
89 //
90 // <https://zips.z.cash/protocol/protocol.pdf#actions>
91 if let Some(orchard_shielded_data) = transaction.orchard_shielded_data() {
92 tracing::debug!(
93 ?orchard_shielded_data.shared_anchor,
94 ?tx_index_in_block,
95 ?height,
96 "observed orchard anchor",
97 );
98
99 if !parent_chain
100 .map(|chain| {
101 chain
102 .orchard_anchors
103 .contains(&orchard_shielded_data.shared_anchor)
104 })
105 .unwrap_or(false)
106 && !finalized_state.contains_orchard_anchor(&orchard_shielded_data.shared_anchor)
107 {
108 return Err(ValidateContextError::UnknownOrchardAnchor {
109 anchor: orchard_shielded_data.shared_anchor,
110 height,
111 tx_index_in_block,
112 transaction_hash,
113 });
114 }
115
116 tracing::debug!(
117 ?orchard_shielded_data.shared_anchor,
118 ?tx_index_in_block,
119 ?height,
120 "validated orchard anchor",
121 );
122 }
123
124 Ok(())
125}
126
127/// This function fetches and returns the Sprout final treestates from the state,
128/// so [`sprout_anchors_refer_to_treestates()`] can check Sprout final and interstitial treestates,
129/// without accessing the disk.
130///
131/// Sprout anchors may also refer to the interstitial output treestate of any prior
132/// `JoinSplit` _within the same transaction_; these are created on the fly
133/// in [`sprout_anchors_refer_to_treestates()`].
134#[tracing::instrument(skip(sprout_final_treestates, finalized_state, parent_chain, transaction))]
135fn fetch_sprout_final_treestates(
136 sprout_final_treestates: &mut HashMap<
137 sprout::tree::Root,
138 Arc<sprout::tree::NoteCommitmentTree>,
139 >,
140 finalized_state: &ZebraDb,
141 parent_chain: Option<&Arc<Chain>>,
142 transaction: &Arc<Transaction>,
143 tx_index_in_block: Option<usize>,
144 height: Option<Height>,
145) {
146 // Fetch and return Sprout JoinSplit final treestates
147 for (joinsplit_index_in_tx, joinsplit) in transaction.sprout_groth16_joinsplits().enumerate() {
148 // Avoid duplicate fetches
149 if sprout_final_treestates.contains_key(&joinsplit.anchor) {
150 continue;
151 }
152
153 let input_tree = parent_chain
154 .and_then(|chain| chain.sprout_trees_by_anchor.get(&joinsplit.anchor).cloned())
155 .or_else(|| finalized_state.sprout_tree_by_anchor(&joinsplit.anchor));
156
157 if let Some(input_tree) = input_tree {
158 sprout_final_treestates.insert(joinsplit.anchor, input_tree);
159
160 /* TODO:
161 - fix tests that generate incorrect root data
162 - assert that joinsplit.anchor matches input_tree.root() during tests,
163 but don't assert in production, because the check is CPU-intensive,
164 and sprout_anchors_refer_to_treestates() constructs the map correctly
165 */
166
167 tracing::debug!(
168 sprout_final_treestate_count = ?sprout_final_treestates.len(),
169 ?joinsplit.anchor,
170 ?joinsplit_index_in_tx,
171 ?tx_index_in_block,
172 ?height,
173 "observed sprout final treestate anchor",
174 );
175 }
176 }
177
178 tracing::trace!(
179 sprout_final_treestate_count = ?sprout_final_treestates.len(),
180 ?sprout_final_treestates,
181 ?height,
182 "returning sprout final treestate anchors",
183 );
184}
185
186/// Checks the Sprout anchors specified by `transactions`.
187///
188/// Sprout anchors may refer to some earlier block's final treestate (like
189/// Sapling and Orchard do exclusively) _or_ to the interstitial output
190/// treestate of any prior `JoinSplit` _within the same transaction_.
191///
192/// This method searches for anchors in the supplied `sprout_final_treestates`
193/// (which must be populated with all treestates pointed to in the `semantically_verified` block;
194/// see [`fetch_sprout_final_treestates()`]); or in the interstitial
195/// treestates which are computed on the fly in this function.
196#[tracing::instrument(skip(sprout_final_treestates, transaction))]
197fn sprout_anchors_refer_to_treestates(
198 sprout_final_treestates: &HashMap<sprout::tree::Root, Arc<sprout::tree::NoteCommitmentTree>>,
199 transaction: &Arc<Transaction>,
200 transaction_hash: TransactionHash,
201 tx_index_in_block: Option<usize>,
202 height: Option<Height>,
203) -> Result<(), ValidateContextError> {
204 // Sprout JoinSplits, with interstitial treestates to check as well.
205 let mut interstitial_trees: HashMap<sprout::tree::Root, Arc<sprout::tree::NoteCommitmentTree>> =
206 HashMap::new();
207
208 let joinsplit_count = transaction.sprout_groth16_joinsplits().count();
209
210 for (joinsplit_index_in_tx, joinsplit) in transaction.sprout_groth16_joinsplits().enumerate() {
211 // Check all anchor sets, including the one for interstitial
212 // anchors.
213 //
214 // The anchor is checked and the matching tree is obtained,
215 // which is used to create the interstitial tree state for this
216 // JoinSplit:
217 //
218 // > For each JoinSplit description in a transaction, an
219 // > interstitial output treestate is constructed which adds the
220 // > note commitments and nullifiers specified in that JoinSplit
221 // > description to the input treestate referred to by its
222 // > anchor. This interstitial output treestate is available for
223 // > use as the anchor of subsequent JoinSplit descriptions in
224 // > the same transaction.
225 //
226 // <https://zips.z.cash/protocol/protocol.pdf#joinsplit>
227 //
228 // # Consensus
229 //
230 // > The anchor of each JoinSplit description in a transaction
231 // > MUST refer to either some earlier block’s final Sprout
232 // > treestate, or to the interstitial output treestate of any
233 // > prior JoinSplit description in the same transaction.
234 //
235 // > For the first JoinSplit description of a transaction, the
236 // > anchor MUST be the output Sprout treestate of a previous
237 // > block.
238 //
239 // <https://zips.z.cash/protocol/protocol.pdf#joinsplit>
240 //
241 // Note that in order to satisfy the latter consensus rule above,
242 // [`interstitial_trees`] is always empty in the first iteration
243 // of the loop.
244 let input_tree = interstitial_trees
245 .get(&joinsplit.anchor)
246 .cloned()
247 .or_else(|| sprout_final_treestates.get(&joinsplit.anchor).cloned());
248
249 tracing::trace!(
250 ?input_tree,
251 final_lookup = ?sprout_final_treestates.get(&joinsplit.anchor),
252 interstitial_lookup = ?interstitial_trees.get(&joinsplit.anchor),
253 interstitial_tree_count = ?interstitial_trees.len(),
254 ?interstitial_trees,
255 ?height,
256 "looked up sprout treestate anchor",
257 );
258
259 let mut input_tree = match input_tree {
260 Some(tree) => tree,
261 None => {
262 tracing::debug!(
263 ?joinsplit.anchor,
264 ?joinsplit_index_in_tx,
265 ?tx_index_in_block,
266 ?height,
267 "failed to find sprout anchor",
268 );
269 return Err(ValidateContextError::UnknownSproutAnchor {
270 anchor: joinsplit.anchor,
271 height,
272 tx_index_in_block,
273 transaction_hash,
274 });
275 }
276 };
277
278 tracing::debug!(
279 ?joinsplit.anchor,
280 ?joinsplit_index_in_tx,
281 ?tx_index_in_block,
282 ?height,
283 "validated sprout anchor",
284 );
285
286 // The last interstitial treestate in a transaction can never be used,
287 // so we avoid generating it.
288 if joinsplit_index_in_tx == joinsplit_count - 1 {
289 continue;
290 }
291
292 let input_tree_inner = Arc::make_mut(&mut input_tree);
293
294 // Add new anchors to the interstitial note commitment tree.
295 for cm in joinsplit.commitments {
296 input_tree_inner
297 .append(cm)
298 .expect("note commitment should be appendable to the tree");
299 }
300
301 interstitial_trees.insert(input_tree.root(), input_tree);
302
303 tracing::debug!(
304 ?joinsplit.anchor,
305 ?joinsplit_index_in_tx,
306 ?tx_index_in_block,
307 ?height,
308 "observed sprout interstitial anchor",
309 );
310 }
311
312 Ok(())
313}
314
315/// Accepts a [`ZebraDb`], [`Chain`], and [`SemanticallyVerifiedBlock`].
316///
317/// Iterates over the transactions in the [`SemanticallyVerifiedBlock`] checking the final Sapling and Orchard anchors.
318///
319/// This method checks for anchors computed from the final treestate of each block in
320/// the `parent_chain` or `finalized_state`.
321#[tracing::instrument(skip_all)]
322pub(crate) fn block_sapling_orchard_anchors_refer_to_final_treestates(
323 finalized_state: &ZebraDb,
324 parent_chain: &Arc<Chain>,
325 semantically_verified: &SemanticallyVerifiedBlock,
326) -> Result<(), ValidateContextError> {
327 semantically_verified
328 .block
329 .transactions
330 .iter()
331 .enumerate()
332 .try_for_each(|(tx_index_in_block, transaction)| {
333 sapling_orchard_anchors_refer_to_final_treestates(
334 finalized_state,
335 Some(parent_chain),
336 transaction,
337 semantically_verified.transaction_hashes[tx_index_in_block],
338 Some(tx_index_in_block),
339 Some(semantically_verified.height),
340 )
341 })
342}
343
344/// Accepts a [`ZebraDb`], [`Arc<Chain>`](Chain), and [`SemanticallyVerifiedBlock`].
345///
346/// Iterates over the transactions in the [`SemanticallyVerifiedBlock`], and fetches the Sprout final treestates
347/// from the state.
348///
349/// Returns a `HashMap` of the Sprout final treestates from the state for [`sprout_anchors_refer_to_treestates()`]
350/// to check Sprout final and interstitial treestates without accessing the disk.
351///
352/// Sprout anchors may also refer to the interstitial output treestate of any prior
353/// `JoinSplit` _within the same transaction_; these are created on the fly
354/// in [`sprout_anchors_refer_to_treestates()`].
355#[tracing::instrument(skip_all)]
356pub(crate) fn block_fetch_sprout_final_treestates(
357 finalized_state: &ZebraDb,
358 parent_chain: &Arc<Chain>,
359 semantically_verified: &SemanticallyVerifiedBlock,
360) -> HashMap<sprout::tree::Root, Arc<sprout::tree::NoteCommitmentTree>> {
361 let mut sprout_final_treestates = HashMap::new();
362
363 for (tx_index_in_block, transaction) in
364 semantically_verified.block.transactions.iter().enumerate()
365 {
366 fetch_sprout_final_treestates(
367 &mut sprout_final_treestates,
368 finalized_state,
369 Some(parent_chain),
370 transaction,
371 Some(tx_index_in_block),
372 Some(semantically_verified.height),
373 );
374 }
375
376 sprout_final_treestates
377}
378
379/// Accepts a [`ZebraDb`], [`Arc<Chain>`](Chain), [`Arc<Block>`](Block), and an
380/// [`Arc<[transaction::Hash]>`](TransactionHash) of hashes corresponding to the transactions in [`Block`]
381///
382/// Iterates over the transactions in the [`Block`] checking the final Sprout anchors.
383///
384/// Sprout anchors may refer to some earlier block's final treestate (like
385/// Sapling and Orchard do exclusively) _or_ to the interstitial output
386/// treestate of any prior `JoinSplit` _within the same transaction_.
387///
388/// This method searches for anchors in the supplied `sprout_final_treestates`
389/// (which must be populated with all treestates pointed to in the `semantically_verified` block;
390/// see [`fetch_sprout_final_treestates()`]); or in the interstitial
391/// treestates which are computed on the fly in this function.
392#[tracing::instrument(skip(sprout_final_treestates, block, transaction_hashes))]
393pub(crate) fn block_sprout_anchors_refer_to_treestates(
394 sprout_final_treestates: HashMap<sprout::tree::Root, Arc<sprout::tree::NoteCommitmentTree>>,
395 block: Arc<Block>,
396 // Only used for debugging
397 transaction_hashes: Arc<[TransactionHash]>,
398 height: Height,
399) -> Result<(), ValidateContextError> {
400 tracing::trace!(
401 sprout_final_treestate_count = ?sprout_final_treestates.len(),
402 ?sprout_final_treestates,
403 ?height,
404 "received sprout final treestate anchors",
405 );
406
407 let check_tx_sprout_anchors = |(tx_index_in_block, transaction)| {
408 sprout_anchors_refer_to_treestates(
409 &sprout_final_treestates,
410 transaction,
411 transaction_hashes[tx_index_in_block],
412 Some(tx_index_in_block),
413 Some(height),
414 )?;
415
416 Ok(())
417 };
418
419 // The overhead for a parallel iterator is unwarranted if sprout_final_treestates is empty
420 // because it will either return an error for the first transaction or only check that `joinsplit_data`
421 // is `None` for each transaction.
422 if sprout_final_treestates.is_empty() {
423 // The block has no valid sprout anchors
424 block
425 .transactions
426 .iter()
427 .enumerate()
428 .try_for_each(check_tx_sprout_anchors)
429 } else {
430 block
431 .transactions
432 .par_iter()
433 .enumerate()
434 .try_for_each(check_tx_sprout_anchors)
435 }
436}
437
438/// Accepts a [`ZebraDb`], an optional [`Option<Arc<Chain>>`](Chain), and an [`UnminedTx`].
439///
440/// Checks the final Sprout, Sapling and Orchard anchors specified in the [`UnminedTx`].
441///
442/// This method checks for anchors computed from the final treestate of each block in
443/// the `parent_chain` or `finalized_state`.
444#[tracing::instrument(skip_all)]
445pub(crate) fn tx_anchors_refer_to_final_treestates(
446 finalized_state: &ZebraDb,
447 parent_chain: Option<&Arc<Chain>>,
448 unmined_tx: &UnminedTx,
449) -> Result<(), ValidateContextError> {
450 sapling_orchard_anchors_refer_to_final_treestates(
451 finalized_state,
452 parent_chain,
453 &unmined_tx.transaction,
454 unmined_tx.id.mined_id(),
455 None,
456 None,
457 )?;
458
459 // If there are no sprout transactions in the block, avoid running a rayon scope
460 if unmined_tx.transaction.has_sprout_joinsplit_data() {
461 let mut sprout_final_treestates = HashMap::new();
462
463 fetch_sprout_final_treestates(
464 &mut sprout_final_treestates,
465 finalized_state,
466 parent_chain,
467 &unmined_tx.transaction,
468 None,
469 None,
470 );
471
472 let mut sprout_anchors_result = None;
473 rayon::in_place_scope_fifo(|s| {
474 // This check is expensive, because it updates a note commitment tree for each sprout JoinSplit.
475 // Since we could be processing attacker-controlled mempool transactions, we need to run each one
476 // in its own thread, separately from tokio's blocking I/O threads. And if we are under heavy load,
477 // we want verification to finish in order, so that later transactions can't delay earlier ones.
478 s.spawn_fifo(|_s| {
479 tracing::trace!(
480 sprout_final_treestate_count = ?sprout_final_treestates.len(),
481 ?sprout_final_treestates,
482 "received sprout final treestate anchors",
483 );
484
485 sprout_anchors_result = Some(sprout_anchors_refer_to_treestates(
486 &sprout_final_treestates,
487 &unmined_tx.transaction,
488 unmined_tx.id.mined_id(),
489 None,
490 None,
491 ));
492 });
493 });
494
495 sprout_anchors_result.expect("scope has finished")?;
496 }
497
498 Ok(())
499}