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 | NetworkUpgrade::Nu6 => {
106 let tree = Tree::<OrchardOnward>::new_from_cache(
107 network,
108 network_upgrade,
109 size,
110 &peaks,
111 &Default::default(),
112 )?;
113 InnerHistoryTree::OrchardOnward(tree)
114 }
115 };
116 Ok(Self {
117 network: network.clone(),
118 network_upgrade,
119 inner,
120 size,
121 peaks: peaks.into(),
122 current_height,
123 })
124 }
125
126 #[allow(clippy::unwrap_in_result)]
132 pub fn from_block(
133 network: &Network,
134 block: Arc<Block>,
135 sapling_root: &sapling::tree::Root,
136 orchard_root: &orchard::tree::Root,
137 ) -> Result<Self, HistoryTreeError> {
138 let height = block
139 .coinbase_height()
140 .expect("block must have coinbase height during contextual verification");
141 let network_upgrade = NetworkUpgrade::current(network, height);
142 let (tree, entry) = match network_upgrade {
143 NetworkUpgrade::Genesis
144 | NetworkUpgrade::BeforeOverwinter
145 | NetworkUpgrade::Overwinter
146 | NetworkUpgrade::Sapling
147 | NetworkUpgrade::Blossom => {
148 panic!("HistoryTree does not exist for pre-Heartwood upgrades")
149 }
150 NetworkUpgrade::Heartwood | NetworkUpgrade::Canopy => {
151 let (tree, entry) = Tree::<PreOrchard>::new_from_block(
152 network,
153 block,
154 sapling_root,
155 &Default::default(),
156 )?;
157 (InnerHistoryTree::PreOrchard(tree), entry)
158 }
159 NetworkUpgrade::Nu5 | NetworkUpgrade::Nu6 => {
160 let (tree, entry) = Tree::<OrchardOnward>::new_from_block(
161 network,
162 block,
163 sapling_root,
164 orchard_root,
165 )?;
166 (InnerHistoryTree::OrchardOnward(tree), entry)
167 }
168 };
169 let mut peaks = BTreeMap::new();
170 peaks.insert(0u32, entry);
171 Ok(NonEmptyHistoryTree {
172 network: network.clone(),
173 network_upgrade,
174 inner: tree,
175 size: 1,
176 peaks: peaks.into(),
177 current_height: height,
178 })
179 }
180
181 #[allow(clippy::unwrap_in_result)]
191 pub fn push(
192 &mut self,
193 block: Arc<Block>,
194 sapling_root: &sapling::tree::Root,
195 orchard_root: &orchard::tree::Root,
196 ) -> Result<(), HistoryTreeError> {
197 let height = block
201 .coinbase_height()
202 .expect("block must have coinbase height during contextual verification");
203
204 assert!(
205 Some(height) == self.current_height + 1,
206 "added block with height {:?} but it must be {:?}+1",
207 height,
208 self.current_height
209 );
210
211 let network_upgrade = NetworkUpgrade::current(&self.network, height);
212 if network_upgrade != self.network_upgrade {
213 let new_tree = Self::from_block(&self.network, block, sapling_root, orchard_root)?;
216 *self = new_tree;
218 assert_eq!(self.network_upgrade, network_upgrade);
219 return Ok(());
220 }
221
222 let new_entries = match &mut self.inner {
223 InnerHistoryTree::PreOrchard(tree) => tree
224 .append_leaf(block, sapling_root, orchard_root)
225 .map_err(|e| HistoryTreeError::InnerError { inner: e })?,
226 InnerHistoryTree::OrchardOnward(tree) => tree
227 .append_leaf(block, sapling_root, orchard_root)
228 .map_err(|e| HistoryTreeError::InnerError { inner: e })?,
229 };
230 for entry in new_entries {
231 self.peaks.insert(self.size, entry);
233 self.size += 1;
234 }
235 self.prune()?;
236 self.current_height = height;
237 Ok(())
238 }
239
240 pub fn try_extend<
242 'a,
243 T: IntoIterator<Item = (Arc<Block>, &'a sapling::tree::Root, &'a orchard::tree::Root)>,
244 >(
245 &mut self,
246 iter: T,
247 ) -> Result<(), HistoryTreeError> {
248 for (block, sapling_root, orchard_root) in iter {
249 self.push(block, sapling_root, orchard_root)?;
250 }
251 Ok(())
252 }
253
254 fn prune(&mut self) -> Result<(), io::Error> {
256 let mut peak_pos_set = HashSet::new();
263
264 let mut alt = (32 - (self.size + 1).leading_zeros() - 1) - 1;
279
280 let mut peak_pos = (1 << (alt + 1)) - 2;
285
286 loop {
303 if peak_pos >= self.size {
306 peak_pos -= 1 << alt;
308 alt -= 1;
309 }
310
311 if peak_pos < self.size {
313 peak_pos_set.insert(peak_pos);
315
316 peak_pos = peak_pos + (1 << (alt + 1)) - 1;
318 }
319
320 if alt == 0 {
321 break;
322 }
323 }
324
325 self.peaks.retain(|k, _| peak_pos_set.contains(k));
327 self.inner = match self.inner {
329 InnerHistoryTree::PreOrchard(_) => {
330 InnerHistoryTree::PreOrchard(Tree::<PreOrchard>::new_from_cache(
331 &self.network,
332 self.network_upgrade,
333 self.size,
334 &self.peaks,
335 &Default::default(),
336 )?)
337 }
338 InnerHistoryTree::OrchardOnward(_) => {
339 InnerHistoryTree::OrchardOnward(Tree::<OrchardOnward>::new_from_cache(
340 &self.network,
341 self.network_upgrade,
342 self.size,
343 &self.peaks,
344 &Default::default(),
345 )?)
346 }
347 };
348 Ok(())
349 }
350
351 pub fn hash(&self) -> ChainHistoryMmrRootHash {
353 match &self.inner {
354 InnerHistoryTree::PreOrchard(tree) => tree.hash(),
355 InnerHistoryTree::OrchardOnward(tree) => tree.hash(),
356 }
357 }
358
359 pub fn peaks(&self) -> &BTreeMap<u32, Entry> {
361 &self.peaks
362 }
363
364 pub fn size(&self) -> u32 {
366 self.size
367 }
368
369 pub fn current_height(&self) -> Height {
371 self.current_height
372 }
373
374 pub fn network(&self) -> &Network {
376 &self.network
377 }
378}
379
380impl Clone for NonEmptyHistoryTree {
381 fn clone(&self) -> Self {
382 let tree = match self.inner {
383 InnerHistoryTree::PreOrchard(_) => InnerHistoryTree::PreOrchard(
384 Tree::<PreOrchard>::new_from_cache(
385 &self.network,
386 self.network_upgrade,
387 self.size,
388 &self.peaks,
389 &Default::default(),
390 )
391 .expect("rebuilding an existing tree should always work"),
392 ),
393 InnerHistoryTree::OrchardOnward(_) => InnerHistoryTree::OrchardOnward(
394 Tree::<OrchardOnward>::new_from_cache(
395 &self.network,
396 self.network_upgrade,
397 self.size,
398 &self.peaks,
399 &Default::default(),
400 )
401 .expect("rebuilding an existing tree should always work"),
402 ),
403 };
404 NonEmptyHistoryTree {
405 network: self.network.clone(),
406 network_upgrade: self.network_upgrade,
407 inner: tree,
408 size: self.size,
409 peaks: self.peaks.clone(),
410 current_height: self.current_height,
411 }
412 }
413}
414
415#[derive(Debug, Default, Clone)]
418pub struct HistoryTree(Option<NonEmptyHistoryTree>);
419
420impl HistoryTree {
421 #[allow(clippy::unwrap_in_result)]
425 pub fn from_block(
426 network: &Network,
427 block: Arc<Block>,
428 sapling_root: &sapling::tree::Root,
429 orchard_root: &orchard::tree::Root,
430 ) -> Result<Self, HistoryTreeError> {
431 let Some(heartwood_height) = NetworkUpgrade::Heartwood.activation_height(network) else {
432 return Ok(HistoryTree(None));
434 };
435
436 match block
437 .coinbase_height()
438 .expect("must have height")
439 .cmp(&heartwood_height)
440 {
441 std::cmp::Ordering::Less => Ok(HistoryTree(None)),
442 _ => Ok(
443 NonEmptyHistoryTree::from_block(network, block, sapling_root, orchard_root)?.into(),
444 ),
445 }
446 }
447
448 #[allow(clippy::unwrap_in_result)]
453 pub fn push(
454 &mut self,
455 network: &Network,
456 block: Arc<Block>,
457 sapling_root: &sapling::tree::Root,
458 orchard_root: &orchard::tree::Root,
459 ) -> Result<(), HistoryTreeError> {
460 let Some(heartwood_height) = NetworkUpgrade::Heartwood.activation_height(network) else {
461 assert!(
462 self.0.is_none(),
463 "history tree must not exist pre-Heartwood"
464 );
465
466 return Ok(());
467 };
468
469 match block
470 .coinbase_height()
471 .expect("must have height")
472 .cmp(&heartwood_height)
473 {
474 std::cmp::Ordering::Less => {
475 assert!(
476 self.0.is_none(),
477 "history tree must not exist pre-Heartwood"
478 );
479 }
480 std::cmp::Ordering::Equal => {
481 let tree = Some(NonEmptyHistoryTree::from_block(
482 network,
483 block,
484 sapling_root,
485 orchard_root,
486 )?);
487 *self = HistoryTree(tree);
489 }
490 std::cmp::Ordering::Greater => {
491 self.0
492 .as_mut()
493 .expect("history tree must exist Heartwood-onward")
494 .push(block.clone(), sapling_root, orchard_root)?;
495 }
496 };
497 Ok(())
498 }
499
500 pub fn hash(&self) -> Option<ChainHistoryMmrRootHash> {
502 Some(self.0.as_ref()?.hash())
503 }
504}
505
506impl From<NonEmptyHistoryTree> for HistoryTree {
507 fn from(tree: NonEmptyHistoryTree) -> Self {
508 HistoryTree(Some(tree))
509 }
510}
511
512impl From<Option<NonEmptyHistoryTree>> for HistoryTree {
513 fn from(tree: Option<NonEmptyHistoryTree>) -> Self {
514 HistoryTree(tree)
515 }
516}
517
518impl Deref for HistoryTree {
519 type Target = Option<NonEmptyHistoryTree>;
520 fn deref(&self) -> &Self::Target {
521 &self.0
522 }
523}
524
525impl PartialEq for HistoryTree {
526 fn eq(&self, other: &Self) -> bool {
527 self.as_ref().map(|tree| tree.hash()) == other.as_ref().map(|other_tree| other_tree.hash())
528 }
529}
530
531impl Eq for HistoryTree {}