Skip to main content

miden_protocol/block/
proven_block.rs

1use miden_core::Word;
2use miden_crypto::dsa::ecdsa_k256_keccak::Signature;
3
4use crate::MIN_PROOF_SECURITY_LEVEL;
5use crate::block::{BlockBody, BlockHeader, BlockProof};
6use crate::utils::serde::{
7    ByteReader,
8    ByteWriter,
9    Deserializable,
10    DeserializationError,
11    Serializable,
12};
13
14// PROVEN BLOCK ERROR
15// ================================================================================================
16
17#[derive(Debug, thiserror::Error)]
18pub enum ProvenBlockError {
19    #[error(
20        "ECDSA signature verification failed based on the proven block's header commitment, validator public key and signature"
21    )]
22    InvalidSignature,
23    #[error(
24        "header tx commitment ({header_tx_commitment}) does not match body tx commitment ({body_tx_commitment})"
25    )]
26    TxCommitmentMismatch {
27        header_tx_commitment: Word,
28        body_tx_commitment: Word,
29    },
30    #[error(
31        "proven block header note root ({header_root}) does not match the corresponding body's note root ({body_root})"
32    )]
33    NoteRootMismatch { header_root: Word, body_root: Word },
34}
35
36// PROVEN BLOCK
37// ================================================================================================
38
39/// Represents a block in the Miden blockchain that has been signed and proven.
40///
41/// Blocks transition through proposed, signed, and proven states. This struct represents the final,
42/// proven state of a block.
43///
44/// Proven blocks are the final, canonical blocks in the chain.
45#[derive(Debug, Clone, PartialEq, Eq)]
46pub struct ProvenBlock {
47    /// The header of the proven block.
48    header: BlockHeader,
49
50    /// The body of the proven block.
51    body: BlockBody,
52
53    /// The Validator's signature over the block header.
54    signature: Signature,
55
56    /// The proof of the block.
57    proof: BlockProof,
58}
59
60impl ProvenBlock {
61    /// Returns a new [`ProvenBlock`] instantiated from the provided components.
62    ///
63    /// Validates that the provided components correspond to each other by verifying the signature,
64    /// and checking for matching transaction commitments and note roots.
65    ///
66    /// Involves non-trivial computation. Use [`Self::new_unchecked`] if the validation is not
67    /// necessary.
68    ///
69    /// Note: this does not fully validate the consistency of provided components. Specifically,
70    /// we cannot validate that:
71    /// - That applying the account updates in the block body to the account tree represented by the
72    ///   root from the previous block header would actually result in the account root in the
73    ///   provided header.
74    /// - That inserting the created nullifiers in the block body to the nullifier tree represented
75    ///   by the root from the previous block header would actually result in the nullifier root in
76    ///   the provided header.
77    ///
78    /// # Errors
79    /// Returns an error if:
80    /// - If the validator signature does not verify against the block header commitment and the
81    ///   validator key.
82    /// - If the transaction commitment in the block header is inconsistent with the transactions
83    ///   included in the block body.
84    /// - If the note root in the block header is inconsistent with the notes included in the block
85    ///   body.
86    pub fn new(
87        header: BlockHeader,
88        body: BlockBody,
89        signature: Signature,
90        proof: BlockProof,
91    ) -> Result<Self, ProvenBlockError> {
92        let proven_block = Self { header, signature, body, proof };
93
94        proven_block.validate()?;
95
96        Ok(proven_block)
97    }
98
99    /// Returns a new [`ProvenBlock`] instantiated from the provided components.
100    ///
101    /// # Warning
102    ///
103    /// This constructor does not do any validation as to whether the arguments correctly correspond
104    /// to each other, which could cause errors downstream.
105    pub fn new_unchecked(
106        header: BlockHeader,
107        body: BlockBody,
108        signature: Signature,
109        proof: BlockProof,
110    ) -> Self {
111        Self { header, signature, body, proof }
112    }
113
114    /// Validates that the components of the proven block correspond to each other by verifying the
115    /// signature, and checking for matching transaction commitments and note roots.
116    ///
117    /// Validation involves non-trivial computation, and depending on the size of the block may
118    /// take non-negligible amount of time.
119    ///
120    /// Note: this does not fully validate the consistency of internal components. Specifically,
121    /// we cannot validate that:
122    /// - That applying the account updates in the block body to the account tree represented by the
123    ///   root from the previous block header would actually result in the account root in the
124    ///   provided header.
125    /// - That inserting the created nullifiers in the block body to the nullifier tree represented
126    ///   by the root from the previous block header would actually result in the nullifier root in
127    ///   the provided header.
128    ///
129    /// # Errors
130    /// Returns an error if:
131    /// - If the validator signature does not verify against the block header commitment and the
132    ///   validator key.
133    /// - If the transaction commitment in the block header is inconsistent with the transactions
134    ///   included in the block body.
135    /// - If the note root in the block header is inconsistent with the notes included in the block
136    ///   body.
137    pub fn validate(&self) -> Result<(), ProvenBlockError> {
138        // Verify signature.
139        self.validate_signature()?;
140
141        // Validate that header / body transaction commitments match.
142        self.validate_tx_commitment()?;
143
144        // Validate that header / body note roots match.
145        self.validate_note_root()?;
146
147        Ok(())
148    }
149
150    /// Returns the proof security level of the block.
151    pub fn proof_security_level(&self) -> u32 {
152        MIN_PROOF_SECURITY_LEVEL
153    }
154
155    /// Returns the header of the block.
156    pub fn header(&self) -> &BlockHeader {
157        &self.header
158    }
159
160    /// Returns the body of the block.
161    pub fn body(&self) -> &BlockBody {
162        &self.body
163    }
164
165    /// Returns the Validator's signature over the block header.
166    pub fn signature(&self) -> &Signature {
167        &self.signature
168    }
169
170    /// Returns the proof of the block.
171    pub fn proof(&self) -> &BlockProof {
172        &self.proof
173    }
174
175    /// Destructures this proven block into individual parts.
176    pub fn into_parts(self) -> (BlockHeader, BlockBody, Signature, BlockProof) {
177        (self.header, self.body, self.signature, self.proof)
178    }
179
180    // HELPER METHODS
181    // --------------------------------------------------------------------------------------------
182
183    /// Performs ECDSA signature verification against the header commitment and validator key.
184    fn validate_signature(&self) -> Result<(), ProvenBlockError> {
185        if !self.signature.verify(self.header.commitment(), self.header.validator_key()) {
186            Err(ProvenBlockError::InvalidSignature)
187        } else {
188            Ok(())
189        }
190    }
191
192    /// Validates that the transaction commitments between the header and body match for this proven
193    /// block.
194    ///
195    /// Involves non-trivial computation of the body's transaction commitment.
196    fn validate_tx_commitment(&self) -> Result<(), ProvenBlockError> {
197        let header_tx_commitment = self.header.tx_commitment();
198        let body_tx_commitment = self.body.transactions().commitment();
199        if header_tx_commitment != body_tx_commitment {
200            Err(ProvenBlockError::TxCommitmentMismatch { header_tx_commitment, body_tx_commitment })
201        } else {
202            Ok(())
203        }
204    }
205
206    /// Validates that the header's note tree root matches that of the body.
207    ///
208    /// Involves non-trivial computation of the body's note tree.
209    fn validate_note_root(&self) -> Result<(), ProvenBlockError> {
210        let header_root = self.header.note_root();
211        let body_root = self.body.compute_block_note_tree().root();
212        if header_root != body_root {
213            Err(ProvenBlockError::NoteRootMismatch { header_root, body_root })
214        } else {
215            Ok(())
216        }
217    }
218}
219
220// SERIALIZATION
221// ================================================================================================
222
223impl Serializable for ProvenBlock {
224    fn write_into<W: ByteWriter>(&self, target: &mut W) {
225        self.header.write_into(target);
226        self.body.write_into(target);
227        self.signature.write_into(target);
228        self.proof.write_into(target);
229    }
230}
231
232impl Deserializable for ProvenBlock {
233    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
234        let block = Self {
235            header: BlockHeader::read_from(source)?,
236            body: BlockBody::read_from(source)?,
237            signature: Signature::read_from(source)?,
238            proof: BlockProof::read_from(source)?,
239        };
240
241        Ok(block)
242    }
243}