diff --git a/acvm-repo/acir/README.md b/acvm-repo/acir/README.md index a0e7094b4d6..12f5ea52c96 100644 --- a/acvm-repo/acir/README.md +++ b/acvm-repo/acir/README.md @@ -211,27 +211,29 @@ The black box functions supported by ACIR are: **Sha256Compression**: SHA256 compression function **RecursiveAggregation**: verify a proof inside the circuit. -**Warning: this opcode is subject to change.** -This black box function does not fully verify a proof, what it does is verify -that the provided `key_hash` is indeed a hash of `verification_key`, allowing -the user to use the verification key as private inputs and only have the -`key_hash` as public input, which is more performant. +Computes a recursive aggregation object internally when verifying a proof inside +another circuit. +The outputted aggregation object will then be either checked in a +top-level verifier or aggregated upon again. +The aggregation object should be maintained by the backend implementer. -Another thing that it does is preparing the verification of the proof. In order -to fully verify a proof, some operations may still be required to be done by the -final verifier. This is why this black box function does not say if verification -is passing or not. +This opcode prepares the verification of the final proof. +In order to fully verify a recursive proof, some operations may still be required +to be done by the final verifier (e.g. a pairing check). +This is why this black box function does not say if verification is passing or not. +It delays the expensive part of verification out of the SNARK +and leaves it to the final verifier outside of the SNARK circuit. -If you have several proofs to verify in one ACIR program, you would call -`RecursiveAggregation()` multiple times while passing the -`output_aggregation_object` as `input_aggregation_object` to the next -`RecursiveAggregation()` call, except for the first call where you do not have -any `input_aggregation_object`. +This opcode also verifies that the key_hash is indeed a hash of verification_key, +allowing the user to use the verification key as private inputs and only +have the key_hash as public input, which is more performant. -If one of the proof you verify with the black box function does not verify, then -the verification of the proof of the main ACIR program will ultimately fail. +**Warning: the key hash logic does not need to be part of the black box and subject to be removed.** +If one of the recursive proofs you verify with the black box function fails to +verify, then the verification of the final proof of the main ACIR program will +ultimately fail. ### Brillig diff --git a/acvm-repo/acir/src/circuit/black_box_functions.rs b/acvm-repo/acir/src/circuit/black_box_functions.rs index d0ec7d02201..01a7a1da07a 100644 --- a/acvm-repo/acir/src/circuit/black_box_functions.rs +++ b/acvm-repo/acir/src/circuit/black_box_functions.rs @@ -6,155 +6,51 @@ use serde::{Deserialize, Serialize}; use strum_macros::EnumIter; +/// Representation of available black box function names. +/// This enum should be used to represent a black box before we have set up the +/// appropriate inputs and outputs. At which point it should be converted to a [crate::circuit::opcodes::BlackBoxFuncCall] #[allow(clippy::upper_case_acronyms)] #[derive(Clone, Debug, Hash, Copy, PartialEq, Eq, Serialize, Deserialize, EnumIter)] pub enum BlackBoxFunc { - /// Ciphers (encrypts) the provided plaintext using AES128 in CBC mode, - /// padding the input using PKCS#7. - /// - inputs: byte array `[u8; N]` - /// - iv: initialization vector `[u8; 16]` - /// - key: user key `[u8; 16]` - /// - outputs: byte vector `[u8]` of length `input.len() + (16 - input.len() % 16)` + /// More details can be found at [crate::circuit::opcodes::BlackBoxFuncCall::AES128Encrypt] AES128Encrypt, - - /// Performs the bitwise AND of `lhs` and `rhs`. `bit_size` must be the same for - /// both inputs. - /// - lhs: (witness, bit_size) - /// - rhs: (witness, bit_size) - /// - output: a witness whose value is constrained to be lhs AND rhs, as - /// bit_size bit integers + /// More details can be found at [crate::circuit::opcodes::BlackBoxFuncCall::AND] AND, - - /// Performs the bitwise XOR of `lhs` and `rhs`. `bit_size` must be the same for - /// both inputs. - /// - lhs: (witness, bit_size) - /// - rhs: (witness, bit_size) - /// - output: a witness whose value is constrained to be lhs XOR rhs, as - /// bit_size bit integers + /// More details can be found at [crate::circuit::opcodes::BlackBoxFuncCall::XOR] XOR, - - /// Range constraint to ensure that a witness - /// can be represented in the specified number of bits. - /// - input: (witness, bit_size) + /// More details can be found at [crate::circuit::opcodes::BlackBoxFuncCall::RANGE] RANGE, - - /// Computes the Blake2s hash of the inputs, as specified in - /// https://tools.ietf.org/html/rfc7693 - /// - inputs are a byte array, i.e a vector of (witness, 8) - /// - output is a byte array of length 32, i.e. an array of 32 - /// (witness, 8), constrained to be the blake2s of the inputs. + /// More details can be found at [crate::circuit::opcodes::BlackBoxFuncCall::Blake2s] Blake2s, - - /// Computes the Blake3 hash of the inputs - /// - inputs are a byte array, i.e a vector of (witness, 8) - /// - output is a byte array of length 32, i.e an array of 32 - /// (witness, 8), constrained to be the blake3 of the inputs. + /// More details can be found at [crate::circuit::opcodes::BlackBoxFuncCall::Blake3] Blake3, - - /// Verifies a ECDSA signature over the secp256k1 curve. - /// - inputs: - /// - x coordinate of public key as 32 bytes - /// - y coordinate of public key as 32 bytes - /// - the signature, as a 64 bytes array - /// - the hash of the message, as a vector of bytes - /// - output: 0 for failure and 1 for success + /// More details can be found at [crate::circuit::opcodes::BlackBoxFuncCall::EcdsaSecp256k1] EcdsaSecp256k1, - - /// Verifies a ECDSA signature over the secp256r1 curve. - /// - /// Same as EcdsaSecp256k1, but done over another curve. + /// More details can be found at [crate::circuit::opcodes::BlackBoxFuncCall::EcdsaSecp256r1] EcdsaSecp256r1, - - /// Multiple scalar multiplication (MSM) with a variable base/input point - /// (P) of the embedded curve. An MSM multiplies the points and scalars and - /// sums the results. - /// - input: - /// points (witness, N) a vector of x and y coordinates of input - /// points `[x1, y1, x2, y2,...]`. - /// scalars (witness, N) a vector of low and high limbs of input - /// scalars `[s1_low, s1_high, s2_low, s2_high, ...]`. (witness, N) - /// For Barretenberg, they must both be less than 128 bits. - /// - output: - /// a tuple of `x` and `y` coordinates of output. - /// Points computed as `s_low*P+s_high*2^{128}*P` - /// - /// Because the Grumpkin scalar field is bigger than the ACIR field, we - /// provide 2 ACIR fields representing the low and high parts of the Grumpkin - /// scalar $a$: `a=low+high*2^{128}`, with `low, high < 2^{128}` + /// More details can be found at [crate::circuit::opcodes::BlackBoxFuncCall::MultiScalarMul] MultiScalarMul, - - /// Keccak Permutation function of width 1600 - /// - inputs: An array of 25 64-bit Keccak lanes that represent a keccak sponge of 1600 bits - /// - outputs: The result of a keccak f1600 permutation on the input state. Also an array of 25 Keccak lanes. + /// More details can be found at [crate::circuit::opcodes::BlackBoxFuncCall::Keccakf1600] Keccakf1600, - - /// Compute a recursive aggregation object when verifying a proof inside - /// another circuit. - /// This outputted aggregation object will then be either checked in a - /// top-level verifier or aggregated upon again. - /// - /// **Warning: this opcode is subject to change.** - /// Note that the `254` in `(witness, 254)` refers to the upper bound of - /// the `witness`. - /// - verification_key: Vector of (witness, 254) representing the - /// verification key of the circuit being verified - /// - public_inputs: Vector of (witness, 254) representing the public - /// inputs corresponding to the proof being verified - /// - key_hash: one (witness, 254). It should be the hash of the - /// verification key. Barretenberg expects the Pedersen hash of the - /// verification key - /// - /// Another thing that it does is preparing the verification of the proof. - /// In order to fully verify a proof, some operations may still be required - /// to be done by the final verifier. This is why this black box function - /// does not say if verification is passing or not. - /// - /// This black box function does not fully verify a proof, what it does is - /// verifying that the key_hash is indeed a hash of verification_key, - /// allowing the user to use the verification key as private inputs and only - /// have the key_hash as public input, which is more performant. - /// - /// If one of the recursive proofs you verify with the black box function does not - /// verify, then the verification of the proof of the main ACIR program will - /// ultimately fail. + /// More details can be found at [crate::circuit::opcodes::BlackBoxFuncCall::RecursiveAggregation] RecursiveAggregation, - - /// Addition over the embedded curve on which the witness is defined - /// The opcode makes the following assumptions but does not enforce them because - /// it is more efficient to do it only when required. For instance, adding two - /// points that are on the curve it guarantee to give a point on the curve. - /// - /// It assumes that the points are on the curve. - /// If the inputs are the same witnesses index, it will perform a doubling, - /// If not, it assumes that the points' x-coordinates are not equal. - /// It also assumes neither point is the infinity point. + /// More details can be found at [crate::circuit::opcodes::BlackBoxFuncCall::EmbeddedCurveAdd] EmbeddedCurveAdd, - - /// BigInt addition + /// More details can be found at [crate::circuit::opcodes::BlackBoxFuncCall::BigIntAdd] BigIntAdd, - - /// BigInt subtraction + /// More details can be found at [crate::circuit::opcodes::BlackBoxFuncCall::BigIntSub] BigIntSub, - - /// BigInt multiplication + /// More details can be found at [crate::circuit::opcodes::BlackBoxFuncCall::BigIntMul] BigIntMul, - - /// BigInt division + /// More details can be found at [crate::circuit::opcodes::BlackBoxFuncCall::BigIntDiv] BigIntDiv, - - /// BigInt from le bytes + /// More details can be found at [crate::circuit::opcodes::BlackBoxFuncCall::BigIntFromLeBytes] BigIntFromLeBytes, - - /// BigInt to le bytes + /// More details can be found at [crate::circuit::opcodes::BlackBoxFuncCall::BigIntToLeBytes] BigIntToLeBytes, - - /// Permutation function of Poseidon2 + /// More details can be found at [crate::circuit::opcodes::BlackBoxFuncCall::Poseidon2Permutation] Poseidon2Permutation, - - /// SHA256 compression function - /// - input: [(witness, 32); 16] - /// - state: [(witness, 32); 8] - /// - output: [(witness, 32); 8] + /// More details can be found at [crate::circuit::opcodes::BlackBoxFuncCall::Sha256Compression] Sha256Compression, } diff --git a/acvm-repo/acir/src/circuit/opcodes/black_box_function_call.rs b/acvm-repo/acir/src/circuit/opcodes/black_box_function_call.rs index da70e9b6eb5..1a14971efdf 100644 --- a/acvm-repo/acir/src/circuit/opcodes/black_box_function_call.rs +++ b/acvm-repo/acir/src/circuit/opcodes/black_box_function_call.rs @@ -83,33 +83,62 @@ impl std::fmt::Display for FunctionInput { #[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Hash)] pub enum BlackBoxFuncCall { + /// Ciphers (encrypts) the provided plaintext using AES128 in CBC mode, + /// padding the input using PKCS#7. + /// - inputs: byte array `[u8; N]` + /// - iv: initialization vector `[u8; 16]` + /// - key: user key `[u8; 16]` + /// - outputs: byte vector `[u8]` of length `input.len() + (16 - input.len() % 16)` AES128Encrypt { inputs: Vec>, iv: Box<[FunctionInput; 16]>, key: Box<[FunctionInput; 16]>, outputs: Vec, }, - AND { - lhs: FunctionInput, - rhs: FunctionInput, - output: Witness, - }, - XOR { - lhs: FunctionInput, - rhs: FunctionInput, - output: Witness, - }, - RANGE { - input: FunctionInput, - }, - Blake2s { - inputs: Vec>, - outputs: Box<[Witness; 32]>, - }, - Blake3 { - inputs: Vec>, - outputs: Box<[Witness; 32]>, - }, + /// Performs the bitwise AND of `lhs` and `rhs`. `bit_size` must be the same for + /// both inputs. + /// - lhs: (witness, bit_size) + /// - rhs: (witness, bit_size) + /// - output: a witness whose value is constrained to be lhs AND rhs, as + /// bit_size bit integers + AND { lhs: FunctionInput, rhs: FunctionInput, output: Witness }, + /// Performs the bitwise XOR of `lhs` and `rhs`. `bit_size` must be the same for + /// both inputs. + /// - lhs: (witness, bit_size) + /// - rhs: (witness, bit_size) + /// - output: a witness whose value is constrained to be lhs XOR rhs, as + /// bit_size bit integers + XOR { lhs: FunctionInput, rhs: FunctionInput, output: Witness }, + /// Range constraint to ensure that a witness + /// can be represented in the specified number of bits. + /// - input: (witness, bit_size) + RANGE { input: FunctionInput }, + /// Computes the Blake2s hash of the inputs, as specified in + /// https://tools.ietf.org/html/rfc7693 + /// - inputs are a byte array, i.e a vector of (witness, 8) + /// - output is a byte array of length 32, i.e. an array of 32 + /// (witness, 8), constrained to be the blake2s of the inputs. + Blake2s { inputs: Vec>, outputs: Box<[Witness; 32]> }, + /// Computes the Blake3 hash of the inputs + /// - inputs are a byte array, i.e a vector of (witness, 8) + /// - output is a byte array of length 32, i.e an array of 32 + /// (witness, 8), constrained to be the blake3 of the inputs. + Blake3 { inputs: Vec>, outputs: Box<[Witness; 32]> }, + /// Verifies a ECDSA signature over the secp256k1 curve. + /// - inputs: + /// - x coordinate of public key as 32 bytes + /// - y coordinate of public key as 32 bytes + /// - the signature, as a 64 bytes array + /// The signature internally will be represented as `(r, s)`, + /// where `r` and `s` are fixed-sized big endian scalar values. + /// As the `secp256k1` has a 256-bit modulus, we have a 64 byte signature + /// while `r` and `s` will both be 32 bytes. + /// We expect `s` to be normalized. This means given the curve's order, + /// `s` should be less than or equal to `order / 2`. + /// This is done to prevent malleability. + /// For more context regarding malleability you can reference BIP 0062. + /// - the hash of the message, as a vector of bytes + /// - output: 0 for failure and 1 for success EcdsaSecp256k1 { public_key_x: Box<[FunctionInput; 32]>, public_key_y: Box<[FunctionInput; 32]>, @@ -121,6 +150,9 @@ pub enum BlackBoxFuncCall { hashed_message: Box<[FunctionInput; 32]>, output: Witness, }, + /// Verifies a ECDSA signature over the secp256r1 curve. + /// + /// Same as EcdsaSecp256k1, but done over another curve. EcdsaSecp256r1 { public_key_x: Box<[FunctionInput; 32]>, public_key_y: Box<[FunctionInput; 32]>, @@ -132,21 +164,69 @@ pub enum BlackBoxFuncCall { hashed_message: Box<[FunctionInput; 32]>, output: Witness, }, + /// Multiple scalar multiplication (MSM) with a variable base/input point + /// (P) of the embedded curve. An MSM multiplies the points and scalars and + /// sums the results. + /// - input: + /// points (witness, N) a vector of x and y coordinates of input + /// points `[x1, y1, x2, y2,...]`. + /// scalars (witness, N) a vector of low and high limbs of input + /// scalars `[s1_low, s1_high, s2_low, s2_high, ...]`. (witness, N) + /// For Barretenberg, they must both be less than 128 bits. + /// - output: + /// a tuple of `x` and `y` coordinates of output. + /// Points computed as `s_low*P+s_high*2^{128}*P` + /// + /// Because the Grumpkin scalar field is bigger than the ACIR field, we + /// provide 2 ACIR fields representing the low and high parts of the Grumpkin + /// scalar $a$: `a=low+high*2^{128}`, with `low, high < 2^{128}` MultiScalarMul { points: Vec>, scalars: Vec>, outputs: (Witness, Witness, Witness), }, + /// Addition over the embedded curve on which the witness is defined + /// The opcode makes the following assumptions but does not enforce them because + /// it is more efficient to do it only when required. For instance, adding two + /// points that are on the curve it guarantee to give a point on the curve. + /// + /// It assumes that the points are on the curve. + /// If the inputs are the same witnesses index, it will perform a doubling, + /// If not, it assumes that the points' x-coordinates are not equal. + /// It also assumes neither point is the infinity point. EmbeddedCurveAdd { input1: Box<[FunctionInput; 3]>, input2: Box<[FunctionInput; 3]>, outputs: (Witness, Witness, Witness), }, - Keccakf1600 { - inputs: Box<[FunctionInput; 25]>, - outputs: Box<[Witness; 25]>, - }, + /// Keccak Permutation function of width 1600 + /// - inputs: An array of 25 64-bit Keccak lanes that represent a keccak sponge of 1600 bits + /// - outputs: The result of a keccak f1600 permutation on the input state. Also an array of 25 Keccak lanes. + Keccakf1600 { inputs: Box<[FunctionInput; 25]>, outputs: Box<[Witness; 25]> }, + /// Computes a recursive aggregation object when verifying a proof inside + /// another circuit. + /// The outputted aggregation object will then be either checked in a + /// top-level verifier or aggregated upon again. + /// The aggregation object should be maintained by the backend implementer. + /// + /// This opcode prepares the verification of the final proof. + /// In order to fully verify a recursive proof, some operations may still be required + /// to be done by the final verifier (e.g. a pairing check). + /// This is why this black box function does not say if verification is passing or not. + /// It delays the expensive part of verification out of the SNARK + /// and leaves it to the final verifier outside of the SNARK circuit. + /// + /// This opcode also verifies that the key_hash is indeed a hash of verification_key, + /// allowing the user to use the verification key as private inputs and only + /// have the key_hash as public input, which is more performant. + /// + /// **Warning: the key hash logic does not need to be part of the black box and subject to be removed.** + /// + /// If one of the recursive proofs you verify with the black box function fails to + /// verify, then the verification of the final proof of the main ACIR program will + /// ultimately fail. RecursiveAggregation { + /// Verification key of the circuit being verified verification_key: Vec>, proof: Vec>, /// These represent the public inputs of the proof we are verifying @@ -157,37 +237,26 @@ pub enum BlackBoxFuncCall { /// The circuit implementing this opcode can use this hash to ensure that the /// key provided to the circuit matches the key produced by the circuit creator key_hash: FunctionInput, + /// Backend-specific proof type constant. + /// The proof field is agnostic and can come from witness inputs. + /// However, a backend may have many different verifiers which affect + /// the circuit construction. + /// In order for a backend to construct the correct recursive verifier + /// it expects the user to specify a proof type. proof_type: u32, }, - BigIntAdd { - lhs: u32, - rhs: u32, - output: u32, - }, - BigIntSub { - lhs: u32, - rhs: u32, - output: u32, - }, - BigIntMul { - lhs: u32, - rhs: u32, - output: u32, - }, - BigIntDiv { - lhs: u32, - rhs: u32, - output: u32, - }, - BigIntFromLeBytes { - inputs: Vec>, - modulus: Vec, - output: u32, - }, - BigIntToLeBytes { - input: u32, - outputs: Vec, - }, + /// BigInt addition + BigIntAdd { lhs: u32, rhs: u32, output: u32 }, + /// BigInt subtraction + BigIntSub { lhs: u32, rhs: u32, output: u32 }, + /// BigInt multiplication + BigIntMul { lhs: u32, rhs: u32, output: u32 }, + /// BigInt division + BigIntDiv { lhs: u32, rhs: u32, output: u32 }, + /// BigInt from le bytes + BigIntFromLeBytes { inputs: Vec>, modulus: Vec, output: u32 }, + /// BigInt to le bytes + BigIntToLeBytes { input: u32, outputs: Vec }, /// Applies the Poseidon2 permutation function to the given state, /// outputting the permuted state. Poseidon2Permutation { diff --git a/cspell.json b/cspell.json index ece6836ac8d..58ece5bdef3 100644 --- a/cspell.json +++ b/cspell.json @@ -180,6 +180,7 @@ "pedersen", "peekable", "petgraph", + "PKCS", "plonkc", "PLONKish", "pprof", diff --git a/noir_stdlib/src/ecdsa_secp256k1.nr b/noir_stdlib/src/ecdsa_secp256k1.nr index 1b0f32343b9..88fa0a5580b 100644 --- a/noir_stdlib/src/ecdsa_secp256k1.nr +++ b/noir_stdlib/src/ecdsa_secp256k1.nr @@ -1,5 +1,21 @@ +// TODO(https://github.com/noir-lang/noir/issues/7711): Switch the regular comments to doc comments once they do not trigger a warning anymore #[foreign(ecdsa_secp256k1)] // docs:start:ecdsa_secp256k1 +// Verifies a ECDSA signature over the secp256k1 curve. +// - inputs: +// - x coordinate of public key as 32 bytes +// - y coordinate of public key as 32 bytes +// - the signature, as a 64 bytes array +// The signature internally will be represented as `(r, s)`, +// where `r` and `s` are fixed-sized big endian scalar values. +// As the `secp256k1` has a 256-bit modulus, we have a 64 byte signature +// while `r` and `s` will both be 32 bytes. +// We expect `s` to be normalized. This means given the curve's order, +// `s` should be less than or equal to `order / 2`. +// This is done to prevent malleability. +// For more context regarding malleability you can reference BIP 0062. +// - the hash of the message, as a vector of bytes +// - output: false for failure and true for success pub fn verify_signature( public_key_x: [u8; 32], public_key_y: [u8; 32],