Skip to main content

miden_objects/conversion/
primitives.rs

1use 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
32// FELT
33// ================================================================================================
34
35impl 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
63// WORD
64// ================================================================================================
65
66impl 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
96// EXECUTION PROOF
97// ================================================================================================
98
99impl 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
129// MAST FOREST
130// ================================================================================================
131
132impl 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
162// ADVICE INPUTS
163// ================================================================================================
164
165impl 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
302// PUBLIC KEY
303// ================================================================================================
304
305fn 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
352// SIGNATURE
353// ================================================================================================
354
355fn 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}