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: Sync from aztec-packages #5125

Merged
merged 5 commits into from
May 28, 2024
Merged
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
2 changes: 1 addition & 1 deletion .aztec-sync-commit
Original file line number Diff line number Diff line change
@@ -1 +1 @@
221e2479622aef8e70120dc0a9f91ffcbc99efba
1d785fd1087d7387fc29213ca3be50b2fc9c4725
57 changes: 5 additions & 52 deletions acvm-repo/acvm_js/src/execute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,6 @@ use crate::{
JsExecutionError, JsSolvedAndReturnWitness, JsWitnessMap, JsWitnessStack,
};

#[wasm_bindgen]
pub struct WasmBlackBoxFunctionSolver;

impl WasmBlackBoxFunctionSolver {
async fn initialize() -> WasmBlackBoxFunctionSolver {
WasmBlackBoxFunctionSolver
}
}

#[wasm_bindgen(js_name = "createBlackBoxSolver")]
pub async fn create_black_box_solver() -> WasmBlackBoxFunctionSolver {
WasmBlackBoxFunctionSolver::initialize().await
}

/// Executes an ACIR circuit to generate the solved witness from the initial witness.
///
/// @param {Uint8Array} circuit - A serialized representation of an ACIR circuit
Expand All @@ -58,14 +44,12 @@ pub async fn execute_circuit(
/// Executes an ACIR circuit to generate the solved witness from the initial witness.
/// This method also extracts the public return values from the solved witness into its own return witness.
///
/// @param {&WasmBlackBoxFunctionSolver} solver - A black box solver.
/// @param {Uint8Array} circuit - A serialized representation of an ACIR circuit
/// @param {WitnessMap} initial_witness - The initial witness map defining all of the inputs to `circuit`..
/// @param {ForeignCallHandler} foreign_call_handler - A callback to process any foreign calls from the circuit.
/// @returns {SolvedAndReturnWitness} The solved witness calculated by executing the circuit on the provided inputs, as well as the return witness indices as specified by the circuit.
#[wasm_bindgen(js_name = executeCircuitWithReturnWitness, skip_jsdoc)]
pub async fn execute_circuit_with_return_witness(
_solver: &WasmBlackBoxFunctionSolver,
program: Vec<u8>,
initial_witness: JsWitnessMap,
foreign_call_handler: ForeignCallHandler,
Expand Down Expand Up @@ -94,28 +78,10 @@ pub async fn execute_circuit_with_return_witness(

/// Executes an ACIR circuit to generate the solved witness from the initial witness.
///
/// @param {&WasmBlackBoxFunctionSolver} solver - A black box solver.
/// @param {Uint8Array} circuit - A serialized representation of an ACIR circuit
/// @param {WitnessMap} initial_witness - The initial witness map defining all of the inputs to `circuit`..
/// @param {ForeignCallHandler} foreign_call_handler - A callback to process any foreign calls from the circuit.
/// @returns {WitnessMap} The solved witness calculated by executing the circuit on the provided inputs.
#[wasm_bindgen(js_name = executeCircuitWithBlackBoxSolver, skip_jsdoc)]
pub async fn execute_circuit_with_black_box_solver(
_solver: &WasmBlackBoxFunctionSolver,
program: Vec<u8>,
initial_witness: JsWitnessMap,
foreign_call_handler: ForeignCallHandler,
) -> Result<JsWitnessMap, Error> {
console_error_panic_hook::set_once();

let mut witness_stack =
execute_program_with_native_type_return(program, initial_witness, &foreign_call_handler)
.await?;
let witness_map =
witness_stack.pop().expect("Should have at least one witness on the stack").witness;
Ok(witness_map.into())
}

/// @param {Uint8Array} program - A serialized representation of an ACIR program
/// @param {WitnessMap} initial_witness - The initial witness map defining all of the inputs to `program`.
/// @param {ForeignCallHandler} foreign_call_handler - A callback to process any foreign calls from the program.
/// @returns {WitnessStack} The solved witness calculated by executing the program on the provided inputs.
#[wasm_bindgen(js_name = executeProgram, skip_jsdoc)]
pub async fn execute_program(
program: Vec<u8>,
Expand All @@ -124,21 +90,8 @@ pub async fn execute_program(
) -> Result<JsWitnessStack, Error> {
console_error_panic_hook::set_once();

let solver = WasmBlackBoxFunctionSolver::initialize().await;

execute_program_with_black_box_solver(&solver, program, initial_witness, &foreign_call_handler)
.await
}

#[wasm_bindgen(js_name = executeProgramWithBlackBoxSolver, skip_jsdoc)]
pub async fn execute_program_with_black_box_solver(
_solver: &WasmBlackBoxFunctionSolver,
program: Vec<u8>,
initial_witness: JsWitnessMap,
foreign_call_executor: &ForeignCallHandler,
) -> Result<JsWitnessStack, Error> {
let witness_stack =
execute_program_with_native_type_return(program, initial_witness, foreign_call_executor)
execute_program_with_native_type_return(program, initial_witness, &foreign_call_handler)
.await?;

Ok(witness_stack.into())
Expand Down
5 changes: 1 addition & 4 deletions acvm-repo/acvm_js/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,7 @@ pub use build_info::build_info;
pub use compression::{
compress_witness, compress_witness_stack, decompress_witness, decompress_witness_stack,
};
pub use execute::{
create_black_box_solver, execute_circuit, execute_circuit_with_black_box_solver,
execute_circuit_with_return_witness, execute_program, execute_program_with_black_box_solver,
};
pub use execute::{execute_circuit, execute_circuit_with_return_witness, execute_program};
pub use js_execution_error::JsExecutionError;
pub use js_witness_map::JsSolvedAndReturnWitness;
pub use js_witness_map::JsWitnessMap;
Expand Down
30 changes: 1 addition & 29 deletions acvm-repo/acvm_js/test/browser/execute_circuit.test.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,5 @@
import { expect } from '@esm-bundle/chai';
import initACVM, {
createBlackBoxSolver,
executeCircuit,
executeCircuitWithBlackBoxSolver,
WasmBlackBoxFunctionSolver,
WitnessMap,
initLogLevel,
ForeignCallHandler,
} from '@noir-lang/acvm_js';
import initACVM, { executeCircuit, WitnessMap, initLogLevel, ForeignCallHandler } from '@noir-lang/acvm_js';

beforeEach(async () => {
await initACVM();
Expand Down Expand Up @@ -122,23 +114,3 @@ it('successfully executes a MemoryOp opcode', async () => {

expect(solvedWitness).to.be.deep.eq(expectedWitnessMap);
});

it('successfully executes two circuits with same backend', async function () {
// chose pedersen op here because it is the one with slow initialization
// that led to the decision to pull backend initialization into a separate
// function/wasmbind
const solver: WasmBlackBoxFunctionSolver = await createBlackBoxSolver();

const { bytecode, initialWitnessMap, expectedWitnessMap } = await import('../shared/pedersen');

const solvedWitness0: WitnessMap = await executeCircuitWithBlackBoxSolver(solver, bytecode, initialWitnessMap, () => {
throw Error('unexpected oracle');
});

expect(solvedWitness0).to.be.deep.eq(expectedWitnessMap);

const solvedWitness1: WitnessMap = await executeCircuitWithBlackBoxSolver(solver, bytecode, initialWitnessMap, () => {
throw Error('unexpected oracle');
});
expect(solvedWitness1).to.be.deep.eq(expectedWitnessMap);
});
37 changes: 6 additions & 31 deletions acvm-repo/acvm_js/test/node/execute_circuit.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
import { expect } from 'chai';
import {
createBlackBoxSolver,
executeCircuit,
executeCircuitWithBlackBoxSolver,
WasmBlackBoxFunctionSolver,
WitnessMap,
ForeignCallHandler,
executeProgram,
Expand Down Expand Up @@ -120,40 +117,18 @@ it('successfully executes a MemoryOp opcode', async () => {
expect(solvedWitness).to.be.deep.eq(expectedWitnessMap);
});

it('successfully executes two circuits with same backend', async function () {
this.timeout(10000);

// chose pedersen op here because it is the one with slow initialization
// that led to the decision to pull backend initialization into a separate
// function/wasmbind
const solver: WasmBlackBoxFunctionSolver = await createBlackBoxSolver();

const { bytecode, initialWitnessMap, expectedWitnessMap } = await import('../shared/pedersen');

const solvedWitness0 = await executeCircuitWithBlackBoxSolver(solver, bytecode, initialWitnessMap, () => {
throw Error('unexpected oracle');
});

const solvedWitness1 = await executeCircuitWithBlackBoxSolver(solver, bytecode, initialWitnessMap, () => {
throw Error('unexpected oracle');
});

expect(solvedWitness0).to.be.deep.eq(expectedWitnessMap);
expect(solvedWitness1).to.be.deep.eq(expectedWitnessMap);
});

it('successfully executes 500 circuits with same backend', async function () {
it('successfully executes 500 pedersen circuits', async function () {
this.timeout(100000);

// chose pedersen op here because it is the one with slow initialization
// that led to the decision to pull backend initialization into a separate
// function/wasmbind
const solver: WasmBlackBoxFunctionSolver = await createBlackBoxSolver();
// Pedersen opcodes used to have a large upfront cost due to generator calculation
// so we'd need to pass around the blackbox solver in JS to avoid redoing this work.
//
// This test now shows that we don't need to do this anymore without a performance regression.

const { bytecode, initialWitnessMap, expectedWitnessMap } = await import('../shared/pedersen');

for (let i = 0; i < 500; i++) {
const solvedWitness = await executeCircuitWithBlackBoxSolver(solver, bytecode, initialWitnessMap, () => {
const solvedWitness = await executeCircuit(bytecode, initialWitnessMap, () => {
throw Error('unexpected oracle');
});

Expand Down
13 changes: 2 additions & 11 deletions aztec_macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,6 @@
for func in module.functions.iter_mut() {
let mut is_private = false;
let mut is_public = false;
let mut is_public_vm = false;
let mut is_initializer = false;
let mut is_internal = false;
let mut insert_init_check = has_initializer;
Expand All @@ -146,29 +145,21 @@
} else if is_custom_attribute(&secondary_attribute, "aztec(initializer)") {
is_initializer = true;
insert_init_check = false;
} else if is_custom_attribute(&secondary_attribute, "aztec(noinitcheck)") {

Check warning on line 148 in aztec_macros/src/lib.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (noinitcheck)
insert_init_check = false;
} else if is_custom_attribute(&secondary_attribute, "aztec(internal)") {
is_internal = true;
} else if is_custom_attribute(&secondary_attribute, "aztec(public)") {
is_public = true;
} else if is_custom_attribute(&secondary_attribute, "aztec(public-vm)") {
is_public_vm = true;
}
if is_custom_attribute(&secondary_attribute, "aztec(view)") {
is_static = true;
}
}

// Apply transformations to the function based on collected attributes
if is_private || is_public || is_public_vm {
let fn_type = if is_private {
"Private"
} else if is_public_vm {
"Avm"
} else {
"Public"
};
if is_private || is_public {
let fn_type = if is_private { "Private" } else { "Public" };
let stub_src = stub_function(fn_type, func, is_static);
stubs.push((stub_src, Location { file: *file_id, span: func.name_ident().span() }));

Expand Down
6 changes: 3 additions & 3 deletions aztec_macros/src/transforms/contract_interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ pub fn stub_function(aztec_visibility: &str, func: &NoirFunction, is_static_call
})
.collect::<Vec<_>>()
.join("");
if aztec_visibility != "Avm" {
if aztec_visibility != "Public" {
let args_hash = if !parameters.is_empty() {
format!(
"let mut args_acc: [Field] = &[];
Expand Down Expand Up @@ -125,7 +125,7 @@ pub fn stub_function(aztec_visibility: &str, func: &NoirFunction, is_static_call
);
let fn_body = format!(
"{}
dep::aztec::context::Avm{}{}CallInterface {{
dep::aztec::context::Public{}{}CallInterface {{
target_contract: self.target_contract,
selector: {},
args: args_acc,
Expand All @@ -134,7 +134,7 @@ pub fn stub_function(aztec_visibility: &str, func: &NoirFunction, is_static_call
args, is_static, is_void, fn_selector,
);
format!(
"pub fn {}(self, {}) -> dep::aztec::context::Avm{}{}CallInterface{} {{
"pub fn {}(self, {}) -> dep::aztec::context::Public{}{}CallInterface{} {{
{}
}}",
fn_name, fn_parameters, is_static, is_void, return_type_hint, fn_body
Expand Down
47 changes: 25 additions & 22 deletions aztec_macros/src/transforms/functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,15 @@ pub fn transform_function(
is_internal: bool,
is_static: bool,
) -> Result<(), AztecMacroError> {
assert!(matches!(ty, "Private" | "Public"));
let context_name = format!("{}Context", ty);
let inputs_name = format!("{}ContextInputs", ty);
let return_type_name = format!("{}CircuitPublicInputs", ty);
let is_avm = ty == "Avm";
let is_private = ty == "Private";

// Force a static context if the function is static
if is_static {
let is_static_check = create_static_check(func.name(), is_avm);
let is_static_check = create_static_check(func.name(), is_private);
func.def.body.statements.insert(0, is_static_check);
}

Expand Down Expand Up @@ -72,10 +72,10 @@ pub fn transform_function(
}

// Insert the context creation as the first action
let create_context = if !is_avm {
create_context(&context_name, &func.def.parameters)?
let create_context = if is_private {
create_context_private(&context_name, &func.def.parameters)?
} else {
create_context_avm()?
create_context_public()?
};
func.def.body.statements.splice(0..0, (create_context).iter().cloned());

Expand All @@ -84,7 +84,7 @@ pub fn transform_function(
func.def.parameters.insert(0, input);

// Abstract return types such that they get added to the kernel's return_values
if !is_avm {
if is_private {
if let Some(return_values_statements) = abstract_return_values(func)? {
// In case we are pushing return values to the context, we remove the statement that originated it
// This avoids running duplicate code, since blocks like if/else can be value returning statements
Expand All @@ -101,13 +101,13 @@ pub fn transform_function(
}

// Push the finish method call to the end of the function
if !is_avm {
if is_private {
let finish_def = create_context_finish();
func.def.body.statements.push(finish_def);
}

// The AVM doesn't need a return type yet.
if !is_avm {
if is_private {
let return_type = create_return_type(&return_type_name);
func.def.return_type = return_type;
func.def.return_visibility = Visibility::Public;
Expand All @@ -116,7 +116,7 @@ pub fn transform_function(
}

// Public functions should have unconstrained auto-inferred
func.def.is_unconstrained = matches!(ty, "Public" | "Avm");
func.def.is_unconstrained = !is_private;

// Private functions need to be recursive
if is_private {
Expand Down Expand Up @@ -285,8 +285,8 @@ fn create_mark_as_initialized(ty: &str) -> Statement {
/// ```noir
/// assert(context.inputs.call_context.is_static_call == true, "Function can only be called statically")
/// ```
fn create_static_check(fname: &str, is_avm: bool) -> Statement {
let is_static_call_expr = if !is_avm {
fn create_static_check(fname: &str, is_private: bool) -> Statement {
let is_static_call_expr = if is_private {
["inputs", "call_context", "is_static_call"]
.iter()
.fold(variable("context"), |acc, member| member_access(acc, member))
Expand Down Expand Up @@ -410,7 +410,7 @@ fn serialize_to_hasher(
/// let mut context = PrivateContext::new(inputs, hasher.hash());
/// }
/// ```
fn create_context(ty: &str, params: &[Param]) -> Result<Vec<Statement>, AztecMacroError> {
fn create_context_private(ty: &str, params: &[Param]) -> Result<Vec<Statement>, AztecMacroError> {
let mut injected_statements: Vec<Statement> = vec![];

let hasher_name = "args_hasher";
Expand Down Expand Up @@ -471,30 +471,33 @@ fn create_context(ty: &str, params: &[Param]) -> Result<Vec<Statement>, AztecMac
Ok(injected_statements)
}

/// Creates the private context object to be accessed within the function, the parameters need to be extracted to be
/// appended into the args hash object.
/// Creates the public context object to be accessed within the function.
///
/// The replaced code:
/// ```noir
/// #[aztec(public-vm)]
/// fn foo(inputs: AvmContextInputs, ...) -> Field {
/// let mut context = AvmContext::new(inputs);
/// #[aztec(public)]
/// fn foo(inputs: PublicContextInputs, ...) -> Field {
/// let mut context = PublicContext::new(inputs);
/// }
/// ```
fn create_context_avm() -> Result<Vec<Statement>, AztecMacroError> {
fn create_context_public() -> Result<Vec<Statement>, AztecMacroError> {
let mut injected_expressions: Vec<Statement> = vec![];

// Create the inputs to the context
let ty = "AvmContext";
let inputs_expression = variable("inputs");
let path_snippet = ty.to_case(Case::Snake); // e.g. private_context

// let mut context = {ty}::new(inputs, hash);
let let_context = mutable_assignment(
"context", // Assigned to
call(
variable_path(chained_dep!("aztec", "context", &path_snippet, ty, "new")), // Path
vec![inputs_expression], // args
variable_path(chained_dep!(
"aztec",
"context",
"public_context",
"PublicContext",
"new"
)), // Path
vec![inputs_expression], // args
),
);
injected_expressions.push(let_context);
Expand Down
Loading
Loading