miden_stdlib/handlers/
mod.rs

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