openvm_algebra_guest/
lib.rs

1#![no_std]
2extern crate self as openvm_algebra_guest;
3
4/// This is custom-1 defined in RISC-V spec document
5pub const OPCODE: u8 = 0x2b;
6pub const MODULAR_ARITHMETIC_FUNCT3: u8 = 0b000;
7pub const COMPLEX_EXT_FIELD_FUNCT3: u8 = 0b010;
8
9/// Modular arithmetic is configurable.
10/// The funct7 field equals `mod_idx * MODULAR_ARITHMETIC_MAX_KINDS + base_funct7`.
11#[derive(Debug, Copy, Clone, PartialEq, Eq, FromRepr)]
12#[repr(u8)]
13pub enum ModArithBaseFunct7 {
14    AddMod = 0,
15    SubMod,
16    MulMod,
17    DivMod,
18    IsEqMod,
19    SetupMod,
20    HintNonQr,
21    HintSqrt,
22}
23
24impl ModArithBaseFunct7 {
25    pub const MODULAR_ARITHMETIC_MAX_KINDS: u8 = 8;
26}
27
28/// Complex extension field is configurable.
29/// The funct7 field equals `fp2_idx * COMPLEX_EXT_FIELD_MAX_KINDS + base_funct7`.
30#[derive(Debug, Copy, Clone, PartialEq, Eq, FromRepr)]
31#[repr(u8)]
32pub enum ComplexExtFieldBaseFunct7 {
33    Add = 0,
34    Sub,
35    Mul,
36    Div,
37    Setup,
38}
39
40impl ComplexExtFieldBaseFunct7 {
41    pub const COMPLEX_EXT_FIELD_MAX_KINDS: u8 = 8;
42}
43
44/// Modular arithmetic traits for use with OpenVM intrinsics.
45extern crate alloc;
46
47use alloc::vec::Vec;
48use core::{
49    fmt::Debug,
50    iter::{Product, Sum},
51    ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign},
52};
53
54pub use field::Field;
55#[cfg(not(target_os = "zkvm"))]
56use num_bigint::BigUint;
57pub use openvm_algebra_complex_macros as complex_macros;
58pub use openvm_algebra_moduli_macros as moduli_macros;
59#[cfg(target_os = "zkvm")]
60pub use openvm_custom_insn;
61#[cfg(target_os = "zkvm")]
62pub use openvm_rv32im_guest;
63pub use serde_big_array::BigArray;
64use strum_macros::FromRepr;
65
66/// Implementation of this library's traits on halo2curves types.
67/// Used for testing and also VM runtime execution.
68/// These should **only** be importable on a host machine.
69#[cfg(all(not(target_os = "zkvm"), feature = "halo2curves"))]
70mod halo2curves;
71
72/// Exponentiation by bytes
73mod exp_bytes;
74/// Field traits
75pub mod field;
76pub use exp_bytes::*;
77pub use once_cell;
78
79/// Division operation that is undefined behavior when the denominator is not invertible.
80pub trait DivUnsafe<Rhs = Self>: Sized {
81    /// Output type of `div_unsafe`.
82    type Output;
83
84    /// Undefined behavior when denominator is not invertible.
85    fn div_unsafe(self, other: Rhs) -> Self::Output;
86}
87
88/// Division assignment operation that is undefined behavior when the denominator is not invertible.
89pub trait DivAssignUnsafe<Rhs = Self>: Sized {
90    /// Undefined behavior when denominator is not invertible.
91    fn div_assign_unsafe(&mut self, other: Rhs);
92}
93
94/// Trait definition for OpenVM modular integers, where each operation
95/// is done modulo MODULUS.
96///
97/// Division is only defined over the group of units in the ring of integers modulo MODULUS.
98/// It is undefined behavior outside of this group.
99pub trait IntMod:
100    Sized
101    + Eq
102    + Clone
103    + Debug
104    + Neg<Output = Self>
105    + Add<Output = Self>
106    + Sub<Output = Self>
107    + Mul<Output = Self>
108    + DivUnsafe<Output = Self>
109    + Sum
110    + Product
111    + for<'a> Add<&'a Self, Output = Self>
112    + for<'a> Sub<&'a Self, Output = Self>
113    + for<'a> Mul<&'a Self, Output = Self>
114    + for<'a> DivUnsafe<&'a Self, Output = Self>
115    + for<'a> Sum<&'a Self>
116    + for<'a> Product<&'a Self>
117    + AddAssign
118    + SubAssign
119    + MulAssign
120    + DivAssignUnsafe
121    + for<'a> AddAssign<&'a Self>
122    + for<'a> SubAssign<&'a Self>
123    + for<'a> MulAssign<&'a Self>
124    + for<'a> DivAssignUnsafe<&'a Self>
125{
126    /// Underlying representation of IntMod. Usually of the form `[u8; NUM_LIMBS]`.
127    type Repr: AsRef<[u8]> + AsMut<[u8]>;
128    /// `SelfRef<'a>` should almost always be `&'a Self`. This is a way to include implementations
129    /// of binary operations where both sides are `&'a Self`.
130    type SelfRef<'a>: Add<&'a Self, Output = Self>
131        + Sub<&'a Self, Output = Self>
132        + Neg<Output = Self>
133        + Mul<&'a Self, Output = Self>
134        + DivUnsafe<&'a Self, Output = Self>
135    where
136        Self: 'a;
137
138    /// Modulus as a Repr.
139    const MODULUS: Self::Repr;
140
141    /// Number of limbs used to internally represent an element of `Self`.
142    const NUM_LIMBS: usize;
143
144    /// The zero element (i.e. the additive identity).
145    const ZERO: Self;
146
147    /// The one element (i.e. the multiplicative identity).
148    const ONE: Self;
149
150    /// Creates a new IntMod from an instance of Repr.
151    /// Does not enforce the integer value of `bytes` must be less than the modulus.
152    fn from_repr(repr: Self::Repr) -> Self;
153
154    /// Creates a new IntMod from an array of bytes, little endian.
155    /// Returns `None` if `bytes.len() != NUM_LIMBS` or if the integer value of `bytes` is greater
156    /// than or equal to the modulus.
157    fn from_le_bytes(bytes: &[u8]) -> Option<Self>;
158
159    /// Creates a new IntMod from an array of bytes, big endian.
160    /// Returns `None` if `bytes.len() != NUM_LIMBS` or if the integer value of `bytes` is greater
161    /// than or equal to the modulus.
162    fn from_be_bytes(bytes: &[u8]) -> Option<Self>;
163
164    /// Creates a new IntMod from an array of bytes, little endian.
165    /// Does not enforce the integer value of `bytes` must be less than the modulus.
166    fn from_le_bytes_unchecked(bytes: &[u8]) -> Self;
167
168    /// Creates a new IntMod from an array of bytes, big endian.
169    /// Does not enforce the integer value of `bytes` must be less than the modulus.
170    fn from_be_bytes_unchecked(bytes: &[u8]) -> Self;
171
172    /// Creates a new IntMod from a u8.
173    /// Does not enforce the integer value of `bytes` must be less than the modulus.
174    fn from_u8(val: u8) -> Self;
175
176    /// Creates a new IntMod from a u32.
177    /// Does not enforce the integer value of `bytes` must be less than the modulus.
178    fn from_u32(val: u32) -> Self;
179
180    /// Creates a new IntMod from a u64.
181    /// Does not enforce the integer value of `bytes` must be less than the modulus.
182    fn from_u64(val: u64) -> Self;
183
184    /// Value of this IntMod as an array of bytes, little endian.
185    fn as_le_bytes(&self) -> &[u8];
186
187    /// Value of this IntMod as an array of bytes, big endian.
188    fn to_be_bytes(&self) -> Self::Repr;
189
190    /// Modulus N as a BigUint.
191    #[cfg(not(target_os = "zkvm"))]
192    fn modulus_biguint() -> BigUint;
193
194    /// Creates a new IntMod from a BigUint.
195    #[cfg(not(target_os = "zkvm"))]
196    fn from_biguint(biguint: BigUint) -> Self;
197
198    /// Value of this IntMod as a BigUint.
199    #[cfg(not(target_os = "zkvm"))]
200    fn as_biguint(&self) -> BigUint;
201
202    fn neg_assign(&mut self);
203
204    /// Doubles `self` in-place.
205    fn double_assign(&mut self);
206
207    /// Doubles this IntMod.
208    fn double(&self) -> Self {
209        let mut ret = self.clone();
210        ret += self;
211        ret
212    }
213
214    /// Squares `self` in-place.
215    fn square_assign(&mut self);
216
217    /// Squares this IntMod.
218    fn square(&self) -> Self {
219        let mut ret = self.clone();
220        ret *= self;
221        ret
222    }
223
224    /// Cubes this IntMod.
225    fn cube(&self) -> Self {
226        let mut ret = self.square();
227        ret *= self;
228        ret
229    }
230
231    /// VM specific concept: during guest execution, it is not enforced that the representation
232    /// of `Self` must be the unique integer less than the modulus. The guest code may sometimes
233    /// want to enforce that the representation is the canonical one less than the modulus.
234    /// the host to an honest host to provide the canonical representation less than the modulus.
235    ///
236    /// This function should enforce that guest execution proceeds **if and only if** `self`
237    /// is in the unique representation less than the modulus.
238    fn assert_reduced(&self);
239
240    /// Is the integer representation of `self` less than the modulus?
241    fn is_reduced(&self) -> bool;
242
243    /// Calls any setup required for this modulus. The implementation should internally use
244    /// `OnceBool` to ensure that setup is only called once.
245    fn set_up_once();
246
247    /// Returns whether the two integers are congrument modulo the modulus.
248    ///
249    /// # Safety
250    /// - If `CHECK_SETUP` is true, checks if setup has been called for this curve and if not, calls
251    ///   `Self::set_up_once()`. Only set `CHECK_SETUP` to `false` if you are sure that setup has
252    ///   been called already.
253    unsafe fn eq_impl<const CHECK_SETUP: bool>(&self, other: &Self) -> bool;
254
255    /// Add two elements.
256    ///
257    /// # Safety
258    /// - If `CHECK_SETUP` is true, checks if setup has been called for this curve and if not, calls
259    ///   `Self::set_up_once()`. Only set `CHECK_SETUP` to `false` if you are sure that setup has
260    ///   been called already.
261    unsafe fn add_ref<const CHECK_SETUP: bool>(&self, other: &Self) -> Self;
262}
263
264// Ref: https://docs.rs/elliptic-curve/latest/elliptic_curve/ops/trait.Reduce.html
265pub trait Reduce: Sized {
266    /// Interpret the given bytes as an integer and perform a modular reduction.
267    ///
268    /// **Note:** `bytes.len()` must be a multiple of the modulus byte size.
269    /// Non-aligned lengths will panic.
270    fn reduce_le_bytes(bytes: &[u8]) -> Self;
271    fn reduce_be_bytes(bytes: &[u8]) -> Self {
272        Self::reduce_le_bytes(&bytes.iter().rev().copied().collect::<Vec<_>>())
273    }
274}
275
276// Note that we use a hint-based approach to prove whether the square root exists.
277// This approach works for prime moduli, but not necessarily for composite moduli,
278// which is why the Sqrt trait requires the Field trait, not just the IntMod trait.
279pub trait Sqrt: Field {
280    /// Returns a square root of `self` if it exists.
281    fn sqrt(&self) -> Option<Self>;
282}