From 469f28e22f1e6fc3b57e562680f5df15cc017287 Mon Sep 17 00:00:00 2001 From: Philippe Laferriere Date: Wed, 20 Nov 2024 08:28:06 -0500 Subject: [PATCH 1/8] refactor: remove parameter from `AdviceInjector::SigToStack` Also renames the variant to `FalconSigToStack` --- assembly/src/ast/instruction/advice.rs | 4 +++- core/src/mast/serialization/decorator.rs | 20 +++++++------------- core/src/mast/serialization/tests.rs | 9 +++------ core/src/operations/decorators/advice.rs | 6 +++--- processor/src/lib.rs | 7 +++---- 5 files changed, 19 insertions(+), 27 deletions(-) diff --git a/assembly/src/ast/instruction/advice.rs b/assembly/src/ast/instruction/advice.rs index d917686ed..cb3a60a74 100644 --- a/assembly/src/ast/instruction/advice.rs +++ b/assembly/src/ast/instruction/advice.rs @@ -56,7 +56,9 @@ impl From<&AdviceInjectorNode> for AdviceInjector { }, InsertHdwordImm { domain } => panic!("unresolved constant '{domain}'"), InsertHperm => Self::HpermToMap, - PushSignature { kind } => Self::SigToStack { kind: (*kind).into() }, + PushSignature { kind } => match kind { + SignatureKind::RpoFalcon512 => Self::FalconSigToStack, + }, } } } diff --git a/core/src/mast/serialization/decorator.rs b/core/src/mast/serialization/decorator.rs index 5dfed28d3..0c6e24e63 100644 --- a/core/src/mast/serialization/decorator.rs +++ b/core/src/mast/serialization/decorator.rs @@ -11,7 +11,7 @@ use super::{ string_table::{StringTable, StringTableBuilder}, DecoratorDataOffset, }; -use crate::{AdviceInjector, AssemblyOp, DebugOptions, Decorator, SignatureKind}; +use crate::{AdviceInjector, AssemblyOp, DebugOptions, Decorator}; /// Represents a serialized [`Decorator`]. /// @@ -106,10 +106,8 @@ impl DecoratorInfo { EncodedDecoratorVariant::AdviceInjectorHpermToMap => { Ok(Decorator::Advice(AdviceInjector::HpermToMap)) }, - EncodedDecoratorVariant::AdviceInjectorSigToStack => { - Ok(Decorator::Advice(AdviceInjector::SigToStack { - kind: SignatureKind::RpoFalcon512, - })) + EncodedDecoratorVariant::AdviceInjectorFalconSigToStack => { + Ok(Decorator::Advice(AdviceInjector::FalconSigToStack)) }, EncodedDecoratorVariant::AssemblyOp => { let num_cycles = data_reader.read_u8()?; @@ -225,7 +223,7 @@ pub enum EncodedDecoratorVariant { AdviceInjectorMemToMap, AdviceInjectorHdwordToMap, AdviceInjectorHpermToMap, - AdviceInjectorSigToStack, + AdviceInjectorFalconSigToStack, AssemblyOp, DebugOptionsStackAll, DebugOptionsStackTop, @@ -272,7 +270,7 @@ impl From<&Decorator> for EncodedDecoratorVariant { AdviceInjector::MemToMap => Self::AdviceInjectorMemToMap, AdviceInjector::HdwordToMap { domain: _ } => Self::AdviceInjectorHdwordToMap, AdviceInjector::HpermToMap => Self::AdviceInjectorHpermToMap, - AdviceInjector::SigToStack { kind: _ } => Self::AdviceInjectorSigToStack, + AdviceInjector::FalconSigToStack => Self::AdviceInjectorFalconSigToStack, }, Decorator::AsmOp(_) => Self::AssemblyOp, Decorator::Debug(debug_options) => match debug_options { @@ -345,11 +343,6 @@ impl DecoratorDataBuilder { Some(data_offset) }, - - // Note: Since there is only 1 variant, we don't need to write any extra bytes. - AdviceInjector::SigToStack { kind } => match kind { - SignatureKind::RpoFalcon512 => None, - }, AdviceInjector::MerkleNodeMerge | AdviceInjector::MerkleNodeToStack | AdviceInjector::UpdateMerkleNode @@ -363,7 +356,8 @@ impl DecoratorDataBuilder { | AdviceInjector::U32Cto | AdviceInjector::ILog2 | AdviceInjector::MemToMap - | AdviceInjector::HpermToMap => None, + | AdviceInjector::HpermToMap + | AdviceInjector::FalconSigToStack => None, }, Decorator::AsmOp(assembly_op) => { self.decorator_data.push(assembly_op.num_cycles()); diff --git a/core/src/mast/serialization/tests.rs b/core/src/mast/serialization/tests.rs index d4b9d3b8b..5f2007130 100644 --- a/core/src/mast/serialization/tests.rs +++ b/core/src/mast/serialization/tests.rs @@ -5,7 +5,7 @@ use miden_crypto::{hash::rpo::RpoDigest, Felt, ONE}; use super::*; use crate::{ mast::MastForestError, operations::Operation, AdviceInjector, AssemblyOp, DebugOptions, - Decorator, SignatureKind, + Decorator, }; /// If this test fails to compile, it means that `Operation` or `Decorator` was changed. Make sure @@ -126,7 +126,7 @@ fn confirm_operation_and_decorator_structure() { AdviceInjector::MemToMap => (), AdviceInjector::HdwordToMap { domain: _ } => (), AdviceInjector::HpermToMap => (), - AdviceInjector::SigToStack { kind: _ } => (), + AdviceInjector::FalconSigToStack => (), }, Decorator::AsmOp(_) => (), Decorator::Debug(debug_options) => match debug_options { @@ -263,10 +263,7 @@ fn serialize_deserialize_all_nodes() { (10, Decorator::Advice(AdviceInjector::MemToMap)), (10, Decorator::Advice(AdviceInjector::HdwordToMap { domain: Felt::new(423) })), (15, Decorator::Advice(AdviceInjector::HpermToMap)), - ( - 15, - Decorator::Advice(AdviceInjector::SigToStack { kind: SignatureKind::RpoFalcon512 }), - ), + (15, Decorator::Advice(AdviceInjector::FalconSigToStack)), ( 15, Decorator::AsmOp(AssemblyOp::new( diff --git a/core/src/operations/decorators/advice.rs b/core/src/operations/decorators/advice.rs index 916ffec3c..4bcc27b49 100644 --- a/core/src/operations/decorators/advice.rs +++ b/core/src/operations/decorators/advice.rs @@ -258,7 +258,7 @@ pub enum AdviceInjector { HpermToMap, /// Reads two words from the stack and pushes values onto the advice stack which are required - /// for verification of a DSA in Miden VM. + /// for verification of Falcon DSA in Miden VM. /// /// Inputs: /// Operand stack: [PK, MSG, ...] @@ -270,7 +270,7 @@ pub enum AdviceInjector { /// /// Where PK is the public key corresponding to the signing key, MSG is the message, SIG_DATA /// is the signature data. - SigToStack { kind: SignatureKind }, + FalconSigToStack, } impl crate::prettier::PrettyPrint for AdviceInjector { @@ -306,7 +306,7 @@ impl fmt::Display for AdviceInjector { Self::MemToMap => write!(f, "mem_to_map"), Self::HdwordToMap { domain } => write!(f, "hdword_to_map.{domain}"), Self::HpermToMap => write!(f, "hperm_to_map"), - Self::SigToStack { kind } => write!(f, "sig_to_stack.{kind}"), + Self::FalconSigToStack => write!(f, "sig_to_stack.{}", SignatureKind::RpoFalcon512), } } } diff --git a/processor/src/lib.rs b/processor/src/lib.rs index 1ab1b1c47..040a56b6f 100644 --- a/processor/src/lib.rs +++ b/processor/src/lib.rs @@ -26,7 +26,7 @@ use vm_core::{ mast::{ BasicBlockNode, CallNode, DynNode, JoinNode, LoopNode, OpBatch, SplitNode, OP_GROUP_SIZE, }, - Decorator, DecoratorIterator, FieldElement, + Decorator, DecoratorIterator, FieldElement, SignatureKind, }; pub use winter_prover::matrix::ColMatrix; @@ -633,9 +633,8 @@ impl Process { AdviceInjector::HpermToMap => { advice_provider.insert_hperm_into_adv_map(process_state)?; }, - AdviceInjector::SigToStack { kind } => { - advice_provider.push_signature(process_state, *kind)? - }, + AdviceInjector::FalconSigToStack => advice_provider + .push_signature(process_state, SignatureKind::RpoFalcon512)?, } }, Decorator::Debug(options) => { From 0e4054e3c40145322843f7e5e844d9da481770f3 Mon Sep 17 00:00:00 2001 From: Philippe Laferriere Date: Wed, 20 Nov 2024 08:51:47 -0500 Subject: [PATCH 2/8] refactor: remove `key_offset` parameter from `AdviceInjector::MapValueToStack` In preparation for converting advice injectors to instructions. This parameter was never used, and is not crucial - the caller can always put the key on top of the stack before calling this injector. --- assembly/src/ast/instruction/advice.rs | 18 +------ assembly/src/ast/visit.rs | 10 +--- assembly/src/parser/grammar.lalrpop | 10 +--- core/src/mast/serialization/decorator.rs | 8 ++- core/src/mast/serialization/tests.rs | 10 +--- core/src/operations/decorators/advice.rs | 19 +++---- docs/src/user_docs/assembly/io_operations.md | 4 +- .../operations/decorators/advice.rs | 53 ------------------- processor/src/errors.rs | 4 -- processor/src/host/advice/mod.rs | 13 ++--- processor/src/lib.rs | 4 +- 11 files changed, 25 insertions(+), 128 deletions(-) diff --git a/assembly/src/ast/instruction/advice.rs b/assembly/src/ast/instruction/advice.rs index cb3a60a74..1bd1d34b6 100644 --- a/assembly/src/ast/instruction/advice.rs +++ b/assembly/src/ast/instruction/advice.rs @@ -18,9 +18,7 @@ pub enum AdviceInjectorNode { PushExt2intt, PushSmtPeek, PushMapVal, - PushMapValImm { offset: ImmU8 }, PushMapValN, - PushMapValNImm { offset: ImmU8 }, PushMtNode, InsertMem, InsertHdword, @@ -36,18 +34,8 @@ impl From<&AdviceInjectorNode> for AdviceInjector { PushU64Div => Self::U64Div, PushExt2intt => Self::Ext2Intt, PushSmtPeek => Self::SmtPeek, - PushMapVal => Self::MapValueToStack { include_len: false, key_offset: 0 }, - PushMapValImm { offset: ImmU8::Value(offset) } => Self::MapValueToStack { - include_len: false, - key_offset: offset.into_inner() as usize, - }, - PushMapValImm { offset } => panic!("unresolved constant '{offset}'"), - PushMapValN => Self::MapValueToStack { include_len: true, key_offset: 0 }, - PushMapValNImm { offset: ImmU8::Value(offset) } => Self::MapValueToStack { - include_len: true, - key_offset: offset.into_inner() as usize, - }, - PushMapValNImm { offset } => panic!("unresolved constant '{offset}'"), + PushMapVal => Self::MapValueToStack { include_len: false }, + PushMapValN => Self::MapValueToStack { include_len: true }, PushMtNode => Self::MerkleNodeToStack, InsertMem => Self::MemToMap, InsertHdword => Self::HdwordToMap { domain: ZERO }, @@ -76,9 +64,7 @@ impl fmt::Display for AdviceInjectorNode { Self::PushExt2intt => write!(f, "push_ext2intt"), Self::PushSmtPeek => write!(f, "push_smtpeek"), Self::PushMapVal => write!(f, "push_mapval"), - Self::PushMapValImm { offset } => write!(f, "push_mapval.{offset}"), Self::PushMapValN => write!(f, "push_mapvaln"), - Self::PushMapValNImm { offset } => write!(f, "push_mapvaln.{offset}"), Self::PushMtNode => write!(f, "push_mtnode"), Self::InsertMem => write!(f, "insert_mem"), Self::InsertHdword => write!(f, "insert_hdword"), diff --git a/assembly/src/ast/visit.rs b/assembly/src/ast/visit.rs index 637a82f81..c16388526 100644 --- a/assembly/src/ast/visit.rs +++ b/assembly/src/ast/visit.rs @@ -354,11 +354,7 @@ where V: ?Sized + Visit, { match node.into_inner() { - AdviceInjectorNode::PushMapValImm { offset: ref imm } - | AdviceInjectorNode::PushMapValNImm { offset: ref imm } - | AdviceInjectorNode::InsertHdwordImm { domain: ref imm } => { - visitor.visit_immediate_u8(imm) - }, + AdviceInjectorNode::InsertHdwordImm { domain: ref imm } => visitor.visit_immediate_u8(imm), AdviceInjectorNode::PushU64Div | AdviceInjectorNode::PushExt2intt | AdviceInjectorNode::PushSmtPeek @@ -804,9 +800,7 @@ where V: ?Sized + VisitMut, { match node.into_inner() { - AdviceInjectorNode::PushMapValImm { offset: ref mut imm } - | AdviceInjectorNode::PushMapValNImm { offset: ref mut imm } - | AdviceInjectorNode::InsertHdwordImm { domain: ref mut imm } => { + AdviceInjectorNode::InsertHdwordImm { domain: ref mut imm } => { visitor.visit_mut_immediate_u8(imm) }, AdviceInjectorNode::PushU64Div diff --git a/assembly/src/parser/grammar.lalrpop b/assembly/src/parser/grammar.lalrpop index dcb469d47..3717d822b 100644 --- a/assembly/src/parser/grammar.lalrpop +++ b/assembly/src/parser/grammar.lalrpop @@ -672,14 +672,8 @@ AdviceInjector: Instruction = { "adv" "." "insert_hperm" => Instruction::AdvInject(AdviceInjectorNode::InsertHperm), "adv" "." "insert_mem" => Instruction::AdvInject(AdviceInjectorNode::InsertMem), "adv" "." "push_ext2intt" => Instruction::AdvInject(AdviceInjectorNode::PushExt2intt), - "adv" "." "push_mapval" > => { - i.map(|offset| Instruction::AdvInject(AdviceInjectorNode::PushMapValImm { offset })) - .unwrap_or(Instruction::AdvInject(AdviceInjectorNode::PushMapVal)) - }, - "adv" "." "push_mapvaln" > => { - i.map(|offset| Instruction::AdvInject(AdviceInjectorNode::PushMapValNImm { offset })) - .unwrap_or(Instruction::AdvInject(AdviceInjectorNode::PushMapValN)) - }, + "adv" "." "push_mapval" => Instruction::AdvInject(AdviceInjectorNode::PushMapVal), + "adv" "." "push_mapvaln" => Instruction::AdvInject(AdviceInjectorNode::PushMapValN), "adv" "." "push_mtnode" => Instruction::AdvInject(AdviceInjectorNode::PushMtNode), "adv" "." "push_sig" "." => Instruction::AdvInject(AdviceInjectorNode::PushSignature { kind }), "adv" "." "push_smtpeek" => Instruction::AdvInject(AdviceInjectorNode::PushSmtPeek), diff --git a/core/src/mast/serialization/decorator.rs b/core/src/mast/serialization/decorator.rs index 0c6e24e63..25af53db4 100644 --- a/core/src/mast/serialization/decorator.rs +++ b/core/src/mast/serialization/decorator.rs @@ -59,9 +59,8 @@ impl DecoratorInfo { }, EncodedDecoratorVariant::AdviceInjectorMapValueToStack => { let include_len = data_reader.read_bool()?; - let key_offset = data_reader.read_usize()?; - Ok(Decorator::Advice(AdviceInjector::MapValueToStack { include_len, key_offset })) + Ok(Decorator::Advice(AdviceInjector::MapValueToStack { include_len })) }, EncodedDecoratorVariant::AdviceInjectorU64Div => { Ok(Decorator::Advice(AdviceInjector::U64Div)) @@ -255,7 +254,7 @@ impl From<&Decorator> for EncodedDecoratorVariant { AdviceInjector::MerkleNodeMerge => Self::AdviceInjectorMerkleNodeMerge, AdviceInjector::MerkleNodeToStack => Self::AdviceInjectorMerkleNodeToStack, AdviceInjector::UpdateMerkleNode => Self::AdviceInjectorUpdateMerkleNode, - AdviceInjector::MapValueToStack { include_len: _, key_offset: _ } => { + AdviceInjector::MapValueToStack { include_len: _ } => { Self::AdviceInjectorMapValueToStack }, AdviceInjector::U64Div => Self::AdviceInjectorU64Div, @@ -332,9 +331,8 @@ impl DecoratorDataBuilder { match decorator { Decorator::Advice(advice_injector) => match advice_injector { - AdviceInjector::MapValueToStack { include_len, key_offset } => { + AdviceInjector::MapValueToStack { include_len } => { self.decorator_data.write_bool(*include_len); - self.decorator_data.write_usize(*key_offset); Some(data_offset) }, diff --git a/core/src/mast/serialization/tests.rs b/core/src/mast/serialization/tests.rs index 5f2007130..02f0cd22c 100644 --- a/core/src/mast/serialization/tests.rs +++ b/core/src/mast/serialization/tests.rs @@ -113,7 +113,7 @@ fn confirm_operation_and_decorator_structure() { AdviceInjector::MerkleNodeMerge => (), AdviceInjector::MerkleNodeToStack => (), AdviceInjector::UpdateMerkleNode => (), - AdviceInjector::MapValueToStack { include_len: _, key_offset: _ } => (), + AdviceInjector::MapValueToStack { include_len: _ } => (), AdviceInjector::U64Div => (), AdviceInjector::Ext2Inv => (), AdviceInjector::Ext2Intt => (), @@ -244,13 +244,7 @@ fn serialize_deserialize_all_nodes() { (0, Decorator::Advice(AdviceInjector::MerkleNodeMerge)), (0, Decorator::Advice(AdviceInjector::MerkleNodeToStack)), (0, Decorator::Advice(AdviceInjector::UpdateMerkleNode)), - ( - 0, - Decorator::Advice(AdviceInjector::MapValueToStack { - include_len: true, - key_offset: 1023, - }), - ), + (0, Decorator::Advice(AdviceInjector::MapValueToStack { include_len: true })), (1, Decorator::Advice(AdviceInjector::U64Div)), (3, Decorator::Advice(AdviceInjector::Ext2Inv)), (5, Decorator::Advice(AdviceInjector::Ext2Intt)), diff --git a/core/src/operations/decorators/advice.rs b/core/src/operations/decorators/advice.rs index 4bcc27b49..29c8d4a74 100644 --- a/core/src/operations/decorators/advice.rs +++ b/core/src/operations/decorators/advice.rs @@ -69,22 +69,15 @@ pub enum AdviceInjector { /// true, the number of elements in the value is also pushed onto the advice stack. /// /// Inputs: - /// Operand stack: [..., KEY, ...] + /// Operand stack: [KEY, ...] /// Advice stack: [...] /// Advice map: {KEY: values} /// /// Outputs: - /// Operand stack: [..., KEY, ...] + /// 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). - MapValueToStack { include_len: bool, key_offset: usize }, + MapValueToStack { include_len: bool }, /// Pushes the result of [u64] division (both the quotient and the remainder) onto the advice /// stack. @@ -287,11 +280,11 @@ impl fmt::Display for AdviceInjector { Self::UpdateMerkleNode => { write!(f, "update_merkle_node") }, - Self::MapValueToStack { include_len, key_offset } => { + Self::MapValueToStack { include_len } => { if *include_len { - write!(f, "map_value_to_stack_with_len.{key_offset}") + write!(f, "map_value_to_stack_with_len") } else { - write!(f, "map_value_to_stack.{key_offset}") + write!(f, "map_value_to_stack") } }, Self::U64Div => write!(f, "div_u64"), diff --git a/docs/src/user_docs/assembly/io_operations.md b/docs/src/user_docs/assembly/io_operations.md index fc8c08608..ca180adc6 100644 --- a/docs/src/user_docs/assembly/io_operations.md +++ b/docs/src/user_docs/assembly/io_operations.md @@ -49,8 +49,8 @@ Advice injectors fall into two categories: (1) injectors which push new data ont | Instruction | Stack_input | Stack_output | Notes | | -------------------------------------------- | -------------------------- | -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| adv.push_mapval
adv.push_mapval.*s* | [K, ... ] | [K, ... ] | Pushes a list of field elements onto the advice stack. The list is looked up in the advice map using word $K$ as the key. If offset $s$ is provided, the key is taken starting from item $s$ on the stack. | -| adv.push_mapvaln
adv.push_mapvaln.*s* | [K, ... ] | [K, ... ] | Pushes a list of field elements together with the number of elements onto the advice stack. The list is looked up in the advice map using word $K$ as the key. If offset $s$ is provided, the key is taken starting from item $s$ on the stack. | +| adv.push_mapval | [K, ... ] | [K, ... ] | Pushes a list of field elements onto the advice stack. The list is looked up in the advice map using word $K$ as the key. | +| adv.push_mapvaln | [K, ... ] | [K, ... ] | Pushes a list of field elements together with the number of elements onto the advice stack (`[n, ele1, ele2, ...]`, where `n` is the number of elements pushed). The list is looked up in the advice map using word $K$ as the key. | | adv.push_mtnode | [d, i, R, ... ] | [d, i, R, ... ] | Pushes a node of a Merkle tree with root $R$ at depth $d$ and index $i$ from Merkle store onto the advice stack. | | adv.push_u64div | [b1, b0, a1, a0, ...] | [b1, b0, a1, a0, ...] | Pushes the result of `u64` division $a / b$ onto the advice stack. Both $a$ and $b$ are represented using 32-bit limbs. The result consists of both the quotient and the remainder. | | adv.push_ext2intt | [osize, isize, iptr, ... ] | [osize, isize, iptr, ... ] | 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. | diff --git a/miden/tests/integration/operations/decorators/advice.rs b/miden/tests/integration/operations/decorators/advice.rs index 4c80720f0..868fb338a 100644 --- a/miden/tests/integration/operations/decorators/advice.rs +++ b/miden/tests/integration/operations/decorators/advice.rs @@ -251,32 +251,6 @@ fn advice_push_mapval() { let test = build_test!(source, &stack_inputs, [], MerkleStore::default(), adv_map); test.expect_stack(&[5, 6, 7, 8]); - // --- test adv.mapval with offset ---------------------------------------- - let source: &str = " - begin - # stack: [4, 3, 2, 1, ...] - - # shift the key on the stack by 2 slots - push.0 push.0 - - # load the advice stack with values from the advice map and drop the key - adv.push_mapval.2 - dropw drop drop - - # move the values from the advice stack to the operand stack - adv_push.4 - swapw dropw - end"; - - let stack_inputs = [1, 2, 3, 4]; - let adv_map = [( - RpoDigest::try_from(stack_inputs).unwrap(), - vec![Felt::new(8), Felt::new(7), Felt::new(6), Felt::new(5)], - )]; - - let test = build_test!(source, &stack_inputs, [], MerkleStore::default(), adv_map); - test.expect_stack(&[5, 6, 7, 8]); - // --- test simple adv.mapvaln -------------------------------------------- let source: &str = " begin @@ -300,33 +274,6 @@ fn advice_push_mapval() { let test = build_test!(source, &stack_inputs, [], MerkleStore::default(), adv_map); test.expect_stack(&[15, 14, 13, 12, 11, 5]); - - // --- test adv.mapval with offset ---------------------------------------- - let source: &str = " - begin - # stack: [4, 3, 2, 1, ...] - - # shift the key on the stack by 2 slots - push.0 push.0 - - # load the advice stack with values from the advice map (including the number - # of elements) and drop the key - adv.push_mapvaln.2 - dropw drop drop - - # move the values from the advice stack to the operand stack - adv_push.6 - swapdw dropw dropw - end"; - - let stack_inputs = [1, 2, 3, 4]; - let adv_map = [( - RpoDigest::try_from(stack_inputs).unwrap(), - vec![Felt::new(11), Felt::new(12), Felt::new(13), Felt::new(14), Felt::new(15)], - )]; - - let test = build_test!(source, &stack_inputs, [], MerkleStore::default(), adv_map); - test.expect_stack(&[15, 14, 13, 12, 11, 5]); } #[test] diff --git a/processor/src/errors.rs b/processor/src/errors.rs index ac86171e2..ae1f548ec 100644 --- a/processor/src/errors.rs +++ b/processor/src/errors.rs @@ -55,7 +55,6 @@ pub enum ExecutionError { end_addr: u64, }, InvalidStackDepthOnReturn(usize), - InvalidStackWordOffset(usize), InvalidTreeDepth { depth: Felt, }, @@ -162,9 +161,6 @@ impl Display for ExecutionError { InvalidStackDepthOnReturn(depth) => { write!(f, "When returning from a call, stack depth must be {MIN_STACK_DEPTH}, but was {depth}") }, - InvalidStackWordOffset(offset) => { - write!(f, "Stack word offset cannot exceed 12, but was {offset}") - }, InvalidTreeDepth { depth } => { write!(f, "The provided {depth} is out of bounds and cannot be represented as an unsigned 8-bits integer") }, diff --git a/processor/src/host/advice/mod.rs b/processor/src/host/advice/mod.rs index d85838d2a..f6f548b07 100644 --- a/processor/src/host/advice/mod.rs +++ b/processor/src/host/advice/mod.rs @@ -393,17 +393,12 @@ pub trait AdviceProvider: Sized { &mut self, process: ProcessState, include_len: bool, - key_offset: usize, ) -> 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), + process.get_stack_item(3), + process.get_stack_item(2), + process.get_stack_item(1), + process.get_stack_item(0), ]; self.push_stack(AdviceSource::Map { key, include_len })?; diff --git a/processor/src/lib.rs b/processor/src/lib.rs index 040a56b6f..ef65b0cde 100644 --- a/processor/src/lib.rs +++ b/processor/src/lib.rs @@ -603,8 +603,8 @@ impl Process { AdviceInjector::MerkleNodeToStack => { advice_provider.copy_merkle_node_to_adv_stack(process_state)? }, - AdviceInjector::MapValueToStack { include_len, key_offset } => advice_provider - .copy_map_value_to_adv_stack(process_state, *include_len, *key_offset)?, + AdviceInjector::MapValueToStack { include_len } => advice_provider + .copy_map_value_to_adv_stack(process_state, *include_len, 0)?, AdviceInjector::UpdateMerkleNode => { let _ = advice_provider.update_operand_stack_merkle_node(process_state)?; }, From 3dfdd2003ed5ce60949988359d7d4d1349943810 Mon Sep 17 00:00:00 2001 From: Philippe Laferriere Date: Wed, 20 Nov 2024 09:13:16 -0500 Subject: [PATCH 3/8] refactor: split `AdviceInjector::MapValueToStack` into `MapValueToStack{N}` --- assembly/src/ast/instruction/advice.rs | 4 +-- core/src/mast/serialization/decorator.rs | 22 ++++++++--------- core/src/mast/serialization/tests.rs | 6 +++-- core/src/operations/decorators/advice.rs | 31 +++++++++++++++--------- processor/src/lib.rs | 8 ++++-- 5 files changed, 42 insertions(+), 29 deletions(-) diff --git a/assembly/src/ast/instruction/advice.rs b/assembly/src/ast/instruction/advice.rs index 1bd1d34b6..472d70e50 100644 --- a/assembly/src/ast/instruction/advice.rs +++ b/assembly/src/ast/instruction/advice.rs @@ -34,8 +34,8 @@ impl From<&AdviceInjectorNode> for AdviceInjector { PushU64Div => Self::U64Div, PushExt2intt => Self::Ext2Intt, PushSmtPeek => Self::SmtPeek, - PushMapVal => Self::MapValueToStack { include_len: false }, - PushMapValN => Self::MapValueToStack { include_len: true }, + PushMapVal => Self::MapValueToStack, + PushMapValN => Self::MapValueToStackN, PushMtNode => Self::MerkleNodeToStack, InsertMem => Self::MemToMap, InsertHdword => Self::HdwordToMap { domain: ZERO }, diff --git a/core/src/mast/serialization/decorator.rs b/core/src/mast/serialization/decorator.rs index 25af53db4..0004e9080 100644 --- a/core/src/mast/serialization/decorator.rs +++ b/core/src/mast/serialization/decorator.rs @@ -58,9 +58,10 @@ impl DecoratorInfo { Ok(Decorator::Advice(AdviceInjector::UpdateMerkleNode)) }, EncodedDecoratorVariant::AdviceInjectorMapValueToStack => { - let include_len = data_reader.read_bool()?; - - Ok(Decorator::Advice(AdviceInjector::MapValueToStack { include_len })) + Ok(Decorator::Advice(AdviceInjector::MapValueToStack)) + }, + EncodedDecoratorVariant::AdviceInjectorMapValueToStackN => { + Ok(Decorator::Advice(AdviceInjector::MapValueToStackN)) }, EncodedDecoratorVariant::AdviceInjectorU64Div => { Ok(Decorator::Advice(AdviceInjector::U64Div)) @@ -210,6 +211,7 @@ pub enum EncodedDecoratorVariant { AdviceInjectorMerkleNodeToStack, AdviceInjectorUpdateMerkleNode, AdviceInjectorMapValueToStack, + AdviceInjectorMapValueToStackN, AdviceInjectorU64Div, AdviceInjectorExt2Inv, AdviceInjectorExt2Intt, @@ -254,9 +256,8 @@ impl From<&Decorator> for EncodedDecoratorVariant { AdviceInjector::MerkleNodeMerge => Self::AdviceInjectorMerkleNodeMerge, AdviceInjector::MerkleNodeToStack => Self::AdviceInjectorMerkleNodeToStack, AdviceInjector::UpdateMerkleNode => Self::AdviceInjectorUpdateMerkleNode, - AdviceInjector::MapValueToStack { include_len: _ } => { - Self::AdviceInjectorMapValueToStack - }, + AdviceInjector::MapValueToStack => Self::AdviceInjectorMapValueToStack, + AdviceInjector::MapValueToStackN => Self::AdviceInjectorMapValueToStackN, AdviceInjector::U64Div => Self::AdviceInjectorU64Div, AdviceInjector::Ext2Inv => Self::AdviceInjectorExt2Inv, AdviceInjector::Ext2Intt => Self::AdviceInjectorExt2Intt, @@ -331,17 +332,14 @@ impl DecoratorDataBuilder { match decorator { Decorator::Advice(advice_injector) => match advice_injector { - AdviceInjector::MapValueToStack { include_len } => { - self.decorator_data.write_bool(*include_len); - - Some(data_offset) - }, AdviceInjector::HdwordToMap { domain } => { self.decorator_data.extend(domain.as_int().to_le_bytes()); Some(data_offset) }, - AdviceInjector::MerkleNodeMerge + AdviceInjector::MapValueToStack + | AdviceInjector::MapValueToStackN + | AdviceInjector::MerkleNodeMerge | AdviceInjector::MerkleNodeToStack | AdviceInjector::UpdateMerkleNode | AdviceInjector::U64Div diff --git a/core/src/mast/serialization/tests.rs b/core/src/mast/serialization/tests.rs index 02f0cd22c..3c0b2adf7 100644 --- a/core/src/mast/serialization/tests.rs +++ b/core/src/mast/serialization/tests.rs @@ -113,7 +113,8 @@ fn confirm_operation_and_decorator_structure() { AdviceInjector::MerkleNodeMerge => (), AdviceInjector::MerkleNodeToStack => (), AdviceInjector::UpdateMerkleNode => (), - AdviceInjector::MapValueToStack { include_len: _ } => (), + AdviceInjector::MapValueToStack => (), + AdviceInjector::MapValueToStackN => (), AdviceInjector::U64Div => (), AdviceInjector::Ext2Inv => (), AdviceInjector::Ext2Intt => (), @@ -244,7 +245,8 @@ fn serialize_deserialize_all_nodes() { (0, Decorator::Advice(AdviceInjector::MerkleNodeMerge)), (0, Decorator::Advice(AdviceInjector::MerkleNodeToStack)), (0, Decorator::Advice(AdviceInjector::UpdateMerkleNode)), - (0, Decorator::Advice(AdviceInjector::MapValueToStack { include_len: true })), + (0, Decorator::Advice(AdviceInjector::MapValueToStack)), + (0, Decorator::Advice(AdviceInjector::MapValueToStackN)), (1, Decorator::Advice(AdviceInjector::U64Div)), (3, Decorator::Advice(AdviceInjector::Ext2Inv)), (5, Decorator::Advice(AdviceInjector::Ext2Intt)), diff --git a/core/src/operations/decorators/advice.rs b/core/src/operations/decorators/advice.rs index 29c8d4a74..66210952b 100644 --- a/core/src/operations/decorators/advice.rs +++ b/core/src/operations/decorators/advice.rs @@ -65,8 +65,7 @@ pub enum AdviceInjector { UpdateMerkleNode, /// 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. + /// map using the specified word from the operand stack as the key. /// /// Inputs: /// Operand stack: [KEY, ...] @@ -75,9 +74,24 @@ pub enum AdviceInjector { /// /// Outputs: /// Operand stack: [KEY, ...] - /// Advice stack: [values_len?, values, ...] + /// Advice stack: [values, ...] /// Advice map: {KEY: values} - MapValueToStack { include_len: bool }, + MapValueToStack, + + /// Pushes a list of field elements onto the advice stack, and then the number of elements + /// pushed. The list is looked up in the advice map using the specified word from the operand + /// stack as the key. + /// + /// Inputs: + /// Operand stack: [KEY, ...] + /// Advice stack: [...] + /// Advice map: {KEY: values} + /// + /// Outputs: + /// Operand stack: [KEY, ...] + /// Advice stack: [num_values, values, ...] + /// Advice map: {KEY: values} + MapValueToStackN, /// Pushes the result of [u64] division (both the quotient and the remainder) onto the advice /// stack. @@ -280,13 +294,8 @@ impl fmt::Display for AdviceInjector { Self::UpdateMerkleNode => { write!(f, "update_merkle_node") }, - Self::MapValueToStack { include_len } => { - if *include_len { - write!(f, "map_value_to_stack_with_len") - } else { - write!(f, "map_value_to_stack") - } - }, + Self::MapValueToStack => write!(f, "map_value_to_stack"), + Self::MapValueToStackN => write!(f, "map_value_to_stack_with_len"), Self::U64Div => write!(f, "div_u64"), Self::Ext2Inv => write!(f, "ext2_inv"), Self::Ext2Intt => write!(f, "ext2_intt"), diff --git a/processor/src/lib.rs b/processor/src/lib.rs index ef65b0cde..279a86000 100644 --- a/processor/src/lib.rs +++ b/processor/src/lib.rs @@ -603,8 +603,12 @@ impl Process { AdviceInjector::MerkleNodeToStack => { advice_provider.copy_merkle_node_to_adv_stack(process_state)? }, - AdviceInjector::MapValueToStack { include_len } => advice_provider - .copy_map_value_to_adv_stack(process_state, *include_len, 0)?, + AdviceInjector::MapValueToStack => { + advice_provider.copy_map_value_to_adv_stack(process_state, false, 0)? + }, + AdviceInjector::MapValueToStackN => { + advice_provider.copy_map_value_to_adv_stack(process_state, true, 0)? + }, AdviceInjector::UpdateMerkleNode => { let _ = advice_provider.update_operand_stack_merkle_node(process_state)?; }, From 7333d55bada584d94d633800826cc3eaeaa7f90c Mon Sep 17 00:00:00 2001 From: Philippe Laferriere Date: Wed, 20 Nov 2024 12:37:11 -0500 Subject: [PATCH 4/8] refactor: split `AdviceInjector::HdWordToMap` into `HdwordToMap{WithDomain}` --- assembly/src/ast/instruction/advice.rs | 13 ++---- assembly/src/ast/visit.rs | 38 +++-------------- assembly/src/parser/grammar.lalrpop | 7 ++-- assembly/src/parser/token.rs | 4 ++ core/src/mast/serialization/decorator.rs | 42 ++++--------------- core/src/mast/serialization/tests.rs | 6 ++- core/src/operations/decorators/advice.rs | 23 +++++++--- docs/src/user_docs/assembly/io_operations.md | 5 ++- .../operations/decorators/advice.rs | 8 ++-- processor/src/lib.rs | 12 ++++-- 10 files changed, 63 insertions(+), 95 deletions(-) diff --git a/assembly/src/ast/instruction/advice.rs b/assembly/src/ast/instruction/advice.rs index 472d70e50..b8b7b0d4b 100644 --- a/assembly/src/ast/instruction/advice.rs +++ b/assembly/src/ast/instruction/advice.rs @@ -2,8 +2,6 @@ use core::fmt; use vm_core::AdviceInjector; -use crate::{ast::ImmU8, Felt, ZERO}; - // ADVICE INJECTOR NODE // ================================================================================================ @@ -22,7 +20,7 @@ pub enum AdviceInjectorNode { PushMtNode, InsertMem, InsertHdword, - InsertHdwordImm { domain: ImmU8 }, + InsertHdwordWithDomain, InsertHperm, PushSignature { kind: SignatureKind }, } @@ -38,11 +36,8 @@ impl From<&AdviceInjectorNode> for AdviceInjector { PushMapValN => Self::MapValueToStackN, PushMtNode => Self::MerkleNodeToStack, InsertMem => Self::MemToMap, - InsertHdword => Self::HdwordToMap { domain: ZERO }, - InsertHdwordImm { domain: ImmU8::Value(domain) } => { - Self::HdwordToMap { domain: Felt::from(domain.into_inner()) } - }, - InsertHdwordImm { domain } => panic!("unresolved constant '{domain}'"), + InsertHdword => Self::HdwordToMap, + InsertHdwordWithDomain => Self::HdwordToMapWithDomain, InsertHperm => Self::HpermToMap, PushSignature { kind } => match kind { SignatureKind::RpoFalcon512 => Self::FalconSigToStack, @@ -68,7 +63,7 @@ impl fmt::Display for AdviceInjectorNode { Self::PushMtNode => write!(f, "push_mtnode"), Self::InsertMem => write!(f, "insert_mem"), Self::InsertHdword => write!(f, "insert_hdword"), - Self::InsertHdwordImm { domain } => write!(f, "insert_hdword.{domain}"), + Self::InsertHdwordWithDomain => write!(f, "insert_hdword_d"), Self::InsertHperm => writeln!(f, "insert_hperm"), Self::PushSignature { kind } => write!(f, "push_sig.{kind}"), } diff --git a/assembly/src/ast/visit.rs b/assembly/src/ast/visit.rs index c16388526..9a9afd8d2 100644 --- a/assembly/src/ast/visit.rs +++ b/assembly/src/ast/visit.rs @@ -347,25 +347,13 @@ where } pub fn visit_advice_injector( - visitor: &mut V, - node: Span<&AdviceInjectorNode>, + _visitor: &mut V, + _node: Span<&AdviceInjectorNode>, ) -> ControlFlow where V: ?Sized + Visit, { - match node.into_inner() { - AdviceInjectorNode::InsertHdwordImm { domain: ref imm } => visitor.visit_immediate_u8(imm), - AdviceInjectorNode::PushU64Div - | AdviceInjectorNode::PushExt2intt - | AdviceInjectorNode::PushSmtPeek - | AdviceInjectorNode::PushMapVal - | AdviceInjectorNode::PushMapValN - | AdviceInjectorNode::PushMtNode - | AdviceInjectorNode::InsertMem - | AdviceInjectorNode::InsertHdword - | AdviceInjectorNode::InsertHperm - | AdviceInjectorNode::PushSignature { .. } => ControlFlow::Continue(()), - } + ControlFlow::Continue(()) } pub fn visit_debug_options(visitor: &mut V, options: Span<&DebugOptions>) -> ControlFlow @@ -793,27 +781,13 @@ where } pub fn visit_mut_advice_injector( - visitor: &mut V, - node: Span<&mut AdviceInjectorNode>, + _visitor: &mut V, + _node: Span<&mut AdviceInjectorNode>, ) -> ControlFlow where V: ?Sized + VisitMut, { - match node.into_inner() { - AdviceInjectorNode::InsertHdwordImm { domain: ref mut imm } => { - visitor.visit_mut_immediate_u8(imm) - }, - AdviceInjectorNode::PushU64Div - | AdviceInjectorNode::PushExt2intt - | AdviceInjectorNode::PushSmtPeek - | AdviceInjectorNode::PushMapVal - | AdviceInjectorNode::PushMapValN - | AdviceInjectorNode::PushMtNode - | AdviceInjectorNode::InsertMem - | AdviceInjectorNode::InsertHdword - | AdviceInjectorNode::InsertHperm - | AdviceInjectorNode::PushSignature { .. } => ControlFlow::Continue(()), - } + ControlFlow::Continue(()) } pub fn visit_mut_debug_options( diff --git a/assembly/src/parser/grammar.lalrpop b/assembly/src/parser/grammar.lalrpop index 3717d822b..f0b6d882f 100644 --- a/assembly/src/parser/grammar.lalrpop +++ b/assembly/src/parser/grammar.lalrpop @@ -43,6 +43,7 @@ extern { "add" => Token::Add, "adv" => Token::Adv, "insert_hdword" => Token::InsertHdword, + "insert_hdword_d" => Token::InsertHdwordWithDomain, "insert_hperm" => Token::InsertHperm, "insert_mem" => Token::InsertMem, "adv_loadw" => Token::AdvLoadw, @@ -665,10 +666,8 @@ Inst: Instruction = { #[inline] AdviceInjector: Instruction = { - "adv" "." "insert_hdword" > => { - domain.map(|domain| Instruction::AdvInject(AdviceInjectorNode::InsertHdwordImm { domain })) - .unwrap_or(Instruction::AdvInject(AdviceInjectorNode::InsertHdword)) - }, + "adv" "." "insert_hdword" => Instruction::AdvInject(AdviceInjectorNode::InsertHdword), + "adv" "." "insert_hdword_d" => Instruction::AdvInject(AdviceInjectorNode::InsertHdwordWithDomain), "adv" "." "insert_hperm" => Instruction::AdvInject(AdviceInjectorNode::InsertHperm), "adv" "." "insert_mem" => Instruction::AdvInject(AdviceInjectorNode::InsertMem), "adv" "." "push_ext2intt" => Instruction::AdvInject(AdviceInjectorNode::PushExt2intt), diff --git a/assembly/src/parser/token.rs b/assembly/src/parser/token.rs index b55201f1a..94c4f73fd 100644 --- a/assembly/src/parser/token.rs +++ b/assembly/src/parser/token.rs @@ -138,6 +138,7 @@ pub enum Token<'input> { Add, Adv, InsertHdword, + InsertHdwordWithDomain, InsertHperm, InsertMem, AdvLoadw, @@ -322,6 +323,7 @@ impl fmt::Display for Token<'_> { Token::Add => write!(f, "add"), Token::Adv => write!(f, "adv"), Token::InsertHdword => write!(f, "insert_hdword"), + Token::InsertHdwordWithDomain => write!(f, "insert_hdword_d"), Token::InsertHperm => write!(f, "insert_hperm"), Token::InsertMem => write!(f, "insert_mem"), Token::AdvLoadw => write!(f, "adv_loadw"), @@ -514,6 +516,7 @@ impl<'input> Token<'input> { Token::Add | Token::Adv | Token::InsertHdword + | Token::InsertHdwordWithDomain | Token::InsertHperm | Token::InsertMem | Token::AdvLoadw @@ -658,6 +661,7 @@ impl<'input> Token<'input> { ("add", Token::Add), ("adv", Token::Adv), ("insert_hdword", Token::InsertHdword), + ("insert_hdword_d", Token::InsertHdwordWithDomain), ("insert_hperm", Token::InsertHperm), ("insert_mem", Token::InsertMem), ("adv_loadw", Token::AdvLoadw), diff --git a/core/src/mast/serialization/decorator.rs b/core/src/mast/serialization/decorator.rs index 0004e9080..5815b988c 100644 --- a/core/src/mast/serialization/decorator.rs +++ b/core/src/mast/serialization/decorator.rs @@ -1,6 +1,5 @@ use alloc::vec::Vec; -use miden_crypto::Felt; use num_derive::{FromPrimitive, ToPrimitive}; use num_traits::{FromPrimitive, ToPrimitive}; use winter_utils::{ @@ -94,14 +93,10 @@ impl DecoratorInfo { Ok(Decorator::Advice(AdviceInjector::MemToMap)) }, EncodedDecoratorVariant::AdviceInjectorHdwordToMap => { - let domain = data_reader.read_u64()?; - let domain = Felt::try_from(domain).map_err(|err| { - DeserializationError::InvalidValue(format!( - "Error when deserializing HdwordToMap decorator domain: {err}" - )) - })?; - - Ok(Decorator::Advice(AdviceInjector::HdwordToMap { domain })) + Ok(Decorator::Advice(AdviceInjector::HdwordToMap)) + }, + EncodedDecoratorVariant::AdviceInjectorHdwordToMapWithDomain => { + Ok(Decorator::Advice(AdviceInjector::HdwordToMapWithDomain)) }, EncodedDecoratorVariant::AdviceInjectorHpermToMap => { Ok(Decorator::Advice(AdviceInjector::HpermToMap)) @@ -223,6 +218,7 @@ pub enum EncodedDecoratorVariant { AdviceInjectorILog2, AdviceInjectorMemToMap, AdviceInjectorHdwordToMap, + AdviceInjectorHdwordToMapWithDomain, AdviceInjectorHpermToMap, AdviceInjectorFalconSigToStack, AssemblyOp, @@ -268,7 +264,8 @@ impl From<&Decorator> for EncodedDecoratorVariant { AdviceInjector::U32Cto => Self::AdviceInjectorU32Cto, AdviceInjector::ILog2 => Self::AdviceInjectorILog2, AdviceInjector::MemToMap => Self::AdviceInjectorMemToMap, - AdviceInjector::HdwordToMap { domain: _ } => Self::AdviceInjectorHdwordToMap, + AdviceInjector::HdwordToMap => Self::AdviceInjectorHdwordToMap, + AdviceInjector::HdwordToMapWithDomain => Self::AdviceInjectorHdwordToMapWithDomain, AdviceInjector::HpermToMap => Self::AdviceInjectorHpermToMap, AdviceInjector::FalconSigToStack => Self::AdviceInjectorFalconSigToStack, }, @@ -331,30 +328,7 @@ impl DecoratorDataBuilder { let data_offset = self.decorator_data.len() as DecoratorDataOffset; match decorator { - Decorator::Advice(advice_injector) => match advice_injector { - AdviceInjector::HdwordToMap { domain } => { - self.decorator_data.extend(domain.as_int().to_le_bytes()); - - Some(data_offset) - }, - AdviceInjector::MapValueToStack - | AdviceInjector::MapValueToStackN - | AdviceInjector::MerkleNodeMerge - | AdviceInjector::MerkleNodeToStack - | AdviceInjector::UpdateMerkleNode - | AdviceInjector::U64Div - | AdviceInjector::Ext2Inv - | AdviceInjector::Ext2Intt - | AdviceInjector::SmtPeek - | AdviceInjector::U32Clz - | AdviceInjector::U32Ctz - | AdviceInjector::U32Clo - | AdviceInjector::U32Cto - | AdviceInjector::ILog2 - | AdviceInjector::MemToMap - | AdviceInjector::HpermToMap - | AdviceInjector::FalconSigToStack => None, - }, + Decorator::Advice(_) => None, Decorator::AsmOp(assembly_op) => { self.decorator_data.push(assembly_op.num_cycles()); self.decorator_data.write_bool(assembly_op.should_break()); diff --git a/core/src/mast/serialization/tests.rs b/core/src/mast/serialization/tests.rs index 3c0b2adf7..cf02a6eaf 100644 --- a/core/src/mast/serialization/tests.rs +++ b/core/src/mast/serialization/tests.rs @@ -125,7 +125,8 @@ fn confirm_operation_and_decorator_structure() { AdviceInjector::U32Cto => (), AdviceInjector::ILog2 => (), AdviceInjector::MemToMap => (), - AdviceInjector::HdwordToMap { domain: _ } => (), + AdviceInjector::HdwordToMap => (), + AdviceInjector::HdwordToMapWithDomain => (), AdviceInjector::HpermToMap => (), AdviceInjector::FalconSigToStack => (), }, @@ -257,7 +258,8 @@ fn serialize_deserialize_all_nodes() { (10, Decorator::Advice(AdviceInjector::U32Cto)), (10, Decorator::Advice(AdviceInjector::ILog2)), (10, Decorator::Advice(AdviceInjector::MemToMap)), - (10, Decorator::Advice(AdviceInjector::HdwordToMap { domain: Felt::new(423) })), + (10, Decorator::Advice(AdviceInjector::HdwordToMap)), + (10, Decorator::Advice(AdviceInjector::HdwordToMapWithDomain)), (15, Decorator::Advice(AdviceInjector::HpermToMap)), (15, Decorator::Advice(AdviceInjector::FalconSigToStack)), ( diff --git a/core/src/operations/decorators/advice.rs b/core/src/operations/decorators/advice.rs index 66210952b..4cc60ec4b 100644 --- a/core/src/operations/decorators/advice.rs +++ b/core/src/operations/decorators/advice.rs @@ -1,7 +1,6 @@ use core::fmt; use super::SignatureKind; -use crate::Felt; // ADVICE INJECTORS // ================================================================================================ @@ -245,9 +244,22 @@ pub enum AdviceInjector { /// 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. - HdwordToMap { domain: Felt }, + /// Where KEY is computed as hash(A || B, domain=0) + HdwordToMap, + + /// Reads two word from the operand stack and inserts them into the advice map under the key + /// defined by the hash of these words (using `d` as the domain). + /// + /// Inputs: + /// Operand stack: [B, A, d, ...] + /// Advice map: {...} + /// + /// Outputs: + /// Operand stack: [B, A, d, ...] + /// Advice map: {KEY: [a0, a1, a2, a3, b0, b1, b2, b3]} + /// + /// Where KEY is computed as hash(A || B, d). + HdwordToMapWithDomain, /// 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. @@ -306,7 +318,8 @@ impl fmt::Display for AdviceInjector { Self::U32Cto => write!(f, "u32cto"), Self::ILog2 => write!(f, "ilog2"), Self::MemToMap => write!(f, "mem_to_map"), - Self::HdwordToMap { domain } => write!(f, "hdword_to_map.{domain}"), + Self::HdwordToMap => write!(f, "hdword_to_map"), + Self::HdwordToMapWithDomain => write!(f, "hdword_to_map_with_domain"), Self::HpermToMap => write!(f, "hperm_to_map"), Self::FalconSigToStack => write!(f, "sig_to_stack.{}", SignatureKind::RpoFalcon512), } diff --git a/docs/src/user_docs/assembly/io_operations.md b/docs/src/user_docs/assembly/io_operations.md index ca180adc6..23e31bd18 100644 --- a/docs/src/user_docs/assembly/io_operations.md +++ b/docs/src/user_docs/assembly/io_operations.md @@ -57,8 +57,9 @@ Advice injectors fall into two categories: (1) injectors which push new data ont | adv.push_sig.*kind* | [K, M, ...] | [K, M, ...] | Pushes values onto the advice stack which are required for verification of a DSA with scheme specified by *kind* against the public key commitment $K$ and message $M$. | | adv.push_smtpeek | [K, R, ... ] | [K, R, ... ] | Pushes value onto the advice stack which is associated with key $K$ in a Sparse Merkle Tree with root $R$. | | adv.insert_mem | [K, a, b, ... ] | [K, a, b, ... ] | Reads words $data \leftarrow mem[a] .. mem[b]$ from memory, and save the data into $advice\_map[K] \leftarrow data$. | -| adv.insert_hdword
adv.insert_hdword.*d* | [B, A, ... ] | [B, A, ... ] | Reads top two words from the stack, computes a key as $K \leftarrow hash(A || b, d)$, and saves the data into $advice\_map[K] \leftarrow [A, B]$. $d$ is an optional domain value which can be between $0$ and $255$, default value $0$. | -| adv.insert_hperm | [B, A, C, ...] | [B, A, C, ...] | Reads top three words from the stack, computes a key as $K \leftarrow permute(C, A, B).digest$, and saves data into $advice\_mpa[K] \leftarrow [A, B]$. | +| adv.insert_hdword | [B, A, ... ] | [B, A, ... ] | Reads top two words from the stack, computes a key as $K \leftarrow hash(A || b, domain=0)$, and saves the data into $advice\_map[K] \leftarrow [A, B]$. | +| adv.insert_hdword_d | [B, A, d, ... ] | [B, A, d, ... ] | Reads top two words from the stack, computes a key as $K \leftarrow hash(A || b, domain=d)$, and saves the data into $advice\_map[K] \leftarrow [A, B]$. $d$ is the domain value, where changing the domain changes the resulting hash given the same `A` and `B`. | +| adv.insert_hperm | [B, A, C, ...] | [B, A, C, ...] | Reads top three words from the stack, computes a key as $K \leftarrow permute(C, A, B).digest$, and saves data into $advice\_map[K] \leftarrow [A, B]$. | ### Random access memory diff --git a/miden/tests/integration/operations/decorators/advice.rs b/miden/tests/integration/operations/decorators/advice.rs index 868fb338a..3e3c6a6bd 100644 --- a/miden/tests/integration/operations/decorators/advice.rs +++ b/miden/tests/integration/operations/decorators/advice.rs @@ -305,13 +305,13 @@ fn advice_insert_hdword() { // --- test hashing with domain ------------------------------------------- let source: &str = " begin - # stack: [1, 2, 3, 4, 5, 6, 7, 8, ...] + # stack: [1, 2, 3, 4, 5, 6, 7, 8, 9, ...] # hash and insert top two words into the advice map - adv.insert_hdword.3 + adv.insert_hdword_d # manually compute the hash of the two words - push.0.3.0.0 + push.0.9.0.0 swapw.2 swapw hperm dropw swapw dropw @@ -325,7 +325,7 @@ fn advice_insert_hdword() { adv_push.8 swapdw dropw dropw end"; - let stack_inputs = [8, 7, 6, 5, 4, 3, 2, 1]; + let stack_inputs = [9, 8, 7, 6, 5, 4, 3, 2, 1]; let test = build_test!(source, &stack_inputs); test.expect_stack(&[1, 2, 3, 4, 5, 6, 7, 8]); } diff --git a/processor/src/lib.rs b/processor/src/lib.rs index 279a86000..ce6a15bc3 100644 --- a/processor/src/lib.rs +++ b/processor/src/lib.rs @@ -595,7 +595,7 @@ impl Process { match decorator { Decorator::Advice(injector) => { let advice_provider = host.advice_provider_mut(); - let process_state: ProcessState = self.into(); + let process_state: ProcessState = (&*self).into(); match injector { AdviceInjector::MerkleNodeMerge => { advice_provider.merge_merkle_nodes(process_state)?; @@ -631,8 +631,14 @@ impl Process { AdviceInjector::MemToMap => { advice_provider.insert_mem_values_into_adv_map(process_state)?; }, - AdviceInjector::HdwordToMap { domain } => { - advice_provider.insert_hdword_into_adv_map(process_state, *domain)?; + AdviceInjector::HdwordToMap => { + advice_provider.insert_hdword_into_adv_map(process_state, ZERO)?; + }, + AdviceInjector::HdwordToMapWithDomain => { + // TODO(plafer): get domain from stack + let domain = self.stack.get(8); + advice_provider.insert_hdword_into_adv_map(process_state, domain)?; + todo!() }, AdviceInjector::HpermToMap => { advice_provider.insert_hperm_into_adv_map(process_state)?; From 1b2f23e19fedc3bd9ea6113c0b38be584710ce4c Mon Sep 17 00:00:00 2001 From: Philippe Laferriere Date: Wed, 20 Nov 2024 13:12:32 -0500 Subject: [PATCH 5/8] refactor: compile advice injectors down to `Operation::Emit` --- CHANGELOG.md | 1 + assembly/src/assembler/basic_block_builder.rs | 11 +- assembly/src/assembler/instruction/adv_ops.rs | 5 +- .../src/assembler/instruction/crypto_ops.rs | 22 ++-- .../src/assembler/instruction/ext2_ops.rs | 8 +- .../src/assembler/instruction/field_ops.rs | 6 +- assembly/src/assembler/instruction/mod.rs | 18 ++-- assembly/src/assembler/instruction/u32_ops.rs | 20 ++-- core/src/mast/serialization/decorator.rs | 100 +----------------- core/src/mast/serialization/tests.rs | 49 +-------- core/src/operations/decorators/advice.rs | 80 +++++++++++++- core/src/operations/decorators/mod.rs | 6 +- processor/src/lib.rs | 56 +--------- processor/src/operations/sys_ops.rs | 61 ++++++++++- 14 files changed, 180 insertions(+), 263 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d67d6b24d..51771a566 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,7 @@ - [BREAKING] `DYNCALL` operation fixed, and now expects a memory address pointing to the procedure hash (#1535). - Permit child `MastNodeId`s to exceed the `MastNodeId`s of their parents (#1542). - Don't validate export names on `Library` deserialization (#1554) +- Compile advice injectors down to `Emit` operations (#1581) #### Fixes diff --git a/assembly/src/assembler/basic_block_builder.rs b/assembly/src/assembler/basic_block_builder.rs index 3246484d2..85f886f97 100644 --- a/assembly/src/assembler/basic_block_builder.rs +++ b/assembly/src/assembler/basic_block_builder.rs @@ -93,6 +93,12 @@ impl BasicBlockBuilder<'_> { let new_len = self.ops.len() + n; self.ops.resize(new_len, op); } + + /// Converts the advice injector into its corresponding event ID, and adds an `Emit` operation + /// to the list of basic block operations. + pub fn push_advice_injector(&mut self, injector: AdviceInjector) { + self.push_op(Operation::Emit(injector.into_event_id())) + } } /// Decorators @@ -105,11 +111,6 @@ impl BasicBlockBuilder<'_> { Ok(()) } - /// Adds the specified advice injector to the list of basic block decorators. - pub fn push_advice_injector(&mut self, injector: AdviceInjector) -> Result<(), AssemblyError> { - self.push_decorator(Decorator::Advice(injector)) - } - /// Adds an AsmOp decorator to the list of basic block decorators. /// /// This indicates that the provided instruction should be tracked and the cycle count for diff --git a/assembly/src/assembler/instruction/adv_ops.rs b/assembly/src/assembler/instruction/adv_ops.rs index 86a38c1fa..049ace4e4 100644 --- a/assembly/src/assembler/instruction/adv_ops.rs +++ b/assembly/src/assembler/instruction/adv_ops.rs @@ -23,9 +23,6 @@ pub fn adv_push(block_builder: &mut BasicBlockBuilder, n: u8) -> Result<(), Asse // ================================================================================================ /// Appends advice injector decorator to the span. -pub fn adv_inject( - block_builder: &mut BasicBlockBuilder, - injector: &AdviceInjectorNode, -) -> Result<(), AssemblyError> { +pub fn adv_inject(block_builder: &mut BasicBlockBuilder, injector: &AdviceInjectorNode) { block_builder.push_advice_injector(injector.into()) } diff --git a/assembly/src/assembler/instruction/crypto_ops.rs b/assembly/src/assembler/instruction/crypto_ops.rs index 54e6a2acf..a89015a06 100644 --- a/assembly/src/assembler/instruction/crypto_ops.rs +++ b/assembly/src/assembler/instruction/crypto_ops.rs @@ -113,10 +113,10 @@ pub(super) fn hmerge(block_builder: &mut BasicBlockBuilder) { /// - root of the tree, 4 elements. /// /// This operation takes 9 VM cycles. -pub(super) fn mtree_get(block_builder: &mut BasicBlockBuilder) -> Result<(), AssemblyError> { +pub(super) fn mtree_get(block_builder: &mut BasicBlockBuilder) { // stack: [d, i, R, ...] // pops the value of the node we are looking for from the advice stack - read_mtree_node(block_builder)?; + read_mtree_node(block_builder); #[rustfmt::skip] let ops = [ // verify the node V for root R with depth d and index i @@ -128,8 +128,6 @@ pub(super) fn mtree_get(block_builder: &mut BasicBlockBuilder) -> Result<(), Ass MovUp4, Drop, MovUp4, Drop, ]; block_builder.push_ops(ops); - - Ok(()) } /// Appends the MRUPDATE op with a parameter of "false" and stack manipulations to the span block @@ -165,18 +163,16 @@ pub(super) fn mtree_set(block_builder: &mut BasicBlockBuilder) -> Result<(), Ass /// It is not checked whether the provided roots exist as Merkle trees in the advide providers. /// /// This operation takes 16 VM cycles. -pub(super) fn mtree_merge(block_builder: &mut BasicBlockBuilder) -> Result<(), AssemblyError> { +pub(super) fn mtree_merge(block_builder: &mut BasicBlockBuilder) { // stack input: [R_rhs, R_lhs, ...] // stack output: [R_merged, ...] // invoke the advice provider function to merge 2 Merkle trees defined by the roots on the top // of the operand stack - block_builder.push_advice_injector(AdviceInjector::MerkleNodeMerge)?; + block_builder.push_advice_injector(AdviceInjector::MerkleNodeMerge); // perform the `hmerge`, updating the operand stack - hmerge(block_builder); - - Ok(()) + hmerge(block_builder) } // MERKLE TREES - HELPERS @@ -199,20 +195,18 @@ pub(super) fn mtree_merge(block_builder: &mut BasicBlockBuilder) -> Result<(), A /// - new value of the node, 4 elements (only in the case of mtree_set) /// /// This operation takes 4 VM cycles. -fn read_mtree_node(block_builder: &mut BasicBlockBuilder) -> Result<(), AssemblyError> { +fn read_mtree_node(block_builder: &mut BasicBlockBuilder) { // The stack should be arranged in the following way: [d, i, R, ...] so that the decorator // can fetch the node value from the root. In the `mtree.get` operation we have the stack in // the following format: [d, i, R], whereas in the case of `mtree.set` we would also have the // new node value post the tree root: [d, i, R, V_new] // // pops the value of the node we are looking for from the advice stack - block_builder.push_advice_injector(AdviceInjector::MerkleNodeToStack)?; + block_builder.push_advice_injector(AdviceInjector::MerkleNodeToStack); // pops the old node value from advice the stack => MPVERIFY: [V_old, d, i, R, ...] // MRUPDATE: [V_old, d, i, R, V_new, ...] block_builder.push_op_many(AdvPop, 4); - - Ok(()) } /// Update a node in the merkle tree. This operation will always copy the tree into a new instance, @@ -225,7 +219,7 @@ fn update_mtree(block_builder: &mut BasicBlockBuilder) -> Result<(), AssemblyErr // Inject the old node value onto the stack for the call to MRUPDATE. // stack: [V_old, d, i, R_old, V_new, ...] (4 cycles) - read_mtree_node(block_builder)?; + read_mtree_node(block_builder); #[rustfmt::skip] let ops = [ diff --git a/assembly/src/assembler/instruction/ext2_ops.rs b/assembly/src/assembler/instruction/ext2_ops.rs index 6cc3ae88a..b2729553e 100644 --- a/assembly/src/assembler/instruction/ext2_ops.rs +++ b/assembly/src/assembler/instruction/ext2_ops.rs @@ -53,8 +53,8 @@ pub fn ext2_mul(block_builder: &mut BasicBlockBuilder) { /// operations outputs the result c = (c1, c0) where c = a * b^-1. /// /// This operation takes 11 VM cycles. -pub fn ext2_div(block_builder: &mut BasicBlockBuilder) -> Result<(), AssemblyError> { - block_builder.push_advice_injector(Ext2Inv)?; +pub fn ext2_div(block_builder: &mut BasicBlockBuilder) { + block_builder.push_advice_injector(Ext2Inv); #[rustfmt::skip] let ops = [ AdvPop, // [b0', b1, b0, a1, a0, ...] @@ -70,8 +70,6 @@ pub fn ext2_div(block_builder: &mut BasicBlockBuilder) -> Result<(), AssemblyErr Drop // [a1*b1', a0*b0'...] ]; block_builder.push_ops(ops); - - Ok(()) } /// Given a stack with initial configuration given by [a1, a0, ...] where a = (a0, a1) represents @@ -116,7 +114,7 @@ pub fn ext2_neg(block_builder: &mut BasicBlockBuilder) { /// /// This operation takes 8 VM cycles. pub fn ext2_inv(block_builder: &mut BasicBlockBuilder) -> Result<(), AssemblyError> { - block_builder.push_advice_injector(Ext2Inv)?; + block_builder.push_advice_injector(Ext2Inv); #[rustfmt::skip] let ops = [ AdvPop, // [a0', a1, a0, ...] diff --git a/assembly/src/assembler/instruction/field_ops.rs b/assembly/src/assembler/instruction/field_ops.rs index 74063bb48..7033a92ef 100644 --- a/assembly/src/assembler/instruction/field_ops.rs +++ b/assembly/src/assembler/instruction/field_ops.rs @@ -259,8 +259,8 @@ fn perform_exp_for_small_power(span_builder: &mut BasicBlockBuilder, pow: u64) { /// /// # Errors /// Returns an error if the logarithm argument (top stack element) equals ZERO. -pub fn ilog2(block_builder: &mut BasicBlockBuilder) -> Result<(), AssemblyError> { - block_builder.push_advice_injector(AdviceInjector::ILog2)?; +pub fn ilog2(block_builder: &mut BasicBlockBuilder) { + block_builder.push_advice_injector(AdviceInjector::ILog2); block_builder.push_op(AdvPop); // [ilog2, n, ...] // compute the power-of-two for the value given in the advice tape (17 cycles) @@ -290,8 +290,6 @@ pub fn ilog2(block_builder: &mut BasicBlockBuilder) -> Result<(), AssemblyError> ]; block_builder.push_ops(ops); - - Ok(()) } // COMPARISON OPERATIONS diff --git a/assembly/src/assembler/instruction/mod.rs b/assembly/src/assembler/instruction/mod.rs index 49e8bec16..76de7147c 100644 --- a/assembly/src/assembler/instruction/mod.rs +++ b/assembly/src/assembler/instruction/mod.rs @@ -89,7 +89,7 @@ impl Assembler { Instruction::ExpBitLength(num_pow_bits) => { field_ops::exp(block_builder, *num_pow_bits)? }, - Instruction::ILog2 => field_ops::ilog2(block_builder)?, + Instruction::ILog2 => field_ops::ilog2(block_builder), Instruction::Not => block_builder.push_op(Not), Instruction::And => block_builder.push_op(And), @@ -111,7 +111,7 @@ impl Assembler { Instruction::Ext2Add => ext2_ops::ext2_add(block_builder), Instruction::Ext2Sub => ext2_ops::ext2_sub(block_builder), Instruction::Ext2Mul => ext2_ops::ext2_mul(block_builder), - Instruction::Ext2Div => ext2_ops::ext2_div(block_builder)?, + Instruction::Ext2Div => ext2_ops::ext2_div(block_builder), Instruction::Ext2Neg => ext2_ops::ext2_neg(block_builder), Instruction::Ext2Inv => ext2_ops::ext2_inv(block_builder)?, @@ -190,10 +190,10 @@ impl Assembler { Instruction::U32Rotr => u32_ops::u32rotr(block_builder, None)?, Instruction::U32RotrImm(v) => u32_ops::u32rotr(block_builder, Some(v.expect_value()))?, Instruction::U32Popcnt => u32_ops::u32popcnt(block_builder), - Instruction::U32Clz => u32_ops::u32clz(block_builder)?, - Instruction::U32Ctz => u32_ops::u32ctz(block_builder)?, - Instruction::U32Clo => u32_ops::u32clo(block_builder)?, - Instruction::U32Cto => u32_ops::u32cto(block_builder)?, + Instruction::U32Clz => u32_ops::u32clz(block_builder), + Instruction::U32Ctz => u32_ops::u32ctz(block_builder), + Instruction::U32Clo => u32_ops::u32clo(block_builder), + Instruction::U32Cto => u32_ops::u32cto(block_builder), Instruction::U32Lt => u32_ops::u32lt(block_builder), Instruction::U32Lte => u32_ops::u32lte(block_builder), Instruction::U32Gt => u32_ops::u32gt(block_builder), @@ -361,15 +361,15 @@ impl Assembler { false, )?, - Instruction::AdvInject(injector) => adv_ops::adv_inject(block_builder, injector)?, + Instruction::AdvInject(injector) => adv_ops::adv_inject(block_builder, injector), // ----- cryptographic instructions --------------------------------------------------- Instruction::Hash => crypto_ops::hash(block_builder), Instruction::HPerm => block_builder.push_op(HPerm), Instruction::HMerge => crypto_ops::hmerge(block_builder), - Instruction::MTreeGet => crypto_ops::mtree_get(block_builder)?, + Instruction::MTreeGet => crypto_ops::mtree_get(block_builder), Instruction::MTreeSet => crypto_ops::mtree_set(block_builder)?, - Instruction::MTreeMerge => crypto_ops::mtree_merge(block_builder)?, + Instruction::MTreeMerge => crypto_ops::mtree_merge(block_builder), Instruction::MTreeVerify => block_builder.push_op(MpVerify(0)), Instruction::MTreeVerifyWithError(err_code) => { block_builder.push_op(MpVerify(err_code.expect_value())) diff --git a/assembly/src/assembler/instruction/u32_ops.rs b/assembly/src/assembler/instruction/u32_ops.rs index 8826d0593..23801be09 100644 --- a/assembly/src/assembler/instruction/u32_ops.rs +++ b/assembly/src/assembler/instruction/u32_ops.rs @@ -298,12 +298,11 @@ pub fn u32popcnt(span_builder: &mut BasicBlockBuilder) { /// provider). /// /// This operation takes 42 VM cycles. -pub fn u32clz(block_builder: &mut BasicBlockBuilder) -> Result<(), AssemblyError> { - block_builder.push_advice_injector(AdviceInjector::U32Clz)?; +pub fn u32clz(block_builder: &mut BasicBlockBuilder) { + block_builder.push_advice_injector(AdviceInjector::U32Clz); block_builder.push_op(AdvPop); // [clz, n, ...] verify_clz(block_builder); - Ok(()) } /// Translates `u32ctz` assembly instruction to VM operations. `u32ctz` counts the number of @@ -311,12 +310,11 @@ pub fn u32clz(block_builder: &mut BasicBlockBuilder) -> Result<(), AssemblyError /// provider). /// /// This operation takes 34 VM cycles. -pub fn u32ctz(block_builder: &mut BasicBlockBuilder) -> Result<(), AssemblyError> { - block_builder.push_advice_injector(AdviceInjector::U32Ctz)?; +pub fn u32ctz(block_builder: &mut BasicBlockBuilder) { + block_builder.push_advice_injector(AdviceInjector::U32Ctz); block_builder.push_op(AdvPop); // [ctz, n, ...] verify_ctz(block_builder); - Ok(()) } /// Translates `u32clo` assembly instruction to VM operations. `u32clo` counts the number of @@ -324,12 +322,11 @@ pub fn u32ctz(block_builder: &mut BasicBlockBuilder) -> Result<(), AssemblyError /// provider). /// /// This operation takes 41 VM cycles. -pub fn u32clo(block_builder: &mut BasicBlockBuilder) -> Result<(), AssemblyError> { - block_builder.push_advice_injector(AdviceInjector::U32Clo)?; +pub fn u32clo(block_builder: &mut BasicBlockBuilder) { + block_builder.push_advice_injector(AdviceInjector::U32Clo); block_builder.push_op(AdvPop); // [clo, n, ...] verify_clo(block_builder); - Ok(()) } /// Translates `u32cto` assembly instruction to VM operations. `u32cto` counts the number of @@ -337,12 +334,11 @@ pub fn u32clo(block_builder: &mut BasicBlockBuilder) -> Result<(), AssemblyError /// provider). /// /// This operation takes 33 VM cycles. -pub fn u32cto(block_builder: &mut BasicBlockBuilder) -> Result<(), AssemblyError> { - block_builder.push_advice_injector(AdviceInjector::U32Cto)?; +pub fn u32cto(block_builder: &mut BasicBlockBuilder) { + block_builder.push_advice_injector(AdviceInjector::U32Cto); block_builder.push_op(AdvPop); // [cto, n, ...] verify_cto(block_builder); - Ok(()) } /// Specifically handles these specific inputs per the spec. diff --git a/core/src/mast/serialization/decorator.rs b/core/src/mast/serialization/decorator.rs index 5815b988c..f4e2f31a0 100644 --- a/core/src/mast/serialization/decorator.rs +++ b/core/src/mast/serialization/decorator.rs @@ -10,7 +10,7 @@ use super::{ string_table::{StringTable, StringTableBuilder}, DecoratorDataOffset, }; -use crate::{AdviceInjector, AssemblyOp, DebugOptions, Decorator}; +use crate::{AssemblyOp, DebugOptions, Decorator}; /// Represents a serialized [`Decorator`]. /// @@ -47,63 +47,6 @@ impl DecoratorInfo { let mut data_reader = SliceReader::new(&decorator_data[self.decorator_data_offset as usize..]); match self.variant { - EncodedDecoratorVariant::AdviceInjectorMerkleNodeMerge => { - Ok(Decorator::Advice(AdviceInjector::MerkleNodeMerge)) - }, - EncodedDecoratorVariant::AdviceInjectorMerkleNodeToStack => { - Ok(Decorator::Advice(AdviceInjector::MerkleNodeToStack)) - }, - EncodedDecoratorVariant::AdviceInjectorUpdateMerkleNode => { - Ok(Decorator::Advice(AdviceInjector::UpdateMerkleNode)) - }, - EncodedDecoratorVariant::AdviceInjectorMapValueToStack => { - Ok(Decorator::Advice(AdviceInjector::MapValueToStack)) - }, - EncodedDecoratorVariant::AdviceInjectorMapValueToStackN => { - Ok(Decorator::Advice(AdviceInjector::MapValueToStackN)) - }, - EncodedDecoratorVariant::AdviceInjectorU64Div => { - Ok(Decorator::Advice(AdviceInjector::U64Div)) - }, - EncodedDecoratorVariant::AdviceInjectorExt2Inv => { - Ok(Decorator::Advice(AdviceInjector::Ext2Inv)) - }, - EncodedDecoratorVariant::AdviceInjectorExt2Intt => { - Ok(Decorator::Advice(AdviceInjector::Ext2Intt)) - }, - EncodedDecoratorVariant::AdviceInjectorSmtPeek => { - Ok(Decorator::Advice(AdviceInjector::SmtPeek)) - }, - EncodedDecoratorVariant::AdviceInjectorU32Clz => { - Ok(Decorator::Advice(AdviceInjector::U32Clz)) - }, - EncodedDecoratorVariant::AdviceInjectorU32Ctz => { - Ok(Decorator::Advice(AdviceInjector::U32Ctz)) - }, - EncodedDecoratorVariant::AdviceInjectorU32Clo => { - Ok(Decorator::Advice(AdviceInjector::U32Clo)) - }, - EncodedDecoratorVariant::AdviceInjectorU32Cto => { - Ok(Decorator::Advice(AdviceInjector::U32Cto)) - }, - EncodedDecoratorVariant::AdviceInjectorILog2 => { - Ok(Decorator::Advice(AdviceInjector::ILog2)) - }, - EncodedDecoratorVariant::AdviceInjectorMemToMap => { - Ok(Decorator::Advice(AdviceInjector::MemToMap)) - }, - EncodedDecoratorVariant::AdviceInjectorHdwordToMap => { - Ok(Decorator::Advice(AdviceInjector::HdwordToMap)) - }, - EncodedDecoratorVariant::AdviceInjectorHdwordToMapWithDomain => { - Ok(Decorator::Advice(AdviceInjector::HdwordToMapWithDomain)) - }, - EncodedDecoratorVariant::AdviceInjectorHpermToMap => { - Ok(Decorator::Advice(AdviceInjector::HpermToMap)) - }, - EncodedDecoratorVariant::AdviceInjectorFalconSigToStack => { - Ok(Decorator::Advice(AdviceInjector::FalconSigToStack)) - }, EncodedDecoratorVariant::AssemblyOp => { let num_cycles = data_reader.read_u8()?; let should_break = data_reader.read_bool()?; @@ -202,25 +145,6 @@ impl Deserializable for DecoratorInfo { #[derive(Debug, FromPrimitive, ToPrimitive)] #[repr(u8)] pub enum EncodedDecoratorVariant { - AdviceInjectorMerkleNodeMerge, - AdviceInjectorMerkleNodeToStack, - AdviceInjectorUpdateMerkleNode, - AdviceInjectorMapValueToStack, - AdviceInjectorMapValueToStackN, - AdviceInjectorU64Div, - AdviceInjectorExt2Inv, - AdviceInjectorExt2Intt, - AdviceInjectorSmtPeek, - AdviceInjectorU32Clz, - AdviceInjectorU32Ctz, - AdviceInjectorU32Clo, - AdviceInjectorU32Cto, - AdviceInjectorILog2, - AdviceInjectorMemToMap, - AdviceInjectorHdwordToMap, - AdviceInjectorHdwordToMapWithDomain, - AdviceInjectorHpermToMap, - AdviceInjectorFalconSigToStack, AssemblyOp, DebugOptionsStackAll, DebugOptionsStackTop, @@ -248,27 +172,6 @@ impl EncodedDecoratorVariant { impl From<&Decorator> for EncodedDecoratorVariant { fn from(decorator: &Decorator) -> Self { match decorator { - Decorator::Advice(advice_injector) => match advice_injector { - AdviceInjector::MerkleNodeMerge => Self::AdviceInjectorMerkleNodeMerge, - AdviceInjector::MerkleNodeToStack => Self::AdviceInjectorMerkleNodeToStack, - AdviceInjector::UpdateMerkleNode => Self::AdviceInjectorUpdateMerkleNode, - AdviceInjector::MapValueToStack => Self::AdviceInjectorMapValueToStack, - AdviceInjector::MapValueToStackN => Self::AdviceInjectorMapValueToStackN, - AdviceInjector::U64Div => Self::AdviceInjectorU64Div, - AdviceInjector::Ext2Inv => Self::AdviceInjectorExt2Inv, - AdviceInjector::Ext2Intt => Self::AdviceInjectorExt2Intt, - AdviceInjector::SmtPeek => Self::AdviceInjectorSmtPeek, - AdviceInjector::U32Clz => Self::AdviceInjectorU32Clz, - AdviceInjector::U32Ctz => Self::AdviceInjectorU32Ctz, - AdviceInjector::U32Clo => Self::AdviceInjectorU32Clo, - AdviceInjector::U32Cto => Self::AdviceInjectorU32Cto, - AdviceInjector::ILog2 => Self::AdviceInjectorILog2, - AdviceInjector::MemToMap => Self::AdviceInjectorMemToMap, - AdviceInjector::HdwordToMap => Self::AdviceInjectorHdwordToMap, - AdviceInjector::HdwordToMapWithDomain => Self::AdviceInjectorHdwordToMapWithDomain, - AdviceInjector::HpermToMap => Self::AdviceInjectorHpermToMap, - AdviceInjector::FalconSigToStack => Self::AdviceInjectorFalconSigToStack, - }, Decorator::AsmOp(_) => Self::AssemblyOp, Decorator::Debug(debug_options) => match debug_options { DebugOptions::StackAll => Self::DebugOptionsStackAll, @@ -328,7 +231,6 @@ impl DecoratorDataBuilder { let data_offset = self.decorator_data.len() as DecoratorDataOffset; match decorator { - Decorator::Advice(_) => None, Decorator::AsmOp(assembly_op) => { self.decorator_data.push(assembly_op.num_cycles()); self.decorator_data.write_bool(assembly_op.should_break()); diff --git a/core/src/mast/serialization/tests.rs b/core/src/mast/serialization/tests.rs index cf02a6eaf..4962f9959 100644 --- a/core/src/mast/serialization/tests.rs +++ b/core/src/mast/serialization/tests.rs @@ -3,10 +3,7 @@ use alloc::{string::ToString, sync::Arc}; use miden_crypto::{hash::rpo::RpoDigest, Felt, ONE}; use super::*; -use crate::{ - mast::MastForestError, operations::Operation, AdviceInjector, AssemblyOp, DebugOptions, - Decorator, -}; +use crate::{mast::MastForestError, operations::Operation, AssemblyOp, DebugOptions, Decorator}; /// If this test fails to compile, it means that `Operation` or `Decorator` was changed. Make sure /// that all tests in this file are updated accordingly. For example, if a new `Operation` variant @@ -109,27 +106,6 @@ fn confirm_operation_and_decorator_structure() { }; match Decorator::Trace(0) { - Decorator::Advice(advice) => match advice { - AdviceInjector::MerkleNodeMerge => (), - AdviceInjector::MerkleNodeToStack => (), - AdviceInjector::UpdateMerkleNode => (), - AdviceInjector::MapValueToStack => (), - AdviceInjector::MapValueToStackN => (), - AdviceInjector::U64Div => (), - AdviceInjector::Ext2Inv => (), - AdviceInjector::Ext2Intt => (), - AdviceInjector::SmtPeek => (), - AdviceInjector::U32Clz => (), - AdviceInjector::U32Ctz => (), - AdviceInjector::U32Clo => (), - AdviceInjector::U32Cto => (), - AdviceInjector::ILog2 => (), - AdviceInjector::MemToMap => (), - AdviceInjector::HdwordToMap => (), - AdviceInjector::HdwordToMapWithDomain => (), - AdviceInjector::HpermToMap => (), - AdviceInjector::FalconSigToStack => (), - }, Decorator::AsmOp(_) => (), Decorator::Debug(debug_options) => match debug_options { DebugOptions::StackAll => (), @@ -243,27 +219,8 @@ fn serialize_deserialize_all_nodes() { let num_operations = operations.len(); let decorators = vec![ - (0, Decorator::Advice(AdviceInjector::MerkleNodeMerge)), - (0, Decorator::Advice(AdviceInjector::MerkleNodeToStack)), - (0, Decorator::Advice(AdviceInjector::UpdateMerkleNode)), - (0, Decorator::Advice(AdviceInjector::MapValueToStack)), - (0, Decorator::Advice(AdviceInjector::MapValueToStackN)), - (1, Decorator::Advice(AdviceInjector::U64Div)), - (3, Decorator::Advice(AdviceInjector::Ext2Inv)), - (5, Decorator::Advice(AdviceInjector::Ext2Intt)), - (5, Decorator::Advice(AdviceInjector::SmtPeek)), - (5, Decorator::Advice(AdviceInjector::U32Clz)), - (10, Decorator::Advice(AdviceInjector::U32Ctz)), - (10, Decorator::Advice(AdviceInjector::U32Clo)), - (10, Decorator::Advice(AdviceInjector::U32Cto)), - (10, Decorator::Advice(AdviceInjector::ILog2)), - (10, Decorator::Advice(AdviceInjector::MemToMap)), - (10, Decorator::Advice(AdviceInjector::HdwordToMap)), - (10, Decorator::Advice(AdviceInjector::HdwordToMapWithDomain)), - (15, Decorator::Advice(AdviceInjector::HpermToMap)), - (15, Decorator::Advice(AdviceInjector::FalconSigToStack)), ( - 15, + 0, Decorator::AsmOp(AssemblyOp::new( Some(crate::debuginfo::Location { path: Arc::from("test"), @@ -276,7 +233,7 @@ fn serialize_deserialize_all_nodes() { false, )), ), - (15, Decorator::Debug(DebugOptions::StackAll)), + (0, Decorator::Debug(DebugOptions::StackAll)), (15, Decorator::Debug(DebugOptions::StackTop(255))), (15, Decorator::Debug(DebugOptions::MemAll)), (15, Decorator::Debug(DebugOptions::MemInterval(0, 16))), diff --git a/core/src/operations/decorators/advice.rs b/core/src/operations/decorators/advice.rs index 4cc60ec4b..5ce7dcead 100644 --- a/core/src/operations/decorators/advice.rs +++ b/core/src/operations/decorators/advice.rs @@ -1,10 +1,35 @@ use core::fmt; - use super::SignatureKind; // ADVICE INJECTORS // ================================================================================================ +// Randomly generated constant values for the VM's native events. All values were sampled +// between 0 and 2^32. +pub use constants::*; +#[rustfmt::skip] +mod constants { + pub const EVENT_MERKLE_NODE_MERGE: u32 = 276124218; + pub const EVENT_MERKLE_NODE_TO_STACK: u32 = 361943238; + pub const EVENT_UPDATE_MERKLE_NODE: u32 = 483702215; + pub const EVENT_MAP_VALUE_TO_STACK: u32 = 574478993; + pub const EVENT_MAP_VALUE_TO_STACK_N: u32 = 630847990; + pub const EVENT_U64_DIV: u32 = 678156251; + pub const EVENT_EXT2_INV: u32 = 1251967401; + pub const EVENT_EXT2_INTT: u32 = 1347499010; + pub const EVENT_SMT_PEEK: u32 = 1889584556; + pub const EVENT_U32_CLZ: u32 = 1951932030; + pub const EVENT_U32_CTZ: u32 = 2008979519; + pub const EVENT_U32_CLO: u32 = 2032895094; + pub const EVENT_U32_CTO: u32 = 2083700134; + pub const EVENT_ILOG2: u32 = 2297972669; + pub const EVENT_MEM_TO_MAP: u32 = 2389394361; + pub const EVENT_HDWORD_TO_MAP: u32 = 2391452729; + pub const EVENT_HDWORD_TO_MAP_WITH_DOMAIN: u32 = 2822590340; + pub const EVENT_HPERM_TO_MAP: u32 = 3297060969; + pub const EVENT_FALCON_SIG_TO_STACK: u32 = 3419226139; +} + /// Defines a set of actions which can be initiated from the VM to inject new data into the advice /// provider. /// @@ -292,6 +317,59 @@ pub enum AdviceInjector { FalconSigToStack, } +impl AdviceInjector { + pub fn into_event_id(self) -> u32 { + match self { + AdviceInjector::MerkleNodeMerge => EVENT_MERKLE_NODE_MERGE, + AdviceInjector::MerkleNodeToStack => EVENT_MERKLE_NODE_TO_STACK, + AdviceInjector::UpdateMerkleNode => EVENT_UPDATE_MERKLE_NODE, + AdviceInjector::MapValueToStack => EVENT_MAP_VALUE_TO_STACK, + AdviceInjector::MapValueToStackN => EVENT_MAP_VALUE_TO_STACK_N, + AdviceInjector::U64Div => EVENT_U64_DIV, + AdviceInjector::Ext2Inv => EVENT_EXT2_INV, + AdviceInjector::Ext2Intt => EVENT_EXT2_INTT, + AdviceInjector::SmtPeek => EVENT_SMT_PEEK, + AdviceInjector::U32Clz => EVENT_U32_CLZ, + AdviceInjector::U32Ctz => EVENT_U32_CTZ, + AdviceInjector::U32Clo => EVENT_U32_CLO, + AdviceInjector::U32Cto => EVENT_U32_CTO, + AdviceInjector::ILog2 => EVENT_ILOG2, + AdviceInjector::MemToMap => EVENT_MEM_TO_MAP, + AdviceInjector::HdwordToMap => EVENT_HDWORD_TO_MAP, + AdviceInjector::HdwordToMapWithDomain => EVENT_HDWORD_TO_MAP_WITH_DOMAIN, + AdviceInjector::HpermToMap => EVENT_HPERM_TO_MAP, + AdviceInjector::FalconSigToStack => EVENT_FALCON_SIG_TO_STACK, + } + } + + /// Returns an advice injector corresponding to the specified event ID, or `None` if the event + /// ID is not recognized. + pub fn from_event_id(event_id: u32) -> Option { + match event_id { + EVENT_MERKLE_NODE_MERGE => Some(AdviceInjector::MerkleNodeMerge), + EVENT_MERKLE_NODE_TO_STACK => Some(AdviceInjector::MerkleNodeToStack), + EVENT_UPDATE_MERKLE_NODE => Some(AdviceInjector::UpdateMerkleNode), + EVENT_MAP_VALUE_TO_STACK => Some(AdviceInjector::MapValueToStack), + EVENT_MAP_VALUE_TO_STACK_N => Some(AdviceInjector::MapValueToStackN), + EVENT_U64_DIV => Some(AdviceInjector::U64Div), + EVENT_EXT2_INV => Some(AdviceInjector::Ext2Inv), + EVENT_EXT2_INTT => Some(AdviceInjector::Ext2Intt), + EVENT_SMT_PEEK => Some(AdviceInjector::SmtPeek), + EVENT_U32_CLZ => Some(AdviceInjector::U32Clz), + EVENT_U32_CTZ => Some(AdviceInjector::U32Ctz), + EVENT_U32_CLO => Some(AdviceInjector::U32Clo), + EVENT_U32_CTO => Some(AdviceInjector::U32Cto), + EVENT_ILOG2 => Some(AdviceInjector::ILog2), + EVENT_MEM_TO_MAP => Some(AdviceInjector::MemToMap), + EVENT_HDWORD_TO_MAP => Some(AdviceInjector::HdwordToMap), + EVENT_HDWORD_TO_MAP_WITH_DOMAIN => Some(AdviceInjector::HdwordToMapWithDomain), + EVENT_HPERM_TO_MAP => Some(AdviceInjector::HpermToMap), + EVENT_FALCON_SIG_TO_STACK => Some(AdviceInjector::FalconSigToStack), + _ => None, + } + } +} + impl crate::prettier::PrettyPrint for AdviceInjector { fn render(&self) -> crate::prettier::Document { crate::prettier::display(self) diff --git a/core/src/operations/decorators/mod.rs b/core/src/operations/decorators/mod.rs index 02e054eb0..9a12789e1 100644 --- a/core/src/operations/decorators/mod.rs +++ b/core/src/operations/decorators/mod.rs @@ -21,14 +21,12 @@ use crate::mast::{DecoratorFingerprint, DecoratorId}; /// A set of decorators which can be executed by the VM. /// /// Executing a decorator does not affect the state of the main VM components such as operand stack -/// and memory. However, decorators may modify the advice provider. +/// and memory. /// /// Executing decorators does not advance the VM clock. As such, many decorators can be executed in /// a single VM cycle. #[derive(Clone, Debug, Eq, PartialEq)] pub enum Decorator { - /// Injects new data into the advice provider, as specified by the injector. - Advice(AdviceInjector), /// Adds information about the assembly instruction at a particular index (only applicable in /// debug mode). AsmOp(AssemblyOp), @@ -42,7 +40,6 @@ pub enum Decorator { impl Decorator { pub fn fingerprint(&self) -> DecoratorFingerprint { match self { - Self::Advice(advice) => Blake3_256::hash(advice.to_string().as_bytes()), Self::AsmOp(asm_op) => { let mut bytes_to_hash = Vec::new(); if let Some(location) = asm_op.location() { @@ -72,7 +69,6 @@ impl crate::prettier::PrettyPrint for Decorator { impl fmt::Display for Decorator { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Self::Advice(injector) => write!(f, "advice({injector})"), Self::AsmOp(assembly_op) => { write!(f, "asmOp({}, {})", assembly_op.op(), assembly_op.num_cycles()) }, diff --git a/processor/src/lib.rs b/processor/src/lib.rs index ce6a15bc3..76a07dd5c 100644 --- a/processor/src/lib.rs +++ b/processor/src/lib.rs @@ -26,7 +26,7 @@ use vm_core::{ mast::{ BasicBlockNode, CallNode, DynNode, JoinNode, LoopNode, OpBatch, SplitNode, OP_GROUP_SIZE, }, - Decorator, DecoratorIterator, FieldElement, SignatureKind, + Decorator, DecoratorIterator, FieldElement, }; pub use winter_prover::matrix::ColMatrix; @@ -593,60 +593,6 @@ impl Process { host: &mut impl Host, ) -> Result<(), ExecutionError> { match decorator { - Decorator::Advice(injector) => { - let advice_provider = host.advice_provider_mut(); - let process_state: ProcessState = (&*self).into(); - match injector { - AdviceInjector::MerkleNodeMerge => { - advice_provider.merge_merkle_nodes(process_state)?; - }, - AdviceInjector::MerkleNodeToStack => { - advice_provider.copy_merkle_node_to_adv_stack(process_state)? - }, - AdviceInjector::MapValueToStack => { - advice_provider.copy_map_value_to_adv_stack(process_state, false, 0)? - }, - AdviceInjector::MapValueToStackN => { - advice_provider.copy_map_value_to_adv_stack(process_state, true, 0)? - }, - AdviceInjector::UpdateMerkleNode => { - let _ = advice_provider.update_operand_stack_merkle_node(process_state)?; - }, - AdviceInjector::U64Div => advice_provider.push_u64_div_result(process_state)?, - AdviceInjector::Ext2Inv => { - advice_provider.push_ext2_inv_result(process_state)? - }, - AdviceInjector::Ext2Intt => { - advice_provider.push_ext2_intt_result(process_state)? - }, - AdviceInjector::SmtPeek => { - advice_provider.push_smtpeek_result(process_state)? - }, - AdviceInjector::U32Clz => advice_provider.push_leading_zeros(process_state)?, - AdviceInjector::U32Ctz => advice_provider.push_trailing_zeros(process_state)?, - AdviceInjector::U32Clo => advice_provider.push_leading_ones(process_state)?, - AdviceInjector::U32Cto => advice_provider.push_trailing_ones(process_state)?, - AdviceInjector::ILog2 => advice_provider.push_ilog2(process_state)?, - - AdviceInjector::MemToMap => { - advice_provider.insert_mem_values_into_adv_map(process_state)?; - }, - AdviceInjector::HdwordToMap => { - advice_provider.insert_hdword_into_adv_map(process_state, ZERO)?; - }, - AdviceInjector::HdwordToMapWithDomain => { - // TODO(plafer): get domain from stack - let domain = self.stack.get(8); - advice_provider.insert_hdword_into_adv_map(process_state, domain)?; - todo!() - }, - AdviceInjector::HpermToMap => { - advice_provider.insert_hperm_into_adv_map(process_state)?; - }, - AdviceInjector::FalconSigToStack => advice_provider - .push_signature(process_state, SignatureKind::RpoFalcon512)?, - } - }, Decorator::Debug(options) => { if self.decoder.in_debug_mode() { host.on_debug(self.into(), options)?; diff --git a/processor/src/operations/sys_ops.rs b/processor/src/operations/sys_ops.rs index 1a198da3b..f4e70b5f7 100644 --- a/processor/src/operations/sys_ops.rs +++ b/processor/src/operations/sys_ops.rs @@ -1,4 +1,4 @@ -use vm_core::{Felt, Operation}; +use vm_core::{AdviceInjector, Felt, Operation, SignatureKind, ZERO}; use super::{ super::{ @@ -7,7 +7,7 @@ use super::{ }, ExecutionError, Process, }; -use crate::Host; +use crate::{AdviceProvider, Host, ProcessState}; // SYSTEM OPERATIONS // ================================================================================================ @@ -122,9 +122,62 @@ impl Process { { self.stack.copy_state(0); self.decoder.set_user_op_helpers(Operation::Emit(event_id), &[event_id.into()]); - host.on_event(self.into(), event_id)?; - Ok(()) + // If it's a native event, handle it directly. Otherwise, forward it to the host. + if let Some(advice_injector) = AdviceInjector::from_event_id(event_id) { + self.handle_advice_injector(advice_injector, host) + } else { + host.on_event(self.into(), event_id) + } + } + + fn handle_advice_injector( + &self, + advice_injector: AdviceInjector, + host: &mut impl Host, + ) -> Result<(), ExecutionError> { + let advice_provider = host.advice_provider_mut(); + let process_state: ProcessState = self.into(); + match advice_injector { + AdviceInjector::MerkleNodeMerge => advice_provider.merge_merkle_nodes(process_state), + AdviceInjector::MerkleNodeToStack => { + advice_provider.copy_merkle_node_to_adv_stack(process_state) + }, + AdviceInjector::MapValueToStack => { + advice_provider.copy_map_value_to_adv_stack(process_state, false) + }, + AdviceInjector::MapValueToStackN => { + advice_provider.copy_map_value_to_adv_stack(process_state, true) + }, + AdviceInjector::UpdateMerkleNode => { + let _ = advice_provider.update_operand_stack_merkle_node(process_state)?; + Ok(()) + }, + AdviceInjector::U64Div => advice_provider.push_u64_div_result(process_state), + AdviceInjector::Ext2Inv => advice_provider.push_ext2_inv_result(process_state), + AdviceInjector::Ext2Intt => advice_provider.push_ext2_intt_result(process_state), + AdviceInjector::SmtPeek => advice_provider.push_smtpeek_result(process_state), + AdviceInjector::U32Clz => advice_provider.push_leading_zeros(process_state), + AdviceInjector::U32Ctz => advice_provider.push_trailing_zeros(process_state), + AdviceInjector::U32Clo => advice_provider.push_leading_ones(process_state), + AdviceInjector::U32Cto => advice_provider.push_trailing_ones(process_state), + AdviceInjector::ILog2 => advice_provider.push_ilog2(process_state), + + AdviceInjector::MemToMap => { + advice_provider.insert_mem_values_into_adv_map(process_state) + }, + AdviceInjector::HdwordToMap => { + advice_provider.insert_hdword_into_adv_map(process_state, ZERO) + }, + AdviceInjector::HdwordToMapWithDomain => { + let domain = self.stack.get(8); + advice_provider.insert_hdword_into_adv_map(process_state, domain) + }, + AdviceInjector::HpermToMap => advice_provider.insert_hperm_into_adv_map(process_state), + AdviceInjector::FalconSigToStack => { + advice_provider.push_signature(process_state, SignatureKind::RpoFalcon512) + }, + } } } From 596e14f4389e62b98f017678459f8509401df7ec Mon Sep 17 00:00:00 2001 From: Philippe Laferriere Date: Thu, 21 Nov 2024 08:47:16 -0500 Subject: [PATCH 6/8] refactor: remove default methods from `AdviceProvider` trait --- processor/src/host/advice/mod.rs | 1230 +++++++++++------------- processor/src/operations/crypto_ops.rs | 10 +- processor/src/operations/sys_ops.rs | 49 +- 3 files changed, 604 insertions(+), 685 deletions(-) diff --git a/processor/src/host/advice/mod.rs b/processor/src/host/advice/mod.rs index f6f548b07..331714f9b 100644 --- a/processor/src/host/advice/mod.rs +++ b/processor/src/host/advice/mod.rs @@ -171,727 +171,635 @@ pub trait AdviceProvider: Sized { /// It is not checked whether a Merkle tree for either of the specified roots can be found in /// this advice provider. fn merge_roots(&mut self, lhs: Word, rhs: Word) -> Result; +} - // PROVIDED METHODS - // -------------------------------------------------------------------------------------------- - - // DEFAULT 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`. - fn insert_mem_values_into_adv_map( - &mut self, - process: ProcessState, - ) -> 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); - } +// NATIVE EVENTS +// =============================================================================================== - let key = process.get_stack_word(0); - self.insert_into_map(key, values); +/// 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 fn insert_mem_values_into_adv_map( + advice_provider: &mut impl AdviceProvider, + process: ProcessState, +) -> Result<(), ExecutionError> { + let (start_addr, end_addr) = get_mem_addr_range(process, 4, 5)?; + let ctx = process.ctx(); - Ok(()) + 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); } - /// 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. - fn insert_hdword_into_adv_map( - &mut self, - process: ProcessState, - domain: Felt, - ) -> 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(()) - } + let key = process.get_stack_word(0); + advice_provider.insert_into_map(key, values); - /// 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). - 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(()) - } + Ok(()) +} - /// 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. - 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); +/// 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 fn insert_hdword_into_adv_map( + advice_provider: &mut impl AdviceProvider, + process: ProcessState, + domain: Felt, +) -> 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); + advice_provider.insert_into_map(key.into(), values); - // perform the merge - self.merge_roots(lhs, rhs)?; + Ok(()) +} - Ok(()) - } +/// 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 fn insert_hperm_into_adv_map( + advice_provider: &mut impl AdviceProvider, + 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"), + ); + + advice_provider.insert_into_map(key.into(), values); - // DEFAULT ADVICE STACK INJECTORS - // -------------------------------------------------------------------------------------------- + Ok(()) +} - /// 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. - fn copy_merkle_node_to_adv_stack( - &mut self, - process: ProcessState, - ) -> 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(()) - } +/// 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 fn merge_merkle_nodes( + advice_provider: &mut impl AdviceProvider, + process: ProcessState, +) -> Result<(), ExecutionError> { + // fetch the arguments from the stack + let lhs = process.get_stack_word(1); + let rhs = process.get_stack_word(0); - /// 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. - fn copy_map_value_to_adv_stack( - &mut self, - process: ProcessState, - include_len: bool, - ) -> Result<(), ExecutionError> { - let key = [ - process.get_stack_item(3), - process.get_stack_item(2), - process.get_stack_item(1), - process.get_stack_item(0), - ]; - self.push_stack(AdviceSource::Map { key, include_len })?; - - Ok(()) - } + // perform the merge + advice_provider.merge_roots(lhs, rhs)?; - /// 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. - 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())); - } + Ok(()) +} - 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; +/// 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 fn copy_merkle_node_to_adv_stack( + advice_provider: &mut impl AdviceProvider, + process: ProcessState, +) -> 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 = advice_provider.get_tree_node(root, &depth, &index)?; + + 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]))?; - let quotient = dividend / divisor; - let remainder = dividend - quotient * divisor; + Ok(()) +} - let (q_hi, q_lo) = u64_to_u32_elements(quotient); - let (r_hi, r_lo) = u64_to_u32_elements(remainder); +/// 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 fn copy_map_value_to_adv_stack( + advice_provider: &mut impl AdviceProvider, + process: ProcessState, + include_len: bool, +) -> Result<(), ExecutionError> { + let key = [ + process.get_stack_item(3), + process.get_stack_item(2), + process.get_stack_item(1), + process.get_stack_item(0), + ]; + advice_provider.push_stack(AdviceSource::Map { key, include_len })?; - 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(()) +} - Ok(()) - } +/// 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 fn push_u64_div_result( + advice_provider: &mut impl AdviceProvider, + 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; - /// 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. - 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(); + if divisor == 0 { + return Err(ExecutionError::DivideByZero(process.clk())); + } - self.push_stack(AdviceSource::Value(result[1]))?; - self.push_stack(AdviceSource::Value(result[0]))?; + 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; - Ok(()) - } + let quotient = dividend / divisor; + let remainder = dividend - quotient * divisor; - /// 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. - 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 (q_hi, q_lo) = u64_to_u32_elements(quotient); + let (r_hi, r_lo) = u64_to_u32_elements(remainder); - 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()); - } + 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))?; - if output_size == 0 { - return Err(Ext2InttError::OutputSizeIsZero.into()); - } - if output_size > input_size { - return Err(Ext2InttError::OutputSizeTooBig(output_size, input_size).into()); - } + Ok(()) +} - 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))?; +/// 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 fn push_ext2_inv_result( + advice_provider: &mut impl AdviceProvider, + process: ProcessState, +) -> Result<(), ExecutionError> { + let coef0 = process.get_stack_item(1); + let coef1 = process.get_stack_item(0); - poly.push(QuadFelt::new(word[0], word[1])); - poly.push(QuadFelt::new(word[2], word[3])); - } + let element = QuadFelt::new(coef0, coef1); + if element == QuadFelt::ZERO { + return Err(ExecutionError::DivideByZero(process.clk())); + } + let result = element.inv().to_base_elements(); - let twiddles = fft::get_inv_twiddles::(input_size); - fft::interpolate_poly::(&mut poly, &twiddles); + advice_provider.push_stack(AdviceSource::Value(result[1]))?; + advice_provider.push_stack(AdviceSource::Value(result[0]))?; - for element in QuadFelt::slice_as_base_elements(&poly[..output_size]).iter().rev() { - self.push_stack(AdviceSource::Value(*element))?; - } + Ok(()) +} - Ok(()) - } +/// 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 fn push_ext2_intt_result( + advice_provider: &mut impl AdviceProvider, + 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(); - /// 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. - fn push_signature( - &mut self, - process: ProcessState, - kind: SignatureKind, - ) -> 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(()) + if input_size <= 1 { + return Err(Ext2InttError::DomainSizeTooSmall(input_size as u64).into()); } - - /// 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, ...] - fn push_leading_zeros(&mut self, process: ProcessState) -> Result<(), ExecutionError> { - push_transformed_stack_top(self, process, |stack_top| Felt::from(stack_top.leading_zeros())) + if !input_size.is_power_of_two() { + return Err(Ext2InttError::DomainSizeNotPowerOf2(input_size as u64).into()); } - - /// 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, ...] - fn push_trailing_zeros(&mut self, process: ProcessState) -> Result<(), ExecutionError> { - push_transformed_stack_top(self, process, |stack_top| { - Felt::from(stack_top.trailing_zeros()) - }) + if input_start_ptr >= u32::MAX as u64 { + return Err(Ext2InttError::InputStartAddressTooBig(input_start_ptr).into()); } - - /// 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, ...] - fn push_leading_ones(&mut self, process: ProcessState) -> Result<(), ExecutionError> { - push_transformed_stack_top(self, process, |stack_top| Felt::from(stack_top.leading_ones())) + if input_size > u32::MAX as usize { + return Err(Ext2InttError::InputSizeTooBig(input_size as u64).into()); } - /// 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, ...] - fn push_trailing_ones(&mut self, process: ProcessState) -> Result<(), ExecutionError> { - push_transformed_stack_top(self, process, |stack_top| Felt::from(stack_top.trailing_ones())) + 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()); } - /// 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. - 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(()) + if output_size == 0 { + return Err(Ext2InttError::OutputSizeIsZero.into()); } - - // DEFAULT MERKLE STORE INJECTORS - // -------------------------------------------------------------------------------------------- - - /// Updates the node of a Merkle tree specified by the values on the top of the operand stack. - /// Returns the path from the updated node to the new root of the tree to the caller. - /// - /// Inputs: - /// Operand stack: [OLD_NODE, depth, index, OLD_ROOT, NEW_NODE, ...] - /// Advice: [...] - /// Merkle store: {...} - /// - /// Outputs: - /// Operand stack: [OLD_NODE, depth, index, OLD_ROOT, NEW_NODE, ...] - /// Advice stack: [...] - /// Merkle store: {path, ...} - /// Return: \[path\] - fn update_operand_stack_merkle_node( - &mut self, - 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, _) = self.update_merkle_node(old_root, &depth, &index, new_node)?; - Ok(path) + if output_size > input_size { + return Err(Ext2InttError::OutputSizeTooBig(output_size, input_size).into()); } - // DEFAULT MERKLE STORE EXTRACTORS - // -------------------------------------------------------------------------------------------- + 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))?; - /// Extracts a Merkle path for the node specified by the values at the top of the operand stack - /// and returns it to the caller. - /// - /// # Errors - /// Returns an error if the Merkle store does not contain the specified Merkle path. - /// - /// Inputs: - /// Operand stack: [WORD, depth, index, ROOT, ...] - /// Advice stack: [...] - /// Advice map: {...} - /// Merkle store: {path, ...} - /// - /// Outputs: - /// Operand stack: [WORD, depth, index, ROOT, ...] - /// Advice stack: [...] - /// Advice map: {...} - /// Merkle store: {path, ...} - /// Return: \[path\] - fn get_operand_stack_merkle_path( - &mut self, - process: ProcessState, - ) -> Result { - let depth = process.get_stack_item(4); - let index = process.get_stack_item(5); - let root = [ - process.get_stack_item(9), - process.get_stack_item(8), - process.get_stack_item(7), - process.get_stack_item(6), - ]; - self.get_merkle_path(root, &depth, &index) + poly.push(QuadFelt::new(word[0], word[1])); + poly.push(QuadFelt::new(word[2], word[3])); } - // DEFAULT 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. - /// - /// # Panics - /// Will panic as unimplemented if the target depth is `64`. - 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(()); - } - } + let twiddles = fft::get_inv_twiddles::(input_size); + fft::interpolate_poly::(&mut poly, &twiddles); - // 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(()) + for element in QuadFelt::slice_as_base_elements(&poly[..output_size]).iter().rev() { + advice_provider.push_stack(AdviceSource::Value(*element))?; } - // 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), - } - } + Ok(()) } -impl AdviceProvider for &mut T -where - T: AdviceProvider, -{ - fn pop_stack(&mut self, process: ProcessState) -> Result { - T::pop_stack(self, process) - } - - fn pop_stack_word(&mut self, process: ProcessState) -> Result { - T::pop_stack_word(self, process) - } - - fn pop_stack_dword(&mut self, process: ProcessState) -> Result<[Word; 2], ExecutionError> { - T::pop_stack_dword(self, process) +/// 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 fn push_signature( + advice_provider: &mut impl AdviceProvider, + process: ProcessState, + kind: SignatureKind, +) -> Result<(), ExecutionError> { + let pub_key = process.get_stack_word(0); + let msg = process.get_stack_word(1); + let result: Vec = get_signature(advice_provider, kind, pub_key, msg)?; + for r in result { + advice_provider.push_stack(AdviceSource::Value(r))?; } + Ok(()) +} - fn push_stack(&mut self, source: AdviceSource) -> Result<(), ExecutionError> { - T::push_stack(self, source) - } +/// 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 fn push_leading_zeros( + advice_provider: &mut impl AdviceProvider, + process: ProcessState, +) -> Result<(), ExecutionError> { + push_transformed_stack_top(advice_provider, process, |stack_top| { + Felt::from(stack_top.leading_zeros()) + }) +} - fn insert_into_map(&mut self, key: Word, values: Vec) { - T::insert_into_map(self, key, values) - } +/// 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 fn push_trailing_zeros( + advice_provider: &mut impl AdviceProvider, + process: ProcessState, +) -> Result<(), ExecutionError> { + push_transformed_stack_top(advice_provider, process, |stack_top| { + Felt::from(stack_top.trailing_zeros()) + }) +} - fn get_signature( - &self, - kind: SignatureKind, - pub_key: Word, - msg: Word, - ) -> Result, ExecutionError> { - T::get_signature(self, kind, pub_key, msg) - } +/// 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 fn push_leading_ones( + advice_provider: &mut impl AdviceProvider, + process: ProcessState, +) -> Result<(), ExecutionError> { + push_transformed_stack_top(advice_provider, process, |stack_top| { + Felt::from(stack_top.leading_ones()) + }) +} - fn get_mapped_values(&self, key: &RpoDigest) -> Option<&[Felt]> { - T::get_mapped_values(self, key) - } +/// 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 fn push_trailing_ones( + advice_provider: &mut impl AdviceProvider, + process: ProcessState, +) -> Result<(), ExecutionError> { + push_transformed_stack_top(advice_provider, process, |stack_top| { + Felt::from(stack_top.trailing_ones()) + }) +} - fn get_tree_node( - &self, - root: Word, - depth: &Felt, - index: &Felt, - ) -> Result { - T::get_tree_node(self, root, depth, index) +/// 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 fn push_ilog2( + advice_provider: &mut impl AdviceProvider, + 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()); + advice_provider.push_stack(AdviceSource::Value(ilog2))?; + Ok(()) +} - fn get_merkle_path( - &self, - root: Word, - depth: &Felt, - index: &Felt, - ) -> Result { - T::get_merkle_path(self, root, depth, index) - } +/// Updates the node of a Merkle tree specified by the values on the top of the operand stack. +/// Returns the path from the updated node to the new root of the tree to the caller. +/// +/// Inputs: +/// Operand stack: [OLD_NODE, depth, index, OLD_ROOT, NEW_NODE, ...] +/// Advice: [...] +/// Merkle store: {...} +/// +/// Outputs: +/// Operand stack: [OLD_NODE, depth, index, OLD_ROOT, NEW_NODE, ...] +/// Advice stack: [...] +/// Merkle store: {path, ...} +/// Return: \[path\] +pub fn update_operand_stack_merkle_node( + advice_provider: &mut impl AdviceProvider, + 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(path) +} - fn get_leaf_depth( - &self, - root: Word, - tree_depth: &Felt, - index: &Felt, - ) -> Result { - T::get_leaf_depth(self, root, tree_depth, index) - } +/// 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. +/// +/// # Panics +/// Will panic as unimplemented if the target depth is `64`. +pub fn push_smtpeek_result( + advice_provider: &mut impl AdviceProvider, + 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 = 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(()); + } + } - fn update_merkle_node( - &mut self, - root: Word, - depth: &Felt, - index: &Felt, - value: Word, - ) -> Result<(MerklePath, Word), ExecutionError> { - T::update_merkle_node(self, root, depth, index, value) + // 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(()) +} - fn merge_roots(&mut self, lhs: Word, rhs: Word) -> Result { - T::merge_roots(self, lhs, rhs) +/// Returns a signature on a message using a public key. +pub fn get_signature( + advice_provider: &impl AdviceProvider, + kind: SignatureKind, + pub_key: Word, + msg: Word, +) -> Result, ExecutionError> { + let pk_sk = advice_provider + .get_mapped_values(&pub_key.into()) + .ok_or(ExecutionError::AdviceMapKeyNotFound(pub_key))?; + + match kind { + SignatureKind::RpoFalcon512 => dsa::falcon_sign(pk_sk, msg), } } diff --git a/processor/src/operations/crypto_ops.rs b/processor/src/operations/crypto_ops.rs index acbecd545..cb5b9e90c 100644 --- a/processor/src/operations/crypto_ops.rs +++ b/processor/src/operations/crypto_ops.rs @@ -1,5 +1,7 @@ use super::{ExecutionError, Operation, Process}; -use crate::{crypto::MerklePath, AdviceProvider, Host}; +use crate::{ + crypto::MerklePath, host::advice::update_operand_stack_merkle_node, AdviceProvider, Host, +}; // CRYPTOGRAPHIC OPERATIONS // ================================================================================================ @@ -70,13 +72,13 @@ impl Process { ) -> Result<(), ExecutionError> { // read node value, depth, index and root value from the stack let node = [self.stack.get(3), self.stack.get(2), self.stack.get(1), self.stack.get(0)]; + let depth = self.stack.get(4); let index = self.stack.get(5); let root = [self.stack.get(9), self.stack.get(8), self.stack.get(7), self.stack.get(6)]; // 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())?; + let path = host.advice_provider_mut().get_merkle_path(root, &depth, &index)?; // use hasher to compute the Merkle root of the path let (addr, computed_root) = self.chiplets.build_merkle_root(node, &path, index); @@ -148,7 +150,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())?; + update_operand_stack_merkle_node(host.advice_provider_mut(), self.into())?; assert_eq!(path.len(), depth.as_int() as usize); diff --git a/processor/src/operations/sys_ops.rs b/processor/src/operations/sys_ops.rs index f4e70b5f7..f67a03148 100644 --- a/processor/src/operations/sys_ops.rs +++ b/processor/src/operations/sys_ops.rs @@ -7,7 +7,16 @@ use super::{ }, ExecutionError, Process, }; -use crate::{AdviceProvider, Host, ProcessState}; +use crate::{ + host::advice::{ + copy_map_value_to_adv_stack, copy_merkle_node_to_adv_stack, insert_hdword_into_adv_map, + insert_hperm_into_adv_map, insert_mem_values_into_adv_map, merge_merkle_nodes, + push_ext2_intt_result, push_ext2_inv_result, push_ilog2, push_leading_ones, + push_leading_zeros, push_signature, push_smtpeek_result, push_trailing_ones, + push_trailing_zeros, push_u64_div_result, update_operand_stack_merkle_node, + }, + Host, ProcessState, +}; // SYSTEM OPERATIONS // ================================================================================================ @@ -139,43 +148,43 @@ impl Process { let advice_provider = host.advice_provider_mut(); let process_state: ProcessState = self.into(); match advice_injector { - AdviceInjector::MerkleNodeMerge => advice_provider.merge_merkle_nodes(process_state), + AdviceInjector::MerkleNodeMerge => merge_merkle_nodes(advice_provider, process_state), AdviceInjector::MerkleNodeToStack => { - advice_provider.copy_merkle_node_to_adv_stack(process_state) + copy_merkle_node_to_adv_stack(advice_provider, process_state) }, AdviceInjector::MapValueToStack => { - advice_provider.copy_map_value_to_adv_stack(process_state, false) + copy_map_value_to_adv_stack(advice_provider, process_state, false) }, AdviceInjector::MapValueToStackN => { - advice_provider.copy_map_value_to_adv_stack(process_state, true) + copy_map_value_to_adv_stack(advice_provider, process_state, true) }, AdviceInjector::UpdateMerkleNode => { - let _ = advice_provider.update_operand_stack_merkle_node(process_state)?; + let _ = update_operand_stack_merkle_node(advice_provider, process_state)?; Ok(()) }, - AdviceInjector::U64Div => advice_provider.push_u64_div_result(process_state), - AdviceInjector::Ext2Inv => advice_provider.push_ext2_inv_result(process_state), - AdviceInjector::Ext2Intt => advice_provider.push_ext2_intt_result(process_state), - AdviceInjector::SmtPeek => advice_provider.push_smtpeek_result(process_state), - AdviceInjector::U32Clz => advice_provider.push_leading_zeros(process_state), - AdviceInjector::U32Ctz => advice_provider.push_trailing_zeros(process_state), - AdviceInjector::U32Clo => advice_provider.push_leading_ones(process_state), - AdviceInjector::U32Cto => advice_provider.push_trailing_ones(process_state), - AdviceInjector::ILog2 => advice_provider.push_ilog2(process_state), + AdviceInjector::U64Div => push_u64_div_result(advice_provider, process_state), + AdviceInjector::Ext2Inv => push_ext2_inv_result(advice_provider, process_state), + AdviceInjector::Ext2Intt => push_ext2_intt_result(advice_provider, process_state), + AdviceInjector::SmtPeek => push_smtpeek_result(advice_provider, process_state), + AdviceInjector::U32Clz => push_leading_zeros(advice_provider, process_state), + AdviceInjector::U32Ctz => push_trailing_zeros(advice_provider, process_state), + AdviceInjector::U32Clo => push_leading_ones(advice_provider, process_state), + AdviceInjector::U32Cto => push_trailing_ones(advice_provider, process_state), + AdviceInjector::ILog2 => push_ilog2(advice_provider, process_state), AdviceInjector::MemToMap => { - advice_provider.insert_mem_values_into_adv_map(process_state) + insert_mem_values_into_adv_map(advice_provider, process_state) }, AdviceInjector::HdwordToMap => { - advice_provider.insert_hdword_into_adv_map(process_state, ZERO) + insert_hdword_into_adv_map(advice_provider, process_state, ZERO) }, AdviceInjector::HdwordToMapWithDomain => { let domain = self.stack.get(8); - advice_provider.insert_hdword_into_adv_map(process_state, domain) + insert_hdword_into_adv_map(advice_provider, process_state, domain) }, - AdviceInjector::HpermToMap => advice_provider.insert_hperm_into_adv_map(process_state), + AdviceInjector::HpermToMap => insert_hperm_into_adv_map(advice_provider, process_state), AdviceInjector::FalconSigToStack => { - advice_provider.push_signature(process_state, SignatureKind::RpoFalcon512) + push_signature(advice_provider, process_state, SignatureKind::RpoFalcon512) }, } } From 9716730cde1c0a8ebd59387f1d83b28aabc634d7 Mon Sep 17 00:00:00 2001 From: Philippe Laferriere Date: Fri, 22 Nov 2024 08:58:55 -0500 Subject: [PATCH 7/8] refactor: remove unused `AdviceInjector::UpdateMerkleNode` --- core/src/operations/decorators/advice.rs | 21 -------------- processor/src/host/advice/mod.rs | 35 ------------------------ processor/src/operations/crypto_ops.rs | 9 +++--- processor/src/operations/sys_ops.rs | 6 +--- 4 files changed, 5 insertions(+), 66 deletions(-) diff --git a/core/src/operations/decorators/advice.rs b/core/src/operations/decorators/advice.rs index 5ce7dcead..7197486c6 100644 --- a/core/src/operations/decorators/advice.rs +++ b/core/src/operations/decorators/advice.rs @@ -11,7 +11,6 @@ pub use constants::*; mod constants { pub const EVENT_MERKLE_NODE_MERGE: u32 = 276124218; pub const EVENT_MERKLE_NODE_TO_STACK: u32 = 361943238; - pub const EVENT_UPDATE_MERKLE_NODE: u32 = 483702215; pub const EVENT_MAP_VALUE_TO_STACK: u32 = 574478993; pub const EVENT_MAP_VALUE_TO_STACK_N: u32 = 630847990; pub const EVENT_U64_DIV: u32 = 678156251; @@ -73,21 +72,6 @@ pub enum AdviceInjector { /// Merkle store: {TREE_ROOT<-NODE} MerkleNodeToStack, - /// Updates the node of a Merkle tree specified by the values at the top of the operand stack. - /// Returns the path from the updated node to the new root of the tree to the caller. - /// - /// Inputs: - /// Operand stack: [OLD_NODE, depth, index, OLD_ROOT, NEW_NODE, ...] - /// Advice: [...] - /// Merkle store: {...} - /// - /// Outputs: - /// Operand stack: [OLD_NODE, depth, index, OLD_ROOT, NEW_NODE, ...] - /// Advice stack: [...] - /// Merkle store: {path, ...} - /// Return: \[path\] - UpdateMerkleNode, - /// 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. /// @@ -322,7 +306,6 @@ impl AdviceInjector { match self { AdviceInjector::MerkleNodeMerge => EVENT_MERKLE_NODE_MERGE, AdviceInjector::MerkleNodeToStack => EVENT_MERKLE_NODE_TO_STACK, - AdviceInjector::UpdateMerkleNode => EVENT_UPDATE_MERKLE_NODE, AdviceInjector::MapValueToStack => EVENT_MAP_VALUE_TO_STACK, AdviceInjector::MapValueToStackN => EVENT_MAP_VALUE_TO_STACK_N, AdviceInjector::U64Div => EVENT_U64_DIV, @@ -348,7 +331,6 @@ impl AdviceInjector { match event_id { EVENT_MERKLE_NODE_MERGE => Some(AdviceInjector::MerkleNodeMerge), EVENT_MERKLE_NODE_TO_STACK => Some(AdviceInjector::MerkleNodeToStack), - EVENT_UPDATE_MERKLE_NODE => Some(AdviceInjector::UpdateMerkleNode), EVENT_MAP_VALUE_TO_STACK => Some(AdviceInjector::MapValueToStack), EVENT_MAP_VALUE_TO_STACK_N => Some(AdviceInjector::MapValueToStackN), EVENT_U64_DIV => Some(AdviceInjector::U64Div), @@ -381,9 +363,6 @@ impl fmt::Display for AdviceInjector { match self { Self::MerkleNodeMerge => write!(f, "merkle_node_merge"), Self::MerkleNodeToStack => write!(f, "merkle_node_to_stack"), - Self::UpdateMerkleNode => { - write!(f, "update_merkle_node") - }, Self::MapValueToStack => write!(f, "map_value_to_stack"), Self::MapValueToStackN => write!(f, "map_value_to_stack_with_len"), Self::U64Div => write!(f, "div_u64"), diff --git a/processor/src/host/advice/mod.rs b/processor/src/host/advice/mod.rs index 331714f9b..c3ed79db6 100644 --- a/processor/src/host/advice/mod.rs +++ b/processor/src/host/advice/mod.rs @@ -697,41 +697,6 @@ pub fn push_ilog2( Ok(()) } -/// Updates the node of a Merkle tree specified by the values on the top of the operand stack. -/// Returns the path from the updated node to the new root of the tree to the caller. -/// -/// Inputs: -/// Operand stack: [OLD_NODE, depth, index, OLD_ROOT, NEW_NODE, ...] -/// Advice: [...] -/// Merkle store: {...} -/// -/// Outputs: -/// Operand stack: [OLD_NODE, depth, index, OLD_ROOT, NEW_NODE, ...] -/// Advice stack: [...] -/// Merkle store: {path, ...} -/// Return: \[path\] -pub fn update_operand_stack_merkle_node( - advice_provider: &mut impl AdviceProvider, - 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(path) -} - /// Pushes onto the advice stack the value associated with the specified key in a Sparse /// Merkle Tree defined by the specified root. /// diff --git a/processor/src/operations/crypto_ops.rs b/processor/src/operations/crypto_ops.rs index cb5b9e90c..4850cd310 100644 --- a/processor/src/operations/crypto_ops.rs +++ b/processor/src/operations/crypto_ops.rs @@ -1,7 +1,5 @@ use super::{ExecutionError, Operation, Process}; -use crate::{ - crypto::MerklePath, host::advice::update_operand_stack_merkle_node, AdviceProvider, Host, -}; +use crate::{AdviceProvider, Host}; // CRYPTOGRAPHIC OPERATIONS // ================================================================================================ @@ -149,8 +147,9 @@ impl Process { // get a Merkle path to it. the length of the returned path is expected to match the // 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 = - update_operand_stack_merkle_node(host.advice_provider_mut(), self.into())?; + let (path, _) = host + .advice_provider_mut() + .update_merkle_node(old_root, &depth, &index, new_node)?; assert_eq!(path.len(), depth.as_int() as usize); diff --git a/processor/src/operations/sys_ops.rs b/processor/src/operations/sys_ops.rs index f67a03148..ac3a11976 100644 --- a/processor/src/operations/sys_ops.rs +++ b/processor/src/operations/sys_ops.rs @@ -13,7 +13,7 @@ use crate::{ insert_hperm_into_adv_map, insert_mem_values_into_adv_map, merge_merkle_nodes, push_ext2_intt_result, push_ext2_inv_result, push_ilog2, push_leading_ones, push_leading_zeros, push_signature, push_smtpeek_result, push_trailing_ones, - push_trailing_zeros, push_u64_div_result, update_operand_stack_merkle_node, + push_trailing_zeros, push_u64_div_result, }, Host, ProcessState, }; @@ -158,10 +158,6 @@ impl Process { AdviceInjector::MapValueToStackN => { copy_map_value_to_adv_stack(advice_provider, process_state, true) }, - AdviceInjector::UpdateMerkleNode => { - let _ = update_operand_stack_merkle_node(advice_provider, process_state)?; - Ok(()) - }, AdviceInjector::U64Div => push_u64_div_result(advice_provider, process_state), AdviceInjector::Ext2Inv => push_ext2_inv_result(advice_provider, process_state), AdviceInjector::Ext2Intt => push_ext2_intt_result(advice_provider, process_state), From b82f2f1f2770825af4985c398d18eafa8265ebd8 Mon Sep 17 00:00:00 2001 From: Philippe Laferriere Date: Fri, 22 Nov 2024 09:30:29 -0500 Subject: [PATCH 8/8] refactor: move system event handlers to new `sys_event_handlers` module, and rename AdviceInjector --- assembly/src/assembler/basic_block_builder.rs | 9 +- assembly/src/assembler/instruction/adv_ops.rs | 10 +- .../src/assembler/instruction/crypto_ops.rs | 6 +- .../src/assembler/instruction/ext2_ops.rs | 6 +- .../src/assembler/instruction/field_ops.rs | 4 +- assembly/src/assembler/instruction/mod.rs | 4 +- assembly/src/assembler/instruction/u32_ops.rs | 19 +- assembly/src/ast/instruction/advice.rs | 16 +- assembly/src/ast/instruction/mod.rs | 4 +- assembly/src/ast/instruction/print.rs | 2 +- assembly/src/ast/mod.rs | 2 +- assembly/src/ast/tests.rs | 8 +- assembly/src/ast/visit.rs | 35 +- assembly/src/parser/grammar.lalrpop | 26 +- core/src/lib.rs | 6 +- core/src/operations/decorators/mod.rs | 3 - core/src/operations/mod.rs | 3 +- .../decorators/advice.rs => sys_events.rs} | 95 +-- .../user_docs/assembly/code_organization.md | 2 - docs/src/user_docs/assembly/io_operations.md | 6 +- processor/src/host/advice/inputs.rs | 10 +- processor/src/host/advice/mod.rs | 685 +--------------- processor/src/host/advice/providers.rs | 5 +- processor/src/lib.rs | 5 +- .../advice => operations/sys_ops}/dsa.rs | 13 +- .../operations/{sys_ops.rs => sys_ops/mod.rs} | 66 +- .../operations/sys_ops/sys_event_handlers.rs | 732 ++++++++++++++++++ 27 files changed, 886 insertions(+), 896 deletions(-) rename core/src/{operations/decorators/advice.rs => sys_events.rs} (82%) rename processor/src/{host/advice => operations/sys_ops}/dsa.rs (94%) rename processor/src/operations/{sys_ops.rs => sys_ops/mod.rs} (77%) create mode 100644 processor/src/operations/sys_ops/sys_event_handlers.rs diff --git a/assembly/src/assembler/basic_block_builder.rs b/assembly/src/assembler/basic_block_builder.rs index 85f886f97..4d924b58b 100644 --- a/assembly/src/assembler/basic_block_builder.rs +++ b/assembly/src/assembler/basic_block_builder.rs @@ -2,7 +2,8 @@ use alloc::{borrow::Borrow, string::ToString, vec::Vec}; use vm_core::{ mast::{DecoratorId, MastNodeId}, - AdviceInjector, AssemblyOp, Decorator, Operation, + sys_events::SystemEvent, + AssemblyOp, Decorator, Operation, }; use super::{mast_forest_builder::MastForestBuilder, BodyWrapper, DecoratorList, ProcedureContext}; @@ -94,10 +95,10 @@ impl BasicBlockBuilder<'_> { self.ops.resize(new_len, op); } - /// Converts the advice injector into its corresponding event ID, and adds an `Emit` operation + /// Converts the system event into its corresponding event ID, and adds an `Emit` operation /// to the list of basic block operations. - pub fn push_advice_injector(&mut self, injector: AdviceInjector) { - self.push_op(Operation::Emit(injector.into_event_id())) + pub fn push_system_event(&mut self, sys_event: SystemEvent) { + self.push_op(Operation::Emit(sys_event.into_event_id())) } } diff --git a/assembly/src/assembler/instruction/adv_ops.rs b/assembly/src/assembler/instruction/adv_ops.rs index 049ace4e4..399913946 100644 --- a/assembly/src/assembler/instruction/adv_ops.rs +++ b/assembly/src/assembler/instruction/adv_ops.rs @@ -1,7 +1,7 @@ use vm_core::Operation; use super::{validate_param, BasicBlockBuilder}; -use crate::{ast::AdviceInjectorNode, AssemblyError, ADVICE_READ_LIMIT}; +use crate::{AssemblyError, ADVICE_READ_LIMIT}; // NON-DETERMINISTIC (ADVICE) INPUTS // ================================================================================================ @@ -18,11 +18,3 @@ pub fn adv_push(block_builder: &mut BasicBlockBuilder, n: u8) -> Result<(), Asse block_builder.push_op_many(Operation::AdvPop, n as usize); Ok(()) } - -// ADVICE INJECTORS -// ================================================================================================ - -/// Appends advice injector decorator to the span. -pub fn adv_inject(block_builder: &mut BasicBlockBuilder, injector: &AdviceInjectorNode) { - block_builder.push_advice_injector(injector.into()) -} diff --git a/assembly/src/assembler/instruction/crypto_ops.rs b/assembly/src/assembler/instruction/crypto_ops.rs index a89015a06..7efed9ee9 100644 --- a/assembly/src/assembler/instruction/crypto_ops.rs +++ b/assembly/src/assembler/instruction/crypto_ops.rs @@ -1,4 +1,4 @@ -use vm_core::{AdviceInjector, Felt, Operation::*}; +use vm_core::{sys_events::SystemEvent, Felt, Operation::*}; use super::BasicBlockBuilder; use crate::AssemblyError; @@ -169,7 +169,7 @@ pub(super) fn mtree_merge(block_builder: &mut BasicBlockBuilder) { // invoke the advice provider function to merge 2 Merkle trees defined by the roots on the top // of the operand stack - block_builder.push_advice_injector(AdviceInjector::MerkleNodeMerge); + block_builder.push_system_event(SystemEvent::MerkleNodeMerge); // perform the `hmerge`, updating the operand stack hmerge(block_builder) @@ -202,7 +202,7 @@ fn read_mtree_node(block_builder: &mut BasicBlockBuilder) { // new node value post the tree root: [d, i, R, V_new] // // pops the value of the node we are looking for from the advice stack - block_builder.push_advice_injector(AdviceInjector::MerkleNodeToStack); + block_builder.push_system_event(SystemEvent::MerkleNodeToStack); // pops the old node value from advice the stack => MPVERIFY: [V_old, d, i, R, ...] // MRUPDATE: [V_old, d, i, R, V_new, ...] diff --git a/assembly/src/assembler/instruction/ext2_ops.rs b/assembly/src/assembler/instruction/ext2_ops.rs index b2729553e..b3d2cfc76 100644 --- a/assembly/src/assembler/instruction/ext2_ops.rs +++ b/assembly/src/assembler/instruction/ext2_ops.rs @@ -1,4 +1,4 @@ -use vm_core::{AdviceInjector::Ext2Inv, Operation::*}; +use vm_core::{sys_events::SystemEvent::Ext2Inv, Operation::*}; use super::BasicBlockBuilder; use crate::AssemblyError; @@ -54,7 +54,7 @@ pub fn ext2_mul(block_builder: &mut BasicBlockBuilder) { /// /// This operation takes 11 VM cycles. pub fn ext2_div(block_builder: &mut BasicBlockBuilder) { - block_builder.push_advice_injector(Ext2Inv); + block_builder.push_system_event(Ext2Inv); #[rustfmt::skip] let ops = [ AdvPop, // [b0', b1, b0, a1, a0, ...] @@ -114,7 +114,7 @@ pub fn ext2_neg(block_builder: &mut BasicBlockBuilder) { /// /// This operation takes 8 VM cycles. pub fn ext2_inv(block_builder: &mut BasicBlockBuilder) -> Result<(), AssemblyError> { - block_builder.push_advice_injector(Ext2Inv); + block_builder.push_system_event(Ext2Inv); #[rustfmt::skip] let ops = [ AdvPop, // [a0', a1, a0, ...] diff --git a/assembly/src/assembler/instruction/field_ops.rs b/assembly/src/assembler/instruction/field_ops.rs index 7033a92ef..e4797c441 100644 --- a/assembly/src/assembler/instruction/field_ops.rs +++ b/assembly/src/assembler/instruction/field_ops.rs @@ -1,4 +1,4 @@ -use vm_core::{AdviceInjector, FieldElement, Operation::*}; +use vm_core::{sys_events::SystemEvent, FieldElement, Operation::*}; use super::{validate_param, BasicBlockBuilder}; use crate::{ @@ -260,7 +260,7 @@ fn perform_exp_for_small_power(span_builder: &mut BasicBlockBuilder, pow: u64) { /// # Errors /// Returns an error if the logarithm argument (top stack element) equals ZERO. pub fn ilog2(block_builder: &mut BasicBlockBuilder) { - block_builder.push_advice_injector(AdviceInjector::ILog2); + block_builder.push_system_event(SystemEvent::ILog2); block_builder.push_op(AdvPop); // [ilog2, n, ...] // compute the power-of-two for the value given in the advice tape (17 cycles) diff --git a/assembly/src/assembler/instruction/mod.rs b/assembly/src/assembler/instruction/mod.rs index 76de7147c..34b45bc15 100644 --- a/assembly/src/assembler/instruction/mod.rs +++ b/assembly/src/assembler/instruction/mod.rs @@ -361,7 +361,9 @@ impl Assembler { false, )?, - Instruction::AdvInject(injector) => adv_ops::adv_inject(block_builder, injector), + Instruction::SysEvent(system_event) => { + block_builder.push_system_event(system_event.into()) + }, // ----- cryptographic instructions --------------------------------------------------- Instruction::Hash => crypto_ops::hash(block_builder), diff --git a/assembly/src/assembler/instruction/u32_ops.rs b/assembly/src/assembler/instruction/u32_ops.rs index 23801be09..42159babf 100644 --- a/assembly/src/assembler/instruction/u32_ops.rs +++ b/assembly/src/assembler/instruction/u32_ops.rs @@ -1,5 +1,6 @@ use vm_core::{ - AdviceInjector, Felt, + sys_events::SystemEvent, + Felt, Operation::{self, *}, }; @@ -299,7 +300,7 @@ pub fn u32popcnt(span_builder: &mut BasicBlockBuilder) { /// /// This operation takes 42 VM cycles. pub fn u32clz(block_builder: &mut BasicBlockBuilder) { - block_builder.push_advice_injector(AdviceInjector::U32Clz); + block_builder.push_system_event(SystemEvent::U32Clz); block_builder.push_op(AdvPop); // [clz, n, ...] verify_clz(block_builder); @@ -311,7 +312,7 @@ pub fn u32clz(block_builder: &mut BasicBlockBuilder) { /// /// This operation takes 34 VM cycles. pub fn u32ctz(block_builder: &mut BasicBlockBuilder) { - block_builder.push_advice_injector(AdviceInjector::U32Ctz); + block_builder.push_system_event(SystemEvent::U32Ctz); block_builder.push_op(AdvPop); // [ctz, n, ...] verify_ctz(block_builder); @@ -323,7 +324,7 @@ pub fn u32ctz(block_builder: &mut BasicBlockBuilder) { /// /// This operation takes 41 VM cycles. pub fn u32clo(block_builder: &mut BasicBlockBuilder) { - block_builder.push_advice_injector(AdviceInjector::U32Clo); + block_builder.push_system_event(SystemEvent::U32Clo); block_builder.push_op(AdvPop); // [clo, n, ...] verify_clo(block_builder); @@ -335,7 +336,7 @@ pub fn u32clo(block_builder: &mut BasicBlockBuilder) { /// /// This operation takes 33 VM cycles. pub fn u32cto(block_builder: &mut BasicBlockBuilder) { - block_builder.push_advice_injector(AdviceInjector::U32Cto); + block_builder.push_system_event(SystemEvent::U32Cto); block_builder.push_op(AdvPop); // [cto, n, ...] verify_cto(block_builder); @@ -415,7 +416,7 @@ fn prepare_bitwise( } /// Appends relevant operations to the span block for the correctness check of the `U32Clz` -/// injector. +/// system event. /// The idea is to compare the actual value with a bitmask consisting of `clz` leading ones to /// check that every bit in `clz` leading bits is zero and `1` additional one to check that /// `clz + 1`'th leading bit is one: @@ -496,7 +497,7 @@ fn verify_clz(block_builder: &mut BasicBlockBuilder) { } /// Appends relevant operations to the span block for the correctness check of the `U32Clo` -/// injector. +/// system event. /// The idea is to compare the actual value with a bitmask consisting of `clo` leading ones to /// check that every bit in `clo` leading bits is one and `1` additional one to check that /// `clo + 1`'th leading bit is zero: @@ -571,7 +572,7 @@ fn verify_clo(block_builder: &mut BasicBlockBuilder) { } /// Appends relevant operations to the span block for the correctness check of the `U32Ctz` -/// injector. +/// system event. /// The idea is to compare the actual value with a bitmask consisting of `ctz` trailing ones to /// check that every bit in `ctz` trailing bits is zero and `1` additional one to check that /// `ctz + 1`'th trailing bit is one: @@ -645,7 +646,7 @@ fn verify_ctz(block_builder: &mut BasicBlockBuilder) { } /// Appends relevant operations to the span block for the correctness check of the `U32Cto` -/// injector. +/// system event. /// The idea is to compare the actual value with a bitmask consisting of `cto` trailing ones to /// check that every bit in `cto` trailing bits is one and `1` additional one to check that /// `cto + 1`'th trailing bit is zero: diff --git a/assembly/src/ast/instruction/advice.rs b/assembly/src/ast/instruction/advice.rs index b8b7b0d4b..f6bfe807d 100644 --- a/assembly/src/ast/instruction/advice.rs +++ b/assembly/src/ast/instruction/advice.rs @@ -1,8 +1,8 @@ use core::fmt; -use vm_core::AdviceInjector; +use vm_core::sys_events::SystemEvent; -// ADVICE INJECTOR NODE +// SYSTEM EVENT NODE // ================================================================================================ /// Instructions which inject data into the advice provider. @@ -11,7 +11,7 @@ use vm_core::AdviceInjector; /// - Push new data onto the advice stack. /// - Insert new data into the advice map. #[derive(Clone, PartialEq, Eq, Debug)] -pub enum AdviceInjectorNode { +pub enum SystemEventNode { PushU64Div, PushExt2intt, PushSmtPeek, @@ -25,9 +25,9 @@ pub enum AdviceInjectorNode { PushSignature { kind: SignatureKind }, } -impl From<&AdviceInjectorNode> for AdviceInjector { - fn from(value: &AdviceInjectorNode) -> Self { - use AdviceInjectorNode::*; +impl From<&SystemEventNode> for SystemEvent { + fn from(value: &SystemEventNode) -> Self { + use SystemEventNode::*; match value { PushU64Div => Self::U64Div, PushExt2intt => Self::Ext2Intt, @@ -46,13 +46,13 @@ impl From<&AdviceInjectorNode> for AdviceInjector { } } -impl crate::prettier::PrettyPrint for AdviceInjectorNode { +impl crate::prettier::PrettyPrint for SystemEventNode { fn render(&self) -> crate::prettier::Document { crate::prettier::display(self) } } -impl fmt::Display for AdviceInjectorNode { +impl fmt::Display for SystemEventNode { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::PushU64Div => write!(f, "push_u64div"), diff --git a/assembly/src/ast/instruction/mod.rs b/assembly/src/ast/instruction/mod.rs index a88441d89..b42d976d8 100644 --- a/assembly/src/ast/instruction/mod.rs +++ b/assembly/src/ast/instruction/mod.rs @@ -4,7 +4,7 @@ mod print; use alloc::vec::Vec; -pub use self::{advice::AdviceInjectorNode, debug::DebugOptions}; +pub use self::{advice::SystemEventNode, debug::DebugOptions}; use crate::{ ast::{immediate::*, InvocationTarget}, Felt, Word, @@ -239,7 +239,7 @@ pub enum Instruction { AdvPush(ImmU8), AdvLoadW, - AdvInject(AdviceInjectorNode), + SysEvent(SystemEventNode), // ----- cryptographic operations ------------------------------------------------------------ Hash, diff --git a/assembly/src/ast/instruction/print.rs b/assembly/src/ast/instruction/print.rs index b7ca11c37..07cecbab0 100644 --- a/assembly/src/ast/instruction/print.rs +++ b/assembly/src/ast/instruction/print.rs @@ -248,7 +248,7 @@ impl PrettyPrint for Instruction { Self::AdvPush(value) => inst_with_imm("adv_push", value), Self::AdvLoadW => const_text("adv_loadw"), - Self::AdvInject(injector) => inst_with_imm("adv", injector), + Self::SysEvent(sys_event) => inst_with_imm("adv", sys_event), // ----- cryptographic operations ----------------------------------------------------- Self::Hash => const_text("hash"), diff --git a/assembly/src/ast/mod.rs b/assembly/src/ast/mod.rs index a606d9e90..19d8c50e5 100644 --- a/assembly/src/ast/mod.rs +++ b/assembly/src/ast/mod.rs @@ -27,7 +27,7 @@ pub use self::{ ident::{CaseKindError, Ident, IdentError}, immediate::{ErrorCode, ImmFelt, ImmU16, ImmU32, ImmU8, Immediate}, imports::Import, - instruction::{advice::SignatureKind, AdviceInjectorNode, DebugOptions, Instruction}, + instruction::{advice::SignatureKind, DebugOptions, Instruction, SystemEventNode}, invocation_target::{InvocationTarget, Invoke, InvokeKind}, module::{Module, ModuleKind}, op::Op, diff --git a/assembly/src/ast/tests.rs b/assembly/src/ast/tests.rs index e2c81b08d..dd9e91029 100644 --- a/assembly/src/ast/tests.rs +++ b/assembly/src/ast/tests.rs @@ -467,14 +467,14 @@ fn test_ast_parsing_adv_ops() -> Result<(), Report> { #[test] fn test_ast_parsing_adv_injection() -> Result<(), Report> { - use super::AdviceInjectorNode::*; + use super::SystemEventNode::*; let context = TestContext::new(); 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)), - inst!(AdvInject(InsertMem)) + inst!(SysEvent(PushU64Div)), + inst!(SysEvent(PushMapVal)), + inst!(SysEvent(InsertMem)) )); assert_eq!(context.parse_forms(source)?, forms); Ok(()) diff --git a/assembly/src/ast/visit.rs b/assembly/src/ast/visit.rs index 9a9afd8d2..083a4d4fe 100644 --- a/assembly/src/ast/visit.rs +++ b/assembly/src/ast/visit.rs @@ -98,8 +98,8 @@ pub trait Visit { fn visit_inst(&mut self, inst: &Span) -> ControlFlow { visit_inst(self, inst) } - fn visit_advice_injector(&mut self, injector: Span<&AdviceInjectorNode>) -> ControlFlow { - visit_advice_injector(self, injector) + fn visit_system_event(&mut self, sys_event: Span<&SystemEventNode>) -> ControlFlow { + visit_system_event(self, sys_event) } fn visit_debug_options(&mut self, options: Span<&DebugOptions>) -> ControlFlow { visit_debug_options(self, options) @@ -167,8 +167,8 @@ where fn visit_inst(&mut self, inst: &Span) -> ControlFlow { (**self).visit_inst(inst) } - fn visit_advice_injector(&mut self, injector: Span<&AdviceInjectorNode>) -> ControlFlow { - (**self).visit_advice_injector(injector) + fn visit_system_event(&mut self, sys_event: Span<&SystemEventNode>) -> ControlFlow { + (**self).visit_system_event(sys_event) } fn visit_debug_options(&mut self, options: Span<&DebugOptions>) -> ControlFlow { (**self).visit_debug_options(options) @@ -315,7 +315,7 @@ where | MemStoreWImm(ref imm) | Emit(ref imm) | Trace(ref imm) => visitor.visit_immediate_u32(imm), - AdvInject(ref injector) => visitor.visit_advice_injector(Span::new(span, injector)), + SysEvent(ref sys_event) => visitor.visit_system_event(Span::new(span, sys_event)), Exec(ref target) => visitor.visit_exec(target), Call(ref target) => visitor.visit_call(target), SysCall(ref target) => visitor.visit_syscall(target), @@ -346,10 +346,7 @@ where } } -pub fn visit_advice_injector( - _visitor: &mut V, - _node: Span<&AdviceInjectorNode>, -) -> ControlFlow +pub fn visit_system_event(_visitor: &mut V, _node: Span<&SystemEventNode>) -> ControlFlow where V: ?Sized + Visit, { @@ -513,11 +510,8 @@ pub trait VisitMut { fn visit_mut_inst(&mut self, inst: &mut Span) -> ControlFlow { visit_mut_inst(self, inst) } - fn visit_mut_advice_injector( - &mut self, - injector: Span<&mut AdviceInjectorNode>, - ) -> ControlFlow { - visit_mut_advice_injector(self, injector) + fn visit_mut_system_event(&mut self, sys_event: Span<&mut SystemEventNode>) -> ControlFlow { + visit_mut_system_event(self, sys_event) } fn visit_mut_debug_options(&mut self, options: Span<&mut DebugOptions>) -> ControlFlow { visit_mut_debug_options(self, options) @@ -585,11 +579,8 @@ where fn visit_mut_inst(&mut self, inst: &mut Span) -> ControlFlow { (**self).visit_mut_inst(inst) } - fn visit_mut_advice_injector( - &mut self, - injector: Span<&mut AdviceInjectorNode>, - ) -> ControlFlow { - (**self).visit_mut_advice_injector(injector) + fn visit_mut_system_event(&mut self, sys_event: Span<&mut SystemEventNode>) -> ControlFlow { + (**self).visit_mut_system_event(sys_event) } fn visit_mut_debug_options(&mut self, options: Span<&mut DebugOptions>) -> ControlFlow { (**self).visit_mut_debug_options(options) @@ -749,7 +740,7 @@ where | MemStoreWImm(ref mut imm) | Emit(ref mut imm) | Trace(ref mut imm) => visitor.visit_mut_immediate_u32(imm), - AdvInject(ref mut injector) => visitor.visit_mut_advice_injector(Span::new(span, injector)), + SysEvent(ref mut sys_event) => visitor.visit_mut_system_event(Span::new(span, sys_event)), Exec(ref mut target) => visitor.visit_mut_exec(target), Call(ref mut target) => visitor.visit_mut_call(target), SysCall(ref mut target) => visitor.visit_mut_syscall(target), @@ -780,9 +771,9 @@ where } } -pub fn visit_mut_advice_injector( +pub fn visit_mut_system_event( _visitor: &mut V, - _node: Span<&mut AdviceInjectorNode>, + _node: Span<&mut SystemEventNode>, ) -> ControlFlow where V: ?Sized + VisitMut, diff --git a/assembly/src/parser/grammar.lalrpop b/assembly/src/parser/grammar.lalrpop index f0b6d882f..378ae1ec5 100644 --- a/assembly/src/parser/grammar.lalrpop +++ b/assembly/src/parser/grammar.lalrpop @@ -598,7 +598,7 @@ MacroInst: SmallOpsVec = { #[inline] Inst: Instruction = { - AdviceInjector, + SystemEvent, Call, Debug, InstWithBitSizeImmediate, @@ -665,18 +665,18 @@ Inst: Instruction = { } #[inline] -AdviceInjector: Instruction = { - "adv" "." "insert_hdword" => Instruction::AdvInject(AdviceInjectorNode::InsertHdword), - "adv" "." "insert_hdword_d" => Instruction::AdvInject(AdviceInjectorNode::InsertHdwordWithDomain), - "adv" "." "insert_hperm" => Instruction::AdvInject(AdviceInjectorNode::InsertHperm), - "adv" "." "insert_mem" => Instruction::AdvInject(AdviceInjectorNode::InsertMem), - "adv" "." "push_ext2intt" => Instruction::AdvInject(AdviceInjectorNode::PushExt2intt), - "adv" "." "push_mapval" => Instruction::AdvInject(AdviceInjectorNode::PushMapVal), - "adv" "." "push_mapvaln" => Instruction::AdvInject(AdviceInjectorNode::PushMapValN), - "adv" "." "push_mtnode" => Instruction::AdvInject(AdviceInjectorNode::PushMtNode), - "adv" "." "push_sig" "." => Instruction::AdvInject(AdviceInjectorNode::PushSignature { kind }), - "adv" "." "push_smtpeek" => Instruction::AdvInject(AdviceInjectorNode::PushSmtPeek), - "adv" "." "push_u64div" => Instruction::AdvInject(AdviceInjectorNode::PushU64Div), +SystemEvent: Instruction = { + "adv" "." "insert_hdword" => Instruction::SysEvent(SystemEventNode::InsertHdword), + "adv" "." "insert_hdword_d" => Instruction::SysEvent(SystemEventNode::InsertHdwordWithDomain), + "adv" "." "insert_hperm" => Instruction::SysEvent(SystemEventNode::InsertHperm), + "adv" "." "insert_mem" => Instruction::SysEvent(SystemEventNode::InsertMem), + "adv" "." "push_ext2intt" => Instruction::SysEvent(SystemEventNode::PushExt2intt), + "adv" "." "push_mapval" => Instruction::SysEvent(SystemEventNode::PushMapVal), + "adv" "." "push_mapvaln" => Instruction::SysEvent(SystemEventNode::PushMapValN), + "adv" "." "push_mtnode" => Instruction::SysEvent(SystemEventNode::PushMtNode), + "adv" "." "push_sig" "." => Instruction::SysEvent(SystemEventNode::PushSignature { kind }), + "adv" "." "push_smtpeek" => Instruction::SysEvent(SystemEventNode::PushSmtPeek), + "adv" "." "push_u64div" => Instruction::SysEvent(SystemEventNode::PushU64Div), } #[inline] diff --git a/core/src/lib.rs b/core/src/lib.rs index 355c07395..650c72657 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -116,13 +116,15 @@ pub mod prettier { mod operations; pub use operations::{ - opcode_constants::*, AdviceInjector, AssemblyOp, DebugOptions, Decorator, DecoratorIterator, - DecoratorList, Operation, SignatureKind, + opcode_constants::*, AssemblyOp, DebugOptions, Decorator, DecoratorIterator, DecoratorList, + Operation, SignatureKind, }; pub mod stack; pub use stack::{StackInputs, StackOutputs}; +pub mod sys_events; + mod advice; pub use advice::map::AdviceMap; diff --git a/core/src/operations/decorators/mod.rs b/core/src/operations/decorators/mod.rs index 9a12789e1..72c409a48 100644 --- a/core/src/operations/decorators/mod.rs +++ b/core/src/operations/decorators/mod.rs @@ -4,9 +4,6 @@ use core::fmt; use miden_crypto::hash::blake::Blake3_256; use num_traits::ToBytes; -mod advice; -pub use advice::AdviceInjector; - mod assembly_op; pub use assembly_op::AssemblyOp; diff --git a/core/src/operations/mod.rs b/core/src/operations/mod.rs index 7a2eb6e3c..725c37725 100644 --- a/core/src/operations/mod.rs +++ b/core/src/operations/mod.rs @@ -3,8 +3,7 @@ use core::fmt; use super::Felt; mod decorators; pub use decorators::{ - AdviceInjector, AssemblyOp, DebugOptions, Decorator, DecoratorIterator, DecoratorList, - SignatureKind, + AssemblyOp, DebugOptions, Decorator, DecoratorIterator, DecoratorList, SignatureKind, }; // OPERATIONS OP CODES // ================================================================================================ diff --git a/core/src/operations/decorators/advice.rs b/core/src/sys_events.rs similarity index 82% rename from core/src/operations/decorators/advice.rs rename to core/src/sys_events.rs index 7197486c6..0a4c8df2a 100644 --- a/core/src/operations/decorators/advice.rs +++ b/core/src/sys_events.rs @@ -1,12 +1,13 @@ use core::fmt; -use super::SignatureKind; -// ADVICE INJECTORS +// SYSTEM EVENTS // ================================================================================================ -// Randomly generated constant values for the VM's native events. All values were sampled +// Randomly generated constant values for the VM's system events. All values were sampled // between 0 and 2^32. pub use constants::*; + +use super::SignatureKind; #[rustfmt::skip] mod constants { pub const EVENT_MERKLE_NODE_MERGE: u32 = 276124218; @@ -38,8 +39,8 @@ mod constants { /// All actions, except for `MerkleNodeMerge`, `Ext2Inv` and `UpdateMerkleNode` can be invoked /// directly from Miden assembly via dedicated instructions. #[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub enum AdviceInjector { - // MERKLE STORE INJECTORS +pub enum SystemEvent { + // MERKLE STORE EVENTS // -------------------------------------------------------------------------------------------- /// 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)`. @@ -56,7 +57,7 @@ pub enum AdviceInjector { /// provider (i.e., the input trees are not removed). MerkleNodeMerge, - // ADVICE STACK INJECTORS + // ADVICE STACK SYSTEM EVENTS // -------------------------------------------------------------------------------------------- /// Pushes a node of the Merkle tree specified by the values on the top of the operand stack /// onto the advice stack. @@ -226,7 +227,7 @@ pub enum AdviceInjector { /// Advice stack: [ilog2(n), ...] ILog2, - // ADVICE MAP INJECTORS + // ADVICE MAP SYSTEM EVENTS // -------------------------------------------------------------------------------------------- /// 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. @@ -301,64 +302,64 @@ pub enum AdviceInjector { FalconSigToStack, } -impl AdviceInjector { +impl SystemEvent { pub fn into_event_id(self) -> u32 { match self { - AdviceInjector::MerkleNodeMerge => EVENT_MERKLE_NODE_MERGE, - AdviceInjector::MerkleNodeToStack => EVENT_MERKLE_NODE_TO_STACK, - AdviceInjector::MapValueToStack => EVENT_MAP_VALUE_TO_STACK, - AdviceInjector::MapValueToStackN => EVENT_MAP_VALUE_TO_STACK_N, - AdviceInjector::U64Div => EVENT_U64_DIV, - AdviceInjector::Ext2Inv => EVENT_EXT2_INV, - AdviceInjector::Ext2Intt => EVENT_EXT2_INTT, - AdviceInjector::SmtPeek => EVENT_SMT_PEEK, - AdviceInjector::U32Clz => EVENT_U32_CLZ, - AdviceInjector::U32Ctz => EVENT_U32_CTZ, - AdviceInjector::U32Clo => EVENT_U32_CLO, - AdviceInjector::U32Cto => EVENT_U32_CTO, - AdviceInjector::ILog2 => EVENT_ILOG2, - AdviceInjector::MemToMap => EVENT_MEM_TO_MAP, - AdviceInjector::HdwordToMap => EVENT_HDWORD_TO_MAP, - AdviceInjector::HdwordToMapWithDomain => EVENT_HDWORD_TO_MAP_WITH_DOMAIN, - AdviceInjector::HpermToMap => EVENT_HPERM_TO_MAP, - AdviceInjector::FalconSigToStack => EVENT_FALCON_SIG_TO_STACK, + SystemEvent::MerkleNodeMerge => EVENT_MERKLE_NODE_MERGE, + SystemEvent::MerkleNodeToStack => EVENT_MERKLE_NODE_TO_STACK, + SystemEvent::MapValueToStack => EVENT_MAP_VALUE_TO_STACK, + SystemEvent::MapValueToStackN => EVENT_MAP_VALUE_TO_STACK_N, + SystemEvent::U64Div => EVENT_U64_DIV, + SystemEvent::Ext2Inv => EVENT_EXT2_INV, + SystemEvent::Ext2Intt => EVENT_EXT2_INTT, + SystemEvent::SmtPeek => EVENT_SMT_PEEK, + SystemEvent::U32Clz => EVENT_U32_CLZ, + SystemEvent::U32Ctz => EVENT_U32_CTZ, + SystemEvent::U32Clo => EVENT_U32_CLO, + SystemEvent::U32Cto => EVENT_U32_CTO, + SystemEvent::ILog2 => EVENT_ILOG2, + SystemEvent::MemToMap => EVENT_MEM_TO_MAP, + SystemEvent::HdwordToMap => EVENT_HDWORD_TO_MAP, + SystemEvent::HdwordToMapWithDomain => EVENT_HDWORD_TO_MAP_WITH_DOMAIN, + SystemEvent::HpermToMap => EVENT_HPERM_TO_MAP, + SystemEvent::FalconSigToStack => EVENT_FALCON_SIG_TO_STACK, } } - /// Returns an advice injector corresponding to the specified event ID, or `None` if the event + /// Returns a system event corresponding to the specified event ID, or `None` if the event /// ID is not recognized. pub fn from_event_id(event_id: u32) -> Option { match event_id { - EVENT_MERKLE_NODE_MERGE => Some(AdviceInjector::MerkleNodeMerge), - EVENT_MERKLE_NODE_TO_STACK => Some(AdviceInjector::MerkleNodeToStack), - EVENT_MAP_VALUE_TO_STACK => Some(AdviceInjector::MapValueToStack), - EVENT_MAP_VALUE_TO_STACK_N => Some(AdviceInjector::MapValueToStackN), - EVENT_U64_DIV => Some(AdviceInjector::U64Div), - EVENT_EXT2_INV => Some(AdviceInjector::Ext2Inv), - EVENT_EXT2_INTT => Some(AdviceInjector::Ext2Intt), - EVENT_SMT_PEEK => Some(AdviceInjector::SmtPeek), - EVENT_U32_CLZ => Some(AdviceInjector::U32Clz), - EVENT_U32_CTZ => Some(AdviceInjector::U32Ctz), - EVENT_U32_CLO => Some(AdviceInjector::U32Clo), - EVENT_U32_CTO => Some(AdviceInjector::U32Cto), - EVENT_ILOG2 => Some(AdviceInjector::ILog2), - EVENT_MEM_TO_MAP => Some(AdviceInjector::MemToMap), - EVENT_HDWORD_TO_MAP => Some(AdviceInjector::HdwordToMap), - EVENT_HDWORD_TO_MAP_WITH_DOMAIN => Some(AdviceInjector::HdwordToMapWithDomain), - EVENT_HPERM_TO_MAP => Some(AdviceInjector::HpermToMap), - EVENT_FALCON_SIG_TO_STACK => Some(AdviceInjector::FalconSigToStack), + EVENT_MERKLE_NODE_MERGE => Some(SystemEvent::MerkleNodeMerge), + EVENT_MERKLE_NODE_TO_STACK => Some(SystemEvent::MerkleNodeToStack), + EVENT_MAP_VALUE_TO_STACK => Some(SystemEvent::MapValueToStack), + EVENT_MAP_VALUE_TO_STACK_N => Some(SystemEvent::MapValueToStackN), + EVENT_U64_DIV => Some(SystemEvent::U64Div), + EVENT_EXT2_INV => Some(SystemEvent::Ext2Inv), + EVENT_EXT2_INTT => Some(SystemEvent::Ext2Intt), + EVENT_SMT_PEEK => Some(SystemEvent::SmtPeek), + EVENT_U32_CLZ => Some(SystemEvent::U32Clz), + EVENT_U32_CTZ => Some(SystemEvent::U32Ctz), + EVENT_U32_CLO => Some(SystemEvent::U32Clo), + EVENT_U32_CTO => Some(SystemEvent::U32Cto), + EVENT_ILOG2 => Some(SystemEvent::ILog2), + EVENT_MEM_TO_MAP => Some(SystemEvent::MemToMap), + EVENT_HDWORD_TO_MAP => Some(SystemEvent::HdwordToMap), + EVENT_HDWORD_TO_MAP_WITH_DOMAIN => Some(SystemEvent::HdwordToMapWithDomain), + EVENT_HPERM_TO_MAP => Some(SystemEvent::HpermToMap), + EVENT_FALCON_SIG_TO_STACK => Some(SystemEvent::FalconSigToStack), _ => None, } } } -impl crate::prettier::PrettyPrint for AdviceInjector { +impl crate::prettier::PrettyPrint for SystemEvent { fn render(&self) -> crate::prettier::Document { crate::prettier::display(self) } } -impl fmt::Display for AdviceInjector { +impl fmt::Display for SystemEvent { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::MerkleNodeMerge => write!(f, "merkle_node_merge"), diff --git a/docs/src/user_docs/assembly/code_organization.md b/docs/src/user_docs/assembly/code_organization.md index 4fabdd2d7..19db52043 100644 --- a/docs/src/user_docs/assembly/code_organization.md +++ b/docs/src/user_docs/assembly/code_organization.md @@ -43,8 +43,6 @@ begin end ``` -Finally, a procedure cannot contain *solely* any number of [advice injectors](./io_operations.md#nondeterministic-inputs), `emit`, `debug` and `trace` instructions. In other words, it must contain at least one instruction which is not in the aforementioned list. - #### Dynamic procedure invocation It is also possible to invoke procedures dynamically - i.e., without specifying target procedure labels at compile time. A procedure can only call itself using dynamic invocation. There are two instructions, `dynexec` and `dyncall`, which can be used to execute dynamically-specified code targets. Both instructions expect the [MAST root](../../design/programs.md) of the target to be stored in memory, and the memory address of the MAST root to be on the top of the stack. The difference between `dynexec` and `dyncall` corresponds to the difference between `exec` and `call`, see the documentation on [procedure invocation semantics](./execution_contexts.md#procedure-invocation-semantics) for more details. diff --git a/docs/src/user_docs/assembly/io_operations.md b/docs/src/user_docs/assembly/io_operations.md index 23e31bd18..78501df52 100644 --- a/docs/src/user_docs/assembly/io_operations.md +++ b/docs/src/user_docs/assembly/io_operations.md @@ -3,7 +3,7 @@ Miden assembly provides a set of instructions for moving data between the operan * **Program code**: values to be moved onto the operand stack can be hard-coded in a program's source code. * **Environment**: values can be moved onto the operand stack from environment variables. These include current clock cycle, current stack depth, and a few others. -* **Advice provider**: values can be moved onto the operand stack from the advice provider by popping them from the advice stack (see more about the advice provider [here](../../intro/overview.md#nondeterministic-inputs)). The VM can also inject new data into the advice provider via *advice injector* instructions. +* **Advice provider**: values can be moved onto the operand stack from the advice provider by popping them from the advice stack (see more about the advice provider [here](../../intro/overview.md#nondeterministic-inputs)). The VM can also inject new data into the advice provider via *system event* instructions. * **Memory**: values can be moved between the stack and random-access memory. The memory is word-addressable, meaning, four elements are located at each address, and we can read and write elements to/from memory in batches of four. Memory can be accessed via absolute memory references (i.e., via memory addresses) as well as via local procedure references (i.e., local index). The latter approach ensures that a procedure does not access locals of another procedure. ### Constant inputs @@ -43,9 +43,9 @@ As mentioned above, nondeterministic inputs are provided to the VM via the advic > **Note**: The opcodes above always push data onto the operand stack so that the first element is placed deepest in the stack. For example, if the data on the stack is `a,b,c,d` and you use the opcode `adv_push.4`, the data will be `d,c,b,a` on your stack. This is also the behavior of the other opcodes. -The second category injects new data into the advice provider. These operations are called *advice injectors* and they affect only the advice provider state. That is, the state of all other VM components (e.g., stack, memory) are unaffected. Executing advice injectors does not consume any VM cycles (i.e., these instructions are executed in $0$ cycles). +The second category injects new data into the advice provider. These operations are called *system events* and they affect only the advice provider state. That is, the state of all other VM components (e.g., stack, memory) are unaffected. Handling system events does not consume any VM cycles (i.e., these instructions are executed in $0$ cycles). -Advice injectors fall into two categories: (1) injectors which push new data onto the advice stack, and (2) injectors which insert new data into the advice map. +System events fall into two categories: (1) events which push new data onto the advice stack, and (2) events which insert new data into the advice map. | Instruction | Stack_input | Stack_output | Notes | | -------------------------------------------- | -------------------------- | -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | diff --git a/processor/src/host/advice/inputs.rs b/processor/src/host/advice/inputs.rs index 2941d4a9d..8cd83bb15 100644 --- a/processor/src/host/advice/inputs.rs +++ b/processor/src/host/advice/inputs.rs @@ -1,13 +1,15 @@ use alloc::vec::Vec; use vm_core::{ - crypto::hash::RpoDigest, + crypto::{ + hash::RpoDigest, + merkle::{InnerNodeInfo, MerkleStore}, + }, + errors::InputError, utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable}, - AdviceMap, + AdviceMap, Felt, }; -use super::{Felt, InnerNodeInfo, InputError, MerkleStore}; - // ADVICE INPUTS // ================================================================================================ diff --git a/processor/src/host/advice/mod.rs b/processor/src/host/advice/mod.rs index c3ed79db6..31e8816ff 100644 --- a/processor/src/host/advice/mod.rs +++ b/processor/src/host/advice/mod.rs @@ -1,20 +1,11 @@ use alloc::vec::Vec; use vm_core::{ - crypto::{ - hash::{Rpo256, RpoDigest}, - merkle::{ - EmptySubtreeRoots, InnerNodeInfo, MerklePath, MerkleStore, NodeIndex, Smt, StoreNode, - SMT_DEPTH, - }, - }, - FieldElement, SignatureKind, EMPTY_WORD, WORD_SIZE, ZERO, + crypto::{hash::RpoDigest, merkle::MerklePath}, + Felt, }; -use winter_prover::math::fft; -use crate::{ExecutionError, Ext2InttError, Felt, InputError, ProcessState, QuadFelt, Word}; - -mod dsa; +use crate::{ExecutionError, ProcessState, Word}; mod inputs; pub use inputs::AdviceInputs; @@ -172,673 +163,3 @@ pub trait AdviceProvider: Sized { /// this advice provider. fn merge_roots(&mut self, lhs: Word, rhs: Word) -> Result; } - -// NATIVE EVENTS -// =============================================================================================== - -/// 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 fn insert_mem_values_into_adv_map( - advice_provider: &mut impl AdviceProvider, - process: ProcessState, -) -> 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); - advice_provider.insert_into_map(key, values); - - Ok(()) -} - -/// 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 fn insert_hdword_into_adv_map( - advice_provider: &mut impl AdviceProvider, - process: ProcessState, - domain: Felt, -) -> 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); - advice_provider.insert_into_map(key.into(), values); - - Ok(()) -} - -/// 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 fn insert_hperm_into_adv_map( - advice_provider: &mut impl AdviceProvider, - 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"), - ); - - advice_provider.insert_into_map(key.into(), values); - - Ok(()) -} - -/// 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 fn merge_merkle_nodes( - advice_provider: &mut impl AdviceProvider, - 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 - advice_provider.merge_roots(lhs, rhs)?; - - Ok(()) -} - -/// 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 fn copy_merkle_node_to_adv_stack( - advice_provider: &mut impl AdviceProvider, - process: ProcessState, -) -> 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 = advice_provider.get_tree_node(root, &depth, &index)?; - - 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(()) -} - -/// 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 fn copy_map_value_to_adv_stack( - advice_provider: &mut impl AdviceProvider, - process: ProcessState, - include_len: bool, -) -> Result<(), ExecutionError> { - let key = [ - process.get_stack_item(3), - process.get_stack_item(2), - process.get_stack_item(1), - process.get_stack_item(0), - ]; - advice_provider.push_stack(AdviceSource::Map { key, include_len })?; - - Ok(()) -} - -/// 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 fn push_u64_div_result( - advice_provider: &mut impl AdviceProvider, - 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); - - 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(()) -} - -/// 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 fn push_ext2_inv_result( - advice_provider: &mut impl AdviceProvider, - 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(); - - advice_provider.push_stack(AdviceSource::Value(result[1]))?; - advice_provider.push_stack(AdviceSource::Value(result[0]))?; - - Ok(()) -} - -/// 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 fn push_ext2_intt_result( - advice_provider: &mut impl AdviceProvider, - 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() { - advice_provider.push_stack(AdviceSource::Value(*element))?; - } - - Ok(()) -} - -/// 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 fn push_signature( - advice_provider: &mut impl AdviceProvider, - process: ProcessState, - kind: SignatureKind, -) -> Result<(), ExecutionError> { - let pub_key = process.get_stack_word(0); - let msg = process.get_stack_word(1); - let result: Vec = get_signature(advice_provider, kind, pub_key, msg)?; - for r in result { - advice_provider.push_stack(AdviceSource::Value(r))?; - } - Ok(()) -} - -/// 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 fn push_leading_zeros( - advice_provider: &mut impl AdviceProvider, - process: ProcessState, -) -> Result<(), ExecutionError> { - 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 fn push_trailing_zeros( - advice_provider: &mut impl AdviceProvider, - process: ProcessState, -) -> Result<(), ExecutionError> { - 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 fn push_leading_ones( - advice_provider: &mut impl AdviceProvider, - process: ProcessState, -) -> Result<(), ExecutionError> { - 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 fn push_trailing_ones( - advice_provider: &mut impl AdviceProvider, - process: ProcessState, -) -> Result<(), ExecutionError> { - 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 fn push_ilog2( - advice_provider: &mut impl AdviceProvider, - 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()); - advice_provider.push_stack(AdviceSource::Value(ilog2))?; - Ok(()) -} - -/// 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. -/// -/// # Panics -/// Will panic as unimplemented if the target depth is `64`. -pub fn push_smtpeek_result( - advice_provider: &mut impl AdviceProvider, - 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 = 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(()); - } - } - - // 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(()) -} - -/// Returns a signature on a message using a public key. -pub fn get_signature( - advice_provider: &impl AdviceProvider, - kind: SignatureKind, - pub_key: Word, - msg: Word, -) -> Result, ExecutionError> { - let pk_sk = advice_provider - .get_mapped_values(&pub_key.into()) - .ok_or(ExecutionError::AdviceMapKeyNotFound(pub_key))?; - - match kind { - SignatureKind::RpoFalcon512 => dsa::falcon_sign(pk_sk, msg), - } -} - -// 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 9a1717c9e..6edcb85ad 100644 --- a/processor/src/host/advice/providers.rs +++ b/processor/src/host/advice/providers.rs @@ -1,8 +1,9 @@ use alloc::{collections::BTreeMap, vec::Vec}; +use vm_core::crypto::merkle::{MerkleStore, NodeIndex, StoreNode}; + use super::{ - AdviceInputs, AdviceProvider, AdviceSource, ExecutionError, Felt, MerklePath, MerkleStore, - NodeIndex, RpoDigest, StoreNode, Word, + AdviceInputs, AdviceProvider, AdviceSource, ExecutionError, Felt, MerklePath, RpoDigest, Word, }; use crate::{ utils::collections::{KvMap, RecordingMap}, diff --git a/processor/src/lib.rs b/processor/src/lib.rs index 76a07dd5c..cf0a80bc9 100644 --- a/processor/src/lib.rs +++ b/processor/src/lib.rs @@ -18,9 +18,10 @@ pub use vm_core::{ crypto::merkle::SMT_DEPTH, errors::InputError, mast::{MastForest, MastNode, MastNodeId}, + sys_events::SystemEvent, utils::{collections::KvMap, DeserializationError}, - AdviceInjector, AssemblyOp, Felt, Kernel, Operation, Program, ProgramInfo, QuadExtension, - StackInputs, StackOutputs, Word, EMPTY_WORD, ONE, ZERO, + AssemblyOp, Felt, Kernel, Operation, Program, ProgramInfo, QuadExtension, StackInputs, + StackOutputs, Word, EMPTY_WORD, ONE, ZERO, }; use vm_core::{ mast::{ diff --git a/processor/src/host/advice/dsa.rs b/processor/src/operations/sys_ops/dsa.rs similarity index 94% rename from processor/src/host/advice/dsa.rs rename to processor/src/operations/sys_ops/dsa.rs index 0311999c1..5c7fa1c81 100644 --- a/processor/src/host/advice/dsa.rs +++ b/processor/src/operations/sys_ops/dsa.rs @@ -1,6 +1,12 @@ use alloc::vec::Vec; -use super::{ExecutionError, Felt, Word}; +use vm_core::{ + crypto::dsa::rpo_falcon512::{Polynomial, SecretKey}, + utils::Deserializable, + Felt, Word, +}; + +use crate::ExecutionError; /// 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. @@ -18,11 +24,6 @@ use super::{ExecutionError, Felt, Word}; /// - The signature generation failed. #[cfg(feature = "std")] pub fn falcon_sign(sk: &[Felt], msg: Word) -> Result, ExecutionError> { - use vm_core::{ - crypto::dsa::rpo_falcon512::{Polynomial, SecretKey}, - utils::Deserializable, - }; - // Create the corresponding secret key let mut sk_bytes = Vec::with_capacity(sk.len()); for element in sk { diff --git a/processor/src/operations/sys_ops.rs b/processor/src/operations/sys_ops/mod.rs similarity index 77% rename from processor/src/operations/sys_ops.rs rename to processor/src/operations/sys_ops/mod.rs index ac3a11976..d29a23cb5 100644 --- a/processor/src/operations/sys_ops.rs +++ b/processor/src/operations/sys_ops/mod.rs @@ -1,4 +1,4 @@ -use vm_core::{AdviceInjector, Felt, Operation, SignatureKind, ZERO}; +use vm_core::{sys_events::SystemEvent, Felt, Operation}; use super::{ super::{ @@ -7,16 +7,9 @@ use super::{ }, ExecutionError, Process, }; -use crate::{ - host::advice::{ - copy_map_value_to_adv_stack, copy_merkle_node_to_adv_stack, insert_hdword_into_adv_map, - insert_hperm_into_adv_map, insert_mem_values_into_adv_map, merge_merkle_nodes, - push_ext2_intt_result, push_ext2_inv_result, push_ilog2, push_leading_ones, - push_leading_zeros, push_signature, push_smtpeek_result, push_trailing_ones, - push_trailing_zeros, push_u64_div_result, - }, - Host, ProcessState, -}; +use crate::Host; +mod dsa; +mod sys_event_handlers; // SYSTEM OPERATIONS // ================================================================================================ @@ -132,58 +125,13 @@ impl Process { self.stack.copy_state(0); self.decoder.set_user_op_helpers(Operation::Emit(event_id), &[event_id.into()]); - // If it's a native event, handle it directly. Otherwise, forward it to the host. - if let Some(advice_injector) = AdviceInjector::from_event_id(event_id) { - self.handle_advice_injector(advice_injector, host) + // If it's a system event, handle it directly. Otherwise, forward it to the host. + if let Some(system_event) = SystemEvent::from_event_id(event_id) { + self.handle_sytem_event(system_event, host) } else { host.on_event(self.into(), event_id) } } - - fn handle_advice_injector( - &self, - advice_injector: AdviceInjector, - host: &mut impl Host, - ) -> Result<(), ExecutionError> { - let advice_provider = host.advice_provider_mut(); - let process_state: ProcessState = self.into(); - match advice_injector { - AdviceInjector::MerkleNodeMerge => merge_merkle_nodes(advice_provider, process_state), - AdviceInjector::MerkleNodeToStack => { - copy_merkle_node_to_adv_stack(advice_provider, process_state) - }, - AdviceInjector::MapValueToStack => { - copy_map_value_to_adv_stack(advice_provider, process_state, false) - }, - AdviceInjector::MapValueToStackN => { - copy_map_value_to_adv_stack(advice_provider, process_state, true) - }, - AdviceInjector::U64Div => push_u64_div_result(advice_provider, process_state), - AdviceInjector::Ext2Inv => push_ext2_inv_result(advice_provider, process_state), - AdviceInjector::Ext2Intt => push_ext2_intt_result(advice_provider, process_state), - AdviceInjector::SmtPeek => push_smtpeek_result(advice_provider, process_state), - AdviceInjector::U32Clz => push_leading_zeros(advice_provider, process_state), - AdviceInjector::U32Ctz => push_trailing_zeros(advice_provider, process_state), - AdviceInjector::U32Clo => push_leading_ones(advice_provider, process_state), - AdviceInjector::U32Cto => push_trailing_ones(advice_provider, process_state), - AdviceInjector::ILog2 => push_ilog2(advice_provider, process_state), - - AdviceInjector::MemToMap => { - insert_mem_values_into_adv_map(advice_provider, process_state) - }, - AdviceInjector::HdwordToMap => { - insert_hdword_into_adv_map(advice_provider, process_state, ZERO) - }, - AdviceInjector::HdwordToMapWithDomain => { - let domain = self.stack.get(8); - insert_hdword_into_adv_map(advice_provider, process_state, domain) - }, - AdviceInjector::HpermToMap => insert_hperm_into_adv_map(advice_provider, process_state), - AdviceInjector::FalconSigToStack => { - push_signature(advice_provider, process_state, SignatureKind::RpoFalcon512) - }, - } - } } // TESTS diff --git a/processor/src/operations/sys_ops/sys_event_handlers.rs b/processor/src/operations/sys_ops/sys_event_handlers.rs new file mode 100644 index 000000000..13e4a796b --- /dev/null +++ b/processor/src/operations/sys_ops/sys_event_handlers.rs @@ -0,0 +1,732 @@ +use alloc::vec::Vec; + +use vm_core::{ + crypto::{ + hash::{Rpo256, RpoDigest}, + merkle::{EmptySubtreeRoots, Smt, SMT_DEPTH}, + }, + sys_events::SystemEvent, + Felt, FieldElement, SignatureKind, Word, EMPTY_WORD, WORD_SIZE, ZERO, +}; +use winter_prover::math::fft; + +use super::dsa; +use crate::{ + AdviceProvider, AdviceSource, ExecutionError, Ext2InttError, Host, Process, ProcessState, + QuadFelt, +}; + +/// The offset of the domain value on the stack in the `hdword_to_map_with_domain` system event. +const HDWORD_TO_MAP_WITH_DOMAIN_DOMAIN_OFFSET: usize = 8; + +impl Process { + pub(super) fn handle_sytem_event( + &self, + system_event: SystemEvent, + host: &mut impl Host, + ) -> Result<(), ExecutionError> { + let advice_provider = host.advice_provider_mut(); + let process_state: ProcessState = self.into(); + match system_event { + SystemEvent::MerkleNodeMerge => merge_merkle_nodes(advice_provider, process_state), + SystemEvent::MerkleNodeToStack => { + copy_merkle_node_to_adv_stack(advice_provider, process_state) + }, + SystemEvent::MapValueToStack => { + copy_map_value_to_adv_stack(advice_provider, process_state, false) + }, + SystemEvent::MapValueToStackN => { + copy_map_value_to_adv_stack(advice_provider, process_state, true) + }, + SystemEvent::U64Div => push_u64_div_result(advice_provider, process_state), + SystemEvent::Ext2Inv => push_ext2_inv_result(advice_provider, process_state), + SystemEvent::Ext2Intt => push_ext2_intt_result(advice_provider, process_state), + SystemEvent::SmtPeek => push_smtpeek_result(advice_provider, process_state), + SystemEvent::U32Clz => push_leading_zeros(advice_provider, process_state), + SystemEvent::U32Ctz => push_trailing_zeros(advice_provider, process_state), + SystemEvent::U32Clo => push_leading_ones(advice_provider, process_state), + SystemEvent::U32Cto => push_trailing_ones(advice_provider, process_state), + SystemEvent::ILog2 => push_ilog2(advice_provider, process_state), + + SystemEvent::MemToMap => insert_mem_values_into_adv_map(advice_provider, process_state), + SystemEvent::HdwordToMap => { + insert_hdword_into_adv_map(advice_provider, process_state, ZERO) + }, + SystemEvent::HdwordToMapWithDomain => { + let domain = self.stack.get(HDWORD_TO_MAP_WITH_DOMAIN_DOMAIN_OFFSET); + insert_hdword_into_adv_map(advice_provider, process_state, domain) + }, + SystemEvent::HpermToMap => insert_hperm_into_adv_map(advice_provider, process_state), + SystemEvent::FalconSigToStack => { + push_signature(advice_provider, process_state, SignatureKind::RpoFalcon512) + }, + } + } +} + +/// 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 fn insert_mem_values_into_adv_map( + advice_provider: &mut impl AdviceProvider, + process: ProcessState, +) -> 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); + advice_provider.insert_into_map(key, values); + + Ok(()) +} + +/// 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 fn insert_hdword_into_adv_map( + advice_provider: &mut impl AdviceProvider, + process: ProcessState, + domain: Felt, +) -> 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); + advice_provider.insert_into_map(key.into(), values); + + Ok(()) +} + +/// 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 fn insert_hperm_into_adv_map( + advice_provider: &mut impl AdviceProvider, + 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"), + ); + + advice_provider.insert_into_map(key.into(), values); + + Ok(()) +} + +/// 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 fn merge_merkle_nodes( + advice_provider: &mut impl AdviceProvider, + 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 + advice_provider.merge_roots(lhs, rhs)?; + + Ok(()) +} + +/// 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 fn copy_merkle_node_to_adv_stack( + advice_provider: &mut impl AdviceProvider, + process: ProcessState, +) -> 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 = advice_provider.get_tree_node(root, &depth, &index)?; + + 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(()) +} + +/// 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 fn copy_map_value_to_adv_stack( + advice_provider: &mut impl AdviceProvider, + process: ProcessState, + include_len: bool, +) -> Result<(), ExecutionError> { + let key = [ + process.get_stack_item(3), + process.get_stack_item(2), + process.get_stack_item(1), + process.get_stack_item(0), + ]; + advice_provider.push_stack(AdviceSource::Map { key, include_len })?; + + Ok(()) +} + +/// 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 fn push_u64_div_result( + advice_provider: &mut impl AdviceProvider, + 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); + + 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(()) +} + +/// 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 fn push_ext2_inv_result( + advice_provider: &mut impl AdviceProvider, + 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(); + + advice_provider.push_stack(AdviceSource::Value(result[1]))?; + advice_provider.push_stack(AdviceSource::Value(result[0]))?; + + Ok(()) +} + +/// 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 fn push_ext2_intt_result( + advice_provider: &mut impl AdviceProvider, + 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() { + advice_provider.push_stack(AdviceSource::Value(*element))?; + } + + Ok(()) +} + +/// 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 fn push_signature( + advice_provider: &mut impl AdviceProvider, + process: ProcessState, + kind: SignatureKind, +) -> Result<(), ExecutionError> { + let pub_key = process.get_stack_word(0); + let msg = process.get_stack_word(1); + let result: Vec = get_signature(advice_provider, kind, pub_key, msg)?; + for r in result { + advice_provider.push_stack(AdviceSource::Value(r))?; + } + Ok(()) +} + +/// 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 fn push_leading_zeros( + advice_provider: &mut impl AdviceProvider, + process: ProcessState, +) -> Result<(), ExecutionError> { + 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 fn push_trailing_zeros( + advice_provider: &mut impl AdviceProvider, + process: ProcessState, +) -> Result<(), ExecutionError> { + 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 fn push_leading_ones( + advice_provider: &mut impl AdviceProvider, + process: ProcessState, +) -> Result<(), ExecutionError> { + 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 fn push_trailing_ones( + advice_provider: &mut impl AdviceProvider, + process: ProcessState, +) -> Result<(), ExecutionError> { + 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 fn push_ilog2( + advice_provider: &mut impl AdviceProvider, + 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()); + advice_provider.push_stack(AdviceSource::Value(ilog2))?; + Ok(()) +} + +/// 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. +/// +/// # Panics +/// Will panic as unimplemented if the target depth is `64`. +pub fn push_smtpeek_result( + advice_provider: &mut impl AdviceProvider, + 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 = 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(()); + } + } + + // 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(()) +} + +/// Returns a signature on a message using a public key. +pub fn get_signature( + advice_provider: &impl AdviceProvider, + kind: SignatureKind, + pub_key: Word, + msg: Word, +) -> Result, ExecutionError> { + let pk_sk = advice_provider + .get_mapped_values(&pub_key.into()) + .ok_or(ExecutionError::AdviceMapKeyNotFound(pub_key))?; + + match kind { + SignatureKind::RpoFalcon512 => dsa::falcon_sign(pk_sk, msg), + } +} + +// 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()) +}