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

feat(acvm)!: Support stepwise execution of ACIR #399

Merged
merged 16 commits into from
Jul 26, 2023
Merged
Show file tree
Hide file tree
Changes from 12 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
1 change: 0 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ brillig = { version = "0.20.0", path = "brillig", default-features = false }
blackbox_solver = { package = "acvm_blackbox_solver", version = "0.20.0", path = "blackbox_solver", default-features = false }

bincode = "1.3.3"
rmp-serde = "1.1.0"

num-bigint = "0.4"
num-traits = "0.2"
Expand Down
2 changes: 1 addition & 1 deletion acir/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ acir_field.workspace = true
brillig.workspace = true
serde.workspace = true
thiserror.workspace = true
rmp-serde = { workspace = true, optional = true }
rmp-serde = { version = "1.1.0", optional = true }
flate2 = "1.0.24"
bincode.workspace = true

Expand Down
4 changes: 2 additions & 2 deletions acir/src/circuit/brillig.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@ use serde::{Deserialize, Serialize};

/// Inputs for the Brillig VM. These are the initial inputs
/// that the Brillig VM will use to start.
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug, Hash)]
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)]
pub enum BrilligInputs {
Single(Expression),
Array(Vec<Expression>),
}

/// Outputs for the Brillig VM. Once the VM has completed
/// execution, this will be the object that is returned.
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug, Hash)]
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)]
pub enum BrilligOutputs {
Simple(Witness),
Array(Vec<Witness>),
Expand Down
9 changes: 9 additions & 0 deletions acir/src/circuit/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ pub struct Circuit {
pub current_witness_index: u32,
pub opcodes: Vec<Opcode>,

/// The set of private inputs to the circuit.
pub private_parameters: BTreeSet<Witness>,
TomAFrench marked this conversation as resolved.
Show resolved Hide resolved
// ACIR distinguishes between the public inputs which are provided externally or calculated within the circuit and returned.
// The elements of these sets may not be mutually exclusive, i.e. a parameter may be returned from the circuit.
// All public inputs (parameters and return values) must be provided to the verifier at verification time.
Expand All @@ -43,6 +45,11 @@ impl Circuit {
self.current_witness_index + 1
}

/// Returns all witnesses which are required to execute the circuit successfully.
pub fn circuit_arguments(&self) -> BTreeSet<Witness> {
self.private_parameters.union(&self.public_parameters.0).cloned().collect()
}

/// Returns all public inputs. This includes those provided as parameters to the circuit and those
/// computed as return values.
pub fn public_inputs(&self) -> PublicInputs {
Expand Down Expand Up @@ -178,6 +185,7 @@ mod tests {
let circuit = Circuit {
current_witness_index: 5,
opcodes: vec![and_opcode(), range_opcode(), directive_opcode()],
private_parameters: BTreeSet::new(),
public_parameters: PublicInputs(BTreeSet::from_iter(vec![Witness(2), Witness(12)])),
return_values: PublicInputs(BTreeSet::from_iter(vec![Witness(4), Witness(12)])),
};
Expand Down Expand Up @@ -206,6 +214,7 @@ mod tests {
range_opcode(),
and_opcode(),
],
private_parameters: BTreeSet::new(),
public_parameters: PublicInputs(BTreeSet::from_iter(vec![Witness(2)])),
return_values: PublicInputs(BTreeSet::from_iter(vec![Witness(2)])),
};
Expand Down
2 changes: 1 addition & 1 deletion acir/src/circuit/opcodes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ impl std::fmt::Display for Opcode {
let is_read = op.operation.is_zero();
let is_write = op.operation == Expression::one();
if is_read {
write!(f, "(id: {}, read at: {}) ", block_id.0, op.index)
write!(f, "(id: {}, read at: {}, value: {}) ", block_id.0, op.index, op.value)
} else if is_write {
write!(f, "(id: {}, write {} at: {}) ", block_id.0, op.value, op.index)
} else {
Expand Down
1 change: 0 additions & 1 deletion acvm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ repository.workspace = true
num-bigint.workspace = true
num-traits.workspace = true
thiserror.workspace = true
rmp-serde.workspace = true

acir.workspace = true
stdlib.workspace = true
Expand Down
120 changes: 113 additions & 7 deletions acvm/src/compiler/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
use acir::{
circuit::{opcodes::UnsupportedMemoryOpcode, Circuit, Opcode, OpcodeLabel},
circuit::{
brillig::BrilligOutputs, directives::Directive, opcodes::UnsupportedMemoryOpcode, Circuit,
Opcode, OpcodeLabel,
},
native_types::{Expression, Witness},
BlackBoxFunc, FieldElement,
};
Expand Down Expand Up @@ -57,12 +60,18 @@ pub fn compile(
let range_optimizer = RangeOptimizer::new(acir);
let (acir, opcode_label) = range_optimizer.replace_redundant_ranges(opcode_label);

let transformer = match &np_language {
let mut transformer = match &np_language {
crate::Language::R1CS => {
let transformer = R1CSTransformer::new(acir);
return Ok((transformer.transform(), opcode_label));
}
crate::Language::PLONKCSat { width } => CSatTransformer::new(*width),
crate::Language::PLONKCSat { width } => {
let mut csat = CSatTransformer::new(*width);
for value in acir.circuit_arguments() {
csat.mark_solvable(value);
}
csat
}
};

// TODO: the code below is only for CSAT transformer
Expand Down Expand Up @@ -101,26 +110,123 @@ pub fn compile(
new_gates.push(intermediate_gate);
}
new_gates.push(arith_expr);
new_gates.sort();
for gate in new_gates {
new_opcode_labels.push(opcode_label[index]);
transformed_gates.push(Opcode::Arithmetic(gate));
}
}
other_gate => {
Opcode::BlackBoxFuncCall(func) => {
match func {
acir::circuit::opcodes::BlackBoxFuncCall::AND { output, .. }
| acir::circuit::opcodes::BlackBoxFuncCall::XOR { output, .. } => {
transformer.mark_solvable(*output)
}
acir::circuit::opcodes::BlackBoxFuncCall::RANGE { .. } => (),
acir::circuit::opcodes::BlackBoxFuncCall::SHA256 { outputs, .. }
| acir::circuit::opcodes::BlackBoxFuncCall::Keccak256 { outputs, .. }
| acir::circuit::opcodes::BlackBoxFuncCall::Keccak256VariableLength {
outputs,
..
}
| acir::circuit::opcodes::BlackBoxFuncCall::RecursiveAggregation {
output_aggregation_object: outputs,
..
}
| acir::circuit::opcodes::BlackBoxFuncCall::Blake2s { outputs, .. } => {
for witness in outputs {
transformer.mark_solvable(*witness);
}
}
acir::circuit::opcodes::BlackBoxFuncCall::FixedBaseScalarMul {
outputs,
..
}
| acir::circuit::opcodes::BlackBoxFuncCall::Pedersen { outputs, .. } => {
transformer.mark_solvable(outputs.0);
transformer.mark_solvable(outputs.1)
}
acir::circuit::opcodes::BlackBoxFuncCall::HashToField128Security {
output,
..
}
| acir::circuit::opcodes::BlackBoxFuncCall::EcdsaSecp256k1 { output, .. }
| acir::circuit::opcodes::BlackBoxFuncCall::EcdsaSecp256r1 { output, .. }
| acir::circuit::opcodes::BlackBoxFuncCall::SchnorrVerify { output, .. } => {
transformer.mark_solvable(*output)
}
}

new_opcode_labels.push(opcode_label[index]);
transformed_gates.push(opcode.clone());
}
Opcode::Directive(directive) => {
match directive {
Directive::Invert { result, .. } => {
transformer.mark_solvable(*result);
}
Directive::Quotient(quotient_directive) => {
transformer.mark_solvable(quotient_directive.q);
transformer.mark_solvable(quotient_directive.r);
}
Directive::ToLeRadix { b, .. } => {
for w in b {
kevaundray marked this conversation as resolved.
Show resolved Hide resolved
transformer.mark_solvable(*w);
}
}
Directive::PermutationSort { bits, .. } => {
for w in bits {
transformer.mark_solvable(*w);
}
}
Directive::Log(_) => (),
}
new_opcode_labels.push(opcode_label[index]);
transformed_gates.push(opcode.clone());
}
Opcode::MemoryInit { .. } => {
// `MemoryInit` does not write values to the `WitnessMap`
new_opcode_labels.push(opcode_label[index]);
transformed_gates.push(other_gate.clone())
transformed_gates.push(opcode.clone());
}
Opcode::MemoryOp { op, .. } => {
for (_, w1, w2) in &op.value.mul_terms {
transformer.mark_solvable(*w1);
transformer.mark_solvable(*w2);
}
for (_, w) in &op.value.linear_combinations {
transformer.mark_solvable(*w);
}
new_opcode_labels.push(opcode_label[index]);
transformed_gates.push(opcode.clone());
}

Opcode::Block(_) | Opcode::ROM(_) | Opcode::RAM(_) => {
unimplemented!("Stepwise execution is not compatible with {}", opcode.name())
}
Opcode::Brillig(brillig) => {
for output in &brillig.outputs {
match output {
BrilligOutputs::Simple(w) => transformer.mark_solvable(*w),
BrilligOutputs::Array(v) => {
for w in v {
transformer.mark_solvable(*w);
}
}
}
}
new_opcode_labels.push(opcode_label[index]);
transformed_gates.push(opcode.clone());
}
}
}

let current_witness_index = next_witness_index - 1;

Ok((
Circuit {
current_witness_index,
opcodes: transformed_gates,
// The optimizer does not add new public inputs
private_parameters: acir.private_parameters,
public_parameters: acir.public_parameters,
return_values: acir.return_values,
},
Expand Down
4 changes: 4 additions & 0 deletions acvm/src/compiler/optimizers/redundant_range.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ impl RangeOptimizer {
Circuit {
current_witness_index: self.circuit.current_witness_index,
opcodes: optimized_opcodes,
private_parameters: self.circuit.private_parameters,
public_parameters: self.circuit.public_parameters,
return_values: self.circuit.return_values,
},
Expand Down Expand Up @@ -139,6 +140,8 @@ fn extract_range_opcode(opcode: &Opcode) -> Option<(Witness, u32)> {

#[cfg(test)]
mod tests {
use std::collections::BTreeSet;

use crate::compiler::optimizers::redundant_range::{extract_range_opcode, RangeOptimizer};
use acir::{
circuit::{
Expand All @@ -163,6 +166,7 @@ mod tests {
Circuit {
current_witness_index: 1,
opcodes,
private_parameters: BTreeSet::new(),
public_parameters: PublicInputs::default(),
return_values: PublicInputs::default(),
}
Expand Down
Loading