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}