Skip to content

Commit

Permalink
Merge branch 'master' into euclid-unnecessary-witnesses
Browse files Browse the repository at this point in the history
  • Loading branch information
TomAFrench committed Jul 21, 2023
2 parents ac1de86 + 59f92e3 commit 49c9b01
Show file tree
Hide file tree
Showing 27 changed files with 549 additions and 280 deletions.
185 changes: 89 additions & 96 deletions Cargo.lock

Large diffs are not rendered by default.

3 changes: 1 addition & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ noirc_errors = { path = "crates/noirc_errors" }
noirc_evaluator = { path = "crates/noirc_evaluator" }
noirc_frontend = { path = "crates/noirc_frontend" }
noir_wasm = { path = "crates/wasm" }

cfg-if = "1.0.0"
clap = { version = "4.1.4", features = ["derive"] }
codespan = { version = "0.11.1", features = ["serialization"] }
Expand All @@ -58,4 +57,4 @@ wasm-bindgen-test = "0.3.33"
base64 = "0.21.2"

[patch.crates-io]
async-lsp = { git = "https://github.com/oxalica/async-lsp", rev = "09dbcc11046f7a188a80137f8d36484d86c78c78" }
async-lsp = { git = "https://github.com/oxalica/async-lsp", rev = "09dbcc11046f7a188a80137f8d36484d86c78c78" }
1 change: 1 addition & 0 deletions crates/nargo/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ noirc_driver.workspace = true
iter-extended.workspace = true
toml.workspace = true
serde.workspace = true
serde_json.workspace = true
thiserror.workspace = true
noirc_errors.workspace = true
base64.workspace = true
18 changes: 18 additions & 0 deletions crates/nargo/src/errors.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use acvm::pwg::OpcodeResolutionError;
use noirc_abi::errors::{AbiError, InputParserError};
use thiserror::Error;

#[derive(Debug, Error)]
Expand All @@ -10,4 +11,21 @@ pub enum NargoError {
/// ACIR circuit solving error
#[error(transparent)]
SolvingError(#[from] OpcodeResolutionError),

#[error(transparent)]
ForeignCallError(#[from] ForeignCallError),
}

#[derive(Debug, Error)]
pub enum ForeignCallError {
#[error("Foreign call inputs needed for execution are missing")]
MissingForeignCallInputs,

/// ABI encoding/decoding error
#[error(transparent)]
AbiError(#[from] AbiError),

/// Input parsing error
#[error(transparent)]
InputParserError(#[from] InputParserError),
}
46 changes: 4 additions & 42 deletions crates/nargo/src/ops/execute.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use acvm::acir::brillig::{ForeignCallResult, Value};
use acvm::pwg::{ACVMStatus, ForeignCallWaitInfo, ACVM};
use acvm::pwg::{ACVMStatus, ACVM};
use acvm::BlackBoxFunctionSolver;
use acvm::{acir::circuit::Circuit, acir::native_types::WitnessMap};
use iter_extended::vecmap;

use crate::NargoError;

use super::foreign_calls::ForeignCall;

pub fn execute_circuit<B: BlackBoxFunctionSolver + Default>(
_backend: &B,
circuit: Circuit,
Expand All @@ -24,7 +24,7 @@ pub fn execute_circuit<B: BlackBoxFunctionSolver + Default>(
ACVMStatus::Failure(error) => return Err(error.into()),
ACVMStatus::RequiresForeignCall => {
while let Some(foreign_call) = acvm.get_pending_foreign_call() {
let foreign_call_result = execute_foreign_call(foreign_call);
let foreign_call_result = ForeignCall::execute(foreign_call)?;
acvm.resolve_pending_foreign_call(foreign_call_result);
}
}
Expand All @@ -34,41 +34,3 @@ pub fn execute_circuit<B: BlackBoxFunctionSolver + Default>(
let solved_witness = acvm.finalize();
Ok(solved_witness)
}

fn execute_foreign_call(foreign_call: &ForeignCallWaitInfo) -> ForeignCallResult {
// TODO(#1615): Nargo only supports "oracle_print_**_impl" functions that print a singular value or an array and nothing else
// This should be expanded in a general logging refactor
match foreign_call.function.as_str() {
// TODO(#1910): Move to an enum and don't match directly on these strings
"oracle_print_impl" => {
let values = &foreign_call.inputs[0];
println!("{:?}", values[0].to_field().to_hex());
values[0].into()
}
"oracle_print_array_impl" => {
let mut outputs_hex = Vec::new();
for values in &foreign_call.inputs {
for value in values {
outputs_hex.push(value.to_field().to_hex());
}
}
// Join all of the hex strings using a comma
let comma_separated_elements = outputs_hex.join(", ");
let output_witnesses_string = "[".to_owned() + &comma_separated_elements + "]";
println!("{output_witnesses_string}");

foreign_call.inputs[0][0].into()
}
"get_number_sequence" => {
let sequence_length: u128 = foreign_call.inputs[0][0].to_field().to_u128();

vecmap(0..sequence_length, Value::from).into()
}
"get_reverse_number_sequence" => {
let sequence_length: u128 = foreign_call.inputs[0][0].to_field().to_u128();

vecmap((0..sequence_length).rev(), Value::from).into()
}
_ => panic!("unexpected foreign call type"),
}
}
93 changes: 93 additions & 0 deletions crates/nargo/src/ops/foreign_calls.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
use acvm::{
acir::brillig::{ForeignCallResult, Value},
pwg::ForeignCallWaitInfo,
};
use iter_extended::vecmap;
use noirc_abi::{decode_string_value, decode_value, input_parser::json::JsonTypes, AbiType};

use crate::errors::ForeignCallError;

/// This enumeration represents the Brillig foreign calls that are natively supported by nargo.
/// After resolution of a foreign call, nargo will restart execution of the ACVM
pub(crate) enum ForeignCall {
Println,
Sequence,
ReverseSequence,
}

impl std::fmt::Display for ForeignCall {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.name())
}
}

impl ForeignCall {
pub(crate) fn name(&self) -> &'static str {
match self {
ForeignCall::Println => "println",
ForeignCall::Sequence => "get_number_sequence",
ForeignCall::ReverseSequence => "get_reverse_number_sequence",
}
}

pub(crate) fn lookup(op_name: &str) -> Option<ForeignCall> {
match op_name {
"println" => Some(ForeignCall::Println),
"get_number_sequence" => Some(ForeignCall::Sequence),
"get_reverse_number_sequence" => Some(ForeignCall::ReverseSequence),
_ => None,
}
}

pub(crate) fn execute(
foreign_call: &ForeignCallWaitInfo,
) -> Result<ForeignCallResult, ForeignCallError> {
let foreign_call_name = foreign_call.function.as_str();
match Self::lookup(foreign_call_name) {
Some(ForeignCall::Println) => {
Self::execute_println(&foreign_call.inputs)?;
Ok(ForeignCallResult { values: vec![] })
}
Some(ForeignCall::Sequence) => {
let sequence_length: u128 = foreign_call.inputs[0][0].to_field().to_u128();

Ok(vecmap(0..sequence_length, Value::from).into())
}
Some(ForeignCall::ReverseSequence) => {
let sequence_length: u128 = foreign_call.inputs[0][0].to_field().to_u128();

Ok(vecmap((0..sequence_length).rev(), Value::from).into())
}
None => panic!("unexpected foreign call {:?}", foreign_call_name),
}
}

fn execute_println(foreign_call_inputs: &[Vec<Value>]) -> Result<(), ForeignCallError> {
let (abi_type, input_values) = fetch_abi_type(foreign_call_inputs)?;

// We must use a flat map here as each value in a struct will be in a separate input value
let mut input_values_as_fields =
input_values.iter().flat_map(|values| values.iter().map(|value| value.to_field()));
let decoded_value = decode_value(&mut input_values_as_fields, &abi_type)?;

let json_value = JsonTypes::try_from_input_value(&decoded_value, &abi_type)?;

println!("{json_value}");
Ok(())
}
}

/// Fetch the abi type from the foreign call input
/// The remaining input values should hold the values to be printed
fn fetch_abi_type(
foreign_call_inputs: &[Vec<Value>],
) -> Result<(AbiType, &[Vec<Value>]), ForeignCallError> {
let (abi_type_as_values, input_values) =
foreign_call_inputs.split_last().ok_or(ForeignCallError::MissingForeignCallInputs)?;
let abi_type_as_fields = vecmap(abi_type_as_values, |value| value.to_field());
let abi_type_as_string = decode_string_value(&abi_type_as_fields);
let abi_type: AbiType = serde_json::from_str(&abi_type_as_string)
.map_err(|err| ForeignCallError::InputParserError(err.into()))?;

Ok((abi_type, input_values))
}
1 change: 1 addition & 0 deletions crates/nargo/src/ops/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ pub use self::verify::verify_proof;

mod codegen_verifier;
mod execute;
mod foreign_calls;
mod preprocess;
mod prove;
mod verify;
6 changes: 3 additions & 3 deletions crates/nargo_cli/src/cli/check_cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -220,9 +220,9 @@ pub(crate) fn check_crate_and_report_errors(
context: &mut Context,
crate_id: CrateId,
deny_warnings: bool,
enable_slices: bool,
experimental_ssa: bool,
) -> Result<(), ReportedErrors> {
let result =
check_crate(context, crate_id, deny_warnings, enable_slices).map(|warnings| ((), warnings));
let result = check_crate(context, crate_id, deny_warnings, experimental_ssa)
.map(|warnings| ((), warnings));
super::compile_cmd::report_errors(result, context, deny_warnings)
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,9 @@ use dep::std::slice;

// Tests oracle usage in brillig/unconstrained functions
fn main(x: Field) {
// call through a brillig wrapper
oracle_print_array_wrapper([x, x]);

// TODO(#1615) Nargo currently only supports resolving one foreign call
// oracle_print_wrapper(x);

get_number_sequence_wrapper(20);
}

// TODO(#1911)
#[oracle(oracle_print_impl)]
unconstrained fn oracle_print(_x : Field) -> Field {}

unconstrained fn oracle_print_wrapper(x: Field) {
oracle_print(x);
}

// TODO(#1911)
#[oracle(oracle_print_array_impl)]
unconstrained fn oracle_print_array(_arr : [Field; 2]) -> Field {}

unconstrained fn oracle_print_array_wrapper(arr: [Field; 2]) {
oracle_print_array(arr);
}

// TODO(#1911): This function does not need to be an oracle but acts
// as a useful test while we finalize code generation for slices in Brillig
#[oracle(get_number_sequence)]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[package]
authors = [""]
compiler_version = "0.8.0"

[dependencies]
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
x = "5"
y = "10"
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
use dep::std;

fn main(x : Field, y : pub Field) {

std::println("*** println ***");
std::println(x);
std::println([x, y]);

let s = myStruct { y: x, x: y };
let foo = fooStruct { my_struct: s, foo: 15 };
std::println(foo);
}

struct myStruct {
y: Field,
x: Field,
}

struct fooStruct {
my_struct: myStruct,
foo: Field,
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[package]
authors = [""]
compiler_version = "0.1"

[dependencies]
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
x = [0x3f, 0x1c, 0xb8, 0x99, 0xab]
z = 3
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
global NIBBLE_LENGTH: comptime Field = 16;

fn compact_decode<N>(input: [u8; N], length: Field) -> ([u4; NIBBLE_LENGTH], Field)
{
assert(2*input.len() as u64 <= NIBBLE_LENGTH as u64);
assert(length as u64 <= input.len() as u64);

let mut nibble = [0 as u4; NIBBLE_LENGTH];

let first_nibble = (input[0] >> 4) as u4;
let parity = first_nibble as u1;

if parity == 1
{
nibble[0] = (input[0] & 0x0f) as u4;
for i in 1..input.len()
{
if i as u64 < length as u64
{
let x = input[i];
nibble[2*i - 1] = (x >> 4) as u4;
nibble[2*i] = (x & 0x0f) as u4;
}
}
}
else
{
for i in 0..2
{
if (i as u64) < length as u64 - 1
{
let x = input[i + 1];
nibble[2*i] = (x >> 4) as u4;
nibble[2*i + 1] = (x & 0x0f) as u4;
}
}
}

let out = (nibble, 2*length + (parity as Field) - 2);

out
}

fn enc<N>(value: [u8; N], value_length: Field) -> ([u8; 32], Field)
{
assert(value.len() as u8 >= value_length as u8);
let mut out_value = [0; 32];
if value_length == 0
{
let out = (out_value, value_length);
out
}
else { if value_length as u8 < 31
{
out_value[0] = 0x80 + value_length as u8;

for i in 1..value.len()
{
out_value[i] = value[i-1];
}

let out = (out_value, value_length + 1);

out
}
else
{
let out = (out_value, 32);
out
}
}
}

fn main(x: [u8; 5], z: Field)
{
//Issue 1144
let (nib, len) = compact_decode(x,z);
assert(len == 5);
assert([nib[0], nib[1], nib[2], nib[3], nib[4]] == [15, 1, 12, 11, 8]);

// Issue 1169
let val1 = [0xb8,0x8f,0x61,0xe6,0xfb,0xda,0x83,0xfb,0xff,0xfa,0xbe,0x36,0x41,0x12,0x13,0x74,0x80,0x39,0x80,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00];
let val1_length = 20;

let enc_val1 = enc(val1,val1_length);

assert(enc_val1.0 == [0x94,0xb8,0x8f,0x61,0xe6,0xfb,0xda,0x83,0xfb,0xff,0xfa,0xbe,0x36,0x41,0x12,0x13,0x74,0x80,0x39,0x80,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00]);
assert(enc_val1.1 == 21);

}
Loading

0 comments on commit 49c9b01

Please sign in to comment.