miden_objects/conversion/
primitives.rs1use alloc::collections::BTreeMap;
2use alloc::format;
3use alloc::vec::Vec;
4
5use miden_protocol::crypto::dsa::ecdsa_k256_keccak::{PublicKey, Signature};
6use miden_protocol::crypto::merkle::InnerNodeInfo;
7use miden_protocol::crypto::merkle::store::MerkleStore;
8use miden_protocol::utils::serde::{Deserializable, Serializable};
9use miden_protocol::vm::{AdviceInputs, AdviceMap, AdviceStack, ExecutionProof};
10use miden_protocol::{Felt, MastForest, Word};
11
12use super::{MessageDecodeExt, required};
13use crate::{ConversionError, ConversionResultExt, proto};
14
15const WORD_SERIALIZED_SIZE: usize = Word::SERIALIZED_SIZE;
16
17fn ensure_exact_length(
18 encoded: &[u8],
19 expected: usize,
20 field: &'static str,
21) -> Result<(), ConversionError> {
22 if encoded.len() != expected {
23 return Err(ConversionError::message(format!(
24 "expected exactly {expected} bytes, got {}",
25 encoded.len()
26 ))
27 .context(field));
28 }
29 Ok(())
30}
31
32impl From<Felt> for proto::primitives::Felt {
36 fn from(value: Felt) -> Self {
37 Self { value: value.as_canonical_u64() }
38 }
39}
40
41impl From<&Felt> for proto::primitives::Felt {
42 fn from(value: &Felt) -> Self {
43 Self { value: value.as_canonical_u64() }
44 }
45}
46
47impl TryFrom<proto::primitives::Felt> for Felt {
48 type Error = ConversionError;
49
50 fn try_from(value: proto::primitives::Felt) -> Result<Self, Self::Error> {
51 Self::try_from(&value)
52 }
53}
54
55impl TryFrom<&proto::primitives::Felt> for Felt {
56 type Error = ConversionError;
57
58 fn try_from(value: &proto::primitives::Felt) -> Result<Self, Self::Error> {
59 Self::try_from(value.value).map_err(ConversionError::new).context("felt.value")
60 }
61}
62
63impl From<Word> for proto::primitives::Word {
67 fn from(value: Word) -> Self {
68 Self { encoded: value.to_bytes() }
69 }
70}
71
72impl From<&Word> for proto::primitives::Word {
73 fn from(value: &Word) -> Self {
74 Self { encoded: value.to_bytes() }
75 }
76}
77
78impl TryFrom<proto::primitives::Word> for Word {
79 type Error = ConversionError;
80
81 fn try_from(value: proto::primitives::Word) -> Result<Self, Self::Error> {
82 Self::try_from(&value)
83 }
84}
85
86impl TryFrom<&proto::primitives::Word> for Word {
87 type Error = ConversionError;
88
89 fn try_from(value: &proto::primitives::Word) -> Result<Self, Self::Error> {
90 ensure_exact_length(&value.encoded, WORD_SERIALIZED_SIZE, "word.encoded")?;
91 Self::read_from_bytes(&value.encoded)
92 .map_err(|error| ConversionError::deserialization("word.encoded", error))
93 }
94}
95
96impl From<&ExecutionProof> for proto::primitives::ExecutionProof {
100 fn from(value: &ExecutionProof) -> Self {
101 Self { encoded: value.to_bytes() }
102 }
103}
104
105impl From<ExecutionProof> for proto::primitives::ExecutionProof {
106 fn from(value: ExecutionProof) -> Self {
107 (&value).into()
108 }
109}
110
111impl TryFrom<proto::primitives::ExecutionProof> for ExecutionProof {
112 type Error = ConversionError;
113
114 fn try_from(value: proto::primitives::ExecutionProof) -> Result<Self, Self::Error> {
115 Self::try_from(&value)
116 }
117}
118
119impl TryFrom<&proto::primitives::ExecutionProof> for ExecutionProof {
120 type Error = ConversionError;
121
122 fn try_from(value: &proto::primitives::ExecutionProof) -> Result<Self, Self::Error> {
123 Self::read_from_bytes(&value.encoded)
124 .map_err(|error| ConversionError::deserialization("ExecutionProof", error))
125 .map_err(|error| error.context("encoded"))
126 }
127}
128
129impl From<&MastForest> for proto::primitives::MastForest {
133 fn from(value: &MastForest) -> Self {
134 Self { encoded: value.to_bytes() }
135 }
136}
137
138impl From<MastForest> for proto::primitives::MastForest {
139 fn from(value: MastForest) -> Self {
140 (&value).into()
141 }
142}
143
144impl TryFrom<proto::primitives::MastForest> for MastForest {
145 type Error = ConversionError;
146
147 fn try_from(value: proto::primitives::MastForest) -> Result<Self, Self::Error> {
148 Self::try_from(&value)
149 }
150}
151
152impl TryFrom<&proto::primitives::MastForest> for MastForest {
153 type Error = ConversionError;
154
155 fn try_from(value: &proto::primitives::MastForest) -> Result<Self, Self::Error> {
156 Self::read_from_bytes(&value.encoded)
157 .map_err(|error| ConversionError::deserialization("MastForest", error))
158 .map_err(|error| error.context("encoded"))
159 }
160}
161
162impl From<&AdviceStack> for proto::primitives::AdviceStack {
166 fn from(value: &AdviceStack) -> Self {
167 Self {
168 values: value.iter().map(Into::into).collect(),
169 }
170 }
171}
172
173impl TryFrom<proto::primitives::AdviceStack> for AdviceStack {
174 type Error = ConversionError;
175
176 fn try_from(value: proto::primitives::AdviceStack) -> Result<Self, Self::Error> {
177 value
178 .values
179 .into_iter()
180 .enumerate()
181 .map(|(index, value)| Felt::try_from(value).context(format!("values[{index}]")))
182 .collect::<Result<AdviceStack, _>>()
183 }
184}
185
186impl From<&AdviceMap> for proto::primitives::AdviceMap {
187 fn from(value: &AdviceMap) -> Self {
188 Self {
189 entries: value
190 .iter()
191 .map(|(key, values)| proto::primitives::AdviceMapEntry {
192 key: Some(key.into()),
193 values: values.iter().map(Into::into).collect(),
194 })
195 .collect(),
196 }
197 }
198}
199
200impl TryFrom<proto::primitives::AdviceMap> for AdviceMap {
201 type Error = ConversionError;
202
203 fn try_from(value: proto::primitives::AdviceMap) -> Result<Self, Self::Error> {
204 let mut entries = BTreeMap::new();
205 for (index, entry) in value.entries.into_iter().enumerate() {
206 let decoder = entry.decoder();
207 let entry_context = format!("entries[{index}]");
208 let key = required!(decoder, entry.key).context(&entry_context)?;
209 let values = entry
210 .values
211 .into_iter()
212 .enumerate()
213 .map(|(value_index, value)| {
214 Felt::try_from(value).context(format!("{entry_context}.values[{value_index}]"))
215 })
216 .collect::<Result<Vec<_>, _>>()?;
217 if entries.insert(key, values).is_some() {
218 return Err(ConversionError::message("duplicate advice map key")
219 .context(format!("{entry_context}.key")));
220 }
221 }
222
223 Ok(entries.into())
224 }
225}
226
227impl From<&MerkleStore> for proto::primitives::MerkleStore {
228 fn from(value: &MerkleStore) -> Self {
229 let default_nodes = MerkleStore::new()
230 .inner_nodes()
231 .map(|node| (node.value, (node.left, node.right)))
232 .collect::<BTreeMap<_, _>>();
233 let mut nodes = value
234 .inner_nodes()
235 .filter(|node| default_nodes.get(&node.value) != Some(&(node.left, node.right)))
236 .collect::<Vec<_>>();
237 nodes.sort_by_key(|node| node.value);
238
239 Self {
240 nodes: nodes
241 .into_iter()
242 .map(|node| proto::primitives::MerkleStoreNode {
243 value: Some(node.value.into()),
244 left: Some(node.left.into()),
245 right: Some(node.right.into()),
246 })
247 .collect(),
248 }
249 }
250}
251
252impl TryFrom<proto::primitives::MerkleStore> for MerkleStore {
253 type Error = ConversionError;
254
255 fn try_from(value: proto::primitives::MerkleStore) -> Result<Self, Self::Error> {
256 let mut nodes = BTreeMap::new();
257 for (index, node) in value.nodes.into_iter().enumerate() {
258 let decoder = node.decoder();
259 let node_context = format!("nodes[{index}]");
260 let parent = required!(decoder, node.value).context(&node_context)?;
261 let left = required!(decoder, node.left).context(&node_context)?;
262 let right = required!(decoder, node.right).context(&node_context)?;
263 if nodes.insert(parent, (left, right)).is_some() {
264 return Err(ConversionError::message("duplicate Merkle store parent")
265 .context(format!("{node_context}.value")));
266 }
267 }
268
269 let mut store = MerkleStore::new();
270 store.extend(nodes.into_iter().map(|(value, (left, right))| InnerNodeInfo {
271 value,
272 left,
273 right,
274 }));
275 Ok(store)
276 }
277}
278
279impl From<&AdviceInputs> for proto::primitives::AdviceInputs {
280 fn from(value: &AdviceInputs) -> Self {
281 Self {
282 advice_stack: Some((&value.stack()).into()),
283 advice_map: Some(value.map().into()),
284 merkle_store: Some(value.store().into()),
285 }
286 }
287}
288
289impl TryFrom<proto::primitives::AdviceInputs> for AdviceInputs {
290 type Error = ConversionError;
291
292 fn try_from(value: proto::primitives::AdviceInputs) -> Result<Self, Self::Error> {
293 let decoder = value.decoder();
294 let advice_stack = required!(decoder, value.advice_stack)?;
295 let advice_map: AdviceMap = required!(decoder, value.advice_map)?;
296 let merkle_store: MerkleStore = required!(decoder, value.merkle_store)?;
297
298 Ok(AdviceInputs::new(advice_stack, advice_map, merkle_store))
299 }
300}
301
302fn decode_public_key_variant(variant: i32) -> Result<(), ConversionError> {
306 match proto::primitives::PublicKeyVariant::try_from(variant) {
307 Ok(proto::primitives::PublicKeyVariant::EcdsaK256Keccak) => Ok(()),
308 Ok(proto::primitives::PublicKeyVariant::Unspecified) => {
309 Err(ConversionError::message("public key variant is unspecified"))
310 },
311 Err(error) => Err(ConversionError::with_source(
312 format!("unknown public key variant {variant}"),
313 error,
314 )),
315 }
316}
317
318impl From<&PublicKey> for proto::primitives::PublicKey {
319 fn from(value: &PublicKey) -> Self {
320 Self {
321 variant: proto::primitives::PublicKeyVariant::EcdsaK256Keccak as i32,
322 encoded: value.to_bytes(),
323 }
324 }
325}
326
327impl From<PublicKey> for proto::primitives::PublicKey {
328 fn from(value: PublicKey) -> Self {
329 (&value).into()
330 }
331}
332
333impl TryFrom<proto::primitives::PublicKey> for PublicKey {
334 type Error = ConversionError;
335
336 fn try_from(value: proto::primitives::PublicKey) -> Result<Self, Self::Error> {
337 Self::try_from(&value)
338 }
339}
340
341impl TryFrom<&proto::primitives::PublicKey> for PublicKey {
342 type Error = ConversionError;
343
344 fn try_from(value: &proto::primitives::PublicKey) -> Result<Self, Self::Error> {
345 decode_public_key_variant(value.variant).context("variant")?;
346 Self::read_from_bytes(&value.encoded)
347 .map_err(|error| ConversionError::deserialization("PublicKey", error))
348 .map_err(|error| error.context("encoded"))
349 }
350}
351
352fn decode_signature_variant(variant: i32) -> Result<(), ConversionError> {
356 match proto::primitives::SignatureVariant::try_from(variant) {
357 Ok(proto::primitives::SignatureVariant::EcdsaK256Keccak) => Ok(()),
358 Ok(proto::primitives::SignatureVariant::Unspecified) => {
359 Err(ConversionError::message("signature variant is unspecified"))
360 },
361 Err(error) => Err(ConversionError::with_source(
362 format!("unknown signature variant {variant}"),
363 error,
364 )),
365 }
366}
367
368impl From<&Signature> for proto::primitives::Signature {
369 fn from(value: &Signature) -> Self {
370 Self {
371 variant: proto::primitives::SignatureVariant::EcdsaK256Keccak as i32,
372 encoded: value.to_bytes(),
373 }
374 }
375}
376
377impl From<Signature> for proto::primitives::Signature {
378 fn from(value: Signature) -> Self {
379 (&value).into()
380 }
381}
382
383impl TryFrom<proto::primitives::Signature> for Signature {
384 type Error = ConversionError;
385
386 fn try_from(value: proto::primitives::Signature) -> Result<Self, Self::Error> {
387 Self::try_from(&value)
388 }
389}
390
391impl TryFrom<&proto::primitives::Signature> for Signature {
392 type Error = ConversionError;
393
394 fn try_from(value: &proto::primitives::Signature) -> Result<Self, Self::Error> {
395 decode_signature_variant(value.variant).context("variant")?;
396 Self::read_from_bytes(&value.encoded)
397 .map_err(|error| ConversionError::deserialization("Signature", error))
398 .map_err(|error| error.context("encoded"))
399 }
400}
401
402#[cfg(test)]
403mod tests {
404 use alloc::string::ToString;
405 use alloc::vec;
406 use core::error::Error;
407
408 use assert_matches::assert_matches;
409 use miden_protocol::testing::dummy_execution_proof;
410 use miden_protocol::testing::random_secret_key::random_secret_key;
411 use miden_protocol::utils::serde::DeserializationError;
412
413 use super::*;
414
415 #[test]
416 fn felt_roundtrips_zero_and_rejects_the_field_order() {
417 for felt in [Felt::ZERO, Felt::from(42_u32)] {
418 let encoded = proto::primitives::Felt::from(felt);
419 assert_eq!(encoded.value, felt.as_canonical_u64());
420 assert_eq!(Felt::try_from(encoded).unwrap(), felt);
421 }
422
423 let error = Felt::try_from(proto::primitives::Felt { value: Felt::ORDER }).unwrap_err();
424 assert_matches!(
425 error
426 .source()
427 .and_then(|source| source.downcast_ref::<<Felt as TryFrom<u64>>::Error>()),
428 Some(source) if source.as_u64() == Felt::ORDER
429 );
430 }
431
432 #[test]
433 fn word_roundtrips_and_rejects_invalid_lengths() {
434 let felt = Felt::from(42_u32);
435
436 let word = Word::new([felt, Felt::ZERO, Felt::ONE, Felt::new_unchecked(7)]);
437 assert_eq!(Word::try_from(proto::primitives::Word::from(word)).unwrap(), word);
438
439 let error = Word::try_from(proto::primitives::Word { encoded: vec![0; 31] }).unwrap_err();
440 assert_eq!(error.to_string(), "word.encoded: expected exactly 32 bytes, got 31");
441 }
442
443 #[test]
444 fn public_key_and_signature_roundtrip_with_ecdsa_k256_keccak_variants() {
445 let signing_key = random_secret_key();
446 let public_key = signing_key.public_key();
447 let signature = signing_key.sign(Word::empty());
448
449 let encoded_public_key = proto::primitives::PublicKey::from(&public_key);
450 assert_eq!(
451 encoded_public_key.variant,
452 proto::primitives::PublicKeyVariant::EcdsaK256Keccak as i32
453 );
454 assert_eq!(PublicKey::try_from(encoded_public_key).unwrap(), public_key);
455
456 let encoded_signature = proto::primitives::Signature::from(&signature);
457 assert_eq!(
458 encoded_signature.variant,
459 proto::primitives::SignatureVariant::EcdsaK256Keccak as i32
460 );
461 assert_eq!(Signature::try_from(encoded_signature).unwrap(), signature);
462 }
463
464 #[test]
465 fn public_key_and_signature_reject_malformed_encodings() {
466 let public_key_error = PublicKey::try_from(proto::primitives::PublicKey {
467 variant: proto::primitives::PublicKeyVariant::EcdsaK256Keccak as i32,
468 encoded: vec![],
469 })
470 .unwrap_err();
471 assert_matches!(
472 public_key_error
473 .source()
474 .and_then(Error::source)
475 .and_then(|source| source.downcast_ref::<DeserializationError>()),
476 Some(DeserializationError::UnexpectedEOF)
477 );
478
479 let signature_error = Signature::try_from(proto::primitives::Signature {
480 variant: proto::primitives::SignatureVariant::EcdsaK256Keccak as i32,
481 encoded: vec![],
482 })
483 .unwrap_err();
484 assert_matches!(
485 signature_error
486 .source()
487 .and_then(Error::source)
488 .and_then(|source| source.downcast_ref::<DeserializationError>()),
489 Some(DeserializationError::UnexpectedEOF)
490 );
491 }
492
493 #[test]
494 fn public_key_and_signature_reject_unspecified_variants_before_decoding_bytes() {
495 let public_key_error =
496 PublicKey::try_from(proto::primitives::PublicKey { variant: 0, encoded: vec![] })
497 .unwrap_err();
498 assert_eq!(public_key_error.to_string(), "variant: public key variant is unspecified");
499
500 let signature_error =
501 Signature::try_from(proto::primitives::Signature { variant: 0, encoded: vec![] })
502 .unwrap_err();
503 assert_eq!(signature_error.to_string(), "variant: signature variant is unspecified");
504 }
505
506 #[test]
507 fn public_key_and_signature_reject_unknown_variants_before_decoding_bytes() {
508 let public_key_error = PublicKey::try_from(proto::primitives::PublicKey {
509 variant: i32::MAX,
510 encoded: vec![],
511 })
512 .unwrap_err();
513 assert_eq!(public_key_error.to_string(), "variant: unknown public key variant 2147483647");
514
515 let signature_error = Signature::try_from(proto::primitives::Signature {
516 variant: i32::MAX,
517 encoded: vec![],
518 })
519 .unwrap_err();
520 assert_eq!(signature_error.to_string(), "variant: unknown signature variant 2147483647");
521 }
522
523 #[test]
524 fn execution_proof_roundtrips() {
525 let proof = dummy_execution_proof();
526 let encoded = proto::primitives::ExecutionProof::from(&proof);
527 assert_eq!(ExecutionProof::try_from(encoded).unwrap(), proof);
528 }
529
530 #[test]
531 fn execution_proof_rejects_unversioned_wire_bytes() {
532 let proof = dummy_execution_proof();
533 let compatibility = proof.compatibility();
534 let compatibility_len = 1
535 + compatibility.vm_verifier_roots().to_vec().to_bytes().len()
536 + compatibility.pvm_verifier_roots().to_vec().to_bytes().len();
537 let unversioned = proof.to_bytes()[compatibility_len..].to_vec();
538
539 let error =
540 ExecutionProof::try_from(proto::primitives::ExecutionProof { encoded: unversioned })
541 .unwrap_err();
542
543 assert!(error.to_string().starts_with("encoded:"));
544 }
545
546 #[test]
547 fn mast_forest_roundtrips() {
548 let mast = MastForest::new();
549 let encoded = proto::primitives::MastForest::from(&mast);
550 assert_eq!(MastForest::try_from(encoded).unwrap(), mast);
551 }
552}