darkfi/zkas/opcode.rs
1/* This file is part of DarkFi (https://dark.fi)
2 *
3 * Copyright (C) 2020-2026 Dyne.org foundation
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Affero General Public License as
7 * published by the Free Software Foundation, either version 3 of the
8 * License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Affero General Public License for more details.
14 *
15 * You should have received a copy of the GNU Affero General Public License
16 * along with this program. If not, see <https://www.gnu.org/licenses/>.
17 */
18
19use super::VarType;
20
21/// Macro to define all opcodes in a single place.
22/// This generates the enum definition, `from_name`, `from_repr`, `name`, and `arg_types` methods.
23///
24/// Format for each opcode:
25/// ```text
26/// [doc_comments]
27/// VariantName = 0xNN, "string_name", (return_types), (arg_types)
28/// ```
29///
30/// Note: Noop is handled specially - it's excluded from `from_repr` and `from_name`
31/// as it should never appear in compiled binaries or source code.
32macro_rules! define_opcodes {
33 (
34 // Special case for Noop which shouldn't be in from_repr/from_name
35 $(#[doc = $noop_doc:literal])*
36 Noop = $noop_value:literal, $noop_name:literal,
37 ($($noop_ret:expr),*), ($($noop_arg:expr),*);
38
39 // All other opcodes
40 $(
41 $(#[doc = $doc:literal])*
42 $variant:ident = $value:literal, $name:literal,
43 ($($ret:expr),*), ($($arg:expr),*)
44 );* $(;)?
45 ) => {
46 /// Opcodes supported by the zkas VM
47 #[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)]
48 #[repr(u8)]
49 pub enum Opcode {
50 $(#[doc = $noop_doc])*
51 Noop = $noop_value,
52
53 $(
54 $(#[doc = $doc])*
55 $variant = $value,
56 )*
57 }
58
59 impl Opcode {
60 /// Look up an opcode by its string name (used in source code).
61 /// Note: Noop cannot be looked up by name as it's an internal compiler opcode.
62 pub fn from_name(n: &str) -> Option<Self> {
63 match n {
64 $($name => Some(Self::$variant),)*
65 _ => None,
66 }
67 }
68
69 /// Look up an opcode by its binary representation.
70 /// Note: Noop (0x00) is not valid in binary as it should never be compiled.
71 pub fn from_repr(b: u8) -> Option<Self> {
72 match b {
73 $($value => Some(Self::$variant),)*
74 _ => None,
75 }
76 }
77
78 /// Get the string name of an opcode.
79 pub fn name(&self) -> &'static str {
80 match self {
81 Self::Noop => $noop_name,
82 $(Self::$variant => $name,)*
83 }
84 }
85
86 /// Return a tuple of vectors of types that are accepted by a specific opcode.
87 /// `r.0` is the return type(s), and `r.1` is the argument type(s).
88 pub fn arg_types(&self) -> (Vec<VarType>, Vec<VarType>) {
89 match self {
90 Self::Noop => (vec![$($noop_ret),*], vec![$($noop_arg),*]),
91 $(Self::$variant => (vec![$($ret),*], vec![$($arg),*]),)*
92 }
93 }
94 }
95 };
96}
97
98define_opcodes! {
99 /// Intermediate opcode for the compiler, should never appear in the result
100 Noop = 0x00, "noop",
101 (), ();
102
103 /// Elliptic curve addition
104 EcAdd = 0x01, "ec_add",
105 (VarType::EcPoint), (VarType::EcPoint, VarType::EcPoint);
106
107 /// Elliptic curve multiplication
108 EcMul = 0x02, "ec_mul",
109 (VarType::EcPoint), (VarType::Scalar, VarType::EcFixedPoint);
110
111 /// Elliptic curve multiplication with a Base field element
112 EcMulBase = 0x03, "ec_mul_base",
113 (VarType::EcPoint), (VarType::Base, VarType::EcFixedPointBase);
114
115 /// Elliptic curve multiplication with a Base field element of 64bit width
116 EcMulShort = 0x04, "ec_mul_short",
117 (VarType::EcPoint), (VarType::Base, VarType::EcFixedPointShort);
118
119 /// Variable Elliptic curve multiplication with a Base field element
120 EcMulVarBase = 0x05, "ec_mul_var_base",
121 (VarType::EcPoint), (VarType::Base, VarType::EcNiPoint);
122
123 /// Get the x coordinate of an elliptic curve point
124 EcGetX = 0x08, "ec_get_x",
125 (VarType::Base), (VarType::EcPoint);
126
127 /// Get the y coordinate of an elliptic curve point
128 EcGetY = 0x09, "ec_get_y",
129 (VarType::Base), (VarType::EcPoint);
130
131 /// Poseidon hash of N Base field elements
132 PoseidonHash = 0x10, "poseidon_hash",
133 (VarType::Base), (VarType::BaseArray);
134
135 /// Calculate Merkle root, given a position, Merkle path, and an element
136 MerkleRoot = 0x20, "merkle_root",
137 (VarType::Base), (VarType::Uint32, VarType::MerklePath, VarType::Base);
138
139 /// Calculate sparse Merkle root, given the position, path and a member
140 SparseMerkleRoot = 0x21, "sparse_merkle_root",
141 (VarType::Base), (VarType::Base, VarType::SparseMerklePath, VarType::Base);
142
143 /// Base field element addition
144 BaseAdd = 0x30, "base_add",
145 (VarType::Base), (VarType::Base, VarType::Base);
146
147 /// Base field element multiplication
148 BaseMul = 0x31, "base_mul",
149 (VarType::Base), (VarType::Base, VarType::Base);
150
151 /// Base field element subtraction
152 BaseSub = 0x32, "base_sub",
153 (VarType::Base), (VarType::Base, VarType::Base);
154
155 /// Witness an unsigned integer into a Base field element
156 WitnessBase = 0x40, "witness_base",
157 (VarType::Base), (VarType::Uint64);
158
159 /// Range check a Base field element, given bit-width (up to 253)
160 RangeCheck = 0x50, "range_check",
161 (), (VarType::Uint64, VarType::Base);
162
163 /// Strictly compare two Base field elements and see if a is less than b.
164 /// This enforces the sum of remaining bits to be zero.
165 LessThanStrict = 0x51, "less_than_strict",
166 (), (VarType::Base, VarType::Base);
167
168 /// Loosely compare two Base field elements and see if a is less than b.
169 /// This does not enforce the sum of remaining bits to be zero.
170 LessThanLoose = 0x52, "less_than_loose",
171 (), (VarType::Base, VarType::Base);
172
173 /// Check if a field element fits in a boolean (Either 0 or 1)
174 BoolCheck = 0x53, "bool_check",
175 (), (VarType::Base);
176
177 /// Conditionally select between two base field elements given a boolean
178 CondSelect = 0x60, "cond_select",
179 (VarType::Base), (VarType::Base, VarType::Base, VarType::Base);
180
181 /// Conditionally select between a and b (return a if a is zero, and b if a is nonzero)
182 ZeroCondSelect = 0x61, "zero_cond",
183 (VarType::Base), (VarType::Base, VarType::Base);
184
185 /// Constrain equality of two Base field elements inside the circuit
186 ConstrainEqualBase = 0xe0, "constrain_equal_base",
187 (), (VarType::Base, VarType::Base);
188
189 /// Constrain equality of two EcPoint elements inside the circuit
190 ConstrainEqualPoint = 0xe1, "constrain_equal_point",
191 (), (VarType::EcPoint, VarType::EcPoint);
192
193 /// Constrain a Base field element to a circuit's public input
194 ConstrainInstance = 0xf0, "constrain_instance",
195 (), (VarType::Base);
196
197 /// Debug a variable's value in the ZK circuit table
198 DebugPrint = 0xff, "debug",
199 (), (VarType::Any);
200}