zkaluvm/gfa/
exec.rs

1// AluVM extensions for zero knowledge, STARKs and SNARKs"
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 alloc::collections::BTreeSet;
24
25use aluvm::isa::{ExecStep, Instruction};
26use aluvm::regs::Status;
27use aluvm::{Core, CoreExt, Site, SiteId, Supercore};
28
29use super::{FieldInstr, Instr, ISA_GFA128};
30use crate::{fe256, GfaCore, RegE};
31
32impl<Id: SiteId> Instruction<Id> for FieldInstr {
33    const ISA_EXT: &'static [&'static str] = &[ISA_GFA128];
34    type Core = GfaCore;
35    type Context<'ctx> = ();
36
37    fn is_goto_target(&self) -> bool { false }
38
39    fn local_goto_pos(&mut self) -> Option<&mut u16> { None }
40
41    fn remote_goto_pos(&mut self) -> Option<&mut Site<Id>> { None }
42
43    fn src_regs(&self) -> BTreeSet<RegE> {
44        match *self {
45            FieldInstr::Clr { dst: _ }
46            | FieldInstr::PutD { dst: _, data: _ }
47            | FieldInstr::PutZ { dst: _ }
48            | FieldInstr::PutV { dst: _, val: _ } => none!(),
49
50            FieldInstr::Eq { src1, src2 } => bset![src1, src2],
51
52            FieldInstr::Test { src }
53            | FieldInstr::Fits { src, bits: _ }
54            | FieldInstr::Mov { dst: _, src }
55            | FieldInstr::Neg { dst: _, src } => bset![src],
56
57            FieldInstr::Add { dst_src, src } | FieldInstr::Mul { dst_src, src } => bset![src, dst_src],
58        }
59    }
60
61    fn dst_regs(&self) -> BTreeSet<RegE> {
62        match *self {
63            FieldInstr::Clr { dst }
64            | FieldInstr::PutD { dst, data: _ }
65            | FieldInstr::PutZ { dst }
66            | FieldInstr::PutV { dst, val: _ }
67            | FieldInstr::Mov { dst, src: _ } => bset![dst],
68
69            FieldInstr::Eq { src1: _, src2: _ }
70            | FieldInstr::Test { src: _ }
71            | FieldInstr::Fits { src: _, bits: _ } => none!(),
72
73            FieldInstr::Neg { dst, src: _ }
74            | FieldInstr::Add { dst_src: dst, src: _ }
75            | FieldInstr::Mul { dst_src: dst, src: _ } => bset![dst],
76        }
77    }
78
79    fn op_data_bytes(&self) -> u16 {
80        match self {
81            FieldInstr::PutV { dst: _, val: _ } | FieldInstr::Fits { src: _, bits: _ } => 1,
82
83            FieldInstr::Test { src: _ }
84            | FieldInstr::Clr { dst: _ }
85            | FieldInstr::PutD { dst: _, data: _ }
86            | FieldInstr::PutZ { dst: _ }
87            | FieldInstr::Mov { dst: _, src: _ }
88            | FieldInstr::Eq { src1: _, src2: _ }
89            | FieldInstr::Neg { dst: _, src: _ }
90            | FieldInstr::Add { dst_src: _, src: _ }
91            | FieldInstr::Mul { dst_src: _, src: _ } => 0,
92        }
93    }
94
95    fn ext_data_bytes(&self) -> u16 {
96        match self {
97            FieldInstr::PutD { dst: _, data: _ } => 32,
98
99            FieldInstr::Test { src: _ }
100            | FieldInstr::Clr { dst: _ }
101            | FieldInstr::PutZ { dst: _ }
102            | FieldInstr::PutV { dst: _, val: _ }
103            | FieldInstr::Fits { src: _, bits: _ }
104            | FieldInstr::Mov { dst: _, src: _ }
105            | FieldInstr::Eq { src1: _, src2: _ }
106            | FieldInstr::Neg { dst: _, src: _ }
107            | FieldInstr::Add { dst_src: _, src: _ }
108            | FieldInstr::Mul { dst_src: _, src: _ } => 0,
109        }
110    }
111
112    fn complexity(&self) -> u64 {
113        let base = Instruction::<Id>::base_complexity(self);
114        match self {
115            FieldInstr::Test { src: _ }
116            | FieldInstr::Clr { dst: _ }
117            | FieldInstr::PutZ { dst: _ }
118            | FieldInstr::PutV { dst: _, val: _ }
119            | FieldInstr::PutD { dst: _, data: _ }
120            | FieldInstr::Mov { dst: _, src: _ }
121            | FieldInstr::Eq { src1: _, src2: _ } => base,
122
123            FieldInstr::Fits { src: _, bits: _ }
124            | FieldInstr::Neg { dst: _, src: _ }
125            | FieldInstr::Add { dst_src: _, src: _ }
126            | FieldInstr::Mul { dst_src: _, src: _ } => {
127                // Double the default complexity since each instruction performs two operations.
128                base * 2
129            }
130        }
131    }
132
133    fn exec(&self, _: Site<Id>, core: &mut Core<Id, GfaCore>, _: &Self::Context<'_>) -> ExecStep<Site<Id>> {
134        let res = match *self {
135            FieldInstr::Test { src } => {
136                let res = core.cx.test(src);
137                core.set_co(res);
138                Status::Ok
139            }
140            FieldInstr::Clr { dst } => {
141                core.cx.clr(dst);
142                Status::Ok
143            }
144            FieldInstr::PutD { dst, data } => {
145                core.cx.set(dst, data);
146                Status::Ok
147            }
148            FieldInstr::PutZ { dst } => {
149                core.cx.set(dst, fe256::ZERO);
150                Status::Ok
151            }
152            FieldInstr::PutV { dst, val } => {
153                let val = val.to_fe256().unwrap_or_else(|| core.cx.fq().into());
154                core.cx.set(dst, val);
155                Status::Ok
156            }
157            FieldInstr::Mov { dst, src } => {
158                core.cx.mov(dst, src);
159                Status::Ok
160            }
161            FieldInstr::Eq { src1, src2 } => {
162                let res = core.cx.eqv(src1, src2);
163                core.set_co(res);
164                Status::Ok
165            }
166
167            FieldInstr::Fits { src, bits } => match core.cx.fits(src, bits) {
168                None => Status::Fail,
169                Some(true) => {
170                    core.set_co(Status::Ok);
171                    Status::Ok
172                }
173                Some(false) => {
174                    core.set_co(Status::Fail);
175                    Status::Ok
176                }
177            },
178            FieldInstr::Neg { dst, src } => core.cx.neg_mod(dst, src),
179            FieldInstr::Add { dst_src, src } => core.cx.add_mod(dst_src, src),
180            FieldInstr::Mul { dst_src, src } => core.cx.mul_mod(dst_src, src),
181        };
182        if res == Status::Ok {
183            ExecStep::Next
184        } else {
185            ExecStep::Fail
186        }
187    }
188}
189
190impl<Id: SiteId> Instruction<Id> for Instr<Id> {
191    const ISA_EXT: &'static [&'static str] = &[ISA_GFA128];
192    type Core = GfaCore;
193    type Context<'ctx> = ();
194
195    fn is_goto_target(&self) -> bool {
196        match self {
197            Instr::Ctrl(ctrl) => ctrl.is_goto_target(),
198            Instr::Gfa(instr) => Instruction::<Id>::is_goto_target(instr),
199            Instr::Reserved(reserved) => Instruction::<Id>::is_goto_target(reserved),
200        }
201    }
202
203    fn local_goto_pos(&mut self) -> Option<&mut u16> {
204        match self {
205            Instr::Ctrl(ctrl) => ctrl.local_goto_pos(),
206            Instr::Gfa(instr) => Instruction::<Id>::local_goto_pos(instr),
207            Instr::Reserved(reserved) => Instruction::<Id>::local_goto_pos(reserved),
208        }
209    }
210
211    fn remote_goto_pos(&mut self) -> Option<&mut Site<Id>> {
212        match self {
213            Instr::Ctrl(ctrl) => ctrl.remote_goto_pos(),
214            Instr::Gfa(instr) => Instruction::<Id>::remote_goto_pos(instr),
215            Instr::Reserved(reserved) => Instruction::<Id>::remote_goto_pos(reserved),
216        }
217    }
218
219    fn src_regs(&self) -> BTreeSet<<Self::Core as CoreExt>::Reg> {
220        match self {
221            Instr::Ctrl(_) => none!(),
222            Instr::Gfa(instr) => Instruction::<Id>::src_regs(instr),
223            Instr::Reserved(_) => none!(),
224        }
225    }
226
227    fn dst_regs(&self) -> BTreeSet<<Self::Core as CoreExt>::Reg> {
228        match self {
229            Instr::Ctrl(_) => none!(),
230            Instr::Gfa(instr) => Instruction::<Id>::dst_regs(instr),
231            Instr::Reserved(_) => none!(),
232        }
233    }
234
235    fn op_data_bytes(&self) -> u16 {
236        match self {
237            Instr::Ctrl(instr) => instr.op_data_bytes(),
238            Instr::Gfa(instr) => Instruction::<Id>::op_data_bytes(instr),
239            Instr::Reserved(_) => none!(),
240        }
241    }
242
243    fn ext_data_bytes(&self) -> u16 {
244        match self {
245            Instr::Ctrl(instr) => instr.ext_data_bytes(),
246            Instr::Gfa(instr) => Instruction::<Id>::ext_data_bytes(instr),
247            Instr::Reserved(_) => none!(),
248        }
249    }
250
251    fn exec(&self, site: Site<Id>, core: &mut Core<Id, Self::Core>, context: &Self::Context<'_>) -> ExecStep<Site<Id>> {
252        match self {
253            Instr::Ctrl(instr) => {
254                let mut subcore = core.subcore();
255                let step = instr.exec(site, &mut subcore, context);
256                core.merge_subcore(subcore);
257                step
258            }
259            Instr::Gfa(instr) => {
260                let step = instr.exec(site, core, context);
261                step
262            }
263            Instr::Reserved(instr) => {
264                let mut subcore = core.subcore();
265                let step = instr.exec(site, &mut subcore, context);
266                core.merge_subcore(subcore);
267                step
268            }
269        }
270    }
271}
272
273#[cfg(test)]
274mod test {
275    use aluvm::{CoreConfig, Lib, LibId, LibSite, Vm};
276    use amplify::num::u256;
277
278    use super::*;
279    use crate::zk_aluasm;
280
281    const CONFIG: CoreConfig = CoreConfig {
282        halt: true,
283        complexity_lim: None,
284    };
285
286    #[test]
287    fn putd() {
288        const VAL: u256 = u256::from_inner([73864950, 463656, 3456556, 23456657]);
289        let code = zk_aluasm! {
290            mov EA, :VAL;
291        };
292        let lib = Lib::assemble(&code).unwrap();
293        let lib_id = lib.lib_id();
294
295        let mut vm = Vm::<Instr<LibId>>::with(CONFIG, default!());
296        let resolver = |id: LibId| {
297            assert_eq!(id, lib_id);
298            Some(&lib)
299        };
300        let res = vm.exec(LibSite::new(lib_id, 0), &(), resolver).is_ok();
301        assert!(res);
302
303        assert_eq!(vm.core.cx.get(RegE::EA), Some(fe256::from(VAL)));
304    }
305}