1mod 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#[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 format!("{self:?}") == format!("{other:?}")
42 }
43}
44
45impl Eq for HistoryTreeError {}
46
47#[derive(Debug)]
49enum InnerHistoryTree {
50 PreOrchard(Tree<PreOrchard>),
52 OrchardOnward(Tree<OrchardOnward>),
54}
55
56#[derive(Debug)]
59pub struct NonEmptyHistoryTree {
60 network: Network,
61 network_upgrade: NetworkUpgrade,
62 inner: InnerHistoryTree,
66 size: u32,
68 peaks: SummaryDebug<BTreeMap<u32, Entry>>,
71 current_height: Height,
73}
74
75impl NonEmptyHistoryTree {
76 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 #[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 #[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 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 let new_tree = Self::from_block(&self.network, block, sapling_root, orchard_root)?;
247 *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 self.peaks.insert(self.size, entry);
264 self.size += 1;
265 }
266 self.prune()?;
267 self.current_height = height;
268 Ok(())
269 }
270
271 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 fn prune(&mut self) -> Result<(), io::Error> {
287 let mut peak_pos_set = HashSet::new();
294
295 let mut alt = (32 - (self.size + 1).leading_zeros() - 1) - 1;
310
311 let mut peak_pos = (1 << (alt + 1)) - 2;
316
317 loop {
334 if peak_pos >= self.size {
337 peak_pos -= 1 << alt;
339 alt -= 1;
340 }
341
342 if peak_pos < self.size {
344 peak_pos_set.insert(peak_pos);
346
347 peak_pos = peak_pos + (1 << (alt + 1)) - 1;
349 }
350
351 if alt == 0 {
352 break;
353 }
354 }
355
356 self.peaks.retain(|k, _| peak_pos_set.contains(k));
358 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 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 pub fn peaks(&self) -> &BTreeMap<u32, Entry> {
392 &self.peaks
393 }
394
395 pub fn size(&self) -> u32 {
397 self.size
398 }
399
400 pub fn current_height(&self) -> Height {
402 self.current_height
403 }
404
405 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#[derive(Debug, Default, Clone)]
449pub struct HistoryTree(Option<NonEmptyHistoryTree>);
450
451impl HistoryTree {
452 #[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 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 #[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 *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 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 {}