Skip to main content

miden_node_proto/domain/
block.rs

1use std::collections::BTreeMap;
2use std::ops::RangeInclusive;
3
4use miden_protocol::account::AccountId;
5use miden_protocol::block::nullifier_tree::NullifierWitness;
6use miden_protocol::block::{
7    BlockBody,
8    BlockHeader,
9    BlockInputs,
10    BlockNumber,
11    FeeParameters,
12    SignedBlock,
13};
14use miden_protocol::crypto::dsa::ecdsa_k256_keccak::{PublicKey, Signature};
15use miden_protocol::note::{NoteId, NoteInclusionProof};
16use miden_protocol::transaction::PartialBlockchain;
17use miden_protocol::utils::{Deserializable, Serializable};
18use thiserror::Error;
19
20use crate::errors::{ConversionError, MissingFieldHelper};
21use crate::{AccountWitnessRecord, NullifierWitnessRecord, generated as proto};
22
23// BLOCK NUMBER
24// ================================================================================================
25
26impl From<BlockNumber> for proto::blockchain::BlockNumber {
27    fn from(value: BlockNumber) -> Self {
28        proto::blockchain::BlockNumber { block_num: value.as_u32() }
29    }
30}
31
32impl From<proto::blockchain::BlockNumber> for BlockNumber {
33    fn from(value: proto::blockchain::BlockNumber) -> Self {
34        BlockNumber::from(value.block_num)
35    }
36}
37
38// BLOCK HEADER
39// ================================================================================================
40
41impl From<&BlockHeader> for proto::blockchain::BlockHeader {
42    fn from(header: &BlockHeader) -> Self {
43        Self {
44            version: header.version(),
45            prev_block_commitment: Some(header.prev_block_commitment().into()),
46            block_num: header.block_num().as_u32(),
47            chain_commitment: Some(header.chain_commitment().into()),
48            account_root: Some(header.account_root().into()),
49            nullifier_root: Some(header.nullifier_root().into()),
50            note_root: Some(header.note_root().into()),
51            tx_commitment: Some(header.tx_commitment().into()),
52            tx_kernel_commitment: Some(header.tx_kernel_commitment().into()),
53            validator_key: Some(header.validator_key().into()),
54            timestamp: header.timestamp(),
55            fee_parameters: Some(header.fee_parameters().into()),
56        }
57    }
58}
59
60impl From<BlockHeader> for proto::blockchain::BlockHeader {
61    fn from(header: BlockHeader) -> Self {
62        (&header).into()
63    }
64}
65
66impl TryFrom<&proto::blockchain::BlockHeader> for BlockHeader {
67    type Error = ConversionError;
68
69    fn try_from(value: &proto::blockchain::BlockHeader) -> Result<Self, Self::Error> {
70        value.try_into()
71    }
72}
73
74impl TryFrom<proto::blockchain::BlockHeader> for BlockHeader {
75    type Error = ConversionError;
76
77    fn try_from(value: proto::blockchain::BlockHeader) -> Result<Self, Self::Error> {
78        Ok(BlockHeader::new(
79            value.version,
80            value
81                .prev_block_commitment
82                .ok_or(proto::blockchain::BlockHeader::missing_field(stringify!(
83                    prev_block_commitment
84                )))?
85                .try_into()?,
86            value.block_num.into(),
87            value
88                .chain_commitment
89                .ok_or(proto::blockchain::BlockHeader::missing_field(stringify!(chain_commitment)))?
90                .try_into()?,
91            value
92                .account_root
93                .ok_or(proto::blockchain::BlockHeader::missing_field(stringify!(account_root)))?
94                .try_into()?,
95            value
96                .nullifier_root
97                .ok_or(proto::blockchain::BlockHeader::missing_field(stringify!(nullifier_root)))?
98                .try_into()?,
99            value
100                .note_root
101                .ok_or(proto::blockchain::BlockHeader::missing_field(stringify!(note_root)))?
102                .try_into()?,
103            value
104                .tx_commitment
105                .ok_or(proto::blockchain::BlockHeader::missing_field(stringify!(tx_commitment)))?
106                .try_into()?,
107            value
108                .tx_kernel_commitment
109                .ok_or(proto::blockchain::BlockHeader::missing_field(stringify!(
110                    tx_kernel_commitment
111                )))?
112                .try_into()?,
113            value
114                .validator_key
115                .ok_or(proto::blockchain::BlockHeader::missing_field(stringify!(validator_key)))?
116                .try_into()?,
117            FeeParameters::try_from(value.fee_parameters.ok_or(
118                proto::blockchain::FeeParameters::missing_field(stringify!(fee_parameters)),
119            )?)?,
120            value.timestamp,
121        ))
122    }
123}
124
125// BLOCK BODY
126// ================================================================================================
127
128impl From<&BlockBody> for proto::blockchain::BlockBody {
129    fn from(body: &BlockBody) -> Self {
130        Self { block_body: body.to_bytes() }
131    }
132}
133
134impl From<BlockBody> for proto::blockchain::BlockBody {
135    fn from(body: BlockBody) -> Self {
136        (&body).into()
137    }
138}
139
140impl TryFrom<&proto::blockchain::BlockBody> for BlockBody {
141    type Error = ConversionError;
142
143    fn try_from(value: &proto::blockchain::BlockBody) -> Result<Self, Self::Error> {
144        value.try_into()
145    }
146}
147
148impl TryFrom<proto::blockchain::BlockBody> for BlockBody {
149    type Error = ConversionError;
150    fn try_from(value: proto::blockchain::BlockBody) -> Result<Self, Self::Error> {
151        BlockBody::read_from_bytes(&value.block_body)
152            .map_err(|source| ConversionError::deserialization_error("BlockBody", source))
153    }
154}
155
156// SIGNED BLOCK
157// ================================================================================================
158
159impl From<&SignedBlock> for proto::blockchain::SignedBlock {
160    fn from(block: &SignedBlock) -> Self {
161        Self {
162            header: Some(block.header().into()),
163            body: Some(block.body().into()),
164            signature: Some(block.signature().into()),
165        }
166    }
167}
168
169impl From<SignedBlock> for proto::blockchain::SignedBlock {
170    fn from(block: SignedBlock) -> Self {
171        (&block).into()
172    }
173}
174
175impl TryFrom<&proto::blockchain::SignedBlock> for SignedBlock {
176    type Error = ConversionError;
177
178    fn try_from(value: &proto::blockchain::SignedBlock) -> Result<Self, Self::Error> {
179        value.try_into()
180    }
181}
182
183impl TryFrom<proto::blockchain::SignedBlock> for SignedBlock {
184    type Error = ConversionError;
185    fn try_from(value: proto::blockchain::SignedBlock) -> Result<Self, Self::Error> {
186        let header = value
187            .header
188            .ok_or(proto::blockchain::SignedBlock::missing_field(stringify!(header)))?
189            .try_into()?;
190        let body = value
191            .body
192            .ok_or(proto::blockchain::SignedBlock::missing_field(stringify!(body)))?
193            .try_into()?;
194        let signature = value
195            .signature
196            .ok_or(proto::blockchain::SignedBlock::missing_field(stringify!(signature)))?
197            .try_into()?;
198
199        Ok(SignedBlock::new_unchecked(header, body, signature))
200    }
201}
202
203// BLOCK INPUTS
204// ================================================================================================
205
206impl From<BlockInputs> for proto::store::BlockInputs {
207    fn from(inputs: BlockInputs) -> Self {
208        let (
209            prev_block_header,
210            partial_block_chain,
211            account_witnesses,
212            nullifier_witnesses,
213            unauthenticated_note_proofs,
214        ) = inputs.into_parts();
215
216        proto::store::BlockInputs {
217            latest_block_header: Some(prev_block_header.into()),
218            account_witnesses: account_witnesses
219                .into_iter()
220                .map(|(id, witness)| AccountWitnessRecord { account_id: id, witness }.into())
221                .collect(),
222            nullifier_witnesses: nullifier_witnesses
223                .into_iter()
224                .map(|(nullifier, witness)| {
225                    let proof = witness.into_proof();
226                    NullifierWitnessRecord { nullifier, proof }.into()
227                })
228                .collect(),
229            partial_block_chain: partial_block_chain.to_bytes(),
230            unauthenticated_note_proofs: unauthenticated_note_proofs
231                .iter()
232                .map(proto::note::NoteInclusionInBlockProof::from)
233                .collect(),
234        }
235    }
236}
237
238impl TryFrom<proto::store::BlockInputs> for BlockInputs {
239    type Error = ConversionError;
240
241    fn try_from(response: proto::store::BlockInputs) -> Result<Self, Self::Error> {
242        let latest_block_header: BlockHeader = response
243            .latest_block_header
244            .ok_or(proto::blockchain::BlockHeader::missing_field("block_header"))?
245            .try_into()?;
246
247        let account_witnesses = response
248            .account_witnesses
249            .into_iter()
250            .map(|entry| {
251                let witness_record: AccountWitnessRecord = entry.try_into()?;
252                Ok((witness_record.account_id, witness_record.witness))
253            })
254            .collect::<Result<BTreeMap<_, _>, ConversionError>>()?;
255
256        let nullifier_witnesses = response
257            .nullifier_witnesses
258            .into_iter()
259            .map(|entry| {
260                let witness: NullifierWitnessRecord = entry.try_into()?;
261                Ok((witness.nullifier, NullifierWitness::new(witness.proof)))
262            })
263            .collect::<Result<BTreeMap<_, _>, ConversionError>>()?;
264
265        let unauthenticated_note_proofs = response
266            .unauthenticated_note_proofs
267            .iter()
268            .map(<(NoteId, NoteInclusionProof)>::try_from)
269            .collect::<Result<_, ConversionError>>()?;
270
271        let partial_block_chain = PartialBlockchain::read_from_bytes(&response.partial_block_chain)
272            .map_err(|source| {
273                ConversionError::deserialization_error("PartialBlockchain", source)
274            })?;
275
276        Ok(BlockInputs::new(
277            latest_block_header,
278            partial_block_chain,
279            account_witnesses,
280            nullifier_witnesses,
281            unauthenticated_note_proofs,
282        ))
283    }
284}
285
286// PUBLIC KEY
287// ================================================================================================
288
289impl TryFrom<proto::blockchain::ValidatorPublicKey> for PublicKey {
290    type Error = ConversionError;
291    fn try_from(public_key: proto::blockchain::ValidatorPublicKey) -> Result<Self, Self::Error> {
292        PublicKey::read_from_bytes(&public_key.validator_key)
293            .map_err(|source| ConversionError::deserialization_error("PublicKey", source))
294    }
295}
296
297impl From<PublicKey> for proto::blockchain::ValidatorPublicKey {
298    fn from(value: PublicKey) -> Self {
299        Self::from(&value)
300    }
301}
302
303impl From<&PublicKey> for proto::blockchain::ValidatorPublicKey {
304    fn from(value: &PublicKey) -> Self {
305        Self { validator_key: value.to_bytes() }
306    }
307}
308
309// SIGNATURE
310// ================================================================================================
311
312impl TryFrom<proto::blockchain::BlockSignature> for Signature {
313    type Error = ConversionError;
314    fn try_from(signature: proto::blockchain::BlockSignature) -> Result<Self, Self::Error> {
315        Signature::read_from_bytes(&signature.signature)
316            .map_err(|source| ConversionError::deserialization_error("Signature", source))
317    }
318}
319
320impl From<Signature> for proto::blockchain::BlockSignature {
321    fn from(value: Signature) -> Self {
322        Self::from(&value)
323    }
324}
325
326impl From<&Signature> for proto::blockchain::BlockSignature {
327    fn from(value: &Signature) -> Self {
328        Self { signature: value.to_bytes() }
329    }
330}
331
332// FEE PARAMETERS
333// ================================================================================================
334
335impl TryFrom<proto::blockchain::FeeParameters> for FeeParameters {
336    type Error = ConversionError;
337    fn try_from(fee_params: proto::blockchain::FeeParameters) -> Result<Self, Self::Error> {
338        let native_asset_id = fee_params.native_asset_id.map(AccountId::try_from).ok_or(
339            proto::blockchain::FeeParameters::missing_field(stringify!(native_asset_id)),
340        )??;
341        let fee_params = FeeParameters::new(native_asset_id, fee_params.verification_base_fee)?;
342        Ok(fee_params)
343    }
344}
345
346impl From<FeeParameters> for proto::blockchain::FeeParameters {
347    fn from(value: FeeParameters) -> Self {
348        Self::from(&value)
349    }
350}
351
352impl From<&FeeParameters> for proto::blockchain::FeeParameters {
353    fn from(value: &FeeParameters) -> Self {
354        Self {
355            native_asset_id: Some(value.native_asset_id().into()),
356            verification_base_fee: value.verification_base_fee(),
357        }
358    }
359}
360
361// BLOCK RANGE
362// ================================================================================================
363
364#[derive(Debug, Clone, Error, PartialEq, Eq)]
365pub enum InvalidBlockRange {
366    #[error("start ({start}) greater than end ({end})")]
367    StartGreaterThanEnd { start: BlockNumber, end: BlockNumber },
368    #[error("empty range: start ({start})..end ({end})")]
369    EmptyRange { start: BlockNumber, end: BlockNumber },
370}
371
372impl proto::rpc::BlockRange {
373    /// Converts the block range into an inclusive range, using the fallback block number if the
374    /// block to is not specified.
375    pub fn into_inclusive_range<T: From<InvalidBlockRange>>(
376        self,
377        fallback: &BlockNumber,
378    ) -> Result<RangeInclusive<BlockNumber>, T> {
379        let block_range = RangeInclusive::new(
380            self.block_from.into(),
381            self.block_to.map_or(*fallback, BlockNumber::from),
382        );
383
384        if block_range.start() > block_range.end() {
385            return Err(InvalidBlockRange::StartGreaterThanEnd {
386                start: *block_range.start(),
387                end: *block_range.end(),
388            }
389            .into());
390        }
391
392        if block_range.is_empty() {
393            return Err(InvalidBlockRange::EmptyRange {
394                start: *block_range.start(),
395                end: *block_range.end(),
396            }
397            .into());
398        }
399
400        Ok(block_range)
401    }
402}
403
404impl From<RangeInclusive<BlockNumber>> for proto::rpc::BlockRange {
405    fn from(range: RangeInclusive<BlockNumber>) -> Self {
406        Self {
407            block_from: range.start().as_u32(),
408            block_to: Some(range.end().as_u32()),
409        }
410    }
411}