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}