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

feat(std_lib): Support extracting intermediate witnesses from a circuit using traces #872

Closed
wants to merge 2 commits into from
Closed
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
295 changes: 228 additions & 67 deletions Cargo.lock

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ edition = "2021"
rust-version = "1.66"

[workspace.dependencies]
acvm = "0.4.1"
# acvm = "0.4.1"
acvm = { features = ["bn254"], git = "https://github.com/noir-lang/acvm", branch = "mv/trace" }
arena = { path = "crates/arena" }
fm = { path = "crates/fm" }
iter-extended = { path = "crates/iter-extended" }
Expand Down
4 changes: 2 additions & 2 deletions crates/nargo/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ termcolor = "1.1.2"
tempdir = "0.3.7"

# Backends
aztec_backend = { optional = true, package = "barretenberg_static_lib", git = "https://github.com/noir-lang/aztec_backend", rev = "cff757dca7971161e4bd25e7a744d910c37c22be" }
aztec_wasm_backend = { optional = true, package = "barretenberg_wasm", git = "https://github.com/noir-lang/aztec_backend", rev = "cff757dca7971161e4bd25e7a744d910c37c22be" }
aztec_backend = { optional = true, package = "barretenberg_static_lib", git = "https://github.com/noir-lang/aztec_backend", branch = "mv/trace" }
aztec_wasm_backend = { optional = true, package = "barretenberg_wasm", git = "https://github.com/noir-lang/aztec_backend", branch = "mv/trace" }
marlin_arkworks_backend = { optional = true, git = "https://github.com/noir-lang/marlin_arkworks_backend", rev = "144378edad821bfaa52bf2cacca8ecc87514a4fc" }
color-eyre = "0.6.2"

Expand Down
8 changes: 6 additions & 2 deletions crates/nargo/src/cli/execute_cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use noirc_abi::{InputMap, WitnessMap, MAIN_RETURN_NAME};
use noirc_driver::CompiledProgram;

use super::NargoConfig;
use super::{create_named_dir, read_inputs_from_file, write_to_file};
use super::{create_named_dir, handle_logs, read_inputs_from_file, write_to_file};
use crate::{
cli::compile_cmd::compile_circuit,
constants::{PROVER_INPUT_FILE, TARGET_DIR, WITNESS_EXT},
Expand Down Expand Up @@ -79,8 +79,12 @@ pub(crate) fn execute_program(
) -> Result<WitnessMap, CliError> {
let mut solved_witness = compiled_program.abi.encode(inputs_map, true)?;

let mut logs = Vec::new();

let backend = crate::backends::ConcreteBackend;
backend.solve(&mut solved_witness, compiled_program.circuit.opcodes.clone())?;
backend.solve(&mut solved_witness, compiled_program.circuit.opcodes.clone(), &mut logs)?;

handle_logs(logs)?;

Ok(solved_witness)
}
Expand Down
31 changes: 30 additions & 1 deletion crates/nargo/src/cli/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
use acvm::{acir::circuit::Circuit, hash_constraint_system, ProofSystemCompiler};
use acvm::{
acir::circuit::{
directives::{Directive, LogOutputInfo},
Circuit,
},
hash_constraint_system, ProofSystemCompiler,
};
pub use check_cmd::check_from_path;
use clap::{Args, Parser, Subcommand};
use const_format::formatcp;
Expand Down Expand Up @@ -238,6 +244,29 @@ fn fetch_pk_and_vk<P: AsRef<Path>>(
}
}

pub fn handle_logs(logs: Vec<Directive>) -> Result<(), CliError> {
for log in logs {
match &log {
Directive::Log { is_trace, output_info } => {
if !is_trace {
match output_info {
LogOutputInfo::FinalizedOutput(output_string) => println!("{output_string}"),
_ => return Err(CliError::Generic(format!("Critical error: expected a log object from the compiler but got {log:?}\n",)))
}
} else {
dbg!("we got a trace still have to implement!");
}
}
_ => {
return Err(CliError::Generic(
"Critical error: a log opcode was returned from evaluation unsolved".to_owned(),
))
}
}
}
Ok(())
}

// FIXME: I not sure that this is the right place for this tests.
#[cfg(test)]
mod tests {
Expand Down
13 changes: 10 additions & 3 deletions crates/nargo/src/cli/test_cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor};

use crate::{errors::CliError, resolver::Resolver};

use super::{add_std_lib, NargoConfig};
use super::{add_std_lib, handle_logs, NargoConfig};

/// Run the tests for this program
#[derive(Debug, Clone, Args)]
Expand Down Expand Up @@ -94,20 +94,27 @@ fn run_test(
let language = backend.np_language();

let program = driver
.compile_no_check(language, false, allow_warnings, Some(main), show_output)
.compile_no_check(language, false, allow_warnings, Some(main))
.map_err(|_| CliError::Generic(format!("Test '{test_name}' failed to compile")))?;

let mut solved_witness = BTreeMap::new();

let mut logs = Vec::new();
// Run the backend to ensure the PWG evaluates functions like std::hash::pedersen,
// otherwise constraints involving these expressions will not error.
if let Err(error) = backend.solve(&mut solved_witness, program.circuit.opcodes) {
if let Err(error) = backend.solve(&mut solved_witness, program.circuit.opcodes, &mut logs) {
handle_logs(logs)?;

let writer = StandardStream::stderr(ColorChoice::Always);
let mut writer = writer.lock();
writer.set_color(ColorSpec::new().set_fg(Some(Color::Red))).ok();
writeln!(writer, "failed").ok();
writer.reset().ok();
return Err(error.into());
}
if show_output {
handle_logs(logs)?;
}

Ok(())
}
5 changes: 2 additions & 3 deletions crates/noirc_driver/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ impl Driver {
allow_warnings: bool,
) -> Result<CompiledProgram, ReportedError> {
self.check_crate(allow_warnings)?;
self.compile_no_check(np_language, show_ssa, allow_warnings, None, true)
self.compile_no_check(np_language, show_ssa, allow_warnings, None)
}

/// Compile the current crate. Assumes self.check_crate is called beforehand!
Expand All @@ -163,7 +163,6 @@ impl Driver {
allow_warnings: bool,
// Optional override to provide a different `main` function to start execution
main_function: Option<FuncId>,
show_output: bool,
) -> Result<CompiledProgram, ReportedError> {
// Find the local crate, one should always be present
let local_crate = self.context.def_map(LOCAL_CRATE).unwrap();
Expand All @@ -185,7 +184,7 @@ impl Driver {

let blackbox_supported = acvm::default_is_black_box_supported(np_language.clone());

match create_circuit(program, np_language, blackbox_supported, show_ssa, show_output) {
match create_circuit(program, np_language, blackbox_supported, show_ssa) {
Ok((circuit, abi)) => Ok(CompiledProgram { circuit, abi }),
Err(err) => {
// The FileId here will be the file id of the file with the main file
Expand Down
8 changes: 3 additions & 5 deletions crates/noirc_evaluator/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ mod ssa;
use acvm::{
acir::circuit::{opcodes::Opcode as AcirOpcode, Circuit, PublicInputs},
acir::native_types::{Expression, Witness},
compiler::fallback::IsBlackBoxSupported,
compiler::transformers::IsBlackBoxSupported,
Language,
};
use errors::{RuntimeError, RuntimeErrorKind};
Expand Down Expand Up @@ -43,12 +43,11 @@ pub fn create_circuit(
np_language: Language,
is_blackbox_supported: IsBlackBoxSupported,
enable_logging: bool,
show_output: bool,
) -> Result<(Circuit, Abi), RuntimeError> {
let mut evaluator = Evaluator::new();

// First evaluate the main function
evaluator.evaluate_main_alt(program.clone(), enable_logging, show_output)?;
evaluator.evaluate_main_alt(program.clone(), enable_logging)?;

let witness_index = evaluator.current_witness_index();

Expand Down Expand Up @@ -120,7 +119,6 @@ impl Evaluator {
&mut self,
program: Program,
enable_logging: bool,
show_output: bool,
) -> Result<(), RuntimeError> {
let mut ir_gen = IrGenerator::new(program);
self.parse_abi_alt(&mut ir_gen);
Expand All @@ -129,7 +127,7 @@ impl Evaluator {
ir_gen.ssa_gen_main()?;

//Generates ACIR representation:
ir_gen.context.ir_to_acir(self, enable_logging, show_output)?;
ir_gen.context.ir_to_acir(self, enable_logging)?;
Ok(())
}

Expand Down
13 changes: 1 addition & 12 deletions crates/noirc_evaluator/src/ssa/acir_gen.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use crate::ssa::{
builtin,
context::SsaContext,
node::{Instruction, Operation},
};
Expand Down Expand Up @@ -35,7 +34,6 @@ impl Acir {
ins: &Instruction,
evaluator: &mut Evaluator,
ctx: &SsaContext,
show_output: bool,
) -> Result<(), RuntimeErrorKind> {
use operations::{
binary, condition, constrain, intrinsics, load, not, r#return, store, truncate,
Expand All @@ -59,16 +57,7 @@ impl Acir {
truncate::evaluate(value, *bit_size, *max_bit_size, var_cache, evaluator, ctx)
}
Operation::Intrinsic(opcode, args) => {
let opcode = match opcode {
builtin::Opcode::Println(print_info) => {
builtin::Opcode::Println(builtin::PrintlnInfo {
is_string_output: print_info.is_string_output,
show_output,
})
}
_ => *opcode,
};
intrinsics::evaluate(args, ins, opcode, var_cache, acir_mem, ctx, evaluator)
intrinsics::evaluate(args, ins, *opcode, var_cache, acir_mem, ctx, evaluator)
}
Operation::Return(node_ids) => {
r#return::evaluate(node_ids, acir_mem, var_cache, evaluator, ctx)?
Expand Down
1 change: 1 addition & 0 deletions crates/noirc_evaluator/src/ssa/acir_gen/constraints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,7 @@ pub(crate) fn to_radix_base(
a: lhs.clone(),
b: result.clone(),
radix,
is_little_endian: true,
}));

evaluator.opcodes.push(AcirOpcode::Arithmetic(subtract(lhs, FieldElement::one(), &bytes)));
Expand Down
27 changes: 12 additions & 15 deletions crates/noirc_evaluator/src/ssa/acir_gen/operations/intrinsics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use crate::{
use acvm::{
acir::{
circuit::{
directives::{Directive, LogInfo},
directives::{Directive, LogOutputInfo},
opcodes::{BlackBoxFuncCall, FunctionInput, Opcode as AcirOpcode},
},
native_types::{Expression, Witness},
Expand Down Expand Up @@ -65,18 +65,15 @@ pub(crate) fn evaluate(
memory_map.map_array(a, &outputs, ctx);
}
}
Opcode::Println(print_info) => {
Opcode::Println => {
outputs = Vec::new(); // print statements do not output anything
if print_info.show_output {
evaluate_println(
var_cache,
memory_map,
print_info.is_string_output,
args,
ctx,
evaluator,
);
}

let is_string_output = ctx.get_as_constant(args[1]).unwrap().to_u128();
evaluate_println(var_cache, memory_map, is_string_output != 0, args, ctx, evaluator);
}
Opcode::Trace => {
outputs = Vec::new();
evaluate_println(var_cache, memory_map, false, args, ctx, evaluator)
}
Opcode::LowLevel(op) => {
let inputs = prepare_inputs(var_cache, memory_map, args, ctx, evaluator);
Expand Down Expand Up @@ -246,7 +243,7 @@ fn evaluate_println(
ctx: &SsaContext,
evaluator: &mut Evaluator,
) {
assert_eq!(args.len(), 1, "print statements can only support one argument");
// assert_eq!(args.len(), 1, "print statements can only support one argument");
let node_id = args[0];

let mut log_string = "".to_owned();
Expand Down Expand Up @@ -310,9 +307,9 @@ fn evaluate_println(
assert!(log_witnesses.is_empty() ^ log_string.is_empty());

let log_directive = if !log_string.is_empty() {
Directive::Log(LogInfo::FinalizedOutput(log_string))
Directive::Log { is_trace: false, output_info: LogOutputInfo::FinalizedOutput(log_string) }
} else {
Directive::Log(LogInfo::WitnessOutput(log_witnesses))
Directive::Log { is_trace: false, output_info: LogOutputInfo::WitnessOutput(log_witnesses) }
};

evaluator.opcodes.push(AcirOpcode::Directive(log_directive));
Expand Down
2 changes: 1 addition & 1 deletion crates/noirc_evaluator/src/ssa/acir_gen/operations/sort.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ mod test {
// compute the network output by solving the constraints
let backend = MockBackend {};
backend
.solve(&mut solved_witness, eval.opcodes.clone())
.solve(&mut solved_witness, eval.opcodes.clone(), &mut vec![])
.expect("Could not solve permutation constraints");
let mut b_val = Vec::new();
for i in 0..output.len() {
Expand Down
23 changes: 15 additions & 8 deletions crates/noirc_evaluator/src/ssa/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ pub enum Opcode {
LowLevel(BlackBoxFunc),
ToBits,
ToRadix,
Println(PrintlnInfo),
Println,
Trace,
Sort,
}

Expand All @@ -35,10 +36,9 @@ impl Opcode {
match op_name {
"to_le_bits" => Some(Opcode::ToBits),
"to_radix" => Some(Opcode::ToRadix),
"println" => {
Some(Opcode::Println(PrintlnInfo { is_string_output: false, show_output: true }))
}
"println" => Some(Opcode::Println),
"arraysort" => Some(Opcode::Sort),
"trace" => Some(Opcode::Trace),
_ => BlackBoxFunc::lookup(op_name).map(Opcode::LowLevel),
}
}
Expand All @@ -48,8 +48,9 @@ impl Opcode {
Opcode::LowLevel(op) => op.name(),
Opcode::ToBits => "to_le_bits",
Opcode::ToRadix => "to_radix",
Opcode::Println(_) => "println",
Opcode::Println => "println",
Opcode::Sort => "arraysort",
Opcode::Trace => "trace",
}
}

Expand All @@ -60,6 +61,7 @@ impl Opcode {
// Pointers do not overflow
BlackBoxFunc::SHA256
| BlackBoxFunc::Blake2s
| BlackBoxFunc::Keccak256
| BlackBoxFunc::Pedersen
| BlackBoxFunc::FixedBaseScalarMul => BigUint::zero(),
// Verify returns zero or one
Expand All @@ -75,7 +77,9 @@ impl Opcode {
}
}
}
Opcode::ToBits | Opcode::ToRadix | Opcode::Println(_) | Opcode::Sort => BigUint::zero(), //pointers do not overflow
Opcode::ToBits | Opcode::ToRadix | Opcode::Println | Opcode::Sort | Opcode::Trace => {
BigUint::zero()
} //pointers do not overflow
}
}

Expand All @@ -86,7 +90,9 @@ impl Opcode {
Opcode::LowLevel(op) => {
match op {
BlackBoxFunc::AES => todo!("ICE: AES is unimplemented"),
BlackBoxFunc::SHA256 | BlackBoxFunc::Blake2s => (32, ObjectType::Unsigned(8)),
BlackBoxFunc::SHA256 | BlackBoxFunc::Blake2s | BlackBoxFunc::Keccak256 => {
(32, ObjectType::Unsigned(8))
}
BlackBoxFunc::HashToField128Security => (1, ObjectType::NativeField),
// See issue #775 on changing this to return a boolean
BlackBoxFunc::MerkleMembership
Expand All @@ -101,7 +107,8 @@ impl Opcode {
}
Opcode::ToBits => (FieldElement::max_num_bits(), ObjectType::Boolean),
Opcode::ToRadix => (FieldElement::max_num_bits(), ObjectType::NativeField),
Opcode::Println(_) => (0, ObjectType::NotAnObject),
Opcode::Println => (0, ObjectType::NotAnObject),
Opcode::Trace => (0, ObjectType::NotAnObject),
Opcode::Sort => {
let a = super::mem::Memory::deref(ctx, args[0]).unwrap();
(ctx.mem[a].len, ctx.mem[a].element_type)
Expand Down
Loading