miden_objects/
lib.rs

1#![no_std]
2
3#[macro_use]
4extern crate alloc;
5
6#[cfg(feature = "std")]
7extern crate std;
8
9pub mod account;
10pub mod asset;
11pub mod batch;
12pub mod block;
13pub mod note;
14pub mod transaction;
15
16#[cfg(any(feature = "testing", test))]
17pub mod testing;
18
19mod constants;
20mod errors;
21
22// RE-EXPORTS
23// ================================================================================================
24
25pub use constants::*;
26pub use errors::{
27    AccountDeltaError, AccountError, AccountIdError, AccountTreeError, AssetError, AssetVaultError,
28    BatchAccountUpdateError, FungibleFaucetError, NetworkIdError, NoteError, NullifierTreeError,
29    PartialBlockchainError, ProposedBatchError, ProposedBlockError, ProvenBatchError,
30    ProvenTransactionError, TokenSymbolError, TransactionInputError, TransactionOutputError,
31    TransactionScriptError,
32};
33pub use miden_crypto::hash::rpo::{Rpo256 as Hasher, RpoDigest as Digest};
34pub use vm_core::{
35    EMPTY_WORD, Felt, FieldElement, ONE, StarkField, WORD_SIZE, Word, ZERO,
36    mast::{MastForest, MastNodeId},
37    prettier::PrettyPrint,
38};
39
40pub mod assembly {
41    pub use assembly::{
42        Assembler, AssemblyError, Compile, CompileOptions, DefaultSourceManager, KernelLibrary,
43        Library, LibraryNamespace, LibraryPath, SourceManager, Version,
44        ast::{Module, ModuleKind, ProcedureName, QualifiedProcedureName},
45        diagnostics, mast,
46    };
47}
48
49pub mod crypto {
50    pub use miden_crypto::{dsa, hash, merkle, rand, utils};
51}
52
53pub mod utils {
54    use alloc::string::{String, ToString};
55
56    pub use miden_crypto::utils::{HexParseError, bytes_to_hex_string, collections, hex_to_bytes};
57    pub use vm_core::utils::*;
58    use vm_core::{Felt, StarkField, Word};
59
60    pub mod serde {
61        pub use miden_crypto::utils::{
62            ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable,
63        };
64    }
65
66    /// Converts a word into a string of the word's field elements separated by periods, which can
67    /// be used on a MASM `push` instruction to push the word onto the stack.
68    ///
69    /// # Example
70    ///
71    /// ```
72    /// # use miden_objects::{Word, Felt, utils::word_to_masm_push_string};
73    /// let word = Word::from([Felt::new(1), Felt::new(2), Felt::new(3), Felt::new(4)]);
74    /// assert_eq!(word_to_masm_push_string(&word), "1.2.3.4");
75    /// ```
76    pub fn word_to_masm_push_string(word: &Word) -> String {
77        format!("{}.{}.{}.{}", word[0], word[1], word[2], word[3])
78    }
79
80    pub const fn parse_hex_string_as_word(hex: &str) -> Result<[Felt; 4], &'static str> {
81        const fn parse_hex_digit(digit: u8) -> Result<u8, &'static str> {
82            match digit {
83                b'0'..=b'9' => Ok(digit - b'0'),
84                b'A'..=b'F' => Ok(digit - b'A' + 0x0a),
85                b'a'..=b'f' => Ok(digit - b'a' + 0x0a),
86                _ => Err("Invalid hex character"),
87            }
88        }
89        // Enforce and skip the '0x' prefix.
90        let hex_bytes = match hex.as_bytes() {
91            [b'0', b'x', rest @ ..] => rest,
92            _ => return Err("Hex string must have a \"0x\" prefix"),
93        };
94
95        if hex_bytes.len() > 64 {
96            return Err("Hex string has more than 64 characters");
97        }
98
99        let mut felts = [0u64; 4];
100        let mut i = 0;
101        while i < hex_bytes.len() {
102            let hex_digit = match parse_hex_digit(hex_bytes[i]) {
103                // SAFETY: u8 cast to u64 is safe. We cannot use u64::from in const context so we
104                // are forced to cast.
105                Ok(v) => v as u64,
106                Err(e) => return Err(e),
107            };
108
109            // This digit's nibble offset within the felt. We need to invert the nibbles per
110            // byte for endianness reasons i.e. ABCD -> BADC.
111            let inibble = if i % 2 == 0 { (i + 1) % 16 } else { (i - 1) % 16 };
112
113            let value = hex_digit << (inibble * 4);
114            felts[i / 2 / 8] += value;
115
116            i += 1;
117        }
118
119        // Ensure each felt is within bounds as `Felt::new` silently wraps around.
120        // This matches the behaviour of `Digest::try_from(String)`.
121        let mut idx = 0;
122        while idx < felts.len() {
123            if felts[idx] > Felt::MODULUS {
124                return Err("Felt overflow");
125            }
126            idx += 1;
127        }
128
129        Ok([
130            Felt::new(felts[0]),
131            Felt::new(felts[1]),
132            Felt::new(felts[2]),
133            Felt::new(felts[3]),
134        ])
135    }
136
137    /// Construct a new `Digest` from a hex value.
138    ///
139    /// Expects a '0x' prefixed hex string followed by up to 64 hex digits.
140    #[macro_export]
141    macro_rules! digest {
142        ($hex:expr) => {{
143            let felts: [$crate::Felt; 4] = match $crate::utils::parse_hex_string_as_word($hex) {
144                Ok(v) => v,
145                Err(e) => panic!("{}", e),
146            };
147
148            $crate::Digest::new(felts)
149        }};
150    }
151
152    pub fn parse_hex_to_felts(hex: &str) -> Result<[Felt; 4], String> {
153        match parse_hex_string_as_word(hex) {
154            Ok(felts) => Ok(felts),
155            Err(e) => Err(e.to_string()),
156        }
157    }
158
159    #[cfg(test)]
160    mod tests {
161        #[rstest::rstest]
162        #[case::missing_prefix("1234")]
163        #[case::invalid_character("1234567890abcdefg")]
164        #[case::too_long("0xx00000000000000000000000000000000000000000000000000000000000000001")]
165        #[case::overflow_felt0(
166            "0xffffffffffffffff000000000000000000000000000000000000000000000000"
167        )]
168        #[case::overflow_felt1(
169            "0x0000000000000000ffffffffffffffff00000000000000000000000000000000"
170        )]
171        #[case::overflow_felt2(
172            "0x00000000000000000000000000000000ffffffffffffffff0000000000000000"
173        )]
174        #[case::overflow_felt3(
175            "0x000000000000000000000000000000000000000000000000ffffffffffffffff"
176        )]
177        #[should_panic]
178        fn digest_macro_invalid(#[case] bad_input: &str) {
179            digest!(bad_input);
180        }
181
182        #[rstest::rstest]
183        #[case::each_digit("0x1234567890abcdef")]
184        #[case::empty("0x")]
185        #[case::zero("0x0")]
186        #[case::zero_full("0x0000000000000000000000000000000000000000000000000000000000000000")]
187        #[case::one_lsb("0x1")]
188        #[case::one_msb("0x0000000000000000000000000000000000000000000000000000000000000001")]
189        #[case::one_partial("0x0001")]
190        #[case::odd("0x123")]
191        #[case::even("0x1234")]
192        #[case::touch_each_felt(
193            "0x00000000000123450000000000067890000000000000abcd00000000000000ef"
194        )]
195        #[case::unique_felt("0x111111111111111155555555555555559999999999999999cccccccccccccccc")]
196        #[case::digits_on_repeat(
197            "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"
198        )]
199        fn digest_macro(#[case] input: &str) {
200            let uut = digest!(input);
201
202            // Right pad to 64 hex digits (66 including prefix). This is required by the
203            // Digest::try_from(String) implementation.
204            let padded_input = format!("{input:<66}").replace(" ", "0");
205            let expected = crate::Digest::try_from(std::dbg!(padded_input)).unwrap();
206
207            assert_eq!(uut, expected);
208        }
209    }
210}
211
212pub mod vm {
213    pub use miden_verifier::ExecutionProof;
214    pub use vm_core::{AdviceMap, Program, ProgramInfo, sys_events::SystemEvent};
215    pub use vm_processor::{AdviceInputs, RowIndex, StackInputs, StackOutputs};
216}