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::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 #[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 #[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 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 let new_tree = Self::from_block(&self.network, block, sapling_root, orchard_root)?;
245 *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 self.peaks.insert(self.size, entry);
262 self.size += 1;
263 }
264 self.prune()?;
265 self.current_height = height;
266 Ok(())
267 }
268
269 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 fn prune(&mut self) -> Result<(), io::Error> {
285 let mut peak_pos_set = HashSet::new();
292
293 let mut alt = (32 - (self.size + 1).leading_zeros() - 1) - 1;
308
309 let mut peak_pos = (1 << (alt + 1)) - 2;
314
315 loop {
332 if peak_pos >= self.size {
335 peak_pos -= 1 << alt;
337 alt -= 1;
338 }
339
340 if peak_pos < self.size {
342 peak_pos_set.insert(peak_pos);
344
345 peak_pos = peak_pos + (1 << (alt + 1)) - 1;
347 }
348
349 if alt == 0 {
350 break;
351 }
352 }
353
354 self.peaks.retain(|k, _| peak_pos_set.contains(k));
356 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 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 pub fn peaks(&self) -> &BTreeMap<u32, Entry> {
390 &self.peaks
391 }
392
393 pub fn size(&self) -> u32 {
395 self.size
396 }
397
398 pub fn current_height(&self) -> Height {
400 self.current_height
401 }
402
403 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#[derive(Debug, Default, Clone)]
447pub struct HistoryTree(Option<NonEmptyHistoryTree>);
448
449impl HistoryTree {
450 #[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 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 #[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 *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 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 {}