penumbra_sdk_tct/tree.rs
1use std::{
2 fmt::{Debug, Display},
3 sync::Arc,
4};
5
6use decaf377::Fq;
7use penumbra_sdk_proto::{penumbra::crypto::tct::v1 as pb, DomainType};
8
9use crate::error::*;
10use crate::prelude::{Witness as _, *};
11use crate::Witness;
12
13#[path = "epoch.rs"]
14pub(crate) mod epoch;
15pub(crate) use epoch::block;
16
17/// A sparse merkle tree witnessing up to 65,536 epochs of up to 65,536 blocks of up to 65,536
18/// [`Commitment`]s.
19#[derive(Debug, Clone, Serialize, Deserialize)]
20pub struct Tree {
21 index: HashedMap<StateCommitment, index::within::Tree>,
22 inner: Arc<frontier::Top<frontier::Tier<frontier::Tier<frontier::Item>>>>,
23}
24
25impl Default for Tree {
26 fn default() -> Self {
27 Self {
28 index: HashedMap::default(),
29 inner: Arc::new(frontier::Top::new(frontier::TrackForgotten::Yes)),
30 }
31 }
32}
33
34impl PartialEq for Tree {
35 fn eq(&self, other: &Tree) -> bool {
36 self.position() == other.position() // two trees could have identical contents but different positions
37 && self.root() == other.root() // if the roots match, they represent the same commitments, but may witness different ones
38 && self.index == other.index // we ensure they witness the same commitments by checking equality of indices
39 }
40}
41
42impl Eq for Tree {}
43
44/// The root hash of a [`Tree`].
45#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
46#[serde(try_from = "pb::MerkleRoot", into = "pb::MerkleRoot")]
47#[cfg_attr(any(test, feature = "arbitrary"), derive(proptest_derive::Arbitrary))]
48pub struct Root(pub Hash);
49
50impl Root {
51 /// Check if this is the root of an empty tree.
52 pub fn is_empty(&self) -> bool {
53 self.0 == Hash::zero()
54 }
55}
56
57impl From<Root> for Fq {
58 fn from(root: Root) -> Self {
59 root.0.into()
60 }
61}
62
63/// An error occurred when decoding a tree root from bytes.
64#[derive(Debug, Clone, Copy, PartialEq, Eq, Error)]
65#[error("could not decode tree root")]
66pub struct RootDecodeError;
67
68impl TryFrom<pb::MerkleRoot> for Root {
69 type Error = RootDecodeError;
70
71 fn try_from(root: pb::MerkleRoot) -> Result<Root, Self::Error> {
72 let bytes: [u8; 32] = (&root.inner[..]).try_into().map_err(|_| RootDecodeError)?;
73 let inner = Fq::from_bytes_checked(&bytes).map_err(|_| RootDecodeError)?;
74 Ok(Root(Hash::new(inner)))
75 }
76}
77
78impl From<Root> for pb::MerkleRoot {
79 fn from(root: Root) -> Self {
80 Self {
81 inner: Fq::from(root.0).to_bytes().to_vec(),
82 }
83 }
84}
85
86impl DomainType for Root {
87 type Proto = pb::MerkleRoot;
88}
89
90impl Display for Root {
91 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
92 write!(f, "{}", hex::encode(Fq::from(self.0).to_bytes()))
93 }
94}
95
96/// The index of a [`Commitment`] within a [`Tree`].
97#[derive(
98 Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, Default,
99)]
100#[cfg_attr(any(test, feature = "arbitrary"), derive(proptest_derive::Arbitrary))]
101pub struct Position(index::within::Tree);
102
103impl Position {
104 /// The index of the [`Commitment`] to which this [`Position`] refers within its own block.
105 pub fn commitment(&self) -> u16 {
106 self.0.commitment.into()
107 }
108
109 /// The index of the block to which this [`Position`] refers within its own epoch.
110 pub fn block(&self) -> u16 {
111 self.0.block.into()
112 }
113
114 /// The index of the epoch to which this [`Position`] refers within its [`Tree`].
115 pub fn epoch(&self) -> u16 {
116 self.0.epoch.into()
117 }
118}
119
120impl From<Position> for u64 {
121 fn from(position: Position) -> Self {
122 position.0.into()
123 }
124}
125
126impl From<u64> for Position {
127 fn from(position: u64) -> Self {
128 Position(position.into())
129 }
130}
131
132impl From<(u16, u16, u16)> for Position {
133 fn from((epoch, block, commitment): (u16, u16, u16)) -> Self {
134 Position(index::within::Tree {
135 epoch: epoch.into(),
136 block: block.into(),
137 commitment: commitment.into(),
138 })
139 }
140}
141
142impl From<Position> for (u16, u16, u16) {
143 fn from(position: Position) -> Self {
144 (position.epoch(), position.block(), position.commitment())
145 }
146}
147
148impl Tree {
149 /// Create a new empty [`Tree`] for storing all commitments to the end of time.
150 pub fn new() -> Self {
151 Self::default()
152 }
153
154 // Assemble a tree from its two parts without checking any invariants.
155 pub(crate) fn unchecked_from_parts(
156 index: HashedMap<StateCommitment, index::within::Tree>,
157 inner: frontier::Top<frontier::Tier<frontier::Tier<frontier::Item>>>,
158 ) -> Self {
159 Self {
160 index,
161 inner: Arc::new(inner),
162 }
163 }
164
165 /// Get the root hash of this [`Tree`].
166 ///
167 /// Internal hashing is performed lazily to prevent unnecessary intermediary hashes from being
168 /// computed, so the first hash returned after a long sequence of insertions may take more time
169 /// than subsequent calls.
170 ///
171 /// Computed hashes are cached so that subsequent calls without further modification are very
172 /// fast.
173 #[instrument(level = "trace", skip(self))]
174 pub fn root(&self) -> Root {
175 let root = Root(self.inner.hash());
176 trace!(?root);
177 root
178 }
179
180 /// Add a new [`Commitment`] to the most recent block of the most recent epoch of this [`Tree`].
181 ///
182 /// If successful, returns the [`Position`] at which the commitment was inserted.
183 ///
184 /// # Errors
185 ///
186 /// Returns [`InsertError`] if any of:
187 ///
188 /// - the [`Tree`] is full,
189 /// - the current epoch is full, or
190 /// - the current block is full.
191 #[instrument(level = "trace", skip(self))]
192 pub fn insert(
193 &mut self,
194 witness: Witness,
195 commitment: StateCommitment,
196 ) -> Result<Position, InsertError> {
197 let item = match witness {
198 Witness::Keep => commitment.into(),
199 Witness::Forget => Hash::of(commitment).into(),
200 };
201
202 // Get the position of the insertion, if it would succeed
203 let position = (self.inner.position().ok_or(InsertError::Full)?).into();
204
205 // Try to insert the commitment into the latest block
206 Arc::make_mut(&mut self.inner)
207 .update(|epoch| {
208 epoch
209 .update(|block| {
210 // Don't insert into a finalized block (this will fail); create a new one
211 // instead (below)
212 if block.is_finalized() {
213 return None;
214 }
215
216 Some(block
217 .insert(item)
218 .map_err(|_| InsertError::BlockFull))
219 })
220 .flatten()
221 // If the latest block was finalized already or doesn't exist, create a new block and
222 // insert into that block
223 .or_else(|| {
224 // Don't insert into a finalized epoch (this will fail); create a new one
225 // instead (below)
226 if epoch.is_finalized() {
227 return None;
228 }
229
230 Some(epoch
231 .insert(frontier::Tier::new(item))
232 .map_err(|_| InsertError::EpochFull))
233 })
234 })
235 .flatten()
236 // If the latest epoch was finalized already or doesn't exist, create a new epoch and
237 // insert into that epoch
238 .unwrap_or_else(|| {
239 Arc::make_mut(&mut self.inner)
240 .insert(frontier::Tier::new(frontier::Tier::new(item)))
241 .expect("inserting a commitment must succeed because we already checked that the tree is not full");
242 Ok(())
243 })
244 .map_err(|error| {
245 error!(%error); error
246 })?;
247
248 // Keep track of the position of this just-inserted commitment in the index, if it was
249 // slated to be kept
250 if let Witness::Keep = witness {
251 if let Some(replaced) = self.index.insert(commitment, position) {
252 // This case is handled for completeness, but should not happen in
253 // practice because commitments should be unique
254 let forgotten = Arc::make_mut(&mut self.inner).forget(replaced);
255 debug_assert!(forgotten);
256 }
257 }
258
259 let position = Position(position);
260 trace!(?position);
261 Ok(position)
262 }
263
264 /// Get a [`Proof`] of inclusion for the commitment at this index in the tree.
265 ///
266 /// If the index is not witnessed in this tree, return `None`.
267 #[instrument(level = "trace", skip(self))]
268 pub fn witness(&self, commitment: StateCommitment) -> Option<Proof> {
269 let &index = if let Some(index) = self.index.get(&commitment) {
270 index
271 } else {
272 trace!("not witnessed");
273 return None;
274 };
275
276 let (auth_path, leaf) = match self.inner.witness(index) {
277 Some(witness) => witness,
278 None => panic!(
279 "commitment `{commitment:?}` at position `{index:?}` must be witnessed because it is indexed"
280 ),
281 };
282
283 debug_assert_eq!(leaf, Hash::of(commitment));
284
285 let proof = Proof(crate::internal::proof::Proof {
286 position: index.into(),
287 auth_path,
288 leaf: commitment,
289 });
290
291 trace!(?index, ?proof);
292 Some(proof)
293 }
294
295 /// Forget about the witness for the given [`Commitment`].
296 ///
297 /// Returns `true` if the commitment was previously witnessed (and now is forgotten), and `false` if
298 /// it was not witnessed.
299 #[instrument(level = "trace", skip(self))]
300 pub fn forget(&mut self, commitment: StateCommitment) -> bool {
301 let mut forgotten = false;
302
303 if let Some(&within_epoch) = self.index.get(&commitment) {
304 // We forgot something
305 forgotten = true;
306 // Forget the index for this element in the tree
307 let forgotten = Arc::make_mut(&mut self.inner).forget(within_epoch);
308 debug_assert!(forgotten);
309 // Remove this entry from the index
310 self.index.remove(&commitment);
311 }
312
313 trace!(?forgotten);
314 forgotten
315 }
316
317 /// Get the position in this [`Tree`] of the given [`Commitment`], if it is currently witnessed.
318 #[instrument(level = "trace", skip(self))]
319 pub fn position_of(&self, commitment: StateCommitment) -> Option<Position> {
320 let position = self.index.get(&commitment).map(|index| Position(*index));
321 trace!(?position);
322 position
323 }
324
325 /// Add a new block all at once to the most recently inserted epoch of this [`Tree`], returning
326 /// the block root of the finalized block.
327 ///
328 /// This can be used for two purposes:
329 ///
330 /// 1. to insert a [`block::Root`] into the tree as a stand-in for an entire un-witnessed block,
331 /// or
332 /// 2. to insert a [`block::Builder`] into the tree that was constructed separately.
333 ///
334 /// The latter [`block::Builder`] API only accelerates tree construction when used in parallel,
335 /// but the former [`block::Root`] insertion can be used to accelerate the construction of a
336 /// tree even in a single thread, because if the root is already known, only one set of hashes
337 /// need be performed, rather than performing hashing for each commitment in the block.
338 ///
339 /// This function can be called on anything that implements `Into<block::Finalized>`, in
340 /// particular:
341 ///
342 /// - [`block::Root`] (treated as a finalized block with no witnessed commitments).
343 /// - [`block::Builder`] (the block is finalized as it is inserted), and of course
344 /// - [`block::Finalized`].
345 ///
346 /// # Errors
347 ///
348 /// Returns [`InsertBlockError`] containing the inserted block without adding it to the [`Tree`]
349 /// if the [`Tree`] is full or the current epoch is full.
350 #[instrument(level = "trace", skip(self, block))]
351 pub fn insert_block(
352 &mut self,
353 block: impl Into<block::Finalized>,
354 ) -> Result<block::Root, InsertBlockError> {
355 // We split apart the inside so that we get the right instrumentation when this is called as
356 // an inner function in `end_block`
357 let block_root = self.insert_block_uninstrumented(block).map_err(|error| {
358 error!(%error);
359 error
360 })?;
361 trace!(?block_root);
362 Ok(block_root)
363 }
364
365 fn insert_block_uninstrumented(
366 &mut self,
367 block: impl Into<block::Finalized>,
368 ) -> Result<block::Root, InsertBlockError> {
369 let block::Finalized { inner, index } = block.into();
370
371 // Convert the top level inside of the block to a tier that can be slotted into the epoch
372 // We have this be an `Option` because we need to `take` out of it inside closures
373 let mut inner: Option<frontier::Tier<_>> = Some(match inner {
374 Insert::Keep(inner) => inner.into(),
375 Insert::Hash(hash) => hash.into(),
376 });
377
378 // We have this be an `Option` because we need to `take` out of it in closures
379 let mut index = Some(index);
380
381 // Finalize the latest block, if it exists and is not yet finalized -- this means that
382 // position calculations will be correct, since they will start at the next block
383 Arc::make_mut(&mut self.inner).update(|epoch| epoch.update(|block| block.finalize()));
384
385 // Get the epoch and block index of the next insertion
386 let position = self.inner.position();
387
388 // Insert the block into the latest epoch, or create a new epoch for it if the latest epoch
389 // does not exist or is finalized
390 let block_root = Arc::make_mut(&mut self.inner)
391 .update(|epoch| {
392 // If the epoch is finalized, create a new one (below) to insert the block into
393 if epoch.is_finalized() {
394 return None;
395 }
396
397 if epoch.is_full() {
398 // The current epoch would be full when we tried to insert into it
399 return Some(Err(InsertBlockError::EpochFull(block::Finalized {
400 inner: inner
401 .take()
402 .expect("inner option should be Some")
403 .finalize_owned()
404 .map(Into::into),
405 index: index.take().expect("index option should be Some"),
406 })));
407 }
408
409 // Get the inner thing from the `Option` storage
410 let inner = inner.take().expect("inner option should be Some");
411
412 // Calculate the block root
413 let block_root = block::Root(inner.hash());
414
415 epoch
416 .insert(inner)
417 .expect("inserting into the current epoch must succeed when it is not full");
418
419 Some(Ok(block_root))
420 })
421 .flatten()
422 .unwrap_or_else(|| {
423 if self.inner.is_full() {
424 return Err(InsertBlockError::Full(block::Finalized {
425 inner: inner
426 .take()
427 .expect("inner option should be Some")
428 .finalize_owned()
429 .map(Into::into),
430 index: index.take().expect("index option should be Some"),
431 }));
432 }
433
434 // Get the inner thing from the `Option` storage
435 let inner = inner.take().expect("inner option should be Some");
436
437 // Calculate the block root
438 let block_root = block::Root(inner.hash());
439
440 // Create a new epoch and insert the block into it
441 Arc::make_mut(&mut self.inner)
442 .insert(frontier::Tier::new(inner))
443 .expect("inserting a new epoch must succeed when the tree is not full");
444
445 Ok(block_root)
446 })?;
447
448 // Extract from the position we recorded earlier what the epoch/block indexes for each
449 // inserted commitment should be
450 let index::within::Tree { epoch, block, .. } = position
451 .expect("insertion succeeded so position must exist")
452 .into();
453
454 // Add the index of all commitments in the block to the global index
455 for (c, index::within::Block { commitment }) in
456 index.take().expect("index option should be Some")
457 {
458 // If any commitment is repeated, forget the previous one within the tree, since it is
459 // now inaccessible
460 if let Some(replaced) = self.index.insert(
461 c,
462 index::within::Tree {
463 epoch,
464 block,
465 commitment,
466 },
467 ) {
468 // This case is handled for completeness, but should not happen in practice because
469 // commitments should be unique
470 let forgotten = Arc::make_mut(&mut self.inner).forget(replaced);
471 debug_assert!(forgotten);
472 }
473 }
474
475 Ok(block_root)
476 }
477
478 /// Explicitly mark the end of the current block in this tree, advancing the position to the
479 /// next block, and returning the root of the block which was just finalized.
480 #[instrument(level = "trace", skip(self))]
481 pub fn end_block(&mut self) -> Result<block::Root, InsertBlockError> {
482 // Check to see if the latest block is already finalized, and finalize it if
483 // it is not
484 let (already_finalized, finalized_root) = Arc::make_mut(&mut self.inner)
485 .update(|epoch| {
486 epoch.update(|tier| match tier.finalize() {
487 true => (true, block::Finalized::default().root()),
488 false => (false, block::Root(tier.hash())),
489 })
490 })
491 .flatten()
492 // If the entire tree or the latest epoch is empty or finalized, the latest block is
493 // considered already finalized
494 .unwrap_or((true, block::Finalized::default().root()));
495
496 // If the latest block was already finalized (i.e. we are at the start of an unfinalized
497 // empty block), insert an empty finalized block
498 if already_finalized {
499 self.insert_block_uninstrumented(block::Finalized::default())
500 .map_err(|error| {
501 error!(%error);
502 error
503 })?;
504 };
505
506 trace!(finalized_block_root = ?finalized_root);
507 Ok(finalized_root)
508 }
509
510 /// Get the root hash of the most recent block in the most recent epoch of this [`Tree`].
511 #[instrument(level = "trace", skip(self))]
512 pub fn current_block_root(&self) -> block::Root {
513 let root = self
514 .inner
515 .focus()
516 .and_then(|epoch| {
517 let block = epoch.focus()?;
518 if block.is_finalized() {
519 None
520 } else {
521 Some(block::Root(block.hash()))
522 }
523 })
524 // If there is no latest unfinalized block, we return the hash of the empty unfinalized block
525 .unwrap_or_else(|| block::Builder::default().root());
526 trace!(?root);
527 root
528 }
529
530 /// Add a new epoch all at once to this [`Tree`], returning the root of the finalized epoch
531 /// which was inserted.
532 ///
533 /// This can be used for two purposes:
534 ///
535 /// 1. to insert an [`epoch::Root`] into the tree as a stand-in for an entire un-witnessed block,
536 /// or
537 /// 2. to insert an [`epoch::Builder`] into the tree that was constructed separately.
538 ///
539 /// The latter [`epoch::Builder`] API only accelerates tree construction when used in parallel,
540 /// but the former [`epoch::Root`] insertion can be used to accelerate the construction of a
541 /// tree even in a single thread, because if the root is already known, only one set of hashes
542 /// need be performed, rather than performing hashing for each commitment in the epoch.
543 ///
544 /// This function can be called on anything that implements `Into<epoch::Finalized>`, in
545 /// particular:
546 ///
547 /// - [`epoch::Root`] (treated as a finalized epoch with no witnessed commitments).
548 /// - [`epoch::Builder`] (the epoch is finalized as it is inserted), and of course
549 /// - [`epoch::Finalized`].
550 ///
551 /// # Errors
552 ///
553 /// Returns [`InsertEpochError`] containing the epoch without adding it to the [`Tree`] if the
554 /// [`Tree`] is full.
555 #[instrument(level = "trace", skip(self, epoch))]
556 pub fn insert_epoch(
557 &mut self,
558 epoch: impl Into<epoch::Finalized>,
559 ) -> Result<epoch::Root, InsertEpochError> {
560 // We split apart the inside so that we get the right instrumention when this is called as
561 // an inner function in `end_epoch`
562 let epoch_root = self.insert_epoch_uninstrumented(epoch).map_err(|error| {
563 error!(%error);
564 error
565 })?;
566 trace!(?epoch_root);
567 Ok(epoch_root)
568 }
569
570 fn insert_epoch_uninstrumented(
571 &mut self,
572 epoch: impl Into<epoch::Finalized>,
573 ) -> Result<epoch::Root, InsertEpochError> {
574 let epoch::Finalized { inner, index } = epoch.into();
575
576 // If the insertion would fail, return an error
577 if self.inner.is_full() {
578 // There is no room for another epoch to be inserted into the tree
579 return Err(InsertEpochError(epoch::Finalized { inner, index }));
580 }
581
582 // Convert the top level inside of the epoch to a tier that can be slotted into the tree
583 let inner: frontier::Tier<frontier::Tier<frontier::Item>> = match inner {
584 Insert::Keep(inner) => inner.into(),
585 Insert::Hash(hash) => hash.into(),
586 };
587
588 // Finalize the latest epoch, if it exists and is not yet finalized -- this means that
589 // position calculations will be correct, since they will start at the next epoch
590 Arc::make_mut(&mut self.inner).update(|epoch| epoch.finalize());
591
592 // Get the epoch index of the next insertion
593 let index::within::Tree { epoch, .. } = self
594 .inner
595 .position()
596 .expect("tree must have a position because it is not full")
597 .into();
598
599 // Calculate the root of the finalized epoch we're about to insert
600 let epoch_root = epoch::Root(inner.hash());
601
602 // Insert the inner tree of the epoch into the global tree
603 Arc::make_mut(&mut self.inner)
604 .insert(inner)
605 .expect("inserting an epoch must succeed when tree is not full");
606
607 // Add the index of all commitments in the epoch to the global tree index
608 for (c, index::within::Epoch { block, commitment }) in index {
609 // If any commitment is repeated, forget the previous one within the tree, since it is
610 // now inaccessible
611 if let Some(replaced) = self.index.insert(
612 c,
613 index::within::Tree {
614 epoch,
615 block,
616 commitment,
617 },
618 ) {
619 // This case is handled for completeness, but should not happen in practice because
620 // commitments should be unique
621 let forgotten = Arc::make_mut(&mut self.inner).forget(replaced);
622 debug_assert!(forgotten);
623 }
624 }
625
626 Ok(epoch_root)
627 }
628
629 /// Explicitly mark the end of the current epoch in this tree, advancing the position to the
630 /// next epoch, and returning the root of the epoch which was just finalized.
631 #[instrument(level = "trace", skip(self))]
632 pub fn end_epoch(&mut self) -> Result<epoch::Root, InsertEpochError> {
633 // Check to see if the latest block is already finalized, and finalize it if
634 // it is not
635 let (already_finalized, finalized_root) = Arc::make_mut(&mut self.inner)
636 .update(|tier| match tier.finalize() {
637 true => (true, epoch::Finalized::default().root()),
638 false => (false, epoch::Root(tier.hash())),
639 })
640 // If there is no focused block, the latest block is considered already finalized
641 .unwrap_or((true, epoch::Finalized::default().root()));
642
643 // If the latest block was already finalized (i.e. we are at the start of an unfinalized
644 // empty block), insert an empty finalized block
645 if already_finalized {
646 self.insert_epoch_uninstrumented(epoch::Finalized::default())
647 .map_err(|error| {
648 error!(%error);
649 error
650 })?;
651 };
652
653 trace!(finalized_epoch_root = ?finalized_root);
654 Ok(finalized_root)
655 }
656
657 /// Get the root hash of the most recent epoch in this [`Tree`].
658 #[instrument(level = "trace", skip(self))]
659 pub fn current_epoch_root(&self) -> epoch::Root {
660 let root = self
661 .inner
662 .focus()
663 .and_then(|epoch| {
664 if epoch.is_finalized() {
665 None
666 } else {
667 Some(epoch::Root(epoch.hash()))
668 }
669 })
670 // In the case where there is no latest unfinalized epoch, we return the hash of the
671 // empty unfinalized epoch
672 .unwrap_or_else(|| epoch::Builder::default().root());
673 trace!(?root);
674 root
675 }
676
677 /// The position in this [`Tree`] at which the next [`Commitment`] would be inserted.
678 ///
679 /// If the [`Tree`] is full, returns `None`.
680 ///
681 /// The maximum capacity of a [`Tree`] is 281,474,976,710,656 = 65,536 epochs of 65,536
682 /// blocks of 65,536 [`Commitment`]s.
683 ///
684 /// Note that [`forget`](Tree::forget)ting a commitment does not decrease this; it only
685 /// decreases the [`witnessed_count`](Tree::witnessed_count).
686 #[instrument(level = "trace", skip(self))]
687 pub fn position(&self) -> Option<Position> {
688 let position = self.inner.position().map(|p| Position(p.into()));
689 trace!(?position);
690 position
691 }
692
693 /// The count of how many commitments have been forgotten explicitly using
694 /// [`forget`](Tree::forget), or implicitly by being overwritten by a subsequent insertion of
695 /// the _same_ commitment (this case is rare in practice).
696 ///
697 /// This does not include commitments that were inserted using [`Witness::Forget`], only those
698 /// forgotten subsequent to their insertion.
699 #[instrument(level = "trace", skip(self))]
700 pub fn forgotten(&self) -> Forgotten {
701 let forgotten = self
702 .inner
703 .forgotten()
704 .expect("inner `Top` of `Tree` must always be in forgotten-tracking mode");
705 trace!(?forgotten);
706 forgotten
707 }
708
709 /// The number of [`Commitment`]s currently witnessed in this [`Tree`].
710 ///
711 /// Note that [`forget`](Tree::forget)ting a commitment decreases this count, but does not
712 /// decrease the [`position`](Tree::position) of the next inserted [`Commitment`].
713 #[instrument(level = "trace", skip(self))]
714 pub fn witnessed_count(&self) -> usize {
715 let count = self.index.len();
716 trace!(?count);
717 count
718 }
719
720 /// Check whether this [`Tree`] is empty.
721 #[instrument(level = "trace", skip(self))]
722 pub fn is_empty(&self) -> bool {
723 let is_empty = self.inner.is_empty();
724 trace!(?is_empty);
725 is_empty
726 }
727
728 /// Get an iterator over all commitments currently witnessed in the tree, **ordered by
729 /// position**.
730 ///
731 /// Unlike [`commitments_unordered`](Tree::commitments_unordered), this guarantees that
732 /// commitments will be returned in order, but it may be slower by a constant factor.
733 #[instrument(level = "trace", skip(self))]
734 pub fn commitments(
735 &self,
736 ) -> impl Iterator<Item = (Position, StateCommitment)> + Send + Sync + '_ {
737 crate::storage::serialize::Serializer::default().commitments(self)
738 }
739
740 /// Get an iterator over all commitments currently witnessed in the tree.
741 ///
742 /// Unlike [`commitments`](Tree::commitments), this **does not** guarantee that commitments will
743 /// be returned in order, but it may be faster by a constant factor.
744 #[instrument(level = "trace", skip(self))]
745 pub fn commitments_unordered(
746 &self,
747 ) -> impl Iterator<Item = (StateCommitment, Position)> + Send + Sync + '_ {
748 self.index.iter().map(|(c, p)| (*c, Position(*p)))
749 }
750
751 /// Get a dynamic representation of the internal structure of the tree, which can be traversed
752 /// and inspected arbitrarily.
753 pub fn structure(&self) -> structure::Node {
754 let _structure_span = trace_span!("structure");
755 // TODO: use the structure span for instrumenting methods of the structure, as it is traversed
756 Node::root(&*self.inner)
757 }
758
759 /// Deserialize a tree from a [`storage::Read`] of its contents, without checking for internal
760 /// consistency.
761 ///
762 /// This can be more convenient than [`Tree::load`], since it is able to internally query the
763 /// storage for the last position and forgotten count.
764 ///
765 /// ⚠️ **WARNING:** Do not deserialize trees you did not serialize yourself, or risk violating
766 /// internal invariants.
767 pub fn from_reader<R: Read>(reader: &mut R) -> Result<Tree, R::Error> {
768 storage::deserialize::from_reader(reader)
769 }
770
771 /// Serialize the tree incrementally from the last stored [`Position`] and [`Forgotten`]
772 /// specified, into a [`storage::Write`], performing only the operations necessary to serialize
773 /// the changes to the tree.
774 ///
775 /// This can be more convenient than using [`Tree::updates`], because it is able to internally
776 /// query the storage for the last position and forgotten count, and drive the storage
777 /// operations itself.
778 pub fn to_writer<W: Write>(&self, writer: &mut W) -> Result<(), W::Error> {
779 storage::serialize::to_writer(writer, self)
780 }
781
782 /// Deserialize a tree from a [`storage::AsyncRead`] of its contents, without checking for
783 /// internal consistency.
784 ///
785 /// This can be more convenient than [`Tree::load`], since it is able to internally query the
786 /// storage for the last position and forgotten count.
787 ///
788 /// ⚠️ **WARNING:** Do not deserialize trees you did not serialize yourself, or risk violating
789 /// internal invariants.
790 pub async fn from_async_reader<R: AsyncRead>(reader: &mut R) -> Result<Tree, R::Error> {
791 storage::deserialize::from_async_reader(reader).await
792 }
793
794 /// Serialize the tree incrementally from the last stored [`Position`] and [`Forgotten`]
795 /// specified, into a [`storage::AsyncWrite`], performing only the operations necessary to
796 /// serialize the changes to the tree.
797 ///
798 /// This can be more convenient than using [`Tree::updates`], because it is able to internally
799 /// query the storage for the last position and forgotten count, and drive the storage
800 /// operations itself.
801 pub async fn to_async_writer<W: AsyncWrite>(&self, writer: &mut W) -> Result<(), W::Error> {
802 storage::serialize::to_async_writer(writer, self).await
803 }
804
805 /// Deserialize a tree using externally driven iteration, without checking for internal
806 /// consistency.
807 ///
808 /// Reconstructing a [`Tree`] using this method requires stepping through a series of states, as
809 /// follows:
810 ///
811 /// 1. [`Tree::load`] returns an object [`LoadCommitments`](storage::LoadCommitments) which can
812 /// be used to [`insert`](storage::LoadCommitments::insert) positioned commitments.
813 /// 2. When all commitments have been inserted, call
814 /// [`.load_hashes()`](storage::LoadCommitments::load_hashes) to get an object
815 /// [`LoadHashes`](storage::LoadHashes).
816 /// 3. [`LoadHashes`](storage::LoadHashes) can be used to
817 /// [`insert`](storage::LoadHashes::insert) positioned, heighted hashes.
818 /// 4. Finally, call [`.finish()`](storage::LoadHashes::finish) on the
819 /// [`LoadHashes`](storage::LoadHashes) to get the [`Tree`].
820 ///
821 /// ⚠️ **WARNING:** Do not deserialize trees you did not serialize yourself, or risk violating
822 /// internal invariants. You *must* insert all the commitments and hashes corresponding to the
823 /// stored tree, or the reconstructed tree will not match what was serialized, and further, it
824 /// may have internal inconsistencies that will mean that the proofs it produces will not
825 /// verify.
826 ///
827 /// ℹ️ **NOTE:** You may prefer to use [`from_reader`](Tree::from_reader) or
828 /// [`from_async_reader`](Tree::from_async_reader), which drive the iteration over the
829 /// underlying storage *internally* rather than requiring the caller to drive the iteration.
830 /// [`Tree::load`] is predominanly useful in circumstances when this inversion of control does
831 /// not make sense.
832 pub fn load(
833 last_position: impl Into<StoredPosition>,
834 last_forgotten: Forgotten,
835 ) -> storage::deserialize::LoadCommitments {
836 storage::deserialize::LoadCommitments::new(last_position, last_forgotten)
837 }
838
839 /// Serialize the tree incrementally from the last stored [`Position`] and [`Forgotten`]
840 /// specified, into an iterator of [`storage::Update`]s.
841 ///
842 /// This returns only the operations necessary to serialize the changes to the tree,
843 /// synchronizing the in-memory representation with what is stored.
844 ///
845 /// The iterator of updates may be [`.collect()`](Iterator::collect)ed into a
846 /// [`storage::Updates`], which is more compact in-memory than
847 /// [`.collect()`](Iterator::collect)ing into a [`Vec<Update>`](Vec).
848 ///
849 /// ℹ️ **NOTE:** You may prefer to use [`to_writer`](Tree::to_writer) or
850 /// [`to_async_writer`](Tree::to_async_writer), which drive the operations on the underlying
851 /// storage *internally* rather than requiring the caller to drive iteration. [`Tree::updates`]
852 /// is predominantly useful in circumstances when this inversion of control does not make sense.
853 pub fn updates(
854 &self,
855 last_position: impl Into<StoredPosition>,
856 last_forgotten: Forgotten,
857 ) -> impl Iterator<Item = Update> + Send + Sync + '_ {
858 storage::serialize::updates(last_position.into(), last_forgotten, self)
859 }
860}
861
862impl From<frontier::Top<frontier::Tier<frontier::Tier<frontier::Item>>>> for Tree {
863 fn from(inner: frontier::Top<frontier::Tier<frontier::Tier<frontier::Item>>>) -> Self {
864 let mut index = HashedMap::default();
865
866 // Traverse the tree to reconstruct the index
867 let mut stack = vec![Node::root(&inner)];
868 while let Some(node) = stack.pop() {
869 stack.extend(node.children());
870
871 if let structure::Kind::Leaf {
872 commitment: Some(commitment),
873 } = node.kind()
874 {
875 index.insert(commitment, node.position().0);
876 }
877 }
878
879 Self {
880 inner: Arc::new(inner),
881 index,
882 }
883 }
884}