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