Skip to main content

miden_core_lib/handlers/
mod.rs

1use miden_core::{Felt, WORD_SIZE};
2use miden_processor::ProcessorState;
3
4pub mod aead_decrypt;
5use alloc::vec::Vec;
6use core::mem::size_of;
7
8/// Number of bytes packed into each u32 field element.
9///
10/// Used for converting between byte arrays and u32-packed field elements in memory.
11pub(crate) const BYTES_PER_U32: usize = size_of::<u32>();
12
13pub mod ecdsa;
14pub mod eddsa_ed25519;
15pub mod falcon_div;
16pub mod keccak256;
17pub mod sha512;
18pub mod smt_peek;
19pub mod sorted_array;
20pub mod u128_div;
21pub mod u64_div;
22
23// HELPER FUNCTIONS
24// ================================================================================================
25
26/// Converts a u64 value into two u32 elements (high and low parts).
27fn u64_to_u32_elements(value: u64) -> (Felt, Felt) {
28    let hi = Felt::from_u32((value >> 32) as u32);
29    let lo = Felt::from_u32(value as u32);
30    (hi, lo)
31}
32
33/// Reads a contiguous region of memory elements.
34///
35/// This is a safe wrapper around memory reads that:
36/// - Validates the starting address fits in u32
37/// - Validates the starting address is word-aligned (multiple of 4)
38/// - Validates the length doesn't overflow when converted to u32
39/// - Uses checked arithmetic to compute the end address
40/// - Returns `None` if any validation fails or if any memory location is uninitialized
41///
42/// # Arguments
43/// * `process` - Process state to read memory from
44/// * `start_ptr` - Starting address (u64 from stack), must be word-aligned
45/// * `len` - Number of elements to read (u64)
46///
47/// # Returns
48/// `Some(Vec<Felt>)` with `len` elements, or `None` if any check fails
49///
50/// # Example
51/// ```ignore
52/// let elements = read_memory_region(process, src_ptr, num_elements)
53///     .ok_or(MyError::MemoryReadFailed)?;
54/// ```
55pub(crate) fn read_memory_region(
56    process: &ProcessorState,
57    start_ptr: u64,
58    len: u64,
59) -> Option<alloc::vec::Vec<Felt>> {
60    // Validate inputs fit in u32
61    let start_addr: u32 = start_ptr.try_into().ok()?;
62    let len_u32: u32 = len.try_into().ok()?;
63
64    // Enforce word alignment (required for crypto_stream, mem_stream operations)
65    if !start_addr.is_multiple_of(4) {
66        return None;
67    }
68
69    // Calculate end address with overflow check
70    let end_addr = start_addr.checked_add(len_u32)?;
71
72    // Read all elements in the range from the current execution context
73    let ctx = process.ctx();
74    (start_addr..end_addr).map(|addr| process.get_mem_value(ctx, addr)).collect()
75}
76
77/// Reads packed u32 values from memory and returns them as a byte vector.
78///
79/// This function reads field elements from memory where each element contains a u32 value
80/// packed in little-endian byte order. It's commonly used for reading precompile inputs
81/// (e.g., Keccak256, ECDSA) where data is stored as packed bytes in memory.
82///
83/// # Memory Layout
84/// - Each field element stores 4 bytes in little-endian format: `felt[i] =
85///   u32::from_le_bytes([byte[4*i], byte[4*i+1], byte[4*i+2], byte[4*i+3]])`
86/// - The function reads `⌈len_bytes/4⌉` field elements from memory
87/// - Memory addresses range from `start` to `start + ⌈len_bytes/4⌉` (exclusive)
88///
89/// # Arguments
90/// - `process`: The process state containing memory to read from
91/// - `start`: Starting memory address (must be word-aligned, i.e., divisible by 4)
92/// - `len_bytes`: Number of bytes to read from memory
93///
94/// # Returns
95/// A vector containing exactly `len_bytes` bytes read from memory.
96///
97/// # Errors
98/// Returns an error if:
99/// - `start` address is not word-aligned (not divisible by 4)
100/// - Address arithmetic overflows (start + length exceeds u32::MAX)
101/// - Any memory location in the range cannot be read (uninitialized or out of bounds)
102/// - Any field element value exceeds u32::MAX
103/// - Padding bytes in the final u32 are non-zero (when `len_bytes` is not a multiple of 4)
104///
105/// # Examples
106/// ```ignore
107/// // Read 5 bytes from address 0x100
108/// // Memory layout: addr[0x100] = 0x04030201, addr[0x104] = 0x00000005
109/// let bytes = read_memory_packed_u32(process, 0x100, 5)?;
110/// // Returns: [0x01, 0x02, 0x03, 0x04, 0x05]
111/// ```
112pub(crate) fn read_memory_packed_u32(
113    process: &ProcessorState,
114    start: u64,
115    len_bytes: usize,
116) -> Result<Vec<u8>, MemoryReadError> {
117    // Validate word alignment
118    if !start.is_multiple_of(WORD_SIZE as u64) {
119        return Err(MemoryReadError::UnalignedAddress { address: start });
120    }
121
122    // Calculate number of field elements to read
123    let len_felt = len_bytes.div_ceil(BYTES_PER_U32);
124    let end = start
125        .checked_add(len_felt as u64)
126        .ok_or(MemoryReadError::AddressOverflow { start, len_bytes })?;
127
128    // Convert to u32 addresses
129    let start_u32 = start
130        .try_into()
131        .map_err(|_| MemoryReadError::AddressOverflow { start, len_bytes })?;
132    let end_u32 = end
133        .try_into()
134        .map_err(|_| MemoryReadError::AddressOverflow { start, len_bytes })?;
135
136    // Read field elements and unpack to bytes
137    let len_padded = len_bytes
138        .checked_next_multiple_of(BYTES_PER_U32)
139        .ok_or(MemoryReadError::AddressOverflow { start, len_bytes })?;
140
141    // Allocate buffer with 4-byte alignment
142    let mut out = Vec::with_capacity(len_padded);
143
144    let ctx = process.ctx();
145    for address in start_u32..end_u32 {
146        let felt = process
147            .get_mem_value(ctx, address)
148            .ok_or(MemoryReadError::MemoryAccessFailed { address })?;
149
150        let value = felt.as_canonical_u64();
151        // Unpack field elements to bytes (little-endian)
152        let packed: u32 =
153            value.try_into().map_err(|_| MemoryReadError::InvalidValue { value, address })?;
154
155        out.extend(packed.to_le_bytes());
156    }
157
158    // Validate zero-padding in the final u32
159    for (offset, &byte) in out[len_bytes..].iter().enumerate() {
160        if byte != 0 {
161            return Err(MemoryReadError::InvalidPadding {
162                value: byte,
163                position: len_bytes + offset,
164            });
165        }
166    }
167
168    out.truncate(len_bytes);
169    Ok(out)
170}
171
172// ERROR TYPES
173// ================================================================================================
174
175/// Error types that can occur during memory reading operations.
176#[derive(Debug, thiserror::Error)]
177pub(crate) enum MemoryReadError {
178    /// Address overflow during conversion or arithmetic.
179    #[error("address overflow: start={start}, len_bytes={len_bytes}")]
180    AddressOverflow { start: u64, len_bytes: usize },
181
182    /// Address is not word-aligned (not divisible by 4).
183    #[error("address {address} is not word-aligned (must be divisible by 4)")]
184    UnalignedAddress { address: u64 },
185
186    /// Failed to read from memory at the specified address.
187    #[error("failed to read memory at address {address}")]
188    MemoryAccessFailed { address: u32 },
189
190    /// Field element value exceeds u32::MAX.
191    #[error("field element value {value} at address {address} exceeds u32::MAX")]
192    InvalidValue { value: u64, address: u32 },
193
194    /// Non-zero padding byte found in unused portion.
195    #[error("non-zero padding byte {value:#x} at byte position {position}")]
196    InvalidPadding { value: u8, position: usize },
197}