Skip to content

Commit 8500d6a

Browse files
authored
feat: merge-train/fairies (#23815)
BEGIN_COMMIT_OVERRIDE chore: document browser kv-store backend migration (#23779) feat(aztec-nr): Compute unconstrained tag in Noir over PXE and generalize get_next_tagging_index oracle (#23796) refactor(txe): migrate rpc_translator to typed oracle registry (#23530) feat(pxe)!: allow apps to inject tagging secrets into getPendingTaggedLogs (#23777) feat(txe): add oracle roundtrip test framework (#23537) fix: embedded wallet defaults to proposed (#23819) feat(aztec-nr): discover non-interactive handshakes in the registry (#23806) refactor(txe): normalize deploy and addAccount to oracle registry (#23536) fix: wait for checkpoint during sandbox setup (#23834) feat(standard-contracts): graduate handshake registry to standard contract (#23833) END_COMMIT_OVERRIDE
2 parents 2aa7f9f + 4ca77c2 commit 8500d6a

74 files changed

Lines changed: 3752 additions & 2041 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

bootstrap.sh

Lines changed: 32 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -337,35 +337,50 @@ function start_txes {
337337
# Until Kev's kzg lib stops using Tokio.
338338
export TOKIO_WORKER_THREADS=1
339339

340-
# Starting txe servers with incrementing port numbers.
341-
# Base port is below the Linux ephemeral range (32768-60999) to avoid conflicts.
342-
local txe_base_port=14730
343-
for i in $(seq 0 $((NUM_TXES-1))); do
344-
port=$((txe_base_port + i))
345-
existing_pid=$(lsof -ti :$port || true)
340+
kill_port() {
341+
local port=$1
342+
local existing_pid=$(lsof -ti :$port || true)
346343
if [ -n "$existing_pid" ]; then
347344
echo "Killing existing process $existing_pid on port: $port"
348345
check_port $port
349346
kill -9 $existing_pid &>/dev/null || true
350347
while kill -0 $existing_pid &>/dev/null; do sleep 0.1; done
351348
fi
349+
}
350+
351+
# Starting txe servers with incrementing port numbers.
352+
# Base port is below the Linux ephemeral range (32768-60999) to avoid conflicts.
353+
local txe_base_port=14730
354+
for i in $(seq 0 $((NUM_TXES-1))); do
355+
port=$((txe_base_port + i))
356+
kill_port $port
352357
dump_fail "LOG_LEVEL=info TXE_PORT=$port retry 'node --no-warnings ./yarn-project/txe/dest/bin/index.js'" &
353358
txe_pids+="$! "
354359
done
355360

356-
echo "Waiting for TXE's to start..."
361+
# Start the oracle test resolver for __oracle_test__-prefixed tests.
362+
local resolver_port=14830
363+
kill_port $resolver_port
364+
dump_fail "LOG_LEVEL=error ORACLE_TEST_PORT=$resolver_port node --no-warnings ./yarn-project/txe/dest/bin/oracle_test_server.js" &
365+
txe_pids+="$! "
366+
367+
wait_for_port() {
368+
local port=$1 name=$2 j=0
369+
echo "Waiting for $name to start..."
370+
while ! nc -z 127.0.0.1 $port &>/dev/null; do
371+
if [ $j == 60 ]; then
372+
echo_stderr "$name failed to start on port $port after 60s."
373+
check_port $port
374+
exit 1
375+
fi
376+
sleep 1
377+
j=$((j+1))
378+
done
379+
}
357380
for i in $(seq 0 $((NUM_TXES-1))); do
358-
local j=0
359-
while ! nc -z 127.0.0.1 $((txe_base_port + i)) &>/dev/null; do
360-
if [ $j == 60 ]; then
361-
echo_stderr "TXE $i failed to start on port $((txe_base_port + i)) after 60s."
362-
check_port $((txe_base_port + i))
363-
exit 1
364-
fi
365-
sleep 1
366-
j=$((j+1))
367-
done
381+
wait_for_port $((txe_base_port + i)) "TXE $i"
368382
done
383+
wait_for_port $resolver_port "oracle test resolver"
369384
}
370385

371386
function stop_txes {

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

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

1010
## TBD
1111

12+
### [Aztec.nr] `get_pending_tagged_logs` oracle interface updated (oracle version 28)
13+
14+
The `aztec_utl_getPendingTaggedLogs` oracle now takes an additional `provided_secrets` parameter of type `EphemeralArray<ProvidedSecret>`. This lets apps pass tagging secrets that PXE cannot derive on its own (e.g. handshake-derived secrets) alongside the secrets PXE manages internally.
15+
1216
### [Aztec.nr] `set_sender_for_tags` oracle removed
1317

1418
The `set_sender_for_tags` oracle has been removed. Contracts that used it to override the sender for discovery tag derivation should now use the `with_sender` builder method on `MessageDelivery`:

noir-projects/aztec-nr/aztec/src/macros/mod.nr

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,4 @@ pub mod notes;
1414
pub mod storage;
1515
pub mod events;
1616
pub mod authorization;
17+
pub(crate) mod oracle_testing;
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/// Marks a function as an oracle integration test.
2+
///
3+
/// Oracle tests verify that TS and Noir agree on oracle serialization. The test calls a real oracle function, which the
4+
/// TS resolver matches against hardcoded fixtures to verify both param deserialization and return serialization across
5+
/// the Noir-TS boundary.
6+
///
7+
/// Oracle tests use a dedicated TS oracle resolver server (different from the one used by TXE). The macro generates a
8+
/// `#[test]` wrapper with a `__oracle_test__` prefix that calls the original function, so that:
9+
/// - They run against the test resolver rather than the TXE oracle resolver
10+
/// - The CI test runner can target them separately
11+
///
12+
/// For oracles with no inputs and multiple fixture scenarios, pass a scenario name to select which fixture to use:
13+
/// `#[oracle_test("some")]`. The resolver matches the name against the fixture's `name` field.
14+
///
15+
/// ## Usage
16+
///
17+
/// ```noir
18+
/// use crate::macros::oracle_testing::oracle_test;
19+
///
20+
/// #[oracle_test]
21+
/// unconstrained fn my_oracle() {
22+
/// let result = my_oracle_internal(42);
23+
/// assert_eq(result, 9999, "expected value");
24+
/// }
25+
///
26+
/// #[oracle_test("some")]
27+
/// unconstrained fn my_oracle_returns_some() {
28+
/// let result = my_optional_oracle();
29+
/// assert(result.is_some(), "expected Some");
30+
/// }
31+
///
32+
/// #[oracle_test("none")]
33+
/// unconstrained fn my_oracle_returns_none() {
34+
/// let result = my_optional_oracle();
35+
/// assert(result.is_none(), "expected None");
36+
/// }
37+
/// ```
38+
#[varargs]
39+
pub comptime fn oracle_test(f: FunctionDefinition, args: [CtString]) -> Quoted {
40+
let name = f.name();
41+
let prefixed_name = f"__oracle_test__{name}".quoted_contents();
42+
43+
// If a scenario was configured, then call the oracle to set the scenario
44+
let setup = if args.len() == 0 {
45+
quote {}
46+
} else {
47+
let scenario = args[0].as_quoted_str!();
48+
quote { crate::macros::oracle_testing::set_oracle_test_scenario_oracle($scenario); }
49+
};
50+
51+
// Generate test to call the function that contains the oracle call and assertions
52+
quote {
53+
#[test]
54+
unconstrained fn $prefixed_name() {
55+
$setup
56+
$name()
57+
}
58+
}
59+
}
60+
61+
/// Tells the oracle test resolver to use a named fixture scenario for the next oracle call in this test session. This
62+
/// oracle is only handled by the test resolver (PXE and TXE do not implement it). Called automatically by the
63+
/// `#[oracle_test("scenario_name")]` macro.
64+
///
65+
#[oracle(aztec_oracle_test_set_scenario)]
66+
pub(crate) unconstrained fn set_oracle_test_scenario_oracle<let N: u32>(_scenario_name: str<N>) {}

noir-projects/aztec-nr/aztec/src/messages/discovery/mod.nr

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use crate::logging::{aztecnr_debug_log, aztecnr_debug_log_format, aztecnr_warn_log_format};
22
use crate::protocol::address::AztecAddress;
3+
use crate::protocol::hash::sha256_to_field;
34

45
pub(crate) mod nonce_discovery;
56
pub(crate) mod partial_notes;
@@ -8,19 +9,24 @@ pub mod private_notes;
89
pub mod process_message;
910

1011
use crate::{
12+
ephemeral::EphemeralArray,
1113
messages::{
1214
discovery::process_message::process_message_ciphertext,
1315
encoding::MAX_MESSAGE_CONTENT_LEN,
1416
logs::note::MAX_NOTE_PACKED_LEN,
1517
processing::{
1618
MessageContext, offchain::OffchainInboxSync, OffchainMessageWithContext,
17-
pending_tagged_log::PendingTaggedLog, validate_and_store_enqueued_notes_and_events,
19+
pending_tagged_log::PendingTaggedLog, provided_secret::ProvidedSecret,
20+
validate_and_store_enqueued_notes_and_events,
1821
},
1922
},
2023
oracle::message_processing,
2124
utils::array,
2225
};
2326

27+
global PROVIDED_SECRETS_ARRAY_BASE_SLOT: Field =
28+
sha256_to_field("AZTEC_NR::PROVIDED_SECRETS_ARRAY_BASE_SLOT".as_bytes());
29+
2430
pub struct NoteHashAndNullifier {
2531
/// The result of [`crate::note::note_interface::NoteHash::compute_note_hash`].
2632
pub note_hash: Field,
@@ -139,7 +145,9 @@ pub unconstrained fn do_sync_state(
139145

140146
// First we process all private logs, which can contain different kinds of messages e.g. private notes, partial
141147
// notes, private events, etc.
142-
let logs = message_processing::get_pending_tagged_logs(scope);
148+
// TODO(F-588): populate with tagging secrets for constrained delivery
149+
let provided_secrets = EphemeralArray::<ProvidedSecret>::at(PROVIDED_SECRETS_ARRAY_BASE_SLOT).clear();
150+
let logs = message_processing::get_pending_tagged_logs(scope, provided_secrets);
143151
logs.for_each(|_i, pending_tagged_log: PendingTaggedLog| {
144152
if pending_tagged_log.log.len() == 0 {
145153
aztecnr_warn_log_format!("Skipping empty log from tx {0}")([pending_tagged_log.context.tx_hash]);

noir-projects/aztec-nr/aztec/src/messages/encryption/aes128.nr

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -509,8 +509,6 @@ mod test {
509509
let randomness = 0x0101010101010101010101010101010101010101010101010101010101010101;
510510
let _ = OracleMock::mock("aztec_utl_getRandomField").returns(randomness).times(1000000);
511511

512-
let _ = OracleMock::mock("aztec_prv_getNextAppTagAsSender").returns(42);
513-
514512
// Encrypt the message
515513
let encrypted_message = BoundedVec::from_array(AES128::encrypt(plaintext, recipient, contract_address));
516514

noir-projects/aztec-nr/aztec/src/messages/logs/utils.nr

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
use crate::oracle::notes::{get_next_app_tag_as_sender, get_sender_for_tags};
2-
use crate::protocol::address::AztecAddress;
1+
use crate::messages::message_delivery::ONCHAIN_UNCONSTRAINED;
2+
use crate::oracle::{notes::{get_app_tagging_secret, get_next_tagging_index, get_sender_for_tags}, random::random};
3+
use crate::protocol::{address::AztecAddress, hash::poseidon2_hash};
34

45
// TODO(#14565): Add constrained tagging
56
/// Returns the next discovery tag for a private log sent to `recipient`.
@@ -18,12 +19,22 @@ pub(crate) fn compute_discovery_tag(recipient: AztecAddress, sender_override: Op
1819
f"Sender for tags is not set when emitting a private log and no override is set. Ensure the wallet provides a default sender.",
1920
)
2021
});
21-
get_next_app_tag_as_sender(sender, recipient)
22+
get_app_tagging_secret(sender, recipient).map_or_else(
23+
|| {
24+
// The oracle returns `None` for invalid recipients. To prevent king-of-the-hill attacks, emit a random
25+
// tag instead of failing: the log shape is preserved, even though no recipient can discover the note.
26+
random()
27+
},
28+
|secret| {
29+
let index = get_next_tagging_index(secret, ONCHAIN_UNCONSTRAINED);
30+
poseidon2_hash([secret, index as Field])
31+
},
32+
)
2233
}
2334
}
2435

2536
mod test {
26-
use crate::protocol::{address::AztecAddress, traits::FromField};
37+
use crate::protocol::{address::AztecAddress, hash::poseidon2_hash, traits::FromField};
2738
use crate::test::helpers::test_environment::TestEnvironment;
2839
use super::compute_discovery_tag;
2940
use std::test::OracleMock;
@@ -32,18 +43,30 @@ mod test {
3243
unconstrained fn no_tag_sender() {
3344
let recipient = AztecAddress::from_field(2);
3445
let _ = OracleMock::mock("aztec_prv_getSenderForTags").returns(Option::<AztecAddress>::none());
35-
let _ = OracleMock::mock("aztec_prv_getNextAppTagAsSender").returns(42);
3646
let _ = compute_discovery_tag(recipient, Option::none());
3747
}
3848

3949
#[test]
40-
unconstrained fn returns_oracle_tag() {
50+
unconstrained fn computes_tag_from_secret_and_index() {
4151
let sender = AztecAddress::from_field(1);
4252
let recipient = AztecAddress::from_field(2);
43-
let expected_tag = 42;
53+
let secret: Field = 7;
54+
let index: u32 = 3;
4455
let _ = OracleMock::mock("aztec_prv_getSenderForTags").returns(Option::some(sender));
45-
let _ = OracleMock::mock("aztec_prv_getNextAppTagAsSender").returns(expected_tag);
46-
assert_eq(compute_discovery_tag(recipient, Option::none()), expected_tag);
56+
let _ = OracleMock::mock("aztec_prv_getAppTaggingSecret").returns(Option::some(secret));
57+
let _ = OracleMock::mock("aztec_prv_getNextTaggingIndex").returns(index);
58+
assert_eq(compute_discovery_tag(recipient, Option::none()), poseidon2_hash([secret, index as Field]));
59+
}
60+
61+
#[test]
62+
unconstrained fn uses_random_tag_for_invalid_recipient() {
63+
let sender = AztecAddress::from_field(1);
64+
let recipient = AztecAddress::from_field(2);
65+
let random_tag = 42;
66+
let _ = OracleMock::mock("aztec_prv_getSenderForTags").returns(Option::some(sender));
67+
let _ = OracleMock::mock("aztec_prv_getAppTaggingSecret").returns(Option::<Field>::none());
68+
let _ = OracleMock::mock("aztec_utl_getRandomField").returns(random_tag);
69+
assert_eq(compute_discovery_tag(recipient, Option::none()), random_tag);
4770
}
4871

4972
#[test]

noir-projects/aztec-nr/aztec/src/messages/processing/log_retrieval_request.nr

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
use crate::protocol::{address::AztecAddress, traits::Serialize};
22

3-
pub(crate) struct LogSourceEnum {
3+
pub struct LogSourceEnum {
44
pub PRIVATE: Field,
55
pub PUBLIC: Field,
66
pub PUBLIC_AND_PRIVATE: Field,
77
}
88

9-
pub(crate) global LogSource: LogSourceEnum = LogSourceEnum { PRIVATE: 0, PUBLIC: 1, PUBLIC_AND_PRIVATE: 2 };
9+
pub global LogSource: LogSourceEnum = LogSourceEnum { PRIVATE: 0, PUBLIC: 1, PUBLIC_AND_PRIVATE: 2 };
1010

1111
/// A request for the `bulk_retrieve_logs` oracle to fetch all logs matching a tag.
1212
#[derive(Serialize)]
13-
pub(crate) struct LogRetrievalRequest {
13+
pub struct LogRetrievalRequest {
1414
pub contract_address: AztecAddress,
1515
pub unsiloed_tag: Field,
1616
/// Which log source to query: public, private, or both (the default). See [`LogSource`].

noir-projects/aztec-nr/aztec/src/messages/processing/log_retrieval_response.nr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ pub(crate) global MAX_LOG_CONTENT_LEN: u32 = std::cmp::max(
1212
/// plaintext for public logs and ciphertext for private), plus contextual information about the transaction in which
1313
/// the log was emitted. This is the data required in order to discover notes that are being delivered in a log.
1414
#[derive(Deserialize, Eq)]
15-
pub(crate) struct LogRetrievalResponse {
15+
pub struct LogRetrievalResponse {
1616
pub log_payload: BoundedVec<Field, MAX_LOG_CONTENT_LEN>,
1717
pub tx_hash: Field,
1818
/// The array of new note hashes created by `tx_hash`

noir-projects/aztec-nr/aztec/src/messages/processing/mod.nr

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,10 @@ pub use message_context::MessageContext;
77
mod note_validation_request;
88
pub use note_validation_request::NoteValidationRequest;
99

10-
pub(crate) mod log_retrieval_request;
11-
pub(crate) mod log_retrieval_response;
10+
pub mod log_retrieval_request;
11+
pub mod log_retrieval_response;
1212
pub(crate) mod pending_tagged_log;
13+
pub(crate) mod provided_secret;
1314

1415
use crate::{
1516
capsules::CapsuleArray,

0 commit comments

Comments
 (0)