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