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
23impl 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
38impl 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
125impl 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
156impl 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
203impl 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
286impl 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
309impl 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
332impl 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#[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 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}