Skip to main content

miden_objects/decoded/
blockchain.rs

1//! Domain construction for decoded blockchain messages.
2use miden_protobuf::unwrap_infallible;
3
4use crate::decoded::VerificationError;
5use crate::{Verify, proto};
6
7#[cfg(test)]
8mod tests;
9
10#[cfg(test)]
11pub(crate) mod test_utils;
12
13pub use proto::blockchain::DecodedBlockNumber as BlockNumber;
14
15impl Verify for BlockNumber {
16    type Verified = miden_protocol::block::BlockNumber;
17    type Error = core::convert::Infallible;
18    fn verify(self) -> Result<Self::Verified, Self::Error> {
19        Ok(self.block_num.into())
20    }
21}
22
23pub use proto::blockchain::DecodedFeeParameters as FeeParameters;
24
25impl Verify for FeeParameters {
26    type Verified = miden_protocol::block::FeeParameters;
27    type Error = core::convert::Infallible;
28    fn verify(self) -> Result<Self::Verified, Self::Error> {
29        Ok(Self::Verified::new(self.verification_base_fee))
30    }
31}
32
33pub use proto::blockchain::DecodedNextProtocolConfig as NextProtocolConfig;
34
35impl Verify for NextProtocolConfig {
36    type Verified = miden_protocol::protocol_config::NextProtocolConfig;
37    type Error = miden_protocol::errors::ProtocolConfigError;
38    fn verify(self) -> Result<Self::Verified, Self::Error> {
39        let effective_from = unwrap_infallible(self.effective_from.verify());
40        Self::Verified::new(effective_from, self.protocol_config)
41    }
42}
43
44pub use proto::blockchain::DecodedValidatorConfig as ValidatorConfig;
45
46impl Verify for ValidatorConfig {
47    type Verified = miden_protocol::block::ValidatorConfig;
48    type Error = VerificationError;
49    fn verify(self) -> Result<Self::Verified, Self::Error> {
50        let keys = self.keys.verify_infallible();
51        Ok(Self::Verified::new(keys, self.quorum.try_into()?)?)
52    }
53}
54
55pub use proto::blockchain::DecodedBlockHeader as BlockHeader;
56
57/// Builds a header without validating its parent linkage, signatures, or protocol transition.
58impl crate::BuildUnchecked for BlockHeader {
59    type Output = miden_protocol::block::BlockHeader;
60    type Error = VerificationError;
61    fn build_unchecked(self) -> Result<Self::Output, Self::Error> {
62        if self.version != proto::blockchain::BlockVersion::V1 {
63            return Err(BlockHeaderError::UnspecifiedVersion.into());
64        }
65        Ok(Self::Output::new(
66            self.prev_block_commitment,
67            unwrap_infallible(self.block_num.verify()),
68            self.chain_commitment,
69            self.account_root,
70            self.nullifier_root,
71            self.note_root,
72            self.tx_commitment,
73            self.validator_config.verify()?,
74            unwrap_infallible(self.fee_parameters.verify()),
75            self.protocol_config_commitment,
76            self.next_protocol_config.verify()?,
77            self.timestamp,
78        ))
79    }
80}
81
82#[derive(Debug, thiserror::Error)]
83pub enum BlockHeaderError {
84    #[error("block header version is unspecified")]
85    UnspecifiedVersion,
86}
87
88pub use proto::blockchain::DecodedPartialBlockchain as PartialBlockchain;
89
90/// Checks MMR reconstruction and header membership, but not header parent linkage or a trusted
91/// root.
92impl crate::BuildUnchecked for PartialBlockchain {
93    type Output = miden_protocol::transaction::PartialBlockchain;
94    type Error = VerificationError;
95    fn build_unchecked(self) -> Result<Self::Output, Self::Error> {
96        let mmr = self.mmr.verify()?;
97        let mut previous = None;
98        let mut headers = alloc::vec::Vec::new();
99        for header in self.block_headers.into_inner() {
100            let header = header.build_unchecked()?;
101            if previous.is_some_and(|previous| header.block_num() <= previous) {
102                return Err(PartialBlockchainError::HeaderOrder.into());
103            }
104            previous = Some(header.block_num());
105            headers.push(header);
106        }
107        Ok(Self::Output::new(mmr, headers)?)
108    }
109}
110
111#[derive(Debug, thiserror::Error)]
112pub enum PartialBlockchainError {
113    #[error("block headers must be unique and ordered by ascending block number")]
114    HeaderOrder,
115}
116
117pub use proto::blockchain::DecodedBlockAccountUpdate as BlockAccountUpdate;
118
119impl Verify for BlockAccountUpdate {
120    type Verified = miden_protocol::block::BlockAccountUpdate;
121    type Error = VerificationError;
122    fn verify(self) -> Result<Self::Verified, Self::Error> {
123        Ok(Self::Verified::new(
124            self.account_id.verify()?,
125            self.final_state_commitment,
126            self.details.verify()?,
127        )?)
128    }
129}
130
131pub use proto::blockchain::DecodedIndexedOutputNote as IndexedOutputNote;
132
133impl Verify for IndexedOutputNote {
134    type Verified = (usize, miden_protocol::transaction::OutputNote);
135    type Error = VerificationError;
136    fn verify(self) -> Result<Self::Verified, Self::Error> {
137        Ok((self.note_index_in_batch.try_into()?, self.note.verify()?))
138    }
139}
140
141pub use proto::blockchain::DecodedOutputNoteBatch as OutputNoteBatch;
142
143impl Verify for OutputNoteBatch {
144    type Verified = miden_protocol::block::OutputNoteBatch;
145    type Error = VerificationError;
146    fn verify(self) -> Result<Self::Verified, Self::Error> {
147        Ok(self.notes.verify()?)
148    }
149}
150
151pub use proto::blockchain::DecodedBlockBody as BlockBody;
152
153/// Checks body invariants, but trusts transaction ordering and unchecked input-note commitments.
154impl crate::BuildUnchecked for BlockBody {
155    type Output = miden_protocol::block::BlockBody;
156    type Error = VerificationError;
157    fn build_unchecked(self) -> Result<Self::Output, Self::Error> {
158        let updates = self.updated_accounts.verify()?;
159        let notes = self.output_note_batches.verify()?;
160        let nullifiers = self.created_nullifiers.map(miden_protocol::note::Nullifier::from_raw);
161        let transactions = self.transactions.build_unchecked()?;
162        Ok(Self::Output::new(
163            updates,
164            notes,
165            nullifiers,
166            miden_protocol::transaction::OrderedTransactionHeaders::new_unchecked(transactions),
167        )?)
168    }
169}
170
171pub use proto::blockchain::DecodedSignedBlock as SignedBlock;
172
173/// Checks header/body consistency but does not authenticate against a trusted parent.
174impl crate::BuildUnchecked for SignedBlock {
175    type Output = miden_protocol::block::SignedBlock;
176    type Error = VerificationError;
177    fn build_unchecked(self) -> Result<Self::Output, Self::Error> {
178        self.build(None)
179    }
180}
181
182impl SignedBlock {
183    fn build(
184        self,
185        parent: Option<&miden_protocol::block::BlockHeader>,
186    ) -> Result<miden_protocol::block::SignedBlock, VerificationError> {
187        use crate::BuildUnchecked;
188
189        let header = self.header.build_unchecked()?;
190        let body = self.body.build_unchecked()?;
191        let signatures = self.signatures.verify_infallible();
192        let signatures = miden_protocol::block::BlockSignatures::new(signatures)
193            .map_err(VerificationError::new)?;
194        let block = miden_protocol::block::SignedBlock::new_unchecked(header, body, signatures);
195        block.validate(parent)?;
196        Ok(block)
197    }
198}
199
200/// Authenticates the block against an already-trusted parent, in addition to self-consistency.
201/// This does not re-execute transactions or validate the account/nullifier state transition.
202impl crate::VerifyWith<&miden_protocol::block::BlockHeader> for SignedBlock {
203    type Verified = miden_protocol::block::SignedBlock;
204    type Error = VerificationError;
205    fn verify_with(
206        self,
207        parent: &miden_protocol::block::BlockHeader,
208    ) -> Result<Self::Verified, Self::Error> {
209        self.build(Some(parent))
210    }
211}