penumbra_sdk_tct/
block.rs

1use std::fmt::Display;
2use std::sync::Arc;
3
4use decaf377::Fq;
5use hash_hasher::HashedMap;
6use penumbra_sdk_proto::{penumbra::crypto::tct::v1 as pb, DomainType};
7use serde::{Deserialize, Serialize};
8
9use crate::error::block::*;
10use crate::{prelude::*, Witness};
11
12/// A sparse merkle tree to witness up to 65,536 individual [`Commitment`]s.
13///
14/// This is one block in an [`epoch`](crate::builder::epoch), which is one epoch in a [`Tree`].
15#[derive(Derivative, Debug, Clone, Serialize, Deserialize)]
16pub struct Builder {
17    index: HashedMap<StateCommitment, index::within::Block>,
18    inner: Arc<frontier::Top<Item>>,
19}
20
21impl Default for Builder {
22    fn default() -> Self {
23        Self {
24            index: HashedMap::default(),
25            inner: Arc::new(frontier::Top::new(frontier::TrackForgotten::No)),
26        }
27    }
28}
29
30/// A finalized block builder, ready to be inserted into an [`epoch::Builder`](super::Builder) or a
31/// [`Tree`].
32#[derive(Derivative, Debug, Clone, Serialize, Deserialize)]
33pub struct Finalized {
34    pub(in super::super) index: HashedMap<StateCommitment, index::within::Block>,
35    pub(in super::super) inner: Insert<complete::Top<complete::Item>>,
36}
37
38impl Default for Finalized {
39    fn default() -> Self {
40        Builder::default().finalize()
41    }
42}
43
44impl Finalized {
45    /// Get the root hash of this finalized block.
46    ///
47    /// Internal hashing is performed lazily to prevent unnecessary intermediary hashes from being
48    /// computed, so the first hash returned after a long sequence of insertions may take more time
49    /// than subsequent calls.
50    ///
51    /// Computed hashes are cached so that subsequent calls without further modification are very
52    /// fast.
53    pub fn root(&self) -> Root {
54        Root(self.inner.hash())
55    }
56}
57
58impl From<Root> for Finalized {
59    fn from(root: Root) -> Self {
60        Self {
61            index: HashedMap::default(),
62            inner: Insert::Hash(root.0),
63        }
64    }
65}
66
67impl From<Builder> for Finalized {
68    fn from(mut builder: Builder) -> Self {
69        builder.finalize()
70    }
71}
72
73/// The root hash of a block.
74#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
75#[serde(try_from = "pb::MerkleRoot", into = "pb::MerkleRoot")]
76#[cfg_attr(any(test, feature = "arbitrary"), derive(proptest_derive::Arbitrary))]
77pub struct Root(pub Hash);
78
79impl Root {
80    /// Check if this is the root of an empty finalized block.
81    pub fn is_empty_finalized(&self) -> bool {
82        self.0 == Hash::one()
83    }
84
85    /// Check if this is the root of an empty unfinalized block.
86    pub fn is_empty_unfinalized(&self) -> bool {
87        self.0 == Hash::zero()
88    }
89}
90
91impl From<Root> for Fq {
92    fn from(root: Root) -> Self {
93        root.0.into()
94    }
95}
96
97impl TryFrom<pb::MerkleRoot> for Root {
98    type Error = RootDecodeError;
99
100    fn try_from(root: pb::MerkleRoot) -> Result<Root, Self::Error> {
101        let bytes: [u8; 32] = (&root.inner[..]).try_into().map_err(|_| RootDecodeError)?;
102        let inner = Fq::from_bytes_checked(&bytes).map_err(|_| RootDecodeError)?;
103        Ok(Root(Hash::new(inner)))
104    }
105}
106
107impl From<Root> for pb::MerkleRoot {
108    fn from(root: Root) -> Self {
109        Self {
110            inner: Fq::from(root.0).to_bytes().to_vec(),
111        }
112    }
113}
114
115impl DomainType for Root {
116    type Proto = pb::MerkleRoot;
117}
118
119impl Display for Root {
120    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
121        write!(f, "{}", hex::encode(Fq::from(self.0).to_bytes()))
122    }
123}
124
125impl Builder {
126    /// Create a new empty [`block::Builder`](Builder).
127    pub fn new() -> Self {
128        Self::default()
129    }
130
131    /// Add a new [`Commitment`] to this [`block::Builder`](Builder).
132    ///
133    /// # Errors
134    ///
135    /// Returns [`InsertError`] if the block is full.
136    pub fn insert(
137        &mut self,
138        witness: Witness,
139        commitment: StateCommitment,
140    ) -> Result<(), InsertError> {
141        let item = match witness {
142            Witness::Keep => commitment.into(),
143            Witness::Forget => Hash::of(commitment).into(),
144        };
145
146        // Get the position of the insertion, if it would succeed
147        let position = u16::try_from(self.inner.position().ok_or(InsertError)?)
148            .expect("position of block is never greater than `u16::MAX`")
149            .into();
150
151        // Insert the commitment into the inner tree
152        Arc::make_mut(&mut self.inner)
153            .insert(item)
154            .expect("inserting a commitment must succeed when block has a position");
155
156        // Keep track of the position of this just-inserted commitment in the index, if it was
157        // slated to be kept
158        if let Witness::Keep = witness {
159            if let Some(replaced) = self.index.insert(commitment, position) {
160                // This case is handled for completeness, but should not happen in
161                // practice because commitments should be unique
162                let forgotten = Arc::make_mut(&mut self.inner).forget(replaced);
163                debug_assert!(forgotten);
164            }
165        }
166
167        Ok(())
168    }
169
170    /// Get the root hash of this block builder.
171    ///
172    /// Note that this root hash will differ from the root hash of the finalized block.
173    pub fn root(&self) -> Root {
174        Root(self.inner.hash())
175    }
176
177    /// Finalize this block builder returning a finalized block and resetting the underlying builder
178    /// to the initial empty state.
179    pub fn finalize(&mut self) -> Finalized {
180        let this = std::mem::take(self);
181
182        // This avoids cloning the arc when we have the only reference to it
183        let inner = Arc::try_unwrap(this.inner).unwrap_or_else(|arc| (*arc).clone());
184
185        let inner = inner.finalize();
186        let index = this.index;
187        Finalized { index, inner }
188    }
189}
190
191#[cfg(test)]
192mod test {
193    use super::*;
194
195    #[test]
196    fn insert_error_sync_send() {
197        static_assertions::assert_impl_all!(InsertError: Sync, Send);
198    }
199}