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 the integer value of `bytes` is greater than or equal to the modulus.
156    fn from_le_bytes(bytes: &[u8]) -> Option<Self>;
157
158    /// Creates a new IntMod from an array of bytes, big endian.
159    /// Returns `None` if the integer value of `bytes` is greater than or equal to the modulus.
160    fn from_be_bytes(bytes: &[u8]) -> Option<Self>;
161
162    /// Creates a new IntMod from an array of bytes, little endian.
163    /// Does not enforce the integer value of `bytes` must be less than the modulus.
164    fn from_le_bytes_unchecked(bytes: &[u8]) -> Self;
165
166    /// Creates a new IntMod from an array of bytes, big endian.
167    /// Does not enforce the integer value of `bytes` must be less than the modulus.
168    fn from_be_bytes_unchecked(bytes: &[u8]) -> Self;
169
170    /// Creates a new IntMod from a u8.
171    /// Does not enforce the integer value of `bytes` must be less than the modulus.
172    fn from_u8(val: u8) -> Self;
173
174    /// Creates a new IntMod from a u32.
175    /// Does not enforce the integer value of `bytes` must be less than the modulus.
176    fn from_u32(val: u32) -> Self;
177
178    /// Creates a new IntMod from a u64.
179    /// Does not enforce the integer value of `bytes` must be less than the modulus.
180    fn from_u64(val: u64) -> Self;
181
182    /// Value of this IntMod as an array of bytes, little endian.
183    fn as_le_bytes(&self) -> &[u8];
184
185    /// Value of this IntMod as an array of bytes, big endian.
186    fn to_be_bytes(&self) -> Self::Repr;
187
188    /// Modulus N as a BigUint.
189    #[cfg(not(target_os = "zkvm"))]
190    fn modulus_biguint() -> BigUint;
191
192    /// Creates a new IntMod from a BigUint.
193    #[cfg(not(target_os = "zkvm"))]
194    fn from_biguint(biguint: BigUint) -> Self;
195
196    /// Value of this IntMod as a BigUint.
197    #[cfg(not(target_os = "zkvm"))]
198    fn as_biguint(&self) -> BigUint;
199
200    fn neg_assign(&mut self);
201
202    /// Doubles `self` in-place.
203    fn double_assign(&mut self);
204
205    /// Doubles this IntMod.
206    fn double(&self) -> Self {
207        let mut ret = self.clone();
208        ret += self;
209        ret
210    }
211
212    /// Squares `self` in-place.
213    fn square_assign(&mut self);
214
215    /// Squares this IntMod.
216    fn square(&self) -> Self {
217        let mut ret = self.clone();
218        ret *= self;
219        ret
220    }
221
222    /// Cubes this IntMod.
223    fn cube(&self) -> Self {
224        let mut ret = self.square();
225        ret *= self;
226        ret
227    }
228
229    /// VM specific concept: during guest execution, it is not enforced that the representation
230    /// of `Self` must be the unique integer less than the modulus. The guest code may sometimes
231    /// want to enforce that the representation is the canonical one less than the modulus.
232    /// the host to an honest host to provide the canonical representation less than the modulus.
233    ///
234    /// This function should enforce that guest execution proceeds **if and only if** `self`
235    /// is in the unique representation less than the modulus.
236    fn assert_reduced(&self);
237
238    /// Is the integer representation of `self` less than the modulus?
239    fn is_reduced(&self) -> bool;
240
241    /// Calls any setup required for this modulus. The implementation should internally use
242    /// `OnceBool` to ensure that setup is only called once.
243    fn set_up_once();
244
245    /// Returns whether the two integers are congrument modulo the modulus.
246    ///
247    /// # Safety
248    /// - If `CHECK_SETUP` is true, checks if setup has been called for this curve and if not, calls
249    ///   `Self::set_up_once()`. Only set `CHECK_SETUP` to `false` if you are sure that setup has
250    ///   been called already.
251    unsafe fn eq_impl<const CHECK_SETUP: bool>(&self, other: &Self) -> bool;
252
253    /// Add two elements.
254    ///
255    /// # Safety
256    /// - If `CHECK_SETUP` is true, checks if setup has been called for this curve and if not, calls
257    ///   `Self::set_up_once()`. Only set `CHECK_SETUP` to `false` if you are sure that setup has
258    ///   been called already.
259    unsafe fn add_ref<const CHECK_SETUP: bool>(&self, other: &Self) -> Self;
260}
261
262// Ref: https://docs.rs/elliptic-curve/latest/elliptic_curve/ops/trait.Reduce.html
263pub trait Reduce: Sized {
264    /// Interpret the given bytes as an integer and perform a modular reduction.
265    fn reduce_le_bytes(bytes: &[u8]) -> Self;
266    fn reduce_be_bytes(bytes: &[u8]) -> Self {
267        Self::reduce_le_bytes(&bytes.iter().rev().copied().collect::<Vec<_>>())
268    }
269}
270
271// Note that we use a hint-based approach to prove whether the square root exists.
272// This approach works for prime moduli, but not necessarily for composite moduli,
273// which is why the Sqrt trait requires the Field trait, not just the IntMod trait.
274pub trait Sqrt: Field {
275    /// Returns a square root of `self` if it exists.
276    fn sqrt(&self) -> Option<Self>;
277}