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

Commit

Permalink
feat(acvm)!: make Backend trait object safe (#180)
Browse files Browse the repository at this point in the history
* chore: add test to check that `Backend` is object safe

* chore: make `Backend` trait object safe
  • Loading branch information
TomAFrench authored Apr 5, 2023
1 parent 4360d6c commit fd28657
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 37 deletions.
69 changes: 33 additions & 36 deletions acvm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,13 @@ pub mod pwg;
use crate::pwg::{arithmetic::ArithmeticSolver, oracle::OracleSolver};
use acir::{
circuit::{
directives::Directive,
opcodes::{BlackBoxFuncCall, OracleData},
Circuit, Opcode,
},
native_types::{Expression, Witness},
BlackBoxFunc,
};
use pwg::block::Blocks;
use pwg::{block::Blocks, directives::solve_directives};
use std::collections::BTreeMap;
use thiserror::Error;

Expand Down Expand Up @@ -65,6 +64,22 @@ pub enum OpcodeResolution {
InProgress,
}

/// 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>,
func_call: &BlackBoxFuncCall,
) -> Option<Witness> {
func_call.inputs.iter().find_map(|input| {
if witness_assignments.contains_key(&input.witness) {
None
} else {
Some(input.witness)
}
})
}

pub trait Backend: SmartContract + ProofSystemCompiler + PartialWitnessGenerator {}

/// This component will generate the backend specific output for
Expand All @@ -89,18 +104,16 @@ pub trait PartialWitnessGenerator {
Opcode::Arithmetic(expr) => ArithmeticSolver::solve(initial_witness, expr),
Opcode::BlackBoxFuncCall(bb_func) => {
if let Some(unassigned_witness) =
Self::any_missing_assignment(initial_witness, bb_func)
first_missing_assignment(initial_witness, bb_func)
{
Ok(OpcodeResolution::Stalled(OpcodeNotSolvable::MissingAssignment(
unassigned_witness.0,
)))
} else {
Self::solve_black_box_function_call(initial_witness, bb_func)
self.solve_black_box_function_call(initial_witness, bb_func)
}
}
Opcode::Directive(directive) => {
Self::solve_directives(initial_witness, directive)
}
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)
}
Expand Down Expand Up @@ -160,37 +173,10 @@ pub trait PartialWitnessGenerator {
}

fn solve_black_box_function_call(
&self,
initial_witness: &mut BTreeMap<Witness, FieldElement>,
func_call: &BlackBoxFuncCall,
) -> Result<OpcodeResolution, OpcodeResolutionError>;

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

fn solve_directives(
initial_witness: &mut BTreeMap<Witness, FieldElement>,
directive: &Directive,
) -> Result<OpcodeResolution, OpcodeResolutionError> {
match pwg::directives::solve_directives(initial_witness, directive) {
Ok(_) => Ok(OpcodeResolution::Solved),
Err(OpcodeResolutionError::OpcodeNotSolvable(unsolved)) => {
Ok(OpcodeResolution::Stalled(unsolved))
}
Err(err) => Err(err),
}
}
}

pub trait SmartContract {
Expand Down Expand Up @@ -325,13 +311,15 @@ mod test {
};

use crate::{
pwg::block::Blocks, OpcodeResolution, OpcodeResolutionError, PartialWitnessGenerator,
pwg::block::Blocks, Backend, OpcodeResolution, OpcodeResolutionError,
PartialWitnessGenerator,
};

struct StubbedPwg;

impl PartialWitnessGenerator for StubbedPwg {
fn solve_black_box_function_call(
&self,
_initial_witness: &mut BTreeMap<Witness, FieldElement>,
_func_call: &BlackBoxFuncCall,
) -> Result<OpcodeResolution, OpcodeResolutionError> {
Expand Down Expand Up @@ -408,4 +396,13 @@ mod test {
assert!(unsolved_opcodes.is_empty(), "should be fully solved");
assert!(unresolved_oracles.is_empty(), "should have no unresolved oracles");
}

#[test]
fn test_backend_object_safety() {
// This test doesn't do anything at runtime.
// We just want to ensure that the `Backend` trait is object safe and this test will refuse to compile
// if this property is broken.
#[allow(dead_code)]
fn check_object_safety(_backend: Box<dyn Backend>) {}
}
}
21 changes: 20 additions & 1 deletion acvm/src/pwg/directives.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,32 @@ use acir::{
use num_bigint::BigUint;
use num_traits::Zero;

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

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

/// Attempts to solve the [`Directive`] opcode `directive`.
/// If successful, `initial_witness` will be mutated to contain the new witness assignment.
///
/// Returns `Ok(OpcodeResolution)` to signal whether the directive was successful solved.
///
/// Returns `Err(OpcodeResolutionError)` if a circuit constraint is unsatisfied.
pub fn solve_directives(
initial_witness: &mut BTreeMap<Witness, FieldElement>,
directive: &Directive,
) -> Result<OpcodeResolution, OpcodeResolutionError> {
match solve_directives_internal(initial_witness, directive) {
Ok(_) => Ok(OpcodeResolution::Solved),
Err(OpcodeResolutionError::OpcodeNotSolvable(unsolved)) => {
Ok(OpcodeResolution::Stalled(unsolved))
}
Err(err) => Err(err),
}
}

fn solve_directives_internal(
initial_witness: &mut BTreeMap<Witness, FieldElement>,
directive: &Directive,
) -> Result<(), OpcodeResolutionError> {
match directive {
Directive::Invert { x, result } => {
Expand Down

0 comments on commit fd28657

Please sign in to comment.