Skip to content

Commit a45d68a

Browse files
committed
verify that the index is reserved when sending multiple msgs within the same tx
1 parent 0afe779 commit a45d68a

3 files changed

Lines changed: 47 additions & 1 deletion

File tree

noir-projects/aztec-nr/aztec/src/messages/delivery/constrained_delivery.nr

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,13 @@ pub fn calculate_secret_and_index(
7070
let secret: Field = context
7171
.call_private_function(registry, selector, [sender.to_field(), recipient.to_field()])
7272
.get_preimage();
73+
74+
// Reserve index 0 for the freshly bootstrapped secret so the PXE seeds its per-secret counter, mirroring
75+
// the `Some` branch. Without this, a later constrained message under the same secret restarts at index 0
76+
// and collides on `(secret, 0)`.
77+
// Safety: this only advances a PXE-side counter; the returned index is constrained to 0 below.
78+
let index = unsafe { get_next_constrained_index(secret) };
79+
assert(index == 0, "freshly bootstrapped secret must start at index 0");
7380
(secret, 0)
7481
} else {
7582
let secret = maybe_secret.unwrap_unchecked();

noir-projects/noir-contracts/contracts/test/constrained_delivery_test_contract/src/main.nr

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ mod test;
66
#[aztec]
77
pub contract ConstrainedDeliveryTest {
88
use aztec::{
9-
macros::functions::external, messages::delivery::constrained_delivery::calculate_secret_and_index,
9+
macros::functions::external,
10+
messages::delivery::constrained_delivery::calculate_secret_and_index,
11+
oracle::notes::get_next_constrained_index,
1012
protocol::address::AztecAddress,
1113
};
1214

@@ -15,4 +17,21 @@ pub contract ConstrainedDeliveryTest {
1517
fn calculate_and_return(registry: AztecAddress, sender: AztecAddress, recipient: AztecAddress) -> (Field, u32) {
1618
calculate_secret_and_index(self.context, registry, sender, recipient)
1719
}
20+
21+
/// Resolves a secret via the helper, then asks the PXE for the next index of that same secret.
22+
///
23+
/// Models a sender emitting a second constrained message under a freshly resolved handshake within one tx.
24+
/// The returned `(secret, first_index, second_index)` lets a test assert that the second index advances past
25+
/// the first rather than resetting, which would collide on `(secret, first_index)`.
26+
#[external("private")]
27+
fn resolve_then_next_index(
28+
registry: AztecAddress,
29+
sender: AztecAddress,
30+
recipient: AztecAddress,
31+
) -> (Field, u32, u32) {
32+
let (secret, first_index) = calculate_secret_and_index(self.context, registry, sender, recipient);
33+
// Safety: test-only observation of the index the PXE hands out next for this secret.
34+
let second_index = unsafe { get_next_constrained_index(secret) };
35+
(secret, first_index, second_index)
36+
}
1837
}

noir-projects/noir-contracts/contracts/test/constrained_delivery_test_contract/src/test.nr

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,26 @@ unconstrained fn handshake_returns_fresh_secret_at_index_zero() {
6767
assert_eq(second_index, 0);
6868
}
6969

70+
// Bootstrap must seed the per-secret index counter, so a second message emitted under the freshly
71+
// bootstrapped handshake within the same tx advances to index 1 instead of resetting to 0 (which would
72+
// collide on `(secret, 0)`). This mirrors the `Some(secret)` branch, which seeds the counter via the same
73+
// oracle call.
74+
#[test]
75+
unconstrained fn bootstrap_seeds_index_counter_for_same_tx_reuse() {
76+
let (env, registry_address, test_address, sender, recipient) = setup();
77+
let test_contract = ConstrainedDeliveryTest::at(test_address);
78+
79+
let (secret, first_index, second_index) = env.call_private_opts(
80+
sender,
81+
helper_options(registry_address),
82+
test_contract.resolve_then_next_index(registry_address, sender, recipient),
83+
);
84+
85+
assert(secret != 0, "bootstrap should return a non-zero secret");
86+
assert_eq(first_index, 0);
87+
assert_eq(second_index, 1);
88+
}
89+
7090
// Existing-handshake path: when a handshake already exists, the helper takes the `Some(secret)` branch and
7191
// (at index 0) calls `validate_handshake`, which completes here because the secret matches the stored note.
7292
#[test]

0 commit comments

Comments
 (0)