Skip to main content

acir/circuit/opcodes/
black_box_function_call.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 std::collections::BTreeSet;
7
8use crate::native_types::Witness;
9use crate::{AcirField, BlackBoxFunc};
10
11use serde::{Deserialize, Deserializer, Serialize, Serializer};
12use thiserror::Error;
13
14/// Enumeration for black box function inputs
15#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, Hash)]
16#[cfg_attr(feature = "arb", derive(proptest_derive::Arbitrary))]
17pub enum ConstantOrWitnessEnum<F> {
18    /// A constant field element
19    Constant(F),
20    /// A witness element, representing dynamic inputs
21    Witness(Witness),
22}
23
24/// Input to a black box call
25#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, Hash)]
26#[cfg_attr(feature = "arb", derive(proptest_derive::Arbitrary))]
27pub struct FunctionInput<F> {
28    /// The actual input value
29    input: ConstantOrWitnessEnum<F>,
30    /// A constant representing the bit size of the input value
31    /// Some functions will not use all of the witness
32    /// So we need to supply how many bits of the witness is needed
33    num_bits: u32,
34}
35
36impl<F> FunctionInput<F> {
37    pub fn to_witness(&self) -> Witness {
38        match self.input {
39            ConstantOrWitnessEnum::Constant(_) => unreachable!("ICE - Expected Witness"),
40            ConstantOrWitnessEnum::Witness(witness) => witness,
41        }
42    }
43
44    pub fn input(self) -> ConstantOrWitnessEnum<F> {
45        self.input
46    }
47
48    pub fn input_ref(&self) -> &ConstantOrWitnessEnum<F> {
49        &self.input
50    }
51
52    pub fn num_bits(&self) -> u32 {
53        self.num_bits
54    }
55
56    pub fn witness(witness: Witness, num_bits: u32) -> FunctionInput<F> {
57        FunctionInput { input: ConstantOrWitnessEnum::Witness(witness), num_bits }
58    }
59
60    pub fn is_constant(&self) -> bool {
61        matches!(self.input, ConstantOrWitnessEnum::Constant(_))
62    }
63}
64
65#[derive(Clone, PartialEq, Eq, Debug, Error)]
66#[error("FunctionInput value has too many bits: value: {value}, {value_num_bits} >= {max_bits}")]
67pub struct InvalidInputBitSize {
68    pub value: String,
69    pub value_num_bits: u32,
70    pub max_bits: u32,
71}
72
73impl<F: AcirField> FunctionInput<F> {
74    pub fn constant(value: F, max_bits: u32) -> Result<FunctionInput<F>, InvalidInputBitSize> {
75        if value.num_bits() <= max_bits {
76            Ok(FunctionInput { input: ConstantOrWitnessEnum::Constant(value), num_bits: max_bits })
77        } else {
78            let value_num_bits = value.num_bits();
79            let value = format!("{value}");
80            Err(InvalidInputBitSize { value, value_num_bits, max_bits })
81        }
82    }
83}
84
85impl<F: std::fmt::Display> std::fmt::Display for FunctionInput<F> {
86    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
87        match &self.input {
88            ConstantOrWitnessEnum::Constant(constant) => {
89                write!(f, "({constant}, {})", self.num_bits)
90            }
91            ConstantOrWitnessEnum::Witness(witness) => {
92                write!(f, "({}, {})", witness, self.num_bits)
93            }
94        }
95    }
96}
97
98/// These opcodes represent a specialized computation.
99/// Even if any computation can be done using only assert-zero opcodes,
100/// it is not always efficient.
101/// Some proving systems, can implement several computations more efficiently using
102/// techniques such as custom gates and lookup tables.
103#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Hash)]
104pub enum BlackBoxFuncCall<F> {
105    /// Ciphers (encrypts) the provided plaintext using AES128 in CBC mode,
106    /// padding the input using PKCS#7.
107    /// - inputs: byte array `[u8; N]`
108    /// - iv: initialization vector `[u8; 16]`
109    /// - key: user key `[u8; 16]`
110    /// - outputs: byte vector `[u8]` of length `input.len() + (16 - input.len() % 16)`
111    AES128Encrypt {
112        inputs: Vec<FunctionInput<F>>,
113        iv: Box<[FunctionInput<F>; 16]>,
114        key: Box<[FunctionInput<F>; 16]>,
115        outputs: Vec<Witness>,
116    },
117    /// Performs the bitwise AND of `lhs` and `rhs`. `bit_size` must be the same for
118    /// both inputs.
119    /// - lhs: (witness, bit_size)
120    /// - rhs: (witness, bit_size)
121    /// - output: a witness whose value is constrained to be lhs AND rhs, as
122    ///   bit_size bit integers
123    AND { lhs: FunctionInput<F>, rhs: FunctionInput<F>, output: Witness },
124    /// Performs the bitwise XOR of `lhs` and `rhs`. `bit_size` must be the same for
125    /// both inputs.
126    /// - lhs: (witness, bit_size)
127    /// - rhs: (witness, bit_size)
128    /// - output: a witness whose value is constrained to be lhs XOR rhs, as
129    ///   bit_size bit integers
130    XOR { lhs: FunctionInput<F>, rhs: FunctionInput<F>, output: Witness },
131    /// Range constraint to ensure that a witness
132    /// can be represented in the specified number of bits.
133    /// - input: (witness, bit_size)
134    RANGE { input: FunctionInput<F> },
135    /// Computes the Blake2s hash of the inputs, as specified in
136    /// <https://tools.ietf.org/html/rfc7693>
137    /// - inputs are a byte array, i.e a vector of (witness, 8)
138    /// - output is a byte array of length 32, i.e. an array of 32
139    ///   (witness, 8), constrained to be the blake2s of the inputs.
140    Blake2s { inputs: Vec<FunctionInput<F>>, outputs: Box<[Witness; 32]> },
141    /// Computes the Blake3 hash of the inputs
142    /// - inputs are a byte array, i.e a vector of (witness, 8)
143    /// - output is a byte array of length 32, i.e an array of 32
144    ///   (witness, 8), constrained to be the blake3 of the inputs.
145    Blake3 { inputs: Vec<FunctionInput<F>>, outputs: Box<[Witness; 32]> },
146    /// Verifies a ECDSA signature over the secp256k1 curve.
147    /// - inputs:
148    ///     - x coordinate of public key as 32 bytes
149    ///     - y coordinate of public key as 32 bytes
150    ///     - the signature, as a 64 bytes array
151    ///       The signature internally will be represented as `(r, s)`,
152    ///       where `r` and `s` are fixed-sized big endian scalar values.
153    ///       As the `secp256k1` has a 256-bit modulus, we have a 64 byte signature
154    ///       while `r` and `s` will both be 32 bytes.
155    ///       We expect `s` to be normalized. This means given the curve's order,
156    ///       `s` should be less than or equal to `order / 2`.
157    ///       This is done to prevent malleability.
158    ///       For more context regarding malleability you can reference BIP 0062.
159    ///     - the hash of the message, as a vector of bytes
160    /// - output: 0 for failure and 1 for success
161    ///
162    /// Expected backend behavior:
163    /// - The backend MAY fail to prove this opcode if the public key is not on the secp256k1 curve.
164    ///    - Otherwise the backend MUST constrain the output to be false.
165    /// - The backend MUST constrain the output to be false if `s` is not normalized.
166    /// - The backend MUST constrain the output to match the signature's validity.
167    EcdsaSecp256k1 {
168        public_key_x: Box<[FunctionInput<F>; 32]>,
169        public_key_y: Box<[FunctionInput<F>; 32]>,
170        #[serde(
171            serialize_with = "serialize_big_array",
172            deserialize_with = "deserialize_big_array_into_box"
173        )]
174        signature: Box<[FunctionInput<F>; 64]>,
175        hashed_message: Box<[FunctionInput<F>; 32]>,
176        output: Witness,
177    },
178    /// Verifies a ECDSA signature over the secp256r1 curve.
179    ///
180    /// Same as EcdsaSecp256k1, but done over another curve.
181    EcdsaSecp256r1 {
182        public_key_x: Box<[FunctionInput<F>; 32]>,
183        public_key_y: Box<[FunctionInput<F>; 32]>,
184        #[serde(
185            serialize_with = "serialize_big_array",
186            deserialize_with = "deserialize_big_array_into_box"
187        )]
188        signature: Box<[FunctionInput<F>; 64]>,
189        hashed_message: Box<[FunctionInput<F>; 32]>,
190        output: Witness,
191    },
192    /// Multiple scalar multiplication (MSM) with a variable base/input point
193    /// (P) of the embedded curve. An MSM multiplies the points and scalars and
194    /// sums the results.
195    /// - input:
196    ///     - points (witness, N) a vector of x and y coordinates of input
197    ///     - points `[x1, y1, x2, y2,...]`.
198    ///     - scalars (witness, N) a vector of low and high limbs of input
199    ///     - scalars `[s1_low, s1_high, s2_low, s2_high, ...]`. (witness, N)
200    ///       For Barretenberg, they must both be less than 128 bits.
201    /// - output:
202    ///     - a tuple of `x` and `y` coordinates of output
203    ///       points computed as `s_low*P+s_high*2^{128}*P`
204    ///
205    /// Because the Grumpkin scalar field is bigger than the ACIR field, we
206    /// provide 2 ACIR fields representing the low and high parts of the Grumpkin
207    /// scalar $a$: `a=low+high*2^{128}`, with `low, high < 2^{128}`
208    MultiScalarMul {
209        points: Vec<FunctionInput<F>>,
210        scalars: Vec<FunctionInput<F>>,
211        outputs: (Witness, Witness, Witness),
212    },
213    /// Addition over the embedded curve on which the witness is defined
214    /// The opcode makes the following assumptions but does not enforce them because
215    /// it is more efficient to do it only when required. For instance, adding two
216    /// points that are on the curve it guarantee to give a point on the curve.
217    ///
218    /// It assumes that the points are on the curve.
219    /// If the inputs are the same witnesses index, it will perform a doubling,
220    /// If not, it assumes that the points' x-coordinates are not equal.
221    /// It also assumes neither point is the infinity point.
222    EmbeddedCurveAdd {
223        input1: Box<[FunctionInput<F>; 3]>,
224        input2: Box<[FunctionInput<F>; 3]>,
225        outputs: (Witness, Witness, Witness),
226    },
227    /// Keccak Permutation function of width 1600
228    /// - inputs: An array of 25 64-bit Keccak lanes that represent a keccak sponge of 1600 bits
229    /// - outputs: The result of a keccak f1600 permutation on the input state. Also an array of 25 Keccak lanes.
230    Keccakf1600 { inputs: Box<[FunctionInput<F>; 25]>, outputs: Box<[Witness; 25]> },
231    /// Computes a recursive aggregation object when verifying a proof inside
232    /// another circuit.
233    /// The outputted aggregation object will then be either checked in a
234    /// top-level verifier or aggregated upon again.
235    /// The aggregation object should be maintained by the backend implementer.
236    ///
237    /// This opcode prepares the verification of the final proof.
238    /// In order to fully verify a recursive proof, some operations may still be required
239    /// to be done by the final verifier (e.g. a pairing check).
240    /// This is why this black box function does not say if verification is passing or not.
241    /// It delays the expensive part of verification out of the SNARK
242    /// and leaves it to the final verifier outside of the SNARK circuit.
243    ///
244    /// This opcode also verifies that the key_hash is indeed a hash of verification_key,
245    /// allowing the user to use the verification key as private inputs and only
246    /// have the key_hash as public input, which is more performant.
247    ///
248    /// **Warning: the key hash logic does not need to be part of the black box and subject to be removed.**
249    ///
250    /// If one of the recursive proofs you verify with the black box function fails to
251    /// verify, then the verification of the final proof of the main ACIR program will
252    /// ultimately fail.
253    RecursiveAggregation {
254        /// Verification key of the circuit being verified
255        verification_key: Vec<FunctionInput<F>>,
256        proof: Vec<FunctionInput<F>>,
257        /// These represent the public inputs of the proof we are verifying
258        /// They should be checked against in the circuit after construction
259        /// of a new aggregation state
260        public_inputs: Vec<FunctionInput<F>>,
261        /// A key hash is used to check the validity of the verification key.
262        /// The circuit implementing this opcode can use this hash to ensure that the
263        /// key provided to the circuit matches the key produced by the circuit creator
264        key_hash: FunctionInput<F>,
265        /// Backend-specific proof type constant.
266        /// The proof field is agnostic and can come from witness inputs.
267        /// However, a backend may have many different verifiers which affect
268        /// the circuit construction.
269        /// In order for a backend to construct the correct recursive verifier
270        /// it expects the user to specify a proof type.
271        proof_type: u32,
272    },
273    /// BigInt addition
274    BigIntAdd { lhs: u32, rhs: u32, output: u32 },
275    /// BigInt subtraction
276    BigIntSub { lhs: u32, rhs: u32, output: u32 },
277    /// BigInt multiplication
278    BigIntMul { lhs: u32, rhs: u32, output: u32 },
279    /// BigInt division
280    BigIntDiv { lhs: u32, rhs: u32, output: u32 },
281    /// BigInt from le bytes
282    BigIntFromLeBytes { inputs: Vec<FunctionInput<F>>, modulus: Vec<u8>, output: u32 },
283    /// BigInt to le bytes
284    BigIntToLeBytes { input: u32, outputs: Vec<Witness> },
285    /// Applies the Poseidon2 permutation function to the given state,
286    /// outputting the permuted state.
287    Poseidon2Permutation {
288        /// Input state for the permutation of Poseidon2
289        inputs: Vec<FunctionInput<F>>,
290        /// Permuted state
291        outputs: Vec<Witness>,
292        /// State length (in number of field elements)
293        /// It is the length of inputs and outputs vectors
294        len: u32,
295    },
296    /// Applies the SHA-256 compression function to the input message
297    ///
298    /// # Arguments
299    ///
300    /// * `inputs` - input message block
301    /// * `hash_values` - state from the previous compression
302    /// * `outputs` - result of the input compressed into 256 bits
303    Sha256Compression {
304        /// 512 bits of the input message, represented by 16 u32s
305        inputs: Box<[FunctionInput<F>; 16]>,
306        /// Vector of 8 u32s used to compress the input
307        hash_values: Box<[FunctionInput<F>; 8]>,
308        /// Output of the compression, represented by 8 u32s
309        outputs: Box<[Witness; 8]>,
310    },
311}
312
313impl<F> BlackBoxFuncCall<F> {
314    pub fn get_black_box_func(&self) -> BlackBoxFunc {
315        match self {
316            BlackBoxFuncCall::AES128Encrypt { .. } => BlackBoxFunc::AES128Encrypt,
317            BlackBoxFuncCall::AND { .. } => BlackBoxFunc::AND,
318            BlackBoxFuncCall::XOR { .. } => BlackBoxFunc::XOR,
319            BlackBoxFuncCall::RANGE { .. } => BlackBoxFunc::RANGE,
320            BlackBoxFuncCall::Blake2s { .. } => BlackBoxFunc::Blake2s,
321            BlackBoxFuncCall::Blake3 { .. } => BlackBoxFunc::Blake3,
322            BlackBoxFuncCall::EcdsaSecp256k1 { .. } => BlackBoxFunc::EcdsaSecp256k1,
323            BlackBoxFuncCall::EcdsaSecp256r1 { .. } => BlackBoxFunc::EcdsaSecp256r1,
324            BlackBoxFuncCall::MultiScalarMul { .. } => BlackBoxFunc::MultiScalarMul,
325            BlackBoxFuncCall::EmbeddedCurveAdd { .. } => BlackBoxFunc::EmbeddedCurveAdd,
326            BlackBoxFuncCall::Keccakf1600 { .. } => BlackBoxFunc::Keccakf1600,
327            BlackBoxFuncCall::RecursiveAggregation { .. } => BlackBoxFunc::RecursiveAggregation,
328            BlackBoxFuncCall::BigIntAdd { .. } => BlackBoxFunc::BigIntAdd,
329            BlackBoxFuncCall::BigIntSub { .. } => BlackBoxFunc::BigIntSub,
330            BlackBoxFuncCall::BigIntMul { .. } => BlackBoxFunc::BigIntMul,
331            BlackBoxFuncCall::BigIntDiv { .. } => BlackBoxFunc::BigIntDiv,
332            BlackBoxFuncCall::BigIntFromLeBytes { .. } => BlackBoxFunc::BigIntFromLeBytes,
333            BlackBoxFuncCall::BigIntToLeBytes { .. } => BlackBoxFunc::BigIntToLeBytes,
334            BlackBoxFuncCall::Poseidon2Permutation { .. } => BlackBoxFunc::Poseidon2Permutation,
335            BlackBoxFuncCall::Sha256Compression { .. } => BlackBoxFunc::Sha256Compression,
336        }
337    }
338
339    pub fn name(&self) -> &str {
340        self.get_black_box_func().name()
341    }
342
343    pub fn get_outputs_vec(&self) -> Vec<Witness> {
344        match self {
345            BlackBoxFuncCall::Blake2s { outputs, .. }
346            | BlackBoxFuncCall::Blake3 { outputs, .. } => outputs.to_vec(),
347
348            BlackBoxFuncCall::Keccakf1600 { outputs, .. } => outputs.to_vec(),
349
350            BlackBoxFuncCall::Sha256Compression { outputs, .. } => outputs.to_vec(),
351
352            BlackBoxFuncCall::AES128Encrypt { outputs, .. }
353            | BlackBoxFuncCall::Poseidon2Permutation { outputs, .. } => outputs.to_vec(),
354
355            BlackBoxFuncCall::AND { output, .. }
356            | BlackBoxFuncCall::XOR { output, .. }
357            | BlackBoxFuncCall::EcdsaSecp256k1 { output, .. }
358            | BlackBoxFuncCall::EcdsaSecp256r1 { output, .. } => vec![*output],
359            BlackBoxFuncCall::MultiScalarMul { outputs, .. }
360            | BlackBoxFuncCall::EmbeddedCurveAdd { outputs, .. } => {
361                vec![outputs.0, outputs.1, outputs.2]
362            }
363            BlackBoxFuncCall::RANGE { .. }
364            | BlackBoxFuncCall::RecursiveAggregation { .. }
365            | BlackBoxFuncCall::BigIntFromLeBytes { .. }
366            | BlackBoxFuncCall::BigIntAdd { .. }
367            | BlackBoxFuncCall::BigIntSub { .. }
368            | BlackBoxFuncCall::BigIntMul { .. }
369            | BlackBoxFuncCall::BigIntDiv { .. } => {
370                vec![]
371            }
372            BlackBoxFuncCall::BigIntToLeBytes { outputs, .. } => outputs.to_vec(),
373        }
374    }
375}
376
377impl<F: Copy> BlackBoxFuncCall<F> {
378    pub fn get_inputs_vec(&self) -> Vec<FunctionInput<F>> {
379        match self {
380            BlackBoxFuncCall::Blake2s { inputs, .. }
381            | BlackBoxFuncCall::Blake3 { inputs, .. }
382            | BlackBoxFuncCall::BigIntFromLeBytes { inputs, .. }
383            | BlackBoxFuncCall::Poseidon2Permutation { inputs, .. } => inputs.to_vec(),
384
385            BlackBoxFuncCall::Keccakf1600 { inputs, .. } => inputs.to_vec(),
386            BlackBoxFuncCall::AES128Encrypt { inputs, iv, key, .. } => {
387                let mut all_inputs: Vec<FunctionInput<F>> =
388                    Vec::with_capacity(inputs.len() + iv.len() + key.len());
389                all_inputs.extend(**iv);
390                all_inputs.extend(**key);
391                all_inputs
392            }
393            BlackBoxFuncCall::Sha256Compression { inputs, hash_values, .. } => {
394                inputs.iter().chain(hash_values.as_ref()).copied().collect()
395            }
396            BlackBoxFuncCall::AND { lhs, rhs, .. } | BlackBoxFuncCall::XOR { lhs, rhs, .. } => {
397                vec![*lhs, *rhs]
398            }
399            BlackBoxFuncCall::BigIntAdd { .. }
400            | BlackBoxFuncCall::BigIntSub { .. }
401            | BlackBoxFuncCall::BigIntMul { .. }
402            | BlackBoxFuncCall::BigIntDiv { .. }
403            | BlackBoxFuncCall::BigIntToLeBytes { .. } => Vec::new(),
404            BlackBoxFuncCall::MultiScalarMul { points, scalars, .. } => {
405                let mut inputs: Vec<FunctionInput<F>> =
406                    Vec::with_capacity(points.len() + scalars.len());
407                inputs.extend(points.iter().copied());
408                inputs.extend(scalars.iter().copied());
409                inputs
410            }
411            BlackBoxFuncCall::EmbeddedCurveAdd { input1, input2, .. } => {
412                vec![input1[0], input1[1], input1[2], input2[0], input2[1], input2[2]]
413            }
414            BlackBoxFuncCall::RANGE { input } => vec![*input],
415            BlackBoxFuncCall::EcdsaSecp256k1 {
416                public_key_x,
417                public_key_y,
418                signature,
419                hashed_message,
420                ..
421            } => {
422                let mut inputs = Vec::with_capacity(
423                    public_key_x.len()
424                        + public_key_y.len()
425                        + signature.len()
426                        + hashed_message.len(),
427                );
428                inputs.extend(public_key_x.iter().copied());
429                inputs.extend(public_key_y.iter().copied());
430                inputs.extend(signature.iter().copied());
431                inputs.extend(hashed_message.iter().copied());
432                inputs
433            }
434            BlackBoxFuncCall::EcdsaSecp256r1 {
435                public_key_x,
436                public_key_y,
437                signature,
438                hashed_message,
439                ..
440            } => {
441                let mut inputs = Vec::with_capacity(
442                    public_key_x.len()
443                        + public_key_y.len()
444                        + signature.len()
445                        + hashed_message.len(),
446                );
447                inputs.extend(public_key_x.iter().copied());
448                inputs.extend(public_key_y.iter().copied());
449                inputs.extend(signature.iter().copied());
450                inputs.extend(hashed_message.iter().copied());
451                inputs
452            }
453            BlackBoxFuncCall::RecursiveAggregation {
454                verification_key: key,
455                proof,
456                public_inputs,
457                key_hash,
458                proof_type: _,
459            } => {
460                let mut inputs = Vec::new();
461                inputs.extend(key.iter().copied());
462                inputs.extend(proof.iter().copied());
463                inputs.extend(public_inputs.iter().copied());
464                inputs.push(*key_hash);
465                inputs
466            }
467        }
468    }
469
470    pub fn get_input_witnesses(&self) -> BTreeSet<Witness> {
471        let mut result = BTreeSet::new();
472        for input in self.get_inputs_vec() {
473            if let ConstantOrWitnessEnum::Witness(w) = input.input() {
474                result.insert(w);
475            }
476        }
477        result
478    }
479}
480
481impl<F: std::fmt::Display + Copy> std::fmt::Display for BlackBoxFuncCall<F> {
482    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
483        let uppercase_name = self.name().to_uppercase();
484        write!(f, "BLACKBOX::{uppercase_name} ")?;
485        // INPUTS
486
487        let inputs_str = &self
488            .get_inputs_vec()
489            .iter()
490            .map(|i| i.to_string())
491            .collect::<Vec<String>>()
492            .join(", ");
493
494        write!(f, "[{inputs_str}]")?;
495
496        write!(f, " ")?;
497
498        // OUTPUTS
499
500        let outputs_str = &self
501            .get_outputs_vec()
502            .iter()
503            .map(|i| format!("_{}", i.0))
504            .collect::<Vec<String>>()
505            .join(", ");
506
507        write!(f, "[{outputs_str}]")
508    }
509}
510
511impl<F: std::fmt::Display + Copy> std::fmt::Debug for BlackBoxFuncCall<F> {
512    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
513        std::fmt::Display::fmt(self, f)
514    }
515}
516
517fn serialize_big_array<S, F: Serialize>(
518    big_array: &[FunctionInput<F>; 64],
519    s: S,
520) -> Result<S::Ok, S::Error>
521where
522    S: Serializer,
523{
524    use serde_big_array::BigArray;
525
526    (*big_array).serialize(s)
527}
528
529fn deserialize_big_array_into_box<'de, D, F: Deserialize<'de>>(
530    deserializer: D,
531) -> Result<Box<[FunctionInput<F>; 64]>, D::Error>
532where
533    D: Deserializer<'de>,
534{
535    use serde_big_array::BigArray;
536
537    let big_array: [FunctionInput<F>; 64] = BigArray::deserialize(deserializer)?;
538    Ok(Box::new(big_array))
539}
540
541#[cfg(test)]
542mod tests {
543
544    use crate::{circuit::Opcode, native_types::Witness};
545    use acir_field::{AcirField, FieldElement};
546
547    use super::{BlackBoxFuncCall, FunctionInput};
548
549    fn keccakf1600_opcode<F: AcirField>() -> Opcode<F> {
550        let inputs: Box<[FunctionInput<F>; 25]> =
551            Box::new(std::array::from_fn(|i| FunctionInput::witness(Witness(i as u32 + 1), 8)));
552        let outputs: Box<[Witness; 25]> = Box::new(std::array::from_fn(|i| Witness(i as u32 + 26)));
553
554        Opcode::BlackBoxFuncCall(BlackBoxFuncCall::Keccakf1600 { inputs, outputs })
555    }
556
557    #[test]
558    fn keccakf1600_serialization_roundtrip() {
559        use crate::serialization::{bincode_deserialize, bincode_serialize};
560
561        let opcode = keccakf1600_opcode::<FieldElement>();
562        let buf = bincode_serialize(&opcode).unwrap();
563        let recovered_opcode = bincode_deserialize(&buf).unwrap();
564        assert_eq!(opcode, recovered_opcode);
565    }
566}
567
568#[cfg(feature = "arb")]
569mod arb {
570    use acir_field::AcirField;
571    use proptest::prelude::*;
572
573    use crate::native_types::Witness;
574
575    use super::{BlackBoxFuncCall, FunctionInput};
576
577    // Implementing this separately because trying to derive leads to stack overflow.
578    impl<F> Arbitrary for BlackBoxFuncCall<F>
579    where
580        F: AcirField + Arbitrary,
581    {
582        type Parameters = ();
583        type Strategy = BoxedStrategy<Self>;
584
585        fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
586            let input = any::<FunctionInput<F>>();
587            let input_vec = any::<Vec<FunctionInput<F>>>();
588            let input_arr_3 = any::<Box<[FunctionInput<F>; 3]>>();
589            let input_arr_8 = any::<Box<[FunctionInput<F>; 8]>>();
590            let input_arr_16 = any::<Box<[FunctionInput<F>; 16]>>();
591            let input_arr_25 = any::<Box<[FunctionInput<F>; 25]>>();
592            let input_arr_32 = any::<Box<[FunctionInput<F>; 32]>>();
593            let input_arr_64 = any::<Box<[FunctionInput<F>; 64]>>();
594            let witness = any::<Witness>();
595            let witness_vec = any::<Vec<Witness>>();
596            let witness_arr_8 = any::<Box<[Witness; 8]>>();
597            let witness_arr_25 = any::<Box<[Witness; 25]>>();
598            let witness_arr_32 = any::<Box<[Witness; 32]>>();
599
600            let case_aes128_encrypt = (
601                input_vec.clone(),
602                input_arr_16.clone(),
603                input_arr_16.clone(),
604                witness_vec.clone(),
605            )
606                .prop_map(|(inputs, iv, key, outputs)| {
607                    BlackBoxFuncCall::AES128Encrypt { inputs, iv, key, outputs }
608                });
609
610            let case_and = (input.clone(), input.clone(), witness.clone())
611                .prop_map(|(lhs, rhs, output)| BlackBoxFuncCall::AND { lhs, rhs, output });
612
613            let case_xor = (input.clone(), input.clone(), witness.clone())
614                .prop_map(|(lhs, rhs, output)| BlackBoxFuncCall::XOR { lhs, rhs, output });
615
616            let case_range = input.clone().prop_map(|input| BlackBoxFuncCall::RANGE { input });
617
618            let case_blake2s = (input_vec.clone(), witness_arr_32.clone())
619                .prop_map(|(inputs, outputs)| BlackBoxFuncCall::Blake2s { inputs, outputs });
620
621            let case_blake3 = (input_vec.clone(), witness_arr_32.clone())
622                .prop_map(|(inputs, outputs)| BlackBoxFuncCall::Blake3 { inputs, outputs });
623
624            let case_ecdsa_secp256k1 = (
625                input_arr_32.clone(),
626                input_arr_32.clone(),
627                input_arr_64.clone(),
628                input_arr_32.clone(),
629                witness.clone(),
630            )
631                .prop_map(
632                    |(public_key_x, public_key_y, signature, hashed_message, output)| {
633                        BlackBoxFuncCall::EcdsaSecp256k1 {
634                            public_key_x,
635                            public_key_y,
636                            signature,
637                            hashed_message,
638                            output,
639                        }
640                    },
641                );
642
643            let case_ecdsa_secp256r1 = (
644                input_arr_32.clone(),
645                input_arr_32.clone(),
646                input_arr_64.clone(),
647                input_arr_32.clone(),
648                witness.clone(),
649            )
650                .prop_map(
651                    |(public_key_x, public_key_y, signature, hashed_message, output)| {
652                        BlackBoxFuncCall::EcdsaSecp256r1 {
653                            public_key_x,
654                            public_key_y,
655                            signature,
656                            hashed_message,
657                            output,
658                        }
659                    },
660                );
661
662            let case_multi_scalar_mul = (
663                input_vec.clone(),
664                input_vec.clone(),
665                witness.clone(),
666                witness.clone(),
667                witness.clone(),
668            )
669                .prop_map(|(points, scalars, w1, w2, w3)| {
670                    BlackBoxFuncCall::MultiScalarMul { points, scalars, outputs: (w1, w2, w3) }
671                });
672
673            let case_embedded_curve_add = (
674                input_arr_3.clone(),
675                input_arr_3.clone(),
676                witness.clone(),
677                witness.clone(),
678                witness.clone(),
679            )
680                .prop_map(|(input1, input2, w1, w2, w3)| {
681                    BlackBoxFuncCall::EmbeddedCurveAdd { input1, input2, outputs: (w1, w2, w3) }
682                });
683
684            let case_keccakf1600 = (input_arr_25.clone(), witness_arr_25.clone())
685                .prop_map(|(inputs, outputs)| BlackBoxFuncCall::Keccakf1600 { inputs, outputs });
686
687            let case_recursive_aggregation = (
688                input_vec.clone(),
689                input_vec.clone(),
690                input_vec.clone(),
691                input.clone(),
692                any::<u32>(),
693            )
694                .prop_map(
695                    |(verification_key, proof, public_inputs, key_hash, proof_type)| {
696                        BlackBoxFuncCall::RecursiveAggregation {
697                            verification_key,
698                            proof,
699                            public_inputs,
700                            key_hash,
701                            proof_type,
702                        }
703                    },
704                );
705
706            let big_int_args = (any::<u32>(), any::<u32>(), any::<u32>());
707
708            let case_big_int_add = big_int_args
709                .prop_map(|(lhs, rhs, output)| BlackBoxFuncCall::BigIntAdd { lhs, rhs, output });
710
711            let case_big_int_sub = big_int_args
712                .prop_map(|(lhs, rhs, output)| BlackBoxFuncCall::BigIntSub { lhs, rhs, output });
713
714            let case_big_int_mul = big_int_args
715                .prop_map(|(lhs, rhs, output)| BlackBoxFuncCall::BigIntMul { lhs, rhs, output });
716
717            let case_big_int_div = big_int_args
718                .prop_map(|(lhs, rhs, output)| BlackBoxFuncCall::BigIntDiv { lhs, rhs, output });
719
720            let case_big_int_from_le_bytes = (input_vec.clone(), any::<Vec<u8>>(), any::<u32>())
721                .prop_map(|(inputs, modulus, output)| BlackBoxFuncCall::BigIntFromLeBytes {
722                    inputs,
723                    modulus,
724                    output,
725                });
726
727            let case_big_int_to_le_bytes = (any::<u32>(), witness_vec.clone())
728                .prop_map(|(input, outputs)| BlackBoxFuncCall::BigIntToLeBytes { input, outputs });
729
730            let case_poseidon2_permutation = (input_vec.clone(), witness_vec.clone(), any::<u32>())
731                .prop_map(|(inputs, outputs, len)| BlackBoxFuncCall::Poseidon2Permutation {
732                    inputs,
733                    outputs,
734                    len,
735                });
736
737            let case_sha256_compression = (input_arr_16, input_arr_8, witness_arr_8).prop_map(
738                |(inputs, hash_values, outputs)| BlackBoxFuncCall::Sha256Compression {
739                    inputs,
740                    hash_values,
741                    outputs,
742                },
743            );
744
745            prop_oneof![
746                case_aes128_encrypt,
747                case_and,
748                case_xor,
749                case_range,
750                case_blake2s,
751                case_blake3,
752                case_ecdsa_secp256k1,
753                case_ecdsa_secp256r1,
754                case_multi_scalar_mul,
755                case_embedded_curve_add,
756                case_keccakf1600,
757                case_recursive_aggregation,
758                case_big_int_add,
759                case_big_int_sub,
760                case_big_int_mul,
761                case_big_int_div,
762                case_big_int_from_le_bytes,
763                case_big_int_to_le_bytes,
764                case_poseidon2_permutation,
765                case_sha256_compression,
766            ]
767            .boxed()
768        }
769    }
770}