Skip to content
This repository has been archived by the owner on Apr 9, 2024. It is now read-only.

feat(acvm)!: update black box solver interfaces to match pwg:black_box::solve #268

Merged
merged 4 commits into from
May 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions acvm/src/compiler/transformers/fallback.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,8 @@ impl FallbackTransformer {
) -> Result<(u32, Vec<Opcode>), 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 (lhs, rhs, result, num_bits) =
crate::pwg::logic::extract_input_output(&gc.inputs, &gc.outputs);
stdlib::fallback::and(
Expression::from(lhs),
Expression::from(rhs),
Expand All @@ -86,7 +87,8 @@ impl FallbackTransformer {
)
}
BlackBoxFunc::XOR => {
let (lhs, rhs, result, num_bits) = crate::pwg::logic::extract_input_output(gc);
let (lhs, rhs, result, num_bits) =
crate::pwg::logic::extract_input_output(&gc.inputs, &gc.outputs);
stdlib::fallback::xor(
Expression::from(lhs),
Expression::from(rhs),
Expand Down
54 changes: 13 additions & 41 deletions acvm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,6 @@ pub trait PartialWitnessGenerator {
&self,
initial_witness: &mut BTreeMap<Witness, FieldElement>,
inputs: &[FunctionInput],
outputs: &[Witness],
) -> Result<pwg::OpcodeResolution, OpcodeResolutionError>;
fn sha256(
&self,
Expand Down Expand Up @@ -283,129 +282,102 @@ mod test {
_inputs: &[FunctionInput],
_outputs: &[Witness],
) -> Result<OpcodeResolution, OpcodeResolutionError> {
{
panic!("Path not trodden by this test")
}
panic!("Path not trodden by this test")
}
fn and(
&self,
_initial_witness: &mut BTreeMap<Witness, FieldElement>,
_inputs: &[FunctionInput],
_outputs: &[Witness],
) -> Result<OpcodeResolution, OpcodeResolutionError> {
{
panic!("Path not trodden by this test")
}
panic!("Path not trodden by this test")
}
fn xor(
&self,
_initial_witness: &mut BTreeMap<Witness, FieldElement>,
_inputs: &[FunctionInput],
_outputs: &[Witness],
) -> Result<OpcodeResolution, OpcodeResolutionError> {
{
panic!("Path not trodden by this test")
}
panic!("Path not trodden by this test")
}
fn range(
&self,
_initial_witness: &mut BTreeMap<Witness, FieldElement>,
_inputs: &[FunctionInput],
_outputs: &[Witness],
) -> Result<OpcodeResolution, OpcodeResolutionError> {
{
panic!("Path not trodden by this test")
}
panic!("Path not trodden by this test")
}
fn sha256(
&self,
_initial_witness: &mut BTreeMap<Witness, FieldElement>,
_inputs: &[FunctionInput],
_outputs: &[Witness],
) -> Result<OpcodeResolution, OpcodeResolutionError> {
{
panic!("Path not trodden by this test")
}
panic!("Path not trodden by this test")
}
fn blake2s(
&self,
_initial_witness: &mut BTreeMap<Witness, FieldElement>,
_inputs: &[FunctionInput],
_outputs: &[Witness],
) -> Result<OpcodeResolution, OpcodeResolutionError> {
{
panic!("Path not trodden by this test")
}
panic!("Path not trodden by this test")
}
fn compute_merkle_root(
&self,
_initial_witness: &mut BTreeMap<Witness, FieldElement>,
_inputs: &[FunctionInput],
_outputs: &[Witness],
) -> Result<OpcodeResolution, OpcodeResolutionError> {
{
panic!("Path not trodden by this test")
}
panic!("Path not trodden by this test")
}
fn schnorr_verify(
&self,
_initial_witness: &mut BTreeMap<Witness, FieldElement>,
_inputs: &[FunctionInput],
_outputs: &[Witness],
) -> Result<OpcodeResolution, OpcodeResolutionError> {
{
panic!("Path not trodden by this test")
}
panic!("Path not trodden by this test")
}
fn pedersen(
&self,
_initial_witness: &mut BTreeMap<Witness, FieldElement>,
_inputs: &[FunctionInput],
_outputs: &[Witness],
) -> Result<OpcodeResolution, OpcodeResolutionError> {
{
panic!("Path not trodden by this test")
}
panic!("Path not trodden by this test")
}
fn hash_to_field128_security(
&self,
_initial_witness: &mut BTreeMap<Witness, FieldElement>,
_inputs: &[FunctionInput],
_outputs: &[Witness],
) -> Result<OpcodeResolution, OpcodeResolutionError> {
{
panic!("Path not trodden by this test")
}
panic!("Path not trodden by this test")
}
fn ecdsa_secp256k1(
&self,
_initial_witness: &mut BTreeMap<Witness, FieldElement>,
_inputs: &[FunctionInput],
_outputs: &[Witness],
) -> Result<OpcodeResolution, OpcodeResolutionError> {
{
panic!("Path not trodden by this test")
}
panic!("Path not trodden by this test")
}
fn fixed_base_scalar_mul(
&self,
_initial_witness: &mut BTreeMap<Witness, FieldElement>,
_inputs: &[FunctionInput],
_outputs: &[Witness],
) -> Result<OpcodeResolution, OpcodeResolutionError> {
{
panic!("Path not trodden by this test")
}
panic!("Path not trodden by this test")
}
fn keccak256(
&self,
_initial_witness: &mut BTreeMap<Witness, FieldElement>,
_inputs: &[FunctionInput],
_outputs: &[Witness],
) -> Result<OpcodeResolution, OpcodeResolutionError> {
{
panic!("Path not trodden by this test")
}
panic!("Path not trodden by this test")
}
}

Expand Down
3 changes: 2 additions & 1 deletion acvm/src/pwg/blackbox.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ pub(crate) fn solve(
backend.xor(initial_witness, inputs, outputs)
}
BlackBoxFuncCall { name: BlackBoxFunc::RANGE, inputs, outputs } => {
backend.range(initial_witness, inputs, outputs)
assert!(outputs.is_empty());
backend.range(initial_witness, inputs)
}
BlackBoxFuncCall { name: BlackBoxFunc::SHA256, inputs, outputs } => {
backend.sha256(initial_witness, inputs, outputs)
Expand Down
34 changes: 19 additions & 15 deletions acvm/src/pwg/hash.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -10,11 +10,12 @@ use super::{insert_value, witness_to_value};

pub fn blake2s256(
initial_witness: &mut BTreeMap<Witness, FieldElement>,
func_call: &BlackBoxFuncCall,
inputs: &[FunctionInput],
outputs: &[Witness],
) -> Result<OpcodeResolution, OpcodeResolutionError> {
let hash = generic_hash_256::<Blake2s256>(initial_witness, func_call)?;
let hash = generic_hash_256::<Blake2s256>(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]),
Expand All @@ -27,11 +28,12 @@ pub fn blake2s256(

pub fn sha256(
initial_witness: &mut BTreeMap<Witness, FieldElement>,
func_call: &BlackBoxFuncCall,
inputs: &[FunctionInput],
outputs: &[Witness],
) -> Result<OpcodeResolution, OpcodeResolutionError> {
let hash = generic_hash_256::<Sha256>(initial_witness, func_call)?;
let hash = generic_hash_256::<Sha256>(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]),
Expand All @@ -44,11 +46,12 @@ pub fn sha256(

pub fn keccak256(
initial_witness: &mut BTreeMap<Witness, FieldElement>,
func_call: &BlackBoxFuncCall,
inputs: &[FunctionInput],
outputs: &[Witness],
) -> Result<OpcodeResolution, OpcodeResolutionError> {
let hash = generic_hash_256::<Keccak256>(initial_witness, func_call)?;
let hash = generic_hash_256::<Keccak256>(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]),
Expand All @@ -61,24 +64,25 @@ pub fn keccak256(

pub fn hash_to_field_128_security(
initial_witness: &mut BTreeMap<Witness, FieldElement>,
func_call: &BlackBoxFuncCall,
inputs: &[FunctionInput],
outputs: &[Witness],
) -> Result<OpcodeResolution, OpcodeResolutionError> {
let hash = generic_hash_256::<Blake2s256>(initial_witness, func_call)?;
let hash = generic_hash_256::<Blake2s256>(initial_witness, inputs)?;

let reduced_res = FieldElement::from_be_bytes_reduce(&hash);
insert_value(&func_call.outputs[0], reduced_res, initial_witness)?;
insert_value(&outputs[0], reduced_res, initial_witness)?;

Ok(OpcodeResolution::Solved)
}

fn generic_hash_256<D: Digest>(
initial_witness: &mut BTreeMap<Witness, FieldElement>,
func_call: &BlackBoxFuncCall,
inputs: &[FunctionInput],
) -> 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;

Expand Down
23 changes: 13 additions & 10 deletions acvm/src/pwg/logic.rs
Original file line number Diff line number Diff line change
@@ -1,43 +1,46 @@
use super::{insert_value, witness_to_value};
use crate::{pwg::OpcodeResolution, OpcodeResolutionError};
use acir::{circuit::opcodes::BlackBoxFuncCall, native_types::Witness, FieldElement};
use acir::{circuit::opcodes::FunctionInput, native_types::Witness, FieldElement};
use std::collections::BTreeMap;

/// Solves a [`BlackBoxFunc::And`][acir::circuit::black_box_functions::BlackBoxFunc::AND] opcode and inserts
/// the result into the supplied witness map
pub fn and(
initial_witness: &mut BTreeMap<Witness, FieldElement>,
gate: &BlackBoxFuncCall,
inputs: &[FunctionInput],
outputs: &[Witness],
) -> Result<OpcodeResolution, OpcodeResolutionError> {
let (a, b, result, num_bits) = extract_input_output(gate);
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))
}

/// 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<Witness, FieldElement>,
gate: &BlackBoxFuncCall,
inputs: &[FunctionInput],
outputs: &[Witness],
) -> Result<OpcodeResolution, OpcodeResolutionError> {
let (a, b, result, num_bits) = extract_input_output(gate);
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(
bb_func_call: &BlackBoxFuncCall,
inputs: &[FunctionInput],
outputs: &[Witness],
) -> (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];
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)
(a.witness, b.witness, result, num_bits)
}

/// Derives the rest of the witness based on the initial low level variables
Expand Down
8 changes: 4 additions & 4 deletions acvm/src/pwg/range.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
use crate::{pwg::witness_to_value, pwg::OpcodeResolution, OpcodeResolutionError};
use acir::{circuit::opcodes::BlackBoxFuncCall, native_types::Witness, BlackBoxFunc, FieldElement};
use acir::{circuit::opcodes::FunctionInput, native_types::Witness, BlackBoxFunc, FieldElement};
use std::collections::BTreeMap;

pub fn solve_range_opcode(
initial_witness: &mut BTreeMap<Witness, FieldElement>,
func_call: &BlackBoxFuncCall,
inputs: &[FunctionInput],
) -> Result<OpcodeResolution, OpcodeResolutionError> {
// TODO: this consistency check can be moved to a general function
let defined_input_size = BlackBoxFunc::RANGE
Expand All @@ -13,7 +13,7 @@ pub fn solve_range_opcode(
.fixed_size()
.expect("infallible: input for range gate is fixed");

let num_arguments = func_call.inputs.len();
let num_arguments = inputs.len();
if num_arguments != defined_input_size as usize {
return Err(OpcodeResolutionError::IncorrectNumFunctionArguments(
defined_input_size as usize,
Expand All @@ -25,7 +25,7 @@ pub fn solve_range_opcode(
// 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 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 {
Expand Down
9 changes: 5 additions & 4 deletions acvm/src/pwg/signature/ecdsa.rs
Original file line number Diff line number Diff line change
@@ -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<Witness, FieldElement>,
gadget_call: &BlackBoxFuncCall,
inputs: &[FunctionInput],
outputs: &[Witness],
) -> Result<OpcodeResolution, OpcodeResolutionError> {
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() {
Expand Down Expand Up @@ -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)
}

Expand Down