Skip to content
This repository was archived by the owner on Apr 9, 2024. It is now read-only.
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
352 changes: 209 additions & 143 deletions acvm/src/lib.rs

Large diffs are not rendered by default.

111 changes: 109 additions & 2 deletions acvm/src/pwg.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,23 @@
// Re-usable methods that backends can use to implement their PWG

use crate::{OpcodeNotSolvable, OpcodeResolutionError};
use crate::{OpcodeNotSolvable, OpcodeResolutionError, PartialWitnessGenerator};
use acir::{
circuit::opcodes::{Opcode, OracleData},
native_types::{Expression, Witness},
FieldElement,
};
use std::collections::BTreeMap;

use self::arithmetic::ArithmeticSolver;
use self::{
arithmetic::ArithmeticSolver, block::Blocks, directives::solve_directives, oracle::OracleSolver,
};

// arithmetic
pub mod arithmetic;
// Directives
pub mod directives;
// black box functions
mod blackbox;
pub mod block;
pub mod hash;
pub mod logic;
Expand All @@ -22,6 +26,109 @@ pub mod range;
pub mod signature;
pub mod sorting;

#[derive(Debug, PartialEq)]
pub enum PartialWitnessGeneratorStatus {
/// All opcodes have been solved.
Solved,

/// The `PartialWitnessGenerator` has encountered a request for [oracle data][Opcode::Oracle].
///
/// The caller must resolve these opcodes externally and insert the results into the intermediate witness.
/// Once this is done, the `PartialWitnessGenerator` can be restarted to solve the remaining opcodes.
RequiresOracleData { required_oracle_data: Vec<OracleData>, unsolved_opcodes: Vec<Opcode> },
}

#[derive(Debug, PartialEq)]
pub enum OpcodeResolution {
/// The opcode is resolved
Solved,
/// The opcode is not solvable
Stalled(OpcodeNotSolvable),
/// The opcode is not solvable but could resolved some witness
InProgress,
}

pub fn solve(
backend: &impl PartialWitnessGenerator,
initial_witness: &mut BTreeMap<Witness, FieldElement>,
blocks: &mut Blocks,
mut opcode_to_solve: Vec<Opcode>,
) -> Result<PartialWitnessGeneratorStatus, OpcodeResolutionError> {
let mut unresolved_opcodes: Vec<Opcode> = Vec::new();
let mut unresolved_oracles: Vec<OracleData> = Vec::new();
while !opcode_to_solve.is_empty() || !unresolved_oracles.is_empty() {
unresolved_opcodes.clear();
let mut stalled = true;
let mut opcode_not_solvable = None;
for opcode in &opcode_to_solve {
let mut solved_oracle_data = None;
let resolution = match opcode {
Opcode::Arithmetic(expr) => ArithmeticSolver::solve(initial_witness, expr),
Opcode::BlackBoxFuncCall(bb_func) => {
blackbox::solve(backend, initial_witness, bb_func)
}
Opcode::Directive(directive) => solve_directives(initial_witness, directive),
Opcode::Block(block) | Opcode::ROM(block) | Opcode::RAM(block) => {
blocks.solve(block.id, &block.trace, initial_witness)
}
Opcode::Oracle(data) => {
let mut data_clone = data.clone();
let result = OracleSolver::solve(initial_witness, &mut data_clone)?;
solved_oracle_data = Some(data_clone);
Ok(result)
}
};
match resolution {
Ok(OpcodeResolution::Solved) => {
stalled = false;
}
Ok(OpcodeResolution::InProgress) => {
stalled = false;
// InProgress Oracles must be externally re-solved
if let Some(oracle) = solved_oracle_data {
unresolved_oracles.push(oracle);
} else {
unresolved_opcodes.push(opcode.clone());
}
}
Ok(OpcodeResolution::Stalled(not_solvable)) => {
if opcode_not_solvable.is_none() {
// we keep track of the first unsolvable opcode
opcode_not_solvable = Some(not_solvable);
}
// We push those opcodes not solvable to the back as
// it could be because the opcodes are out of order, i.e. this assignment
// relies on a later opcodes' results
unresolved_opcodes.push(match solved_oracle_data {
Some(oracle_data) => Opcode::Oracle(oracle_data),
None => opcode.clone(),
});
}
Err(OpcodeResolutionError::OpcodeNotSolvable(_)) => {
unreachable!("ICE - Result should have been converted to GateResolution")
}
Err(err) => return Err(err),
}
}
// We have oracles that must be externally resolved
if !unresolved_oracles.is_empty() {
return Ok(PartialWitnessGeneratorStatus::RequiresOracleData {
required_oracle_data: unresolved_oracles,
unsolved_opcodes: unresolved_opcodes,
});
}
// We are stalled because of an opcode being bad
if stalled && !unresolved_opcodes.is_empty() {
return Err(OpcodeResolutionError::OpcodeNotSolvable(
opcode_not_solvable
.expect("infallible: cannot be stalled and None at the same time"),
));
}
std::mem::swap(&mut opcode_to_solve, &mut unresolved_opcodes);
}
Ok(PartialWitnessGeneratorStatus::Solved)
}

// Returns the concrete value for a particular witness
// If the witness has no assignment, then
// an error is returned
Expand Down
2 changes: 1 addition & 1 deletion acvm/src/pwg/arithmetic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use acir::{
};
use std::collections::BTreeMap;

use crate::{OpcodeNotSolvable, OpcodeResolution, OpcodeResolutionError};
use crate::{pwg::OpcodeResolution, OpcodeNotSolvable, OpcodeResolutionError};

/// An Arithmetic solver will take a Circuit's arithmetic gates with witness assignments
/// and create the other witness variables
Expand Down
94 changes: 94 additions & 0 deletions acvm/src/pwg/blackbox.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
use std::collections::BTreeMap;

use acir::{
circuit::opcodes::{BlackBoxFuncCall, FunctionInput},
native_types::Witness,
BlackBoxFunc, FieldElement,
};

use crate::{OpcodeNotSolvable, OpcodeResolutionError, PartialWitnessGenerator};

use super::OpcodeResolution;

/// Check if all of the inputs to the function have assignments
///
/// Returns the first missing assignment if any are missing
fn first_missing_assignment(
witness_assignments: &BTreeMap<Witness, FieldElement>,
inputs: &[FunctionInput],
) -> Option<Witness> {
inputs.iter().find_map(|input| {
if witness_assignments.contains_key(&input.witness) {
None
} else {
Some(input.witness)
}
})
}

/// Check if all of the inputs to the function have assignments
fn contains_all_inputs(
witness_assignments: &BTreeMap<Witness, FieldElement>,
inputs: &[FunctionInput],
) -> bool {
inputs.iter().all(|input| witness_assignments.contains_key(&input.witness))
}

pub(crate) fn solve(
backend: &impl PartialWitnessGenerator,
initial_witness: &mut BTreeMap<Witness, FieldElement>,
bb_func: &BlackBoxFuncCall,
) -> Result<OpcodeResolution, OpcodeResolutionError> {
match bb_func {
BlackBoxFuncCall { 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,
)))
} else {
// This only exists because Rust won't let us bind in a pattern guard.
// See https://github.com/rust-lang/rust/issues/51114
unreachable!("Only reachable if the blackbox is stalled")
}
}
BlackBoxFuncCall { name: BlackBoxFunc::AES, inputs, outputs } => {
backend.aes(initial_witness, inputs, outputs)
}
BlackBoxFuncCall { name: BlackBoxFunc::AND, inputs, outputs } => {
backend.and(initial_witness, inputs, outputs)
}
BlackBoxFuncCall { name: BlackBoxFunc::XOR, inputs, outputs } => {
backend.xor(initial_witness, inputs, outputs)
}
BlackBoxFuncCall { name: BlackBoxFunc::RANGE, inputs, outputs } => {
backend.range(initial_witness, inputs, outputs)
}
BlackBoxFuncCall { name: BlackBoxFunc::SHA256, inputs, outputs } => {
backend.sha256(initial_witness, inputs, outputs)
}
BlackBoxFuncCall { name: BlackBoxFunc::Blake2s, inputs, outputs } => {
backend.blake2s(initial_witness, inputs, outputs)
}
BlackBoxFuncCall { name: BlackBoxFunc::ComputeMerkleRoot, inputs, outputs } => {
backend.compute_merkle_root(initial_witness, inputs, outputs)
}
BlackBoxFuncCall { name: BlackBoxFunc::SchnorrVerify, inputs, outputs } => {
backend.schnorr_verify(initial_witness, inputs, outputs)
}
BlackBoxFuncCall { name: BlackBoxFunc::Pedersen, inputs, outputs } => {
backend.pedersen(initial_witness, inputs, outputs)
}
BlackBoxFuncCall { name: BlackBoxFunc::HashToField128Security, inputs, outputs } => {
backend.hash_to_field128_security(initial_witness, inputs, outputs)
}
BlackBoxFuncCall { name: BlackBoxFunc::EcdsaSecp256k1, inputs, outputs } => {
backend.ecdsa_secp256k1(initial_witness, inputs, outputs)
}
BlackBoxFuncCall { name: BlackBoxFunc::FixedBaseScalarMul, inputs, outputs } => {
backend.fixed_base_scalar_mul(initial_witness, inputs, outputs)
}
BlackBoxFuncCall { name: BlackBoxFunc::Keccak256, inputs, outputs } => {
backend.keccak256(initial_witness, inputs, outputs)
}
}
}
2 changes: 1 addition & 1 deletion acvm/src/pwg/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use acir::{
FieldElement,
};

use crate::{OpcodeNotSolvable, OpcodeResolution, OpcodeResolutionError};
use crate::{pwg::OpcodeResolution, OpcodeNotSolvable, OpcodeResolutionError};

use super::{
arithmetic::{ArithmeticSolver, GateStatus},
Expand Down
2 changes: 1 addition & 1 deletion acvm/src/pwg/directives.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use acir::{
use num_bigint::BigUint;
use num_traits::Zero;

use crate::{OpcodeResolution, OpcodeResolutionError};
use crate::{pwg::OpcodeResolution, OpcodeResolutionError};

use super::{get_value, insert_value, sorting::route, witness_to_value};

Expand Down
2 changes: 1 addition & 1 deletion acvm/src/pwg/hash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use sha2::Sha256;
use sha3::Keccak256;
use std::collections::BTreeMap;

use crate::{OpcodeResolution, OpcodeResolutionError};
use crate::{pwg::OpcodeResolution, OpcodeResolutionError};

use super::{insert_value, witness_to_value};

Expand Down
2 changes: 1 addition & 1 deletion acvm/src/pwg/logic.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use super::{insert_value, witness_to_value};
use crate::{OpcodeResolution, OpcodeResolutionError};
use crate::{pwg::OpcodeResolution, OpcodeResolutionError};
use acir::{circuit::opcodes::BlackBoxFuncCall, native_types::Witness, BlackBoxFunc, FieldElement};
use std::collections::BTreeMap;

Expand Down
2 changes: 1 addition & 1 deletion acvm/src/pwg/oracle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::collections::BTreeMap;

use acir::{circuit::opcodes::OracleData, native_types::Witness, FieldElement};

use crate::{OpcodeNotSolvable, OpcodeResolution, OpcodeResolutionError};
use crate::{pwg::OpcodeResolution, OpcodeNotSolvable, OpcodeResolutionError};

use super::{arithmetic::ArithmeticSolver, insert_value};

Expand Down
2 changes: 1 addition & 1 deletion acvm/src/pwg/range.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{pwg::witness_to_value, OpcodeResolution, OpcodeResolutionError};
use crate::{pwg::witness_to_value, pwg::OpcodeResolution, OpcodeResolutionError};
use acir::{circuit::opcodes::BlackBoxFuncCall, native_types::Witness, BlackBoxFunc, FieldElement};
use std::collections::BTreeMap;

Expand Down
2 changes: 1 addition & 1 deletion acvm/src/pwg/signature/ecdsa.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use acir::{circuit::opcodes::BlackBoxFuncCall, native_types::Witness, FieldElement};
use std::collections::BTreeMap;

use crate::{pwg::witness_to_value, OpcodeResolution, OpcodeResolutionError};
use crate::{pwg::witness_to_value, pwg::OpcodeResolution, OpcodeResolutionError};

pub fn secp256k1_prehashed(
initial_witness: &mut BTreeMap<Witness, FieldElement>,
Expand Down