Skip to main content

miden_protocol/block/
blockchain.rs

1use alloc::collections::BTreeSet;
2
3use miden_crypto::merkle::mmr::{Forest, Mmr, MmrError, MmrPeaks, MmrProof, PartialMmr};
4
5use crate::Word;
6use crate::block::BlockNumber;
7use crate::utils::serde::{
8    ByteReader,
9    ByteWriter,
10    Deserializable,
11    DeserializationError,
12    Serializable,
13};
14
15/// The [Merkle Mountain Range](Mmr) defining the Miden blockchain.
16///
17/// The values of the leaves in the MMR are the commitments of blocks, i.e.
18/// [`BlockHeader::commitment`](crate::block::BlockHeader::commitment).
19///
20/// Each new block updates the blockchain by adding **the previous block's commitment** to the MMR.
21/// This means the chain commitment found in block 10's header commits to all blocks 0..=9, but not
22/// 10 itself. This results from the fact that block 10 cannot compute its own block commitment
23/// and thus cannot add itself to the chain. Hence, the blockchain is lagging behind by one block.
24///
25/// Some APIs take a _checkpoint_ which is equivalent to the concept of _forest_ of the underlying
26/// MMR. As an example, if the blockchain has 20 blocks in total, and the checkpoint is 10, then
27/// the API works in the context of the chain at the time it had 10 blocks, i.e. it contains blocks
28/// 0..=9. This is useful, for example, to retrieve proofs that are valid when verified against the
29/// chain commitment of block 10.
30///
31/// The maximum number of supported blocks is [`u32::MAX`]. This is not validated however.
32#[derive(Debug, Clone)]
33pub struct Blockchain {
34    mmr: Mmr,
35}
36
37impl Blockchain {
38    // CONSTRUCTORS
39    // --------------------------------------------------------------------------------------------
40
41    /// Returns a new, empty blockchain.
42    pub fn new() -> Self {
43        Self { mmr: Mmr::new() }
44    }
45
46    /// Construct a new blockchain from an [`Mmr`] without validation.
47    pub fn from_mmr_unchecked(mmr: Mmr) -> Self {
48        Self { mmr }
49    }
50
51    // PUBLIC ACCESSORS
52    // --------------------------------------------------------------------------------------------
53
54    /// Returns the number of blocks in the chain.
55    pub fn num_blocks(&self) -> u32 {
56        // SAFETY: The chain should never contain more than u32::MAX blocks, so a non-panicking cast
57        // should be fine.
58        self.mmr.forest().num_leaves() as u32
59    }
60
61    /// Returns the tip of the chain, i.e. the number of the latest block in the chain, unless the
62    /// chain is empty.
63    pub fn chain_tip(&self) -> Option<BlockNumber> {
64        if self.num_blocks() == 0 {
65            return None;
66        }
67
68        Some(BlockNumber::from(self.num_blocks() - 1))
69    }
70
71    /// Returns the chain commitment.
72    pub fn commitment(&self) -> Word {
73        self.peaks().hash_peaks()
74    }
75
76    /// Returns the current peaks of the MMR.
77    pub fn peaks(&self) -> MmrPeaks {
78        self.mmr.peaks()
79    }
80
81    /// Returns the peaks of the chain at the state of the given block.
82    ///
83    /// Note that this represents the state of the chain where the block at the given number **is
84    /// not yet** in the chain. For example, if the given block number is 5, then the returned peaks
85    /// represent the chain whose latest block is 4. See the type-level documentation for why this
86    /// is the case.
87    ///
88    /// # Errors
89    ///
90    /// Returns an error if the specified `block` exceeds the number of blocks in the chain.
91    pub fn peaks_at(&self, checkpoint: BlockNumber) -> Result<MmrPeaks, MmrError> {
92        let forest =
93            Forest::new(checkpoint.as_usize()).map_err(|_| MmrError::ForestSizeExceeded {
94                requested: checkpoint.as_usize(),
95                max: Forest::MAX_LEAVES,
96            })?;
97        self.mmr.peaks_at(forest)
98    }
99
100    /// Returns an [`MmrProof`] for the `block` with the given number.
101    ///
102    /// # Errors
103    ///
104    /// Returns an error if:
105    /// - The specified block number does not exist in the chain.
106    pub fn open(&self, block: BlockNumber) -> Result<MmrProof, MmrError> {
107        self.mmr.open(block.as_usize())
108    }
109
110    /// Returns an [`MmrProof`] for the `block` with the given number at the state of the given
111    /// `checkpoint`.
112    ///
113    /// # Errors
114    ///
115    /// Returns an error if:
116    /// - The specified block number does not exist in the chain.
117    /// - The specified checkpoint number exceeds the number of blocks in the chain.
118    pub fn open_at(
119        &self,
120        block: BlockNumber,
121        checkpoint: BlockNumber,
122    ) -> Result<MmrProof, MmrError> {
123        let forest =
124            Forest::new(checkpoint.as_usize()).map_err(|_| MmrError::ForestSizeExceeded {
125                requested: checkpoint.as_usize(),
126                max: Forest::MAX_LEAVES,
127            })?;
128        self.mmr.open_at(block.as_usize(), forest)
129    }
130
131    /// Returns a reference to the underlying [`Mmr`].
132    pub fn as_mmr(&self) -> &Mmr {
133        &self.mmr
134    }
135
136    /// Creates a [`PartialMmr`] at the state of the given block. This means the hashed peaks of the
137    /// returned partial MMR will match the checkpoint's chain commitment. This MMR will include
138    /// authentication paths for all blocks in the provided `blocks` set.
139    ///
140    /// # Errors
141    ///
142    /// Returns an error if:
143    /// - the specified `latest_block_number` exceeds the number of blocks in the chain.
144    /// - any block in `blocks` is not in the state of the chain specified by `latest_block_number`.
145    pub fn partial_mmr_from_blocks(
146        &self,
147        blocks: &BTreeSet<BlockNumber>,
148        checkpoint: BlockNumber,
149    ) -> Result<PartialMmr, MmrError> {
150        // Using latest block as the target state means we take the state of the MMR one before
151        // the latest block.
152        let peaks = self.peaks_at(checkpoint)?;
153
154        // Track the merkle paths of the requested blocks in the partial MMR.
155        let mut partial_mmr = PartialMmr::from_peaks(peaks);
156        for block_num in blocks.iter() {
157            let leaf = self.mmr.get(block_num.as_usize())?;
158            let proof = self.open_at(*block_num, checkpoint)?;
159
160            // SAFETY: We should be able to fill the partial MMR with data from the partial
161            // blockchain without errors, otherwise it indicates the blockchain is
162            // invalid.
163            partial_mmr
164                .track(block_num.as_usize(), leaf, proof.merkle_path())
165                .expect("filling partial mmr with data from mmr should succeed");
166        }
167
168        Ok(partial_mmr)
169    }
170
171    // PUBLIC MUTATORS
172    // --------------------------------------------------------------------------------------------
173
174    /// Adds a block commitment to the MMR.
175    ///
176    /// The caller must ensure that this commitent is the one for the next block in the chain.
177    ///
178    /// # Panics
179    ///
180    /// Panics if the number of blocks in the chain exceeds [Forest::MAX_LEAVES].
181    pub fn push(&mut self, block_commitment: Word) {
182        self.mmr
183            .add(block_commitment)
184            .expect("mmr leaf count exceeds forest leaf bound");
185    }
186}
187
188impl Default for Blockchain {
189    fn default() -> Self {
190        Self::new()
191    }
192}
193
194// SERIALIZATION
195// ================================================================================================
196
197impl Serializable for Blockchain {
198    fn write_into<W: ByteWriter>(&self, target: &mut W) {
199        self.mmr.write_into(target);
200    }
201}
202
203impl Deserializable for Blockchain {
204    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
205        let chain = Mmr::read_from(source)?;
206        Ok(Self::from_mmr_unchecked(chain))
207    }
208}