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}