Skip to content

Commit 153d036

Browse files
authored
feat: merge-train/fairies-v5 (#24609)
BEGIN_COMMIT_OVERRIDE fix: tweak depositToAztec gas config (#24607) feat!: make inbox secrets be multiple fields (#24599) feat(e2e): interactive handshake e2e (#24590) END_COMMIT_OVERRIDE
2 parents eb19401 + 457f46d commit 153d036

29 files changed

Lines changed: 562 additions & 118 deletions

File tree

docs/docs-developers/docs/resources/migration_notes.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,39 @@ Aztec is in active development. Each version may introduce breaking changes that
99

1010
## TBD
1111

12+
### [Aztec.nr] L1-to-L2 message consumption takes the secret as an array
13+
14+
`PrivateContext::consume_l1_to_l2_message` and `PublicContext::consume_l1_to_l2_message` now take the message secret as an arbitrary-length array `[Field; N]` instead of a single `Field`, so a consumer can derive its secret hash from more than one field. The helpers `compute_secret_hash` and `compute_l1_to_l2_message_nullifier` are likewise now generic over the secret length. A single-field secret behaves exactly as before (the hashes are unchanged for `N = 1`) — just wrap it in an array.
15+
16+
**Migration:**
17+
18+
```diff
19+
- context.consume_l1_to_l2_message(content, secret, sender, leaf_index);
20+
+ context.consume_l1_to_l2_message(content, [secret], sender, leaf_index);
21+
```
22+
23+
```diff
24+
- let secret_hash = compute_secret_hash(secret);
25+
+ let secret_hash = compute_secret_hash([secret]);
26+
```
27+
28+
**Impact**: Contracts that consume L1-to-L2 messages, or that call `compute_secret_hash` / `compute_l1_to_l2_message_nullifier`, must wrap their single-field secret in an array. Already-deployed contracts are unaffected: their unchanged bytecode keeps working, as PXE serves the previous L1-to-L2 membership-witness oracle through a compatibility adaptor.
29+
30+
### [Aztec.js] `computeL1ToL2MessageNullifier` replaced by `computeFeeJuiceMessageNullifier`
31+
32+
The `@aztec/stdlib` helper `computeL1ToL2MessageNullifier(contract, messageHash, secret)`, which returned the siloed message nullifier, has been removed. Its replacement `computeFeeJuiceMessageNullifier(messageHash, secret)` returns the **unsiloed** nullifier — siloing now happens at the point where the nullifier is looked up. `getL1ToL2MessageWitness` accordingly takes an optional `{ contractAddress, nullifier }` (unsiloed) and silos internally. `computeSecretHash` is unchanged, and `getNonNullifiedL1ToL2MessageWitness` keeps the same signature.
33+
34+
**Migration:**
35+
36+
```diff
37+
- const nullifier = await computeL1ToL2MessageNullifier(contract, messageHash, secret);
38+
+ // computeFeeJuiceMessageNullifier returns the UNSILOED nullifier; silo it before looking it up, or hand the
39+
+ // unsiloed value to getL1ToL2MessageWitness which silos for you.
40+
+ const nullifier = await siloNullifier(contract, await computeFeeJuiceMessageNullifier(messageHash, secret));
41+
```
42+
43+
**Impact**: Only affects code calling these low-level messaging helpers directly - most integrations use `getNonNullifiedL1ToL2MessageWitness`, which is unchanged.
44+
1245
### [Aztec.js] Account signing keys are no longer derived from the privacy secret
1346

1447
Schnorr account signing keys used to be derived from the account's privacy secret (via the now-removed `deriveSigningKey`), which meant the ownership key could be reconstructed from a value the PXE holds. The relationship is now reversed: the signing key is the root, and the privacy secret is derived from it with `deriveSecretKeyFromSigningKey` (exported from `@aztec/accounts/utils`).

docs/examples/contracts/aave_bridge/src/main.nr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ pub contract AaveBridge {
5050
// Consume message and emit nullifier
5151
self.context.consume_l1_to_l2_message(
5252
content_hash,
53-
secret,
53+
[secret],
5454
config.portal,
5555
message_leaf_index,
5656
);
@@ -76,7 +76,7 @@ pub contract AaveBridge {
7676
let content_hash = get_mint_to_private_content_hash(amount);
7777
self.context.consume_l1_to_l2_message(
7878
content_hash,
79-
secret_for_L1_to_L2_message_consumption,
79+
[secret_for_L1_to_L2_message_consumption],
8080
config.portal,
8181
message_leaf_index,
8282
);

docs/examples/contracts/nft_bridge/src/main.nr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ pub contract NFTBridge {
3838
// Consume the L1 -> L2 message
3939
self.context.consume_l1_to_l2_message(
4040
content_hash,
41-
secret,
41+
[secret],
4242
self.storage.portal.read(),
4343
message_leaf_index,
4444
);

noir-projects/aztec-nr/aztec/src/context/private_context.nr

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -868,14 +868,20 @@ impl PrivateContext {
868868
///
869869
/// # Arguments
870870
/// * `content` - The message content that was sent from L1
871-
/// * `secret` - Secret value used for message privacy (if needed)
871+
/// * `secret` - Secret fields used for message privacy (if needed)
872872
/// * `sender` - Ethereum address that sent the message
873873
/// * `leaf_index` - Index of the message in the L1-to-L2 message tree
874874
///
875875
/// # Advanced
876876
/// Validates message existence in the L1-to-L2 message tree and nullifies the message to prevent
877877
/// double-consumption.
878-
pub fn consume_l1_to_l2_message(&mut self, content: Field, secret: Field, sender: EthAddress, leaf_index: Field) {
878+
pub fn consume_l1_to_l2_message<let N: u32>(
879+
&mut self,
880+
content: Field,
881+
secret: [Field; N],
882+
sender: EthAddress,
883+
leaf_index: Field,
884+
) {
879885
let nullifier = process_l1_to_l2_message(
880886
self.anchor_block_header.state.l1_to_l2_message_tree.root,
881887
self.this_address(),

noir-projects/aztec-nr/aztec/src/context/public_context.nr

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ impl PublicContext {
226226
///
227227
/// # Arguments
228228
/// * `content` - The message content that was sent from L1
229-
/// * `secret` - Secret value used for message privacy (if needed)
229+
/// * `secret` - Secret fields used for message privacy (if needed)
230230
/// * `sender` - Ethereum address that sent the message
231231
/// * `leaf_index` - Index of the message in the L1-to-L2 message tree
232232
///
@@ -236,7 +236,13 @@ impl PublicContext {
236236
/// * Message hash is computed from all parameters + chain context
237237
/// * Will revert if message doesn't exist or was already consumed
238238
///
239-
pub fn consume_l1_to_l2_message(self: Self, content: Field, secret: Field, sender: EthAddress, leaf_index: Field) {
239+
pub fn consume_l1_to_l2_message<let N: u32>(
240+
self: Self,
241+
content: Field,
242+
secret: [Field; N],
243+
sender: EthAddress,
244+
leaf_index: Field,
245+
) {
240246
let secret_hash = compute_secret_hash(secret);
241247
let message_hash = compute_l1_to_l2_message_hash(
242248
sender,

noir-projects/aztec-nr/aztec/src/hash.nr

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ use crate::protocol::{
1212

1313
pub use crate::protocol::hash::compute_siloed_nullifier;
1414

15-
pub fn compute_secret_hash(secret: Field) -> Field {
16-
poseidon2_hash_with_separator([secret], DOM_SEP__SECRET_HASH)
15+
pub fn compute_secret_hash<let N: u32>(secret: [Field; N]) -> Field {
16+
poseidon2_hash_with_separator(secret, DOM_SEP__SECRET_HASH)
1717
}
1818

1919
pub fn compute_l1_to_l2_message_hash(
@@ -47,9 +47,9 @@ pub fn compute_l1_to_l2_message_hash(
4747
sha256_to_field(hash_bytes)
4848
}
4949

50-
// The nullifier of a l1 to l2 message is the hash of the message salted with the secret
51-
pub fn compute_l1_to_l2_message_nullifier(message_hash: Field, secret: Field) -> Field {
52-
poseidon2_hash_with_separator([message_hash, secret], DOM_SEP__MESSAGE_NULLIFIER)
50+
// The nullifier of an l1 to l2 message is the hash of the message salted with the secret.
51+
pub fn compute_l1_to_l2_message_nullifier<let N: u32>(message_hash: Field, secret: [Field; N]) -> Field {
52+
poseidon2_hash_with_separator([message_hash].concat(secret), DOM_SEP__MESSAGE_NULLIFIER)
5353
}
5454

5555
// Computes the hash of input arguments or return values for private functions, or for authwit creation.
@@ -96,7 +96,7 @@ pub fn compute_public_bytecode_commitment(
9696
#[test]
9797
unconstrained fn secret_hash_matches_typescript() {
9898
let secret = 8;
99-
let hash = compute_secret_hash(secret);
99+
let hash = compute_secret_hash([secret]);
100100

101101
// The following value was generated by `yarn-project/stdlib/src/hash/hash.test.ts`
102102
let secret_hash_from_ts = 0x1848b066724ab0ffb50ecb0ee3398eb839f162823d262bad959721a9c13d1e96;
Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
use crate::{
22
hash::{compute_l1_to_l2_message_hash, compute_l1_to_l2_message_nullifier, compute_secret_hash},
3-
oracle::get_l1_to_l2_membership_witness::get_l1_to_l2_membership_witness,
3+
oracle::get_l1_to_l2_membership_witness::{get_l1_to_l2_membership_witness, UnsiloedNullifier},
44
};
55

66
use crate::protocol::{address::{AztecAddress, EthAddress}, merkle_tree::root::root_from_sibling_path};
77

8-
pub fn process_l1_to_l2_message(
8+
pub fn process_l1_to_l2_message<let N: u32>(
99
l1_to_l2_root: Field,
1010
contract_address: AztecAddress,
1111
portal_contract_address: EthAddress,
1212
chain_id: Field,
1313
version: Field,
1414
content: Field,
15-
secret: Field,
15+
secret: [Field; N],
1616
leaf_index: Field,
1717
) -> Field {
1818
let secret_hash = compute_secret_hash(secret);
@@ -26,14 +26,20 @@ pub fn process_l1_to_l2_message(
2626
leaf_index,
2727
);
2828

29+
let nullifier = compute_l1_to_l2_message_nullifier(message_hash, secret);
30+
2931
// We prove that `message_hash` is in the tree by showing the derivation of the tree root, using a merkle path we
3032
// get from an oracle.
3133
// Safety: The witness is only used as a "magical value" that makes the merkle proof below pass. Hence it's safe.
32-
let (_leaf_index, sibling_path) =
33-
unsafe { get_l1_to_l2_membership_witness(contract_address, message_hash, secret) };
34+
let (_leaf_index, sibling_path) = unsafe {
35+
get_l1_to_l2_membership_witness(
36+
message_hash,
37+
Option::some(UnsiloedNullifier { contract_address, nullifier }),
38+
)
39+
};
3440

3541
let root = root_from_sibling_path(message_hash, leaf_index, sibling_path);
3642
assert_eq(root, l1_to_l2_root, "Message not in state");
3743

38-
compute_l1_to_l2_message_nullifier(message_hash, secret)
44+
nullifier
3945
}
Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,27 @@
11
use crate::protocol::{address::AztecAddress, constants::L1_TO_L2_MSG_TREE_HEIGHT};
22

3+
/// An unsiloed L1 to L2 message nullifier.
4+
pub struct UnsiloedNullifier {
5+
pub contract_address: AztecAddress,
6+
pub nullifier: Field,
7+
}
8+
39
/// Returns the leaf index and sibling path of an entry in the L1 to L2 messaging tree, which can then be used to prove
410
/// its existence.
11+
///
12+
/// When `nullifier` is `Some`, the returned witness is guaranteed to be for a message that has not yet been consumed:
13+
/// PXE checks it is not present in the nullifier tree. Pass `None` to retrieve the witness regardless of whether the
14+
/// message has been consumed.
515
pub unconstrained fn get_l1_to_l2_membership_witness(
6-
contract_address: AztecAddress,
716
message_hash: Field,
8-
secret: Field,
17+
nullifier: Option<UnsiloedNullifier>,
918
) -> (Field, [Field; L1_TO_L2_MSG_TREE_HEIGHT]) {
10-
get_l1_to_l2_membership_witness_oracle(contract_address, message_hash, secret)
19+
get_l1_to_l2_membership_witness_oracle(message_hash, nullifier)
1120
}
1221

1322
// Obtains membership witness (index and sibling path) for a message in the L1 to L2 message tree.
14-
#[oracle(aztec_utl_getL1ToL2MembershipWitness)]
23+
#[oracle(aztec_utl_getL1ToL2MembershipWitnessV2)]
1524
unconstrained fn get_l1_to_l2_membership_witness_oracle(
16-
_contract_address: AztecAddress,
1725
_message_hash: Field,
18-
_secret: Field,
26+
_nullifier: Option<UnsiloedNullifier>,
1927
) -> (Field, [Field; L1_TO_L2_MSG_TREE_HEIGHT]) {}

noir-projects/aztec-nr/aztec/src/oracle/version.nr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
/// immediately if AZTEC_NR_MINOR > PXE_MINOR because if a contract is updated to use a newer Aztec.nr dependency
1212
/// without actually using any of the new oracles then there is no reason to throw.
1313
pub global ORACLE_VERSION_MAJOR: Field = 30;
14-
pub global ORACLE_VERSION_MINOR: Field = 5;
14+
pub global ORACLE_VERSION_MINOR: Field = 6;
1515

1616
/// Asserts that the version of the oracle is compatible with the version expected by the contract.
1717
pub fn assert_compatible_oracle_version() {

noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -887,7 +887,7 @@ impl TestEnvironment {
887887
sender: EthAddress,
888888
recipient: AztecAddress,
889889
) -> Field {
890-
self.send_l1_to_l2_message_from_secret_hash(content, compute_secret_hash(secret), sender, recipient)
890+
self.send_l1_to_l2_message_from_secret_hash(content, compute_secret_hash([secret]), sender, recipient)
891891
}
892892

893893
/// Variant of [`send_l1_to_l2_message`](TestEnvironment::send_l1_to_l2_message) that takes the secret hash

0 commit comments

Comments
 (0)