acir/circuit/
black_box_functions.rs

1//! Black box functions are ACIR opcodes which rely on backends implementing
2//! support for specialized constraints.
3//! This makes certain zk-snark unfriendly computations cheaper than if they were
4//! implemented in more basic constraints.
5
6use serde::{Deserialize, Serialize};
7use strum_macros::EnumIter;
8
9/// Representation of available black box function names.
10/// This enum should be used to represent a black box before we have set up the
11/// appropriate inputs and outputs. At which point it should be converted to a [`crate::circuit::opcodes::BlackBoxFuncCall`]
12#[allow(clippy::upper_case_acronyms)]
13#[derive(Clone, Debug, Hash, Copy, PartialEq, Eq, Serialize, Deserialize, EnumIter)]
14pub enum BlackBoxFunc {
15    /// More details can be found at [`crate::circuit::opcodes::BlackBoxFuncCall::AES128Encrypt`]
16    AES128Encrypt,
17    /// More details can be found at [`crate::circuit::opcodes::BlackBoxFuncCall::AND`]
18    AND,
19    /// More details can be found at [`crate::circuit::opcodes::BlackBoxFuncCall::XOR`]
20    XOR,
21    /// More details can be found at [`crate::circuit::opcodes::BlackBoxFuncCall::RANGE`]
22    RANGE,
23    /// More details can be found at [`crate::circuit::opcodes::BlackBoxFuncCall::Blake2s`]
24    Blake2s,
25    /// More details can be found at [`crate::circuit::opcodes::BlackBoxFuncCall::Blake3`]
26    Blake3,
27    /// More details can be found at [`crate::circuit::opcodes::BlackBoxFuncCall::EcdsaSecp256k1`]
28    EcdsaSecp256k1,
29    /// More details can be found at [`crate::circuit::opcodes::BlackBoxFuncCall::EcdsaSecp256r1`]
30    EcdsaSecp256r1,
31    /// More details can be found at [`crate::circuit::opcodes::BlackBoxFuncCall::MultiScalarMul`]
32    MultiScalarMul,
33    /// More details can be found at [`crate::circuit::opcodes::BlackBoxFuncCall::Keccakf1600`]
34    Keccakf1600,
35    /// More details can be found at [`crate::circuit::opcodes::BlackBoxFuncCall::RecursiveAggregation`]
36    RecursiveAggregation,
37    /// More details can be found at [`crate::circuit::opcodes::BlackBoxFuncCall::EmbeddedCurveAdd`]
38    EmbeddedCurveAdd,
39    /// More details can be found at [`crate::circuit::opcodes::BlackBoxFuncCall::Poseidon2Permutation`]
40    Poseidon2Permutation,
41    /// More details can be found at [`crate::circuit::opcodes::BlackBoxFuncCall::Sha256Compression`]
42    Sha256Compression,
43}
44
45impl std::fmt::Display for BlackBoxFunc {
46    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
47        write!(f, "{}", self.name())
48    }
49}
50
51impl BlackBoxFunc {
52    pub fn name(&self) -> &'static str {
53        match self {
54            BlackBoxFunc::AES128Encrypt => "aes128_encrypt",
55            BlackBoxFunc::Blake2s => "blake2s",
56            BlackBoxFunc::Blake3 => "blake3",
57            BlackBoxFunc::EcdsaSecp256k1 => "ecdsa_secp256k1",
58            BlackBoxFunc::MultiScalarMul => "multi_scalar_mul",
59            BlackBoxFunc::EmbeddedCurveAdd => "embedded_curve_add",
60            BlackBoxFunc::AND => "and",
61            BlackBoxFunc::XOR => "xor",
62            BlackBoxFunc::RANGE => "range",
63            BlackBoxFunc::Keccakf1600 => "keccakf1600",
64            BlackBoxFunc::RecursiveAggregation => "recursive_aggregation",
65            BlackBoxFunc::EcdsaSecp256r1 => "ecdsa_secp256r1",
66            BlackBoxFunc::Poseidon2Permutation => "poseidon2_permutation",
67            BlackBoxFunc::Sha256Compression => "sha256_compression",
68        }
69    }
70
71    pub fn lookup(op_name: &str) -> Option<BlackBoxFunc> {
72        match op_name {
73            "aes128_encrypt" => Some(BlackBoxFunc::AES128Encrypt),
74            "blake2s" => Some(BlackBoxFunc::Blake2s),
75            "blake3" => Some(BlackBoxFunc::Blake3),
76            "ecdsa_secp256k1" => Some(BlackBoxFunc::EcdsaSecp256k1),
77            "ecdsa_secp256r1" => Some(BlackBoxFunc::EcdsaSecp256r1),
78            "multi_scalar_mul" => Some(BlackBoxFunc::MultiScalarMul),
79            "embedded_curve_add" => Some(BlackBoxFunc::EmbeddedCurveAdd),
80            "and" => Some(BlackBoxFunc::AND),
81            "xor" => Some(BlackBoxFunc::XOR),
82            "range" => Some(BlackBoxFunc::RANGE),
83            "keccakf1600" => Some(BlackBoxFunc::Keccakf1600),
84            "recursive_aggregation" => Some(BlackBoxFunc::RecursiveAggregation),
85            "poseidon2_permutation" => Some(BlackBoxFunc::Poseidon2Permutation),
86            "sha256_compression" => Some(BlackBoxFunc::Sha256Compression),
87            _ => None,
88        }
89    }
90
91    pub fn has_side_effects(&self) -> bool {
92        match self {
93            BlackBoxFunc::RecursiveAggregation
94            | BlackBoxFunc::MultiScalarMul
95            | BlackBoxFunc::EmbeddedCurveAdd
96            | BlackBoxFunc::EcdsaSecp256k1
97            | BlackBoxFunc::EcdsaSecp256r1
98            | BlackBoxFunc::RANGE => true,
99
100            BlackBoxFunc::AES128Encrypt
101            | BlackBoxFunc::AND
102            | BlackBoxFunc::XOR
103            | BlackBoxFunc::Blake2s
104            | BlackBoxFunc::Blake3
105            | BlackBoxFunc::Keccakf1600
106            | BlackBoxFunc::Poseidon2Permutation
107            | BlackBoxFunc::Sha256Compression => false,
108        }
109    }
110
111    /// This function will return the number of inputs that a blackbox function
112    /// expects. Returning `None` if there is no expectation.
113    pub fn expected_input_size(&self) -> Option<usize> {
114        match self {
115            // Bitwise opcodes will take in 2 parameters
116            BlackBoxFunc::AND | BlackBoxFunc::XOR => Some(2),
117
118            // All of the hash/cipher methods will take in a
119            // variable number of inputs.
120            BlackBoxFunc::AES128Encrypt | BlackBoxFunc::Blake2s | BlackBoxFunc::Blake3 => None,
121
122            BlackBoxFunc::Keccakf1600 => Some(25),
123            // The permutation takes a fixed number of inputs, but the inputs length depends on the proving system implementation.
124            BlackBoxFunc::Poseidon2Permutation => None,
125
126            // SHA256 compression requires 16 u32s as input message and 8 u32s for the hash state.
127            BlackBoxFunc::Sha256Compression => Some(24),
128            // Can only apply a range constraint to one
129            // witness at a time.
130            BlackBoxFunc::RANGE => Some(1),
131
132            // 64 bytes for the signature, 32 bytes for the hashed message,
133            // and 32 bytes each for the x and y coordinates of the public key, plus a predicate.
134            BlackBoxFunc::EcdsaSecp256k1 | BlackBoxFunc::EcdsaSecp256r1 => Some(161),
135
136            // Inputs for multi scalar multiplication is an arbitrary number of [point, scalar] pairs.
137            BlackBoxFunc::MultiScalarMul => None,
138
139            // Recursive aggregation has a variable number of inputs
140            BlackBoxFunc::RecursiveAggregation => None,
141
142            // Addition over the embedded curve: inputs are coordinates (x1,y1) and (x2,y2) of the Grumpkin points
143            // to add, plus a predicate to conditionally perform the addition.
144            BlackBoxFunc::EmbeddedCurveAdd => Some(5),
145        }
146    }
147
148    /// This function will return the number of outputs that a blackbox function
149    /// expects. Returning `None` if there is no expectation.
150    pub fn expected_output_size(&self) -> Option<usize> {
151        match self {
152            // Bitwise opcodes will return 1 parameter which is the output
153            // or the operation.
154            BlackBoxFunc::AND | BlackBoxFunc::XOR => Some(1),
155
156            // 32 byte hash algorithms
157            BlackBoxFunc::Blake2s | BlackBoxFunc::Blake3 => Some(32),
158
159            BlackBoxFunc::Keccakf1600 => Some(25),
160            // The permutation returns a fixed number of outputs, equals to the inputs length which depends on the proving system implementation.
161            BlackBoxFunc::Poseidon2Permutation => None,
162
163            BlackBoxFunc::Sha256Compression => Some(8),
164
165            BlackBoxFunc::RANGE => Some(0),
166
167            // Signature verification algorithms will return a boolean
168            BlackBoxFunc::EcdsaSecp256k1 | BlackBoxFunc::EcdsaSecp256r1 => Some(1),
169
170            // Output of operations over the embedded curve
171            // will be 2 field elements representing the point, i.e. (x,y)
172            BlackBoxFunc::MultiScalarMul | BlackBoxFunc::EmbeddedCurveAdd => Some(2),
173
174            // Recursive aggregation has no output
175            BlackBoxFunc::RecursiveAggregation => Some(0),
176
177            // AES encryption returns a variable number of outputs
178            BlackBoxFunc::AES128Encrypt => None,
179        }
180    }
181}
182
183#[cfg(test)]
184mod tests {
185    use strum::IntoEnumIterator;
186
187    use crate::BlackBoxFunc;
188
189    #[test]
190    fn consistent_function_names() {
191        for bb_func in BlackBoxFunc::iter() {
192            let resolved_func = BlackBoxFunc::lookup(bb_func.name()).unwrap_or_else(|| {
193                panic!("BlackBoxFunc::lookup couldn't find black box function {bb_func}")
194            });
195            assert_eq!(
196                resolved_func, bb_func,
197                "BlackBoxFunc::lookup returns unexpected BlackBoxFunc"
198            );
199        }
200    }
201}