zkaluvm/core/microcode.rs
1// AluVM ISA extension for Galois fields
2//
3// SPDX-License-Identifier: Apache-2.0
4//
5// Designed in 2024-2025 by Dr Maxim Orlovsky <orlovsky@ubideco.org>
6// Written in 2024-2025 by Dr Maxim Orlovsky <orlovsky@ubideco.org>
7//
8// Copyright (C) 2024-2025 Laboratories for Ubiquitous Deterministic Computing (UBIDECO),
9// Institute for Distributed and Cognitive Systems (InDCS), Switzerland.
10// Copyright (C) 2024-2025 Dr Maxim Orlovsky.
11// All rights under the above copyrights are reserved.
12//
13// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
14// in compliance with the License. You may obtain a copy of the License at
15//
16// http://www.apache.org/licenses/LICENSE-2.0
17//
18// Unless required by applicable law or agreed to in writing, software distributed under the License
19// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
20// or implied. See the License for the specific language governing permissions and limitations under
21// the License.
22
23use aluvm::regs::Status;
24use aluvm::CoreExt;
25use amplify::num::{u256, u512};
26
27use crate::gfa::Bits;
28use crate::{fe256, GfaCore, RegE};
29
30/// Microcode for finite field arithmetics.
31impl GfaCore {
32 /// Get value of the field order register (`FQ`).
33 pub fn fq(&self) -> u256 { self.fq }
34
35 /// Test whether the register has a value, returning a status.
36 ///
37 /// # Register modification
38 ///
39 /// No registers are modified, including `CK` and `CO`.
40 pub fn test(&self, src: RegE) -> Status {
41 if self.get(src).is_some() {
42 Status::Ok
43 } else {
44 Status::Fail
45 }
46 }
47
48 /// Check whether a register value fits the provided number of bits.
49 ///
50 /// # Returns
51 ///
52 /// `None`, if the register contains no value. Otherwise, a boolean value indicating the test
53 /// result.
54 ///
55 /// # Register modification
56 ///
57 /// No registers are modified, including `CK` and `CO`.
58 pub fn fits(&self, src: RegE, bits: Bits) -> Option<bool> {
59 let order = self.fq();
60 let a = self.get(src)?;
61 debug_assert!(a.to_u256() < order);
62 let check = a.to_u256() >> bits.bit_len();
63 Some(check == u256::ZERO)
64 }
65
66 /// Move a value from the `src` to `dst` register.
67 ///
68 /// The value of the `src` register is not changed.
69 ///
70 /// If the `src` register does not have a value, sets `dst` to `None`, clearing any previous
71 /// value in it.
72 pub fn mov(&mut self, dst: RegE, src: RegE) {
73 match self.get(src) {
74 Some(val) => {
75 self.set(dst, val);
76 }
77 None => {
78 self.clr(dst);
79 }
80 }
81 }
82
83 /// Checks the equivalence of values in `src1` and `src2`.
84 ///
85 /// If both registers do not have a value, returns [`Status::Fail`].
86 pub fn eqv(&mut self, src1: RegE, src2: RegE) -> Status {
87 let a = self.get(src1);
88 let b = self.get(src2);
89 if a == b && a.is_some() {
90 Status::Ok
91 } else {
92 Status::Fail
93 }
94 }
95
96 /// Add a value from the `src` register to `dst_src` value, storing the result back in
97 /// `dst_src`.
98 ///
99 /// Overflow is handled according to finite field arithmetics, by doing a modulo-division. The
100 /// fact of the overflow cannot be determined in order to keep the implementation compatible
101 /// with zk-STARK and zk-SNARK circuits and arithmetizations.
102 ///
103 /// # Returns
104 ///
105 /// If any of `src` or `dst_src` registers do not have a value, returns [`Status::Fail`].
106 /// Otherwise, returns success.
107 #[inline]
108 pub fn add_mod(&mut self, dst_src: RegE, src: RegE) -> Status {
109 let order = self.fq();
110
111 let Some(a) = self.get(dst_src) else {
112 return Status::Fail;
113 };
114 let Some(b) = self.get(src) else {
115 return Status::Fail;
116 };
117
118 let a = a.to_u256();
119 let b = b.to_u256();
120 debug_assert!(a < order && b < order);
121
122 let (mut res, overflow) = a.overflowing_add(b);
123 if overflow {
124 res += u256::MAX - order;
125 }
126
127 let res = res % order;
128 self.set(dst_src, fe256::from(res));
129 Status::Ok
130 }
131
132 /// Multiply a value from the `src` register to `dst_src` value, storing the result back in
133 /// `dst_src`.
134 ///
135 /// Overflow is handled according to finite field arithmetics, by doing a modulo-division. The
136 /// fact of the overflow cannot be determined in order to keep the implementation compatible
137 /// with zk-STARK and zk-SNARK circuits and arithmetizations.
138 ///
139 /// # Returns
140 ///
141 /// If any of `src` or `dst_src` registers do not have a value, returns [`Status::Fail`].
142 /// Otherwise, returns success.
143 #[inline]
144 pub fn mul_mod(&mut self, dst_src: RegE, src: RegE) -> Status {
145 let order = self.fq();
146
147 let Some(a) = self.get(dst_src) else {
148 return Status::Fail;
149 };
150 let Some(b) = self.get(src) else {
151 return Status::Fail;
152 };
153
154 let a = a.to_u256();
155 let b = b.to_u256();
156 debug_assert!(a < order && b < order);
157
158 let (res, _) = mul_mod_int(order, a, b);
159
160 let res = res % order;
161 self.set(dst_src, fe256::from(res));
162 Status::Ok
163 }
164
165 /// Negate a value in the `dst_src` register by subtracting it from the field order, stored in
166 /// `FQ` register.
167 ///
168 /// # Returns
169 ///
170 /// If the `dst_src` register does not have a value, returns [`Status::Fail`].
171 /// Otherwise, returns success.
172 #[inline]
173 pub fn neg_mod(&mut self, dst_src: RegE, src: RegE) -> Status {
174 let order = self.fq();
175
176 let Some(a) = self.get(src) else {
177 return Status::Fail;
178 };
179
180 debug_assert!(a.to_u256() < order);
181
182 let res = order - a.to_u256();
183 self.set(dst_src, fe256::from(res));
184 Status::Ok
185 }
186}
187
188fn mul_mod_int(order: u256, a: u256, b: u256) -> (u256, bool) {
189 let a = u512::from(a);
190 let b = u512::from(b);
191 let c = a * b;
192 let o = u512::from(order);
193 let res = u256::from_le_slice(&(c % o).to_le_bytes()[..32]).expect("");
194 (res, c >= o)
195}