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
24impl 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
39impl 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
107impl 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
137impl 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
176impl 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
258impl 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
280impl 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
302impl 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#[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 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}