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}