miden_protocol/block/
signed_block.rs1use miden_core::Word;
2use miden_crypto::dsa::ecdsa_k256_keccak::Signature;
3
4use crate::block::{BlockBody, BlockHeader, BlockNumber};
5use crate::utils::serde::{
6 ByteReader,
7 ByteWriter,
8 Deserializable,
9 DeserializationError,
10 Serializable,
11};
12
13#[derive(Debug, thiserror::Error)]
17pub enum SignedBlockError {
18 #[error(
19 "ECDSA signature verification failed based on the signed block's header commitment, validator public key and signature"
20 )]
21 InvalidSignature,
22 #[error(
23 "header tx commitment ({header_tx_commitment}) does not match body tx commitment ({body_tx_commitment})"
24 )]
25 TxCommitmentMismatch {
26 header_tx_commitment: Word,
27 body_tx_commitment: Word,
28 },
29 #[error(
30 "signed block previous block commitment ({expected}) does not match expected parent's block commitment ({parent})"
31 )]
32 ParentCommitmentMismatch { expected: Word, parent: Word },
33 #[error("parent block number ({parent}) is not signed block number - 1 ({expected})")]
34 ParentNumberMismatch {
35 expected: BlockNumber,
36 parent: BlockNumber,
37 },
38 #[error(
39 "signed block header note root ({header_root}) does not match the corresponding body's note root ({body_root})"
40 )]
41 NoteRootMismatch { header_root: Word, body_root: Word },
42 #[error("supplied parent block ({parent}) cannot be parent to genesis block")]
43 GenesisBlockHasNoParent { parent: BlockNumber },
44}
45
46#[derive(Debug, Clone, PartialEq, Eq)]
53pub struct SignedBlock {
54 header: BlockHeader,
56
57 body: BlockBody,
59
60 signature: Signature,
62}
63
64impl SignedBlock {
65 pub fn new(
73 header: BlockHeader,
74 body: BlockBody,
75 signature: Signature,
76 ) -> Result<Self, SignedBlockError> {
77 let signed_block = Self { header, body, signature };
78
79 signed_block.validate_signature()?;
81
82 signed_block.validate_tx_commitment()?;
84
85 signed_block.validate_note_root()?;
87
88 Ok(signed_block)
89 }
90
91 pub fn new_unchecked(header: BlockHeader, body: BlockBody, signature: Signature) -> Self {
98 Self { header, signature, body }
99 }
100
101 pub fn header(&self) -> &BlockHeader {
103 &self.header
104 }
105
106 pub fn body(&self) -> &BlockBody {
108 &self.body
109 }
110
111 pub fn signature(&self) -> &Signature {
113 &self.signature
114 }
115
116 pub fn into_parts(self) -> (BlockHeader, BlockBody, Signature) {
118 (self.header, self.body, self.signature)
119 }
120
121 fn validate_signature(&self) -> Result<(), SignedBlockError> {
123 if !self.signature.verify(self.header.commitment(), self.header.validator_key()) {
124 Err(SignedBlockError::InvalidSignature)
125 } else {
126 Ok(())
127 }
128 }
129
130 fn validate_tx_commitment(&self) -> Result<(), SignedBlockError> {
135 let header_tx_commitment = self.header.tx_commitment();
136 let body_tx_commitment = self.body.transactions().commitment();
137 if header_tx_commitment != body_tx_commitment {
138 Err(SignedBlockError::TxCommitmentMismatch { header_tx_commitment, body_tx_commitment })
139 } else {
140 Ok(())
141 }
142 }
143
144 fn validate_note_root(&self) -> Result<(), SignedBlockError> {
148 let header_root = self.header.note_root();
149 let body_root = self.body.compute_block_note_tree().root();
150 if header_root != body_root {
151 Err(SignedBlockError::NoteRootMismatch { header_root, body_root })
152 } else {
153 Ok(())
154 }
155 }
156
157 pub fn validate_parent(&self, parent_block: &BlockHeader) -> Result<(), SignedBlockError> {
168 if let Some(expected) = self.header.block_num().checked_sub(1) {
170 let parent = parent_block.block_num();
171 if expected != parent {
172 return Err(SignedBlockError::ParentNumberMismatch { expected, parent });
173 }
174
175 let expected = self.header.prev_block_commitment();
177 let parent = parent_block.commitment();
178 if expected != parent {
179 return Err(SignedBlockError::ParentCommitmentMismatch { expected, parent });
180 }
181
182 Ok(())
183 } else {
184 let parent = parent_block.block_num();
186 Err(SignedBlockError::GenesisBlockHasNoParent { parent })
187 }
188 }
189}
190
191impl Serializable for SignedBlock {
195 fn write_into<W: ByteWriter>(&self, target: &mut W) {
196 self.header.write_into(target);
197 self.body.write_into(target);
198 self.signature.write_into(target);
199 }
200}
201
202impl Deserializable for SignedBlock {
203 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
204 let block = Self {
205 header: BlockHeader::read_from(source)?,
206 body: BlockBody::read_from(source)?,
207 signature: Signature::read_from(source)?,
208 };
209
210 Ok(block)
211 }
212}