zebra_state/service/non_finalized_state/
chain.rs

1//! [`Chain`] implements a single non-finalized blockchain,
2//! starting at the finalized tip.
3
4use std::{
5    cmp::Ordering,
6    collections::{BTreeMap, BTreeSet, HashMap, HashSet},
7    ops::{Deref, DerefMut, RangeInclusive},
8    sync::Arc,
9};
10
11use chrono::{DateTime, Utc};
12use mset::MultiSet;
13use tracing::instrument;
14
15use zebra_chain::{
16    amount::{Amount, NegativeAllowed, NonNegative},
17    block::{self, Height},
18    block_info::BlockInfo,
19    history_tree::HistoryTree,
20    orchard,
21    parallel::tree::NoteCommitmentTrees,
22    parameters::Network,
23    primitives::Groth16Proof,
24    sapling,
25    serialization::ZcashSerialize as _,
26    sprout,
27    subtree::{NoteCommitmentSubtree, NoteCommitmentSubtreeData, NoteCommitmentSubtreeIndex},
28    transaction::{
29        self,
30        Transaction::{self, *},
31    },
32    transparent,
33    value_balance::ValueBalance,
34    work::difficulty::PartialCumulativeWork,
35};
36
37use crate::{
38    request::Treestate, service::check, ContextuallyVerifiedBlock, HashOrHeight, OutputLocation,
39    TransactionLocation, ValidateContextError,
40};
41
42#[cfg(feature = "indexer")]
43use crate::request::Spend;
44
45use self::index::TransparentTransfers;
46
47pub mod index;
48
49/// A single non-finalized partial chain, from the child of the finalized tip,
50/// to a non-finalized chain tip.
51#[derive(Clone, Debug, Default)]
52pub struct Chain {
53    // Config
54    //
55    /// The configured network for this chain.
56    network: Network,
57
58    /// The internal state of this chain.
59    inner: ChainInner,
60
61    // Diagnostics
62    //
63    /// The last height this chain forked at. Diagnostics only.
64    ///
65    /// This field is only used for metrics. It is not consensus-critical, and it is not checked for
66    /// equality.
67    ///
68    /// We keep the same last fork height in both sides of a clone, because every new block clones a
69    /// chain, even if it's just growing that chain.
70    ///
71    /// # Note
72    ///
73    /// Most diagnostics are implemented on the `NonFinalizedState`, rather than each chain. Some
74    /// diagnostics only use the best chain, and others need to modify the Chain state, but that's
75    /// difficult with `Arc<Chain>`s.
76    pub(super) last_fork_height: Option<Height>,
77}
78
79/// Spending transaction id type when the `indexer` feature is selected.
80#[cfg(feature = "indexer")]
81pub(crate) type SpendingTransactionId = transaction::Hash;
82
83/// Spending transaction id type when the `indexer` feature is not selected.
84#[cfg(not(feature = "indexer"))]
85pub(crate) type SpendingTransactionId = ();
86
87/// The internal state of [`Chain`].
88#[derive(Clone, Debug, PartialEq, Eq, Default)]
89pub struct ChainInner {
90    // Blocks, heights, hashes, and transaction locations
91    //
92    /// The contextually valid blocks which form this non-finalized partial chain, in height order.
93    pub(crate) blocks: BTreeMap<block::Height, ContextuallyVerifiedBlock>,
94
95    /// An index of block heights for each block hash in `blocks`.
96    pub height_by_hash: HashMap<block::Hash, block::Height>,
97
98    /// An index of [`TransactionLocation`]s for each transaction hash in `blocks`.
99    pub tx_loc_by_hash: HashMap<transaction::Hash, TransactionLocation>,
100
101    // Transparent outputs and spends
102    //
103    /// The [`transparent::Utxo`]s created by `blocks`.
104    ///
105    /// Note that these UTXOs may not be unspent.
106    /// Outputs can be spent by later transactions or blocks in the chain.
107    //
108    // TODO: replace OutPoint with OutputLocation?
109    pub(crate) created_utxos: HashMap<transparent::OutPoint, transparent::OrderedUtxo>,
110    /// The spending transaction ids by [`transparent::OutPoint`]s spent by `blocks`,
111    /// including spent outputs created by earlier transactions or blocks in the chain.
112    ///
113    /// Note: Spending transaction ids are only tracked when the `indexer` feature is selected.
114    pub(crate) spent_utxos: HashMap<transparent::OutPoint, SpendingTransactionId>,
115
116    // Note commitment trees
117    //
118    /// The Sprout note commitment tree for each anchor.
119    /// This is required for interstitial states.
120    ///
121    /// When a chain is forked from the finalized tip, also contains the finalized tip root.
122    /// This extra root is removed when the first non-finalized block is committed.
123    pub(crate) sprout_trees_by_anchor:
124        HashMap<sprout::tree::Root, Arc<sprout::tree::NoteCommitmentTree>>,
125    /// The Sprout note commitment tree for each height.
126    ///
127    /// When a chain is forked from the finalized tip, also contains the finalized tip tree.
128    /// This extra tree is removed when the first non-finalized block is committed.
129    pub(crate) sprout_trees_by_height:
130        BTreeMap<block::Height, Arc<sprout::tree::NoteCommitmentTree>>,
131
132    /// The Sapling note commitment tree for each height.
133    ///
134    /// When a chain is forked from the finalized tip, also contains the finalized tip tree.
135    /// This extra tree is removed when the first non-finalized block is committed.
136    pub(crate) sapling_trees_by_height:
137        BTreeMap<block::Height, Arc<sapling::tree::NoteCommitmentTree>>,
138
139    /// The Orchard note commitment tree for each height.
140    ///
141    /// When a chain is forked from the finalized tip, also contains the finalized tip tree.
142    /// This extra tree is removed when the first non-finalized block is committed.
143    pub(crate) orchard_trees_by_height:
144        BTreeMap<block::Height, Arc<orchard::tree::NoteCommitmentTree>>,
145
146    // History trees
147    //
148    /// The ZIP-221 history tree for each height, including all finalized blocks,
149    /// and the non-finalized `blocks` below that height in this chain.
150    ///
151    /// When a chain is forked from the finalized tip, also contains the finalized tip tree.
152    /// This extra tree is removed when the first non-finalized block is committed.
153    pub(crate) history_trees_by_height: BTreeMap<block::Height, Arc<HistoryTree>>,
154
155    // Anchors
156    //
157    /// The Sprout anchors created by `blocks`.
158    ///
159    /// When a chain is forked from the finalized tip, also contains the finalized tip root.
160    /// This extra root is removed when the first non-finalized block is committed.
161    pub(crate) sprout_anchors: MultiSet<sprout::tree::Root>,
162    /// The Sprout anchors created by each block in `blocks`.
163    ///
164    /// When a chain is forked from the finalized tip, also contains the finalized tip root.
165    /// This extra root is removed when the first non-finalized block is committed.
166    pub(crate) sprout_anchors_by_height: BTreeMap<block::Height, sprout::tree::Root>,
167
168    /// The Sapling anchors created by `blocks`.
169    ///
170    /// When a chain is forked from the finalized tip, also contains the finalized tip root.
171    /// This extra root is removed when the first non-finalized block is committed.
172    pub(crate) sapling_anchors: MultiSet<sapling::tree::Root>,
173    /// The Sapling anchors created by each block in `blocks`.
174    ///
175    /// When a chain is forked from the finalized tip, also contains the finalized tip root.
176    /// This extra root is removed when the first non-finalized block is committed.
177    pub(crate) sapling_anchors_by_height: BTreeMap<block::Height, sapling::tree::Root>,
178    /// A list of Sapling subtrees completed in the non-finalized state
179    pub(crate) sapling_subtrees:
180        BTreeMap<NoteCommitmentSubtreeIndex, NoteCommitmentSubtreeData<sapling::tree::Node>>,
181
182    /// The Orchard anchors created by `blocks`.
183    ///
184    /// When a chain is forked from the finalized tip, also contains the finalized tip root.
185    /// This extra root is removed when the first non-finalized block is committed.
186    pub(crate) orchard_anchors: MultiSet<orchard::tree::Root>,
187    /// The Orchard anchors created by each block in `blocks`.
188    ///
189    /// When a chain is forked from the finalized tip, also contains the finalized tip root.
190    /// This extra root is removed when the first non-finalized block is committed.
191    pub(crate) orchard_anchors_by_height: BTreeMap<block::Height, orchard::tree::Root>,
192    /// A list of Orchard subtrees completed in the non-finalized state
193    pub(crate) orchard_subtrees:
194        BTreeMap<NoteCommitmentSubtreeIndex, NoteCommitmentSubtreeData<orchard::tree::Node>>,
195
196    // Nullifiers
197    //
198    /// The Sprout nullifiers revealed by `blocks` and, if the `indexer` feature is selected,
199    /// the id of the transaction that revealed them.
200    pub(crate) sprout_nullifiers: HashMap<sprout::Nullifier, SpendingTransactionId>,
201    /// The Sapling nullifiers revealed by `blocks` and, if the `indexer` feature is selected,
202    /// the id of the transaction that revealed them.
203    pub(crate) sapling_nullifiers: HashMap<sapling::Nullifier, SpendingTransactionId>,
204    /// The Orchard nullifiers revealed by `blocks` and, if the `indexer` feature is selected,
205    /// the id of the transaction that revealed them.
206    pub(crate) orchard_nullifiers: HashMap<orchard::Nullifier, SpendingTransactionId>,
207
208    // Transparent Transfers
209    // TODO: move to the transparent section
210    //
211    /// Partial transparent address index data from `blocks`.
212    pub(super) partial_transparent_transfers: HashMap<transparent::Address, TransparentTransfers>,
213
214    // Chain Work
215    //
216    /// The cumulative work represented by `blocks`.
217    ///
218    /// Since the best chain is determined by the largest cumulative work,
219    /// the work represented by finalized blocks can be ignored,
220    /// because they are common to all non-finalized chains.
221    pub(super) partial_cumulative_work: PartialCumulativeWork,
222
223    // Chain Pools
224    //
225    /// The chain value pool balances of the tip of this [`Chain`], including the block value pool
226    /// changes from all finalized blocks, and the non-finalized blocks in this chain.
227    ///
228    /// When a new chain is created from the finalized tip, it is initialized with the finalized tip
229    /// chain value pool balances.
230    pub(crate) chain_value_pools: ValueBalance<NonNegative>,
231    /// The block info after the given block height.
232    pub(crate) block_info_by_height: BTreeMap<block::Height, BlockInfo>,
233}
234
235impl Chain {
236    /// Create a new Chain with the given finalized tip trees and network.
237    pub(crate) fn new(
238        network: &Network,
239        finalized_tip_height: Height,
240        sprout_note_commitment_tree: Arc<sprout::tree::NoteCommitmentTree>,
241        sapling_note_commitment_tree: Arc<sapling::tree::NoteCommitmentTree>,
242        orchard_note_commitment_tree: Arc<orchard::tree::NoteCommitmentTree>,
243        history_tree: Arc<HistoryTree>,
244        finalized_tip_chain_value_pools: ValueBalance<NonNegative>,
245    ) -> Self {
246        let inner = ChainInner {
247            blocks: Default::default(),
248            height_by_hash: Default::default(),
249            tx_loc_by_hash: Default::default(),
250            created_utxos: Default::default(),
251            spent_utxos: Default::default(),
252            sprout_anchors: MultiSet::new(),
253            sprout_anchors_by_height: Default::default(),
254            sprout_trees_by_anchor: Default::default(),
255            sprout_trees_by_height: Default::default(),
256            sapling_anchors: MultiSet::new(),
257            sapling_anchors_by_height: Default::default(),
258            sapling_trees_by_height: Default::default(),
259            sapling_subtrees: Default::default(),
260            orchard_anchors: MultiSet::new(),
261            orchard_anchors_by_height: Default::default(),
262            orchard_trees_by_height: Default::default(),
263            orchard_subtrees: Default::default(),
264            sprout_nullifiers: Default::default(),
265            sapling_nullifiers: Default::default(),
266            orchard_nullifiers: Default::default(),
267            partial_transparent_transfers: Default::default(),
268            partial_cumulative_work: Default::default(),
269            history_trees_by_height: Default::default(),
270            chain_value_pools: finalized_tip_chain_value_pools,
271            block_info_by_height: Default::default(),
272        };
273
274        let mut chain = Self {
275            network: network.clone(),
276            inner,
277            last_fork_height: None,
278        };
279
280        chain.add_sprout_tree_and_anchor(finalized_tip_height, sprout_note_commitment_tree);
281        chain.add_sapling_tree_and_anchor(finalized_tip_height, sapling_note_commitment_tree);
282        chain.add_orchard_tree_and_anchor(finalized_tip_height, orchard_note_commitment_tree);
283        chain.add_history_tree(finalized_tip_height, history_tree);
284
285        chain
286    }
287
288    /// Is the internal state of `self` the same as `other`?
289    ///
290    /// [`Chain`] has custom [`Eq`] and [`Ord`] implementations based on proof of work,
291    /// which are used to select the best chain. So we can't derive [`Eq`] for [`Chain`].
292    ///
293    /// Unlike the custom trait impls, this method returns `true` if the entire internal state
294    /// of two chains is equal.
295    ///
296    /// If the internal states are different, it returns `false`,
297    /// even if the blocks in the two chains are equal.
298    #[cfg(any(test, feature = "proptest-impl"))]
299    pub fn eq_internal_state(&self, other: &Chain) -> bool {
300        self.inner == other.inner
301    }
302
303    /// Returns the last fork height if that height is still in the non-finalized state.
304    /// Otherwise, if that fork has been finalized, returns `None`.
305    #[allow(dead_code)]
306    pub fn recent_fork_height(&self) -> Option<Height> {
307        self.last_fork_height
308            .filter(|last| last >= &self.non_finalized_root_height())
309    }
310
311    /// Returns this chain fork's length, if its fork is still in the non-finalized state.
312    /// Otherwise, if the fork has been finalized, returns `None`.
313    #[allow(dead_code)]
314    pub fn recent_fork_length(&self) -> Option<u32> {
315        let fork_length = self.non_finalized_tip_height() - self.recent_fork_height()?;
316
317        // If the fork is above the tip, it is invalid, so just return `None`
318        // (Ignoring invalid data is ok because this is metrics-only code.)
319        fork_length.try_into().ok()
320    }
321
322    /// Push a contextually valid non-finalized block into this chain as the new tip.
323    ///
324    /// If the block is invalid, drops this chain, and returns an error.
325    ///
326    /// Note: a [`ContextuallyVerifiedBlock`] isn't actually contextually valid until
327    /// [`Self::update_chain_tip_with`] returns success.
328    #[instrument(level = "debug", skip(self, block), fields(block = %block.block))]
329    pub fn push(mut self, block: ContextuallyVerifiedBlock) -> Result<Chain, ValidateContextError> {
330        // update cumulative data members
331        self.update_chain_tip_with(&block)?;
332
333        tracing::debug!(block = %block.block, "adding block to chain");
334        self.blocks.insert(block.height, block);
335
336        Ok(self)
337    }
338
339    /// Pops the lowest height block of the non-finalized portion of a chain,
340    /// and returns it with its associated treestate.
341    #[instrument(level = "debug", skip(self))]
342    pub(crate) fn pop_root(&mut self) -> (ContextuallyVerifiedBlock, Treestate) {
343        // Obtain the lowest height.
344        let block_height = self.non_finalized_root_height();
345
346        // Obtain the treestate associated with the block being finalized.
347        let treestate = self
348            .treestate(block_height.into())
349            .expect("The treestate must be present for the root height.");
350
351        if treestate.note_commitment_trees.sapling_subtree.is_some() {
352            self.sapling_subtrees.pop_first();
353        }
354
355        if treestate.note_commitment_trees.orchard_subtree.is_some() {
356            self.orchard_subtrees.pop_first();
357        }
358
359        // Remove the lowest height block from `self.blocks`.
360        let block = self
361            .blocks
362            .remove(&block_height)
363            .expect("only called while blocks is populated");
364
365        // Update cumulative data members.
366        self.revert_chain_with(&block, RevertPosition::Root);
367
368        (block, treestate)
369    }
370
371    /// Returns the block at the provided height and all of its descendant blocks.
372    pub fn child_blocks(&self, block_height: &block::Height) -> Vec<ContextuallyVerifiedBlock> {
373        self.blocks
374            .range(block_height..)
375            .map(|(_h, b)| b.clone())
376            .collect()
377    }
378
379    /// Returns a new chain without the invalidated block or its descendants.
380    pub fn invalidate_block(
381        &self,
382        block_hash: block::Hash,
383    ) -> Option<(Self, Vec<ContextuallyVerifiedBlock>)> {
384        let block_height = self.height_by_hash(block_hash)?;
385        let mut new_chain = self.fork(block_hash)?;
386        new_chain.pop_tip();
387        new_chain.last_fork_height = None;
388        Some((new_chain, self.child_blocks(&block_height)))
389    }
390
391    /// Returns the height of the chain root.
392    pub fn non_finalized_root_height(&self) -> block::Height {
393        self.blocks
394            .keys()
395            .next()
396            .cloned()
397            .expect("only called while blocks is populated")
398    }
399
400    /// Fork and return a chain at the block with the given `fork_tip`, if it is part of this
401    /// chain. Otherwise, if this chain does not contain `fork_tip`, returns `None`.
402    pub fn fork(&self, fork_tip: block::Hash) -> Option<Self> {
403        if !self.height_by_hash.contains_key(&fork_tip) {
404            return None;
405        }
406
407        let mut forked = self.clone();
408
409        // Revert blocks above the fork
410        while forked.non_finalized_tip_hash() != fork_tip {
411            forked.pop_tip();
412
413            forked.last_fork_height = Some(forked.non_finalized_tip_height());
414        }
415
416        Some(forked)
417    }
418
419    /// Returns the [`Network`] for this chain.
420    pub fn network(&self) -> Network {
421        self.network.clone()
422    }
423
424    /// Returns the [`ContextuallyVerifiedBlock`] with [`block::Hash`] or
425    /// [`Height`], if it exists in this chain.
426    pub fn block(&self, hash_or_height: HashOrHeight) -> Option<&ContextuallyVerifiedBlock> {
427        let height =
428            hash_or_height.height_or_else(|hash| self.height_by_hash.get(&hash).cloned())?;
429
430        self.blocks.get(&height)
431    }
432
433    /// Returns the [`Transaction`] with [`transaction::Hash`], if it exists in this chain.
434    pub fn transaction(
435        &self,
436        hash: transaction::Hash,
437    ) -> Option<(&Arc<Transaction>, block::Height, DateTime<Utc>)> {
438        self.tx_loc_by_hash.get(&hash).map(|tx_loc| {
439            (
440                &self.blocks[&tx_loc.height].block.transactions[tx_loc.index.as_usize()],
441                tx_loc.height,
442                self.blocks[&tx_loc.height].block.header.time,
443            )
444        })
445    }
446
447    /// Returns the [`Transaction`] at [`TransactionLocation`], if it exists in this chain.
448    #[allow(dead_code)]
449    pub fn transaction_by_loc(&self, tx_loc: TransactionLocation) -> Option<&Arc<Transaction>> {
450        self.blocks
451            .get(&tx_loc.height)?
452            .block
453            .transactions
454            .get(tx_loc.index.as_usize())
455    }
456
457    /// Returns the [`transaction::Hash`] for the transaction at [`TransactionLocation`],
458    /// if it exists in this chain.
459    #[allow(dead_code)]
460    pub fn transaction_hash_by_loc(
461        &self,
462        tx_loc: TransactionLocation,
463    ) -> Option<&transaction::Hash> {
464        self.blocks
465            .get(&tx_loc.height)?
466            .transaction_hashes
467            .get(tx_loc.index.as_usize())
468    }
469
470    /// Returns the [`transaction::Hash`]es in the block with `hash_or_height`,
471    /// if it exists in this chain.
472    ///
473    /// Hashes are returned in block order.
474    ///
475    /// Returns `None` if the block is not found.
476    pub fn transaction_hashes_for_block(
477        &self,
478        hash_or_height: HashOrHeight,
479    ) -> Option<Arc<[transaction::Hash]>> {
480        let transaction_hashes = self.block(hash_or_height)?.transaction_hashes.clone();
481
482        Some(transaction_hashes)
483    }
484
485    /// Returns the [`block::Hash`] for `height`, if it exists in this chain.
486    pub fn hash_by_height(&self, height: Height) -> Option<block::Hash> {
487        let hash = self.blocks.get(&height)?.hash;
488
489        Some(hash)
490    }
491
492    /// Returns the [`Height`] for `hash`, if it exists in this chain.
493    pub fn height_by_hash(&self, hash: block::Hash) -> Option<Height> {
494        self.height_by_hash.get(&hash).cloned()
495    }
496
497    /// Returns true is the chain contains the given block hash.
498    /// Returns false otherwise.
499    pub fn contains_block_hash(&self, hash: block::Hash) -> bool {
500        self.height_by_hash.contains_key(&hash)
501    }
502
503    /// Returns true is the chain contains the given block height.
504    /// Returns false otherwise.
505    pub fn contains_block_height(&self, height: Height) -> bool {
506        self.blocks.contains_key(&height)
507    }
508
509    /// Returns true is the chain contains the given block hash or height.
510    /// Returns false otherwise.
511    #[allow(dead_code)]
512    pub fn contains_hash_or_height(&self, hash_or_height: impl Into<HashOrHeight>) -> bool {
513        use HashOrHeight::*;
514
515        let hash_or_height = hash_or_height.into();
516
517        match hash_or_height {
518            Hash(hash) => self.contains_block_hash(hash),
519            Height(height) => self.contains_block_height(height),
520        }
521    }
522
523    /// Returns the non-finalized tip block height and hash.
524    pub fn non_finalized_tip(&self) -> (Height, block::Hash) {
525        (
526            self.non_finalized_tip_height(),
527            self.non_finalized_tip_hash(),
528        )
529    }
530
531    /// Returns the non-finalized tip block height, hash, and total pool value balances.
532    pub fn non_finalized_tip_with_value_balance(
533        &self,
534    ) -> (Height, block::Hash, ValueBalance<NonNegative>) {
535        (
536            self.non_finalized_tip_height(),
537            self.non_finalized_tip_hash(),
538            self.chain_value_pools,
539        )
540    }
541
542    /// Returns the total pool balance after the block specified by
543    /// [`HashOrHeight`], if it exists in the non-finalized [`Chain`].
544    pub fn block_info(&self, hash_or_height: HashOrHeight) -> Option<BlockInfo> {
545        let height =
546            hash_or_height.height_or_else(|hash| self.height_by_hash.get(&hash).cloned())?;
547
548        self.block_info_by_height.get(&height).cloned()
549    }
550
551    /// Returns the Sprout note commitment tree of the tip of this [`Chain`],
552    /// including all finalized notes, and the non-finalized notes in this chain.
553    ///
554    /// If the chain is empty, instead returns the tree of the finalized tip,
555    /// which was supplied in [`Chain::new()`]
556    ///
557    /// # Panics
558    ///
559    /// If this chain has no sprout trees. (This should be impossible.)
560    pub fn sprout_note_commitment_tree_for_tip(&self) -> Arc<sprout::tree::NoteCommitmentTree> {
561        self.sprout_trees_by_height
562            .last_key_value()
563            .expect("only called while sprout_trees_by_height is populated")
564            .1
565            .clone()
566    }
567
568    /// Returns the Sprout [`NoteCommitmentTree`](sprout::tree::NoteCommitmentTree) specified by
569    /// a [`HashOrHeight`], if it exists in the non-finalized [`Chain`].
570    pub fn sprout_tree(
571        &self,
572        hash_or_height: HashOrHeight,
573    ) -> Option<Arc<sprout::tree::NoteCommitmentTree>> {
574        let height =
575            hash_or_height.height_or_else(|hash| self.height_by_hash.get(&hash).cloned())?;
576
577        self.sprout_trees_by_height
578            .range(..=height)
579            .next_back()
580            .map(|(_height, tree)| tree.clone())
581    }
582
583    /// Adds the Sprout `tree` to the tree and anchor indexes at `height`.
584    ///
585    /// `height` can be either:
586    ///
587    /// - the height of a new block that has just been added to the chain tip, or
588    /// - the finalized tip height—the height of the parent of the first block of a new chain.
589    ///
590    /// Stores only the first tree in each series of identical trees.
591    ///
592    /// # Panics
593    ///
594    /// - If there's a tree already stored at `height`.
595    /// - If there's an anchor already stored at `height`.
596    fn add_sprout_tree_and_anchor(
597        &mut self,
598        height: Height,
599        tree: Arc<sprout::tree::NoteCommitmentTree>,
600    ) {
601        // Having updated all the note commitment trees and nullifier sets in
602        // this block, the roots of the note commitment trees as of the last
603        // transaction are the anchor treestates of this block.
604        //
605        // Use the previously cached root which was calculated in parallel.
606        let anchor = tree.root();
607        trace!(?height, ?anchor, "adding sprout tree");
608
609        // Add the new tree only if:
610        //
611        // - it differs from the previous one, or
612        // - there's no previous tree.
613        if height.is_min()
614            || self
615                .sprout_tree(height.previous().expect("prev height").into())
616                .is_none_or(|prev_tree| prev_tree != tree)
617        {
618            assert_eq!(
619                self.sprout_trees_by_height.insert(height, tree.clone()),
620                None,
621                "incorrect overwrite of sprout tree: trees must be reverted then inserted",
622            );
623        }
624
625        // Store the root.
626        assert_eq!(
627            self.sprout_anchors_by_height.insert(height, anchor),
628            None,
629            "incorrect overwrite of sprout anchor: anchors must be reverted then inserted",
630        );
631
632        // Multiple inserts are expected here,
633        // because the anchors only change if a block has shielded transactions.
634        self.sprout_anchors.insert(anchor);
635        self.sprout_trees_by_anchor.insert(anchor, tree);
636    }
637
638    /// Removes the Sprout tree and anchor indexes at `height`.
639    ///
640    /// `height` can be at two different [`RevertPosition`]s in the chain:
641    ///
642    /// - a tip block above a chain fork—only the tree and anchor at that height are removed, or
643    /// - a root block—all trees and anchors at and below that height are removed, including
644    ///   temporary finalized tip trees.
645    ///
646    ///   # Panics
647    ///
648    ///  - If the anchor being removed is not present.
649    ///  - If there is no tree at `height`.
650    fn remove_sprout_tree_and_anchor(&mut self, position: RevertPosition, height: Height) {
651        let (removed_heights, highest_removed_tree) = if position == RevertPosition::Root {
652            (
653                // Remove all trees and anchors at or below the removed block.
654                // This makes sure the temporary trees from finalized tip forks are removed.
655                self.sprout_anchors_by_height
656                    .keys()
657                    .cloned()
658                    .filter(|index_height| *index_height <= height)
659                    .collect(),
660                // Cache the highest (rightmost) tree before its removal.
661                self.sprout_tree(height.into()),
662            )
663        } else {
664            // Just remove the reverted tip trees and anchors.
665            // We don't need to cache the highest (rightmost) tree.
666            (vec![height], None)
667        };
668
669        for height in &removed_heights {
670            let anchor = self
671                .sprout_anchors_by_height
672                .remove(height)
673                .expect("Sprout anchor must be present if block was added to chain");
674
675            self.sprout_trees_by_height.remove(height);
676
677            trace!(?height, ?position, ?anchor, "removing sprout tree");
678
679            // Multiple removals are expected here,
680            // because the anchors only change if a block has shielded transactions.
681            assert!(
682                self.sprout_anchors.remove(&anchor),
683                "Sprout anchor must be present if block was added to chain"
684            );
685            if !self.sprout_anchors.contains(&anchor) {
686                self.sprout_trees_by_anchor.remove(&anchor);
687            }
688        }
689
690        // # Invariant
691        //
692        // The height following after the removed heights in a non-empty non-finalized state must
693        // always have its tree.
694        //
695        // The loop above can violate the invariant, and if `position` is [`RevertPosition::Root`],
696        // it will always violate the invariant. We restore the invariant by storing the highest
697        // (rightmost) removed tree just above `height` if there is no tree at that height.
698        if !self.is_empty() && height < self.non_finalized_tip_height() {
699            let next_height = height
700                .next()
701                .expect("Zebra should never reach the max height in normal operation.");
702
703            self.sprout_trees_by_height
704                .entry(next_height)
705                .or_insert_with(|| {
706                    highest_removed_tree.expect("There should be a cached removed tree.")
707                });
708        }
709    }
710
711    /// Returns the Sapling note commitment tree of the tip of this [`Chain`],
712    /// including all finalized notes, and the non-finalized notes in this chain.
713    ///
714    /// If the chain is empty, instead returns the tree of the finalized tip,
715    /// which was supplied in [`Chain::new()`]
716    ///
717    /// # Panics
718    ///
719    /// If this chain has no sapling trees. (This should be impossible.)
720    pub fn sapling_note_commitment_tree_for_tip(&self) -> Arc<sapling::tree::NoteCommitmentTree> {
721        self.sapling_trees_by_height
722            .last_key_value()
723            .expect("only called while sapling_trees_by_height is populated")
724            .1
725            .clone()
726    }
727
728    /// Returns the Sapling [`NoteCommitmentTree`](sapling::tree::NoteCommitmentTree) specified
729    /// by a [`HashOrHeight`], if it exists in the non-finalized [`Chain`].
730    pub fn sapling_tree(
731        &self,
732        hash_or_height: HashOrHeight,
733    ) -> Option<Arc<sapling::tree::NoteCommitmentTree>> {
734        let height =
735            hash_or_height.height_or_else(|hash| self.height_by_hash.get(&hash).cloned())?;
736
737        self.sapling_trees_by_height
738            .range(..=height)
739            .next_back()
740            .map(|(_height, tree)| tree.clone())
741    }
742
743    /// Returns the Sapling [`NoteCommitmentSubtree`] that was completed at a block with
744    /// [`HashOrHeight`], if it exists in the non-finalized [`Chain`].
745    ///
746    /// # Concurrency
747    ///
748    /// This method should not be used to get subtrees in concurrent code by height,
749    /// because the same heights in different chain forks can have different subtrees.
750    pub fn sapling_subtree(
751        &self,
752        hash_or_height: HashOrHeight,
753    ) -> Option<NoteCommitmentSubtree<sapling::tree::Node>> {
754        let height =
755            hash_or_height.height_or_else(|hash| self.height_by_hash.get(&hash).cloned())?;
756
757        self.sapling_subtrees
758            .iter()
759            .find(|(_index, subtree)| subtree.end_height == height)
760            .map(|(index, subtree)| subtree.with_index(*index))
761    }
762
763    /// Returns a list of Sapling [`NoteCommitmentSubtree`]s in the provided range.
764    ///
765    /// Unlike the finalized state and `ReadRequest::SaplingSubtrees`, the returned subtrees
766    /// can start after `start_index`. These subtrees are continuous up to the tip.
767    ///
768    /// There is no API for retrieving single subtrees by index, because it can accidentally be
769    /// used to create an inconsistent list of subtrees after concurrent non-finalized and
770    /// finalized updates.
771    pub fn sapling_subtrees_in_range(
772        &self,
773        range: impl std::ops::RangeBounds<NoteCommitmentSubtreeIndex>,
774    ) -> BTreeMap<NoteCommitmentSubtreeIndex, NoteCommitmentSubtreeData<sapling::tree::Node>> {
775        self.sapling_subtrees
776            .range(range)
777            .map(|(index, subtree)| (*index, *subtree))
778            .collect()
779    }
780
781    /// Returns the Sapling [`NoteCommitmentSubtree`] if it was completed at the tip height.
782    pub fn sapling_subtree_for_tip(&self) -> Option<NoteCommitmentSubtree<sapling::tree::Node>> {
783        if !self.is_empty() {
784            let tip = self.non_finalized_tip_height();
785            self.sapling_subtree(tip.into())
786        } else {
787            None
788        }
789    }
790
791    /// Adds the Sapling `tree` to the tree and anchor indexes at `height`.
792    ///
793    /// `height` can be either:
794    ///
795    /// - the height of a new block that has just been added to the chain tip, or
796    /// - the finalized tip height—the height of the parent of the first block of a new chain.
797    ///
798    /// Stores only the first tree in each series of identical trees.
799    ///
800    /// # Panics
801    ///
802    /// - If there's a tree already stored at `height`.
803    /// - If there's an anchor already stored at `height`.
804    fn add_sapling_tree_and_anchor(
805        &mut self,
806        height: Height,
807        tree: Arc<sapling::tree::NoteCommitmentTree>,
808    ) {
809        let anchor = tree.root();
810        trace!(?height, ?anchor, "adding sapling tree");
811
812        // Add the new tree only if:
813        //
814        // - it differs from the previous one, or
815        // - there's no previous tree.
816        if height.is_min()
817            || self
818                .sapling_tree(height.previous().expect("prev height").into())
819                .is_none_or(|prev_tree| prev_tree != tree)
820        {
821            assert_eq!(
822                self.sapling_trees_by_height.insert(height, tree),
823                None,
824                "incorrect overwrite of sapling tree: trees must be reverted then inserted",
825            );
826        }
827
828        // Store the root.
829        assert_eq!(
830            self.sapling_anchors_by_height.insert(height, anchor),
831            None,
832            "incorrect overwrite of sapling anchor: anchors must be reverted then inserted",
833        );
834
835        // Multiple inserts are expected here,
836        // because the anchors only change if a block has shielded transactions.
837        self.sapling_anchors.insert(anchor);
838    }
839
840    /// Removes the Sapling tree and anchor indexes at `height`.
841    ///
842    /// `height` can be at two different [`RevertPosition`]s in the chain:
843    ///
844    /// - a tip block above a chain fork—only the tree and anchor at that height are removed, or
845    /// - a root block—all trees and anchors at and below that height are removed, including
846    ///   temporary finalized tip trees.
847    ///
848    ///   # Panics
849    ///
850    ///  - If the anchor being removed is not present.
851    ///  - If there is no tree at `height`.
852    fn remove_sapling_tree_and_anchor(&mut self, position: RevertPosition, height: Height) {
853        let (removed_heights, highest_removed_tree) = if position == RevertPosition::Root {
854            (
855                // Remove all trees and anchors at or below the removed block.
856                // This makes sure the temporary trees from finalized tip forks are removed.
857                self.sapling_anchors_by_height
858                    .keys()
859                    .cloned()
860                    .filter(|index_height| *index_height <= height)
861                    .collect(),
862                // Cache the highest (rightmost) tree before its removal.
863                self.sapling_tree(height.into()),
864            )
865        } else {
866            // Just remove the reverted tip trees and anchors.
867            // We don't need to cache the highest (rightmost) tree.
868            (vec![height], None)
869        };
870
871        for height in &removed_heights {
872            let anchor = self
873                .sapling_anchors_by_height
874                .remove(height)
875                .expect("Sapling anchor must be present if block was added to chain");
876
877            self.sapling_trees_by_height.remove(height);
878
879            trace!(?height, ?position, ?anchor, "removing sapling tree");
880
881            // Multiple removals are expected here,
882            // because the anchors only change if a block has shielded transactions.
883            assert!(
884                self.sapling_anchors.remove(&anchor),
885                "Sapling anchor must be present if block was added to chain"
886            );
887        }
888
889        // # Invariant
890        //
891        // The height following after the removed heights in a non-empty non-finalized state must
892        // always have its tree.
893        //
894        // The loop above can violate the invariant, and if `position` is [`RevertPosition::Root`],
895        // it will always violate the invariant. We restore the invariant by storing the highest
896        // (rightmost) removed tree just above `height` if there is no tree at that height.
897        if !self.is_empty() && height < self.non_finalized_tip_height() {
898            let next_height = height
899                .next()
900                .expect("Zebra should never reach the max height in normal operation.");
901
902            self.sapling_trees_by_height
903                .entry(next_height)
904                .or_insert_with(|| {
905                    highest_removed_tree.expect("There should be a cached removed tree.")
906                });
907        }
908    }
909
910    /// Returns the Orchard note commitment tree of the tip of this [`Chain`],
911    /// including all finalized notes, and the non-finalized notes in this chain.
912    ///
913    /// If the chain is empty, instead returns the tree of the finalized tip,
914    /// which was supplied in [`Chain::new()`]
915    ///
916    /// # Panics
917    ///
918    /// If this chain has no orchard trees. (This should be impossible.)
919    pub fn orchard_note_commitment_tree_for_tip(&self) -> Arc<orchard::tree::NoteCommitmentTree> {
920        self.orchard_trees_by_height
921            .last_key_value()
922            .expect("only called while orchard_trees_by_height is populated")
923            .1
924            .clone()
925    }
926
927    /// Returns the Orchard
928    /// [`NoteCommitmentTree`](orchard::tree::NoteCommitmentTree) specified by a
929    /// [`HashOrHeight`], if it exists in the non-finalized [`Chain`].
930    pub fn orchard_tree(
931        &self,
932        hash_or_height: HashOrHeight,
933    ) -> Option<Arc<orchard::tree::NoteCommitmentTree>> {
934        let height =
935            hash_or_height.height_or_else(|hash| self.height_by_hash.get(&hash).cloned())?;
936
937        self.orchard_trees_by_height
938            .range(..=height)
939            .next_back()
940            .map(|(_height, tree)| tree.clone())
941    }
942
943    /// Returns the Orchard [`NoteCommitmentSubtree`] that was completed at a block with
944    /// [`HashOrHeight`], if it exists in the non-finalized [`Chain`].
945    ///
946    /// # Concurrency
947    ///
948    /// This method should not be used to get subtrees in concurrent code by height,
949    /// because the same heights in different chain forks can have different subtrees.
950    pub fn orchard_subtree(
951        &self,
952        hash_or_height: HashOrHeight,
953    ) -> Option<NoteCommitmentSubtree<orchard::tree::Node>> {
954        let height =
955            hash_or_height.height_or_else(|hash| self.height_by_hash.get(&hash).cloned())?;
956
957        self.orchard_subtrees
958            .iter()
959            .find(|(_index, subtree)| subtree.end_height == height)
960            .map(|(index, subtree)| subtree.with_index(*index))
961    }
962
963    /// Returns a list of Orchard [`NoteCommitmentSubtree`]s in the provided range.
964    ///
965    /// Unlike the finalized state and `ReadRequest::OrchardSubtrees`, the returned subtrees
966    /// can start after `start_index`. These subtrees are continuous up to the tip.
967    ///
968    /// There is no API for retrieving single subtrees by index, because it can accidentally be
969    /// used to create an inconsistent list of subtrees after concurrent non-finalized and
970    /// finalized updates.
971    pub fn orchard_subtrees_in_range(
972        &self,
973        range: impl std::ops::RangeBounds<NoteCommitmentSubtreeIndex>,
974    ) -> BTreeMap<NoteCommitmentSubtreeIndex, NoteCommitmentSubtreeData<orchard::tree::Node>> {
975        self.orchard_subtrees
976            .range(range)
977            .map(|(index, subtree)| (*index, *subtree))
978            .collect()
979    }
980
981    /// Returns the Orchard [`NoteCommitmentSubtree`] if it was completed at the tip height.
982    pub fn orchard_subtree_for_tip(&self) -> Option<NoteCommitmentSubtree<orchard::tree::Node>> {
983        if !self.is_empty() {
984            let tip = self.non_finalized_tip_height();
985            self.orchard_subtree(tip.into())
986        } else {
987            None
988        }
989    }
990
991    /// Adds the Orchard `tree` to the tree and anchor indexes at `height`.
992    ///
993    /// `height` can be either:
994    ///
995    /// - the height of a new block that has just been added to the chain tip, or
996    /// - the finalized tip height—the height of the parent of the first block of a new chain.
997    ///
998    /// Stores only the first tree in each series of identical trees.
999    ///
1000    /// # Panics
1001    ///
1002    /// - If there's a tree already stored at `height`.
1003    /// - If there's an anchor already stored at `height`.
1004    fn add_orchard_tree_and_anchor(
1005        &mut self,
1006        height: Height,
1007        tree: Arc<orchard::tree::NoteCommitmentTree>,
1008    ) {
1009        // Having updated all the note commitment trees and nullifier sets in
1010        // this block, the roots of the note commitment trees as of the last
1011        // transaction are the anchor treestates of this block.
1012        //
1013        // Use the previously cached root which was calculated in parallel.
1014        let anchor = tree.root();
1015        trace!(?height, ?anchor, "adding orchard tree");
1016
1017        // Add the new tree only if:
1018        //
1019        // - it differs from the previous one, or
1020        // - there's no previous tree.
1021        if height.is_min()
1022            || self
1023                .orchard_tree(height.previous().expect("prev height").into())
1024                .is_none_or(|prev_tree| prev_tree != tree)
1025        {
1026            assert_eq!(
1027                self.orchard_trees_by_height.insert(height, tree),
1028                None,
1029                "incorrect overwrite of orchard tree: trees must be reverted then inserted",
1030            );
1031        }
1032
1033        // Store the root.
1034        assert_eq!(
1035            self.orchard_anchors_by_height.insert(height, anchor),
1036            None,
1037            "incorrect overwrite of orchard anchor: anchors must be reverted then inserted",
1038        );
1039
1040        // Multiple inserts are expected here,
1041        // because the anchors only change if a block has shielded transactions.
1042        self.orchard_anchors.insert(anchor);
1043    }
1044
1045    /// Removes the Orchard tree and anchor indexes at `height`.
1046    ///
1047    /// `height` can be at two different [`RevertPosition`]s in the chain:
1048    ///
1049    /// - a tip block above a chain fork—only the tree and anchor at that height are removed, or
1050    /// - a root block—all trees and anchors at and below that height are removed, including
1051    ///   temporary finalized tip trees.
1052    ///
1053    ///   # Panics
1054    ///
1055    ///  - If the anchor being removed is not present.
1056    ///  - If there is no tree at `height`.
1057    fn remove_orchard_tree_and_anchor(&mut self, position: RevertPosition, height: Height) {
1058        let (removed_heights, highest_removed_tree) = if position == RevertPosition::Root {
1059            (
1060                // Remove all trees and anchors at or below the removed block.
1061                // This makes sure the temporary trees from finalized tip forks are removed.
1062                self.orchard_anchors_by_height
1063                    .keys()
1064                    .cloned()
1065                    .filter(|index_height| *index_height <= height)
1066                    .collect(),
1067                // Cache the highest (rightmost) tree before its removal.
1068                self.orchard_tree(height.into()),
1069            )
1070        } else {
1071            // Just remove the reverted tip trees and anchors.
1072            // We don't need to cache the highest (rightmost) tree.
1073            (vec![height], None)
1074        };
1075
1076        for height in &removed_heights {
1077            let anchor = self
1078                .orchard_anchors_by_height
1079                .remove(height)
1080                .expect("Orchard anchor must be present if block was added to chain");
1081
1082            self.orchard_trees_by_height.remove(height);
1083
1084            trace!(?height, ?position, ?anchor, "removing orchard tree");
1085
1086            // Multiple removals are expected here,
1087            // because the anchors only change if a block has shielded transactions.
1088            assert!(
1089                self.orchard_anchors.remove(&anchor),
1090                "Orchard anchor must be present if block was added to chain"
1091            );
1092        }
1093
1094        // # Invariant
1095        //
1096        // The height following after the removed heights in a non-empty non-finalized state must
1097        // always have its tree.
1098        //
1099        // The loop above can violate the invariant, and if `position` is [`RevertPosition::Root`],
1100        // it will always violate the invariant. We restore the invariant by storing the highest
1101        // (rightmost) removed tree just above `height` if there is no tree at that height.
1102        if !self.is_empty() && height < self.non_finalized_tip_height() {
1103            let next_height = height
1104                .next()
1105                .expect("Zebra should never reach the max height in normal operation.");
1106
1107            self.orchard_trees_by_height
1108                .entry(next_height)
1109                .or_insert_with(|| {
1110                    highest_removed_tree.expect("There should be a cached removed tree.")
1111                });
1112        }
1113    }
1114
1115    /// Returns the History tree of the tip of this [`Chain`],
1116    /// including all finalized blocks, and the non-finalized blocks below the chain tip.
1117    ///
1118    /// If the chain is empty, instead returns the tree of the finalized tip,
1119    /// which was supplied in [`Chain::new()`]
1120    ///
1121    /// # Panics
1122    ///
1123    /// If this chain has no history trees. (This should be impossible.)
1124    pub fn history_block_commitment_tree(&self) -> Arc<HistoryTree> {
1125        self.history_trees_by_height
1126            .last_key_value()
1127            .expect("only called while history_trees_by_height is populated")
1128            .1
1129            .clone()
1130    }
1131
1132    /// Returns the [`HistoryTree`] specified by a [`HashOrHeight`], if it
1133    /// exists in the non-finalized [`Chain`].
1134    pub fn history_tree(&self, hash_or_height: HashOrHeight) -> Option<Arc<HistoryTree>> {
1135        let height =
1136            hash_or_height.height_or_else(|hash| self.height_by_hash.get(&hash).cloned())?;
1137
1138        self.history_trees_by_height.get(&height).cloned()
1139    }
1140
1141    /// Add the History `tree` to the history tree index at `height`.
1142    ///
1143    /// `height` can be either:
1144    /// - the height of a new block that has just been added to the chain tip, or
1145    /// - the finalized tip height: the height of the parent of the first block of a new chain.
1146    fn add_history_tree(&mut self, height: Height, tree: Arc<HistoryTree>) {
1147        // The history tree commits to all the blocks before this block.
1148        //
1149        // Use the previously cached root which was calculated in parallel.
1150        trace!(?height, "adding history tree");
1151
1152        assert_eq!(
1153            self.history_trees_by_height.insert(height, tree),
1154            None,
1155            "incorrect overwrite of history tree: trees must be reverted then inserted",
1156        );
1157    }
1158
1159    /// Remove the History tree index at `height`.
1160    ///
1161    /// `height` can be at two different [`RevertPosition`]s in the chain:
1162    /// - a tip block above a chain fork: only that height is removed, or
1163    /// - a root block: all trees below that height are removed,
1164    ///   including temporary finalized tip trees.
1165    fn remove_history_tree(&mut self, position: RevertPosition, height: Height) {
1166        trace!(?height, ?position, "removing history tree");
1167
1168        if position == RevertPosition::Root {
1169            // Remove all trees at or below the reverted root block.
1170            // This makes sure the temporary trees from finalized tip forks are removed.
1171            self.history_trees_by_height
1172                .retain(|index_height, _tree| *index_height > height);
1173        } else {
1174            // Just remove the reverted tip tree.
1175            self.history_trees_by_height
1176                .remove(&height)
1177                .expect("History tree must be present if block was added to chain");
1178        }
1179    }
1180
1181    fn treestate(&self, hash_or_height: HashOrHeight) -> Option<Treestate> {
1182        let sprout_tree = self.sprout_tree(hash_or_height)?;
1183        let sapling_tree = self.sapling_tree(hash_or_height)?;
1184        let orchard_tree = self.orchard_tree(hash_or_height)?;
1185        let history_tree = self.history_tree(hash_or_height)?;
1186        let sapling_subtree = self.sapling_subtree(hash_or_height);
1187        let orchard_subtree = self.orchard_subtree(hash_or_height);
1188
1189        Some(Treestate::new(
1190            sprout_tree,
1191            sapling_tree,
1192            orchard_tree,
1193            sapling_subtree,
1194            orchard_subtree,
1195            history_tree,
1196        ))
1197    }
1198
1199    /// Returns the block hash of the tip block.
1200    pub fn non_finalized_tip_hash(&self) -> block::Hash {
1201        self.blocks
1202            .values()
1203            .next_back()
1204            .expect("only called while blocks is populated")
1205            .hash
1206    }
1207
1208    /// Returns the non-finalized root block hash and height.
1209    #[allow(dead_code)]
1210    pub fn non_finalized_root(&self) -> (block::Hash, block::Height) {
1211        (
1212            self.non_finalized_root_hash(),
1213            self.non_finalized_root_height(),
1214        )
1215    }
1216
1217    /// Returns the block hash of the non-finalized root block.
1218    pub fn non_finalized_root_hash(&self) -> block::Hash {
1219        self.blocks
1220            .values()
1221            .next()
1222            .expect("only called while blocks is populated")
1223            .hash
1224    }
1225
1226    /// Returns the block hash of the `n`th block from the non-finalized root.
1227    ///
1228    /// This is the block at `non_finalized_root_height() + n`.
1229    #[allow(dead_code)]
1230    pub fn non_finalized_nth_hash(&self, n: usize) -> Option<block::Hash> {
1231        self.blocks.values().nth(n).map(|block| block.hash)
1232    }
1233
1234    /// Remove the highest height block of the non-finalized portion of a chain.
1235    fn pop_tip(&mut self) {
1236        let block_height = self.non_finalized_tip_height();
1237
1238        let block = self
1239            .blocks
1240            .remove(&block_height)
1241            .expect("only called while blocks is populated");
1242
1243        assert!(
1244            !self.blocks.is_empty(),
1245            "Non-finalized chains must have at least one block to be valid"
1246        );
1247
1248        self.revert_chain_with(&block, RevertPosition::Tip);
1249    }
1250
1251    /// Return the non-finalized tip height for this chain.
1252    ///
1253    /// # Panics
1254    ///
1255    /// Panics if called while the chain is empty,
1256    /// or while the chain is updating its internal state with the first block.
1257    pub fn non_finalized_tip_height(&self) -> block::Height {
1258        self.max_block_height()
1259            .expect("only called while blocks is populated")
1260    }
1261
1262    /// Return the non-finalized tip height for this chain,
1263    /// or `None` if `self.blocks` is empty.
1264    fn max_block_height(&self) -> Option<block::Height> {
1265        self.blocks.keys().next_back().cloned()
1266    }
1267
1268    /// Return the non-finalized tip block for this chain,
1269    /// or `None` if `self.blocks` is empty.
1270    pub fn tip_block(&self) -> Option<&ContextuallyVerifiedBlock> {
1271        self.blocks.values().next_back()
1272    }
1273
1274    /// Returns true if the non-finalized part of this chain is empty.
1275    pub fn is_empty(&self) -> bool {
1276        self.blocks.is_empty()
1277    }
1278
1279    /// Returns the non-finalized length of this chain.
1280    #[allow(dead_code)]
1281    pub fn len(&self) -> usize {
1282        self.blocks.len()
1283    }
1284
1285    /// Returns the unspent transaction outputs (UTXOs) in this non-finalized chain.
1286    ///
1287    /// Callers should also check the finalized state for available UTXOs.
1288    /// If UTXOs remain unspent when a block is finalized, they are stored in the finalized state,
1289    /// and removed from the relevant chain(s).
1290    pub fn unspent_utxos(&self) -> HashMap<transparent::OutPoint, transparent::OrderedUtxo> {
1291        let mut unspent_utxos = self.created_utxos.clone();
1292        unspent_utxos.retain(|outpoint, _utxo| !self.spent_utxos.contains_key(outpoint));
1293
1294        unspent_utxos
1295    }
1296
1297    /// Returns the [`transparent::Utxo`] pointed to by the given
1298    /// [`transparent::OutPoint`] if it was created by this chain.
1299    ///
1300    /// UTXOs are returned regardless of whether they have been spent.
1301    pub fn created_utxo(&self, outpoint: &transparent::OutPoint) -> Option<transparent::Utxo> {
1302        self.created_utxos
1303            .get(outpoint)
1304            .map(|utxo| utxo.utxo.clone())
1305    }
1306
1307    /// Returns the [`Hash`](transaction::Hash) of the transaction that spent an output at
1308    /// the provided [`transparent::OutPoint`] or revealed the provided nullifier, if it exists
1309    /// and is spent or revealed by this chain.
1310    #[cfg(feature = "indexer")]
1311    pub fn spending_transaction_hash(&self, spend: &Spend) -> Option<transaction::Hash> {
1312        match spend {
1313            Spend::OutPoint(outpoint) => self.spent_utxos.get(outpoint),
1314            Spend::Sprout(nullifier) => self.sprout_nullifiers.get(nullifier),
1315            Spend::Sapling(nullifier) => self.sapling_nullifiers.get(nullifier),
1316            Spend::Orchard(nullifier) => self.orchard_nullifiers.get(nullifier),
1317        }
1318        .cloned()
1319    }
1320
1321    // Address index queries
1322
1323    /// Returns the transparent transfers for `addresses` in this non-finalized chain.
1324    ///
1325    /// If none of the addresses have an address index, returns an empty iterator.
1326    ///
1327    /// # Correctness
1328    ///
1329    /// Callers should apply the returned indexes to the corresponding finalized state indexes.
1330    ///
1331    /// The combined result will only be correct if the chains match.
1332    /// The exact type of match varies by query.
1333    pub fn partial_transparent_indexes<'a>(
1334        &'a self,
1335        addresses: &'a HashSet<transparent::Address>,
1336    ) -> impl Iterator<Item = &'a TransparentTransfers> {
1337        addresses
1338            .iter()
1339            .flat_map(|address| self.partial_transparent_transfers.get(address))
1340    }
1341
1342    /// Returns a tuple of the transparent balance change and the total received funds for
1343    /// `addresses` in this non-finalized chain.
1344    ///
1345    /// If the balance doesn't change for any of the addresses, returns zero.
1346    ///
1347    /// # Correctness
1348    ///
1349    /// Callers should apply this balance change to the finalized state balance for `addresses`.
1350    ///
1351    /// The total balance will only be correct if this partial chain matches the finalized state.
1352    /// Specifically, the root of this partial chain must be a child block of the finalized tip.
1353    pub fn partial_transparent_balance_change(
1354        &self,
1355        addresses: &HashSet<transparent::Address>,
1356    ) -> (Amount<NegativeAllowed>, u64) {
1357        let (balance, received) = self.partial_transparent_indexes(addresses).fold(
1358            (Ok(Amount::zero()), 0),
1359            |(balance, received), transfers| {
1360                let balance = balance + transfers.balance();
1361                (balance, received + transfers.received())
1362            },
1363        );
1364
1365        (balance.expect("unexpected amount overflow"), received)
1366    }
1367
1368    /// Returns the transparent UTXO changes for `addresses` in this non-finalized chain.
1369    ///
1370    /// If the UTXOs don't change for any of the addresses, returns empty lists.
1371    ///
1372    /// # Correctness
1373    ///
1374    /// Callers should apply these non-finalized UTXO changes to the finalized state UTXOs.
1375    ///
1376    /// The UTXOs will only be correct if the non-finalized chain matches or overlaps with
1377    /// the finalized state.
1378    ///
1379    /// Specifically, a block in the partial chain must be a child block of the finalized tip.
1380    /// (But the child block does not have to be the partial chain root.)
1381    pub fn partial_transparent_utxo_changes(
1382        &self,
1383        addresses: &HashSet<transparent::Address>,
1384    ) -> (
1385        BTreeMap<OutputLocation, transparent::Output>,
1386        BTreeSet<OutputLocation>,
1387    ) {
1388        let created_utxos = self
1389            .partial_transparent_indexes(addresses)
1390            .flat_map(|transfers| transfers.created_utxos())
1391            .map(|(out_loc, output)| (*out_loc, output.clone()))
1392            .collect();
1393
1394        let spent_utxos = self
1395            .partial_transparent_indexes(addresses)
1396            .flat_map(|transfers| transfers.spent_utxos())
1397            .cloned()
1398            .collect();
1399
1400        (created_utxos, spent_utxos)
1401    }
1402
1403    /// Returns the [`transaction::Hash`]es used by `addresses` to receive or spend funds,
1404    /// in the non-finalized chain, filtered using the `query_height_range`.
1405    ///
1406    /// If none of the addresses receive or spend funds in this partial chain, returns an empty list.
1407    ///
1408    /// # Correctness
1409    ///
1410    /// Callers should combine these non-finalized transactions with the finalized state transactions.
1411    ///
1412    /// The transaction IDs will only be correct if the non-finalized chain matches or overlaps with
1413    /// the finalized state.
1414    ///
1415    /// Specifically, a block in the partial chain must be a child block of the finalized tip.
1416    /// (But the child block does not have to be the partial chain root.)
1417    ///
1418    /// This condition does not apply if there is only one address.
1419    /// Since address transactions are only appended by blocks,
1420    /// and the finalized state query reads them in order,
1421    /// it is impossible to get inconsistent transactions for a single address.
1422    pub fn partial_transparent_tx_ids(
1423        &self,
1424        addresses: &HashSet<transparent::Address>,
1425        query_height_range: RangeInclusive<Height>,
1426    ) -> BTreeMap<TransactionLocation, transaction::Hash> {
1427        self.partial_transparent_indexes(addresses)
1428            .flat_map(|transfers| {
1429                transfers.tx_ids(&self.tx_loc_by_hash, query_height_range.clone())
1430            })
1431            .collect()
1432    }
1433
1434    /// Update the chain tip with the `contextually_valid` block,
1435    /// running note commitment tree updates in parallel with other updates.
1436    ///
1437    /// Used to implement `update_chain_tip_with::<ContextuallyVerifiedBlock>`.
1438    #[instrument(skip(self, contextually_valid), fields(block = %contextually_valid.block))]
1439    #[allow(clippy::unwrap_in_result)]
1440    fn update_chain_tip_with_block_parallel(
1441        &mut self,
1442        contextually_valid: &ContextuallyVerifiedBlock,
1443    ) -> Result<(), ValidateContextError> {
1444        let height = contextually_valid.height;
1445
1446        // Prepare data for parallel execution
1447        let mut nct = NoteCommitmentTrees {
1448            sprout: self.sprout_note_commitment_tree_for_tip(),
1449            sapling: self.sapling_note_commitment_tree_for_tip(),
1450            sapling_subtree: self.sapling_subtree_for_tip(),
1451            orchard: self.orchard_note_commitment_tree_for_tip(),
1452            orchard_subtree: self.orchard_subtree_for_tip(),
1453        };
1454
1455        let mut tree_result = None;
1456        let mut partial_result = None;
1457
1458        // Run 4 tasks in parallel:
1459        // - sprout, sapling, and orchard tree updates and root calculations
1460        // - the rest of the Chain updates
1461        rayon::in_place_scope_fifo(|scope| {
1462            // Spawns a separate rayon task for each note commitment tree
1463            tree_result = Some(nct.update_trees_parallel(&contextually_valid.block.clone()));
1464
1465            scope.spawn_fifo(|_scope| {
1466                partial_result =
1467                    Some(self.update_chain_tip_with_block_except_trees(contextually_valid));
1468            });
1469        });
1470
1471        tree_result.expect("scope has already finished")?;
1472        partial_result.expect("scope has already finished")?;
1473
1474        // Update the note commitment trees in the chain.
1475        self.add_sprout_tree_and_anchor(height, nct.sprout);
1476        self.add_sapling_tree_and_anchor(height, nct.sapling);
1477        self.add_orchard_tree_and_anchor(height, nct.orchard);
1478
1479        if let Some(subtree) = nct.sapling_subtree {
1480            self.sapling_subtrees
1481                .insert(subtree.index, subtree.into_data());
1482        }
1483        if let Some(subtree) = nct.orchard_subtree {
1484            self.orchard_subtrees
1485                .insert(subtree.index, subtree.into_data());
1486        }
1487
1488        let sapling_root = self.sapling_note_commitment_tree_for_tip().root();
1489        let orchard_root = self.orchard_note_commitment_tree_for_tip().root();
1490
1491        // TODO: update the history trees in a rayon thread, if they show up in CPU profiles
1492        let mut history_tree = self.history_block_commitment_tree();
1493        let history_tree_mut = Arc::make_mut(&mut history_tree);
1494        history_tree_mut
1495            .push(
1496                &self.network,
1497                contextually_valid.block.clone(),
1498                &sapling_root,
1499                &orchard_root,
1500            )
1501            .map_err(Arc::new)?;
1502
1503        self.add_history_tree(height, history_tree);
1504
1505        Ok(())
1506    }
1507
1508    /// Update the chain tip with the `contextually_valid` block,
1509    /// except for the note commitment and history tree updates.
1510    ///
1511    /// Used to implement `update_chain_tip_with::<ContextuallyVerifiedBlock>`.
1512    #[instrument(skip(self, contextually_valid), fields(block = %contextually_valid.block))]
1513    #[allow(clippy::unwrap_in_result)]
1514    fn update_chain_tip_with_block_except_trees(
1515        &mut self,
1516        contextually_valid: &ContextuallyVerifiedBlock,
1517    ) -> Result<(), ValidateContextError> {
1518        let (
1519            block,
1520            hash,
1521            height,
1522            new_outputs,
1523            spent_outputs,
1524            transaction_hashes,
1525            chain_value_pool_change,
1526        ) = (
1527            contextually_valid.block.as_ref(),
1528            contextually_valid.hash,
1529            contextually_valid.height,
1530            &contextually_valid.new_outputs,
1531            &contextually_valid.spent_outputs,
1532            &contextually_valid.transaction_hashes,
1533            &contextually_valid.chain_value_pool_change,
1534        );
1535
1536        // add hash to height_by_hash
1537        let prior_height = self.height_by_hash.insert(hash, height);
1538        assert!(
1539            prior_height.is_none(),
1540            "block heights must be unique within a single chain"
1541        );
1542
1543        // add work to partial cumulative work
1544        let block_work = block
1545            .header
1546            .difficulty_threshold
1547            .to_work()
1548            .expect("work has already been validated");
1549        self.partial_cumulative_work += block_work;
1550
1551        // for each transaction in block
1552        for (transaction_index, (transaction, transaction_hash)) in block
1553            .transactions
1554            .iter()
1555            .zip(transaction_hashes.iter().cloned())
1556            .enumerate()
1557        {
1558            let (
1559                inputs,
1560                outputs,
1561                joinsplit_data,
1562                sapling_shielded_data_per_spend_anchor,
1563                sapling_shielded_data_shared_anchor,
1564                orchard_shielded_data,
1565            ) = match transaction.deref() {
1566                V4 {
1567                    inputs,
1568                    outputs,
1569                    joinsplit_data,
1570                    sapling_shielded_data,
1571                    ..
1572                } => (inputs, outputs, joinsplit_data, sapling_shielded_data, &None, &None),
1573                V5 {
1574                    inputs,
1575                    outputs,
1576                    sapling_shielded_data,
1577                    orchard_shielded_data,
1578                    ..
1579                } => (
1580                    inputs,
1581                    outputs,
1582                    &None,
1583                    &None,
1584                    sapling_shielded_data,
1585                    orchard_shielded_data,
1586                ),
1587                #[cfg(feature="tx_v6")]
1588                V6 {
1589                    inputs,
1590                    outputs,
1591                    sapling_shielded_data,
1592                    orchard_shielded_data,
1593                    ..
1594                } => (
1595                    inputs,
1596                    outputs,
1597                    &None,
1598                    &None,
1599                    sapling_shielded_data,
1600                    orchard_shielded_data,
1601                ),
1602
1603                V1 { .. } | V2 { .. } | V3 { .. } => unreachable!(
1604                    "older transaction versions only exist in finalized blocks, because of the mandatory canopy checkpoint",
1605                ),
1606            };
1607
1608            // add key `transaction.hash` and value `(height, tx_index)` to `tx_loc_by_hash`
1609            let transaction_location = TransactionLocation::from_usize(height, transaction_index);
1610            let prior_pair = self
1611                .tx_loc_by_hash
1612                .insert(transaction_hash, transaction_location);
1613            assert_eq!(
1614                prior_pair, None,
1615                "transactions must be unique within a single chain"
1616            );
1617
1618            // add the utxos this produced
1619            self.update_chain_tip_with(&(outputs, &transaction_hash, new_outputs))?;
1620            // delete the utxos this consumed
1621            self.update_chain_tip_with(&(inputs, &transaction_hash, spent_outputs))?;
1622
1623            // add the shielded data
1624
1625            #[cfg(not(feature = "indexer"))]
1626            let transaction_hash = ();
1627
1628            self.update_chain_tip_with(&(joinsplit_data, &transaction_hash))?;
1629            self.update_chain_tip_with(&(
1630                sapling_shielded_data_per_spend_anchor,
1631                &transaction_hash,
1632            ))?;
1633            self.update_chain_tip_with(&(sapling_shielded_data_shared_anchor, &transaction_hash))?;
1634            self.update_chain_tip_with(&(orchard_shielded_data, &transaction_hash))?;
1635        }
1636
1637        // update the chain value pool balances
1638        let size = block.zcash_serialized_size();
1639        self.update_chain_tip_with(&(*chain_value_pool_change, height, size))?;
1640
1641        Ok(())
1642    }
1643}
1644
1645impl Deref for Chain {
1646    type Target = ChainInner;
1647
1648    fn deref(&self) -> &Self::Target {
1649        &self.inner
1650    }
1651}
1652
1653impl DerefMut for Chain {
1654    fn deref_mut(&mut self) -> &mut Self::Target {
1655        &mut self.inner
1656    }
1657}
1658
1659/// The revert position being performed on a chain.
1660#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
1661pub(crate) enum RevertPosition {
1662    /// The chain root is being reverted via [`Chain::pop_root`], when a block
1663    /// is finalized.
1664    Root,
1665
1666    /// The chain tip is being reverted via [`Chain::pop_tip`],
1667    /// when a chain is forked.
1668    Tip,
1669}
1670
1671/// Helper trait to organize inverse operations done on the [`Chain`] type.
1672///
1673/// Used to overload update and revert methods, based on the type of the argument,
1674/// and the position of the removed block in the chain.
1675///
1676/// This trait was motivated by the length of the `push`, [`Chain::pop_root`],
1677/// and [`Chain::pop_tip`] functions, and fear that it would be easy to
1678/// introduce bugs when updating them, unless the code was reorganized to keep
1679/// related operations adjacent to each other.
1680pub(crate) trait UpdateWith<T> {
1681    /// When `T` is added to the chain tip,
1682    /// update [`Chain`] cumulative data members to add data that are derived from `T`.
1683    fn update_chain_tip_with(&mut self, _: &T) -> Result<(), ValidateContextError>;
1684
1685    /// When `T` is removed from `position` in the chain,
1686    /// revert [`Chain`] cumulative data members to remove data that are derived from `T`.
1687    fn revert_chain_with(&mut self, _: &T, position: RevertPosition);
1688}
1689
1690impl UpdateWith<ContextuallyVerifiedBlock> for Chain {
1691    #[instrument(skip(self, contextually_valid), fields(block = %contextually_valid.block))]
1692    #[allow(clippy::unwrap_in_result)]
1693    fn update_chain_tip_with(
1694        &mut self,
1695        contextually_valid: &ContextuallyVerifiedBlock,
1696    ) -> Result<(), ValidateContextError> {
1697        self.update_chain_tip_with_block_parallel(contextually_valid)
1698    }
1699
1700    #[instrument(skip(self, contextually_valid), fields(block = %contextually_valid.block))]
1701    fn revert_chain_with(
1702        &mut self,
1703        contextually_valid: &ContextuallyVerifiedBlock,
1704        position: RevertPosition,
1705    ) {
1706        let (
1707            block,
1708            hash,
1709            height,
1710            new_outputs,
1711            spent_outputs,
1712            transaction_hashes,
1713            chain_value_pool_change,
1714        ) = (
1715            contextually_valid.block.as_ref(),
1716            contextually_valid.hash,
1717            contextually_valid.height,
1718            &contextually_valid.new_outputs,
1719            &contextually_valid.spent_outputs,
1720            &contextually_valid.transaction_hashes,
1721            &contextually_valid.chain_value_pool_change,
1722        );
1723
1724        // remove the blocks hash from `height_by_hash`
1725        assert!(
1726            self.height_by_hash.remove(&hash).is_some(),
1727            "hash must be present if block was added to chain"
1728        );
1729
1730        // TODO: move this to a Work or block header UpdateWith.revert...()?
1731        // remove work from partial_cumulative_work
1732        let block_work = block
1733            .header
1734            .difficulty_threshold
1735            .to_work()
1736            .expect("work has already been validated");
1737        self.partial_cumulative_work -= block_work;
1738
1739        // for each transaction in block
1740        for (transaction, transaction_hash) in
1741            block.transactions.iter().zip(transaction_hashes.iter())
1742        {
1743            let (
1744                inputs,
1745                outputs,
1746                joinsplit_data,
1747                sapling_shielded_data_per_spend_anchor,
1748                sapling_shielded_data_shared_anchor,
1749                orchard_shielded_data,
1750            ) = match transaction.deref() {
1751                V4 {
1752                    inputs,
1753                    outputs,
1754                    joinsplit_data,
1755                    sapling_shielded_data,
1756                    ..
1757                } => (inputs, outputs, joinsplit_data, sapling_shielded_data, &None, &None),
1758                V5 {
1759                    inputs,
1760                    outputs,
1761                    sapling_shielded_data,
1762                    orchard_shielded_data,
1763                    ..
1764                } => (
1765                    inputs,
1766                    outputs,
1767                    &None,
1768                    &None,
1769                    sapling_shielded_data,
1770                    orchard_shielded_data,
1771                ),
1772                #[cfg(feature="tx_v6")]
1773                V6 {
1774                    inputs,
1775                    outputs,
1776                    sapling_shielded_data,
1777                    orchard_shielded_data,
1778                    ..
1779                } => (
1780                    inputs,
1781                    outputs,
1782                    &None,
1783                    &None,
1784                    sapling_shielded_data,
1785                    orchard_shielded_data,
1786                ),
1787
1788                V1 { .. } | V2 { .. } | V3 { .. } => unreachable!(
1789                    "older transaction versions only exist in finalized blocks, because of the mandatory canopy checkpoint",
1790                ),
1791            };
1792
1793            // remove the utxos this produced
1794            self.revert_chain_with(&(outputs, transaction_hash, new_outputs), position);
1795            // reset the utxos this consumed
1796            self.revert_chain_with(&(inputs, transaction_hash, spent_outputs), position);
1797
1798            // TODO: move this to the history tree UpdateWith.revert...()?
1799            // remove `transaction.hash` from `tx_loc_by_hash`
1800            assert!(
1801                self.tx_loc_by_hash.remove(transaction_hash).is_some(),
1802                "transactions must be present if block was added to chain"
1803            );
1804
1805            // remove the shielded data
1806
1807            #[cfg(not(feature = "indexer"))]
1808            let transaction_hash = &();
1809
1810            self.revert_chain_with(&(joinsplit_data, transaction_hash), position);
1811            self.revert_chain_with(
1812                &(sapling_shielded_data_per_spend_anchor, transaction_hash),
1813                position,
1814            );
1815            self.revert_chain_with(
1816                &(sapling_shielded_data_shared_anchor, transaction_hash),
1817                position,
1818            );
1819            self.revert_chain_with(&(orchard_shielded_data, transaction_hash), position);
1820        }
1821
1822        // TODO: move these to the shielded UpdateWith.revert...()?
1823        self.remove_sprout_tree_and_anchor(position, height);
1824        self.remove_sapling_tree_and_anchor(position, height);
1825        self.remove_orchard_tree_and_anchor(position, height);
1826
1827        // TODO: move this to the history tree UpdateWith.revert...()?
1828        self.remove_history_tree(position, height);
1829
1830        // revert the chain value pool balances, if needed
1831        // note that size is 0 because it isn't need for reverting
1832        self.revert_chain_with(&(*chain_value_pool_change, height, 0), position);
1833    }
1834}
1835
1836// Created UTXOs
1837//
1838// TODO: replace arguments with a struct
1839impl
1840    UpdateWith<(
1841        // The outputs from a transaction in this block
1842        &Vec<transparent::Output>,
1843        // The hash of the transaction that the outputs are from
1844        &transaction::Hash,
1845        // The UTXOs for all outputs created by this transaction (or block)
1846        &HashMap<transparent::OutPoint, transparent::OrderedUtxo>,
1847    )> for Chain
1848{
1849    #[allow(clippy::unwrap_in_result)]
1850    fn update_chain_tip_with(
1851        &mut self,
1852        &(created_outputs, creating_tx_hash, block_created_outputs): &(
1853            &Vec<transparent::Output>,
1854            &transaction::Hash,
1855            &HashMap<transparent::OutPoint, transparent::OrderedUtxo>,
1856        ),
1857    ) -> Result<(), ValidateContextError> {
1858        for output_index in 0..created_outputs.len() {
1859            let outpoint = transparent::OutPoint {
1860                hash: *creating_tx_hash,
1861                index: output_index.try_into().expect("valid indexes fit in u32"),
1862            };
1863            let created_utxo = block_created_outputs
1864                .get(&outpoint)
1865                .expect("new_outputs contains all created UTXOs");
1866
1867            // Update the chain's created UTXOs
1868            let previous_entry = self.created_utxos.insert(outpoint, created_utxo.clone());
1869            assert_eq!(
1870                previous_entry, None,
1871                "unexpected created output: duplicate update or duplicate UTXO",
1872            );
1873
1874            // Update the address index with this UTXO
1875            if let Some(receiving_address) = created_utxo.utxo.output.address(&self.network) {
1876                let address_transfers = self
1877                    .partial_transparent_transfers
1878                    .entry(receiving_address)
1879                    .or_default();
1880
1881                address_transfers.update_chain_tip_with(&(&outpoint, created_utxo))?;
1882            }
1883        }
1884
1885        Ok(())
1886    }
1887
1888    fn revert_chain_with(
1889        &mut self,
1890        &(created_outputs, creating_tx_hash, block_created_outputs): &(
1891            &Vec<transparent::Output>,
1892            &transaction::Hash,
1893            &HashMap<transparent::OutPoint, transparent::OrderedUtxo>,
1894        ),
1895        position: RevertPosition,
1896    ) {
1897        for output_index in 0..created_outputs.len() {
1898            let outpoint = transparent::OutPoint {
1899                hash: *creating_tx_hash,
1900                index: output_index.try_into().expect("valid indexes fit in u32"),
1901            };
1902            let created_utxo = block_created_outputs
1903                .get(&outpoint)
1904                .expect("new_outputs contains all created UTXOs");
1905
1906            // Revert the chain's created UTXOs
1907            let removed_entry = self.created_utxos.remove(&outpoint);
1908            assert!(
1909                removed_entry.is_some(),
1910                "unexpected revert of created output: duplicate revert or duplicate UTXO",
1911            );
1912
1913            // Revert the address index for this UTXO
1914            if let Some(receiving_address) = created_utxo.utxo.output.address(&self.network) {
1915                let address_transfers = self
1916                    .partial_transparent_transfers
1917                    .get_mut(&receiving_address)
1918                    .expect("block has previously been applied to the chain");
1919
1920                address_transfers.revert_chain_with(&(&outpoint, created_utxo), position);
1921
1922                // Remove this transfer if it is now empty
1923                if address_transfers.is_empty() {
1924                    self.partial_transparent_transfers
1925                        .remove(&receiving_address);
1926                }
1927            }
1928        }
1929    }
1930}
1931
1932// Transparent inputs
1933//
1934// TODO: replace arguments with a struct
1935impl
1936    UpdateWith<(
1937        // The inputs from a transaction in this block
1938        &Vec<transparent::Input>,
1939        // The hash of the transaction that the inputs are from
1940        // (not the transaction the spent output was created by)
1941        &transaction::Hash,
1942        // The outputs for all inputs spent in this transaction (or block)
1943        &HashMap<transparent::OutPoint, transparent::OrderedUtxo>,
1944    )> for Chain
1945{
1946    fn update_chain_tip_with(
1947        &mut self,
1948        &(spending_inputs, spending_tx_hash, spent_outputs): &(
1949            &Vec<transparent::Input>,
1950            &transaction::Hash,
1951            &HashMap<transparent::OutPoint, transparent::OrderedUtxo>,
1952        ),
1953    ) -> Result<(), ValidateContextError> {
1954        for spending_input in spending_inputs.iter() {
1955            let spent_outpoint = if let Some(spent_outpoint) = spending_input.outpoint() {
1956                spent_outpoint
1957            } else {
1958                continue;
1959            };
1960
1961            #[cfg(feature = "indexer")]
1962            let insert_value = *spending_tx_hash;
1963            #[cfg(not(feature = "indexer"))]
1964            let insert_value = ();
1965
1966            // Index the spent outpoint in the chain
1967            let was_spend_newly_inserted = self
1968                .spent_utxos
1969                .insert(spent_outpoint, insert_value)
1970                .is_none();
1971            assert!(
1972                was_spend_newly_inserted,
1973                "unexpected duplicate spent output: should be checked earlier"
1974            );
1975
1976            // TODO: fix tests to supply correct spent outputs, then turn this into an expect()
1977            let spent_output = if let Some(spent_output) = spent_outputs.get(&spent_outpoint) {
1978                spent_output
1979            } else if !cfg!(test) {
1980                panic!("unexpected missing spent output: all spent outputs must be indexed");
1981            } else {
1982                continue;
1983            };
1984
1985            // Index the spent output for the address
1986            if let Some(spending_address) = spent_output.utxo.output.address(&self.network) {
1987                let address_transfers = self
1988                    .partial_transparent_transfers
1989                    .entry(spending_address)
1990                    .or_default();
1991
1992                address_transfers.update_chain_tip_with(&(
1993                    spending_input,
1994                    spending_tx_hash,
1995                    spent_output,
1996                ))?;
1997            }
1998        }
1999
2000        Ok(())
2001    }
2002
2003    fn revert_chain_with(
2004        &mut self,
2005        &(spending_inputs, spending_tx_hash, spent_outputs): &(
2006            &Vec<transparent::Input>,
2007            &transaction::Hash,
2008            &HashMap<transparent::OutPoint, transparent::OrderedUtxo>,
2009        ),
2010        position: RevertPosition,
2011    ) {
2012        for spending_input in spending_inputs.iter() {
2013            let spent_outpoint = if let Some(spent_outpoint) = spending_input.outpoint() {
2014                spent_outpoint
2015            } else {
2016                continue;
2017            };
2018
2019            // Revert the spent outpoint in the chain
2020            let was_spent_outpoint_removed = self.spent_utxos.remove(&spent_outpoint).is_some();
2021            assert!(
2022                was_spent_outpoint_removed,
2023                "spent_utxos must be present if block was added to chain"
2024            );
2025
2026            // TODO: fix tests to supply correct spent outputs, then turn this into an expect()
2027            let spent_output = if let Some(spent_output) = spent_outputs.get(&spent_outpoint) {
2028                spent_output
2029            } else if !cfg!(test) {
2030                panic!(
2031                    "unexpected missing reverted spent output: all spent outputs must be indexed"
2032                );
2033            } else {
2034                continue;
2035            };
2036
2037            // Revert the spent output for the address
2038            if let Some(receiving_address) = spent_output.utxo.output.address(&self.network) {
2039                let address_transfers = self
2040                    .partial_transparent_transfers
2041                    .get_mut(&receiving_address)
2042                    .expect("block has previously been applied to the chain");
2043
2044                address_transfers
2045                    .revert_chain_with(&(spending_input, spending_tx_hash, spent_output), position);
2046
2047                // Remove this transfer if it is now empty
2048                if address_transfers.is_empty() {
2049                    self.partial_transparent_transfers
2050                        .remove(&receiving_address);
2051                }
2052            }
2053        }
2054    }
2055}
2056
2057impl
2058    UpdateWith<(
2059        &Option<transaction::JoinSplitData<Groth16Proof>>,
2060        &SpendingTransactionId,
2061    )> for Chain
2062{
2063    #[instrument(skip(self, joinsplit_data))]
2064    fn update_chain_tip_with(
2065        &mut self,
2066        &(joinsplit_data, revealing_tx_id): &(
2067            &Option<transaction::JoinSplitData<Groth16Proof>>,
2068            &SpendingTransactionId,
2069        ),
2070    ) -> Result<(), ValidateContextError> {
2071        if let Some(joinsplit_data) = joinsplit_data {
2072            // We do note commitment tree updates in parallel rayon threads.
2073
2074            check::nullifier::add_to_non_finalized_chain_unique(
2075                &mut self.sprout_nullifiers,
2076                joinsplit_data.nullifiers(),
2077                *revealing_tx_id,
2078            )?;
2079        }
2080        Ok(())
2081    }
2082
2083    /// # Panics
2084    ///
2085    /// Panics if any nullifier is missing from the chain when we try to remove it.
2086    ///
2087    /// See [`check::nullifier::remove_from_non_finalized_chain`] for details.
2088    #[instrument(skip(self, joinsplit_data))]
2089    fn revert_chain_with(
2090        &mut self,
2091        &(joinsplit_data, _revealing_tx_id): &(
2092            &Option<transaction::JoinSplitData<Groth16Proof>>,
2093            &SpendingTransactionId,
2094        ),
2095        _position: RevertPosition,
2096    ) {
2097        if let Some(joinsplit_data) = joinsplit_data {
2098            // Note commitments are removed from the Chain during a fork,
2099            // by removing trees above the fork height from the note commitment index.
2100            // This happens when reverting the block itself.
2101
2102            check::nullifier::remove_from_non_finalized_chain(
2103                &mut self.sprout_nullifiers,
2104                joinsplit_data.nullifiers(),
2105            );
2106        }
2107    }
2108}
2109
2110impl<AnchorV>
2111    UpdateWith<(
2112        &Option<sapling::ShieldedData<AnchorV>>,
2113        &SpendingTransactionId,
2114    )> for Chain
2115where
2116    AnchorV: sapling::AnchorVariant + Clone,
2117{
2118    #[instrument(skip(self, sapling_shielded_data))]
2119    fn update_chain_tip_with(
2120        &mut self,
2121        &(sapling_shielded_data, revealing_tx_id): &(
2122            &Option<sapling::ShieldedData<AnchorV>>,
2123            &SpendingTransactionId,
2124        ),
2125    ) -> Result<(), ValidateContextError> {
2126        if let Some(sapling_shielded_data) = sapling_shielded_data {
2127            // We do note commitment tree updates in parallel rayon threads.
2128
2129            check::nullifier::add_to_non_finalized_chain_unique(
2130                &mut self.sapling_nullifiers,
2131                sapling_shielded_data.nullifiers(),
2132                *revealing_tx_id,
2133            )?;
2134        }
2135        Ok(())
2136    }
2137
2138    /// # Panics
2139    ///
2140    /// Panics if any nullifier is missing from the chain when we try to remove it.
2141    ///
2142    /// See [`check::nullifier::remove_from_non_finalized_chain`] for details.
2143    #[instrument(skip(self, sapling_shielded_data))]
2144    fn revert_chain_with(
2145        &mut self,
2146        &(sapling_shielded_data, _revealing_tx_id): &(
2147            &Option<sapling::ShieldedData<AnchorV>>,
2148            &SpendingTransactionId,
2149        ),
2150        _position: RevertPosition,
2151    ) {
2152        if let Some(sapling_shielded_data) = sapling_shielded_data {
2153            // Note commitments are removed from the Chain during a fork,
2154            // by removing trees above the fork height from the note commitment index.
2155            // This happens when reverting the block itself.
2156
2157            check::nullifier::remove_from_non_finalized_chain(
2158                &mut self.sapling_nullifiers,
2159                sapling_shielded_data.nullifiers(),
2160            );
2161        }
2162    }
2163}
2164
2165impl UpdateWith<(&Option<orchard::ShieldedData>, &SpendingTransactionId)> for Chain {
2166    #[instrument(skip(self, orchard_shielded_data))]
2167    fn update_chain_tip_with(
2168        &mut self,
2169        &(orchard_shielded_data, revealing_tx_id): &(
2170            &Option<orchard::ShieldedData>,
2171            &SpendingTransactionId,
2172        ),
2173    ) -> Result<(), ValidateContextError> {
2174        if let Some(orchard_shielded_data) = orchard_shielded_data {
2175            // We do note commitment tree updates in parallel rayon threads.
2176
2177            check::nullifier::add_to_non_finalized_chain_unique(
2178                &mut self.orchard_nullifiers,
2179                orchard_shielded_data.nullifiers(),
2180                *revealing_tx_id,
2181            )?;
2182        }
2183        Ok(())
2184    }
2185
2186    /// # Panics
2187    ///
2188    /// Panics if any nullifier is missing from the chain when we try to remove it.
2189    ///
2190    /// See [`check::nullifier::remove_from_non_finalized_chain`] for details.
2191    #[instrument(skip(self, orchard_shielded_data))]
2192    fn revert_chain_with(
2193        &mut self,
2194        (orchard_shielded_data, _revealing_tx_id): &(
2195            &Option<orchard::ShieldedData>,
2196            &SpendingTransactionId,
2197        ),
2198        _position: RevertPosition,
2199    ) {
2200        if let Some(orchard_shielded_data) = orchard_shielded_data {
2201            // Note commitments are removed from the Chain during a fork,
2202            // by removing trees above the fork height from the note commitment index.
2203            // This happens when reverting the block itself.
2204
2205            check::nullifier::remove_from_non_finalized_chain(
2206                &mut self.orchard_nullifiers,
2207                orchard_shielded_data.nullifiers(),
2208            );
2209        }
2210    }
2211}
2212
2213impl UpdateWith<(ValueBalance<NegativeAllowed>, Height, usize)> for Chain {
2214    #[allow(clippy::unwrap_in_result)]
2215    fn update_chain_tip_with(
2216        &mut self,
2217        (block_value_pool_change, height, size): &(ValueBalance<NegativeAllowed>, Height, usize),
2218    ) -> Result<(), ValidateContextError> {
2219        match self
2220            .chain_value_pools
2221            .add_chain_value_pool_change(*block_value_pool_change)
2222        {
2223            Ok(chain_value_pools) => {
2224                self.chain_value_pools = chain_value_pools;
2225                self.block_info_by_height
2226                    .insert(*height, BlockInfo::new(chain_value_pools, *size as u32));
2227            }
2228            Err(value_balance_error) => Err(ValidateContextError::AddValuePool {
2229                value_balance_error,
2230                chain_value_pools: self.chain_value_pools,
2231                block_value_pool_change: *block_value_pool_change,
2232                height: Some(*height),
2233            })?,
2234        };
2235
2236        Ok(())
2237    }
2238
2239    /// Revert the chain state using a block chain value pool change.
2240    ///
2241    /// When forking from the tip, subtract the block's chain value pool change.
2242    ///
2243    /// When finalizing the root, leave the chain value pool balances unchanged.
2244    /// [`ChainInner::chain_value_pools`] tracks the chain value pools for all finalized blocks, and
2245    /// the non-finalized blocks in this chain. So finalizing the root doesn't change the set of
2246    /// blocks it tracks.
2247    ///
2248    /// # Panics
2249    ///
2250    /// Panics if the chain pool value balance is invalid after we subtract the block value pool
2251    /// change.
2252    fn revert_chain_with(
2253        &mut self,
2254        (block_value_pool_change, height, _size): &(ValueBalance<NegativeAllowed>, Height, usize),
2255        position: RevertPosition,
2256    ) {
2257        use std::ops::Neg;
2258
2259        if position == RevertPosition::Tip {
2260            self.chain_value_pools = self
2261                .chain_value_pools
2262                .add_chain_value_pool_change(block_value_pool_change.neg())
2263                .expect("reverting the tip will leave the pools in a previously valid state");
2264        }
2265        self.block_info_by_height.remove(height);
2266    }
2267}
2268
2269impl Ord for Chain {
2270    /// Chain order for the [`NonFinalizedState`][1]'s `chain_set`.
2271    ///
2272    /// Chains with higher cumulative Proof of Work are [`Ordering::Greater`],
2273    /// breaking ties using the tip block hash.
2274    ///
2275    /// Despite the consensus rules, Zebra uses the tip block hash as a
2276    /// tie-breaker. Zebra blocks are downloaded in parallel, so download
2277    /// timestamps may not be unique. (And Zebra currently doesn't track
2278    /// download times, because [`Block`](block::Block)s are immutable.)
2279    ///
2280    /// This departure from the consensus rules may delay network convergence,
2281    /// for as long as the greater hash belongs to the later mined block.
2282    /// But Zebra nodes should converge as soon as the tied work is broken.
2283    ///
2284    /// "At a given point in time, each full validator is aware of a set of candidate blocks.
2285    /// These form a tree rooted at the genesis block, where each node in the tree
2286    /// refers to its parent via the hashPrevBlock block header field.
2287    ///
2288    /// A path from the root toward the leaves of the tree consisting of a sequence
2289    /// of one or more valid blocks consistent with consensus rules,
2290    /// is called a valid block chain.
2291    ///
2292    /// In order to choose the best valid block chain in its view of the overall block tree,
2293    /// a node sums the work ... of all blocks in each valid block chain,
2294    /// and considers the valid block chain with greatest total work to be best.
2295    ///
2296    /// To break ties between leaf blocks, a node will prefer the block that it received first.
2297    ///
2298    /// The consensus protocol is designed to ensure that for any given block height,
2299    /// the vast majority of nodes should eventually agree on their best valid block chain
2300    /// up to that height."
2301    ///
2302    /// <https://zips.z.cash/protocol/protocol.pdf#blockchain>
2303    ///
2304    /// # Correctness
2305    ///
2306    /// `Chain::cmp` is used in a `BTreeSet`, so the fields accessed by `cmp` must not have
2307    /// interior mutability.
2308    ///
2309    /// # Panics
2310    ///
2311    /// If two chains compare equal.
2312    ///
2313    /// This panic enforces the [`NonFinalizedState::chain_set`][2] unique chain invariant.
2314    ///
2315    /// If the chain set contains duplicate chains, the non-finalized state might
2316    /// handle new blocks or block finalization incorrectly.
2317    ///
2318    /// [1]: super::NonFinalizedState
2319    /// [2]: super::NonFinalizedState::chain_set
2320    fn cmp(&self, other: &Self) -> Ordering {
2321        if self.partial_cumulative_work != other.partial_cumulative_work {
2322            self.partial_cumulative_work
2323                .cmp(&other.partial_cumulative_work)
2324        } else {
2325            let self_hash = self
2326                .blocks
2327                .values()
2328                .last()
2329                .expect("always at least 1 element")
2330                .hash;
2331
2332            let other_hash = other
2333                .blocks
2334                .values()
2335                .last()
2336                .expect("always at least 1 element")
2337                .hash;
2338
2339            // This comparison is a tie-breaker within the local node, so it does not need to
2340            // be consistent with the ordering on `ExpandedDifficulty` and `block::Hash`.
2341            match self_hash.0.cmp(&other_hash.0) {
2342                Ordering::Equal => unreachable!("Chain tip block hashes are always unique"),
2343                ordering => ordering,
2344            }
2345        }
2346    }
2347}
2348
2349impl PartialOrd for Chain {
2350    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
2351        Some(self.cmp(other))
2352    }
2353}
2354
2355impl PartialEq for Chain {
2356    /// Chain equality for [`NonFinalizedState::chain_set`][1], using proof of
2357    /// work, then the tip block hash as a tie-breaker.
2358    ///
2359    /// # Panics
2360    ///
2361    /// If two chains compare equal.
2362    ///
2363    /// See [`Chain::cmp`] for details.
2364    ///
2365    /// [1]: super::NonFinalizedState::chain_set
2366    fn eq(&self, other: &Self) -> bool {
2367        self.partial_cmp(other) == Some(Ordering::Equal)
2368    }
2369}
2370
2371impl Eq for Chain {}
2372
2373#[cfg(test)]
2374impl Chain {
2375    /// Inserts the supplied Sapling note commitment subtree into the chain.
2376    pub(crate) fn insert_sapling_subtree(
2377        &mut self,
2378        subtree: NoteCommitmentSubtree<sapling::tree::Node>,
2379    ) {
2380        self.inner
2381            .sapling_subtrees
2382            .insert(subtree.index, subtree.into_data());
2383    }
2384
2385    /// Inserts the supplied Orchard note commitment subtree into the chain.
2386    pub(crate) fn insert_orchard_subtree(
2387        &mut self,
2388        subtree: NoteCommitmentSubtree<orchard::tree::Node>,
2389    ) {
2390        self.inner
2391            .orchard_subtrees
2392            .insert(subtree.index, subtree.into_data());
2393    }
2394}