Skip to main content

zebra_chain/
history_tree.rs

1//! History tree (Merkle mountain range) structure that contains information about
2//! the block history as specified in ZIP-221.
3
4mod tests;
5
6use std::{
7    collections::{BTreeMap, HashSet},
8    io,
9    ops::Deref,
10    sync::Arc,
11};
12
13use thiserror::Error;
14
15use crate::{
16    block::{Block, ChainHistoryMmrRootHash, Height},
17    fmt::SummaryDebug,
18    orchard,
19    parameters::{Network, NetworkUpgrade},
20    primitives::zcash_history::{Entry, Tree, V1 as PreOrchard, V2 as OrchardOnward},
21    sapling,
22};
23
24/// An error describing why a history tree operation failed.
25#[derive(Debug, Error)]
26#[non_exhaustive]
27#[allow(missing_docs)]
28pub enum HistoryTreeError {
29    #[error("zcash_history error: {inner:?}")]
30    #[non_exhaustive]
31    InnerError { inner: zcash_history::Error },
32
33    #[error("I/O error: {0}")]
34    IOError(#[from] io::Error),
35}
36
37impl PartialEq for HistoryTreeError {
38    fn eq(&self, other: &Self) -> bool {
39        // Workaround since subtypes do not implement Eq.
40        // This is only used for tests anyway.
41        format!("{self:?}") == format!("{other:?}")
42    }
43}
44
45impl Eq for HistoryTreeError {}
46
47/// The inner [Tree] in one of its supported versions.
48#[derive(Debug)]
49enum InnerHistoryTree {
50    /// A pre-Orchard tree.
51    PreOrchard(Tree<PreOrchard>),
52    /// An Orchard-onward tree.
53    OrchardOnward(Tree<OrchardOnward>),
54}
55
56/// History tree (Merkle mountain range) structure that contains information about
57/// the block history, as specified in [ZIP-221](https://zips.z.cash/zip-0221).
58#[derive(Debug)]
59pub struct NonEmptyHistoryTree {
60    network: Network,
61    network_upgrade: NetworkUpgrade,
62    /// Merkle mountain range tree from `zcash_history`.
63    /// This is a "runtime" structure used to add / remove nodes, and it's not
64    /// persistent.
65    inner: InnerHistoryTree,
66    /// The number of nodes in the tree.
67    size: u32,
68    /// The peaks of the tree, indexed by their position in the array representation
69    /// of the tree. This can be persisted to save the tree.
70    peaks: SummaryDebug<BTreeMap<u32, Entry>>,
71    /// The height of the most recent block added to the tree.
72    current_height: Height,
73}
74
75impl NonEmptyHistoryTree {
76    /// Recreate a [`HistoryTree`] from previously saved data.
77    ///
78    /// The parameters must come from the values of [`NonEmptyHistoryTree::size`],
79    /// [`NonEmptyHistoryTree::peaks`] and [`NonEmptyHistoryTree::current_height`] of a HistoryTree.
80    pub fn from_cache(
81        network: &Network,
82        size: u32,
83        peaks: BTreeMap<u32, Entry>,
84        current_height: Height,
85    ) -> Result<Self, HistoryTreeError> {
86        let network_upgrade = NetworkUpgrade::current(network, current_height);
87        let inner = match network_upgrade {
88            NetworkUpgrade::Genesis
89            | NetworkUpgrade::BeforeOverwinter
90            | NetworkUpgrade::Overwinter
91            | NetworkUpgrade::Sapling
92            | NetworkUpgrade::Blossom => {
93                panic!("HistoryTree does not exist for pre-Heartwood upgrades")
94            }
95            NetworkUpgrade::Heartwood | NetworkUpgrade::Canopy => {
96                let tree = Tree::<PreOrchard>::new_from_cache(
97                    network,
98                    network_upgrade,
99                    size,
100                    &peaks,
101                    &Default::default(),
102                )?;
103                InnerHistoryTree::PreOrchard(tree)
104            }
105            NetworkUpgrade::Nu5
106            | NetworkUpgrade::Nu6
107            | NetworkUpgrade::Nu6_1
108            | NetworkUpgrade::Nu6_2
109            | NetworkUpgrade::Nu7 => {
110                let tree = Tree::<OrchardOnward>::new_from_cache(
111                    network,
112                    network_upgrade,
113                    size,
114                    &peaks,
115                    &Default::default(),
116                )?;
117                InnerHistoryTree::OrchardOnward(tree)
118            }
119
120            #[cfg(zcash_unstable = "zfuture")]
121            NetworkUpgrade::ZFuture => {
122                let tree = Tree::<OrchardOnward>::new_from_cache(
123                    network,
124                    network_upgrade,
125                    size,
126                    &peaks,
127                    &Default::default(),
128                )?;
129                InnerHistoryTree::OrchardOnward(tree)
130            }
131        };
132        Ok(Self {
133            network: network.clone(),
134            network_upgrade,
135            inner,
136            size,
137            peaks: peaks.into(),
138            current_height,
139        })
140    }
141
142    /// Create a new history tree with a single block.
143    ///
144    /// `sapling_root` is the root of the Sapling note commitment tree of the block.
145    /// `orchard_root` is the root of the Orchard note commitment tree of the block;
146    ///  (ignored for pre-Orchard blocks).
147    #[allow(clippy::unwrap_in_result)]
148    pub fn from_block(
149        network: &Network,
150        block: Arc<Block>,
151        sapling_root: &sapling::tree::Root,
152        orchard_root: &orchard::tree::Root,
153    ) -> Result<Self, HistoryTreeError> {
154        let height = block
155            .coinbase_height()
156            .expect("block must have coinbase height during contextual verification");
157        let network_upgrade = NetworkUpgrade::current(network, height);
158        let (tree, entry) = match network_upgrade {
159            NetworkUpgrade::Genesis
160            | NetworkUpgrade::BeforeOverwinter
161            | NetworkUpgrade::Overwinter
162            | NetworkUpgrade::Sapling
163            | NetworkUpgrade::Blossom => {
164                panic!("HistoryTree does not exist for pre-Heartwood upgrades")
165            }
166            NetworkUpgrade::Heartwood | NetworkUpgrade::Canopy => {
167                let (tree, entry) = Tree::<PreOrchard>::new_from_block(
168                    network,
169                    block,
170                    sapling_root,
171                    &Default::default(),
172                )?;
173                (InnerHistoryTree::PreOrchard(tree), entry)
174            }
175            NetworkUpgrade::Nu5
176            | NetworkUpgrade::Nu6
177            | NetworkUpgrade::Nu6_1
178            | NetworkUpgrade::Nu6_2
179            | NetworkUpgrade::Nu7 => {
180                let (tree, entry) = Tree::<OrchardOnward>::new_from_block(
181                    network,
182                    block,
183                    sapling_root,
184                    orchard_root,
185                )?;
186                (InnerHistoryTree::OrchardOnward(tree), entry)
187            }
188
189            #[cfg(zcash_unstable = "zfuture")]
190            NetworkUpgrade::ZFuture => {
191                let (tree, entry) = Tree::<OrchardOnward>::new_from_block(
192                    network,
193                    block,
194                    sapling_root,
195                    orchard_root,
196                )?;
197                (InnerHistoryTree::OrchardOnward(tree), entry)
198            }
199        };
200        let mut peaks = BTreeMap::new();
201        peaks.insert(0u32, entry);
202        Ok(NonEmptyHistoryTree {
203            network: network.clone(),
204            network_upgrade,
205            inner: tree,
206            size: 1,
207            peaks: peaks.into(),
208            current_height: height,
209        })
210    }
211
212    /// Add block data to the tree.
213    ///
214    /// `sapling_root` is the root of the Sapling note commitment tree of the block.
215    /// `orchard_root` is the root of the Orchard note commitment tree of the block;
216    ///  (ignored for pre-Orchard blocks).
217    ///
218    /// # Panics
219    ///
220    /// If the block height is not one more than the previously pushed block.
221    #[allow(clippy::unwrap_in_result)]
222    pub fn push(
223        &mut self,
224        block: Arc<Block>,
225        sapling_root: &sapling::tree::Root,
226        orchard_root: &orchard::tree::Root,
227    ) -> Result<(), HistoryTreeError> {
228        // Check if the block has the expected height.
229        // librustzcash assumes the heights are correct and corrupts the tree if they are wrong,
230        // resulting in a confusing error, which we prevent here.
231        let height = block
232            .coinbase_height()
233            .expect("block must have coinbase height during contextual verification");
234
235        assert!(
236            Some(height) == self.current_height + 1,
237            "added block with height {:?} but it must be {:?}+1",
238            height,
239            self.current_height
240        );
241
242        let network_upgrade = NetworkUpgrade::current(&self.network, height);
243        if network_upgrade != self.network_upgrade {
244            // This is the activation block of a network upgrade.
245            // Create a new tree.
246            let new_tree = Self::from_block(&self.network, block, sapling_root, orchard_root)?;
247            // Replaces self with the new tree
248            *self = new_tree;
249            assert_eq!(self.network_upgrade, network_upgrade);
250            return Ok(());
251        }
252
253        let new_entries = match &mut self.inner {
254            InnerHistoryTree::PreOrchard(tree) => tree
255                .append_leaf(block, sapling_root, orchard_root)
256                .map_err(|e| HistoryTreeError::InnerError { inner: e })?,
257            InnerHistoryTree::OrchardOnward(tree) => tree
258                .append_leaf(block, sapling_root, orchard_root)
259                .map_err(|e| HistoryTreeError::InnerError { inner: e })?,
260        };
261        for entry in new_entries {
262            // Not every entry is a peak; those will be trimmed later
263            self.peaks.insert(self.size, entry);
264            self.size += 1;
265        }
266        self.prune()?;
267        self.current_height = height;
268        Ok(())
269    }
270
271    /// Extend the history tree with the given blocks.
272    pub fn try_extend<
273        'a,
274        T: IntoIterator<Item = (Arc<Block>, &'a sapling::tree::Root, &'a orchard::tree::Root)>,
275    >(
276        &mut self,
277        iter: T,
278    ) -> Result<(), HistoryTreeError> {
279        for (block, sapling_root, orchard_root) in iter {
280            self.push(block, sapling_root, orchard_root)?;
281        }
282        Ok(())
283    }
284
285    /// Prune tree, removing all non-peak entries.
286    fn prune(&mut self) -> Result<(), io::Error> {
287        // Go through all the peaks of the tree.
288        // This code is based on a librustzcash example:
289        // https://github.com/zcash/librustzcash/blob/02052526925fba9389f1428d6df254d4dec967e6/zcash_history/examples/long.rs
290        // The explanation of how it works is from zcashd:
291        // https://github.com/zcash/zcash/blob/0247c0c682d59184a717a6536edb0d18834be9a7/src/coins.cpp#L351
292
293        let mut peak_pos_set = HashSet::new();
294
295        // Assume the following example peak layout with 14 leaves, and 25 stored nodes in
296        // total (the "tree length"):
297        //
298        //             P
299        //            /\
300        //           /  \
301        //          / \  \
302        //        /    \  \  Altitude
303        //     _A_      \  \    3
304        //   _/   \_     B  \   2
305        //  / \   / \   / \  C  1
306        // /\ /\ /\ /\ /\ /\ /\ 0
307        //
308        // We start by determining the altitude of the highest peak (A).
309        let mut alt = (32 - (self.size + 1).leading_zeros() - 1) - 1;
310
311        // We determine the position of the highest peak (A) by pretending it is the right
312        // sibling in a tree, and its left-most leaf has position 0. Then the left sibling
313        // of (A) has position -1, and so we can "jump" to the peak's position by computing
314        // -1 + 2^(alt + 1) - 1.
315        let mut peak_pos = (1 << (alt + 1)) - 2;
316
317        // Now that we have the position and altitude of the highest peak (A), we collect
318        // the remaining peaks (B, C). We navigate the peaks as if they were nodes in this
319        // Merkle tree (with additional imaginary nodes 1 and 2, that have positions beyond
320        // the MMR's length):
321        //
322        //             / \
323        //            /   \
324        //           /     \
325        //         /         \
326        //       A ==========> 1
327        //      / \          //  \
328        //    _/   \_       B ==> 2
329        //   /\     /\     /\    //
330        //  /  \   /  \   /  \   C
331        // /\  /\ /\  /\ /\  /\ /\
332        //
333        loop {
334            // If peak_pos is out of bounds of the tree, we compute the position of its left
335            // child, and drop down one level in the tree.
336            if peak_pos >= self.size {
337                // left child, -2^alt
338                peak_pos -= 1 << alt;
339                alt -= 1;
340            }
341
342            // If the peak exists, we take it and then continue with its right sibling.
343            if peak_pos < self.size {
344                // There is a peak at index `peak_pos`
345                peak_pos_set.insert(peak_pos);
346
347                // right sibling
348                peak_pos = peak_pos + (1 << (alt + 1)) - 1;
349            }
350
351            if alt == 0 {
352                break;
353            }
354        }
355
356        // Remove all non-peak entries
357        self.peaks.retain(|k, _| peak_pos_set.contains(k));
358        // Rebuild tree
359        self.inner = match self.inner {
360            InnerHistoryTree::PreOrchard(_) => {
361                InnerHistoryTree::PreOrchard(Tree::<PreOrchard>::new_from_cache(
362                    &self.network,
363                    self.network_upgrade,
364                    self.size,
365                    &self.peaks,
366                    &Default::default(),
367                )?)
368            }
369            InnerHistoryTree::OrchardOnward(_) => {
370                InnerHistoryTree::OrchardOnward(Tree::<OrchardOnward>::new_from_cache(
371                    &self.network,
372                    self.network_upgrade,
373                    self.size,
374                    &self.peaks,
375                    &Default::default(),
376                )?)
377            }
378        };
379        Ok(())
380    }
381
382    /// Return the hash of the tree root.
383    pub fn hash(&self) -> ChainHistoryMmrRootHash {
384        match &self.inner {
385            InnerHistoryTree::PreOrchard(tree) => tree.hash(),
386            InnerHistoryTree::OrchardOnward(tree) => tree.hash(),
387        }
388    }
389
390    /// Return the peaks of the tree.
391    pub fn peaks(&self) -> &BTreeMap<u32, Entry> {
392        &self.peaks
393    }
394
395    /// Return the (total) number of nodes in the tree.
396    pub fn size(&self) -> u32 {
397        self.size
398    }
399
400    /// Return the height of the last added block.
401    pub fn current_height(&self) -> Height {
402        self.current_height
403    }
404
405    /// Return the network where this tree is used.
406    pub fn network(&self) -> &Network {
407        &self.network
408    }
409}
410
411impl Clone for NonEmptyHistoryTree {
412    fn clone(&self) -> Self {
413        let tree = match self.inner {
414            InnerHistoryTree::PreOrchard(_) => InnerHistoryTree::PreOrchard(
415                Tree::<PreOrchard>::new_from_cache(
416                    &self.network,
417                    self.network_upgrade,
418                    self.size,
419                    &self.peaks,
420                    &Default::default(),
421                )
422                .expect("rebuilding an existing tree should always work"),
423            ),
424            InnerHistoryTree::OrchardOnward(_) => InnerHistoryTree::OrchardOnward(
425                Tree::<OrchardOnward>::new_from_cache(
426                    &self.network,
427                    self.network_upgrade,
428                    self.size,
429                    &self.peaks,
430                    &Default::default(),
431                )
432                .expect("rebuilding an existing tree should always work"),
433            ),
434        };
435        NonEmptyHistoryTree {
436            network: self.network.clone(),
437            network_upgrade: self.network_upgrade,
438            inner: tree,
439            size: self.size,
440            peaks: self.peaks.clone(),
441            current_height: self.current_height,
442        }
443    }
444}
445
446/// A History Tree that keeps track of its own creation in the Heartwood
447/// activation block, being empty beforehand.
448#[derive(Debug, Default, Clone)]
449pub struct HistoryTree(Option<NonEmptyHistoryTree>);
450
451impl HistoryTree {
452    /// Create a HistoryTree from a block.
453    ///
454    /// If the block is pre-Heartwood, it returns an empty history tree.
455    #[allow(clippy::unwrap_in_result)]
456    pub fn from_block(
457        network: &Network,
458        block: Arc<Block>,
459        sapling_root: &sapling::tree::Root,
460        orchard_root: &orchard::tree::Root,
461    ) -> Result<Self, HistoryTreeError> {
462        let Some(heartwood_height) = NetworkUpgrade::Heartwood.activation_height(network) else {
463            // Return early if there is no Heartwood activation height.
464            return Ok(HistoryTree(None));
465        };
466
467        match block
468            .coinbase_height()
469            .expect("must have height")
470            .cmp(&heartwood_height)
471        {
472            std::cmp::Ordering::Less => Ok(HistoryTree(None)),
473            _ => Ok(
474                NonEmptyHistoryTree::from_block(network, block, sapling_root, orchard_root)?.into(),
475            ),
476        }
477    }
478
479    /// Push a block to a maybe-existing HistoryTree, handling network upgrades.
480    ///
481    /// The tree is updated in-place. It is created when pushing the Heartwood
482    /// activation block.
483    #[allow(clippy::unwrap_in_result)]
484    pub fn push(
485        &mut self,
486        network: &Network,
487        block: Arc<Block>,
488        sapling_root: &sapling::tree::Root,
489        orchard_root: &orchard::tree::Root,
490    ) -> Result<(), HistoryTreeError> {
491        let Some(heartwood_height) = NetworkUpgrade::Heartwood.activation_height(network) else {
492            assert!(
493                self.0.is_none(),
494                "history tree must not exist pre-Heartwood"
495            );
496
497            return Ok(());
498        };
499
500        match block
501            .coinbase_height()
502            .expect("must have height")
503            .cmp(&heartwood_height)
504        {
505            std::cmp::Ordering::Less => {
506                assert!(
507                    self.0.is_none(),
508                    "history tree must not exist pre-Heartwood"
509                );
510            }
511            std::cmp::Ordering::Equal => {
512                let tree = Some(NonEmptyHistoryTree::from_block(
513                    network,
514                    block,
515                    sapling_root,
516                    orchard_root,
517                )?);
518                // Replace the current object with the new tree
519                *self = HistoryTree(tree);
520            }
521            std::cmp::Ordering::Greater => {
522                self.0
523                    .as_mut()
524                    .expect("history tree must exist Heartwood-onward")
525                    .push(block.clone(), sapling_root, orchard_root)?;
526            }
527        };
528        Ok(())
529    }
530
531    /// Return the hash of the tree root if the tree is not empty.
532    pub fn hash(&self) -> Option<ChainHistoryMmrRootHash> {
533        Some(self.0.as_ref()?.hash())
534    }
535}
536
537impl From<NonEmptyHistoryTree> for HistoryTree {
538    fn from(tree: NonEmptyHistoryTree) -> Self {
539        HistoryTree(Some(tree))
540    }
541}
542
543impl From<Option<NonEmptyHistoryTree>> for HistoryTree {
544    fn from(tree: Option<NonEmptyHistoryTree>) -> Self {
545        HistoryTree(tree)
546    }
547}
548
549impl Deref for HistoryTree {
550    type Target = Option<NonEmptyHistoryTree>;
551    fn deref(&self) -> &Self::Target {
552        &self.0
553    }
554}
555
556impl PartialEq for HistoryTree {
557    fn eq(&self, other: &Self) -> bool {
558        self.as_ref().map(|tree| tree.hash()) == other.as_ref().map(|other_tree| other_tree.hash())
559    }
560}
561
562impl Eq for HistoryTree {}