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