Skip to content

Commit

Permalink
feat: Sync from aztec-packages (#5125)
Browse files Browse the repository at this point in the history
Automated pull of Noir development from
[aztec-packages](https://github.com/AztecProtocol/aztec-packages).
BEGIN_COMMIT_OVERRIDE
feat: remove external blackbox solver from acir simulator
(AztecProtocol/aztec-packages#6586)
chore: remove aes slice
(AztecProtocol/aztec-packages#6550)
END_COMMIT_OVERRIDE

---------

Co-authored-by: Tom French <15848336+TomAFrench@users.noreply.github.com>
Co-authored-by: TomAFrench <tom@tomfren.ch>
  • Loading branch information
3 people committed May 28, 2024
1 parent 33c2b6b commit a40a9a5
Show file tree
Hide file tree
Showing 17 changed files with 221 additions and 196 deletions.
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 @@ fn transform_module(
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 @@ -152,23 +151,15 @@ fn transform_module(
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

0 comments on commit a40a9a5

Please sign in to comment.