Skip to content

Commit 1ded83a

Browse files
nventuroTomAFrench
authored andcommitted
feat!: make compute_nullifier_without_context unconstrained (#8742)
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 <15848336+TomAFrench@users.noreply.github.com>
1 parent 5a37344 commit 1ded83a

File tree

18 files changed

+42
-28
lines changed

18 files changed

+42
-28
lines changed

docs/docs/migration_notes.md

+10
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,16 @@ Aztec is in full-speed development. Literally every version breaks compatibility
88

99
## TBD
1010

11+
### [Aztec.nr] Changes to `NullifiableNote`
12+
13+
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:
14+
15+
```diff
16+
impl NullifiableNote for MyCustomNote {
17+
- fn compute_nullifier_without_context(self) -> Field {
18+
+ unconstrained fn compute_nullifier_without_context(self) -> Field {
19+
```
20+
1121
### [Aztec.nr] Make `TestEnvironment` unconstrained
1222

1323
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:

noir-projects/aztec-nr/address-note/src/address_note.nr

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ impl NullifiableNote for AddressNote {
3030
)
3131
}
3232

33-
fn compute_nullifier_without_context(self) -> Field {
33+
unconstrained fn compute_nullifier_without_context(self) -> Field {
3434
let note_hash_for_nullify = compute_note_hash_for_nullify(self);
3535
let secret = get_nsk_app(self.npk_m_hash);
3636
poseidon2_hash_with_separator(

noir-projects/aztec-nr/aztec/src/encrypted_logs/incoming_body.nr

+1-1
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ mod test {
6363
1
6464
}
6565

66-
fn compute_nullifier_without_context(_self: Self) -> Field {
66+
unconstrained fn compute_nullifier_without_context(_self: Self) -> Field {
6767
1
6868
}
6969
}

noir-projects/aztec-nr/aztec/src/note/note_interface.nr

+1-2
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ pub trait NullifiableNote {
1919

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

2525
// docs:start:note_interface
@@ -48,4 +48,3 @@ pub trait NoteInterface<let N: u32> {
4848
fn compute_note_hash(self) -> Field;
4949
}
5050
// docs:end:note_interface
51-

noir-projects/aztec-nr/aztec/src/test/mocks/mock_note.nr

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ impl NullifiableNote for MockNote {
2424
)
2525
}
2626

27-
fn compute_nullifier_without_context(self) -> Field {
27+
unconstrained fn compute_nullifier_without_context(self) -> Field {
2828
// We don't use any kind of secret here since this is only a mock note and having it here would make tests
2929
// more cumbersome
3030
let note_hash_for_nullify = compute_note_hash_for_nullify(self);

noir-projects/aztec-nr/uint-note/src/uint_note.nr

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ impl NullifiableNote for UintNote {
2626
)
2727
}
2828

29-
fn compute_nullifier_without_context(self) -> Field {
29+
unconstrained fn compute_nullifier_without_context(self) -> Field {
3030
let note_hash_for_nullify = compute_note_hash_for_nullify(self);
3131
let secret = get_nsk_app(self.npk_m_hash);
3232
poseidon2_hash_with_separator(

noir-projects/aztec-nr/value-note/src/value_note.nr

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ impl NullifiableNote for ValueNote {
3636

3737
// docs:end:nullifier
3838

39-
fn compute_nullifier_without_context(self) -> Field {
39+
unconstrained fn compute_nullifier_without_context(self) -> Field {
4040
let note_hash_for_nullify = compute_note_hash_for_nullify(self);
4141
let secret = get_nsk_app(self.npk_m_hash);
4242
poseidon2_hash_with_separator(

noir-projects/noir-contracts/contracts/app_subscription_contract/src/subscription_note.nr

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ impl NullifiableNote for SubscriptionNote {
2727
)
2828
}
2929

30-
fn compute_nullifier_without_context(self) -> Field {
30+
unconstrained fn compute_nullifier_without_context(self) -> Field {
3131
let note_hash_for_nullify = compute_note_hash_for_nullify(self);
3232
let secret = get_nsk_app(self.npk_m_hash);
3333
poseidon2_hash_with_separator(

noir-projects/noir-contracts/contracts/docs_example_contract/src/types/card_note.nr

+1-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ impl NullifiableNote for CardNote {
3838
)
3939
}
4040

41-
fn compute_nullifier_without_context(self) -> Field {
41+
unconstrained fn compute_nullifier_without_context(self) -> Field {
4242
let note_hash_for_nullify = compute_note_hash_for_nullify(self);
4343
let secret = get_nsk_app(self.npk_m_hash);
4444
poseidon2_hash_with_separator(

noir-projects/noir-contracts/contracts/ecdsa_public_key_note/src/lib.nr

+1-1
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ impl NullifiableNote for EcdsaPublicKeyNote {
136136
)
137137
}
138138

139-
fn compute_nullifier_without_context(self) -> Field {
139+
unconstrained fn compute_nullifier_without_context(self) -> Field {
140140
let note_hash_for_nullify = compute_note_hash_for_nullify(self);
141141
let secret = get_nsk_app(self.npk_m_hash);
142142
poseidon2_hash_with_separator(

noir-projects/noir-contracts/contracts/nft_contract/src/types/nft_note.nr

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ impl NullifiableNote for NFTNote {
2727
)
2828
}
2929

30-
fn compute_nullifier_without_context(self) -> Field {
30+
unconstrained fn compute_nullifier_without_context(self) -> Field {
3131
let note_hash_for_nullify = compute_note_hash_for_nullify(self);
3232
let secret = get_nsk_app(self.npk_m_hash);
3333
poseidon2_hash_with_separator(

noir-projects/noir-contracts/contracts/schnorr_account_contract/src/public_key_note.nr

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ impl NullifiableNote for PublicKeyNote {
2727
)
2828
}
2929

30-
fn compute_nullifier_without_context(self) -> Field {
30+
unconstrained fn compute_nullifier_without_context(self) -> Field {
3131
let note_hash_for_nullify = compute_note_hash_for_nullify(self);
3232
let secret = get_nsk_app(self.npk_m_hash);
3333
poseidon2_hash_with_separator(

noir-projects/noir-contracts/contracts/spam_contract/src/types/token_note.nr

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ impl NullifiableNote for TokenNote {
3535
}
3636
// docs:end:nullifier
3737

38-
fn compute_nullifier_without_context(self) -> Field {
38+
unconstrained fn compute_nullifier_without_context(self) -> Field {
3939
let note_hash_for_nullify = compute_note_hash_for_nullify(self);
4040
let secret = get_nsk_app(self.npk_m_hash);
4141
poseidon2_hash_with_separator(

noir-projects/noir-contracts/contracts/test_contract/src/test_note.nr

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ impl NullifiableNote for TestNote {
1919
0
2020
}
2121

22-
fn compute_nullifier_without_context(_self: Self) -> Field {
22+
unconstrained fn compute_nullifier_without_context(_self: Self) -> Field {
2323
// This note is expected to be shared between users and for this reason can't be nullified using a secret.
2424
0
2525
}

noir-projects/noir-contracts/contracts/token_blacklist_contract/src/types/token_note.nr

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ impl NullifiableNote for TokenNote {
3434
}
3535
// docs:end:nullifier
3636

37-
fn compute_nullifier_without_context(self) -> Field {
37+
unconstrained fn compute_nullifier_without_context(self) -> Field {
3838
let note_hash_for_nullify = compute_note_hash_for_nullify(self);
3939
let secret = get_nsk_app(self.npk_m_hash);
4040
poseidon2_hash_with_separator(

noir-projects/noir-contracts/contracts/token_blacklist_contract/src/types/transparent_note.nr

+8-6
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ use dep::aztec::{
66
macros::notes::note
77
};
88

9+
use dep::std::mem::zeroed;
10+
911
// Transparent note represents a note that is created in the clear (public execution), but can only be spent by those
1012
// that know the preimage of the "secret_hash" (the secret). This is typically used when shielding a token balance.
1113
// Owner of the tokens provides a "secret_hash" as an argument to the public "shield" function and then the tokens
@@ -17,11 +19,6 @@ pub struct TransparentNote {
1719
}
1820

1921
impl NullifiableNote for TransparentNote {
20-
21-
fn compute_nullifier(self, _context: &mut PrivateContext, _note_hash_for_nullify: Field) -> Field {
22-
self.compute_nullifier_without_context()
23-
}
24-
2522
// Computing a nullifier in a transparent note is not guarded by making secret a part of the nullifier preimage (as
2623
// is common in other cases) and instead is guarded by the functionality of "redeem_shield" function. There we do
2724
// the following:
@@ -30,13 +27,18 @@ impl NullifiableNote for TransparentNote {
3027
// 3) the "get_notes" oracle constrains that the secret hash in the returned note matches the one computed in
3128
// circuit.
3229
// This achieves that the note can only be spent by the party that knows the secret.
33-
fn compute_nullifier_without_context(self) -> Field {
30+
fn compute_nullifier(self, _context: &mut PrivateContext, _note_hash_for_nullify: Field) -> Field {
3431
let note_hash_for_nullify = compute_note_hash_for_nullify(self);
3532
poseidon2_hash_with_separator(
3633
[note_hash_for_nullify],
3734
GENERATOR_INDEX__NOTE_NULLIFIER as Field
3835
)
3936
}
37+
38+
unconstrained fn compute_nullifier_without_context(self) -> Field {
39+
// compute_nullifier ignores both of its parameters so we can reuse it here
40+
self.compute_nullifier(zeroed(), zeroed())
41+
}
4042
}
4143

4244
impl TransparentNote {

noir-projects/noir-contracts/contracts/token_contract/src/types/token_note.nr

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ impl NullifiableNote for TokenNote {
3636
}
3737
// docs:end:nullifier
3838

39-
fn compute_nullifier_without_context(self) -> Field {
39+
unconstrained fn compute_nullifier_without_context(self) -> Field {
4040
let note_hash_for_nullify = compute_note_hash_for_nullify(self);
4141
let secret = get_nsk_app(self.npk_m_hash);
4242
poseidon2_hash_with_separator(

noir-projects/noir-contracts/contracts/token_contract/src/types/transparent_note.nr

+9-6
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ use dep::aztec::{
55
macros::notes::note
66
};
77

8+
use dep::std::mem::zeroed;
9+
810
// Transparent note represents a note that is created in the clear (public execution), but can only be spent by those
911
// that know the preimage of the "secret_hash" (the secret). This is typically used when shielding a token balance.
1012
// Owner of the tokens provides a "secret_hash" as an argument to the public "shield" function and then the tokens
@@ -16,10 +18,6 @@ pub struct TransparentNote {
1618
}
1719

1820
impl NullifiableNote for TransparentNote {
19-
fn compute_nullifier(self, _context: &mut PrivateContext, _note_hash_for_nullify: Field) -> Field {
20-
self.compute_nullifier_without_context()
21-
}
22-
2321
// Computing a nullifier in a transparent note is not guarded by making secret a part of the nullifier preimage (as
2422
// is common in other cases) and instead is guarded by the functionality of "redeem_shield" function. There we do
2523
// the following:
@@ -28,13 +26,18 @@ impl NullifiableNote for TransparentNote {
2826
// 3) the "get_notes" oracle constrains that the secret hash in the returned note matches the one computed in
2927
// circuit.
3028
// This achieves that the note can only be spent by the party that knows the secret.
31-
fn compute_nullifier_without_context(self) -> Field {
29+
fn compute_nullifier(self, _context: &mut PrivateContext, _note_hash_for_nullify: Field) -> Field {
3230
let note_hash_for_nullify = compute_note_hash_for_nullify(self);
3331
poseidon2_hash_with_separator(
3432
[note_hash_for_nullify],
3533
GENERATOR_INDEX__NOTE_NULLIFIER as Field
3634
)
3735
}
36+
37+
unconstrained fn compute_nullifier_without_context(self) -> Field {
38+
// compute_nullifier ignores both of its parameters so we can reuse it here
39+
self.compute_nullifier(zeroed(), zeroed())
40+
}
3841
}
3942

4043
impl TransparentNote {
@@ -50,4 +53,4 @@ impl Eq for TransparentNote {
5053
}
5154
}
5255

53-
// docs:end:token_types_all
56+
// docs:end:token_types_all

0 commit comments

Comments
 (0)