Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: provide a canonical "failing" BlackBoxFunctionSolver #4028

Merged
merged 3 commits into from
Jan 12, 2024
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
56 changes: 9 additions & 47 deletions acvm-repo/acvm/tests/solver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,46 +11,8 @@ use acir::{
FieldElement,
};

use acvm::{
pwg::{ACVMStatus, ErrorLocation, ForeignCallWaitInfo, OpcodeResolutionError, ACVM},
BlackBoxFunctionSolver,
};
use acvm_blackbox_solver::BlackBoxResolutionError;

pub(crate) struct StubbedBackend;

impl BlackBoxFunctionSolver for StubbedBackend {
fn schnorr_verify(
&self,
_public_key_x: &FieldElement,
_public_key_y: &FieldElement,
_signature: &[u8],
_message: &[u8],
) -> Result<bool, BlackBoxResolutionError> {
panic!("Path not trodden by this test")
}
fn pedersen_commitment(
&self,
_inputs: &[FieldElement],
_domain_separator: u32,
) -> Result<(FieldElement, FieldElement), BlackBoxResolutionError> {
panic!("Path not trodden by this test")
}
fn pedersen_hash(
&self,
_inputs: &[FieldElement],
_domain_separator: u32,
) -> Result<FieldElement, BlackBoxResolutionError> {
panic!("Path not trodden by this test")
}
fn fixed_base_scalar_mul(
&self,
_low: &FieldElement,
_high: &FieldElement,
) -> Result<(FieldElement, FieldElement), BlackBoxResolutionError> {
panic!("Path not trodden by this test")
}
}
use acvm::pwg::{ACVMStatus, ErrorLocation, ForeignCallWaitInfo, OpcodeResolutionError, ACVM};
use acvm_blackbox_solver::StubbedBlackBoxSolver;

// Reenable these test cases once we move the brillig implementation of inversion down into the acvm stdlib.

Expand Down Expand Up @@ -135,7 +97,7 @@ fn inversion_brillig_oracle_equivalence() {
])
.into();

let mut acvm = ACVM::new(&StubbedBackend, &opcodes, witness_assignments);
let mut acvm = ACVM::new(&StubbedBlackBoxSolver, &opcodes, witness_assignments);
// use the partial witness generation solver with our acir program
let solver_status = acvm.solve();

Expand Down Expand Up @@ -264,7 +226,7 @@ fn double_inversion_brillig_oracle() {
])
.into();

let mut acvm = ACVM::new(&StubbedBackend, &opcodes, witness_assignments);
let mut acvm = ACVM::new(&StubbedBlackBoxSolver, &opcodes, witness_assignments);

// use the partial witness generation solver with our acir program
let solver_status = acvm.solve();
Expand Down Expand Up @@ -385,7 +347,7 @@ fn oracle_dependent_execution() {
let witness_assignments =
BTreeMap::from([(w_x, FieldElement::from(2u128)), (w_y, FieldElement::from(2u128))]).into();

let mut acvm = ACVM::new(&StubbedBackend, &opcodes, witness_assignments);
let mut acvm = ACVM::new(&StubbedBlackBoxSolver, &opcodes, witness_assignments);

// use the partial witness generation solver with our acir program
let solver_status = acvm.solve();
Expand Down Expand Up @@ -484,7 +446,7 @@ fn brillig_oracle_predicate() {
])
.into();

let mut acvm = ACVM::new(&StubbedBackend, &opcodes, witness_assignments);
let mut acvm = ACVM::new(&StubbedBlackBoxSolver, &opcodes, witness_assignments);
let solver_status = acvm.solve();
assert_eq!(solver_status, ACVMStatus::Solved, "should be fully solved");

Expand Down Expand Up @@ -517,7 +479,7 @@ fn unsatisfied_opcode_resolved() {
values.insert(d, FieldElement::from(2_i128));

let opcodes = vec![Opcode::AssertZero(opcode_a)];
let mut acvm = ACVM::new(&StubbedBackend, &opcodes, values);
let mut acvm = ACVM::new(&StubbedBlackBoxSolver, &opcodes, values);
let solver_status = acvm.solve();
assert_eq!(
solver_status,
Expand Down Expand Up @@ -597,7 +559,7 @@ fn unsatisfied_opcode_resolved_brillig() {

let opcodes = vec![brillig_opcode, Opcode::AssertZero(opcode_a)];

let mut acvm = ACVM::new(&StubbedBackend, &opcodes, values);
let mut acvm = ACVM::new(&StubbedBlackBoxSolver, &opcodes, values);
let solver_status = acvm.solve();
assert_eq!(
solver_status,
Expand Down Expand Up @@ -641,7 +603,7 @@ fn memory_operations() {

let opcodes = vec![init, read_op, expression];

let mut acvm = ACVM::new(&StubbedBackend, &opcodes, initial_witness);
let mut acvm = ACVM::new(&StubbedBlackBoxSolver, &opcodes, initial_witness);
let solver_status = acvm.solve();
assert_eq!(solver_status, ACVMStatus::Solved);
let witness_map = acvm.finalize();
Expand Down
76 changes: 76 additions & 0 deletions acvm-repo/blackbox_solver/src/curve_specific_solver.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
use acir::{BlackBoxFunc, FieldElement};

use crate::BlackBoxResolutionError;

/// This component will generate outputs for Blackbox function calls where the underlying [`acir::BlackBoxFunc`]
/// doesn't have a canonical Rust implementation.
///
/// Returns an [`BlackBoxResolutionError`] if the backend does not support the given [`acir::BlackBoxFunc`].
pub trait BlackBoxFunctionSolver {
fn schnorr_verify(
&self,
public_key_x: &FieldElement,
public_key_y: &FieldElement,
signature: &[u8],
message: &[u8],
) -> Result<bool, BlackBoxResolutionError>;
fn pedersen_commitment(
&self,
inputs: &[FieldElement],
domain_separator: u32,
) -> Result<(FieldElement, FieldElement), BlackBoxResolutionError>;
fn pedersen_hash(
&self,
inputs: &[FieldElement],
domain_separator: u32,
) -> Result<FieldElement, BlackBoxResolutionError>;
fn fixed_base_scalar_mul(
&self,
low: &FieldElement,
high: &FieldElement,
) -> Result<(FieldElement, FieldElement), BlackBoxResolutionError>;
}

pub struct StubbedBlackBoxSolver;

impl StubbedBlackBoxSolver {
fn fail(black_box_function: BlackBoxFunc) -> BlackBoxResolutionError {
BlackBoxResolutionError::Failed(
black_box_function,
format!("{} is not supported", black_box_function.name()),
)
}
}

impl BlackBoxFunctionSolver for StubbedBlackBoxSolver {
fn schnorr_verify(
&self,
_public_key_x: &FieldElement,
_public_key_y: &FieldElement,
_signature: &[u8],
_message: &[u8],
) -> Result<bool, BlackBoxResolutionError> {
Err(Self::fail(BlackBoxFunc::SchnorrVerify))
}
fn pedersen_commitment(
&self,
_inputs: &[FieldElement],
_domain_separator: u32,
) -> Result<(FieldElement, FieldElement), BlackBoxResolutionError> {
Err(Self::fail(BlackBoxFunc::PedersenCommitment))
}
fn pedersen_hash(
&self,
_inputs: &[FieldElement],
_domain_separator: u32,
) -> Result<FieldElement, BlackBoxResolutionError> {
Err(Self::fail(BlackBoxFunc::PedersenHash))
}
fn fixed_base_scalar_mul(
&self,
_low: &FieldElement,
_high: &FieldElement,
) -> Result<(FieldElement, FieldElement), BlackBoxResolutionError> {
Err(Self::fail(BlackBoxFunc::FixedBaseScalarMul))
}
}
35 changes: 5 additions & 30 deletions acvm-repo/blackbox_solver/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,48 +7,23 @@
//! For functions that are backend-dependent, it provides a Trait [BlackBoxFunctionSolver] that must be implemented by the backend.
//! For functions that have a reference implementation, such as [keccak256], this crate exports the reference implementation directly.

use acir::{BlackBoxFunc, FieldElement};
use acir::BlackBoxFunc;
use blake2::digest::generic_array::GenericArray;
use blake2::{Blake2s256, Digest};
use sha2::Sha256;
use sha3::Keccak256;
use thiserror::Error;

mod curve_specific_solver;

pub use curve_specific_solver::{BlackBoxFunctionSolver, StubbedBlackBoxSolver};

#[derive(Clone, PartialEq, Eq, Debug, Error)]
pub enum BlackBoxResolutionError {
#[error("failed to solve blackbox function: {0}, reason: {1}")]
Failed(BlackBoxFunc, String),
}

/// This component will generate outputs for Blackbox function calls where the underlying [`acir::BlackBoxFunc`]
/// doesn't have a canonical Rust implementation.
///
/// Returns an [`BlackBoxResolutionError`] if the backend does not support the given [`acir::BlackBoxFunc`].
pub trait BlackBoxFunctionSolver {
fn schnorr_verify(
&self,
public_key_x: &FieldElement,
public_key_y: &FieldElement,
signature: &[u8],
message: &[u8],
) -> Result<bool, BlackBoxResolutionError>;
fn pedersen_commitment(
&self,
inputs: &[FieldElement],
domain_separator: u32,
) -> Result<(FieldElement, FieldElement), BlackBoxResolutionError>;
fn pedersen_hash(
&self,
inputs: &[FieldElement],
domain_separator: u32,
) -> Result<FieldElement, BlackBoxResolutionError>;
fn fixed_base_scalar_mul(
&self,
low: &FieldElement,
high: &FieldElement,
) -> Result<(FieldElement, FieldElement), BlackBoxResolutionError>;
}

pub fn sha256(inputs: &[u8]) -> Result<[u8; 32], BlackBoxResolutionError> {
generic_hash_256::<Sha256>(inputs)
.map_err(|err| BlackBoxResolutionError::Failed(BlackBoxFunc::SHA256, err))
Expand Down Expand Up @@ -244,7 +219,7 @@

#[test]
fn sanity_check() {
// Test vectors are copied from XKCP (eXtended Keccak Code Package)

Check warning on line 222 in acvm-repo/blackbox_solver/src/lib.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (XKCP)
// https://github.com/XKCP/XKCP/blob/master/tests/TestVectors/KeccakF-1600-IntermediateValues.txt
let zero_state = [0u64; 25];

Expand Down
57 changes: 8 additions & 49 deletions compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use crate::ssa::ir::{instruction::Endian, types::NumericType};
use acvm::acir::circuit::brillig::{BrilligInputs, BrilligOutputs};
use acvm::acir::circuit::opcodes::{BlockId, MemOp};
use acvm::acir::circuit::Opcode;
use acvm::blackbox_solver;
use acvm::brillig_vm::{brillig::Value, Registers, VMStatus, VM};
use acvm::{
acir::{
Expand All @@ -19,7 +20,6 @@ use acvm::{
},
FieldElement,
};
use acvm::{BlackBoxFunctionSolver, BlackBoxResolutionError};
use fxhash::FxHashMap as HashMap;
use iter_extended::{try_vecmap, vecmap};
use num_bigint::BigUint;
Expand Down Expand Up @@ -1711,53 +1711,6 @@ fn execute_brillig(
code: &[BrilligOpcode],
inputs: &[BrilligInputs],
) -> Option<(Registers, Vec<Value>)> {
struct NullBbSolver;

impl BlackBoxFunctionSolver for NullBbSolver {
fn schnorr_verify(
&self,
_public_key_x: &FieldElement,
_public_key_y: &FieldElement,
_signature: &[u8],
_message: &[u8],
) -> Result<bool, BlackBoxResolutionError> {
Err(BlackBoxResolutionError::Failed(
BlackBoxFunc::SchnorrVerify,
"SchnorrVerify is not supported".to_string(),
))
}
fn pedersen_commitment(
&self,
_inputs: &[FieldElement],
_domain_separator: u32,
) -> Result<(FieldElement, FieldElement), BlackBoxResolutionError> {
Err(BlackBoxResolutionError::Failed(
BlackBoxFunc::PedersenCommitment,
"PedersenCommitment is not supported".to_string(),
))
}
fn pedersen_hash(
&self,
_inputs: &[FieldElement],
_domain_separator: u32,
) -> Result<FieldElement, BlackBoxResolutionError> {
Err(BlackBoxResolutionError::Failed(
BlackBoxFunc::PedersenHash,
"PedersenHash is not supported".to_string(),
))
}
fn fixed_base_scalar_mul(
&self,
_low: &FieldElement,
_high: &FieldElement,
) -> Result<(FieldElement, FieldElement), BlackBoxResolutionError> {
Err(BlackBoxResolutionError::Failed(
BlackBoxFunc::FixedBaseScalarMul,
"FixedBaseScalarMul is not supported".to_string(),
))
}
}

// Set input values
let mut input_register_values: Vec<Value> = Vec::with_capacity(inputs.len());
let mut input_memory: Vec<Value> = Vec::new();
Expand All @@ -1783,7 +1736,13 @@ fn execute_brillig(

// Instantiate a Brillig VM given the solved input registers and memory, along with the Brillig bytecode.
let input_registers = Registers::load(input_register_values);
let mut vm = VM::new(input_registers, input_memory, code, Vec::new(), &NullBbSolver);
let mut vm = VM::new(
input_registers,
input_memory,
code,
Vec::new(),
&blackbox_solver::StubbedBlackBoxSolver,
);

// Run the Brillig VM on these inputs, bytecode, etc!
let vm_status = vm.process_opcodes();
Expand Down
Loading
Loading