diff --git a/CHANGELOG.md b/CHANGELOG.md index a18885e6a7..1f0b8f60db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ #### Changes - [BREAKING] `Process` no longer takes ownership of the `Host` (#1571) - [BREAKING] `ProcessState` was converted from a trait to a struct (#1571) +- [BREAKING] `Host` and `AdviceProvider` traits simplified (#1572) ## 0.11.0 (2024-11-04) diff --git a/assembly/src/ast/tests.rs b/assembly/src/ast/tests.rs index a289a65251..e2c81b08d7 100644 --- a/assembly/src/ast/tests.rs +++ b/assembly/src/ast/tests.rs @@ -470,10 +470,7 @@ fn test_ast_parsing_adv_injection() -> Result<(), Report> { use super::AdviceInjectorNode::*; let context = TestContext::new(); - let source = source_file!( - &context, - "begin adv.push_u64div adv.push_mapval adv.insert_mem end" - ); + let source = source_file!(&context, "begin adv.push_u64div adv.push_mapval adv.insert_mem end"); let forms = module!(begin!( inst!(AdvInject(PushU64Div)), inst!(AdvInject(PushMapVal)), diff --git a/miden/tests/integration/operations/decorators/mod.rs b/miden/tests/integration/operations/decorators/mod.rs index d97f66bac2..43e862751a 100644 --- a/miden/tests/integration/operations/decorators/mod.rs +++ b/miden/tests/integration/operations/decorators/mod.rs @@ -1,7 +1,7 @@ use std::sync::Arc; use processor::{ - AdviceProvider, ExecutionError, Host, HostResponse, MastForest, MemAdviceProvider, ProcessState, + AdviceProvider, ExecutionError, Host, MastForest, MemAdviceProvider, ProcessState, }; use vm_core::DebugOptions; @@ -40,31 +40,23 @@ impl Host for TestHost { &mut self.adv_provider } - fn on_event( - &mut self, - _process: ProcessState, - event_id: u32, - ) -> Result { + fn on_event(&mut self, _process: ProcessState, event_id: u32) -> Result<(), ExecutionError> { self.event_handler.push(event_id); - Ok(HostResponse::None) + Ok(()) } - fn on_trace( - &mut self, - _process: ProcessState, - trace_id: u32, - ) -> Result { + fn on_trace(&mut self, _process: ProcessState, trace_id: u32) -> Result<(), ExecutionError> { self.trace_handler.push(trace_id); - Ok(HostResponse::None) + Ok(()) } fn on_debug( &mut self, _process: ProcessState, _options: &DebugOptions, - ) -> Result { + ) -> Result<(), ExecutionError> { self.debug_handler.push(_options.to_string()); - Ok(HostResponse::None) + Ok(()) } fn get_mast_forest(&self, _node_digest: &prover::Digest) -> Option> { diff --git a/processor/src/host/advice/injectors/dsa.rs b/processor/src/host/advice/dsa.rs similarity index 98% rename from processor/src/host/advice/injectors/dsa.rs rename to processor/src/host/advice/dsa.rs index e0f8056134..0311999c10 100644 --- a/processor/src/host/advice/injectors/dsa.rs +++ b/processor/src/host/advice/dsa.rs @@ -1,6 +1,6 @@ use alloc::vec::Vec; -use super::super::{ExecutionError, Felt, Word}; +use super::{ExecutionError, Felt, Word}; /// Gets as input a vector containing a secret key, and a word representing a message and outputs a /// vector of values to be pushed onto the advice stack. diff --git a/processor/src/host/advice/injectors/adv_map_injectors.rs b/processor/src/host/advice/injectors/adv_map_injectors.rs deleted file mode 100644 index 4eab49baf6..0000000000 --- a/processor/src/host/advice/injectors/adv_map_injectors.rs +++ /dev/null @@ -1,187 +0,0 @@ -use alloc::vec::Vec; - -use vm_core::{ - crypto::hash::{Rpo256, RpoDigest}, - EMPTY_WORD, WORD_SIZE, -}; - -use super::super::{AdviceProvider, ExecutionError, Felt, HostResponse}; -use crate::ProcessState; - -// ADVICE MAP INJECTORS -// ================================================================================================ - -/// Reads words from memory at the specified range and inserts them into the advice map under -/// the key `KEY` located at the top of the stack. -/// -/// Inputs: -/// Operand stack: [KEY, start_addr, end_addr, ...] -/// Advice map: {...} -/// -/// Outputs: -/// Operand stack: [KEY, start_addr, end_addr, ...] -/// Advice map: {KEY: values} -/// -/// Where `values` are the elements located in memory[start_addr..end_addr]. -/// -/// # Errors -/// Returns an error: -/// - `start_addr` is greater than or equal to 2^32. -/// - `end_addr` is greater than or equal to 2^32. -/// - `start_addr` > `end_addr`. -pub(crate) fn insert_mem_values_into_adv_map( - advice_provider: &mut A, - process: ProcessState, -) -> Result { - let (start_addr, end_addr) = get_mem_addr_range(process, 4, 5)?; - let ctx = process.ctx(); - - let mut values = Vec::with_capacity(((end_addr - start_addr) as usize) * WORD_SIZE); - for addr in start_addr..end_addr { - let mem_value = process.get_mem_value(ctx, addr).unwrap_or(EMPTY_WORD); - values.extend_from_slice(&mem_value); - } - - let key = process.get_stack_word(0); - advice_provider.insert_into_map(key, values)?; - - Ok(HostResponse::None) -} - -/// Reads two word from the operand stack and inserts them into the advice map under the key -/// defined by the hash of these words. -/// -/// Inputs: -/// Operand stack: [B, A, ...] -/// Advice map: {...} -/// -/// Outputs: -/// Operand stack: [B, A, ...] -/// Advice map: {KEY: [a0, a1, a2, a3, b0, b1, b2, b3]} -/// -/// Where KEY is computed as hash(A || B, domain), where domain is provided via the immediate -/// value. -pub(crate) fn insert_hdword_into_adv_map( - advice_provider: &mut A, - process: ProcessState, - domain: Felt, -) -> Result { - // get the top two words from the stack and hash them to compute the key value - let word0 = process.get_stack_word(0); - let word1 = process.get_stack_word(1); - let key = Rpo256::merge_in_domain(&[word1.into(), word0.into()], domain); - - // build a vector of values from the two word and insert it into the advice map under the - // computed key - let mut values = Vec::with_capacity(2 * WORD_SIZE); - values.extend_from_slice(&word1); - values.extend_from_slice(&word0); - advice_provider.insert_into_map(key.into(), values)?; - - Ok(HostResponse::None) -} - -/// Reads three words from the operand stack and inserts the top two words into the advice map -/// under the key defined by applying an RPO permutation to all three words. -/// -/// Inputs: -/// Operand stack: [B, A, C, ...] -/// Advice map: {...} -/// -/// Outputs: -/// Operand stack: [B, A, C, ...] -/// Advice map: {KEY: [a0, a1, a2, a3, b0, b1, b2, b3]} -/// -/// Where KEY is computed by extracting the digest elements from hperm([C, A, B]). For example, -/// if C is [0, d, 0, 0], KEY will be set as hash(A || B, d). -pub(crate) fn insert_hperm_into_adv_map( - advice_provider: &mut A, - process: ProcessState, -) -> Result { - // read the state from the stack - let mut state = [ - process.get_stack_item(11), - process.get_stack_item(10), - process.get_stack_item(9), - process.get_stack_item(8), - process.get_stack_item(7), - process.get_stack_item(6), - process.get_stack_item(5), - process.get_stack_item(4), - process.get_stack_item(3), - process.get_stack_item(2), - process.get_stack_item(1), - process.get_stack_item(0), - ]; - - // get the values to be inserted into the advice map from the state - let values = state[Rpo256::RATE_RANGE].to_vec(); - - // apply the permutation to the state and extract the key from it - Rpo256::apply_permutation(&mut state); - let key = RpoDigest::new( - state[Rpo256::DIGEST_RANGE] - .try_into() - .expect("failed to extract digest from state"), - ); - - advice_provider.insert_into_map(key.into(), values)?; - - Ok(HostResponse::None) -} - -/// Creates a new Merkle tree in the advice provider by combining Merkle trees with the -/// specified roots. The root of the new tree is defined as `Hash(LEFT_ROOT, RIGHT_ROOT)`. -/// -/// Inputs: -/// Operand stack: [RIGHT_ROOT, LEFT_ROOT, ...] -/// Merkle store: {RIGHT_ROOT, LEFT_ROOT} -/// -/// Outputs: -/// Operand stack: [RIGHT_ROOT, LEFT_ROOT, ...] -/// Merkle store: {RIGHT_ROOT, LEFT_ROOT, hash(LEFT_ROOT, RIGHT_ROOT)} -/// -/// After the operation, both the original trees and the new tree remains in the advice -/// provider (i.e., the input trees are not removed). -/// -/// It is not checked whether the provided roots exist as Merkle trees in the advide providers. -pub(crate) fn merge_merkle_nodes( - advice_provider: &mut A, - process: ProcessState, -) -> Result { - // fetch the arguments from the stack - let lhs = process.get_stack_word(1); - let rhs = process.get_stack_word(0); - - // perform the merge - advice_provider.merge_roots(lhs, rhs)?; - - Ok(HostResponse::None) -} - -// HELPER METHODS -// -------------------------------------------------------------------------------------------- - -/// Reads (start_addr, end_addr) tuple from the specified elements of the operand stack ( -/// without modifying the state of the stack), and verifies that memory range is valid. -fn get_mem_addr_range( - process: ProcessState, - start_idx: usize, - end_idx: usize, -) -> Result<(u32, u32), ExecutionError> { - let start_addr = process.get_stack_item(start_idx).as_int(); - let end_addr = process.get_stack_item(end_idx).as_int(); - - if start_addr > u32::MAX as u64 { - return Err(ExecutionError::MemoryAddressOutOfBounds(start_addr)); - } - if end_addr > u32::MAX as u64 { - return Err(ExecutionError::MemoryAddressOutOfBounds(end_addr)); - } - - if start_addr > end_addr { - return Err(ExecutionError::InvalidMemoryRange { start_addr, end_addr }); - } - - Ok((start_addr as u32, end_addr as u32)) -} diff --git a/processor/src/host/advice/injectors/adv_stack_injectors.rs b/processor/src/host/advice/injectors/adv_stack_injectors.rs deleted file mode 100644 index 60f4678602..0000000000 --- a/processor/src/host/advice/injectors/adv_stack_injectors.rs +++ /dev/null @@ -1,421 +0,0 @@ -use alloc::vec::Vec; - -use vm_core::{QuadExtension, SignatureKind}; -use winter_prover::math::fft; - -use super::super::{AdviceSource, ExecutionError, Felt, HostResponse}; -use crate::{AdviceProvider, Ext2InttError, FieldElement, ProcessState, ZERO}; - -// TYPE ALIASES -// ================================================================================================ -type QuadFelt = QuadExtension; - -// ADVICE STACK INJECTORS -// ================================================================================================ - -/// Pushes a node of the Merkle tree specified by the values on the top of the operand stack -/// onto the advice stack. -/// -/// Inputs: -/// Operand stack: [depth, index, TREE_ROOT, ...] -/// Advice stack: [...] -/// Merkle store: {TREE_ROOT<-NODE} -/// -/// Outputs: -/// Operand stack: [depth, index, TREE_ROOT, ...] -/// Advice stack: [NODE, ...] -/// Merkle store: {TREE_ROOT<-NODE} -/// -/// # Errors -/// Returns an error if: -/// - Merkle tree for the specified root cannot be found in the advice provider. -/// - The specified depth is either zero or greater than the depth of the Merkle tree identified by -/// the specified root. -/// - Value of the node at the specified depth and index is not known to the advice provider. -pub(crate) fn copy_merkle_node_to_adv_stack( - advice_provider: &mut A, - process: ProcessState, -) -> Result { - // read node depth, node index, and tree root from the stack - let depth = process.get_stack_item(0); - let index = process.get_stack_item(1); - let root = [ - process.get_stack_item(5), - process.get_stack_item(4), - process.get_stack_item(3), - process.get_stack_item(2), - ]; - - // look up the node in the advice provider - let node = advice_provider.get_tree_node(root, &depth, &index)?; - - // push the node onto the advice stack with the first element pushed last so that it can - // be popped first (i.e. stack behavior for word) - advice_provider.push_stack(AdviceSource::Value(node[3]))?; - advice_provider.push_stack(AdviceSource::Value(node[2]))?; - advice_provider.push_stack(AdviceSource::Value(node[1]))?; - advice_provider.push_stack(AdviceSource::Value(node[0]))?; - - Ok(HostResponse::None) -} - -/// Pushes a list of field elements onto the advice stack. The list is looked up in the advice -/// map using the specified word from the operand stack as the key. If `include_len` is set to -/// true, the number of elements in the value is also pushed onto the advice stack. -/// -/// Inputs: -/// Operand stack: [..., KEY, ...] -/// Advice stack: [...] -/// Advice map: {KEY: values} -/// -/// Outputs: -/// Operand stack: [..., KEY, ...] -/// Advice stack: [values_len?, values, ...] -/// Advice map: {KEY: values} -/// -/// The `key_offset` value specifies the location of the `KEY` on the stack. For example, -/// offset value of 0 indicates that the top word on the stack should be used as the key, the -/// offset value of 4, indicates that the second word on the stack should be used as the key -/// etc. -/// -/// The valid values of `key_offset` are 0 through 12 (inclusive). -/// -/// # Errors -/// Returns an error if the required key was not found in the key-value map or if stack offset -/// is greater than 12. -pub(crate) fn copy_map_value_to_adv_stack( - advice_provider: &mut A, - process: ProcessState, - include_len: bool, - key_offset: usize, -) -> Result { - if key_offset > 12 { - return Err(ExecutionError::InvalidStackWordOffset(key_offset)); - } - - let key = [ - process.get_stack_item(key_offset + 3), - process.get_stack_item(key_offset + 2), - process.get_stack_item(key_offset + 1), - process.get_stack_item(key_offset), - ]; - advice_provider.push_stack(AdviceSource::Map { key, include_len })?; - - Ok(HostResponse::None) -} - -/// Pushes the result of [u64] division (both the quotient and the remainder) onto the advice -/// stack. -/// -/// Inputs: -/// Operand stack: [b1, b0, a1, a0, ...] -/// Advice stack: [...] -/// -/// Outputs: -/// Operand stack: [b1, b0, a1, a0, ...] -/// Advice stack: [q0, q1, r0, r1, ...] -/// -/// Where (a0, a1) and (b0, b1) are the 32-bit limbs of the dividend and the divisor -/// respectively (with a0 representing the 32 lest significant bits and a1 representing the -/// 32 most significant bits). Similarly, (q0, q1) and (r0, r1) represent the quotient and -/// the remainder respectively. -/// -/// # Errors -/// Returns an error if the divisor is ZERO. -pub(crate) fn push_u64_div_result( - advice_provider: &mut A, - process: ProcessState, -) -> Result { - let divisor_hi = process.get_stack_item(0).as_int(); - let divisor_lo = process.get_stack_item(1).as_int(); - let divisor = (divisor_hi << 32) + divisor_lo; - - if divisor == 0 { - return Err(ExecutionError::DivideByZero(process.clk())); - } - - let dividend_hi = process.get_stack_item(2).as_int(); - let dividend_lo = process.get_stack_item(3).as_int(); - let dividend = (dividend_hi << 32) + dividend_lo; - - let quotient = dividend / divisor; - let remainder = dividend - quotient * divisor; - - let (q_hi, q_lo) = u64_to_u32_elements(quotient); - let (r_hi, r_lo) = u64_to_u32_elements(remainder); - - advice_provider.push_stack(AdviceSource::Value(r_hi))?; - advice_provider.push_stack(AdviceSource::Value(r_lo))?; - advice_provider.push_stack(AdviceSource::Value(q_hi))?; - advice_provider.push_stack(AdviceSource::Value(q_lo))?; - - Ok(HostResponse::None) -} - -/// Given an element in a quadratic extension field on the top of the stack (i.e., a0, b1), -/// computes its multiplicative inverse and push the result onto the advice stack. -/// -/// Inputs: -/// Operand stack: [a1, a0, ...] -/// Advice stack: [...] -/// -/// Outputs: -/// Operand stack: [a1, a0, ...] -/// Advice stack: [b0, b1...] -/// -/// Where (b0, b1) is the multiplicative inverse of the extension field element (a0, a1) at the -/// top of the stack. -/// -/// # Errors -/// Returns an error if the input is a zero element in the extension field. -pub(crate) fn push_ext2_inv_result( - advice_provider: &mut A, - process: ProcessState, -) -> Result { - let coef0 = process.get_stack_item(1); - let coef1 = process.get_stack_item(0); - - let element = QuadFelt::new(coef0, coef1); - if element == QuadFelt::ZERO { - return Err(ExecutionError::DivideByZero(process.clk())); - } - let result = element.inv().to_base_elements(); - - advice_provider.push_stack(AdviceSource::Value(result[1]))?; - advice_provider.push_stack(AdviceSource::Value(result[0]))?; - - Ok(HostResponse::None) -} - -/// Given evaluations of a polynomial over some specified domain, interpolates the evaluations -/// into a polynomial in coefficient form and pushes the result into the advice stack. -/// -/// The interpolation is performed using the iNTT algorithm. The evaluations are expected to be -/// in the quadratic extension. -/// -/// Inputs: -/// Operand stack: [output_size, input_size, input_start_ptr, ...] -/// Advice stack: [...] -/// -/// Outputs: -/// Operand stack: [output_size, input_size, input_start_ptr, ...] -/// Advice stack: [coefficients...] -/// -/// - `input_size` is the number of evaluations (each evaluation is 2 base field elements). Must be -/// a power of 2 and greater 1. -/// - `output_size` is the number of coefficients in the interpolated polynomial (each coefficient -/// is 2 base field elements). Must be smaller than or equal to the number of input evaluations. -/// - `input_start_ptr` is the memory address of the first evaluation. -/// - `coefficients` are the coefficients of the interpolated polynomial such that lowest degree -/// coefficients are located at the top of the advice stack. -/// -/// # Errors -/// Returns an error if: -/// - `input_size` less than or equal to 1, or is not a power of 2. -/// - `output_size` is 0 or is greater than the `input_size`. -/// - `input_ptr` is greater than 2^32. -/// - `input_ptr + input_size / 2` is greater than 2^32. -pub(crate) fn push_ext2_intt_result( - advice_provider: &mut A, - process: ProcessState, -) -> Result { - let output_size = process.get_stack_item(0).as_int() as usize; - let input_size = process.get_stack_item(1).as_int() as usize; - let input_start_ptr = process.get_stack_item(2).as_int(); - - if input_size <= 1 { - return Err(Ext2InttError::DomainSizeTooSmall(input_size as u64).into()); - } - if !input_size.is_power_of_two() { - return Err(Ext2InttError::DomainSizeNotPowerOf2(input_size as u64).into()); - } - if input_start_ptr >= u32::MAX as u64 { - return Err(Ext2InttError::InputStartAddressTooBig(input_start_ptr).into()); - } - if input_size > u32::MAX as usize { - return Err(Ext2InttError::InputSizeTooBig(input_size as u64).into()); - } - - let input_end_ptr = input_start_ptr + (input_size / 2) as u64; - if input_end_ptr > u32::MAX as u64 { - return Err(Ext2InttError::InputEndAddressTooBig(input_end_ptr).into()); - } - - if output_size == 0 { - return Err(Ext2InttError::OutputSizeIsZero.into()); - } - if output_size > input_size { - return Err(Ext2InttError::OutputSizeTooBig(output_size, input_size).into()); - } - - let mut poly = Vec::with_capacity(input_size); - for addr in (input_start_ptr as u32)..(input_end_ptr as u32) { - let word = process - .get_mem_value(process.ctx(), addr) - .ok_or(Ext2InttError::UninitializedMemoryAddress(addr))?; - - poly.push(QuadFelt::new(word[0], word[1])); - poly.push(QuadFelt::new(word[2], word[3])); - } - - let twiddles = fft::get_inv_twiddles::(input_size); - fft::interpolate_poly::(&mut poly, &twiddles); - - for element in QuadFelt::slice_as_base_elements(&poly[..output_size]).iter().rev() { - advice_provider.push_stack(AdviceSource::Value(*element))?; - } - - Ok(HostResponse::None) -} - -/// Pushes values onto the advice stack which are required for verification of a DSA in Miden VM. -/// -/// Inputs: -/// Operand stack: [PK, MSG, ...] -/// Advice stack: [...] -/// -/// Outputs: -/// Operand stack: [PK, MSG, ...] -/// Advice stack: [DATA] -/// -/// Where: -/// - PK is the digest of an expanded public. -/// - MSG is the digest of the message to be signed. -/// - DATA is the needed data for signature verification in the VM. -/// -/// The advice provider is expected to contain the private key associated to the public key PK. -pub(crate) fn push_signature( - advice_provider: &mut A, - process: ProcessState, - kind: SignatureKind, -) -> Result { - let pub_key = process.get_stack_word(0); - let msg = process.get_stack_word(1); - let result: Vec = advice_provider.get_signature(kind, pub_key, msg)?; - for r in result { - advice_provider.push_stack(AdviceSource::Value(r))?; - } - Ok(HostResponse::None) -} - -/// Pushes the number of the leading zeros of the top stack element onto the advice stack. -/// -/// Inputs: -/// Operand stack: [n, ...] -/// Advice stack: [...] -/// -/// Outputs: -/// Operand stack: [n, ...] -/// Advice stack: [leading_zeros, ...] -pub(crate) fn push_leading_zeros( - advice_provider: &mut A, - process: ProcessState, -) -> Result { - push_transformed_stack_top(advice_provider, process, |stack_top| { - Felt::from(stack_top.leading_zeros()) - }) -} - -/// Pushes the number of the trailing zeros of the top stack element onto the advice stack. -/// -/// Inputs: -/// Operand stack: [n, ...] -/// Advice stack: [...] -/// -/// Outputs: -/// Operand stack: [n, ...] -/// Advice stack: [trailing_zeros, ...] -pub(crate) fn push_trailing_zeros( - advice_provider: &mut A, - process: ProcessState, -) -> Result { - push_transformed_stack_top(advice_provider, process, |stack_top| { - Felt::from(stack_top.trailing_zeros()) - }) -} - -/// Pushes the number of the leading ones of the top stack element onto the advice stack. -/// -/// Inputs: -/// Operand stack: [n, ...] -/// Advice stack: [...] -/// -/// Outputs: -/// Operand stack: [n, ...] -/// Advice stack: [leading_ones, ...] -pub(crate) fn push_leading_ones( - advice_provider: &mut A, - process: ProcessState, -) -> Result { - push_transformed_stack_top(advice_provider, process, |stack_top| { - Felt::from(stack_top.leading_ones()) - }) -} - -/// Pushes the number of the trailing ones of the top stack element onto the advice stack. -/// -/// Inputs: -/// Operand stack: [n, ...] -/// Advice stack: [...] -/// -/// Outputs: -/// Operand stack: [n, ...] -/// Advice stack: [trailing_ones, ...] -pub(crate) fn push_trailing_ones( - advice_provider: &mut A, - process: ProcessState, -) -> Result { - push_transformed_stack_top(advice_provider, process, |stack_top| { - Felt::from(stack_top.trailing_ones()) - }) -} - -/// Pushes the base 2 logarithm of the top stack element, rounded down. -/// Inputs: -/// Operand stack: [n, ...] -/// Advice stack: [...] -/// -/// Outputs: -/// Operand stack: [n, ...] -/// Advice stack: [ilog2(n), ...] -/// -/// # Errors -/// Returns an error if the logarithm argument (top stack element) equals ZERO. -pub(crate) fn push_ilog2( - advice_provider: &mut A, - process: ProcessState, -) -> Result { - let n = process.get_stack_item(0).as_int(); - if n == 0 { - return Err(ExecutionError::LogArgumentZero(process.clk())); - } - let ilog2 = Felt::from(n.ilog2()); - advice_provider.push_stack(AdviceSource::Value(ilog2))?; - Ok(HostResponse::None) -} - -// HELPER FUNCTIONS -// ================================================================================================ - -fn u64_to_u32_elements(value: u64) -> (Felt, Felt) { - let hi = Felt::from((value >> 32) as u32); - let lo = Felt::from(value as u32); - (hi, lo) -} - -/// Gets the top stack element, applies a provided function to it and pushes it to the advice -/// provider. -fn push_transformed_stack_top( - advice_provider: &mut A, - process: ProcessState, - f: impl FnOnce(u32) -> Felt, -) -> Result { - let stack_top = process.get_stack_item(0); - let stack_top: u32 = stack_top - .as_int() - .try_into() - .map_err(|_| ExecutionError::NotU32Value(stack_top, ZERO))?; - let transformed_stack_top = f(stack_top); - advice_provider.push_stack(AdviceSource::Value(transformed_stack_top))?; - Ok(HostResponse::None) -} diff --git a/processor/src/host/advice/injectors/merkle_store_injectors.rs b/processor/src/host/advice/injectors/merkle_store_injectors.rs deleted file mode 100644 index bf6166913d..0000000000 --- a/processor/src/host/advice/injectors/merkle_store_injectors.rs +++ /dev/null @@ -1,23 +0,0 @@ -use super::super::{AdviceProvider, ExecutionError, HostResponse, ProcessState}; - -pub(crate) fn update_operand_stack_merkle_node( - advice_provider: &mut A, - process: ProcessState, -) -> Result { - let depth = process.get_stack_item(4); - let index = process.get_stack_item(5); - let old_root = [ - process.get_stack_item(9), - process.get_stack_item(8), - process.get_stack_item(7), - process.get_stack_item(6), - ]; - let new_node = [ - process.get_stack_item(13), - process.get_stack_item(12), - process.get_stack_item(11), - process.get_stack_item(10), - ]; - let (path, _) = advice_provider.update_merkle_node(old_root, &depth, &index, new_node)?; - Ok(HostResponse::MerklePath(path)) -} diff --git a/processor/src/host/advice/injectors/mod.rs b/processor/src/host/advice/injectors/mod.rs deleted file mode 100644 index 79e38bd268..0000000000 --- a/processor/src/host/advice/injectors/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -pub(super) mod adv_map_injectors; -pub(super) mod adv_stack_injectors; -pub(super) mod dsa; -pub(super) mod merkle_store_injectors; -pub(super) mod smt; diff --git a/processor/src/host/advice/injectors/smt.rs b/processor/src/host/advice/injectors/smt.rs deleted file mode 100644 index e3ba8ec6a6..0000000000 --- a/processor/src/host/advice/injectors/smt.rs +++ /dev/null @@ -1,96 +0,0 @@ -use alloc::vec::Vec; - -use vm_core::{ - crypto::{ - hash::RpoDigest, - merkle::{EmptySubtreeRoots, Smt, SMT_DEPTH}, - }, - WORD_SIZE, -}; - -use super::super::{AdviceSource, ExecutionError, Felt, HostResponse, Word}; -use crate::{AdviceProvider, ProcessState}; - -// SMT INJECTORS -// ================================================================================================ - -/// Pushes onto the advice stack the value associated with the specified key in a Sparse -/// Merkle Tree defined by the specified root. -/// -/// If no value was previously associated with the specified key, [ZERO; 4] is pushed onto -/// the advice stack. -/// -/// Inputs: -/// Operand stack: [KEY, ROOT, ...] -/// Advice stack: [...] -/// -/// Outputs: -/// Operand stack: [KEY, ROOT, ...] -/// Advice stack: [VALUE, ...] -/// -/// # Errors -/// Returns an error if the provided Merkle root doesn't exist on the advice provider. -pub(crate) fn push_smtpeek_result( - advice_provider: &mut A, - process: ProcessState, -) -> Result { - let empty_leaf = EmptySubtreeRoots::entry(SMT_DEPTH, SMT_DEPTH); - // fetch the arguments from the operand stack - let key = process.get_stack_word(0); - let root = process.get_stack_word(1); - - // get the node from the SMT for the specified key; this node can be either a leaf node, - // or a root of an empty subtree at the returned depth - let node = advice_provider.get_tree_node(root, &Felt::new(SMT_DEPTH as u64), &key[3])?; - - if node == Word::from(empty_leaf) { - // if the node is a root of an empty subtree, then there is no value associated with - // the specified key - advice_provider.push_stack(AdviceSource::Word(Smt::EMPTY_VALUE))?; - } else { - let leaf_preimage = get_smt_leaf_preimage(advice_provider, node)?; - - for (key_in_leaf, value_in_leaf) in leaf_preimage { - if key == key_in_leaf { - // Found key - push value associated with key, and return - advice_provider.push_stack(AdviceSource::Word(value_in_leaf))?; - - return Ok(HostResponse::None); - } - } - - // if we can't find any key in the leaf that matches `key`, it means no value is associated - // with `key` - advice_provider.push_stack(AdviceSource::Word(Smt::EMPTY_VALUE))?; - } - - Ok(HostResponse::None) -} - -// HELPER METHODS -// -------------------------------------------------------------------------------------------- - -fn get_smt_leaf_preimage( - advice_provider: &A, - node: Word, -) -> Result, ExecutionError> { - let node_bytes = RpoDigest::from(node); - - let kv_pairs = advice_provider - .get_mapped_values(&node_bytes) - .ok_or(ExecutionError::SmtNodeNotFound(node))?; - - if kv_pairs.len() % WORD_SIZE * 2 != 0 { - return Err(ExecutionError::SmtNodePreImageNotValid(node, kv_pairs.len())); - } - - Ok(kv_pairs - .chunks_exact(WORD_SIZE * 2) - .map(|kv_chunk| { - let key = [kv_chunk[0], kv_chunk[1], kv_chunk[2], kv_chunk[3]]; - let value = [kv_chunk[4], kv_chunk[5], kv_chunk[6], kv_chunk[7]]; - - (key, value) - }) - .collect()) -} diff --git a/processor/src/host/advice/mod.rs b/processor/src/host/advice/mod.rs index b78d404e36..40a09fbe69 100644 --- a/processor/src/host/advice/mod.rs +++ b/processor/src/host/advice/mod.rs @@ -2,20 +2,23 @@ use alloc::vec::Vec; use vm_core::{ crypto::{ - hash::RpoDigest, - merkle::{InnerNodeInfo, MerklePath, MerkleStore, NodeIndex, StoreNode}, + hash::{Rpo256, RpoDigest}, + merkle::{ + EmptySubtreeRoots, InnerNodeInfo, MerklePath, MerkleStore, NodeIndex, Smt, StoreNode, + SMT_DEPTH, + }, }, - SignatureKind, + FieldElement, SignatureKind, EMPTY_WORD, WORD_SIZE, ZERO, }; +use winter_prover::math::fft; -use super::HostResponse; -use crate::{ExecutionError, Felt, InputError, ProcessState, Word}; +use crate::{ExecutionError, Ext2InttError, Felt, InputError, ProcessState, QuadFelt, Word}; + +mod dsa; mod inputs; pub use inputs::AdviceInputs; -mod injectors; - mod providers; pub use providers::{MemAdviceProvider, RecAdviceProvider}; @@ -96,14 +99,6 @@ pub trait AdviceProvider: Sized { /// are replaced with the specified values. fn insert_into_map(&mut self, key: Word, values: Vec) -> Result<(), ExecutionError>; - /// Returns a signature on a message using a public key. - fn get_signature( - &self, - kind: SignatureKind, - pub_key: Word, - msg: Word, - ) -> Result, ExecutionError>; - // MERKLE STORE // -------------------------------------------------------------------------------------------- @@ -207,8 +202,20 @@ pub trait AdviceProvider: Sized { fn insert_mem_values_into_adv_map( &mut self, process: ProcessState, - ) -> Result { - injectors::adv_map_injectors::insert_mem_values_into_adv_map(self, process) + ) -> Result<(), ExecutionError> { + let (start_addr, end_addr) = get_mem_addr_range(process, 4, 5)?; + let ctx = process.ctx(); + + let mut values = Vec::with_capacity(((end_addr - start_addr) as usize) * WORD_SIZE); + for addr in start_addr..end_addr { + let mem_value = process.get_mem_value(ctx, addr).unwrap_or(EMPTY_WORD); + values.extend_from_slice(&mem_value); + } + + let key = process.get_stack_word(0); + self.insert_into_map(key, values)?; + + Ok(()) } /// Reads two word from the operand stack and inserts them into the advice map under the key @@ -228,8 +235,20 @@ pub trait AdviceProvider: Sized { &mut self, process: ProcessState, domain: Felt, - ) -> Result { - injectors::adv_map_injectors::insert_hdword_into_adv_map(self, process, domain) + ) -> Result<(), ExecutionError> { + // get the top two words from the stack and hash them to compute the key value + let word0 = process.get_stack_word(0); + let word1 = process.get_stack_word(1); + let key = Rpo256::merge_in_domain(&[word1.into(), word0.into()], domain); + + // build a vector of values from the two word and insert it into the advice map under the + // computed key + let mut values = Vec::with_capacity(2 * WORD_SIZE); + values.extend_from_slice(&word1); + values.extend_from_slice(&word0); + self.insert_into_map(key.into(), values)?; + + Ok(()) } /// Reads three words from the operand stack and inserts the top two words into the advice map @@ -245,11 +264,37 @@ pub trait AdviceProvider: Sized { /// /// Where KEY is computed by extracting the digest elements from hperm([C, A, B]). For example, /// if C is [0, d, 0, 0], KEY will be set as hash(A || B, d). - fn insert_hperm_into_adv_map( - &mut self, - process: ProcessState, - ) -> Result { - injectors::adv_map_injectors::insert_hperm_into_adv_map(self, process) + fn insert_hperm_into_adv_map(&mut self, process: ProcessState) -> Result<(), ExecutionError> { + // read the state from the stack + let mut state = [ + process.get_stack_item(11), + process.get_stack_item(10), + process.get_stack_item(9), + process.get_stack_item(8), + process.get_stack_item(7), + process.get_stack_item(6), + process.get_stack_item(5), + process.get_stack_item(4), + process.get_stack_item(3), + process.get_stack_item(2), + process.get_stack_item(1), + process.get_stack_item(0), + ]; + + // get the values to be inserted into the advice map from the state + let values = state[Rpo256::RATE_RANGE].to_vec(); + + // apply the permutation to the state and extract the key from it + Rpo256::apply_permutation(&mut state); + let key = RpoDigest::new( + state[Rpo256::DIGEST_RANGE] + .try_into() + .expect("failed to extract digest from state"), + ); + + self.insert_into_map(key.into(), values)?; + + Ok(()) } /// Creates a new Merkle tree in the advice provider by combining Merkle trees with the @@ -267,11 +312,15 @@ pub trait AdviceProvider: Sized { /// provider (i.e., the input trees are not removed). /// /// It is not checked whether the provided roots exist as Merkle trees in the advide providers. - fn merge_merkle_nodes( - &mut self, - process: ProcessState, - ) -> Result { - injectors::adv_map_injectors::merge_merkle_nodes(self, process) + fn merge_merkle_nodes(&mut self, process: ProcessState) -> Result<(), ExecutionError> { + // fetch the arguments from the stack + let lhs = process.get_stack_word(1); + let rhs = process.get_stack_word(0); + + // perform the merge + self.merge_roots(lhs, rhs)?; + + Ok(()) } // DEFAULT ADVICE STACK INJECTORS @@ -299,8 +348,24 @@ pub trait AdviceProvider: Sized { fn copy_merkle_node_to_adv_stack( &mut self, process: ProcessState, - ) -> Result { - injectors::adv_stack_injectors::copy_merkle_node_to_adv_stack(self, process) + ) -> Result<(), ExecutionError> { + let depth = process.get_stack_item(0); + let index = process.get_stack_item(1); + let root = [ + process.get_stack_item(5), + process.get_stack_item(4), + process.get_stack_item(3), + process.get_stack_item(2), + ]; + + let node = self.get_tree_node(root, &depth, &index)?; + + self.push_stack(AdviceSource::Value(node[3]))?; + self.push_stack(AdviceSource::Value(node[2]))?; + self.push_stack(AdviceSource::Value(node[1]))?; + self.push_stack(AdviceSource::Value(node[0]))?; + + Ok(()) } /// Pushes a list of field elements onto the advice stack. The list is looked up in the advice @@ -332,13 +397,20 @@ pub trait AdviceProvider: Sized { process: ProcessState, include_len: bool, key_offset: usize, - ) -> Result { - injectors::adv_stack_injectors::copy_map_value_to_adv_stack( - self, - process, - include_len, - key_offset, - ) + ) -> Result<(), ExecutionError> { + if key_offset > 12 { + return Err(ExecutionError::InvalidStackWordOffset(key_offset)); + } + + let key = [ + process.get_stack_item(key_offset + 3), + process.get_stack_item(key_offset + 2), + process.get_stack_item(key_offset + 1), + process.get_stack_item(key_offset), + ]; + self.push_stack(AdviceSource::Map { key, include_len })?; + + Ok(()) } /// Pushes the result of [u64] division (both the quotient and the remainder) onto the advice @@ -359,11 +431,31 @@ pub trait AdviceProvider: Sized { /// /// # Errors /// Returns an error if the divisor is ZERO. - fn push_u64_div_result( - &mut self, - process: ProcessState, - ) -> Result { - injectors::adv_stack_injectors::push_u64_div_result(self, process) + fn push_u64_div_result(&mut self, process: ProcessState) -> Result<(), ExecutionError> { + let divisor_hi = process.get_stack_item(0).as_int(); + let divisor_lo = process.get_stack_item(1).as_int(); + let divisor = (divisor_hi << 32) + divisor_lo; + + if divisor == 0 { + return Err(ExecutionError::DivideByZero(process.clk())); + } + + let dividend_hi = process.get_stack_item(2).as_int(); + let dividend_lo = process.get_stack_item(3).as_int(); + let dividend = (dividend_hi << 32) + dividend_lo; + + let quotient = dividend / divisor; + let remainder = dividend - quotient * divisor; + + let (q_hi, q_lo) = u64_to_u32_elements(quotient); + let (r_hi, r_lo) = u64_to_u32_elements(remainder); + + self.push_stack(AdviceSource::Value(r_hi))?; + self.push_stack(AdviceSource::Value(r_lo))?; + self.push_stack(AdviceSource::Value(q_hi))?; + self.push_stack(AdviceSource::Value(q_lo))?; + + Ok(()) } /// Given an element in a quadratic extension field on the top of the stack (i.e., a0, b1), @@ -382,11 +474,20 @@ pub trait AdviceProvider: Sized { /// /// # Errors /// Returns an error if the input is a zero element in the extension field. - fn push_ext2_inv_result( - &mut self, - process: ProcessState, - ) -> Result { - injectors::adv_stack_injectors::push_ext2_inv_result(self, process) + fn push_ext2_inv_result(&mut self, process: ProcessState) -> Result<(), ExecutionError> { + let coef0 = process.get_stack_item(1); + let coef1 = process.get_stack_item(0); + + let element = QuadFelt::new(coef0, coef1); + if element == QuadFelt::ZERO { + return Err(ExecutionError::DivideByZero(process.clk())); + } + let result = element.inv().to_base_elements(); + + self.push_stack(AdviceSource::Value(result[1]))?; + self.push_stack(AdviceSource::Value(result[0]))?; + + Ok(()) } /// Given evaluations of a polynomial over some specified domain, interpolates the evaluations @@ -418,11 +519,54 @@ pub trait AdviceProvider: Sized { /// - `output_size` is 0 or is greater than the `input_size`. /// - `input_ptr` is greater than 2^32. /// - `input_ptr + input_size / 2` is greater than 2^32. - fn push_ext2_intt_result( - &mut self, - process: ProcessState, - ) -> Result { - injectors::adv_stack_injectors::push_ext2_intt_result(self, process) + fn push_ext2_intt_result(&mut self, process: ProcessState) -> Result<(), ExecutionError> { + let output_size = process.get_stack_item(0).as_int() as usize; + let input_size = process.get_stack_item(1).as_int() as usize; + let input_start_ptr = process.get_stack_item(2).as_int(); + + if input_size <= 1 { + return Err(Ext2InttError::DomainSizeTooSmall(input_size as u64).into()); + } + if !input_size.is_power_of_two() { + return Err(Ext2InttError::DomainSizeNotPowerOf2(input_size as u64).into()); + } + if input_start_ptr >= u32::MAX as u64 { + return Err(Ext2InttError::InputStartAddressTooBig(input_start_ptr).into()); + } + if input_size > u32::MAX as usize { + return Err(Ext2InttError::InputSizeTooBig(input_size as u64).into()); + } + + let input_end_ptr = input_start_ptr + (input_size / 2) as u64; + if input_end_ptr > u32::MAX as u64 { + return Err(Ext2InttError::InputEndAddressTooBig(input_end_ptr).into()); + } + + if output_size == 0 { + return Err(Ext2InttError::OutputSizeIsZero.into()); + } + if output_size > input_size { + return Err(Ext2InttError::OutputSizeTooBig(output_size, input_size).into()); + } + + let mut poly = Vec::with_capacity(input_size); + for addr in (input_start_ptr as u32)..(input_end_ptr as u32) { + let word = process + .get_mem_value(process.ctx(), addr) + .ok_or(Ext2InttError::UninitializedMemoryAddress(addr))?; + + poly.push(QuadFelt::new(word[0], word[1])); + poly.push(QuadFelt::new(word[2], word[3])); + } + + let twiddles = fft::get_inv_twiddles::(input_size); + fft::interpolate_poly::(&mut poly, &twiddles); + + for element in QuadFelt::slice_as_base_elements(&poly[..output_size]).iter().rev() { + self.push_stack(AdviceSource::Value(*element))?; + } + + Ok(()) } /// Pushes values onto the advice stack which are required for verification of a DSA in Miden @@ -446,8 +590,14 @@ pub trait AdviceProvider: Sized { &mut self, process: ProcessState, kind: SignatureKind, - ) -> Result { - injectors::adv_stack_injectors::push_signature(self, process, kind) + ) -> Result<(), ExecutionError> { + let pub_key = process.get_stack_word(0); + let msg = process.get_stack_word(1); + let result: Vec = self.get_signature(kind, pub_key, msg)?; + for r in result { + self.push_stack(AdviceSource::Value(r))?; + } + Ok(()) } /// Pushes the number of the leading zeros of the top stack element onto the advice stack. @@ -459,11 +609,8 @@ pub trait AdviceProvider: Sized { /// Outputs: /// Operand stack: [n, ...] /// Advice stack: [leading_zeros, ...] - fn push_leading_zeros( - &mut self, - process: ProcessState, - ) -> Result { - injectors::adv_stack_injectors::push_leading_zeros(self, process) + fn push_leading_zeros(&mut self, process: ProcessState) -> Result<(), ExecutionError> { + push_transformed_stack_top(self, process, |stack_top| Felt::from(stack_top.leading_zeros())) } /// Pushes the number of the trailing zeros of the top stack element onto the advice stack. @@ -475,11 +622,10 @@ pub trait AdviceProvider: Sized { /// Outputs: /// Operand stack: [n, ...] /// Advice stack: [trailing_zeros, ...] - fn push_trailing_zeros( - &mut self, - process: ProcessState, - ) -> Result { - injectors::adv_stack_injectors::push_trailing_zeros(self, process) + fn push_trailing_zeros(&mut self, process: ProcessState) -> Result<(), ExecutionError> { + push_transformed_stack_top(self, process, |stack_top| { + Felt::from(stack_top.trailing_zeros()) + }) } /// Pushes the number of the leading ones of the top stack element onto the advice stack. @@ -491,8 +637,8 @@ pub trait AdviceProvider: Sized { /// Outputs: /// Operand stack: [n, ...] /// Advice stack: [leading_ones, ...] - fn push_leading_ones(&mut self, process: ProcessState) -> Result { - injectors::adv_stack_injectors::push_leading_ones(self, process) + fn push_leading_ones(&mut self, process: ProcessState) -> Result<(), ExecutionError> { + push_transformed_stack_top(self, process, |stack_top| Felt::from(stack_top.leading_ones())) } /// Pushes the number of the trailing ones of the top stack element onto the advice stack. @@ -504,11 +650,8 @@ pub trait AdviceProvider: Sized { /// Outputs: /// Operand stack: [n, ...] /// Advice stack: [trailing_ones, ...] - fn push_trailing_ones( - &mut self, - process: ProcessState, - ) -> Result { - injectors::adv_stack_injectors::push_trailing_ones(self, process) + fn push_trailing_ones(&mut self, process: ProcessState) -> Result<(), ExecutionError> { + push_transformed_stack_top(self, process, |stack_top| Felt::from(stack_top.trailing_ones())) } /// Pushes the base 2 logarithm of the top stack element, rounded down. @@ -522,8 +665,14 @@ pub trait AdviceProvider: Sized { /// /// # Errors /// Returns an error if the logarithm argument (top stack element) equals ZERO. - fn push_ilog2(&mut self, process: ProcessState) -> Result { - injectors::adv_stack_injectors::push_ilog2(self, process) + fn push_ilog2(&mut self, process: ProcessState) -> Result<(), ExecutionError> { + let n = process.get_stack_item(0).as_int(); + if n == 0 { + return Err(ExecutionError::LogArgumentZero(process.clk())); + } + let ilog2 = Felt::from(n.ilog2()); + self.push_stack(AdviceSource::Value(ilog2))?; + Ok(()) } // DEFAULT MERKLE STORE INJECTORS @@ -545,8 +694,23 @@ pub trait AdviceProvider: Sized { fn update_operand_stack_merkle_node( &mut self, process: ProcessState, - ) -> Result { - injectors::merkle_store_injectors::update_operand_stack_merkle_node(self, process) + ) -> Result { + let depth = process.get_stack_item(4); + let index = process.get_stack_item(5); + let old_root = [ + process.get_stack_item(9), + process.get_stack_item(8), + process.get_stack_item(7), + process.get_stack_item(6), + ]; + let new_node = [ + process.get_stack_item(13), + process.get_stack_item(12), + process.get_stack_item(11), + process.get_stack_item(10), + ]; + let (path, _) = self.update_merkle_node(old_root, &depth, &index, new_node)?; + Ok(path) } // DEFAULT MERKLE STORE EXTRACTORS @@ -573,7 +737,7 @@ pub trait AdviceProvider: Sized { fn get_operand_stack_merkle_path( &mut self, process: ProcessState, - ) -> Result { + ) -> Result { let depth = process.get_stack_item(4); let index = process.get_stack_item(5); let root = [ @@ -582,7 +746,7 @@ pub trait AdviceProvider: Sized { process.get_stack_item(7), process.get_stack_item(6), ]; - self.get_merkle_path(root, &depth, &index).map(HostResponse::MerklePath) + self.get_merkle_path(root, &depth, &index) } // DEFAULT SMT INJECTORS @@ -607,11 +771,56 @@ pub trait AdviceProvider: Sized { /// /// # Panics /// Will panic as unimplemented if the target depth is `64`. - fn push_smtpeek_result( - &mut self, - process: ProcessState, - ) -> Result { - injectors::smt::push_smtpeek_result(self, process) + fn push_smtpeek_result(&mut self, process: ProcessState) -> Result<(), ExecutionError> { + let empty_leaf = EmptySubtreeRoots::entry(SMT_DEPTH, SMT_DEPTH); + // fetch the arguments from the operand stack + let key = process.get_stack_word(0); + let root = process.get_stack_word(1); + + // get the node from the SMT for the specified key; this node can be either a leaf node, + // or a root of an empty subtree at the returned depth + let node = self.get_tree_node(root, &Felt::new(SMT_DEPTH as u64), &key[3])?; + + if node == Word::from(empty_leaf) { + // if the node is a root of an empty subtree, then there is no value associated with + // the specified key + self.push_stack(AdviceSource::Word(Smt::EMPTY_VALUE))?; + } else { + let leaf_preimage = get_smt_leaf_preimage(self, node)?; + + for (key_in_leaf, value_in_leaf) in leaf_preimage { + if key == key_in_leaf { + // Found key - push value associated with key, and return + self.push_stack(AdviceSource::Word(value_in_leaf))?; + + return Ok(()); + } + } + + // if we can't find any key in the leaf that matches `key`, it means no value is + // associated with `key` + self.push_stack(AdviceSource::Word(Smt::EMPTY_VALUE))?; + } + Ok(()) + } + + // DEFAULT MERKLE STORE EXTRACTORS + // -------------------------------------------------------------------------------------------- + + /// Returns a signature on a message using a public key. + fn get_signature( + &self, + kind: SignatureKind, + pub_key: Word, + msg: Word, + ) -> Result, ExecutionError> { + let pk_sk = self + .get_mapped_values(&pub_key.into()) + .ok_or(ExecutionError::AdviceMapKeyNotFound(pub_key))?; + + match kind { + SignatureKind::RpoFalcon512 => dsa::falcon_sign(pk_sk, msg), + } } } @@ -693,3 +902,78 @@ where T::merge_roots(self, lhs, rhs) } } + +// HELPER METHODS +// -------------------------------------------------------------------------------------------- + +/// Reads (start_addr, end_addr) tuple from the specified elements of the operand stack ( +/// without modifying the state of the stack), and verifies that memory range is valid. +fn get_mem_addr_range( + process: ProcessState, + start_idx: usize, + end_idx: usize, +) -> Result<(u32, u32), ExecutionError> { + let start_addr = process.get_stack_item(start_idx).as_int(); + let end_addr = process.get_stack_item(end_idx).as_int(); + + if start_addr > u32::MAX as u64 { + return Err(ExecutionError::MemoryAddressOutOfBounds(start_addr)); + } + if end_addr > u32::MAX as u64 { + return Err(ExecutionError::MemoryAddressOutOfBounds(end_addr)); + } + + if start_addr > end_addr { + return Err(ExecutionError::InvalidMemoryRange { start_addr, end_addr }); + } + + Ok((start_addr as u32, end_addr as u32)) +} + +fn u64_to_u32_elements(value: u64) -> (Felt, Felt) { + let hi = Felt::from((value >> 32) as u32); + let lo = Felt::from(value as u32); + (hi, lo) +} + +/// Gets the top stack element, applies a provided function to it and pushes it to the advice +/// provider. +fn push_transformed_stack_top( + advice_provider: &mut A, + process: ProcessState, + f: impl FnOnce(u32) -> Felt, +) -> Result<(), ExecutionError> { + let stack_top = process.get_stack_item(0); + let stack_top: u32 = stack_top + .as_int() + .try_into() + .map_err(|_| ExecutionError::NotU32Value(stack_top, ZERO))?; + let transformed_stack_top = f(stack_top); + advice_provider.push_stack(AdviceSource::Value(transformed_stack_top))?; + Ok(()) +} + +fn get_smt_leaf_preimage( + advice_provider: &A, + node: Word, +) -> Result, ExecutionError> { + let node_bytes = RpoDigest::from(node); + + let kv_pairs = advice_provider + .get_mapped_values(&node_bytes) + .ok_or(ExecutionError::SmtNodeNotFound(node))?; + + if kv_pairs.len() % WORD_SIZE * 2 != 0 { + return Err(ExecutionError::SmtNodePreImageNotValid(node, kv_pairs.len())); + } + + Ok(kv_pairs + .chunks_exact(WORD_SIZE * 2) + .map(|kv_chunk| { + let key = [kv_chunk[0], kv_chunk[1], kv_chunk[2], kv_chunk[3]]; + let value = [kv_chunk[4], kv_chunk[5], kv_chunk[6], kv_chunk[7]]; + + (key, value) + }) + .collect()) +} diff --git a/processor/src/host/advice/providers.rs b/processor/src/host/advice/providers.rs index d7afc6b789..5ef1f55007 100644 --- a/processor/src/host/advice/providers.rs +++ b/processor/src/host/advice/providers.rs @@ -1,10 +1,8 @@ use alloc::{collections::BTreeMap, vec::Vec}; -use vm_core::SignatureKind; - use super::{ - injectors, AdviceInputs, AdviceProvider, AdviceSource, ExecutionError, Felt, MerklePath, - MerkleStore, NodeIndex, RpoDigest, StoreNode, Word, + AdviceInputs, AdviceProvider, AdviceSource, ExecutionError, Felt, MerklePath, MerkleStore, + NodeIndex, RpoDigest, StoreNode, Word, }; use crate::{ utils::collections::{KvMap, RecordingMap}, @@ -108,22 +106,6 @@ where Ok(()) } - fn get_signature( - &self, - kind: SignatureKind, - pub_key: Word, - msg: Word, - ) -> Result, ExecutionError> { - let pk_sk = self - .map - .get(&pub_key.into()) - .ok_or(ExecutionError::AdviceMapKeyNotFound(pub_key))?; - - match kind { - SignatureKind::RpoFalcon512 => injectors::dsa::falcon_sign(pk_sk, msg), - } - } - // ADVICE MAP // -------------------------------------------------------------------------------------------- @@ -268,10 +250,6 @@ impl AdviceProvider for MemAdviceProvider { self.provider.insert_into_map(key, values) } - fn get_signature(&self, kind: SignatureKind, pub_key: Word, msg: Word) -> Result, ExecutionError> { - self.provider.get_signature(kind, pub_key, msg) - } - fn get_mapped_values(&self, key: &RpoDigest) -> Option<&[Felt]> { self.provider.get_mapped_values(key) } @@ -378,10 +356,6 @@ impl AdviceProvider for RecAdviceProvider { self.provider.insert_into_map(key, values) } - fn get_signature(&self, kind: SignatureKind, pub_key: Word, msg: Word) -> Result, ExecutionError> { - self.provider.get_signature(kind, pub_key, msg) - } - fn get_mapped_values(&self, key: &RpoDigest) -> Option<&[Felt]> { self.provider.get_mapped_values(key) } diff --git a/processor/src/host/mod.rs b/processor/src/host/mod.rs index f31be242ec..6fb711f9eb 100644 --- a/processor/src/host/mod.rs +++ b/processor/src/host/mod.rs @@ -1,12 +1,8 @@ use alloc::sync::Arc; -use vm_core::{ - crypto::{hash::RpoDigest, merkle::MerklePath}, - mast::MastForest, - DebugOptions, Word, -}; +use vm_core::{crypto::hash::RpoDigest, mast::MastForest, DebugOptions}; -use super::{ExecutionError, Felt, ProcessState}; +use super::{ExecutionError, ProcessState}; use crate::MemAdviceProvider; pub(super) mod advice; @@ -22,10 +18,10 @@ pub use mast_forest_store::{MastForestStore, MemMastForestStore}; // ================================================================================================ /// Defines an interface by which the VM can interact with the host. -/// +/// /// There are four main categories of interactions between the VM and the host: /// 1. accessing the advice provider, -/// 2. getting a library's MAST forest, +/// 2. getting a library's MAST forest, /// 3. handling advice events (which internally mutates the advice provider), and /// 4. handling debug and trace events. pub trait Host { @@ -47,11 +43,7 @@ pub trait Host { // -------------------------------------------------------------------------------------------- /// Handles the event emitted from the VM. - fn on_event( - &mut self, - _process: ProcessState, - _event_id: u32, - ) -> Result { + fn on_event(&mut self, _process: ProcessState, _event_id: u32) -> Result<(), ExecutionError> { #[cfg(feature = "std")] std::println!( "Event with id {} emitted at step {} in context {}", @@ -59,7 +51,7 @@ pub trait Host { _process.clk(), _process.ctx() ); - Ok(HostResponse::None) + Ok(()) } /// Handles the debug request from the VM. @@ -67,18 +59,14 @@ pub trait Host { &mut self, _process: ProcessState, _options: &DebugOptions, - ) -> Result { + ) -> Result<(), ExecutionError> { #[cfg(feature = "std")] debug::print_debug_info(_process, _options); - Ok(HostResponse::None) + Ok(()) } /// Handles the trace emitted from the VM. - fn on_trace( - &mut self, - _process: ProcessState, - _trace_id: u32, - ) -> Result { + fn on_trace(&mut self, _process: ProcessState, _trace_id: u32) -> Result<(), ExecutionError> { #[cfg(feature = "std")] std::println!( "Trace with id {} emitted at step {} in context {}", @@ -86,7 +74,7 @@ pub trait Host { _process.clk(), _process.ctx() ); - Ok(HostResponse::None) + Ok(()) } /// Handles the failure of the assertion instruction. @@ -121,23 +109,15 @@ where &mut self, process: ProcessState, options: &DebugOptions, - ) -> Result { + ) -> Result<(), ExecutionError> { H::on_debug(self, process, options) } - fn on_event( - &mut self, - process: ProcessState, - event_id: u32, - ) -> Result { + fn on_event(&mut self, process: ProcessState, event_id: u32) -> Result<(), ExecutionError> { H::on_event(self, process, event_id) } - fn on_trace( - &mut self, - process: ProcessState, - trace_id: u32, - ) -> Result { + fn on_trace(&mut self, process: ProcessState, trace_id: u32) -> Result<(), ExecutionError> { H::on_trace(self, process, trace_id) } @@ -146,55 +126,6 @@ where } } -// HOST RESPONSE -// ================================================================================================ - -/// Response returned by the host upon successful execution of a [Host] function. -#[derive(Debug)] -pub enum HostResponse { - MerklePath(MerklePath), - DoubleWord([Word; 2]), - Word(Word), - Element(Felt), - None, -} - -impl From for MerklePath { - fn from(response: HostResponse) -> Self { - match response { - HostResponse::MerklePath(path) => path, - _ => panic!("expected MerklePath, but got {:?}", response), - } - } -} - -impl From for Word { - fn from(response: HostResponse) -> Self { - match response { - HostResponse::Word(word) => word, - _ => panic!("expected Word, but got {:?}", response), - } - } -} - -impl From for [Word; 2] { - fn from(response: HostResponse) -> Self { - match response { - HostResponse::DoubleWord(word) => word, - _ => panic!("expected DoubleWord, but got {:?}", response), - } - } -} - -impl From for Felt { - fn from(response: HostResponse) -> Self { - match response { - HostResponse::Element(element) => element, - _ => panic!("expected Element, but got {:?}", response), - } - } -} - // DEFAULT HOST IMPLEMENTATION // ================================================================================================ diff --git a/processor/src/lib.rs b/processor/src/lib.rs index 65020bc6f3..20ff0ac2f0 100644 --- a/processor/src/lib.rs +++ b/processor/src/lib.rs @@ -50,7 +50,7 @@ pub use host::{ advice::{ AdviceInputs, AdviceMap, AdviceProvider, AdviceSource, MemAdviceProvider, RecAdviceProvider, }, - DefaultHost, Host, HostResponse, MastForestStore, MemMastForestStore, + DefaultHost, Host, MastForestStore, MemMastForestStore, }; mod chiplets; @@ -587,9 +587,9 @@ impl Process { Decorator::Advice(injector) => { let advice_provider = host.advice_provider_mut(); let process_state: ProcessState = self.into(); - let _ = match injector { + match injector { AdviceInjector::MerkleNodeMerge => { - advice_provider.merge_merkle_nodes(process_state)? + advice_provider.merge_merkle_nodes(process_state)?; }, AdviceInjector::MerkleNodeToStack => { advice_provider.copy_merkle_node_to_adv_stack(process_state)? @@ -597,7 +597,7 @@ impl Process { AdviceInjector::MapValueToStack { include_len, key_offset } => advice_provider .copy_map_value_to_adv_stack(process_state, *include_len, *key_offset)?, AdviceInjector::UpdateMerkleNode => { - advice_provider.update_operand_stack_merkle_node(process_state)? + let _ = advice_provider.update_operand_stack_merkle_node(process_state)?; }, AdviceInjector::U64Div => advice_provider.push_u64_div_result(process_state)?, AdviceInjector::Ext2Inv => { @@ -616,18 +616,18 @@ impl Process { AdviceInjector::ILog2 => advice_provider.push_ilog2(process_state)?, AdviceInjector::MemToMap => { - advice_provider.insert_mem_values_into_adv_map(process_state)? + advice_provider.insert_mem_values_into_adv_map(process_state)?; }, AdviceInjector::HdwordToMap { domain } => { - advice_provider.insert_hdword_into_adv_map(process_state, *domain)? + advice_provider.insert_hdword_into_adv_map(process_state, *domain)?; }, AdviceInjector::HpermToMap => { - advice_provider.insert_hperm_into_adv_map(process_state)? + advice_provider.insert_hperm_into_adv_map(process_state)?; }, AdviceInjector::SigToStack { kind } => { advice_provider.push_signature(process_state, *kind)? }, - }; + } }, Decorator::Debug(options) => { if self.decoder.in_debug_mode() { @@ -644,7 +644,7 @@ impl Process { host.on_trace(self.into(), *id)?; } }, - } + }; Ok(()) } diff --git a/processor/src/operations/crypto_ops.rs b/processor/src/operations/crypto_ops.rs index f9405cd6df..acbecd545a 100644 --- a/processor/src/operations/crypto_ops.rs +++ b/processor/src/operations/crypto_ops.rs @@ -76,7 +76,7 @@ impl Process { // get a Merkle path from the advice provider for the specified root and node index. // the path is expected to be of the specified depth. let path: MerklePath = - host.advice_provider_mut().get_operand_stack_merkle_path(self.into())?.into(); + host.advice_provider_mut().get_operand_stack_merkle_path(self.into())?; // use hasher to compute the Merkle root of the path let (addr, computed_root) = self.chiplets.build_merkle_root(node, &path, index); @@ -148,7 +148,7 @@ impl Process { // specified depth. if the new node is the root of a tree, this instruction will append the // whole sub-tree to this node. let path: MerklePath = - host.advice_provider_mut().update_operand_stack_merkle_node(self.into())?.into(); + host.advice_provider_mut().update_operand_stack_merkle_node(self.into())?; assert_eq!(path.len(), depth.as_int() as usize);