Skip to content

Commit

Permalink
feat!: make compute_nullifier_without_context unconstrained (#8742)
Browse files Browse the repository at this point in the history
With noir-lang/noir#5717 closed, we can now
make `compute_nullifier_without_context` `unconstrained`, as it should
have been. This clears the multiple warnings caused by calling
`get_nsk_app` without an `unsafe` block.

I also moved the implementation of `TransparentNote` around, since we
were calling the now unconstrained version. This nicely would've
resulted in a warning about a missing `unsafe` block had I not changed
it 😁

---------

Co-authored-by: Tom French <[email protected]>
  • Loading branch information
2 people authored and Rumata888 committed Sep 27, 2024
1 parent 5a37344 commit 1420859
Show file tree
Hide file tree
Showing 18 changed files with 42 additions and 28 deletions.
10 changes: 10 additions & 0 deletions docs/docs/migration_notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,16 @@ Aztec is in full-speed development. Literally every version breaks compatibility

## TBD

### [Aztec.nr] Changes to `NullifiableNote`

The `compute_nullifier_without_context` function is now `unconstrained`. It had always been meant to be called in unconstrained contexts (which is why it did not receive the `context` object), but now that Noir supports trait functions being `unconstrained` this can be implemented properly. Users must add the `unconstrained` keyword to their implementations of the trait:

```diff
impl NullifiableNote for MyCustomNote {
- fn compute_nullifier_without_context(self) -> Field {
+ unconstrained fn compute_nullifier_without_context(self) -> Field {
```

### [Aztec.nr] Make `TestEnvironment` unconstrained

All of `TestEnvironment`'s functions are now `unconstrained`, preventing accidentally calling them in a constrained circuit, among other kinds of user error. Becuase they work with mutable references, and these are not allowed to cross the constrained/unconstrained barrier, tests that use `TestEnvironment` must also become `unconstrained`. The recommended practice is to make _all_ Noir tests and test helper functions be `unconstrained:
Expand Down
2 changes: 1 addition & 1 deletion noir-projects/aztec-nr/address-note/src/address_note.nr
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ impl NullifiableNote for AddressNote {
)
}

fn compute_nullifier_without_context(self) -> Field {
unconstrained fn compute_nullifier_without_context(self) -> Field {
let note_hash_for_nullify = compute_note_hash_for_nullify(self);
let secret = get_nsk_app(self.npk_m_hash);
poseidon2_hash_with_separator(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ mod test {
1
}

fn compute_nullifier_without_context(_self: Self) -> Field {
unconstrained fn compute_nullifier_without_context(_self: Self) -> Field {
1
}
}
Expand Down
3 changes: 1 addition & 2 deletions noir-projects/aztec-nr/aztec/src/note/note_interface.nr
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ pub trait NullifiableNote {

// Unlike compute_nullifier, this function does not take a note hash since it'll only be invoked in unconstrained
// contexts, where there is no gate count.
fn compute_nullifier_without_context(self) -> Field;
unconstrained fn compute_nullifier_without_context(self) -> Field;
}

// docs:start:note_interface
Expand Down Expand Up @@ -48,4 +48,3 @@ pub trait NoteInterface<let N: u32> {
fn compute_note_hash(self) -> Field;
}
// docs:end:note_interface

2 changes: 1 addition & 1 deletion noir-projects/aztec-nr/aztec/src/test/mocks/mock_note.nr
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ impl NullifiableNote for MockNote {
)
}

fn compute_nullifier_without_context(self) -> Field {
unconstrained fn compute_nullifier_without_context(self) -> Field {
// We don't use any kind of secret here since this is only a mock note and having it here would make tests
// more cumbersome
let note_hash_for_nullify = compute_note_hash_for_nullify(self);
Expand Down
2 changes: 1 addition & 1 deletion noir-projects/aztec-nr/uint-note/src/uint_note.nr
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ impl NullifiableNote for UintNote {
)
}

fn compute_nullifier_without_context(self) -> Field {
unconstrained fn compute_nullifier_without_context(self) -> Field {
let note_hash_for_nullify = compute_note_hash_for_nullify(self);
let secret = get_nsk_app(self.npk_m_hash);
poseidon2_hash_with_separator(
Expand Down
2 changes: 1 addition & 1 deletion noir-projects/aztec-nr/value-note/src/value_note.nr
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ impl NullifiableNote for ValueNote {

// docs:end:nullifier

fn compute_nullifier_without_context(self) -> Field {
unconstrained fn compute_nullifier_without_context(self) -> Field {
let note_hash_for_nullify = compute_note_hash_for_nullify(self);
let secret = get_nsk_app(self.npk_m_hash);
poseidon2_hash_with_separator(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ impl NullifiableNote for SubscriptionNote {
)
}

fn compute_nullifier_without_context(self) -> Field {
unconstrained fn compute_nullifier_without_context(self) -> Field {
let note_hash_for_nullify = compute_note_hash_for_nullify(self);
let secret = get_nsk_app(self.npk_m_hash);
poseidon2_hash_with_separator(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ impl NullifiableNote for CardNote {
)
}

fn compute_nullifier_without_context(self) -> Field {
unconstrained fn compute_nullifier_without_context(self) -> Field {
let note_hash_for_nullify = compute_note_hash_for_nullify(self);
let secret = get_nsk_app(self.npk_m_hash);
poseidon2_hash_with_separator(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ impl NullifiableNote for EcdsaPublicKeyNote {
)
}

fn compute_nullifier_without_context(self) -> Field {
unconstrained fn compute_nullifier_without_context(self) -> Field {
let note_hash_for_nullify = compute_note_hash_for_nullify(self);
let secret = get_nsk_app(self.npk_m_hash);
poseidon2_hash_with_separator(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ impl NullifiableNote for NFTNote {
)
}

fn compute_nullifier_without_context(self) -> Field {
unconstrained fn compute_nullifier_without_context(self) -> Field {
let note_hash_for_nullify = compute_note_hash_for_nullify(self);
let secret = get_nsk_app(self.npk_m_hash);
poseidon2_hash_with_separator(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ impl NullifiableNote for PublicKeyNote {
)
}

fn compute_nullifier_without_context(self) -> Field {
unconstrained fn compute_nullifier_without_context(self) -> Field {
let note_hash_for_nullify = compute_note_hash_for_nullify(self);
let secret = get_nsk_app(self.npk_m_hash);
poseidon2_hash_with_separator(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ impl NullifiableNote for TokenNote {
}
// docs:end:nullifier

fn compute_nullifier_without_context(self) -> Field {
unconstrained fn compute_nullifier_without_context(self) -> Field {
let note_hash_for_nullify = compute_note_hash_for_nullify(self);
let secret = get_nsk_app(self.npk_m_hash);
poseidon2_hash_with_separator(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ impl NullifiableNote for TestNote {
0
}

fn compute_nullifier_without_context(_self: Self) -> Field {
unconstrained fn compute_nullifier_without_context(_self: Self) -> Field {
// This note is expected to be shared between users and for this reason can't be nullified using a secret.
0
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ impl NullifiableNote for TokenNote {
}
// docs:end:nullifier

fn compute_nullifier_without_context(self) -> Field {
unconstrained fn compute_nullifier_without_context(self) -> Field {
let note_hash_for_nullify = compute_note_hash_for_nullify(self);
let secret = get_nsk_app(self.npk_m_hash);
poseidon2_hash_with_separator(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ use dep::aztec::{
macros::notes::note
};

use dep::std::mem::zeroed;

// Transparent note represents a note that is created in the clear (public execution), but can only be spent by those
// that know the preimage of the "secret_hash" (the secret). This is typically used when shielding a token balance.
// Owner of the tokens provides a "secret_hash" as an argument to the public "shield" function and then the tokens
Expand All @@ -17,11 +19,6 @@ pub struct TransparentNote {
}

impl NullifiableNote for TransparentNote {

fn compute_nullifier(self, _context: &mut PrivateContext, _note_hash_for_nullify: Field) -> Field {
self.compute_nullifier_without_context()
}

// Computing a nullifier in a transparent note is not guarded by making secret a part of the nullifier preimage (as
// is common in other cases) and instead is guarded by the functionality of "redeem_shield" function. There we do
// the following:
Expand All @@ -30,13 +27,18 @@ impl NullifiableNote for TransparentNote {
// 3) the "get_notes" oracle constrains that the secret hash in the returned note matches the one computed in
// circuit.
// This achieves that the note can only be spent by the party that knows the secret.
fn compute_nullifier_without_context(self) -> Field {
fn compute_nullifier(self, _context: &mut PrivateContext, _note_hash_for_nullify: Field) -> Field {
let note_hash_for_nullify = compute_note_hash_for_nullify(self);
poseidon2_hash_with_separator(
[note_hash_for_nullify],
GENERATOR_INDEX__NOTE_NULLIFIER as Field
)
}

unconstrained fn compute_nullifier_without_context(self) -> Field {
// compute_nullifier ignores both of its parameters so we can reuse it here
self.compute_nullifier(zeroed(), zeroed())
}
}

impl TransparentNote {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ impl NullifiableNote for TokenNote {
}
// docs:end:nullifier

fn compute_nullifier_without_context(self) -> Field {
unconstrained fn compute_nullifier_without_context(self) -> Field {
let note_hash_for_nullify = compute_note_hash_for_nullify(self);
let secret = get_nsk_app(self.npk_m_hash);
poseidon2_hash_with_separator(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ use dep::aztec::{
macros::notes::note
};

use dep::std::mem::zeroed;

// Transparent note represents a note that is created in the clear (public execution), but can only be spent by those
// that know the preimage of the "secret_hash" (the secret). This is typically used when shielding a token balance.
// Owner of the tokens provides a "secret_hash" as an argument to the public "shield" function and then the tokens
Expand All @@ -16,10 +18,6 @@ pub struct TransparentNote {
}

impl NullifiableNote for TransparentNote {
fn compute_nullifier(self, _context: &mut PrivateContext, _note_hash_for_nullify: Field) -> Field {
self.compute_nullifier_without_context()
}

// Computing a nullifier in a transparent note is not guarded by making secret a part of the nullifier preimage (as
// is common in other cases) and instead is guarded by the functionality of "redeem_shield" function. There we do
// the following:
Expand All @@ -28,13 +26,18 @@ impl NullifiableNote for TransparentNote {
// 3) the "get_notes" oracle constrains that the secret hash in the returned note matches the one computed in
// circuit.
// This achieves that the note can only be spent by the party that knows the secret.
fn compute_nullifier_without_context(self) -> Field {
fn compute_nullifier(self, _context: &mut PrivateContext, _note_hash_for_nullify: Field) -> Field {
let note_hash_for_nullify = compute_note_hash_for_nullify(self);
poseidon2_hash_with_separator(
[note_hash_for_nullify],
GENERATOR_INDEX__NOTE_NULLIFIER as Field
)
}

unconstrained fn compute_nullifier_without_context(self) -> Field {
// compute_nullifier ignores both of its parameters so we can reuse it here
self.compute_nullifier(zeroed(), zeroed())
}
}

impl TransparentNote {
Expand All @@ -50,4 +53,4 @@ impl Eq for TransparentNote {
}
}

// docs:end:token_types_all
// docs:end:token_types_all

0 comments on commit 1420859

Please sign in to comment.