Skip to content

Commit

Permalink
feat: Sync from noir (#5725)
Browse files Browse the repository at this point in the history
Automated pull of development from the
[noir](https://github.com/noir-lang/noir) programming language, a
dependency of Aztec.
BEGIN_COMMIT_OVERRIDE
feat: get last mock oracles params
(noir-lang/noir#4789)
feat: split `backend_barretenburg` into prover and verifier classes
(noir-lang/noir#4769)
chore: testing that nargo fmt is idempotent
(noir-lang/noir#4765)
feat: Sync from aztec-packages
(noir-lang/noir#4787)
fix: ArrayGet and Set are not pure
(noir-lang/noir#4783)
END_COMMIT_OVERRIDE

---------

Co-authored-by: Santiago Palladino <santiago@aztecprotocol.com>
  • Loading branch information
AztecBot and spalladino committed Apr 12, 2024
1 parent cac9cba commit 825c455
Show file tree
Hide file tree
Showing 19 changed files with 537 additions and 208 deletions.
2 changes: 1 addition & 1 deletion .noir-sync-commit
Original file line number Diff line number Diff line change
@@ -1 +1 @@
f14d913768294009c264dfaff364e325f33c6e18
1d96937a8e94a91c0c17c97102498d067fca76c3
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,12 @@ impl FunctionBuilder {
self.insert_instruction(Instruction::DecrementRc { value }, None);
}

/// Insert an enable_side_effects_if instruction. These are normally only automatically
/// inserted during the flattening pass when branching is removed.
pub(crate) fn insert_enable_side_effects_if(&mut self, condition: ValueId) {
self.insert_instruction(Instruction::EnableSideEffects { condition }, None);
}

/// Terminates the current block with the given terminator instruction
/// if the current block does not already have a terminator instruction.
fn terminate_block_with(&mut self, terminator: TerminatorInstruction) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ impl Instruction {
// In ACIR, a division with a false predicate outputs (0,0), so it cannot replace another instruction unless they have the same predicate
bin.operator != BinaryOp::Div
}
Cast(_, _) | Truncate { .. } | Not(_) | ArrayGet { .. } | ArraySet { .. } => true,
Cast(_, _) | Truncate { .. } | Not(_) => true,

// These either have side-effects or interact with memory
Constrain(..)
Expand All @@ -266,6 +266,12 @@ impl Instruction {
| DecrementRc { .. }
| RangeCheck { .. } => false,

// These can have different behavior depending on the EnableSideEffectsIf context.
// Enabling constant folding for these potentially enables replacing an enabled
// array get with one that was disabled. See
// https://github.com/noir-lang/noir/pull/4716#issuecomment-2047846328.
ArrayGet { .. } | ArraySet { .. } => false,

Call { func, .. } => match dfg[*func] {
Value::Intrinsic(intrinsic) => !intrinsic.has_side_effects(),
_ => false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -607,4 +607,55 @@ mod test {
assert_eq!(main.dfg[instructions[4]], Instruction::Constrain(v1, v_true, None));
assert_eq!(main.dfg[instructions[5]], Instruction::Constrain(v2, v_false, None));
}

// Regression for #4600
#[test]
fn array_get_regression() {
// fn main f0 {
// b0(v0: u1, v1: u64):
// enable_side_effects_if v0
// v2 = array_get [Field 0, Field 1], index v1
// v3 = not v0
// enable_side_effects_if v3
// v4 = array_get [Field 0, Field 1], index v1
// }
//
// We want to make sure after constant folding both array_gets remain since they are
// under different enable_side_effects_if contexts and thus one may be disabled while
// the other is not. If one is removed, it is possible e.g. v4 is replaced with v2 which
// is disabled (only gets from index 0) and thus returns the wrong result.
let main_id = Id::test_new(0);

// Compiling main
let mut builder = FunctionBuilder::new("main".into(), main_id);
let v0 = builder.add_parameter(Type::bool());
let v1 = builder.add_parameter(Type::unsigned(64));

builder.insert_enable_side_effects_if(v0);

let zero = builder.field_constant(0u128);
let one = builder.field_constant(1u128);

let typ = Type::Array(Rc::new(vec![Type::field()]), 2);
let array = builder.array_constant(vec![zero, one].into(), typ);

let _v2 = builder.insert_array_get(array, v1, Type::field());
let v3 = builder.insert_not(v0);

builder.insert_enable_side_effects_if(v3);
let _v4 = builder.insert_array_get(array, v1, Type::field());

// Expected output is unchanged
let ssa = builder.finish();
let main = ssa.main();
let instructions = main.dfg[main.entry_block()].instructions();
let starting_instruction_count = instructions.len();
assert_eq!(starting_instruction_count, 5);

let ssa = ssa.fold_constants();
let main = ssa.main();
let instructions = main.dfg[main.entry_block()].instructions();
let ending_instruction_count = instructions.len();
assert_eq!(starting_instruction_count, ending_instruction_count);
}
}
7 changes: 7 additions & 0 deletions noir/noir-repo/noir_stdlib/src/test.nr
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ unconstrained fn create_mock_oracle<N>(name: str<N>) -> Field {}
#[oracle(set_mock_params)]
unconstrained fn set_mock_params_oracle<P>(id: Field, params: P) {}

#[oracle(get_mock_last_params)]
unconstrained fn get_mock_last_params_oracle<P>(id: Field) -> P {}

#[oracle(set_mock_returns)]
unconstrained fn set_mock_returns_oracle<R>(id: Field, returns: R) {}

Expand All @@ -27,6 +30,10 @@ impl OracleMock {
self
}

unconstrained pub fn get_last_params<P>(self) -> P {
get_mock_last_params_oracle(self.id)
}

unconstrained pub fn returns<R>(self, returns: R) -> Self {
set_mock_returns_oracle(self.id, returns);
self
Expand Down

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@
name = "mock_oracle"
type = "bin"
authors = [""]
compiler_version = ">=0.23.0"

[dependencies]
[dependencies]
Empty file.
130 changes: 130 additions & 0 deletions noir/noir-repo/test_programs/noir_test_success/mock_oracle/src/main.nr
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
use dep::std::test::OracleMock;

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

impl Eq for Point {
fn eq(self, other: Point) -> bool {
(self.x == other.x) & (self.y == other.y)
}
}

#[oracle(void_field)]
unconstrained fn void_field_oracle() -> Field {}

unconstrained fn void_field() -> Field {
void_field_oracle()
}

#[oracle(field_field)]
unconstrained fn field_field_oracle(_x: Field) -> Field {}

unconstrained fn field_field(x: Field) -> Field {
field_field_oracle(x)
}

#[oracle(struct_field)]
unconstrained fn struct_field_oracle(_point: Point, _array: [Field; 4]) -> Field {}

unconstrained fn struct_field(point: Point, array: [Field; 4]) -> Field {
struct_field_oracle(point, array)
}

#[test(should_fail)]
fn test_mock_no_returns() {
OracleMock::mock("void_field");
void_field(); // Some return value must be set
}

#[test]
fn test_mock() {
OracleMock::mock("void_field").returns(10);
assert_eq(void_field(), 10);
}

#[test]
fn test_multiple_mock() {
let first_mock = OracleMock::mock("void_field").returns(10);
OracleMock::mock("void_field").returns(42);

// The mocks are searched for in creation order, so the first one prevents the second from being called.
assert_eq(void_field(), 10);

first_mock.clear();
assert_eq(void_field(), 42);
}

#[test]
fn test_multiple_mock_times() {
OracleMock::mock("void_field").returns(10).times(2);
OracleMock::mock("void_field").returns(42);

assert_eq(void_field(), 10);
assert_eq(void_field(), 10);
assert_eq(void_field(), 42);
}

#[test]
fn test_mock_with_params() {
OracleMock::mock("field_field").with_params((5,)).returns(10);
assert_eq(field_field(5), 10);
}

#[test]
fn test_multiple_mock_with_params() {
OracleMock::mock("field_field").with_params((5,)).returns(10);
OracleMock::mock("field_field").with_params((7,)).returns(14);

assert_eq(field_field(5), 10);
assert_eq(field_field(7), 14);
}

#[test]
fn test_mock_last_params() {
let mock = OracleMock::mock("field_field").returns(10);
assert_eq(field_field(5), 10);

assert_eq(mock.get_last_params(), 5);
}

#[test]
fn test_mock_last_params_many_calls() {
let mock = OracleMock::mock("field_field").returns(10);
assert_eq(field_field(5), 10);
assert_eq(field_field(7), 10);

assert_eq(mock.get_last_params(), 7);
}

#[test]
fn test_mock_struct_field() {
// Combination of simpler test cases

let array = [1, 2, 3, 4];
let another_array = [4, 3, 2, 1];
let point = Point { x: 14, y: 27 };

OracleMock::mock("struct_field").returns(42).times(2);
let timeless_mock = OracleMock::mock("struct_field").returns(0);

assert_eq(42, struct_field(point, array));
assert_eq(42, struct_field(point, array));
// The times(2) mock is now cleared

assert_eq(0, struct_field(point, array));

let last_params: (Point, [Field; 4]) = timeless_mock.get_last_params();
assert_eq(last_params.0, point);
assert_eq(last_params.1, array);

// We clear the mock with no times() to allow other mocks to be callable
timeless_mock.clear();

OracleMock::mock("struct_field").with_params((point, array)).returns(10);
OracleMock::mock("struct_field").with_params((point, another_array)).returns(20);
assert_eq(10, struct_field(point, array));
assert_eq(20, struct_field(point, another_array));
}

2 changes: 1 addition & 1 deletion noir/noir-repo/tooling/bb_abstraction_leaks/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use const_format::formatcp;

const USERNAME: &str = "AztecProtocol";
const REPO: &str = "aztec-packages";
const VERSION: &str = "0.33.0";
const VERSION: &str = "0.34.0";
const TAG: &str = formatcp!("aztec-packages-v{}", VERSION);

const API_URL: &str =
Expand Down

0 comments on commit 825c455

Please sign in to comment.