From 31ef7c336665b005c98a993097b5a7c8149ff5cd Mon Sep 17 00:00:00 2001 From: sirasistant Date: Mon, 8 May 2023 12:06:20 +0000 Subject: [PATCH 01/15] feat: use enum for bb call --- acir/src/circuit/mod.rs | 17 +- acir/src/circuit/opcodes.rs | 6 +- .../opcodes/black_box_function_call.rs | 337 +++++++++++++----- .../compiler/optimizers/redundant_range.rs | 19 +- acvm/src/compiler/transformers/fallback.rs | 34 +- acvm/src/lib.rs | 50 +-- acvm/src/pwg/blackbox.rs | 51 +-- acvm/src/pwg/hash.rs | 35 +- acvm/src/pwg/logic.rs | 50 ++- acvm/src/pwg/range.rs | 25 +- acvm/src/pwg/signature/ecdsa.rs | 9 +- 11 files changed, 376 insertions(+), 257 deletions(-) diff --git a/acir/src/circuit/mod.rs b/acir/src/circuit/mod.rs index ed6929521..1f2a1a993 100644 --- a/acir/src/circuit/mod.rs +++ b/acir/src/circuit/mod.rs @@ -168,20 +168,15 @@ mod test { use acir_field::FieldElement; fn and_opcode() -> Opcode { - Opcode::BlackBoxFuncCall(BlackBoxFuncCall { - name: crate::BlackBoxFunc::AND, - inputs: vec![ - FunctionInput { witness: Witness(1), num_bits: 4 }, - FunctionInput { witness: Witness(2), num_bits: 4 }, - ], - outputs: vec![Witness(3)], + Opcode::BlackBoxFuncCall(BlackBoxFuncCall::AND { + lhs: FunctionInput { witness: Witness(1), num_bits: 4 }, + rhs: FunctionInput { witness: Witness(2), num_bits: 4 }, + output: Witness(3), }) } fn range_opcode() -> Opcode { - Opcode::BlackBoxFuncCall(BlackBoxFuncCall { - name: crate::BlackBoxFunc::RANGE, - inputs: vec![FunctionInput { witness: Witness(1), num_bits: 8 }], - outputs: vec![], + Opcode::BlackBoxFuncCall(BlackBoxFuncCall::RANGE { + input: FunctionInput { witness: Witness(1), num_bits: 8 }, }) } fn oracle_opcode() -> Opcode { diff --git a/acir/src/circuit/opcodes.rs b/acir/src/circuit/opcodes.rs index b05884b78..1c91e9a76 100644 --- a/acir/src/circuit/opcodes.rs +++ b/acir/src/circuit/opcodes.rs @@ -43,7 +43,7 @@ impl Opcode { match self { Opcode::Arithmetic(_) => "arithmetic", Opcode::Directive(directive) => directive.name(), - Opcode::BlackBoxFuncCall(g) => g.name.name(), + Opcode::BlackBoxFuncCall(g) => g.get_black_box_func().name(), Opcode::Block(_) => "block", Opcode::RAM(_) => "ram", Opcode::ROM(_) => "rom", @@ -228,7 +228,6 @@ impl std::fmt::Debug for Opcode { #[test] fn serialization_roundtrip() { use crate::native_types::Witness; - use crate::BlackBoxFunc; fn read_write(opcode: Opcode) -> (Opcode, Opcode) { let mut bytes = Vec::new(); @@ -239,8 +238,7 @@ fn serialization_roundtrip() { let opcode_arith = Opcode::Arithmetic(Expression::default()); - let opcode_black_box_func = Opcode::BlackBoxFuncCall(BlackBoxFuncCall { - name: BlackBoxFunc::AES, + let opcode_black_box_func = Opcode::BlackBoxFuncCall(BlackBoxFuncCall::AES { inputs: vec![ FunctionInput { witness: Witness(1u32), num_bits: 12 }, FunctionInput { witness: Witness(24u32), num_bits: 32 }, diff --git a/acir/src/circuit/opcodes/black_box_function_call.rs b/acir/src/circuit/opcodes/black_box_function_call.rs index 0dfc23789..a71b473a7 100644 --- a/acir/src/circuit/opcodes/black_box_function_call.rs +++ b/acir/src/circuit/opcodes/black_box_function_call.rs @@ -7,139 +7,296 @@ use serde::{Deserialize, Serialize}; // Note: Some functions will not use all of the witness // So we need to supply how many bits of the witness is needed -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct FunctionInput { pub witness: Witness, pub num_bits: u32, } #[derive(Clone, PartialEq, Eq, Serialize, Deserialize)] -pub struct BlackBoxFuncCall { - pub name: BlackBoxFunc, - pub inputs: Vec, - pub outputs: Vec, +pub enum BlackBoxFuncCall { + #[allow(clippy::upper_case_acronyms)] + AES{ inputs: Vec, outputs: Vec }, + AND{ lhs: FunctionInput, rhs: FunctionInput, output: Witness }, + XOR{ lhs: FunctionInput, rhs: FunctionInput, output: Witness }, + RANGE{ input: FunctionInput }, + SHA256{ inputs: Vec, outputs: Vec }, + Blake2s{ inputs: Vec, outputs: Vec }, + ComputeMerkleRoot{ inputs: Vec, output: Witness }, + SchnorrVerify{ inputs: Vec, output: Witness }, + Pedersen{ + hash_index: u32, + inputs: Vec, + outputs: Vec + }, + // 128 here specifies that this function + // should have 128 bits of security + HashToField128Security{ inputs: Vec, output: Witness }, + EcdsaSecp256k1{ inputs: Vec, output: Witness }, + FixedBaseScalarMul{ input: FunctionInput, outputs: Vec }, + Keccak256{ inputs: Vec, outputs: Vec }, +} + +fn write_input(input: &FunctionInput, mut writer: W) -> std::io::Result<()> { + write_u32(&mut writer, input.witness.witness_index())?; + write_u32(&mut writer, input.num_bits)?; + Ok(()) +} + +fn write_inputs(inputs: &Vec, mut writer: W) -> std::io::Result<()> { + let num_inputs = inputs.len() as u32; + write_u32(&mut writer, num_inputs)?; + + for input in inputs { + write_input(input, &mut writer)?; + } + + Ok(()) +} + +fn write_outputs(outputs: &Vec, mut writer: W) -> std::io::Result<()> { + let num_inputs = outputs.len() as u32; + write_u32(&mut writer, num_inputs)?; + + for output in outputs { + write_u32(&mut writer, output.witness_index())?; + } + + Ok(()) +} + +fn read_input(mut reader: R) -> std::io::Result { + let witness_index = read_u32(&mut reader)?; + let num_bits = read_u32(&mut reader)?; + Ok(FunctionInput{ witness: Witness::new(witness_index), num_bits }) +} + +fn read_inputs(mut reader: R) -> std::io::Result> { + let num_inputs = read_u32(&mut reader)?; + + let mut inputs = Vec::with_capacity(num_inputs as usize); + + for _ in 0..num_inputs { + inputs.push(read_input(&mut reader)?); + } + + Ok(inputs) +} + +fn read_outputs(mut reader: R) -> std::io::Result> { + let num_inputs = read_u32(&mut reader)?; + + let mut inputs = Vec::with_capacity(num_inputs as usize); + + for _ in 0..num_inputs { + let witness_index = read_u32(&mut reader)?; + inputs.push(Witness::new(witness_index)); + } + + Ok(inputs) } impl BlackBoxFuncCall { - pub fn write(&self, mut writer: W) -> std::io::Result<()> { - write_u16(&mut writer, self.name.to_u16())?; + pub fn get_black_box_func(&self) -> BlackBoxFunc { + match self { + BlackBoxFuncCall::AES {..} => BlackBoxFunc::AES, + BlackBoxFuncCall::AND {..} => BlackBoxFunc::AND, + BlackBoxFuncCall::XOR {..} => BlackBoxFunc::XOR, + BlackBoxFuncCall::RANGE {..} =>BlackBoxFunc::RANGE, + BlackBoxFuncCall::SHA256 {..} => BlackBoxFunc::SHA256, + BlackBoxFuncCall::Blake2s {..} =>BlackBoxFunc::Blake2s, + BlackBoxFuncCall::ComputeMerkleRoot {..} => BlackBoxFunc::ComputeMerkleRoot, + BlackBoxFuncCall::SchnorrVerify {..} => BlackBoxFunc::SchnorrVerify, + BlackBoxFuncCall::Pedersen {..} => BlackBoxFunc::Pedersen, + BlackBoxFuncCall::HashToField128Security {..} => BlackBoxFunc::HashToField128Security, + BlackBoxFuncCall::EcdsaSecp256k1 {..} => BlackBoxFunc::EcdsaSecp256k1, + BlackBoxFuncCall::FixedBaseScalarMul {..} => BlackBoxFunc::FixedBaseScalarMul, + BlackBoxFuncCall::Keccak256 {..} => BlackBoxFunc::Keccak256, + } + } - let num_inputs = self.inputs.len() as u32; - write_u32(&mut writer, num_inputs)?; + pub fn get_inputs_vec(&self) -> Vec { + match self { + BlackBoxFuncCall::AES { inputs, .. } + | BlackBoxFuncCall::SHA256 { inputs, .. } + | BlackBoxFuncCall::Blake2s { inputs, .. } + | BlackBoxFuncCall::Keccak256 { inputs, .. } + | BlackBoxFuncCall::ComputeMerkleRoot { inputs, .. } + | BlackBoxFuncCall::SchnorrVerify { inputs, .. } + | BlackBoxFuncCall::Pedersen { inputs, .. } + | BlackBoxFuncCall::HashToField128Security { inputs, .. } + | BlackBoxFuncCall::EcdsaSecp256k1 { inputs, .. } => inputs.to_vec(), + BlackBoxFuncCall::AND { lhs, rhs, .. } + | BlackBoxFuncCall::XOR { lhs, rhs, .. } => vec![*lhs, *rhs], + BlackBoxFuncCall::FixedBaseScalarMul { input, .. } + | BlackBoxFuncCall::RANGE { input } => vec![*input], + } + } - for input in &self.inputs { - write_u32(&mut writer, input.witness.witness_index())?; - write_u32(&mut writer, input.num_bits)?; + pub fn get_outputs_vec(&self) -> Vec { + match self { + BlackBoxFuncCall::AES { outputs, .. } + | BlackBoxFuncCall::SHA256 { outputs, .. } + | BlackBoxFuncCall::Blake2s { outputs, .. } + | BlackBoxFuncCall::FixedBaseScalarMul { outputs, .. } + | BlackBoxFuncCall::Pedersen { outputs, .. } + | BlackBoxFuncCall::Keccak256 { outputs, .. } => outputs.to_vec(), + BlackBoxFuncCall::AND { output, .. } + | BlackBoxFuncCall::XOR { output, .. } + | BlackBoxFuncCall::HashToField128Security { output, .. } + | BlackBoxFuncCall::ComputeMerkleRoot { output, .. } + | BlackBoxFuncCall::SchnorrVerify { output, .. } + | BlackBoxFuncCall::EcdsaSecp256k1 { output, .. } => vec![*output], + BlackBoxFuncCall::RANGE { .. } => vec![], } + } + + pub fn write(&self, mut writer: W) -> std::io::Result<()> { + write_u16(&mut writer, self.get_black_box_func().to_u16())?; - let num_outputs = self.outputs.len() as u32; - write_u32(&mut writer, num_outputs)?; + write_inputs(&self.get_inputs_vec(), &mut writer)?; + write_outputs(&self.get_outputs_vec(), &mut writer)?; - for output in &self.outputs { - write_u32(&mut writer, output.witness_index())?; + match self { + BlackBoxFuncCall::Pedersen { hash_index, .. } => { + write_u32(&mut writer, *hash_index)?; + } + _ => {} } Ok(()) } + pub fn read(mut reader: R) -> std::io::Result { let func_index = read_u16(&mut reader)?; let name = BlackBoxFunc::from_u16(func_index).ok_or(std::io::ErrorKind::InvalidData)?; - let num_inputs = read_u32(&mut reader)?; - let mut inputs = Vec::with_capacity(num_inputs as usize); - for _ in 0..num_inputs { - let witness = Witness(read_u32(&mut reader)?); - let num_bits = read_u32(&mut reader)?; - let input = FunctionInput { witness, num_bits }; - inputs.push(input) + let inputs = read_inputs(&mut reader)?; + let outputs = read_outputs(&mut reader)?; + + let func_call = match name { + BlackBoxFunc::AES => BlackBoxFuncCall::AES { inputs, outputs }, + BlackBoxFunc::AND => { + let lhs = inputs[0]; + let rhs = inputs[1]; + let output = outputs[0]; + BlackBoxFuncCall::AND { lhs, rhs, output } + } + BlackBoxFunc::XOR => { + let lhs = inputs[0]; + let rhs = inputs[1]; + let output = outputs[0]; + BlackBoxFuncCall::XOR { lhs, rhs, output } + } + BlackBoxFunc::RANGE => { + let input = inputs[0]; + BlackBoxFuncCall::RANGE { input } + } + BlackBoxFunc::SHA256 => BlackBoxFuncCall::SHA256 { inputs, outputs }, + BlackBoxFunc::Blake2s => BlackBoxFuncCall::Blake2s { inputs, outputs }, + BlackBoxFunc::ComputeMerkleRoot => BlackBoxFuncCall::ComputeMerkleRoot { inputs, output: outputs[0] }, + BlackBoxFunc::SchnorrVerify => BlackBoxFuncCall::SchnorrVerify { inputs, output: outputs[0] }, + BlackBoxFunc::Pedersen => { + let hash_index: u32 = read_u32(&mut reader)?; + BlackBoxFuncCall::Pedersen { inputs, outputs, hash_index } + } + BlackBoxFunc::HashToField128Security => BlackBoxFuncCall::HashToField128Security { inputs, output: outputs[0] }, + BlackBoxFunc::EcdsaSecp256k1 => BlackBoxFuncCall::EcdsaSecp256k1 { inputs, output: outputs[0] }, + BlackBoxFunc::FixedBaseScalarMul => BlackBoxFuncCall::FixedBaseScalarMul { input: inputs[0].clone(), outputs }, + BlackBoxFunc::Keccak256 => BlackBoxFuncCall::Keccak256 { inputs, outputs }, + }; + + Ok(func_call) + } +} + +const ABBREVIATION_LIMIT: usize = 5; + +fn get_inputs_string(inputs: &Vec) -> String { + // Once a vectors length gets above this limit, + // instead of listing all of their elements, we use ellipses + // to abbreviate them + let should_abbreviate_inputs = inputs.len() <= ABBREVIATION_LIMIT; + + if should_abbreviate_inputs { + let mut result = String::new(); + for (index, inp) in inputs.iter().enumerate() { + result += + &format!("(_{}, num_bits: {})", inp.witness.witness_index(), inp.num_bits); + // Add a comma, unless it is the last entry + if index != inputs.len() - 1 { + result += ", " + } } + result + } else { + let first = inputs.first().unwrap(); + let last = inputs.last().unwrap(); + + let mut result = String::new(); + + result += &format!( + "(_{}, num_bits: {})...(_{}, num_bits: {})", + first.witness.witness_index(), + first.num_bits, + last.witness.witness_index(), + last.num_bits, + ); - let num_outputs = read_u32(&mut reader)?; - let mut outputs = Vec::with_capacity(num_outputs as usize); - for _ in 0..num_outputs { - let witness = Witness(read_u32(&mut reader)?); - outputs.push(witness) + result + } +} + +fn get_outputs_string(outputs: &Vec) -> String { + let should_abbreviate_outputs = outputs.len() <= ABBREVIATION_LIMIT; + + if should_abbreviate_outputs { + let mut result = String::new(); + for (index, output) in outputs.iter().enumerate() { + result += &format!("_{}", output.witness_index()); + // Add a comma, unless it is the last entry + if index != outputs.len() - 1 { + result += ", " + } } + result + } else { + let first = outputs.first().unwrap(); + let last = outputs.last().unwrap(); - Ok(BlackBoxFuncCall { name, inputs, outputs }) + let mut result = String::new(); + result += &format!("(_{},...,_{})", first.witness_index(), last.witness_index()); + result } } impl std::fmt::Display for BlackBoxFuncCall { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let uppercase_name: String = self.name.name().into(); + let uppercase_name: String = self.get_black_box_func().name().into(); let uppercase_name = uppercase_name.to_uppercase(); write!(f, "BLACKBOX::{uppercase_name} ")?; + // INPUTS write!(f, "[")?; - // Once a vectors length gets above this limit, - // instead of listing all of their elements, we use ellipses - // t abbreviate them - const ABBREVIATION_LIMIT: usize = 5; - - let should_abbreviate_inputs = self.inputs.len() <= ABBREVIATION_LIMIT; - let should_abbreviate_outputs = self.outputs.len() <= ABBREVIATION_LIMIT; + let inputs_str = get_inputs_string(&self.get_inputs_vec()); - // INPUTS - // - let inputs_str = if should_abbreviate_inputs { - let mut result = String::new(); - for (index, inp) in self.inputs.iter().enumerate() { - result += - &format!("(_{}, num_bits: {})", inp.witness.witness_index(), inp.num_bits); - // Add a comma, unless it is the last entry - if index != self.inputs.len() - 1 { - result += ", " - } - } - result - } else { - let first = self.inputs.first().unwrap(); - let last = self.inputs.last().unwrap(); - - let mut result = String::new(); - - result += &format!( - "(_{}, num_bits: {})...(_{}, num_bits: {})", - first.witness.witness_index(), - first.num_bits, - last.witness.witness_index(), - last.num_bits, - ); - - result - }; write!(f, "{inputs_str}")?; write!(f, "] ")?; // OUTPUTS - // TODO: Avoid duplication of INPUTS and OUTPUTS code + write!(f, "[ ")?; - if self.outputs.is_empty() { - return Ok(()); - } + let outputs_str = get_outputs_string(&self.get_outputs_vec()); - write!(f, "[ ")?; - let outputs_str = if should_abbreviate_outputs { - let mut result = String::new(); - for (index, output) in self.outputs.iter().enumerate() { - result += &format!("_{}", output.witness_index()); - // Add a comma, unless it is the last entry - if index != self.outputs.len() - 1 { - result += ", " - } - } - result - } else { - let first = self.outputs.first().unwrap(); - let last = self.outputs.last().unwrap(); - - let mut result = String::new(); - result += &format!("(_{},...,_{})", first.witness_index(), last.witness_index()); - result - }; write!(f, "{outputs_str}")?; - write!(f, "]") + + write!(f, "]")?; + + // FUNCTION SPECIFIC PARAMETERS + match self { + BlackBoxFuncCall::Pedersen { hash_index, .. } => write!(f, " hash_index: {hash_index}"), + _ => write!(f, ""), + } } } diff --git a/acvm/src/compiler/optimizers/redundant_range.rs b/acvm/src/compiler/optimizers/redundant_range.rs index 0dd77d96f..b7f72b365 100644 --- a/acvm/src/compiler/optimizers/redundant_range.rs +++ b/acvm/src/compiler/optimizers/redundant_range.rs @@ -1,7 +1,6 @@ use acir::{ - circuit::{Circuit, Opcode}, + circuit::{Circuit, Opcode, opcodes::BlackBoxFuncCall}, native_types::Witness, - BlackBoxFunc, }; use std::collections::{BTreeMap, HashSet}; @@ -121,13 +120,10 @@ fn extract_range_opcode(opcode: &Opcode) -> Option<(Witness, u32)> { }; // Skip if it is not a range constraint - if func_call.name != BlackBoxFunc::RANGE { - return None; + match func_call { + BlackBoxFuncCall::RANGE { input } => Some((input.witness, input.num_bits)), + _ => None, } - - let func_input = - func_call.inputs.first().expect("we expect there to be one input for a range call"); - Some((func_input.witness, func_input.num_bits)) } #[cfg(test)] @@ -139,15 +135,12 @@ mod tests { Circuit, Opcode, PublicInputs, }, native_types::{Expression, Witness}, - BlackBoxFunc, }; fn test_circuit(ranges: Vec<(Witness, u32)>) -> Circuit { fn test_range_constraint(witness: Witness, num_bits: u32) -> Opcode { - Opcode::BlackBoxFuncCall(BlackBoxFuncCall { - name: BlackBoxFunc::RANGE, - inputs: vec![FunctionInput { witness, num_bits }], - outputs: vec![], + Opcode::BlackBoxFuncCall(BlackBoxFuncCall::RANGE { + input: FunctionInput { witness, num_bits }, }) } diff --git a/acvm/src/compiler/transformers/fallback.rs b/acvm/src/compiler/transformers/fallback.rs index 6b669b18a..72b9210dc 100644 --- a/acvm/src/compiler/transformers/fallback.rs +++ b/acvm/src/compiler/transformers/fallback.rs @@ -2,7 +2,6 @@ use super::super::CompileError; use acir::{ circuit::{opcodes::BlackBoxFuncCall, Circuit, Opcode}, native_types::Expression, - BlackBoxFunc, }; // A predicate that returns true if the black box function is supported @@ -66,31 +65,28 @@ impl FallbackTransformer { gc: &BlackBoxFuncCall, current_witness_idx: u32, ) -> Result<(u32, Vec), CompileError> { - let (updated_witness_index, opcodes_fallback) = match gc.name { - BlackBoxFunc::AND => { - let (lhs, rhs, result, num_bits) = crate::pwg::logic::extract_input_output(gc); + let (updated_witness_index, opcodes_fallback) = match gc { + BlackBoxFuncCall::AND { lhs, rhs, output } => { + assert_eq!(lhs.num_bits, rhs.num_bits, "number of bits specified for each input must be the same"); stdlib::fallback::and( - Expression::from(lhs), - Expression::from(rhs), - result, - num_bits, + Expression::from(lhs.witness), + Expression::from(rhs.witness), + *output, + lhs.num_bits, current_witness_idx, ) } - BlackBoxFunc::XOR => { - let (lhs, rhs, result, num_bits) = crate::pwg::logic::extract_input_output(gc); + BlackBoxFuncCall::XOR { lhs, rhs, output } => { + assert_eq!(lhs.num_bits, rhs.num_bits, "number of bits specified for each input must be the same"); stdlib::fallback::xor( - Expression::from(lhs), - Expression::from(rhs), - result, - num_bits, + Expression::from(lhs.witness), + Expression::from(rhs.witness), + *output, + lhs.num_bits, current_witness_idx, ) } - BlackBoxFunc::RANGE => { - // TODO: add consistency checks in one place - // TODO: we aren't checking that range gate should have one input - let input = &gc.inputs[0]; + BlackBoxFuncCall::RANGE {input} => { // Note there are no outputs because range produces no outputs stdlib::fallback::range( Expression::from(input.witness), @@ -99,7 +95,7 @@ impl FallbackTransformer { ) } _ => { - return Err(CompileError::UnsupportedBlackBox(gc.name)); + return Err(CompileError::UnsupportedBlackBox(gc.get_black_box_func())); } }; diff --git a/acvm/src/lib.rs b/acvm/src/lib.rs index a720daf71..440ddf751 100644 --- a/acvm/src/lib.rs +++ b/acvm/src/lib.rs @@ -69,20 +69,21 @@ pub trait PartialWitnessGenerator { fn and( &self, initial_witness: &mut BTreeMap, - inputs: &[FunctionInput], - outputs: &[Witness], + lhs: &FunctionInput, + rhs: &FunctionInput, + output: &Witness, ) -> Result; fn xor( &self, initial_witness: &mut BTreeMap, - inputs: &[FunctionInput], - outputs: &[Witness], + lhs: &FunctionInput, + rhs: &FunctionInput, + output: &Witness, ) -> Result; fn range( &self, initial_witness: &mut BTreeMap, - inputs: &[FunctionInput], - outputs: &[Witness], + input: &FunctionInput, ) -> Result; fn sha256( &self, @@ -100,17 +101,18 @@ pub trait PartialWitnessGenerator { &self, initial_witness: &mut BTreeMap, inputs: &[FunctionInput], - outputs: &[Witness], + output: &Witness, ) -> Result; fn schnorr_verify( &self, initial_witness: &mut BTreeMap, inputs: &[FunctionInput], - outputs: &[Witness], + output: &Witness, ) -> Result; fn pedersen( &self, initial_witness: &mut BTreeMap, + hash_index: u32, inputs: &[FunctionInput], outputs: &[Witness], ) -> Result; @@ -118,18 +120,18 @@ pub trait PartialWitnessGenerator { &self, initial_witness: &mut BTreeMap, inputs: &[FunctionInput], - outputs: &[Witness], + outputs: &Witness, ) -> Result; fn ecdsa_secp256k1( &self, initial_witness: &mut BTreeMap, inputs: &[FunctionInput], - outputs: &[Witness], + outputs: &Witness, ) -> Result; fn fixed_base_scalar_mul( &self, initial_witness: &mut BTreeMap, - inputs: &[FunctionInput], + input: &FunctionInput, outputs: &[Witness], ) -> Result; fn keccak256( @@ -244,7 +246,7 @@ pub fn default_is_opcode_supported( fn plonk_is_supported(opcode: &Opcode) -> bool { !matches!( opcode, - Opcode::BlackBoxFuncCall(BlackBoxFuncCall { name: BlackBoxFunc::AES, .. }) + Opcode::BlackBoxFuncCall(BlackBoxFuncCall::AES {..}) | Opcode::Block(_) ) } @@ -290,8 +292,9 @@ mod test { fn and( &self, _initial_witness: &mut BTreeMap, - _inputs: &[FunctionInput], - _outputs: &[Witness], + _lhs: &FunctionInput, + _rhs: &FunctionInput, + _output: &Witness, ) -> Result { { panic!("Path not trodden by this test") @@ -300,8 +303,9 @@ mod test { fn xor( &self, _initial_witness: &mut BTreeMap, - _inputs: &[FunctionInput], - _outputs: &[Witness], + _lhs: &FunctionInput, + _rhs: &FunctionInput, + _output: &Witness, ) -> Result { { panic!("Path not trodden by this test") @@ -310,8 +314,7 @@ mod test { fn range( &self, _initial_witness: &mut BTreeMap, - _inputs: &[FunctionInput], - _outputs: &[Witness], + _input: &FunctionInput, ) -> Result { { panic!("Path not trodden by this test") @@ -341,7 +344,7 @@ mod test { &self, _initial_witness: &mut BTreeMap, _inputs: &[FunctionInput], - _outputs: &[Witness], + _output: &Witness, ) -> Result { { panic!("Path not trodden by this test") @@ -351,7 +354,7 @@ mod test { &self, _initial_witness: &mut BTreeMap, _inputs: &[FunctionInput], - _outputs: &[Witness], + _output: &Witness, ) -> Result { { panic!("Path not trodden by this test") @@ -360,6 +363,7 @@ mod test { fn pedersen( &self, _initial_witness: &mut BTreeMap, + _hash_index: u32, _inputs: &[FunctionInput], _outputs: &[Witness], ) -> Result { @@ -371,7 +375,7 @@ mod test { &self, _initial_witness: &mut BTreeMap, _inputs: &[FunctionInput], - _outputs: &[Witness], + _output: &Witness, ) -> Result { { panic!("Path not trodden by this test") @@ -381,7 +385,7 @@ mod test { &self, _initial_witness: &mut BTreeMap, _inputs: &[FunctionInput], - _outputs: &[Witness], + _output: &Witness, ) -> Result { { panic!("Path not trodden by this test") @@ -390,7 +394,7 @@ mod test { fn fixed_base_scalar_mul( &self, _initial_witness: &mut BTreeMap, - _inputs: &[FunctionInput], + _input: &FunctionInput, _outputs: &[Witness], ) -> Result { { diff --git a/acvm/src/pwg/blackbox.rs b/acvm/src/pwg/blackbox.rs index 3c507592f..3eb86b6e2 100644 --- a/acvm/src/pwg/blackbox.rs +++ b/acvm/src/pwg/blackbox.rs @@ -3,7 +3,7 @@ use std::collections::BTreeMap; use acir::{ circuit::opcodes::{BlackBoxFuncCall, FunctionInput}, native_types::Witness, - BlackBoxFunc, FieldElement, + FieldElement, }; use crate::{OpcodeNotSolvable, OpcodeResolutionError, PartialWitnessGenerator}; @@ -39,9 +39,10 @@ pub(crate) fn solve( initial_witness: &mut BTreeMap, bb_func: &BlackBoxFuncCall, ) -> Result { + let inputs = bb_func.get_inputs_vec(); match bb_func { - BlackBoxFuncCall { inputs, .. } if !contains_all_inputs(initial_witness, inputs) => { - if let Some(unassigned_witness) = first_missing_assignment(initial_witness, inputs) { + _ if !contains_all_inputs(initial_witness, &inputs) => { + if let Some(unassigned_witness) = first_missing_assignment(initial_witness, &inputs) { Ok(OpcodeResolution::Stalled(OpcodeNotSolvable::MissingAssignment( unassigned_witness.0, ))) @@ -51,43 +52,43 @@ pub(crate) fn solve( unreachable!("Only reachable if the blackbox is stalled") } } - BlackBoxFuncCall { name: BlackBoxFunc::AES, inputs, outputs } => { + BlackBoxFuncCall::AES { inputs, outputs } => { backend.aes(initial_witness, inputs, outputs) } - BlackBoxFuncCall { name: BlackBoxFunc::AND, inputs, outputs } => { - backend.and(initial_witness, inputs, outputs) + BlackBoxFuncCall::AND { lhs, rhs, output } => { + backend.and(initial_witness, lhs, rhs, output) } - BlackBoxFuncCall { name: BlackBoxFunc::XOR, inputs, outputs } => { - backend.xor(initial_witness, inputs, outputs) + BlackBoxFuncCall::XOR { lhs, rhs, output } => { + backend.xor(initial_witness, lhs, rhs, output) } - BlackBoxFuncCall { name: BlackBoxFunc::RANGE, inputs, outputs } => { - backend.range(initial_witness, inputs, outputs) + BlackBoxFuncCall::RANGE { input } => { + backend.range(initial_witness, input) } - BlackBoxFuncCall { name: BlackBoxFunc::SHA256, inputs, outputs } => { + BlackBoxFuncCall::SHA256 { inputs, outputs } => { backend.sha256(initial_witness, inputs, outputs) } - BlackBoxFuncCall { name: BlackBoxFunc::Blake2s, inputs, outputs } => { + BlackBoxFuncCall::Blake2s { inputs, outputs } => { backend.blake2s(initial_witness, inputs, outputs) } - BlackBoxFuncCall { name: BlackBoxFunc::ComputeMerkleRoot, inputs, outputs } => { - backend.compute_merkle_root(initial_witness, inputs, outputs) + BlackBoxFuncCall::ComputeMerkleRoot { inputs, output } => { + backend.compute_merkle_root(initial_witness, inputs, output) } - BlackBoxFuncCall { name: BlackBoxFunc::SchnorrVerify, inputs, outputs } => { - backend.schnorr_verify(initial_witness, inputs, outputs) + BlackBoxFuncCall::SchnorrVerify { inputs, output } => { + backend.schnorr_verify(initial_witness, inputs, output) } - BlackBoxFuncCall { name: BlackBoxFunc::Pedersen, inputs, outputs } => { - backend.pedersen(initial_witness, inputs, outputs) + BlackBoxFuncCall::Pedersen { hash_index, inputs, outputs } => { + backend.pedersen(initial_witness, *hash_index, inputs, outputs) } - BlackBoxFuncCall { name: BlackBoxFunc::HashToField128Security, inputs, outputs } => { - backend.hash_to_field128_security(initial_witness, inputs, outputs) + BlackBoxFuncCall::HashToField128Security { inputs, output } => { + backend.hash_to_field128_security(initial_witness, inputs, output) } - BlackBoxFuncCall { name: BlackBoxFunc::EcdsaSecp256k1, inputs, outputs } => { - backend.ecdsa_secp256k1(initial_witness, inputs, outputs) + BlackBoxFuncCall::EcdsaSecp256k1 { inputs, output } => { + backend.ecdsa_secp256k1(initial_witness, inputs, output) } - BlackBoxFuncCall { name: BlackBoxFunc::FixedBaseScalarMul, inputs, outputs } => { - backend.fixed_base_scalar_mul(initial_witness, inputs, outputs) + BlackBoxFuncCall::FixedBaseScalarMul { input, outputs } => { + backend.fixed_base_scalar_mul(initial_witness, input, outputs) } - BlackBoxFuncCall { name: BlackBoxFunc::Keccak256, inputs, outputs } => { + BlackBoxFuncCall::Keccak256 { inputs, outputs } => { backend.keccak256(initial_witness, inputs, outputs) } } diff --git a/acvm/src/pwg/hash.rs b/acvm/src/pwg/hash.rs index 2333ec21b..00ac890a9 100644 --- a/acvm/src/pwg/hash.rs +++ b/acvm/src/pwg/hash.rs @@ -1,4 +1,4 @@ -use acir::{circuit::opcodes::BlackBoxFuncCall, native_types::Witness, FieldElement}; +use acir::{circuit::opcodes::{FunctionInput}, native_types::Witness, FieldElement}; use blake2::{Blake2s256, Digest}; use sha2::Sha256; use sha3::Keccak256; @@ -10,11 +10,13 @@ use super::{insert_value, witness_to_value}; pub fn blake2s256( initial_witness: &mut BTreeMap, - func_call: &BlackBoxFuncCall, + inputs: &Vec, + outputs: &Vec, + ) -> Result { - let hash = generic_hash_256::(initial_witness, func_call)?; + let hash = generic_hash_256::(initial_witness, inputs)?; - for (output_witness, value) in func_call.outputs.iter().zip(hash.iter()) { + for (output_witness, value) in outputs.iter().zip(hash.iter()) { insert_value( output_witness, FieldElement::from_be_bytes_reduce(&[*value]), @@ -27,11 +29,12 @@ pub fn blake2s256( pub fn sha256( initial_witness: &mut BTreeMap, - func_call: &BlackBoxFuncCall, + inputs: &Vec, + outputs: &Vec, ) -> Result { - let hash = generic_hash_256::(initial_witness, func_call)?; + let hash = generic_hash_256::(initial_witness, inputs)?; - for (output_witness, value) in func_call.outputs.iter().zip(hash.iter()) { + for (output_witness, value) in outputs.iter().zip(hash.iter()) { insert_value( output_witness, FieldElement::from_be_bytes_reduce(&[*value]), @@ -44,11 +47,12 @@ pub fn sha256( pub fn keccak256( initial_witness: &mut BTreeMap, - func_call: &BlackBoxFuncCall, + inputs: &Vec, + outputs: &Vec, ) -> Result { - let hash = generic_hash_256::(initial_witness, func_call)?; + let hash = generic_hash_256::(initial_witness, inputs)?; - for (output_witness, value) in func_call.outputs.iter().zip(hash.iter()) { + for (output_witness, value) in outputs.iter().zip(hash.iter()) { insert_value( output_witness, FieldElement::from_be_bytes_reduce(&[*value]), @@ -61,24 +65,25 @@ pub fn keccak256( pub fn hash_to_field_128_security( initial_witness: &mut BTreeMap, - func_call: &BlackBoxFuncCall, + inputs: &Vec, + output: &Witness, ) -> Result { - let hash = generic_hash_256::(initial_witness, func_call)?; + let hash = generic_hash_256::(initial_witness, inputs)?; let reduced_res = FieldElement::from_be_bytes_reduce(&hash); - insert_value(&func_call.outputs[0], reduced_res, initial_witness)?; + insert_value(output, reduced_res, initial_witness)?; Ok(OpcodeResolution::Solved) } fn generic_hash_256( initial_witness: &mut BTreeMap, - func_call: &BlackBoxFuncCall, + inputs: &Vec, ) -> Result<[u8; 32], OpcodeResolutionError> { let mut hasher = D::new(); // Read witness assignments into hasher. - for input in func_call.inputs.iter() { + for input in inputs.iter() { let witness = input.witness; let num_bits = input.num_bits as usize; diff --git a/acvm/src/pwg/logic.rs b/acvm/src/pwg/logic.rs index 489bc4569..92b398582 100644 --- a/acvm/src/pwg/logic.rs +++ b/acvm/src/pwg/logic.rs @@ -1,16 +1,16 @@ use super::{insert_value, witness_to_value}; use crate::{pwg::OpcodeResolution, OpcodeResolutionError}; -use acir::{circuit::opcodes::BlackBoxFuncCall, native_types::Witness, BlackBoxFunc, FieldElement}; +use acir::{circuit::opcodes::{BlackBoxFuncCall, FunctionInput}, native_types::Witness, FieldElement}; use std::collections::BTreeMap; pub fn solve_logic_opcode( initial_witness: &mut BTreeMap, func_call: &BlackBoxFuncCall, ) -> Result { - match func_call.name { - BlackBoxFunc::AND => LogicSolver::solve_and_gate(initial_witness, func_call), - BlackBoxFunc::XOR => LogicSolver::solve_xor_gate(initial_witness, func_call), - _ => Err(OpcodeResolutionError::UnexpectedOpcode("logic opcode", func_call.name)), + match func_call { + BlackBoxFuncCall::AND {lhs, rhs, output} => LogicSolver::solve_and_gate(initial_witness, lhs, rhs, output), + BlackBoxFuncCall::XOR {lhs, rhs, output} => LogicSolver::solve_xor_gate(initial_witness, lhs, rhs, output), + _ => Err(OpcodeResolutionError::UnexpectedOpcode("logic opcode", func_call.get_black_box_func())), } } @@ -22,7 +22,7 @@ impl LogicSolver { initial_witness: &mut BTreeMap, a: &Witness, b: &Witness, - result: Witness, + result: &Witness, num_bits: u32, is_xor_gate: bool, ) -> Result { @@ -34,38 +34,28 @@ impl LogicSolver { } else { w_l_value.and(w_r_value, num_bits) }; - insert_value(&result, assignment, initial_witness)?; + insert_value(result, assignment, initial_witness)?; Ok(OpcodeResolution::Solved) } pub fn solve_and_gate( initial_witness: &mut BTreeMap, - gate: &BlackBoxFuncCall, + lhs: &FunctionInput, + rhs: &FunctionInput, + output: &Witness, ) -> Result { - let (a, b, result, num_bits) = extract_input_output(gate); - LogicSolver::solve_logic_gate(initial_witness, &a, &b, result, num_bits, false) + assert_eq!(lhs.num_bits, rhs.num_bits, "number of bits specified for each input must be the same"); + + LogicSolver::solve_logic_gate(initial_witness, &lhs.witness, &rhs.witness, output, lhs.num_bits, false) } pub fn solve_xor_gate( initial_witness: &mut BTreeMap, - gate: &BlackBoxFuncCall, + lhs: &FunctionInput, + rhs: &FunctionInput, + output: &Witness, ) -> Result { - let (a, b, result, num_bits) = extract_input_output(gate); - LogicSolver::solve_logic_gate(initial_witness, &a, &b, result, num_bits, true) - } -} -// TODO: Is there somewhere else that we can put this? -// TODO: extraction methods are needed for some opcodes like logic and range -pub(crate) fn extract_input_output( - bb_func_call: &BlackBoxFuncCall, -) -> (Witness, Witness, Witness, u32) { - let a = &bb_func_call.inputs[0]; - let b = &bb_func_call.inputs[1]; - let result = &bb_func_call.outputs[0]; - - // The num_bits variable should be the same for all witnesses - assert_eq!(a.num_bits, b.num_bits, "number of bits specified for each input must be the same"); + assert_eq!(lhs.num_bits, rhs.num_bits, "number of bits specified for each input must be the same"); - let num_bits = a.num_bits; - - (a.witness, b.witness, *result, num_bits) -} + LogicSolver::solve_logic_gate(initial_witness, &lhs.witness, &rhs.witness, output, lhs.num_bits, true) + } +} \ No newline at end of file diff --git a/acvm/src/pwg/range.rs b/acvm/src/pwg/range.rs index b0a0a5855..c7f98aa1a 100644 --- a/acvm/src/pwg/range.rs +++ b/acvm/src/pwg/range.rs @@ -1,32 +1,11 @@ use crate::{pwg::witness_to_value, pwg::OpcodeResolution, OpcodeResolutionError}; -use acir::{circuit::opcodes::BlackBoxFuncCall, native_types::Witness, BlackBoxFunc, FieldElement}; +use acir::{native_types::Witness, FieldElement, circuit::opcodes::FunctionInput}; use std::collections::BTreeMap; pub fn solve_range_opcode( initial_witness: &mut BTreeMap, - func_call: &BlackBoxFuncCall, + input: &FunctionInput, ) -> Result { - // TODO: this consistency check can be moved to a general function - let defined_input_size = BlackBoxFunc::RANGE - .definition() - .input_size - .fixed_size() - .expect("infallible: input for range gate is fixed"); - - let num_arguments = func_call.inputs.len(); - if num_arguments != defined_input_size as usize { - return Err(OpcodeResolutionError::IncorrectNumFunctionArguments( - defined_input_size as usize, - BlackBoxFunc::RANGE, - num_arguments, - )); - } - - // For the range constraint, we know that the input size should be one - assert_eq!(defined_input_size, 1); - - let input = func_call.inputs.first().expect("infallible: checked that input size is 1"); - let w_value = witness_to_value(initial_witness, input.witness)?; if w_value.num_bits() > input.num_bits { return Err(OpcodeResolutionError::UnsatisfiedConstrain); diff --git a/acvm/src/pwg/signature/ecdsa.rs b/acvm/src/pwg/signature/ecdsa.rs index 1687e72ac..07765c103 100644 --- a/acvm/src/pwg/signature/ecdsa.rs +++ b/acvm/src/pwg/signature/ecdsa.rs @@ -1,13 +1,14 @@ -use acir::{circuit::opcodes::BlackBoxFuncCall, native_types::Witness, FieldElement}; +use acir::{circuit::opcodes::{FunctionInput}, native_types::Witness, FieldElement}; use std::collections::BTreeMap; use crate::{pwg::witness_to_value, pwg::OpcodeResolution, OpcodeResolutionError}; pub fn secp256k1_prehashed( initial_witness: &mut BTreeMap, - gadget_call: &BlackBoxFuncCall, + inputs: &Vec, + outputs: &Vec, ) -> Result { - let mut inputs_iter = gadget_call.inputs.iter(); + let mut inputs_iter = inputs.iter(); let mut pub_key_x = [0u8; 32]; for (i, pkx) in pub_key_x.iter_mut().enumerate() { @@ -50,7 +51,7 @@ pub fn secp256k1_prehashed( ecdsa_secp256k1::verify_prehashed(&hashed_message, &pub_key_x, &pub_key_y, &signature) .is_ok(); - initial_witness.insert(gadget_call.outputs[0], FieldElement::from(result)); + initial_witness.insert(outputs[0], FieldElement::from(result)); Ok(OpcodeResolution::Solved) } From 90fa1cd522641e26af3a6e75faf152f1f7331d07 Mon Sep 17 00:00:00 2001 From: sirasistant Date: Mon, 8 May 2023 12:14:44 +0000 Subject: [PATCH 02/15] style: updated format and clippy --- .../opcodes/black_box_function_call.rs | 142 ++++++++++++------ .../compiler/optimizers/redundant_range.rs | 2 +- acvm/src/compiler/transformers/fallback.rs | 14 +- acvm/src/lib.rs | 6 +- acvm/src/pwg/blackbox.rs | 8 +- acvm/src/pwg/hash.rs | 19 ++- acvm/src/pwg/logic.rs | 49 ++++-- acvm/src/pwg/range.rs | 2 +- acvm/src/pwg/signature/ecdsa.rs | 6 +- 9 files changed, 160 insertions(+), 88 deletions(-) diff --git a/acir/src/circuit/opcodes/black_box_function_call.rs b/acir/src/circuit/opcodes/black_box_function_call.rs index a71b473a7..1f4a502d4 100644 --- a/acir/src/circuit/opcodes/black_box_function_call.rs +++ b/acir/src/circuit/opcodes/black_box_function_call.rs @@ -16,25 +16,62 @@ pub struct FunctionInput { #[derive(Clone, PartialEq, Eq, Serialize, Deserialize)] pub enum BlackBoxFuncCall { #[allow(clippy::upper_case_acronyms)] - AES{ inputs: Vec, outputs: Vec }, - AND{ lhs: FunctionInput, rhs: FunctionInput, output: Witness }, - XOR{ lhs: FunctionInput, rhs: FunctionInput, output: Witness }, - RANGE{ input: FunctionInput }, - SHA256{ inputs: Vec, outputs: Vec }, - Blake2s{ inputs: Vec, outputs: Vec }, - ComputeMerkleRoot{ inputs: Vec, output: Witness }, - SchnorrVerify{ inputs: Vec, output: Witness }, - Pedersen{ + AES { + inputs: Vec, + outputs: Vec, + }, + AND { + lhs: FunctionInput, + rhs: FunctionInput, + output: Witness, + }, + XOR { + lhs: FunctionInput, + rhs: FunctionInput, + output: Witness, + }, + RANGE { + input: FunctionInput, + }, + SHA256 { + inputs: Vec, + outputs: Vec, + }, + Blake2s { + inputs: Vec, + outputs: Vec, + }, + ComputeMerkleRoot { + inputs: Vec, + output: Witness, + }, + SchnorrVerify { + inputs: Vec, + output: Witness, + }, + Pedersen { hash_index: u32, - inputs: Vec, - outputs: Vec + inputs: Vec, + outputs: Vec, }, // 128 here specifies that this function // should have 128 bits of security - HashToField128Security{ inputs: Vec, output: Witness }, - EcdsaSecp256k1{ inputs: Vec, output: Witness }, - FixedBaseScalarMul{ input: FunctionInput, outputs: Vec }, - Keccak256{ inputs: Vec, outputs: Vec }, + HashToField128Security { + inputs: Vec, + output: Witness, + }, + EcdsaSecp256k1 { + inputs: Vec, + output: Witness, + }, + FixedBaseScalarMul { + input: FunctionInput, + outputs: Vec, + }, + Keccak256 { + inputs: Vec, + outputs: Vec, + }, } fn write_input(input: &FunctionInput, mut writer: W) -> std::io::Result<()> { @@ -68,7 +105,7 @@ fn write_outputs(outputs: &Vec, mut writer: W) -> std::io::Re fn read_input(mut reader: R) -> std::io::Result { let witness_index = read_u32(&mut reader)?; let num_bits = read_u32(&mut reader)?; - Ok(FunctionInput{ witness: Witness::new(witness_index), num_bits }) + Ok(FunctionInput { witness: Witness::new(witness_index), num_bits }) } fn read_inputs(mut reader: R) -> std::io::Result> { @@ -99,35 +136,36 @@ fn read_outputs(mut reader: R) -> std::io::Result> { impl BlackBoxFuncCall { pub fn get_black_box_func(&self) -> BlackBoxFunc { match self { - BlackBoxFuncCall::AES {..} => BlackBoxFunc::AES, - BlackBoxFuncCall::AND {..} => BlackBoxFunc::AND, - BlackBoxFuncCall::XOR {..} => BlackBoxFunc::XOR, - BlackBoxFuncCall::RANGE {..} =>BlackBoxFunc::RANGE, - BlackBoxFuncCall::SHA256 {..} => BlackBoxFunc::SHA256, - BlackBoxFuncCall::Blake2s {..} =>BlackBoxFunc::Blake2s, - BlackBoxFuncCall::ComputeMerkleRoot {..} => BlackBoxFunc::ComputeMerkleRoot, - BlackBoxFuncCall::SchnorrVerify {..} => BlackBoxFunc::SchnorrVerify, - BlackBoxFuncCall::Pedersen {..} => BlackBoxFunc::Pedersen, - BlackBoxFuncCall::HashToField128Security {..} => BlackBoxFunc::HashToField128Security, - BlackBoxFuncCall::EcdsaSecp256k1 {..} => BlackBoxFunc::EcdsaSecp256k1, - BlackBoxFuncCall::FixedBaseScalarMul {..} => BlackBoxFunc::FixedBaseScalarMul, - BlackBoxFuncCall::Keccak256 {..} => BlackBoxFunc::Keccak256, + BlackBoxFuncCall::AES { .. } => BlackBoxFunc::AES, + BlackBoxFuncCall::AND { .. } => BlackBoxFunc::AND, + BlackBoxFuncCall::XOR { .. } => BlackBoxFunc::XOR, + BlackBoxFuncCall::RANGE { .. } => BlackBoxFunc::RANGE, + BlackBoxFuncCall::SHA256 { .. } => BlackBoxFunc::SHA256, + BlackBoxFuncCall::Blake2s { .. } => BlackBoxFunc::Blake2s, + BlackBoxFuncCall::ComputeMerkleRoot { .. } => BlackBoxFunc::ComputeMerkleRoot, + BlackBoxFuncCall::SchnorrVerify { .. } => BlackBoxFunc::SchnorrVerify, + BlackBoxFuncCall::Pedersen { .. } => BlackBoxFunc::Pedersen, + BlackBoxFuncCall::HashToField128Security { .. } => BlackBoxFunc::HashToField128Security, + BlackBoxFuncCall::EcdsaSecp256k1 { .. } => BlackBoxFunc::EcdsaSecp256k1, + BlackBoxFuncCall::FixedBaseScalarMul { .. } => BlackBoxFunc::FixedBaseScalarMul, + BlackBoxFuncCall::Keccak256 { .. } => BlackBoxFunc::Keccak256, } } pub fn get_inputs_vec(&self) -> Vec { match self { - BlackBoxFuncCall::AES { inputs, .. } + BlackBoxFuncCall::AES { inputs, .. } | BlackBoxFuncCall::SHA256 { inputs, .. } | BlackBoxFuncCall::Blake2s { inputs, .. } - | BlackBoxFuncCall::Keccak256 { inputs, .. } + | BlackBoxFuncCall::Keccak256 { inputs, .. } | BlackBoxFuncCall::ComputeMerkleRoot { inputs, .. } | BlackBoxFuncCall::SchnorrVerify { inputs, .. } | BlackBoxFuncCall::Pedersen { inputs, .. } | BlackBoxFuncCall::HashToField128Security { inputs, .. } | BlackBoxFuncCall::EcdsaSecp256k1 { inputs, .. } => inputs.to_vec(), - BlackBoxFuncCall::AND { lhs, rhs, .. } - | BlackBoxFuncCall::XOR { lhs, rhs, .. } => vec![*lhs, *rhs], + BlackBoxFuncCall::AND { lhs, rhs, .. } | BlackBoxFuncCall::XOR { lhs, rhs, .. } => { + vec![*lhs, *rhs] + } BlackBoxFuncCall::FixedBaseScalarMul { input, .. } | BlackBoxFuncCall::RANGE { input } => vec![*input], } @@ -135,14 +173,14 @@ impl BlackBoxFuncCall { pub fn get_outputs_vec(&self) -> Vec { match self { - BlackBoxFuncCall::AES { outputs, .. } + BlackBoxFuncCall::AES { outputs, .. } | BlackBoxFuncCall::SHA256 { outputs, .. } | BlackBoxFuncCall::Blake2s { outputs, .. } | BlackBoxFuncCall::FixedBaseScalarMul { outputs, .. } | BlackBoxFuncCall::Pedersen { outputs, .. } | BlackBoxFuncCall::Keccak256 { outputs, .. } => outputs.to_vec(), - BlackBoxFuncCall::AND { output, .. } - | BlackBoxFuncCall::XOR { output, .. } + BlackBoxFuncCall::AND { output, .. } + | BlackBoxFuncCall::XOR { output, .. } | BlackBoxFuncCall::HashToField128Security { output, .. } | BlackBoxFuncCall::ComputeMerkleRoot { output, .. } | BlackBoxFuncCall::SchnorrVerify { output, .. } @@ -157,11 +195,8 @@ impl BlackBoxFuncCall { write_inputs(&self.get_inputs_vec(), &mut writer)?; write_outputs(&self.get_outputs_vec(), &mut writer)?; - match self { - BlackBoxFuncCall::Pedersen { hash_index, .. } => { - write_u32(&mut writer, *hash_index)?; - } - _ => {} + if let BlackBoxFuncCall::Pedersen { hash_index, .. } = self { + write_u32(&mut writer, *hash_index)?; } Ok(()) @@ -194,15 +229,25 @@ impl BlackBoxFuncCall { } BlackBoxFunc::SHA256 => BlackBoxFuncCall::SHA256 { inputs, outputs }, BlackBoxFunc::Blake2s => BlackBoxFuncCall::Blake2s { inputs, outputs }, - BlackBoxFunc::ComputeMerkleRoot => BlackBoxFuncCall::ComputeMerkleRoot { inputs, output: outputs[0] }, - BlackBoxFunc::SchnorrVerify => BlackBoxFuncCall::SchnorrVerify { inputs, output: outputs[0] }, + BlackBoxFunc::ComputeMerkleRoot => { + BlackBoxFuncCall::ComputeMerkleRoot { inputs, output: outputs[0] } + } + BlackBoxFunc::SchnorrVerify => { + BlackBoxFuncCall::SchnorrVerify { inputs, output: outputs[0] } + } BlackBoxFunc::Pedersen => { let hash_index: u32 = read_u32(&mut reader)?; BlackBoxFuncCall::Pedersen { inputs, outputs, hash_index } } - BlackBoxFunc::HashToField128Security => BlackBoxFuncCall::HashToField128Security { inputs, output: outputs[0] }, - BlackBoxFunc::EcdsaSecp256k1 => BlackBoxFuncCall::EcdsaSecp256k1 { inputs, output: outputs[0] }, - BlackBoxFunc::FixedBaseScalarMul => BlackBoxFuncCall::FixedBaseScalarMul { input: inputs[0].clone(), outputs }, + BlackBoxFunc::HashToField128Security => { + BlackBoxFuncCall::HashToField128Security { inputs, output: outputs[0] } + } + BlackBoxFunc::EcdsaSecp256k1 => { + BlackBoxFuncCall::EcdsaSecp256k1 { inputs, output: outputs[0] } + } + BlackBoxFunc::FixedBaseScalarMul => { + BlackBoxFuncCall::FixedBaseScalarMul { input: inputs[0], outputs } + } BlackBoxFunc::Keccak256 => BlackBoxFuncCall::Keccak256 { inputs, outputs }, }; @@ -221,8 +266,7 @@ fn get_inputs_string(inputs: &Vec) -> String { if should_abbreviate_inputs { let mut result = String::new(); for (index, inp) in inputs.iter().enumerate() { - result += - &format!("(_{}, num_bits: {})", inp.witness.witness_index(), inp.num_bits); + result += &format!("(_{}, num_bits: {})", inp.witness.witness_index(), inp.num_bits); // Add a comma, unless it is the last entry if index != inputs.len() - 1 { result += ", " @@ -291,7 +335,7 @@ impl std::fmt::Display for BlackBoxFuncCall { write!(f, "{outputs_str}")?; write!(f, "]")?; - + // FUNCTION SPECIFIC PARAMETERS match self { BlackBoxFuncCall::Pedersen { hash_index, .. } => write!(f, " hash_index: {hash_index}"), diff --git a/acvm/src/compiler/optimizers/redundant_range.rs b/acvm/src/compiler/optimizers/redundant_range.rs index b7f72b365..486868f8a 100644 --- a/acvm/src/compiler/optimizers/redundant_range.rs +++ b/acvm/src/compiler/optimizers/redundant_range.rs @@ -1,5 +1,5 @@ use acir::{ - circuit::{Circuit, Opcode, opcodes::BlackBoxFuncCall}, + circuit::{opcodes::BlackBoxFuncCall, Circuit, Opcode}, native_types::Witness, }; use std::collections::{BTreeMap, HashSet}; diff --git a/acvm/src/compiler/transformers/fallback.rs b/acvm/src/compiler/transformers/fallback.rs index 72b9210dc..07d0277f4 100644 --- a/acvm/src/compiler/transformers/fallback.rs +++ b/acvm/src/compiler/transformers/fallback.rs @@ -67,7 +67,10 @@ impl FallbackTransformer { ) -> Result<(u32, Vec), CompileError> { let (updated_witness_index, opcodes_fallback) = match gc { BlackBoxFuncCall::AND { lhs, rhs, output } => { - assert_eq!(lhs.num_bits, rhs.num_bits, "number of bits specified for each input must be the same"); + assert_eq!( + lhs.num_bits, rhs.num_bits, + "number of bits specified for each input must be the same" + ); stdlib::fallback::and( Expression::from(lhs.witness), Expression::from(rhs.witness), @@ -76,8 +79,11 @@ impl FallbackTransformer { current_witness_idx, ) } - BlackBoxFuncCall::XOR { lhs, rhs, output } => { - assert_eq!(lhs.num_bits, rhs.num_bits, "number of bits specified for each input must be the same"); + BlackBoxFuncCall::XOR { lhs, rhs, output } => { + assert_eq!( + lhs.num_bits, rhs.num_bits, + "number of bits specified for each input must be the same" + ); stdlib::fallback::xor( Expression::from(lhs.witness), Expression::from(rhs.witness), @@ -86,7 +92,7 @@ impl FallbackTransformer { current_witness_idx, ) } - BlackBoxFuncCall::RANGE {input} => { + BlackBoxFuncCall::RANGE { input } => { // Note there are no outputs because range produces no outputs stdlib::fallback::range( Expression::from(input.witness), diff --git a/acvm/src/lib.rs b/acvm/src/lib.rs index 440ddf751..438b3b3fd 100644 --- a/acvm/src/lib.rs +++ b/acvm/src/lib.rs @@ -244,11 +244,7 @@ pub fn default_is_opcode_supported( // attempt to transform into supported gates. If these are also not available // then a compiler error will be emitted. fn plonk_is_supported(opcode: &Opcode) -> bool { - !matches!( - opcode, - Opcode::BlackBoxFuncCall(BlackBoxFuncCall::AES {..}) - | Opcode::Block(_) - ) + !matches!(opcode, Opcode::BlackBoxFuncCall(BlackBoxFuncCall::AES { .. }) | Opcode::Block(_)) } match language { diff --git a/acvm/src/pwg/blackbox.rs b/acvm/src/pwg/blackbox.rs index 3eb86b6e2..a76404a8b 100644 --- a/acvm/src/pwg/blackbox.rs +++ b/acvm/src/pwg/blackbox.rs @@ -52,18 +52,14 @@ pub(crate) fn solve( unreachable!("Only reachable if the blackbox is stalled") } } - BlackBoxFuncCall::AES { inputs, outputs } => { - backend.aes(initial_witness, inputs, outputs) - } + BlackBoxFuncCall::AES { inputs, outputs } => backend.aes(initial_witness, inputs, outputs), BlackBoxFuncCall::AND { lhs, rhs, output } => { backend.and(initial_witness, lhs, rhs, output) } BlackBoxFuncCall::XOR { lhs, rhs, output } => { backend.xor(initial_witness, lhs, rhs, output) } - BlackBoxFuncCall::RANGE { input } => { - backend.range(initial_witness, input) - } + BlackBoxFuncCall::RANGE { input } => backend.range(initial_witness, input), BlackBoxFuncCall::SHA256 { inputs, outputs } => { backend.sha256(initial_witness, inputs, outputs) } diff --git a/acvm/src/pwg/hash.rs b/acvm/src/pwg/hash.rs index 00ac890a9..4d630935e 100644 --- a/acvm/src/pwg/hash.rs +++ b/acvm/src/pwg/hash.rs @@ -1,4 +1,4 @@ -use acir::{circuit::opcodes::{FunctionInput}, native_types::Witness, FieldElement}; +use acir::{circuit::opcodes::FunctionInput, native_types::Witness, FieldElement}; use blake2::{Blake2s256, Digest}; use sha2::Sha256; use sha3::Keccak256; @@ -10,9 +10,8 @@ use super::{insert_value, witness_to_value}; pub fn blake2s256( initial_witness: &mut BTreeMap, - inputs: &Vec, - outputs: &Vec, - + inputs: &[FunctionInput], + outputs: &[Witness], ) -> Result { let hash = generic_hash_256::(initial_witness, inputs)?; @@ -29,8 +28,8 @@ pub fn blake2s256( pub fn sha256( initial_witness: &mut BTreeMap, - inputs: &Vec, - outputs: &Vec, + inputs: &[FunctionInput], + outputs: &[Witness], ) -> Result { let hash = generic_hash_256::(initial_witness, inputs)?; @@ -47,8 +46,8 @@ pub fn sha256( pub fn keccak256( initial_witness: &mut BTreeMap, - inputs: &Vec, - outputs: &Vec, + inputs: &[FunctionInput], + outputs: &[Witness], ) -> Result { let hash = generic_hash_256::(initial_witness, inputs)?; @@ -65,7 +64,7 @@ pub fn keccak256( pub fn hash_to_field_128_security( initial_witness: &mut BTreeMap, - inputs: &Vec, + inputs: &[FunctionInput], output: &Witness, ) -> Result { let hash = generic_hash_256::(initial_witness, inputs)?; @@ -78,7 +77,7 @@ pub fn hash_to_field_128_security( fn generic_hash_256( initial_witness: &mut BTreeMap, - inputs: &Vec, + inputs: &[FunctionInput], ) -> Result<[u8; 32], OpcodeResolutionError> { let mut hasher = D::new(); diff --git a/acvm/src/pwg/logic.rs b/acvm/src/pwg/logic.rs index 92b398582..2a38be15b 100644 --- a/acvm/src/pwg/logic.rs +++ b/acvm/src/pwg/logic.rs @@ -1,6 +1,10 @@ use super::{insert_value, witness_to_value}; use crate::{pwg::OpcodeResolution, OpcodeResolutionError}; -use acir::{circuit::opcodes::{BlackBoxFuncCall, FunctionInput}, native_types::Witness, FieldElement}; +use acir::{ + circuit::opcodes::{BlackBoxFuncCall, FunctionInput}, + native_types::Witness, + FieldElement, +}; use std::collections::BTreeMap; pub fn solve_logic_opcode( @@ -8,9 +12,16 @@ pub fn solve_logic_opcode( func_call: &BlackBoxFuncCall, ) -> Result { match func_call { - BlackBoxFuncCall::AND {lhs, rhs, output} => LogicSolver::solve_and_gate(initial_witness, lhs, rhs, output), - BlackBoxFuncCall::XOR {lhs, rhs, output} => LogicSolver::solve_xor_gate(initial_witness, lhs, rhs, output), - _ => Err(OpcodeResolutionError::UnexpectedOpcode("logic opcode", func_call.get_black_box_func())), + BlackBoxFuncCall::AND { lhs, rhs, output } => { + LogicSolver::solve_and_gate(initial_witness, lhs, rhs, output) + } + BlackBoxFuncCall::XOR { lhs, rhs, output } => { + LogicSolver::solve_xor_gate(initial_witness, lhs, rhs, output) + } + _ => Err(OpcodeResolutionError::UnexpectedOpcode( + "logic opcode", + func_call.get_black_box_func(), + )), } } @@ -44,9 +55,19 @@ impl LogicSolver { rhs: &FunctionInput, output: &Witness, ) -> Result { - assert_eq!(lhs.num_bits, rhs.num_bits, "number of bits specified for each input must be the same"); + assert_eq!( + lhs.num_bits, rhs.num_bits, + "number of bits specified for each input must be the same" + ); - LogicSolver::solve_logic_gate(initial_witness, &lhs.witness, &rhs.witness, output, lhs.num_bits, false) + LogicSolver::solve_logic_gate( + initial_witness, + &lhs.witness, + &rhs.witness, + output, + lhs.num_bits, + false, + ) } pub fn solve_xor_gate( initial_witness: &mut BTreeMap, @@ -54,8 +75,18 @@ impl LogicSolver { rhs: &FunctionInput, output: &Witness, ) -> Result { - assert_eq!(lhs.num_bits, rhs.num_bits, "number of bits specified for each input must be the same"); + assert_eq!( + lhs.num_bits, rhs.num_bits, + "number of bits specified for each input must be the same" + ); - LogicSolver::solve_logic_gate(initial_witness, &lhs.witness, &rhs.witness, output, lhs.num_bits, true) + LogicSolver::solve_logic_gate( + initial_witness, + &lhs.witness, + &rhs.witness, + output, + lhs.num_bits, + true, + ) } -} \ No newline at end of file +} diff --git a/acvm/src/pwg/range.rs b/acvm/src/pwg/range.rs index c7f98aa1a..8770993d0 100644 --- a/acvm/src/pwg/range.rs +++ b/acvm/src/pwg/range.rs @@ -1,5 +1,5 @@ use crate::{pwg::witness_to_value, pwg::OpcodeResolution, OpcodeResolutionError}; -use acir::{native_types::Witness, FieldElement, circuit::opcodes::FunctionInput}; +use acir::{circuit::opcodes::FunctionInput, native_types::Witness, FieldElement}; use std::collections::BTreeMap; pub fn solve_range_opcode( diff --git a/acvm/src/pwg/signature/ecdsa.rs b/acvm/src/pwg/signature/ecdsa.rs index 07765c103..b22a81cf0 100644 --- a/acvm/src/pwg/signature/ecdsa.rs +++ b/acvm/src/pwg/signature/ecdsa.rs @@ -1,12 +1,12 @@ -use acir::{circuit::opcodes::{FunctionInput}, native_types::Witness, FieldElement}; +use acir::{circuit::opcodes::FunctionInput, native_types::Witness, FieldElement}; use std::collections::BTreeMap; use crate::{pwg::witness_to_value, pwg::OpcodeResolution, OpcodeResolutionError}; pub fn secp256k1_prehashed( initial_witness: &mut BTreeMap, - inputs: &Vec, - outputs: &Vec, + inputs: &[FunctionInput], + outputs: &[Witness], ) -> Result { let mut inputs_iter = inputs.iter(); From 06605bd2992d7bd9b7515a379a5ffc28a6a01839 Mon Sep 17 00:00:00 2001 From: sirasistant Date: Mon, 8 May 2023 13:04:44 +0000 Subject: [PATCH 03/15] feat(bb): improved structs of bb fns --- .../opcodes/black_box_function_call.rs | 83 +++++++++++++++---- acvm/src/lib.rs | 28 +++++-- acvm/src/pwg/blackbox.rs | 38 +++++++-- 3 files changed, 119 insertions(+), 30 deletions(-) diff --git a/acir/src/circuit/opcodes/black_box_function_call.rs b/acir/src/circuit/opcodes/black_box_function_call.rs index 1f4a502d4..d2b6630b9 100644 --- a/acir/src/circuit/opcodes/black_box_function_call.rs +++ b/acir/src/circuit/opcodes/black_box_function_call.rs @@ -42,11 +42,16 @@ pub enum BlackBoxFuncCall { outputs: Vec, }, ComputeMerkleRoot { - inputs: Vec, + leaf: FunctionInput, + index: FunctionInput, + hash_path: Vec, output: Witness, }, SchnorrVerify { - inputs: Vec, + public_key_x: FunctionInput, + public_key_y: FunctionInput, + signature: Vec, + message: Vec, output: Witness, }, Pedersen { @@ -61,7 +66,10 @@ pub enum BlackBoxFuncCall { output: Witness, }, EcdsaSecp256k1 { - inputs: Vec, + public_key_x: Vec, + public_key_y: Vec, + signature: Vec, + message: Vec, output: Witness, }, FixedBaseScalarMul { @@ -158,16 +166,48 @@ impl BlackBoxFuncCall { | BlackBoxFuncCall::SHA256 { inputs, .. } | BlackBoxFuncCall::Blake2s { inputs, .. } | BlackBoxFuncCall::Keccak256 { inputs, .. } - | BlackBoxFuncCall::ComputeMerkleRoot { inputs, .. } - | BlackBoxFuncCall::SchnorrVerify { inputs, .. } | BlackBoxFuncCall::Pedersen { inputs, .. } - | BlackBoxFuncCall::HashToField128Security { inputs, .. } - | BlackBoxFuncCall::EcdsaSecp256k1 { inputs, .. } => inputs.to_vec(), + | BlackBoxFuncCall::HashToField128Security { inputs, .. } => inputs.to_vec(), BlackBoxFuncCall::AND { lhs, rhs, .. } | BlackBoxFuncCall::XOR { lhs, rhs, .. } => { vec![*lhs, *rhs] } BlackBoxFuncCall::FixedBaseScalarMul { input, .. } | BlackBoxFuncCall::RANGE { input } => vec![*input], + BlackBoxFuncCall::ComputeMerkleRoot { leaf, index, hash_path, .. } => { + let mut inputs = Vec::new(); + inputs.push(*leaf); + inputs.push(*index); + inputs.extend(hash_path.iter().cloned()); + inputs + } + BlackBoxFuncCall::SchnorrVerify { + public_key_x, + public_key_y, + signature, + message, + .. + } => { + let mut inputs = Vec::new(); + inputs.push(*public_key_x); + inputs.push(*public_key_y); + inputs.extend(signature.iter().cloned()); + inputs.extend(message.iter().cloned()); + inputs + } + BlackBoxFuncCall::EcdsaSecp256k1 { + public_key_x, + public_key_y, + signature, + message, + .. + } => { + let mut inputs = Vec::new(); + inputs.extend(public_key_x.iter().cloned()); + inputs.extend(public_key_y.iter().cloned()); + inputs.extend(signature.iter().cloned()); + inputs.extend(message.iter().cloned()); + inputs + } } } @@ -229,12 +269,19 @@ impl BlackBoxFuncCall { } BlackBoxFunc::SHA256 => BlackBoxFuncCall::SHA256 { inputs, outputs }, BlackBoxFunc::Blake2s => BlackBoxFuncCall::Blake2s { inputs, outputs }, - BlackBoxFunc::ComputeMerkleRoot => { - BlackBoxFuncCall::ComputeMerkleRoot { inputs, output: outputs[0] } - } - BlackBoxFunc::SchnorrVerify => { - BlackBoxFuncCall::SchnorrVerify { inputs, output: outputs[0] } - } + BlackBoxFunc::ComputeMerkleRoot => BlackBoxFuncCall::ComputeMerkleRoot { + leaf: inputs[0], + index: inputs[1], + hash_path: inputs[2..].to_vec(), + output: outputs[0], + }, + BlackBoxFunc::SchnorrVerify => BlackBoxFuncCall::SchnorrVerify { + public_key_x: inputs[0], + public_key_y: inputs[1], + signature: inputs[2..66].to_vec(), + message: inputs[66..].to_vec(), + output: outputs[0], + }, BlackBoxFunc::Pedersen => { let hash_index: u32 = read_u32(&mut reader)?; BlackBoxFuncCall::Pedersen { inputs, outputs, hash_index } @@ -242,9 +289,13 @@ impl BlackBoxFuncCall { BlackBoxFunc::HashToField128Security => { BlackBoxFuncCall::HashToField128Security { inputs, output: outputs[0] } } - BlackBoxFunc::EcdsaSecp256k1 => { - BlackBoxFuncCall::EcdsaSecp256k1 { inputs, output: outputs[0] } - } + BlackBoxFunc::EcdsaSecp256k1 => BlackBoxFuncCall::EcdsaSecp256k1 { + public_key_x: inputs[0..32].to_vec(), + public_key_y: inputs[32..64].to_vec(), + signature: inputs[64..128].to_vec(), + message: inputs[128..].to_vec(), + output: outputs[0], + }, BlackBoxFunc::FixedBaseScalarMul => { BlackBoxFuncCall::FixedBaseScalarMul { input: inputs[0], outputs } } diff --git a/acvm/src/lib.rs b/acvm/src/lib.rs index 438b3b3fd..8dce99ca5 100644 --- a/acvm/src/lib.rs +++ b/acvm/src/lib.rs @@ -100,13 +100,18 @@ pub trait PartialWitnessGenerator { fn compute_merkle_root( &self, initial_witness: &mut BTreeMap, - inputs: &[FunctionInput], + leaf: &FunctionInput, + index: &FunctionInput, + hash_path: &[FunctionInput], output: &Witness, ) -> Result; fn schnorr_verify( &self, initial_witness: &mut BTreeMap, - inputs: &[FunctionInput], + public_key_x: &FunctionInput, + public_key_y: &FunctionInput, + signature: &[FunctionInput], + message: &[FunctionInput], output: &Witness, ) -> Result; fn pedersen( @@ -125,7 +130,10 @@ pub trait PartialWitnessGenerator { fn ecdsa_secp256k1( &self, initial_witness: &mut BTreeMap, - inputs: &[FunctionInput], + public_key_x: &[FunctionInput], + public_key_y: &[FunctionInput], + signature: &[FunctionInput], + message: &[FunctionInput], outputs: &Witness, ) -> Result; fn fixed_base_scalar_mul( @@ -339,7 +347,9 @@ mod test { fn compute_merkle_root( &self, _initial_witness: &mut BTreeMap, - _inputs: &[FunctionInput], + _leaf: &FunctionInput, + _index: &FunctionInput, + _hash_path: &[FunctionInput], _output: &Witness, ) -> Result { { @@ -349,7 +359,10 @@ mod test { fn schnorr_verify( &self, _initial_witness: &mut BTreeMap, - _inputs: &[FunctionInput], + _public_key_x: &FunctionInput, + _public_key_y: &FunctionInput, + _signature: &[FunctionInput], + _message: &[FunctionInput], _output: &Witness, ) -> Result { { @@ -380,7 +393,10 @@ mod test { fn ecdsa_secp256k1( &self, _initial_witness: &mut BTreeMap, - _inputs: &[FunctionInput], + _public_key_x: &[FunctionInput], + _public_key_y: &[FunctionInput], + _signature: &[FunctionInput], + _message: &[FunctionInput], _output: &Witness, ) -> Result { { diff --git a/acvm/src/pwg/blackbox.rs b/acvm/src/pwg/blackbox.rs index a76404a8b..0411541d5 100644 --- a/acvm/src/pwg/blackbox.rs +++ b/acvm/src/pwg/blackbox.rs @@ -66,21 +66,43 @@ pub(crate) fn solve( BlackBoxFuncCall::Blake2s { inputs, outputs } => { backend.blake2s(initial_witness, inputs, outputs) } - BlackBoxFuncCall::ComputeMerkleRoot { inputs, output } => { - backend.compute_merkle_root(initial_witness, inputs, output) - } - BlackBoxFuncCall::SchnorrVerify { inputs, output } => { - backend.schnorr_verify(initial_witness, inputs, output) + BlackBoxFuncCall::ComputeMerkleRoot { leaf, index, hash_path, output } => { + backend.compute_merkle_root(initial_witness, leaf, index, hash_path, output) } + BlackBoxFuncCall::SchnorrVerify { + public_key_x, + public_key_y, + signature, + message, + output, + } => backend.schnorr_verify( + initial_witness, + public_key_x, + public_key_y, + signature, + message, + output, + ), BlackBoxFuncCall::Pedersen { hash_index, inputs, outputs } => { backend.pedersen(initial_witness, *hash_index, inputs, outputs) } BlackBoxFuncCall::HashToField128Security { inputs, output } => { backend.hash_to_field128_security(initial_witness, inputs, output) } - BlackBoxFuncCall::EcdsaSecp256k1 { inputs, output } => { - backend.ecdsa_secp256k1(initial_witness, inputs, output) - } + BlackBoxFuncCall::EcdsaSecp256k1 { + public_key_x, + public_key_y, + signature, + message, + output, + } => backend.ecdsa_secp256k1( + initial_witness, + public_key_x, + public_key_y, + signature, + message, + output, + ), BlackBoxFuncCall::FixedBaseScalarMul { input, outputs } => { backend.fixed_base_scalar_mul(initial_witness, input, outputs) } From 1e9f9980dc7c69c65bb1815de21982558bbd3ee2 Mon Sep 17 00:00:00 2001 From: sirasistant Date: Mon, 8 May 2023 13:31:23 +0000 Subject: [PATCH 04/15] fix: fixed optimizer --- acvm/src/compiler/optimizers/simplify.rs | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/acvm/src/compiler/optimizers/simplify.rs b/acvm/src/compiler/optimizers/simplify.rs index 5373d128f..87921eb0f 100644 --- a/acvm/src/compiler/optimizers/simplify.rs +++ b/acvm/src/compiler/optimizers/simplify.rs @@ -7,7 +7,7 @@ use acir::{ Opcode, }, native_types::{Expression, Witness}, - BlackBoxFunc, FieldElement, + FieldElement, }; use num_bigint::BigUint; @@ -177,14 +177,12 @@ impl CircuitSimplifier { gate_idx: usize, first: bool, ) -> SimplifyResult { - match gadget.name { - BlackBoxFunc::AND | BlackBoxFunc::XOR => { - let result = gadget.outputs.first().expect("Logic opcodes have a result"); - self.use_witness(*result, gate_idx, first); + match gadget { + BlackBoxFuncCall::AND {output, ..} | BlackBoxFuncCall::XOR {output, ..} => { + self.use_witness(*output, gate_idx, first); SimplifyResult::Unresolved } - BlackBoxFunc::RANGE => { - let input = gadget.inputs.first().expect("Range has an input"); + BlackBoxFuncCall::RANGE {input, ..} => { if self.contains(input.witness) { self.use_witness(input.witness, gate_idx, first); let max = BigUint::from_u32(2).unwrap().pow(input.num_bits); @@ -197,13 +195,13 @@ impl CircuitSimplifier { } } _ => { - for i in &gadget.inputs { + for i in gadget.get_inputs_vec() { if self.is_solved(&i.witness) && !self.is_abi(i.witness) { self.defined.insert(i.witness); } } - for i in &gadget.outputs { - self.use_witness(*i, gate_idx, first); + for i in gadget.get_outputs_vec() { + self.use_witness(i, gate_idx, first); } SimplifyResult::Unresolved } From f50e894a3919ac7dd9d0174022287684df4b2250 Mon Sep 17 00:00:00 2001 From: sirasistant Date: Mon, 8 May 2023 13:34:59 +0000 Subject: [PATCH 05/15] style: fix format --- acvm/src/compiler/optimizers/simplify.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/acvm/src/compiler/optimizers/simplify.rs b/acvm/src/compiler/optimizers/simplify.rs index 87921eb0f..ae2b378c3 100644 --- a/acvm/src/compiler/optimizers/simplify.rs +++ b/acvm/src/compiler/optimizers/simplify.rs @@ -178,11 +178,11 @@ impl CircuitSimplifier { first: bool, ) -> SimplifyResult { match gadget { - BlackBoxFuncCall::AND {output, ..} | BlackBoxFuncCall::XOR {output, ..} => { + BlackBoxFuncCall::AND { output, .. } | BlackBoxFuncCall::XOR { output, .. } => { self.use_witness(*output, gate_idx, first); SimplifyResult::Unresolved } - BlackBoxFuncCall::RANGE {input, ..} => { + BlackBoxFuncCall::RANGE { input, .. } => { if self.contains(input.witness) { self.use_witness(input.witness, gate_idx, first); let max = BigUint::from_u32(2).unwrap().pow(input.num_bits); From 5727de15f90b27e83feab5a65f7da49cb43121f8 Mon Sep 17 00:00:00 2001 From: sirasistant Date: Mon, 8 May 2023 13:58:52 +0000 Subject: [PATCH 06/15] fix: update ecdsa signature --- acvm/src/pwg/signature/ecdsa.rs | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/acvm/src/pwg/signature/ecdsa.rs b/acvm/src/pwg/signature/ecdsa.rs index b22a81cf0..cc616dfe8 100644 --- a/acvm/src/pwg/signature/ecdsa.rs +++ b/acvm/src/pwg/signature/ecdsa.rs @@ -5,14 +5,17 @@ use crate::{pwg::witness_to_value, pwg::OpcodeResolution, OpcodeResolutionError} pub fn secp256k1_prehashed( initial_witness: &mut BTreeMap, - inputs: &[FunctionInput], - outputs: &[Witness], + public_key_x_inputs: &[FunctionInput], + public_key_y_inputs: &[FunctionInput], + signature_inputs: &[FunctionInput], + message_inputs: &[FunctionInput], + output: Witness, ) -> Result { - let mut inputs_iter = inputs.iter(); - let mut pub_key_x = [0u8; 32]; + let mut public_key_x_inputs = public_key_x_inputs.iter(); + for (i, pkx) in pub_key_x.iter_mut().enumerate() { - let _x_i = inputs_iter + let _x_i = public_key_x_inputs .next() .unwrap_or_else(|| panic!("pub_key_x should be 32 bytes long, found only {i} bytes")); @@ -21,8 +24,10 @@ pub fn secp256k1_prehashed( } let mut pub_key_y = [0u8; 32]; + let mut public_key_y_inputs = public_key_y_inputs.iter(); + for (i, pky) in pub_key_y.iter_mut().enumerate() { - let _y_i = inputs_iter + let _y_i = public_key_y_inputs .next() .unwrap_or_else(|| panic!("pub_key_y should be 32 bytes long, found only {i} bytes")); @@ -31,8 +36,10 @@ pub fn secp256k1_prehashed( } let mut signature = [0u8; 64]; + let mut signature_inputs = signature_inputs.iter(); + for (i, sig) in signature.iter_mut().enumerate() { - let _sig_i = inputs_iter + let _sig_i = signature_inputs .next() .unwrap_or_else(|| panic!("signature should be 64 bytes long, found only {i} bytes")); @@ -41,7 +48,7 @@ pub fn secp256k1_prehashed( } let mut hashed_message = Vec::new(); - for msg in inputs_iter { + for msg in message_inputs.iter() { let msg_i_field = witness_to_value(initial_witness, msg.witness)?; let msg_i = *msg_i_field.to_be_bytes().last().unwrap(); hashed_message.push(msg_i); @@ -51,7 +58,7 @@ pub fn secp256k1_prehashed( ecdsa_secp256k1::verify_prehashed(&hashed_message, &pub_key_x, &pub_key_y, &signature) .is_ok(); - initial_witness.insert(outputs[0], FieldElement::from(result)); + initial_witness.insert(output, FieldElement::from(result)); Ok(OpcodeResolution::Solved) } From beae69f1894673465d9ab2072ff98e7001881cfc Mon Sep 17 00:00:00 2001 From: sirasistant Date: Tue, 9 May 2023 07:16:16 +0000 Subject: [PATCH 07/15] fix: remove hash_index from pedersen --- .../circuit/opcodes/black_box_function_call.rs | 18 ++---------------- acvm/src/lib.rs | 2 -- acvm/src/pwg/blackbox.rs | 4 ++-- 3 files changed, 4 insertions(+), 20 deletions(-) diff --git a/acir/src/circuit/opcodes/black_box_function_call.rs b/acir/src/circuit/opcodes/black_box_function_call.rs index d2b6630b9..d62bd605a 100644 --- a/acir/src/circuit/opcodes/black_box_function_call.rs +++ b/acir/src/circuit/opcodes/black_box_function_call.rs @@ -55,7 +55,6 @@ pub enum BlackBoxFuncCall { output: Witness, }, Pedersen { - hash_index: u32, inputs: Vec, outputs: Vec, }, @@ -235,10 +234,6 @@ impl BlackBoxFuncCall { write_inputs(&self.get_inputs_vec(), &mut writer)?; write_outputs(&self.get_outputs_vec(), &mut writer)?; - if let BlackBoxFuncCall::Pedersen { hash_index, .. } = self { - write_u32(&mut writer, *hash_index)?; - } - Ok(()) } @@ -282,10 +277,7 @@ impl BlackBoxFuncCall { message: inputs[66..].to_vec(), output: outputs[0], }, - BlackBoxFunc::Pedersen => { - let hash_index: u32 = read_u32(&mut reader)?; - BlackBoxFuncCall::Pedersen { inputs, outputs, hash_index } - } + BlackBoxFunc::Pedersen => BlackBoxFuncCall::Pedersen { inputs, outputs }, BlackBoxFunc::HashToField128Security => { BlackBoxFuncCall::HashToField128Security { inputs, output: outputs[0] } } @@ -385,13 +377,7 @@ impl std::fmt::Display for BlackBoxFuncCall { write!(f, "{outputs_str}")?; - write!(f, "]")?; - - // FUNCTION SPECIFIC PARAMETERS - match self { - BlackBoxFuncCall::Pedersen { hash_index, .. } => write!(f, " hash_index: {hash_index}"), - _ => write!(f, ""), - } + write!(f, "]") } } diff --git a/acvm/src/lib.rs b/acvm/src/lib.rs index 8dce99ca5..7c6ea652f 100644 --- a/acvm/src/lib.rs +++ b/acvm/src/lib.rs @@ -117,7 +117,6 @@ pub trait PartialWitnessGenerator { fn pedersen( &self, initial_witness: &mut BTreeMap, - hash_index: u32, inputs: &[FunctionInput], outputs: &[Witness], ) -> Result; @@ -372,7 +371,6 @@ mod test { fn pedersen( &self, _initial_witness: &mut BTreeMap, - _hash_index: u32, _inputs: &[FunctionInput], _outputs: &[Witness], ) -> Result { diff --git a/acvm/src/pwg/blackbox.rs b/acvm/src/pwg/blackbox.rs index 0411541d5..94c587372 100644 --- a/acvm/src/pwg/blackbox.rs +++ b/acvm/src/pwg/blackbox.rs @@ -83,8 +83,8 @@ pub(crate) fn solve( message, output, ), - BlackBoxFuncCall::Pedersen { hash_index, inputs, outputs } => { - backend.pedersen(initial_witness, *hash_index, inputs, outputs) + BlackBoxFuncCall::Pedersen { inputs, outputs } => { + backend.pedersen(initial_witness, inputs, outputs) } BlackBoxFuncCall::HashToField128Security { inputs, output } => { backend.hash_to_field128_security(initial_witness, inputs, output) From b6eda1505c71857314dc0ab71e5aecdccb79b7ec Mon Sep 17 00:00:00 2001 From: sirasistant Date: Tue, 9 May 2023 08:08:56 +0000 Subject: [PATCH 08/15] feat(bb): remove bb fn definitions --- acir/src/circuit/black_box_functions.rs | 84 ------------------------- 1 file changed, 84 deletions(-) diff --git a/acir/src/circuit/black_box_functions.rs b/acir/src/circuit/black_box_functions.rs index 30d6df516..5602f9658 100644 --- a/acir/src/circuit/black_box_functions.rs +++ b/acir/src/circuit/black_box_functions.rs @@ -105,90 +105,6 @@ impl BlackBoxFunc { pub fn is_valid_black_box_func_name(op_name: &str) -> bool { BlackBoxFunc::lookup(op_name).is_some() } - pub fn definition(&self) -> FuncDefinition { - let name = self.name(); - match self { - BlackBoxFunc::AES => unimplemented!(), - BlackBoxFunc::SHA256 => FuncDefinition { - name, - input_size: InputSize::Variable, - output_size: OutputSize(32), - }, - BlackBoxFunc::Blake2s => FuncDefinition { - name, - input_size: InputSize::Variable, - output_size: OutputSize(32), - }, - BlackBoxFunc::HashToField128Security => { - FuncDefinition { name, input_size: InputSize::Variable, output_size: OutputSize(1) } - } - BlackBoxFunc::ComputeMerkleRoot => { - FuncDefinition { name, input_size: InputSize::Variable, output_size: OutputSize(1) } - } - BlackBoxFunc::SchnorrVerify => FuncDefinition { - name, - // XXX: input_size can be changed to fixed, once we hash - // the message before passing it to schnorr. - // This is assuming all hashes will be 256 bits. Reasonable? - input_size: InputSize::Variable, - output_size: OutputSize(1), - }, - BlackBoxFunc::Pedersen => { - FuncDefinition { name, input_size: InputSize::Variable, output_size: OutputSize(2) } - } - BlackBoxFunc::EcdsaSecp256k1 => { - FuncDefinition { name, input_size: InputSize::Variable, output_size: OutputSize(1) } - } - BlackBoxFunc::FixedBaseScalarMul => { - FuncDefinition { name, input_size: InputSize::Fixed(1), output_size: OutputSize(2) } - } - BlackBoxFunc::AND => { - FuncDefinition { name, input_size: InputSize::Fixed(2), output_size: OutputSize(1) } - } - BlackBoxFunc::XOR => { - FuncDefinition { name, input_size: InputSize::Fixed(2), output_size: OutputSize(1) } - } - BlackBoxFunc::RANGE => { - FuncDefinition { name, input_size: InputSize::Fixed(1), output_size: OutputSize(0) } - } - BlackBoxFunc::Keccak256 => FuncDefinition { - name, - input_size: InputSize::Variable, - output_size: OutputSize(32), - }, - } - } -} - -// Descriptor as to whether the input/output is fixed or variable -// Example: The input for Sha256 is Variable and the output is fixed at 2 witnesses -// each holding 128 bits of the actual Sha256 function -#[derive(Clone, Debug, Hash, PartialEq, Eq)] -pub enum InputSize { - Variable, - Fixed(u128), -} - -impl InputSize { - pub fn fixed_size(&self) -> Option { - match self { - InputSize::Variable => None, - InputSize::Fixed(size) => Some(*size), - } - } -} - -// Output size Cannot currently vary, so we use a separate struct -// XXX: In the future, we may be able to allow the output to vary based on the input size, however this implies support for dynamic circuits -#[derive(Clone, Debug, Hash, PartialEq, Eq)] -pub struct OutputSize(pub u128); - -#[derive(Clone, Debug, Hash)] -// Specs for how many inputs/outputs the method takes. -pub struct FuncDefinition { - pub name: &'static str, - pub input_size: InputSize, - pub output_size: OutputSize, } #[cfg(test)] From e4bbf564230a19832f1eb3285d59545d3e7c9a9b Mon Sep 17 00:00:00 2001 From: sirasistant Date: Tue, 9 May 2023 14:57:00 +0000 Subject: [PATCH 09/15] refactor: extract mapping to u8 vec to a fn --- acvm/src/pwg/signature/ecdsa.rs | 60 ++++++++++++++------------------- 1 file changed, 25 insertions(+), 35 deletions(-) diff --git a/acvm/src/pwg/signature/ecdsa.rs b/acvm/src/pwg/signature/ecdsa.rs index cc616dfe8..7021c4c7b 100644 --- a/acvm/src/pwg/signature/ecdsa.rs +++ b/acvm/src/pwg/signature/ecdsa.rs @@ -3,6 +3,19 @@ use std::collections::BTreeMap; use crate::{pwg::witness_to_value, pwg::OpcodeResolution, OpcodeResolutionError}; +fn to_u8_vec( + initial_witness: &mut BTreeMap, + value: &[FunctionInput], +) -> Result, OpcodeResolutionError> { + let mut result = Vec::new(); + for input in value { + let w_value = witness_to_value(initial_witness, input.witness)?.to_be_bytes(); + let byte = w_value.last().unwrap(); + result.push(*byte); + } + Ok(result) +} + pub fn secp256k1_prehashed( initial_witness: &mut BTreeMap, public_key_x_inputs: &[FunctionInput], @@ -11,41 +24,18 @@ pub fn secp256k1_prehashed( message_inputs: &[FunctionInput], output: Witness, ) -> Result { - let mut pub_key_x = [0u8; 32]; - let mut public_key_x_inputs = public_key_x_inputs.iter(); - - for (i, pkx) in pub_key_x.iter_mut().enumerate() { - let _x_i = public_key_x_inputs - .next() - .unwrap_or_else(|| panic!("pub_key_x should be 32 bytes long, found only {i} bytes")); - - let x_i = witness_to_value(initial_witness, _x_i.witness)?; - *pkx = *x_i.to_be_bytes().last().unwrap(); - } - - let mut pub_key_y = [0u8; 32]; - let mut public_key_y_inputs = public_key_y_inputs.iter(); - - for (i, pky) in pub_key_y.iter_mut().enumerate() { - let _y_i = public_key_y_inputs - .next() - .unwrap_or_else(|| panic!("pub_key_y should be 32 bytes long, found only {i} bytes")); - - let y_i = witness_to_value(initial_witness, _y_i.witness)?; - *pky = *y_i.to_be_bytes().last().unwrap(); - } - - let mut signature = [0u8; 64]; - let mut signature_inputs = signature_inputs.iter(); - - for (i, sig) in signature.iter_mut().enumerate() { - let _sig_i = signature_inputs - .next() - .unwrap_or_else(|| panic!("signature should be 64 bytes long, found only {i} bytes")); - - let sig_i = witness_to_value(initial_witness, _sig_i.witness)?; - *sig = *sig_i.to_be_bytes().last().unwrap() - } + let pub_key_x: [u8; 32] = + to_u8_vec(initial_witness, public_key_x_inputs)?.try_into().unwrap_or_else(|_| { + panic!("pub_key_x should be 32 bytes long, found {} bytes", public_key_x_inputs.len()) + }); + let pub_key_y: [u8; 32] = + to_u8_vec(initial_witness, public_key_y_inputs)?.try_into().unwrap_or_else(|_| { + panic!("pub_key_y should be 32 bytes long, found {} bytes", public_key_y_inputs.len()) + }); + let signature: [u8; 32] = + to_u8_vec(initial_witness, signature_inputs)?.try_into().unwrap_or_else(|_| { + panic!("signature should be 64 bytes long, found {} bytes", signature_inputs.len()) + }); let mut hashed_message = Vec::new(); for msg in message_inputs.iter() { From ade54b1dc8797c64e861df429212f9fe5e189e72 Mon Sep 17 00:00:00 2001 From: sirasistant Date: Tue, 9 May 2023 16:14:11 +0000 Subject: [PATCH 10/15] fix: address PR comments --- .../opcodes/black_box_function_call.rs | 143 +++++++++++------- acvm/src/pwg/blackbox.rs | 2 +- acvm/src/pwg/signature/ecdsa.rs | 20 +-- 3 files changed, 98 insertions(+), 67 deletions(-) diff --git a/acir/src/circuit/opcodes/black_box_function_call.rs b/acir/src/circuit/opcodes/black_box_function_call.rs index d62bd605a..8a0cd4c7c 100644 --- a/acir/src/circuit/opcodes/black_box_function_call.rs +++ b/acir/src/circuit/opcodes/black_box_function_call.rs @@ -68,7 +68,7 @@ pub enum BlackBoxFuncCall { public_key_x: Vec, public_key_y: Vec, signature: Vec, - message: Vec, + hashed_message: Vec, output: Witness, }, FixedBaseScalarMul { @@ -173,10 +173,10 @@ impl BlackBoxFuncCall { BlackBoxFuncCall::FixedBaseScalarMul { input, .. } | BlackBoxFuncCall::RANGE { input } => vec![*input], BlackBoxFuncCall::ComputeMerkleRoot { leaf, index, hash_path, .. } => { - let mut inputs = Vec::new(); + let mut inputs = Vec::with_capacity(2 + hash_path.len()); inputs.push(*leaf); inputs.push(*index); - inputs.extend(hash_path.iter().cloned()); + inputs.extend(hash_path.iter().copied()); inputs } BlackBoxFuncCall::SchnorrVerify { @@ -186,25 +186,27 @@ impl BlackBoxFuncCall { message, .. } => { - let mut inputs = Vec::new(); + let mut inputs = Vec::with_capacity(2 + signature.len() + message.len()); inputs.push(*public_key_x); inputs.push(*public_key_y); - inputs.extend(signature.iter().cloned()); - inputs.extend(message.iter().cloned()); + inputs.extend(signature.iter().copied()); + inputs.extend(message.iter().copied()); inputs } BlackBoxFuncCall::EcdsaSecp256k1 { public_key_x, public_key_y, signature, - message, + hashed_message: message, .. } => { - let mut inputs = Vec::new(); - inputs.extend(public_key_x.iter().cloned()); - inputs.extend(public_key_y.iter().cloned()); - inputs.extend(signature.iter().cloned()); - inputs.extend(message.iter().cloned()); + let mut inputs = Vec::with_capacity( + public_key_x.len() + public_key_y.len() + signature.len() + message.len(), + ); + inputs.extend(public_key_x.iter().copied()); + inputs.extend(public_key_y.iter().copied()); + inputs.extend(signature.iter().copied()); + inputs.extend(message.iter().copied()); inputs } } @@ -244,57 +246,92 @@ impl BlackBoxFuncCall { let inputs = read_inputs(&mut reader)?; let outputs = read_outputs(&mut reader)?; - let func_call = match name { - BlackBoxFunc::AES => BlackBoxFuncCall::AES { inputs, outputs }, + match name { + BlackBoxFunc::AES => Ok(BlackBoxFuncCall::AES { inputs, outputs }), BlackBoxFunc::AND => { - let lhs = inputs[0]; - let rhs = inputs[1]; - let output = outputs[0]; - BlackBoxFuncCall::AND { lhs, rhs, output } + if inputs.len() < 2 || outputs.is_empty() { + Err(std::io::ErrorKind::InvalidData.into()) + } else { + let lhs = inputs[0]; + let rhs = inputs[1]; + let output = outputs[0]; + Ok(BlackBoxFuncCall::AND { lhs, rhs, output }) + } } BlackBoxFunc::XOR => { - let lhs = inputs[0]; - let rhs = inputs[1]; - let output = outputs[0]; - BlackBoxFuncCall::XOR { lhs, rhs, output } + if inputs.len() < 2 || outputs.is_empty() { + Err(std::io::ErrorKind::InvalidData.into()) + } else { + let lhs = inputs[0]; + let rhs = inputs[1]; + let output = outputs[0]; + Ok(BlackBoxFuncCall::XOR { lhs, rhs, output }) + } } BlackBoxFunc::RANGE => { - let input = inputs[0]; - BlackBoxFuncCall::RANGE { input } + if inputs.is_empty() { + Err(std::io::ErrorKind::InvalidData.into()) + } else { + Ok(BlackBoxFuncCall::RANGE { input: inputs[0] }) + } + } + BlackBoxFunc::SHA256 => Ok(BlackBoxFuncCall::SHA256 { inputs, outputs }), + BlackBoxFunc::Blake2s => Ok(BlackBoxFuncCall::Blake2s { inputs, outputs }), + BlackBoxFunc::ComputeMerkleRoot => { + if inputs.len() < 2 || outputs.is_empty() { + Err(std::io::ErrorKind::InvalidData.into()) + } else { + Ok(BlackBoxFuncCall::ComputeMerkleRoot { + leaf: inputs[0], + index: inputs[1], + hash_path: inputs[2..].to_vec(), + output: outputs[0], + }) + } + } + BlackBoxFunc::SchnorrVerify => { + if inputs.len() < 66 || outputs.is_empty() { + Err(std::io::ErrorKind::InvalidData.into()) + } else { + Ok(BlackBoxFuncCall::SchnorrVerify { + public_key_x: inputs[0], + public_key_y: inputs[1], + signature: inputs[2..66].to_vec(), + message: inputs[66..].to_vec(), + output: outputs[0], + }) + } } - BlackBoxFunc::SHA256 => BlackBoxFuncCall::SHA256 { inputs, outputs }, - BlackBoxFunc::Blake2s => BlackBoxFuncCall::Blake2s { inputs, outputs }, - BlackBoxFunc::ComputeMerkleRoot => BlackBoxFuncCall::ComputeMerkleRoot { - leaf: inputs[0], - index: inputs[1], - hash_path: inputs[2..].to_vec(), - output: outputs[0], - }, - BlackBoxFunc::SchnorrVerify => BlackBoxFuncCall::SchnorrVerify { - public_key_x: inputs[0], - public_key_y: inputs[1], - signature: inputs[2..66].to_vec(), - message: inputs[66..].to_vec(), - output: outputs[0], - }, - BlackBoxFunc::Pedersen => BlackBoxFuncCall::Pedersen { inputs, outputs }, + BlackBoxFunc::Pedersen => Ok(BlackBoxFuncCall::Pedersen { inputs, outputs }), BlackBoxFunc::HashToField128Security => { - BlackBoxFuncCall::HashToField128Security { inputs, output: outputs[0] } + if outputs.is_empty() { + Err(std::io::ErrorKind::InvalidData.into()) + } else { + Ok(BlackBoxFuncCall::HashToField128Security { inputs, output: outputs[0] }) + } + } + BlackBoxFunc::EcdsaSecp256k1 => { + if inputs.len() < 128 || outputs.is_empty() { + Err(std::io::ErrorKind::InvalidData.into()) + } else { + Ok(BlackBoxFuncCall::EcdsaSecp256k1 { + public_key_x: inputs[0..32].to_vec(), + public_key_y: inputs[32..64].to_vec(), + signature: inputs[64..128].to_vec(), + hashed_message: inputs[128..].to_vec(), + output: outputs[0], + }) + } } - BlackBoxFunc::EcdsaSecp256k1 => BlackBoxFuncCall::EcdsaSecp256k1 { - public_key_x: inputs[0..32].to_vec(), - public_key_y: inputs[32..64].to_vec(), - signature: inputs[64..128].to_vec(), - message: inputs[128..].to_vec(), - output: outputs[0], - }, BlackBoxFunc::FixedBaseScalarMul => { - BlackBoxFuncCall::FixedBaseScalarMul { input: inputs[0], outputs } + if inputs.is_empty() { + Err(std::io::ErrorKind::InvalidData.into()) + } else { + Ok(BlackBoxFuncCall::FixedBaseScalarMul { input: inputs[0], outputs }) + } } - BlackBoxFunc::Keccak256 => BlackBoxFuncCall::Keccak256 { inputs, outputs }, - }; - - Ok(func_call) + BlackBoxFunc::Keccak256 => Ok(BlackBoxFuncCall::Keccak256 { inputs, outputs }), + } } } diff --git a/acvm/src/pwg/blackbox.rs b/acvm/src/pwg/blackbox.rs index 94c587372..ee121037d 100644 --- a/acvm/src/pwg/blackbox.rs +++ b/acvm/src/pwg/blackbox.rs @@ -93,7 +93,7 @@ pub(crate) fn solve( public_key_x, public_key_y, signature, - message, + hashed_message: message, output, } => backend.ecdsa_secp256k1( initial_witness, diff --git a/acvm/src/pwg/signature/ecdsa.rs b/acvm/src/pwg/signature/ecdsa.rs index 7021c4c7b..b3ce4df98 100644 --- a/acvm/src/pwg/signature/ecdsa.rs +++ b/acvm/src/pwg/signature/ecdsa.rs @@ -4,13 +4,13 @@ use std::collections::BTreeMap; use crate::{pwg::witness_to_value, pwg::OpcodeResolution, OpcodeResolutionError}; fn to_u8_vec( - initial_witness: &mut BTreeMap, - value: &[FunctionInput], + initial_witness: &BTreeMap, + inputs: &[FunctionInput], ) -> Result, OpcodeResolutionError> { - let mut result = Vec::new(); - for input in value { - let w_value = witness_to_value(initial_witness, input.witness)?.to_be_bytes(); - let byte = w_value.last().unwrap(); + let mut result = Vec::with_capacity(inputs.len()); + for input in inputs { + let witness_value_bytes = witness_to_value(initial_witness, input.witness)?.to_be_bytes(); + let byte = witness_value_bytes.last().unwrap(); result.push(*byte); } Ok(result) @@ -37,13 +37,7 @@ pub fn secp256k1_prehashed( panic!("signature should be 64 bytes long, found {} bytes", signature_inputs.len()) }); - let mut hashed_message = Vec::new(); - for msg in message_inputs.iter() { - let msg_i_field = witness_to_value(initial_witness, msg.witness)?; - let msg_i = *msg_i_field.to_be_bytes().last().unwrap(); - hashed_message.push(msg_i); - } - + let hashed_message = to_u8_vec(initial_witness, message_inputs)?; let result = ecdsa_secp256k1::verify_prehashed(&hashed_message, &pub_key_x, &pub_key_y, &signature) .is_ok(); From c27768c54d0a4adcc67922b2ad1daa4e7cbbf844 Mon Sep 17 00:00:00 2001 From: sirasistant Date: Tue, 9 May 2023 17:02:15 +0000 Subject: [PATCH 11/15] refactor: use slice instead of vec --- acir/src/circuit/opcodes/black_box_function_call.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/acir/src/circuit/opcodes/black_box_function_call.rs b/acir/src/circuit/opcodes/black_box_function_call.rs index 8a0cd4c7c..b0e9d29eb 100644 --- a/acir/src/circuit/opcodes/black_box_function_call.rs +++ b/acir/src/circuit/opcodes/black_box_function_call.rs @@ -87,7 +87,7 @@ fn write_input(input: &FunctionInput, mut writer: W) -> std::io::Resul Ok(()) } -fn write_inputs(inputs: &Vec, mut writer: W) -> std::io::Result<()> { +fn write_inputs(inputs: &[FunctionInput], mut writer: W) -> std::io::Result<()> { let num_inputs = inputs.len() as u32; write_u32(&mut writer, num_inputs)?; @@ -98,7 +98,7 @@ fn write_inputs(inputs: &Vec, mut writer: W) -> std::io Ok(()) } -fn write_outputs(outputs: &Vec, mut writer: W) -> std::io::Result<()> { +fn write_outputs(outputs: &[Witness], mut writer: W) -> std::io::Result<()> { let num_inputs = outputs.len() as u32; write_u32(&mut writer, num_inputs)?; @@ -337,7 +337,7 @@ impl BlackBoxFuncCall { const ABBREVIATION_LIMIT: usize = 5; -fn get_inputs_string(inputs: &Vec) -> String { +fn get_inputs_string(inputs: &[FunctionInput]) -> String { // Once a vectors length gets above this limit, // instead of listing all of their elements, we use ellipses // to abbreviate them @@ -371,7 +371,7 @@ fn get_inputs_string(inputs: &Vec) -> String { } } -fn get_outputs_string(outputs: &Vec) -> String { +fn get_outputs_string(outputs: &[Witness]) -> String { let should_abbreviate_outputs = outputs.len() <= ABBREVIATION_LIMIT; if should_abbreviate_outputs { From 784f62176e090b7337d39ab79cc608d4118f59f0 Mon Sep 17 00:00:00 2001 From: sirasistant Date: Wed, 10 May 2023 12:02:15 +0000 Subject: [PATCH 12/15] feat: add default is bb supported fn --- acvm/src/lib.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/acvm/src/lib.rs b/acvm/src/lib.rs index fa4bd8a6b..cbe67dc77 100644 --- a/acvm/src/lib.rs +++ b/acvm/src/lib.rs @@ -260,6 +260,23 @@ pub fn default_is_opcode_supported( } } +pub fn default_is_bb_supported( + language: Language, +) -> impl Fn(BlackBoxFunc) -> bool { + fn r1cs_is_supported(_: BlackBoxFunc) -> bool { + false + } + + fn plonk_is_supported(opcode: BlackBoxFunc) -> bool { + !matches!(opcode, BlackBoxFunc::AES) + } + + match language { + Language::R1CS => r1cs_is_supported, + Language::PLONKCSat { .. } => plonk_is_supported, + } +} + #[cfg(test)] mod test { use std::collections::BTreeMap; From fab177484259421a58033db40246d8444465a19c Mon Sep 17 00:00:00 2001 From: sirasistant Date: Wed, 10 May 2023 12:16:28 +0000 Subject: [PATCH 13/15] fix: do not panic in ecdsa --- acvm/src/lib.rs | 4 +--- acvm/src/pwg/signature/ecdsa.rs | 31 +++++++++++++++++++++---------- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/acvm/src/lib.rs b/acvm/src/lib.rs index cbe67dc77..c906f40fc 100644 --- a/acvm/src/lib.rs +++ b/acvm/src/lib.rs @@ -260,9 +260,7 @@ pub fn default_is_opcode_supported( } } -pub fn default_is_bb_supported( - language: Language, -) -> impl Fn(BlackBoxFunc) -> bool { +pub fn default_is_bb_supported(language: Language) -> impl Fn(BlackBoxFunc) -> bool { fn r1cs_is_supported(_: BlackBoxFunc) -> bool { false } diff --git a/acvm/src/pwg/signature/ecdsa.rs b/acvm/src/pwg/signature/ecdsa.rs index b3ce4df98..2106fae2e 100644 --- a/acvm/src/pwg/signature/ecdsa.rs +++ b/acvm/src/pwg/signature/ecdsa.rs @@ -25,17 +25,28 @@ pub fn secp256k1_prehashed( output: Witness, ) -> Result { let pub_key_x: [u8; 32] = - to_u8_vec(initial_witness, public_key_x_inputs)?.try_into().unwrap_or_else(|_| { - panic!("pub_key_x should be 32 bytes long, found {} bytes", public_key_x_inputs.len()) - }); + to_u8_vec(initial_witness, public_key_x_inputs)?.try_into().map_err(|_| { + OpcodeResolutionError::BlackBoxFunctionFailed( + acir::BlackBoxFunc::EcdsaSecp256k1, + format!("expected pubkey_x size 32 but received {}", public_key_x_inputs.len()), + ) + })?; + let pub_key_y: [u8; 32] = - to_u8_vec(initial_witness, public_key_y_inputs)?.try_into().unwrap_or_else(|_| { - panic!("pub_key_y should be 32 bytes long, found {} bytes", public_key_y_inputs.len()) - }); - let signature: [u8; 32] = - to_u8_vec(initial_witness, signature_inputs)?.try_into().unwrap_or_else(|_| { - panic!("signature should be 64 bytes long, found {} bytes", signature_inputs.len()) - }); + to_u8_vec(initial_witness, public_key_y_inputs)?.try_into().map_err(|_| { + OpcodeResolutionError::BlackBoxFunctionFailed( + acir::BlackBoxFunc::EcdsaSecp256k1, + format!("expected pubkey_y size 32 but received {}", public_key_y_inputs.len()), + ) + })?; + + let signature: [u8; 64] = + to_u8_vec(initial_witness, signature_inputs)?.try_into().map_err(|_| { + OpcodeResolutionError::BlackBoxFunctionFailed( + acir::BlackBoxFunc::EcdsaSecp256k1, + format!("expected signature size 64 but received {}", signature_inputs.len()), + ) + })?; let hashed_message = to_u8_vec(initial_witness, message_inputs)?; let result = From 4b41a6f451af9a33bfb707c81349f1dfeced8757 Mon Sep 17 00:00:00 2001 From: sirasistant Date: Wed, 10 May 2023 16:45:16 +0000 Subject: [PATCH 14/15] refactor: add a name fn to bb fn call --- acir/src/circuit/opcodes.rs | 2 +- acir/src/circuit/opcodes/black_box_function_call.rs | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/acir/src/circuit/opcodes.rs b/acir/src/circuit/opcodes.rs index 89441eedd..c0a394bae 100644 --- a/acir/src/circuit/opcodes.rs +++ b/acir/src/circuit/opcodes.rs @@ -43,7 +43,7 @@ impl Opcode { match self { Opcode::Arithmetic(_) => "arithmetic", Opcode::Directive(directive) => directive.name(), - Opcode::BlackBoxFuncCall(g) => g.get_black_box_func().name(), + Opcode::BlackBoxFuncCall(g) => g.name(), Opcode::Block(_) => "block", Opcode::RAM(_) => "ram", Opcode::ROM(_) => "rom", diff --git a/acir/src/circuit/opcodes/black_box_function_call.rs b/acir/src/circuit/opcodes/black_box_function_call.rs index b0e9d29eb..c74fba737 100644 --- a/acir/src/circuit/opcodes/black_box_function_call.rs +++ b/acir/src/circuit/opcodes/black_box_function_call.rs @@ -159,6 +159,10 @@ impl BlackBoxFuncCall { } } + pub fn name(&self) -> &str { + self.get_black_box_func().name() + } + pub fn get_inputs_vec(&self) -> Vec { match self { BlackBoxFuncCall::AES { inputs, .. } @@ -396,8 +400,7 @@ fn get_outputs_string(outputs: &[Witness]) -> String { impl std::fmt::Display for BlackBoxFuncCall { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let uppercase_name: String = self.get_black_box_func().name().into(); - let uppercase_name = uppercase_name.to_uppercase(); + let uppercase_name = self.name().to_uppercase(); write!(f, "BLACKBOX::{uppercase_name} ")?; // INPUTS write!(f, "[")?; From 018d6865e03ddd09ed409b383f38971ec9ec3310 Mon Sep 17 00:00:00 2001 From: sirasistant Date: Thu, 11 May 2023 07:56:17 +0000 Subject: [PATCH 15/15] refactor: address pr comments --- .../opcodes/black_box_function_call.rs | 88 ++++++++++++++++--- acvm/src/lib.rs | 15 ---- 2 files changed, 74 insertions(+), 29 deletions(-) diff --git a/acir/src/circuit/opcodes/black_box_function_call.rs b/acir/src/circuit/opcodes/black_box_function_call.rs index c74fba737..d53486b07 100644 --- a/acir/src/circuit/opcodes/black_box_function_call.rs +++ b/acir/src/circuit/opcodes/black_box_function_call.rs @@ -13,6 +13,12 @@ pub struct FunctionInput { pub num_bits: u32, } +impl FunctionInput { + pub fn dummy() -> Self { + Self { witness: Witness(0), num_bits: 0 } + } +} + #[derive(Clone, PartialEq, Eq, Serialize, Deserialize)] pub enum BlackBoxFuncCall { #[allow(clippy::upper_case_acronyms)] @@ -118,7 +124,8 @@ fn read_input(mut reader: R) -> std::io::Result { fn read_inputs(mut reader: R) -> std::io::Result> { let num_inputs = read_u32(&mut reader)?; - let mut inputs = Vec::with_capacity(num_inputs as usize); + let mut inputs = Vec::new(); + inputs.try_reserve_exact(num_inputs as usize).map_err(|_| std::io::ErrorKind::InvalidData)?; for _ in 0..num_inputs { inputs.push(read_input(&mut reader)?); @@ -128,19 +135,72 @@ fn read_inputs(mut reader: R) -> std::io::Result> { } fn read_outputs(mut reader: R) -> std::io::Result> { - let num_inputs = read_u32(&mut reader)?; + let num_outputs = read_u32(&mut reader)?; - let mut inputs = Vec::with_capacity(num_inputs as usize); + let mut outputs = Vec::new(); + outputs.try_reserve_exact(num_outputs as usize).map_err(|_| std::io::ErrorKind::InvalidData)?; - for _ in 0..num_inputs { + for _ in 0..num_outputs { let witness_index = read_u32(&mut reader)?; - inputs.push(Witness::new(witness_index)); + outputs.push(Witness::new(witness_index)); } - Ok(inputs) + Ok(outputs) } impl BlackBoxFuncCall { + pub fn dummy(bb_func: BlackBoxFunc) -> Self { + match bb_func { + BlackBoxFunc::AES => BlackBoxFuncCall::AES { inputs: vec![], outputs: vec![] }, + BlackBoxFunc::AND => BlackBoxFuncCall::AND { + lhs: FunctionInput::dummy(), + rhs: FunctionInput::dummy(), + output: Witness(0), + }, + BlackBoxFunc::XOR => BlackBoxFuncCall::XOR { + lhs: FunctionInput::dummy(), + rhs: FunctionInput::dummy(), + output: Witness(0), + }, + BlackBoxFunc::RANGE => BlackBoxFuncCall::RANGE { input: FunctionInput::dummy() }, + BlackBoxFunc::SHA256 => BlackBoxFuncCall::SHA256 { inputs: vec![], outputs: vec![] }, + BlackBoxFunc::Blake2s => BlackBoxFuncCall::Blake2s { inputs: vec![], outputs: vec![] }, + BlackBoxFunc::ComputeMerkleRoot => BlackBoxFuncCall::ComputeMerkleRoot { + leaf: FunctionInput::dummy(), + index: FunctionInput::dummy(), + hash_path: vec![], + output: Witness(0), + }, + BlackBoxFunc::SchnorrVerify => BlackBoxFuncCall::SchnorrVerify { + public_key_x: FunctionInput::dummy(), + public_key_y: FunctionInput::dummy(), + signature: vec![], + message: vec![], + output: Witness(0), + }, + BlackBoxFunc::Pedersen => { + BlackBoxFuncCall::Pedersen { inputs: vec![], outputs: vec![] } + } + BlackBoxFunc::HashToField128Security => { + BlackBoxFuncCall::HashToField128Security { inputs: vec![], output: Witness(0) } + } + BlackBoxFunc::EcdsaSecp256k1 => BlackBoxFuncCall::EcdsaSecp256k1 { + public_key_x: vec![], + public_key_y: vec![], + signature: vec![], + hashed_message: vec![], + output: Witness(0), + }, + BlackBoxFunc::FixedBaseScalarMul => BlackBoxFuncCall::FixedBaseScalarMul { + input: FunctionInput::dummy(), + outputs: vec![], + }, + BlackBoxFunc::Keccak256 => { + BlackBoxFuncCall::Keccak256 { inputs: vec![], outputs: vec![] } + } + } + } + pub fn get_black_box_func(&self) -> BlackBoxFunc { match self { BlackBoxFuncCall::AES { .. } => BlackBoxFunc::AES, @@ -253,7 +313,7 @@ impl BlackBoxFuncCall { match name { BlackBoxFunc::AES => Ok(BlackBoxFuncCall::AES { inputs, outputs }), BlackBoxFunc::AND => { - if inputs.len() < 2 || outputs.is_empty() { + if inputs.len() != 2 || outputs.len() != 1 { Err(std::io::ErrorKind::InvalidData.into()) } else { let lhs = inputs[0]; @@ -263,7 +323,7 @@ impl BlackBoxFuncCall { } } BlackBoxFunc::XOR => { - if inputs.len() < 2 || outputs.is_empty() { + if inputs.len() != 2 || outputs.len() != 1 { Err(std::io::ErrorKind::InvalidData.into()) } else { let lhs = inputs[0]; @@ -273,7 +333,7 @@ impl BlackBoxFuncCall { } } BlackBoxFunc::RANGE => { - if inputs.is_empty() { + if inputs.len() != 1 { Err(std::io::ErrorKind::InvalidData.into()) } else { Ok(BlackBoxFuncCall::RANGE { input: inputs[0] }) @@ -282,7 +342,7 @@ impl BlackBoxFuncCall { BlackBoxFunc::SHA256 => Ok(BlackBoxFuncCall::SHA256 { inputs, outputs }), BlackBoxFunc::Blake2s => Ok(BlackBoxFuncCall::Blake2s { inputs, outputs }), BlackBoxFunc::ComputeMerkleRoot => { - if inputs.len() < 2 || outputs.is_empty() { + if inputs.len() < 2 || outputs.len() != 1 { Err(std::io::ErrorKind::InvalidData.into()) } else { Ok(BlackBoxFuncCall::ComputeMerkleRoot { @@ -294,7 +354,7 @@ impl BlackBoxFuncCall { } } BlackBoxFunc::SchnorrVerify => { - if inputs.len() < 66 || outputs.is_empty() { + if inputs.len() < 66 || outputs.len() != 1 { Err(std::io::ErrorKind::InvalidData.into()) } else { Ok(BlackBoxFuncCall::SchnorrVerify { @@ -308,14 +368,14 @@ impl BlackBoxFuncCall { } BlackBoxFunc::Pedersen => Ok(BlackBoxFuncCall::Pedersen { inputs, outputs }), BlackBoxFunc::HashToField128Security => { - if outputs.is_empty() { + if outputs.len() != 1 { Err(std::io::ErrorKind::InvalidData.into()) } else { Ok(BlackBoxFuncCall::HashToField128Security { inputs, output: outputs[0] }) } } BlackBoxFunc::EcdsaSecp256k1 => { - if inputs.len() < 128 || outputs.is_empty() { + if inputs.len() < 128 || outputs.len() != 1 { Err(std::io::ErrorKind::InvalidData.into()) } else { Ok(BlackBoxFuncCall::EcdsaSecp256k1 { @@ -328,7 +388,7 @@ impl BlackBoxFuncCall { } } BlackBoxFunc::FixedBaseScalarMul => { - if inputs.is_empty() { + if inputs.len() != 1 { Err(std::io::ErrorKind::InvalidData.into()) } else { Ok(BlackBoxFuncCall::FixedBaseScalarMul { input: inputs[0], outputs }) diff --git a/acvm/src/lib.rs b/acvm/src/lib.rs index 053b28afe..24a00dcc9 100644 --- a/acvm/src/lib.rs +++ b/acvm/src/lib.rs @@ -262,21 +262,6 @@ pub fn default_is_opcode_supported( } } -pub fn default_is_bb_supported(language: Language) -> impl Fn(BlackBoxFunc) -> bool { - fn r1cs_is_supported(_: BlackBoxFunc) -> bool { - false - } - - fn plonk_is_supported(opcode: BlackBoxFunc) -> bool { - !matches!(opcode, BlackBoxFunc::AES) - } - - match language { - Language::R1CS => r1cs_is_supported, - Language::PLONKCSat { .. } => plonk_is_supported, - } -} - #[cfg(test)] mod test { use std::collections::BTreeMap;