miden_node_proto/domain/
block.rs

1use std::collections::BTreeMap;
2use std::ops::RangeInclusive;
3
4use miden_objects::account::AccountId;
5use miden_objects::block::{
6    BlockHeader,
7    BlockInputs,
8    BlockNumber,
9    FeeParameters,
10    NullifierWitness,
11};
12use miden_objects::note::{NoteId, NoteInclusionProof};
13use miden_objects::transaction::PartialBlockchain;
14use miden_objects::utils::{Deserializable, Serializable};
15use thiserror::Error;
16
17use crate::errors::{ConversionError, MissingFieldHelper};
18use crate::{AccountWitnessRecord, NullifierWitnessRecord, generated as proto};
19
20// BLOCK NUMBER
21// ================================================================================================
22
23impl From<BlockNumber> for proto::blockchain::BlockNumber {
24    fn from(value: BlockNumber) -> Self {
25        proto::blockchain::BlockNumber { block_num: value.as_u32() }
26    }
27}
28
29impl From<proto::blockchain::BlockNumber> for BlockNumber {
30    fn from(value: proto::blockchain::BlockNumber) -> Self {
31        BlockNumber::from(value.block_num)
32    }
33}
34
35// BLOCK HEADER
36// ================================================================================================
37
38impl From<&BlockHeader> for proto::blockchain::BlockHeader {
39    fn from(header: &BlockHeader) -> Self {
40        Self {
41            version: header.version(),
42            prev_block_commitment: Some(header.prev_block_commitment().into()),
43            block_num: header.block_num().as_u32(),
44            chain_commitment: Some(header.chain_commitment().into()),
45            account_root: Some(header.account_root().into()),
46            nullifier_root: Some(header.nullifier_root().into()),
47            note_root: Some(header.note_root().into()),
48            tx_commitment: Some(header.tx_commitment().into()),
49            tx_kernel_commitment: Some(header.tx_kernel_commitment().into()),
50            proof_commitment: Some(header.proof_commitment().into()),
51            timestamp: header.timestamp(),
52            fee_parameters: Some(header.fee_parameters().into()),
53        }
54    }
55}
56
57impl From<BlockHeader> for proto::blockchain::BlockHeader {
58    fn from(header: BlockHeader) -> Self {
59        (&header).into()
60    }
61}
62
63impl TryFrom<&proto::blockchain::BlockHeader> for BlockHeader {
64    type Error = ConversionError;
65
66    fn try_from(value: &proto::blockchain::BlockHeader) -> Result<Self, Self::Error> {
67        value.try_into()
68    }
69}
70
71impl TryFrom<proto::blockchain::BlockHeader> for BlockHeader {
72    type Error = ConversionError;
73
74    fn try_from(value: proto::blockchain::BlockHeader) -> Result<Self, Self::Error> {
75        Ok(BlockHeader::new(
76            value.version,
77            value
78                .prev_block_commitment
79                .ok_or(proto::blockchain::BlockHeader::missing_field(stringify!(
80                    prev_block_commitment
81                )))?
82                .try_into()?,
83            value.block_num.into(),
84            value
85                .chain_commitment
86                .ok_or(proto::blockchain::BlockHeader::missing_field(stringify!(chain_commitment)))?
87                .try_into()?,
88            value
89                .account_root
90                .ok_or(proto::blockchain::BlockHeader::missing_field(stringify!(account_root)))?
91                .try_into()?,
92            value
93                .nullifier_root
94                .ok_or(proto::blockchain::BlockHeader::missing_field(stringify!(nullifier_root)))?
95                .try_into()?,
96            value
97                .note_root
98                .ok_or(proto::blockchain::BlockHeader::missing_field(stringify!(note_root)))?
99                .try_into()?,
100            value
101                .tx_commitment
102                .ok_or(proto::blockchain::BlockHeader::missing_field(stringify!(tx_commitment)))?
103                .try_into()?,
104            value
105                .tx_kernel_commitment
106                .ok_or(proto::blockchain::BlockHeader::missing_field(stringify!(
107                    tx_kernel_commitment
108                )))?
109                .try_into()?,
110            value
111                .proof_commitment
112                .ok_or(proto::blockchain::BlockHeader::missing_field(stringify!(proof_commitment)))?
113                .try_into()?,
114            FeeParameters::try_from(value.fee_parameters.ok_or(
115                proto::blockchain::FeeParameters::missing_field(stringify!(fee_parameters)),
116            )?)?,
117            value.timestamp,
118        ))
119    }
120}
121
122// BLOCK INPUTS
123// ================================================================================================
124
125impl From<BlockInputs> for proto::block_producer_store::BlockInputs {
126    fn from(inputs: BlockInputs) -> Self {
127        let (
128            prev_block_header,
129            partial_block_chain,
130            account_witnesses,
131            nullifier_witnesses,
132            unauthenticated_note_proofs,
133        ) = inputs.into_parts();
134
135        proto::block_producer_store::BlockInputs {
136            latest_block_header: Some(prev_block_header.into()),
137            account_witnesses: account_witnesses
138                .into_iter()
139                .map(|(id, witness)| AccountWitnessRecord { account_id: id, witness }.into())
140                .collect(),
141            nullifier_witnesses: nullifier_witnesses
142                .into_iter()
143                .map(|(nullifier, witness)| {
144                    let proof = witness.into_proof();
145                    NullifierWitnessRecord { nullifier, proof }.into()
146                })
147                .collect(),
148            partial_block_chain: partial_block_chain.to_bytes(),
149            unauthenticated_note_proofs: unauthenticated_note_proofs
150                .iter()
151                .map(proto::note::NoteInclusionInBlockProof::from)
152                .collect(),
153        }
154    }
155}
156
157impl TryFrom<proto::block_producer_store::BlockInputs> for BlockInputs {
158    type Error = ConversionError;
159
160    fn try_from(response: proto::block_producer_store::BlockInputs) -> Result<Self, Self::Error> {
161        let latest_block_header: BlockHeader = response
162            .latest_block_header
163            .ok_or(proto::blockchain::BlockHeader::missing_field("block_header"))?
164            .try_into()?;
165
166        let account_witnesses = response
167            .account_witnesses
168            .into_iter()
169            .map(|entry| {
170                let witness_record: AccountWitnessRecord = entry.try_into()?;
171                Ok((witness_record.account_id, witness_record.witness))
172            })
173            .collect::<Result<BTreeMap<_, _>, ConversionError>>()?;
174
175        let nullifier_witnesses = response
176            .nullifier_witnesses
177            .into_iter()
178            .map(|entry| {
179                let witness: NullifierWitnessRecord = entry.try_into()?;
180                Ok((witness.nullifier, NullifierWitness::new(witness.proof)))
181            })
182            .collect::<Result<BTreeMap<_, _>, ConversionError>>()?;
183
184        let unauthenticated_note_proofs = response
185            .unauthenticated_note_proofs
186            .iter()
187            .map(<(NoteId, NoteInclusionProof)>::try_from)
188            .collect::<Result<_, ConversionError>>()?;
189
190        let partial_block_chain = PartialBlockchain::read_from_bytes(&response.partial_block_chain)
191            .map_err(|source| {
192                ConversionError::deserialization_error("PartialBlockchain", source)
193            })?;
194
195        Ok(BlockInputs::new(
196            latest_block_header,
197            partial_block_chain,
198            account_witnesses,
199            nullifier_witnesses,
200            unauthenticated_note_proofs,
201        ))
202    }
203}
204
205// FEE PARAMETERS
206// ================================================================================================
207
208impl TryFrom<proto::blockchain::FeeParameters> for FeeParameters {
209    type Error = ConversionError;
210    fn try_from(fee_params: proto::blockchain::FeeParameters) -> Result<Self, Self::Error> {
211        let native_asset_id = fee_params.native_asset_id.map(AccountId::try_from).ok_or(
212            proto::blockchain::FeeParameters::missing_field(stringify!(native_asset_id)),
213        )??;
214        let fee_params = FeeParameters::new(native_asset_id, fee_params.verification_base_fee)?;
215        Ok(fee_params)
216    }
217}
218
219impl From<FeeParameters> for proto::blockchain::FeeParameters {
220    fn from(value: FeeParameters) -> Self {
221        Self::from(&value)
222    }
223}
224
225impl From<&FeeParameters> for proto::blockchain::FeeParameters {
226    fn from(value: &FeeParameters) -> Self {
227        Self {
228            native_asset_id: Some(value.native_asset_id().into()),
229            verification_base_fee: value.verification_base_fee(),
230        }
231    }
232}
233
234// BLOCK RANGE
235// ================================================================================================
236
237#[derive(Debug, Clone, Error, PartialEq, Eq)]
238pub enum InvalidBlockRange {
239    #[error("start ({start}) greater than end ({end})")]
240    StartGreaterThanEnd { start: BlockNumber, end: BlockNumber },
241    #[error("empty range: start ({start})..end ({end})")]
242    EmptyRange { start: BlockNumber, end: BlockNumber },
243}
244
245impl proto::rpc_store::BlockRange {
246    /// Converts the block range into an inclusive range, using the fallback block number if the
247    /// block to is not specified.
248    pub fn into_inclusive_range<T: From<InvalidBlockRange>>(
249        self,
250        fallback: &BlockNumber,
251    ) -> Result<RangeInclusive<BlockNumber>, T> {
252        let block_range = RangeInclusive::new(
253            self.block_from.into(),
254            self.block_to.map_or(*fallback, BlockNumber::from),
255        );
256
257        if block_range.start() > block_range.end() {
258            return Err(InvalidBlockRange::StartGreaterThanEnd {
259                start: *block_range.start(),
260                end: *block_range.end(),
261            }
262            .into());
263        }
264
265        if block_range.is_empty() {
266            return Err(InvalidBlockRange::EmptyRange {
267                start: *block_range.start(),
268                end: *block_range.end(),
269            }
270            .into());
271        }
272
273        Ok(block_range)
274    }
275}
276
277impl From<RangeInclusive<BlockNumber>> for proto::rpc_store::BlockRange {
278    fn from(range: RangeInclusive<BlockNumber>) -> Self {
279        Self {
280            block_from: range.start().as_u32(),
281            block_to: Some(range.end().as_u32()),
282        }
283    }
284}