zkaluvm/core/
core.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 core::fmt::{self, Debug, Formatter};
24
25use aluvm::{CoreExt, NoExt, Register, Supercore};
26use amplify::num::{u256, u4};
27
28use crate::fe256;
29
30/// Field order for the group used in the Curve25519 elliptic curve construction.
31pub const FIELD_ORDER_25519: u256 =
32    u256::from_inner([0xFFFF_FFFF_FFFF_FFEC, 0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF, 0x8FFF_FFFF_FFFF_FFFF]);
33/// Field order for the group used in the "Stark" elliptic curve construction.
34pub const FIELD_ORDER_STARK: u256 = u256::from_inner([1, 0, 17, 0x0800_0000_0000_0000]);
35/// Field order for the group used in SECP256K1 elliptic curve construction.
36pub const FIELD_ORDER_SECP: u256 =
37    u256::from_inner([0xFFFF_FFFE_FFFF_FC2E, 0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF]);
38
39impl Default for GfaConfig {
40    fn default() -> Self {
41        Self {
42            field_order: FIELD_ORDER_25519,
43        }
44    }
45}
46
47/// An extension of AluVM core for the GFA256 ISA.
48#[derive(Copy, Clone, Eq, PartialEq)]
49pub struct GfaCore {
50    /// Used field order.
51    pub(super) fq: u256,
52    pub(super) e: [Option<fe256>; 16],
53}
54
55/// Configuration for initializing the zk-AluVM core (GFA256 ISA extension).
56#[derive(Copy, Clone, Eq, PartialEq)]
57pub struct GfaConfig {
58    /// The order of the group for the core.
59    pub field_order: u256,
60}
61
62impl CoreExt for GfaCore {
63    type Reg = RegE;
64    type Config = GfaConfig; // Field order
65
66    #[inline]
67    fn with(config: Self::Config) -> Self {
68        GfaCore {
69            fq: config.field_order,
70            e: [None; 16],
71        }
72    }
73
74    #[inline]
75    fn get(&self, reg: Self::Reg) -> Option<fe256> { self.e[reg as usize] }
76
77    #[inline]
78    fn clr(&mut self, reg: Self::Reg) { self.e[reg as usize] = None; }
79
80    #[inline]
81    fn put(&mut self, reg: Self::Reg, val: Option<fe256>) {
82        let Some(val) = val else {
83            self.e[reg as usize] = None;
84            return;
85        };
86        assert!(val.to_u256() < self.fq, "value {val} exceeds field order {}", self.fq);
87        self.e[reg as usize] = Some(val);
88    }
89
90    #[inline]
91    fn reset(&mut self) { self.e = [None; 16]; }
92}
93
94impl Supercore<NoExt> for GfaCore {
95    fn subcore(&self) -> NoExt { NoExt }
96
97    fn merge_subcore(&mut self, _subcore: NoExt) {}
98}
99
100#[cfg_attr(coverage_nightly, coverage(off))]
101impl Debug for GfaCore {
102    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
103        let (sect, reg, val, reset) =
104            if f.alternate() { ("\x1B[0;4;1m", "\x1B[0;1m", "\x1B[0;32m", "\x1B[0m") } else { ("", "", "", "") };
105
106        writeln!(f)?;
107        writeln!(f, "{reg}FQ{reset} {val}{:X}{reset}#h", self.fq)?;
108        writeln!(f, "{sect}E-regs:{reset}")?;
109        for (no, item) in self.e.iter().enumerate() {
110            write!(f, "{reg}{}{reset} ", RegE::from(u4::with(no as u8)))?;
111            if let Some(e) = item {
112                writeln!(f, "{val}{e}{reset}#h")?;
113            } else {
114                writeln!(f, "~")?;
115            }
116        }
117        writeln!(f)
118    }
119}
120
121/// Registers storing field elements.
122///
123/// # zk-AluVM ABI standard
124///
125/// Totally, there are 16 registers, divided in two groups.
126///
127/// The first group, consisting of 8 registers, from `E1` to `E8` are used for storing local
128/// variables. If a routine calls another routine or external library, it must assume that the
129/// values in these registers may not be preserved.
130///
131/// The second group, consisting of another 8 registers, from `EA` to `EG`, is used for passing
132/// arguments and reading the retuned data from the routine or external procedure calls.
133#[allow(missing_docs)]
134#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Display)]
135#[display(uppercase)]
136#[repr(u8)]
137pub enum RegE {
138    E1 = 0b_0000,
139    E2 = 0b_0001,
140    E3 = 0b_0010,
141    E4 = 0b_0011,
142    E5 = 0b_0100,
143    E6 = 0b_0101,
144    E7 = 0b_0110,
145    E8 = 0b_0111,
146    EA = 0b_1000,
147    EB = 0b_1001,
148    EC = 0b_1010,
149    ED = 0b_1011,
150    EE = 0b_1100,
151    EF = 0b_1101,
152    EG = 0b_1110,
153    EH = 0b_1111,
154}
155
156impl Register for RegE {
157    type Value = fe256;
158
159    #[inline]
160    fn bytes(self) -> u16 { 32 }
161}
162
163impl From<u4> for RegE {
164    fn from(val: u4) -> Self {
165        match val {
166            x if x == RegE::E1.to_u4() => RegE::E1,
167            x if x == RegE::E2.to_u4() => RegE::E2,
168            x if x == RegE::E3.to_u4() => RegE::E3,
169            x if x == RegE::E4.to_u4() => RegE::E4,
170            x if x == RegE::E5.to_u4() => RegE::E5,
171            x if x == RegE::E6.to_u4() => RegE::E6,
172            x if x == RegE::E7.to_u4() => RegE::E7,
173            x if x == RegE::E8.to_u4() => RegE::E8,
174            x if x == RegE::EA.to_u4() => RegE::EA,
175            x if x == RegE::EB.to_u4() => RegE::EB,
176            x if x == RegE::EC.to_u4() => RegE::EC,
177            x if x == RegE::ED.to_u4() => RegE::ED,
178            x if x == RegE::EE.to_u4() => RegE::EE,
179            x if x == RegE::EF.to_u4() => RegE::EF,
180            x if x == RegE::EG.to_u4() => RegE::EG,
181            x if x == RegE::EH.to_u4() => RegE::EH,
182            _ => unreachable!(),
183        }
184    }
185}
186
187impl RegE {
188    /// Enumeration of all available registers.
189    pub const ALL: [Self; 16] = [
190        RegE::E1,
191        RegE::E2,
192        RegE::E3,
193        RegE::E4,
194        RegE::E5,
195        RegE::E6,
196        RegE::E7,
197        RegE::E8,
198        RegE::EA,
199        RegE::EB,
200        RegE::EC,
201        RegE::ED,
202        RegE::EE,
203        RegE::EF,
204        RegE::EG,
205        RegE::EH,
206    ];
207
208    /// Get a 4-bit representation of the register index.
209    #[inline]
210    pub const fn to_u4(self) -> u4 { u4::with(self as u8) }
211}