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}