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)] 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 be1c5a64e..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.name.name(), + Opcode::BlackBoxFuncCall(g) => g.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..d53486b07 100644 --- a/acir/src/circuit/opcodes/black_box_function_call.rs +++ b/acir/src/circuit/opcodes/black_box_function_call.rs @@ -7,138 +7,476 @@ 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, } +impl FunctionInput { + pub fn dummy() -> Self { + Self { witness: Witness(0), num_bits: 0 } + } +} + #[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 { + leaf: FunctionInput, + index: FunctionInput, + hash_path: Vec, + output: Witness, + }, + SchnorrVerify { + public_key_x: FunctionInput, + public_key_y: FunctionInput, + signature: Vec, + message: Vec, + output: Witness, + }, + Pedersen { + inputs: Vec, + outputs: Vec, + }, + // 128 here specifies that this function + // should have 128 bits of security + HashToField128Security { + inputs: Vec, + output: Witness, + }, + EcdsaSecp256k1 { + public_key_x: Vec, + public_key_y: Vec, + signature: Vec, + hashed_message: Vec, + output: Witness, + }, + FixedBaseScalarMul { + input: FunctionInput, + outputs: Vec, + }, + Keccak256 { + inputs: Vec, + outputs: Vec, + }, } -impl BlackBoxFuncCall { - pub fn write(&self, mut writer: W) -> std::io::Result<()> { - write_u16(&mut writer, self.name.to_u16())?; +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: &[FunctionInput], 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: &[Witness], 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::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)?); + } + + Ok(inputs) +} - let num_inputs = self.inputs.len() as u32; - write_u32(&mut writer, num_inputs)?; +fn read_outputs(mut reader: R) -> std::io::Result> { + let num_outputs = read_u32(&mut reader)?; - for input in &self.inputs { - write_u32(&mut writer, input.witness.witness_index())?; - write_u32(&mut writer, input.num_bits)?; + let mut outputs = Vec::new(); + outputs.try_reserve_exact(num_outputs as usize).map_err(|_| std::io::ErrorKind::InvalidData)?; + + for _ in 0..num_outputs { + let witness_index = read_u32(&mut reader)?; + outputs.push(Witness::new(witness_index)); + } + + 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, + 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 name(&self) -> &str { + self.get_black_box_func().name() + } - let num_outputs = self.outputs.len() as u32; - write_u32(&mut writer, num_outputs)?; + pub fn get_inputs_vec(&self) -> Vec { + match self { + BlackBoxFuncCall::AES { inputs, .. } + | BlackBoxFuncCall::SHA256 { inputs, .. } + | BlackBoxFuncCall::Blake2s { inputs, .. } + | BlackBoxFuncCall::Keccak256 { inputs, .. } + | BlackBoxFuncCall::Pedersen { inputs, .. } + | 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::with_capacity(2 + hash_path.len()); + inputs.push(*leaf); + inputs.push(*index); + inputs.extend(hash_path.iter().copied()); + inputs + } + BlackBoxFuncCall::SchnorrVerify { + public_key_x, + public_key_y, + signature, + message, + .. + } => { + 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().copied()); + inputs.extend(message.iter().copied()); + inputs + } + BlackBoxFuncCall::EcdsaSecp256k1 { + public_key_x, + public_key_y, + signature, + hashed_message: message, + .. + } => { + 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 + } + } + } - for output in &self.outputs { - write_u32(&mut writer, output.witness_index())?; + 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())?; + + write_inputs(&self.get_inputs_vec(), &mut writer)?; + write_outputs(&self.get_outputs_vec(), &mut writer)?; 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)?; + + match name { + BlackBoxFunc::AES => Ok(BlackBoxFuncCall::AES { inputs, outputs }), + BlackBoxFunc::AND => { + if inputs.len() != 2 || outputs.len() != 1 { + 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 => { + if inputs.len() != 2 || outputs.len() != 1 { + 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 => { + if inputs.len() != 1 { + 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.len() != 1 { + 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.len() != 1 { + 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::Pedersen => Ok(BlackBoxFuncCall::Pedersen { inputs, outputs }), + BlackBoxFunc::HashToField128Security => { + 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.len() != 1 { + 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::FixedBaseScalarMul => { + if inputs.len() != 1 { + Err(std::io::ErrorKind::InvalidData.into()) + } else { + Ok(BlackBoxFuncCall::FixedBaseScalarMul { input: inputs[0], outputs }) + } + } + BlackBoxFunc::Keccak256 => Ok(BlackBoxFuncCall::Keccak256 { inputs, outputs }), + } + } +} + +const ABBREVIATION_LIMIT: usize = 5; + +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 + 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, + ); + + result + } +} + +fn get_outputs_string(outputs: &[Witness]) -> String { + let should_abbreviate_outputs = outputs.len() <= ABBREVIATION_LIMIT; - 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) + 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 = uppercase_name.to_uppercase(); + let uppercase_name = self.name().to_uppercase(); write!(f, "BLACKBOX::{uppercase_name} ")?; - 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; - // 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(); + write!(f, "[")?; - result += &format!( - "(_{}, num_bits: {})...(_{}, num_bits: {})", - first.witness.witness_index(), - first.num_bits, - last.witness.witness_index(), - last.num_bits, - ); + let inputs_str = get_inputs_string(&self.get_inputs_vec()); - result - }; write!(f, "{inputs_str}")?; write!(f, "] ")?; // OUTPUTS - // TODO: Avoid duplication of INPUTS and OUTPUTS code - - if self.outputs.is_empty() { - return Ok(()); - } - 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 - }; + let outputs_str = get_outputs_string(&self.get_outputs_vec()); + write!(f, "{outputs_str}")?; + write!(f, "]") } } diff --git a/acvm/src/compiler/optimizers/redundant_range.rs b/acvm/src/compiler/optimizers/redundant_range.rs index 0dd77d96f..486868f8a 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::{opcodes::BlackBoxFuncCall, Circuit, Opcode}, 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/optimizers/simplify.rs b/acvm/src/compiler/optimizers/simplify.rs index 5373d128f..ae2b378c3 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 } diff --git a/acvm/src/compiler/transformers/fallback.rs b/acvm/src/compiler/transformers/fallback.rs index b9bc8834e..ea92cbc23 100644 --- a/acvm/src/compiler/transformers/fallback.rs +++ b/acvm/src/compiler/transformers/fallback.rs @@ -4,7 +4,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 @@ -74,33 +73,34 @@ 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.inputs, &gc.outputs); + 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.inputs, &gc.outputs); + 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), @@ -109,7 +109,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 24bd55d91..24a00dcc9 100644 --- a/acvm/src/lib.rs +++ b/acvm/src/lib.rs @@ -71,19 +71,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], + input: &FunctionInput, ) -> Result; fn sha256( &self, @@ -100,14 +102,19 @@ pub trait PartialWitnessGenerator { fn compute_merkle_root( &self, initial_witness: &mut BTreeMap, - inputs: &[FunctionInput], - outputs: &[Witness], + leaf: &FunctionInput, + index: &FunctionInput, + hash_path: &[FunctionInput], + output: &Witness, ) -> Result; fn schnorr_verify( &self, initial_witness: &mut BTreeMap, - inputs: &[FunctionInput], - outputs: &[Witness], + public_key_x: &FunctionInput, + public_key_y: &FunctionInput, + signature: &[FunctionInput], + message: &[FunctionInput], + output: &Witness, ) -> Result; fn pedersen( &self, @@ -119,18 +126,21 @@ 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], + public_key_x: &[FunctionInput], + public_key_y: &[FunctionInput], + signature: &[FunctionInput], + message: &[FunctionInput], + outputs: &Witness, ) -> Result; fn fixed_base_scalar_mul( &self, initial_witness: &mut BTreeMap, - inputs: &[FunctionInput], + input: &FunctionInput, outputs: &[Witness], ) -> Result; fn keccak256( @@ -243,11 +253,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 { name: BlackBoxFunc::AES, .. }) - | Opcode::Block(_) - ) + !matches!(opcode, Opcode::BlackBoxFuncCall(BlackBoxFuncCall::AES { .. }) | Opcode::Block(_)) } match language { @@ -289,23 +295,25 @@ 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") } 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") } fn range( &self, _initial_witness: &mut BTreeMap, - _inputs: &[FunctionInput], + _input: &FunctionInput, ) -> Result { panic!("Path not trodden by this test") } @@ -328,16 +336,21 @@ mod test { fn compute_merkle_root( &self, _initial_witness: &mut BTreeMap, - _inputs: &[FunctionInput], - _outputs: &[Witness], + _leaf: &FunctionInput, + _index: &FunctionInput, + _hash_path: &[FunctionInput], + _output: &Witness, ) -> Result { panic!("Path not trodden by this test") } fn schnorr_verify( &self, _initial_witness: &mut BTreeMap, - _inputs: &[FunctionInput], - _outputs: &[Witness], + _public_key_x: &FunctionInput, + _public_key_y: &FunctionInput, + _signature: &[FunctionInput], + _message: &[FunctionInput], + _output: &Witness, ) -> Result { panic!("Path not trodden by this test") } @@ -353,22 +366,25 @@ mod test { &self, _initial_witness: &mut BTreeMap, _inputs: &[FunctionInput], - _outputs: &[Witness], + _output: &Witness, ) -> Result { panic!("Path not trodden by this test") } fn ecdsa_secp256k1( &self, _initial_witness: &mut BTreeMap, - _inputs: &[FunctionInput], - _outputs: &[Witness], + _public_key_x: &[FunctionInput], + _public_key_y: &[FunctionInput], + _signature: &[FunctionInput], + _message: &[FunctionInput], + _output: &Witness, ) -> Result { panic!("Path not trodden by this test") } fn fixed_base_scalar_mul( &self, _initial_witness: &mut BTreeMap, - _inputs: &[FunctionInput], + _input: &FunctionInput, _outputs: &[Witness], ) -> Result { panic!("Path not trodden by this test") diff --git a/acvm/src/pwg/blackbox.rs b/acvm/src/pwg/blackbox.rs index 7ccfebc61..9b17e5305 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,44 +52,61 @@ pub(crate) fn solve( unreachable!("Only reachable if the blackbox is stalled") } } - BlackBoxFuncCall { name: BlackBoxFunc::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 { name: BlackBoxFunc::AND, inputs, outputs } => { - backend.and(initial_witness, inputs, outputs) + BlackBoxFuncCall::XOR { lhs, rhs, output } => { + backend.xor(initial_witness, lhs, rhs, output) } - BlackBoxFuncCall { name: BlackBoxFunc::XOR, inputs, outputs } => { - backend.xor(initial_witness, inputs, outputs) - } - BlackBoxFuncCall { name: BlackBoxFunc::RANGE, inputs, outputs } => { - assert!(outputs.is_empty()); - backend.range(initial_witness, inputs) - } - BlackBoxFuncCall { name: BlackBoxFunc::SHA256, inputs, outputs } => { + BlackBoxFuncCall::RANGE { input } => backend.range(initial_witness, input), + 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 { leaf, index, hash_path, output } => { + backend.compute_merkle_root(initial_witness, leaf, index, hash_path, output) } - BlackBoxFuncCall { name: BlackBoxFunc::SchnorrVerify, inputs, outputs } => { - backend.schnorr_verify(initial_witness, inputs, outputs) - } - BlackBoxFuncCall { name: BlackBoxFunc::Pedersen, inputs, outputs } => { + 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 { inputs, outputs } => { backend.pedersen(initial_witness, inputs, outputs) } - BlackBoxFuncCall { name: BlackBoxFunc::HashToField128Security, inputs, outputs } => { - backend.hash_to_field_128_security(initial_witness, inputs, outputs) - } - BlackBoxFuncCall { name: BlackBoxFunc::EcdsaSecp256k1, inputs, outputs } => { - backend.ecdsa_secp256k1(initial_witness, inputs, outputs) + BlackBoxFuncCall::HashToField128Security { inputs, output } => { + backend.hash_to_field_128_security(initial_witness, inputs, output) } - BlackBoxFuncCall { name: BlackBoxFunc::FixedBaseScalarMul, inputs, outputs } => { - backend.fixed_base_scalar_mul(initial_witness, inputs, outputs) + BlackBoxFuncCall::EcdsaSecp256k1 { + public_key_x, + public_key_y, + signature, + hashed_message: 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) } - 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 127b8a825..4d630935e 100644 --- a/acvm/src/pwg/hash.rs +++ b/acvm/src/pwg/hash.rs @@ -65,12 +65,12 @@ pub fn keccak256( pub fn hash_to_field_128_security( initial_witness: &mut BTreeMap, inputs: &[FunctionInput], - outputs: &[Witness], + output: &Witness, ) -> Result { let hash = generic_hash_256::(initial_witness, inputs)?; let reduced_res = FieldElement::from_be_bytes_reduce(&hash); - insert_value(&outputs[0], reduced_res, initial_witness)?; + insert_value(output, reduced_res, initial_witness)?; Ok(OpcodeResolution::Solved) } diff --git a/acvm/src/pwg/logic.rs b/acvm/src/pwg/logic.rs index 2ed2cd4b0..acab1b17b 100644 --- a/acvm/src/pwg/logic.rs +++ b/acvm/src/pwg/logic.rs @@ -7,40 +7,34 @@ use std::collections::BTreeMap; /// the result into the supplied witness map pub fn and( initial_witness: &mut BTreeMap, - inputs: &[FunctionInput], - outputs: &[Witness], + lhs: &FunctionInput, + rhs: &FunctionInput, + output: &Witness, ) -> Result { - let (a, b, result, num_bits) = extract_input_output(inputs, outputs); - solve_logic_gate(initial_witness, &a, &b, result, |left, right| left.and(right, num_bits)) + assert_eq!( + lhs.num_bits, rhs.num_bits, + "number of bits specified for each input must be the same" + ); + solve_logic_gate(initial_witness, &lhs.witness, &rhs.witness, *output, |left, right| { + left.and(right, lhs.num_bits) + }) } /// Solves a [`BlackBoxFunc::XOR`][acir::circuit::black_box_functions::BlackBoxFunc::XOR] opcode and inserts /// the result into the supplied witness map pub fn xor( initial_witness: &mut BTreeMap, - inputs: &[FunctionInput], - outputs: &[Witness], + lhs: &FunctionInput, + rhs: &FunctionInput, + output: &Witness, ) -> Result { - let (a, b, result, num_bits) = extract_input_output(inputs, outputs); - solve_logic_gate(initial_witness, &a, &b, result, |left, right| left.xor(right, num_bits)) -} - -// 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( - inputs: &[FunctionInput], - outputs: &[Witness], -) -> (Witness, Witness, Witness, u32) { - let a = &inputs[0]; - let b = &inputs[1]; - let result = 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"); - - let num_bits = a.num_bits; - - (a.witness, b.witness, result, num_bits) + assert_eq!( + lhs.num_bits, rhs.num_bits, + "number of bits specified for each input must be the same" + ); + solve_logic_gate(initial_witness, &lhs.witness, &rhs.witness, *output, |left, right| { + left.xor(right, lhs.num_bits) + }) } /// Derives the rest of the witness based on the initial low level variables diff --git a/acvm/src/pwg/range.rs b/acvm/src/pwg/range.rs index 8754dce06..8770993d0 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::FunctionInput, native_types::Witness, BlackBoxFunc, FieldElement}; +use acir::{circuit::opcodes::FunctionInput, native_types::Witness, FieldElement}; use std::collections::BTreeMap; pub fn solve_range_opcode( initial_witness: &mut BTreeMap, - inputs: &[FunctionInput], + 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 = 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 = 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 b22a81cf0..2106fae2e 100644 --- a/acvm/src/pwg/signature/ecdsa.rs +++ b/acvm/src/pwg/signature/ecdsa.rs @@ -3,55 +3,57 @@ use std::collections::BTreeMap; use crate::{pwg::witness_to_value, pwg::OpcodeResolution, OpcodeResolutionError}; -pub fn secp256k1_prehashed( - initial_witness: &mut BTreeMap, +fn to_u8_vec( + initial_witness: &BTreeMap, inputs: &[FunctionInput], - outputs: &[Witness], -) -> Result { - let mut inputs_iter = inputs.iter(); - - let mut pub_key_x = [0u8; 32]; - for (i, pkx) in pub_key_x.iter_mut().enumerate() { - let _x_i = inputs_iter - .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]; - for (i, pky) in pub_key_y.iter_mut().enumerate() { - let _y_i = inputs_iter - .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]; - for (i, sig) in signature.iter_mut().enumerate() { - let _sig_i = inputs_iter - .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 mut hashed_message = Vec::new(); - for msg in 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); +) -> Result, OpcodeResolutionError> { + 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) +} +pub fn secp256k1_prehashed( + initial_witness: &mut BTreeMap, + public_key_x_inputs: &[FunctionInput], + public_key_y_inputs: &[FunctionInput], + signature_inputs: &[FunctionInput], + message_inputs: &[FunctionInput], + output: Witness, +) -> Result { + let pub_key_x: [u8; 32] = + 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().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 = 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) }