diff --git a/acir/src/circuit/directives.rs b/acir/src/circuit/directives.rs index c3d8e2cd0..cd94fde0e 100644 --- a/acir/src/circuit/directives.rs +++ b/acir/src/circuit/directives.rs @@ -48,6 +48,8 @@ pub enum Directive { b: Vec, radix: u32, }, + + Log(LogInfo), } impl Directive { @@ -58,6 +60,7 @@ impl Directive { Directive::Truncate { .. } => "truncate", Directive::OddRange { .. } => "odd_range", Directive::ToRadix { .. } => "to_radix", + Directive::Log { .. } => "log", } } fn to_u16(&self) -> u16 { @@ -67,6 +70,7 @@ impl Directive { Directive::Truncate { .. } => 2, Directive::OddRange { .. } => 3, Directive::ToRadix { .. } => 4, + Directive::Log { .. } => 5, } } @@ -116,6 +120,17 @@ impl Directive { } write_u32(&mut writer, *radix)?; } + Directive::Log(info) => match info { + LogInfo::FinalizedOutput(output_string) => { + write_bytes(&mut writer, output_string.as_bytes())?; + } + LogInfo::WitnessOutput(witnesses) => { + write_u32(&mut writer, witnesses.len() as u32)?; + for w in witnesses { + write_u32(&mut writer, w.witness_index())?; + } + } + }, }; Ok(()) @@ -184,6 +199,16 @@ impl Directive { } } +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +// If values are compile time and/or known during +// evaluation, we can form an output string during ACIR generation. +// Otherwise, we must store witnesses whose values will +// be fetched during the PWG stage. +pub enum LogInfo { + FinalizedOutput(String), + WitnessOutput(Vec), +} + #[test] fn serialisation_roundtrip() { fn read_write(directive: Directive) -> (Directive, Directive) { diff --git a/acir/src/circuit/opcodes.rs b/acir/src/circuit/opcodes.rs index 37017adde..a253be75b 100644 --- a/acir/src/circuit/opcodes.rs +++ b/acir/src/circuit/opcodes.rs @@ -1,6 +1,6 @@ use std::io::{Read, Write}; -use super::directives::Directive; +use super::directives::{Directive, LogInfo}; use crate::native_types::{Expression, Witness}; use crate::serialisation::{read_n, read_u16, read_u32, write_bytes, write_u16, write_u32}; use crate::BlackBoxFunc; @@ -164,6 +164,15 @@ impl std::fmt::Display for Opcode { b.last().unwrap().witness_index(), ) } + Opcode::Directive(Directive::Log(info)) => match info { + LogInfo::FinalizedOutput(output_string) => write!(f, "Log: {output_string}"), + LogInfo::WitnessOutput(witnesses) => write!( + f, + "Log: _{}..._{}", + witnesses.first().unwrap().witness_index(), + witnesses.last().unwrap().witness_index() + ), + }, } } } diff --git a/acvm/src/lib.rs b/acvm/src/lib.rs index c44fd9632..1e69fce1a 100644 --- a/acvm/src/lib.rs +++ b/acvm/src/lib.rs @@ -211,10 +211,7 @@ pub fn default_is_blackbox_supported( // attempt to transform into supported gates. If these are also not available // then a compiler error will be emitted. fn plonk_is_supported(opcode: &BlackBoxFunc) -> bool { - match opcode { - BlackBoxFunc::AES => false, - _ => true, - } + !matches!(opcode, BlackBoxFunc::AES) } match language { diff --git a/acvm/src/pwg/directives.rs b/acvm/src/pwg/directives.rs index 94bc2d60b..ebe01fb2d 100644 --- a/acvm/src/pwg/directives.rs +++ b/acvm/src/pwg/directives.rs @@ -1,6 +1,10 @@ use std::collections::BTreeMap; -use acir::{circuit::directives::Directive, native_types::Witness, FieldElement}; +use acir::{ + circuit::directives::{Directive, LogInfo}, + native_types::Witness, + FieldElement, +}; use num_bigint::BigUint; use num_traits::{One, Zero}; @@ -108,6 +112,43 @@ pub fn solve_directives( initial_witness.insert(*b, FieldElement::from_be_bytes_reduce(&int_b.to_bytes_be())); initial_witness.insert(*r, FieldElement::from_be_bytes_reduce(&int_r.to_bytes_be())); + Ok(()) + } + Directive::Log(info) => { + let witnesses = match info { + LogInfo::FinalizedOutput(output_string) => { + println!("{output_string}"); + return Ok(()); + } + LogInfo::WitnessOutput(witnesses) => witnesses, + }; + + if witnesses.len() == 1 { + let witness = &witnesses[0]; + let log_value = witness_to_value(initial_witness, *witness)?; + println!("{}", log_value.to_hex()); + + return Ok(()); + } + + // If multiple witnesses are to be fetched for a log directive, + // it assumed that an array is meant to be printed to standard output + // + // Collect all field element values corresponding to the given witness indices + // and convert them to hex strings. + let mut elements_as_hex = Vec::with_capacity(witnesses.len()); + for witness in witnesses { + let element = witness_to_value(initial_witness, *witness)?; + elements_as_hex.push(element.to_hex()); + } + + // Join all of the hex strings using a comma + let comma_separated_elements = elements_as_hex.join(","); + + let output_witnesses_string = "[".to_owned() + &comma_separated_elements + "]"; + + println!("{output_witnesses_string}"); + Ok(()) } }