From 6f5498f98255ef8543f4767df1c1a9fe01f208d1 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Mon, 6 Jul 2026 16:49:35 -0400 Subject: [PATCH 01/21] test(txe): cover unconstrained delivery --- .../aztec-nr/aztec/src/test/helpers/mod.nr | 1 + .../test/helpers/tagging_secret_strategy.nr | 11 ++ .../src/test/helpers/test_environment.nr | 35 +++++- .../aztec/src/test/helpers/txe_oracles.nr | 8 +- .../src/main.nr | 25 +++- .../src/test.nr | 119 +++++++++++++++++- yarn-project/txe/src/oracle/interfaces.ts | 4 + .../oracle/tagging_secret_strategy.test.ts | 64 ++++++++++ .../txe/src/oracle/tagging_secret_strategy.ts | 27 ++++ .../txe/src/oracle/txe_oracle_registry.ts | 7 ++ .../oracle/txe_oracle_top_level_context.ts | 14 ++- .../txe/src/oracle/txe_oracle_version.ts | 4 +- yarn-project/txe/src/rpc_translator.ts | 10 ++ yarn-project/txe/src/txe_session.ts | 9 +- 14 files changed, 322 insertions(+), 16 deletions(-) create mode 100644 yarn-project/txe/src/oracle/tagging_secret_strategy.test.ts create mode 100644 yarn-project/txe/src/oracle/tagging_secret_strategy.ts diff --git a/noir-projects/aztec-nr/aztec/src/test/helpers/mod.nr b/noir-projects/aztec-nr/aztec/src/test/helpers/mod.nr index 6cd7f18ac34f..8f1d9bf05444 100644 --- a/noir-projects/aztec-nr/aztec/src/test/helpers/mod.nr +++ b/noir-projects/aztec-nr/aztec/src/test/helpers/mod.nr @@ -14,6 +14,7 @@ pub mod test_environment; quote { public_call_new_flow_oracle }, // TODO: implement once we support more complex types quote { set_private_txe_context_oracle }, // TODO: implement once we support more complex types quote { set_tagging_secret_strategy }, // TODO: implement once we support more complex types + quote { set_tagging_secret_strategies_by_delivery_mode }, // TODO: implement once we support more complex types ], )] pub mod txe_oracles; diff --git a/noir-projects/aztec-nr/aztec/src/test/helpers/tagging_secret_strategy.nr b/noir-projects/aztec-nr/aztec/src/test/helpers/tagging_secret_strategy.nr index a4f7b52d91ba..a78758b07fcd 100644 --- a/noir-projects/aztec-nr/aztec/src/test/helpers/tagging_secret_strategy.nr +++ b/noir-projects/aztec-nr/aztec/src/test/helpers/tagging_secret_strategy.nr @@ -48,6 +48,17 @@ impl TaggingSecretStrategy { } } +pub struct TaggingSecretStrategiesByDeliveryMode { + pub unconstrained_strategy: TaggingSecretStrategy, + pub constrained_strategy: TaggingSecretStrategy, +} + +impl TaggingSecretStrategiesByDeliveryMode { + pub fn new(unconstrained_strategy: TaggingSecretStrategy, constrained_strategy: TaggingSecretStrategy) -> Self { + Self { unconstrained_strategy, constrained_strategy } + } +} + impl Deserialize for TaggingSecretStrategy { let N: u32 = 3; diff --git a/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr b/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr index ef55059fb0fd..69ec463db50a 100644 --- a/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr +++ b/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr @@ -23,7 +23,11 @@ use crate::{ }, note::{note_interface::{NoteHash, NoteType}, NoteMessage}, oracle::version::assert_compatible_oracle_version, - test::helpers::{tagging_secret_strategy::TaggingSecretStrategy, txe_oracles, utils::ContractDeployment}, + test::helpers::{ + tagging_secret_strategy::{TaggingSecretStrategiesByDeliveryMode, TaggingSecretStrategy}, + txe_oracles, + utils::ContractDeployment, + }, utils::array::subarray, }; @@ -93,12 +97,13 @@ pub struct TestEnvironment { /// ``` pub struct TestEnvironmentOptions { tagging_secret_strategy: Option, + tagging_secret_strategies_by_delivery_mode: Option, } impl TestEnvironmentOptions { /// Creates a new `TestEnvironmentOptions` with default values. pub fn new() -> Self { - Self { tagging_secret_strategy: Option::none() } + Self { tagging_secret_strategy: Option::none(), tagging_secret_strategies_by_delivery_mode: Option::none() } } /// Sets the [`TaggingSecretStrategy`] the wallet reports through the @@ -108,6 +113,22 @@ impl TestEnvironmentOptions { /// If not set, the wallet hook is left unconfigured and the private execution environment applies its own default. pub fn with_tagging_secret_strategy(&mut self, strategy: TaggingSecretStrategy) -> Self { self.tagging_secret_strategy = Option::some(strategy); + self.tagging_secret_strategies_by_delivery_mode = Option::none(); + *self + } + + /// Sets the [`TaggingSecretStrategy`] the wallet reports by delivery mode through the + /// [`resolve_tagging_strategy`](crate::oracle::resolve_tagging_strategy) oracle. + pub fn with_tagging_secret_strategies_by_delivery_mode( + &mut self, + unconstrained_strategy: TaggingSecretStrategy, + constrained_strategy: TaggingSecretStrategy, + ) -> Self { + self.tagging_secret_strategy = Option::none(); + self.tagging_secret_strategies_by_delivery_mode = Option::some(TaggingSecretStrategiesByDeliveryMode::new( + unconstrained_strategy, + constrained_strategy, + )); *self } } @@ -618,7 +639,15 @@ impl TestEnvironment { txe_oracles::assert_compatible_txe_oracle_version(); // Forward the configured strategy to the wallet. - txe_oracles::set_tagging_secret_strategy(options.tagging_secret_strategy); + if options.tagging_secret_strategies_by_delivery_mode.is_some() { + let strategies = options.tagging_secret_strategies_by_delivery_mode.unwrap_unchecked(); + txe_oracles::set_tagging_secret_strategies_by_delivery_mode( + strategies.unconstrained_strategy, + strategies.constrained_strategy, + ); + } else { + txe_oracles::set_tagging_secret_strategy(options.tagging_secret_strategy); + } Self { // Use an offset to avoid secret collision with account secrets. Without this, when diff --git a/noir-projects/aztec-nr/aztec/src/test/helpers/txe_oracles.nr b/noir-projects/aztec-nr/aztec/src/test/helpers/txe_oracles.nr index c388db5e56cb..8f93b10d5c05 100644 --- a/noir-projects/aztec-nr/aztec/src/test/helpers/txe_oracles.nr +++ b/noir-projects/aztec-nr/aztec/src/test/helpers/txe_oracles.nr @@ -22,7 +22,7 @@ use crate::protocol::{ /// /// The TypeScript counterparts are in `yarn-project/txe/src/txe_oracle_version.ts`. pub global TXE_ORACLE_VERSION_MAJOR: Field = 2; -pub global TXE_ORACLE_VERSION_MINOR: Field = 2; +pub global TXE_ORACLE_VERSION_MINOR: Field = 3; /// Asserts that the TXE oracle interface version is compatible. pub unconstrained fn assert_compatible_txe_oracle_version() { @@ -268,6 +268,12 @@ pub unconstrained fn send_l1_to_l2_message( #[oracle(aztec_txe_setTaggingSecretStrategy)] pub unconstrained fn set_tagging_secret_strategy(strategy: Option) {} +#[oracle(aztec_txe_setTaggingSecretStrategiesByDeliveryMode)] +pub unconstrained fn set_tagging_secret_strategies_by_delivery_mode( + unconstrained_strategy: TaggingSecretStrategy, + constrained_strategy: TaggingSecretStrategy, +) {} + #[oracle(aztec_txe_privateCallNewFlow)] unconstrained fn private_call_new_flow_oracle( _from: Option, diff --git a/noir-projects/noir-contracts/contracts/test/constrained_delivery_test_contract/src/main.nr b/noir-projects/noir-contracts/contracts/test/constrained_delivery_test_contract/src/main.nr index 6274a64cb05c..6f316707f260 100644 --- a/noir-projects/noir-contracts/contracts/test/constrained_delivery_test_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/test/constrained_delivery_test_contract/src/main.nr @@ -9,7 +9,7 @@ pub contract ConstrainedDeliveryTest { macros::{events::event, functions::external, storage::storage}, messages::delivery::MessageDelivery, note::note_viewer_options::NoteViewerOptions, - oracle::notes::get_next_tagging_index, + oracle::notes::{get_app_tagging_secret, get_next_tagging_index}, protocol::{ address::AztecAddress, constants::DOM_SEP__CONSTRAINED_MSG_LOG_TAG, @@ -41,6 +41,24 @@ pub contract ConstrainedDeliveryTest { } } + #[external("private")] + fn next_unconstrained_index_for_secret(secret: Field) -> u32 { + // Safety: test-only observation of the index the PXE hands out next for this secret. + unsafe { + get_next_tagging_index(secret, MessageDelivery::onchain_unconstrained()) + } + } + + #[external("private")] + fn address_derived_secret(sender: AztecAddress, recipient: AztecAddress) -> Field { + // Safety: test-only observation of the PXE's address-derived tagging secret. + unsafe { + let secret: Field = + get_app_tagging_secret(sender, recipient).expect(f"expected an address-derived tagging secret"); + secret + } + } + #[external("private")] fn emit_note(recipient: AztecAddress, value: Field) { self.storage.notes.at(recipient).insert(FieldNote { value }).deliver(MessageDelivery::onchain_constrained()); @@ -56,6 +74,11 @@ pub contract ConstrainedDeliveryTest { self.emit(DeliveryEvent { value }).deliver_to(recipient, MessageDelivery::onchain_constrained()); } + #[external("private")] + fn emit_unconstrained_event(recipient: AztecAddress, value: Field) { + self.emit(DeliveryEvent { value }).deliver_to(recipient, MessageDelivery::onchain_unconstrained()); + } + #[external("private")] fn emit_two_events(recipient: AztecAddress) { self.emit(DeliveryEvent { value: 1 }).deliver_to(recipient, MessageDelivery::onchain_constrained()); diff --git a/noir-projects/noir-contracts/contracts/test/constrained_delivery_test_contract/src/test.nr b/noir-projects/noir-contracts/contracts/test/constrained_delivery_test_contract/src/test.nr index 45f4279d149e..78d9bfb4bb64 100644 --- a/noir-projects/noir-contracts/contracts/test/constrained_delivery_test_contract/src/test.nr +++ b/noir-projects/noir-contracts/contracts/test/constrained_delivery_test_contract/src/test.nr @@ -7,14 +7,27 @@ use crate::ConstrainedDeliveryTest; use aztec::{ - protocol::address::AztecAddress, + keys::ecdh_shared_secret::compute_app_siloed_shared_secret, + protocol::{address::AztecAddress, hash::poseidon2_hash, point::EmbeddedCurvePoint, traits::{FromField, ToField}}, standard_addresses::STANDARD_HANDSHAKE_REGISTRY_ADDRESS, - test::helpers::test_environment::{CallPrivateOptions, DeployOptions, ExecuteUtilityOptions, TestEnvironment}, + test::helpers::{ + tagging_secret_strategy::TaggingSecretStrategy, + test_environment::{ + CallPrivateOptions, DeployOptions, ExecuteUtilityOptions, TestEnvironment, TestEnvironmentOptions, + }, + txe_oracles, + }, }; use handshake_registry_contract::HandshakeRegistry; unconstrained fn setup() -> (TestEnvironment, AztecAddress, AztecAddress, AztecAddress, AztecAddress) { - let mut env = TestEnvironment::new(); + setup_opts(TestEnvironmentOptions::new()) +} + +unconstrained fn setup_opts( + options: TestEnvironmentOptions, +) -> (TestEnvironment, AztecAddress, AztecAddress, AztecAddress, AztecAddress) { + let mut env = TestEnvironment::new_opts(options); let sender = env.create_light_account(); let recipient = env.create_light_account(); @@ -35,6 +48,106 @@ unconstrained fn authorizing(registry_address: AztecAddress) -> CallPrivateOptio CallPrivateOptions::new().with_authorized_utility_call_targets([registry_address]) } +unconstrained fn external_recipient() -> AztecAddress { + AztecAddress::from_field(8) +} + +unconstrained fn arbitrary_secret_point() -> EmbeddedCurvePoint { + EmbeddedCurvePoint { + x: 0x15d55a5b3b2caa6a6207f313f05c5113deba5da9927d6421bcaa164822b911bc, + y: 0x0974c3d0825031ae933243d653ebb1a0b08b90ee7f228f94c5c74739ea3c871e, + } +} + +#[test] +unconstrained fn unconstrained_delivery_uses_configured_arbitrary_secret() { + let point = arbitrary_secret_point(); + let options = + TestEnvironmentOptions::new().with_tagging_secret_strategy(TaggingSecretStrategy::arbitrary_secret(point)); + let (env, registry_address, test_address, sender, _) = setup_opts(options); + let recipient = external_recipient(); + let test_contract = ConstrainedDeliveryTest::at(test_address); + let registry = HandshakeRegistry::at(registry_address); + + env.call_private(sender, test_contract.emit_unconstrained_event(recipient, 1)); + + let effects = txe_oracles::get_last_tx_effects(); + assert_eq(effects.private_logs.len(), 1); + + let app_secret = compute_app_siloed_shared_secret(point, test_address); + let expected_secret = poseidon2_hash([app_secret, recipient.to_field()]); + let next_unconstrained_index = + env.call_private(sender, test_contract.next_unconstrained_index_for_secret(expected_secret)); + let next_constrained_index = env.call_private(sender, test_contract.next_index_for_secret(expected_secret)); + assert_eq(next_unconstrained_index, 1); + assert_eq(next_constrained_index, 0); + + let maybe_handshake = env.execute_utility_opts( + ExecuteUtilityOptions::new().with_from(test_address), + registry.get_app_siloed_secrets(sender, recipient), + ); + assert(maybe_handshake.is_none()); +} + +#[test] +unconstrained fn unconstrained_delivery_uses_address_derived_secret() { + let options = TestEnvironmentOptions::new().with_tagging_secret_strategy(TaggingSecretStrategy::address_derived()); + let (env, _, test_address, sender, _) = setup_opts(options); + let recipient = external_recipient(); + let test_contract = ConstrainedDeliveryTest::at(test_address); + let secret = env.call_private(sender, test_contract.address_derived_secret(sender, recipient)); + + env.call_private(sender, test_contract.emit_unconstrained_event(recipient, 1)); + + let private_logs = txe_oracles::get_last_tx_effects().private_logs; + assert_eq(private_logs.len(), 1); + + let next_unconstrained_index = env.call_private(sender, test_contract.next_unconstrained_index_for_secret(secret)); + let next_constrained_index = env.call_private(sender, test_contract.next_index_for_secret(secret)); + assert_eq(next_unconstrained_index, 1); + assert_eq(next_constrained_index, 0); +} + +#[test] +unconstrained fn tagging_secret_strategy_can_differ_by_delivery_mode() { + let point = arbitrary_secret_point(); + let options = TestEnvironmentOptions::new().with_tagging_secret_strategies_by_delivery_mode( + TaggingSecretStrategy::arbitrary_secret(point), + TaggingSecretStrategy::non_interactive_handshake(), + ); + let (env, registry_address, test_address, sender, _) = setup_opts(options); + let recipient = external_recipient(); + let test_contract = ConstrainedDeliveryTest::at(test_address); + let registry = HandshakeRegistry::at(registry_address); + + env.call_private(sender, test_contract.emit_unconstrained_event(recipient, 1)); + + let app_secret = compute_app_siloed_shared_secret(point, test_address); + let expected_unconstrained_secret = poseidon2_hash([app_secret, recipient.to_field()]); + let next_unconstrained_index = + env.call_private(sender, test_contract.next_unconstrained_index_for_secret(expected_unconstrained_secret)); + assert_eq(next_unconstrained_index, 1); + + let maybe_handshake = env.execute_utility_opts( + ExecuteUtilityOptions::new().with_from(test_address), + registry.get_app_siloed_secrets(sender, recipient), + ); + assert(maybe_handshake.is_none()); + + env.call_private_opts(sender, authorizing(registry_address), test_contract.emit_event(recipient, 1)); + + let handshake = env + .execute_utility_opts( + ExecuteUtilityOptions::new().with_from(test_address), + registry.get_app_siloed_secrets(sender, recipient), + ) + .expect(f"constrained delivery should have stored a handshake"); + assert(handshake.shared != expected_unconstrained_secret); + + let next_constrained_index = env.call_private(sender, test_contract.next_index_for_secret(handshake.shared)); + assert_eq(next_constrained_index, 1); +} + #[test] unconstrained fn rehandshake_replaces_registry_secret_for_future_delivery() { let (env, registry_address, test_address, sender, recipient) = setup(); diff --git a/yarn-project/txe/src/oracle/interfaces.ts b/yarn-project/txe/src/oracle/interfaces.ts index b1d29f3f18cc..939b86e76d18 100644 --- a/yarn-project/txe/src/oracle/interfaces.ts +++ b/yarn-project/txe/src/oracle/interfaces.ts @@ -74,6 +74,10 @@ export interface ITxeExecutionOracle { addAuthWitness(address: AztecAddress, messageHash: Fr): Promise; sendL1ToL2Message(content: Fr, secretHash: Fr, sender: EthAddress, recipient: AztecAddress): Promise; setTaggingSecretStrategy(strategy: Option): void; + setTaggingSecretStrategiesByDeliveryMode( + unconstrained: TaggingSecretStrategy, + constrained: TaggingSecretStrategy, + ): void; getLastBlockTimestamp(): Promise; getLastTxEffects(): Promise<{ txHash: TxHash; diff --git a/yarn-project/txe/src/oracle/tagging_secret_strategy.test.ts b/yarn-project/txe/src/oracle/tagging_secret_strategy.test.ts new file mode 100644 index 000000000000..f7bf48e09a36 --- /dev/null +++ b/yarn-project/txe/src/oracle/tagging_secret_strategy.test.ts @@ -0,0 +1,64 @@ +import { Fr } from '@aztec/foundation/curves/bn254'; +import { Point } from '@aztec/foundation/curves/grumpkin'; +import { AztecAddress } from '@aztec/stdlib/aztec-address'; +import { AppTaggingSecretKind } from '@aztec/stdlib/logs'; + +import { makeResolveTaggingSecretStrategyHook } from './tagging_secret_strategy.js'; +import { callTxeHandler } from './txe_oracle_registry.js'; + +describe('makeResolveTaggingSecretStrategyHook', () => { + it('returns undefined when no TXE strategy is configured', () => { + expect(makeResolveTaggingSecretStrategyHook(undefined)).toBeUndefined(); + }); + + it('returns a static strategy for every delivery mode', async () => { + const strategy = { type: 'non-interactive-handshake' as const }; + const hook = makeResolveTaggingSecretStrategyHook(strategy); + + await expect(hook?.(makeRequest(AppTaggingSecretKind.UNCONSTRAINED))).resolves.toBe(strategy); + await expect(hook?.(makeRequest(AppTaggingSecretKind.CONSTRAINED))).resolves.toBe(strategy); + }); + + it('selects a strategy by delivery mode', async () => { + const unconstrained = { type: 'address-derived' as const }; + const constrained = { type: 'arbitrary-secret' as const, secret: await Point.random() }; + const hook = makeResolveTaggingSecretStrategyHook({ + type: 'by-delivery-mode', + unconstrained, + constrained, + }); + + await expect(hook?.(makeRequest(AppTaggingSecretKind.UNCONSTRAINED))).resolves.toBe(unconstrained); + await expect(hook?.(makeRequest(AppTaggingSecretKind.CONSTRAINED))).resolves.toBe(constrained); + }); + + it('deserializes the mode-aware TXE oracle setter', async () => { + const received = await callTxeHandler({ + oracle: 'aztec_txe_setTaggingSecretStrategiesByDeliveryMode', + inputs: [field(2), field(5), field(6), field(1), field(0), field(0)], + handler: ([unconstrained, constrained]) => { + expect(unconstrained.type).toBe('arbitrary-secret'); + expect( + unconstrained.type === 'arbitrary-secret' && unconstrained.secret.equals(new Point(new Fr(5), new Fr(6))), + ).toBeTruthy(); + expect(constrained).toEqual({ type: 'non-interactive-handshake' }); + }, + }); + + expect(received.values).toEqual([]); + }); +}); + +function makeRequest(deliveryMode: AppTaggingSecretKind) { + return { + contractAddress: AztecAddress.ZERO, + contractClassId: Fr.ZERO, + sender: AztecAddress.ZERO, + recipient: AztecAddress.ZERO, + deliveryMode, + }; +} + +function field(value: number) { + return new Fr(value).toString().replace(/^0x/, ''); +} diff --git a/yarn-project/txe/src/oracle/tagging_secret_strategy.ts b/yarn-project/txe/src/oracle/tagging_secret_strategy.ts new file mode 100644 index 000000000000..70a1f0794312 --- /dev/null +++ b/yarn-project/txe/src/oracle/tagging_secret_strategy.ts @@ -0,0 +1,27 @@ +import type { ResolveTaggingSecretStrategy, TaggingSecretStrategy } from '@aztec/pxe/server'; +import { AppTaggingSecretKind } from '@aztec/stdlib/logs'; + +export type TXETaggingSecretStrategy = + | TaggingSecretStrategy + | { + type: 'by-delivery-mode'; + unconstrained: TaggingSecretStrategy; + constrained: TaggingSecretStrategy; + }; + +export function makeResolveTaggingSecretStrategyHook( + strategy: TXETaggingSecretStrategy | undefined, +): ResolveTaggingSecretStrategy | undefined { + if (strategy === undefined) { + return undefined; + } + + if (strategy.type !== 'by-delivery-mode') { + return () => Promise.resolve(strategy); + } + + return ({ deliveryMode }) => + Promise.resolve( + deliveryMode === AppTaggingSecretKind.UNCONSTRAINED ? strategy.unconstrained : strategy.constrained, + ); +} diff --git a/yarn-project/txe/src/oracle/txe_oracle_registry.ts b/yarn-project/txe/src/oracle/txe_oracle_registry.ts index f6a82f6e019b..95ec203d29f4 100644 --- a/yarn-project/txe/src/oracle/txe_oracle_registry.ts +++ b/yarn-project/txe/src/oracle/txe_oracle_registry.ts @@ -319,6 +319,13 @@ export const TXE_ORACLE_REGISTRY = { params: [{ name: 'strategy', type: OPTION(TAGGING_SECRET_STRATEGY) }], }), + aztec_txe_setTaggingSecretStrategiesByDeliveryMode: makeEntry({ + params: [ + { name: 'unconstrained', type: TAGGING_SECRET_STRATEGY }, + { name: 'constrained', type: TAGGING_SECRET_STRATEGY }, + ], + }), + aztec_txe_getLastBlockTimestamp: makeEntry({ returnType: BIGINT, }), diff --git a/yarn-project/txe/src/oracle/txe_oracle_top_level_context.ts b/yarn-project/txe/src/oracle/txe_oracle_top_level_context.ts index 9c86619698ab..5d3972df7597 100644 --- a/yarn-project/txe/src/oracle/txe_oracle_top_level_context.ts +++ b/yarn-project/txe/src/oracle/txe_oracle_top_level_context.ts @@ -99,6 +99,7 @@ import type { TXEAccountStore } from '../utils/txe_account_store.js'; import type { TXEArtifactResolver } from '../utils/txe_artifact_resolver.js'; import { TXEPublicContractDataSource } from '../utils/txe_public_contract_data_source.js'; import type { ITxeExecutionOracle } from './interfaces.js'; +import { type TXETaggingSecretStrategy, makeResolveTaggingSecretStrategyHook } from './tagging_secret_strategy.js'; export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracle { isMisc = true as const; @@ -123,7 +124,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl private version: Fr, private chainId: Fr, private authwits: Map, - private taggingSecretStrategy: TaggingSecretStrategy | undefined, + private taggingSecretStrategy: TXETaggingSecretStrategy | undefined, private readonly artifactResolver: TXEArtifactResolver, private readonly rootPath: string, private readonly packageName: string, @@ -360,6 +361,13 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl this.taggingSecretStrategy = strategy.value; } + setTaggingSecretStrategiesByDeliveryMode( + unconstrained: TaggingSecretStrategy, + constrained: TaggingSecretStrategy, + ): void { + this.taggingSecretStrategy = { type: 'by-delivery-mode', unconstrained, constrained }; + } + async sendL1ToL2Message(content: Fr, secretHash: Fr, sender: EthAddress, recipient: AztecAddress): Promise { // Messages are appended to the tree, so the next free slot is simply the current tree size. const { size } = await this.stateMachine.synchronizer @@ -512,7 +520,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl ), // Only configure the hook when a strategy was explicitly set, so that otherwise the default tagging secret // strategy is exercised. - resolveTaggingSecretStrategy: taggingSecretStrategy ? () => Promise.resolve(taggingSecretStrategy) : undefined, + resolveTaggingSecretStrategy: makeResolveTaggingSecretStrategyHook(taggingSecretStrategy), }), transientArrayService, }); @@ -964,7 +972,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl } } - close(): [bigint, Map, TaggingSecretStrategy | undefined] { + close(): [bigint, Map, TXETaggingSecretStrategy | undefined] { this.logger.debug('Exiting Top Level Context'); return [this.nextBlockTimestamp, this.authwits, this.taggingSecretStrategy]; } diff --git a/yarn-project/txe/src/oracle/txe_oracle_version.ts b/yarn-project/txe/src/oracle/txe_oracle_version.ts index 081232d389e3..f50649d09b47 100644 --- a/yarn-project/txe/src/oracle/txe_oracle_version.ts +++ b/yarn-project/txe/src/oracle/txe_oracle_version.ts @@ -6,7 +6,7 @@ * The Noir counterparts are in `noir-projects/aztec-nr/aztec/src/test/helpers/txe_oracles.nr`. */ export const TXE_ORACLE_VERSION_MAJOR = 2; -export const TXE_ORACLE_VERSION_MINOR = 2; +export const TXE_ORACLE_VERSION_MINOR = 3; /** * This hash is computed from the TXE oracle interfaces (IAvmExecutionOracle and ITxeExecutionOracle) and is used to @@ -14,4 +14,4 @@ export const TXE_ORACLE_VERSION_MINOR = 2; * - TXE_ORACLE_VERSION_MAJOR (and reset MINOR to 0) for breaking changes, or * - TXE_ORACLE_VERSION_MINOR for additive changes (new oracle method added). */ -export const TXE_ORACLE_INTERFACE_HASH = '4ed3618087fafb4aa63c6580996a69bf6bc257844035a2019692586b5b8daf34'; +export const TXE_ORACLE_INTERFACE_HASH = '67af6782f0f848a81508b44d619f01e6492b5883b30ed97cbed5bcf8bad00529'; diff --git a/yarn-project/txe/src/rpc_translator.ts b/yarn-project/txe/src/rpc_translator.ts index ce11b500c27b..7d7ef663eaf5 100644 --- a/yarn-project/txe/src/rpc_translator.ts +++ b/yarn-project/txe/src/rpc_translator.ts @@ -229,6 +229,16 @@ export class RPCTranslator { }); } + // eslint-disable-next-line camelcase + aztec_txe_setTaggingSecretStrategiesByDeliveryMode(...inputs: ForeignCallArgs) { + return callTxeHandler({ + oracle: 'aztec_txe_setTaggingSecretStrategiesByDeliveryMode', + inputs, + handler: ([unconstrained, constrained]) => + this.handlerAsTxe().setTaggingSecretStrategiesByDeliveryMode(unconstrained, constrained), + }); + } + // PXE oracles // eslint-disable-next-line camelcase diff --git a/yarn-project/txe/src/txe_session.ts b/yarn-project/txe/src/txe_session.ts index b72b46b2b346..7fb8d67a4b8c 100644 --- a/yarn-project/txe/src/txe_session.ts +++ b/yarn-project/txe/src/txe_session.ts @@ -20,7 +20,6 @@ import { RecipientTaggingStore, SenderTaggingStore, TaggingSecretSourcesStore, - type TaggingSecretStrategy, composeHooks, } from '@aztec/pxe/server'; import { @@ -58,6 +57,10 @@ import { z } from 'zod'; import { DEFAULT_ADDRESS, MAX_OFFCHAIN_EFFECTS_PER_TXE_QUERY, MAX_OFFCHAIN_EFFECT_LEN } from './constants.js'; import type { IAvmExecutionOracle, ITxeExecutionOracle } from './oracle/interfaces.js'; +import { + type TXETaggingSecretStrategy, + makeResolveTaggingSecretStrategyHook, +} from './oracle/tagging_secret_strategy.js'; import { TXEOraclePublicContext } from './oracle/txe_oracle_public_context.js'; import { callTxeLegacyHandler } from './oracle/txe_oracle_registry.js'; import { TXEOracleTopLevelContext } from './oracle/txe_oracle_top_level_context.js'; @@ -237,7 +240,7 @@ function emptyLastCallState(): LastCallState { export class TXESession implements TXESessionStateHandler { private state: SessionState = { name: 'TOP_LEVEL' }; private authwits: Map = new Map(); - private taggingSecretStrategy: TaggingSecretStrategy | undefined = undefined; + private taggingSecretStrategy: TXETaggingSecretStrategy | undefined = undefined; private lastCallInfo: LastCallState = emptyLastCallState(); private txeOracleVersion: { major: number; minor: number } | undefined; @@ -767,7 +770,7 @@ export class TXESession implements TXESessionStateHandler { txResolver: this.stateMachine.txResolver, simulator: new WASMSimulator(), hooks: composeHooks({ - resolveTaggingSecretStrategy: taggingSecretStrategy ? () => Promise.resolve(taggingSecretStrategy) : undefined, + resolveTaggingSecretStrategy: makeResolveTaggingSecretStrategyHook(taggingSecretStrategy), }), transientArrayService, }); From 0792034237af69d5b84775279d2f238fbe09ebeb Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Mon, 6 Jul 2026 17:15:34 -0400 Subject: [PATCH 02/21] test(txe): align tagging strategy oracle with PXE --- .../aztec-nr/aztec/src/test/helpers/mod.nr | 1 - .../test/helpers/tagging_secret_strategy.nr | 11 ---- .../src/test/helpers/test_environment.nr | 45 ++++++++-------- .../aztec/src/test/helpers/txe_oracles.nr | 15 +++--- yarn-project/txe/src/oracle/interfaces.ts | 8 +-- .../oracle/tagging_secret_strategy.test.ts | 51 +++++++++++++------ .../txe/src/oracle/tagging_secret_strategy.ts | 21 ++------ .../txe/src/oracle/txe_oracle_registry.ts | 9 ++-- .../oracle/txe_oracle_top_level_context.ts | 28 +++++----- .../txe/src/oracle/txe_oracle_version.ts | 6 +-- yarn-project/txe/src/rpc_translator.ts | 12 +---- yarn-project/txe/src/txe_session.ts | 14 ++--- 12 files changed, 95 insertions(+), 126 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/test/helpers/mod.nr b/noir-projects/aztec-nr/aztec/src/test/helpers/mod.nr index 8f1d9bf05444..6cd7f18ac34f 100644 --- a/noir-projects/aztec-nr/aztec/src/test/helpers/mod.nr +++ b/noir-projects/aztec-nr/aztec/src/test/helpers/mod.nr @@ -14,7 +14,6 @@ pub mod test_environment; quote { public_call_new_flow_oracle }, // TODO: implement once we support more complex types quote { set_private_txe_context_oracle }, // TODO: implement once we support more complex types quote { set_tagging_secret_strategy }, // TODO: implement once we support more complex types - quote { set_tagging_secret_strategies_by_delivery_mode }, // TODO: implement once we support more complex types ], )] pub mod txe_oracles; diff --git a/noir-projects/aztec-nr/aztec/src/test/helpers/tagging_secret_strategy.nr b/noir-projects/aztec-nr/aztec/src/test/helpers/tagging_secret_strategy.nr index a78758b07fcd..a4f7b52d91ba 100644 --- a/noir-projects/aztec-nr/aztec/src/test/helpers/tagging_secret_strategy.nr +++ b/noir-projects/aztec-nr/aztec/src/test/helpers/tagging_secret_strategy.nr @@ -48,17 +48,6 @@ impl TaggingSecretStrategy { } } -pub struct TaggingSecretStrategiesByDeliveryMode { - pub unconstrained_strategy: TaggingSecretStrategy, - pub constrained_strategy: TaggingSecretStrategy, -} - -impl TaggingSecretStrategiesByDeliveryMode { - pub fn new(unconstrained_strategy: TaggingSecretStrategy, constrained_strategy: TaggingSecretStrategy) -> Self { - Self { unconstrained_strategy, constrained_strategy } - } -} - impl Deserialize for TaggingSecretStrategy { let N: u32 = 3; diff --git a/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr b/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr index 69ec463db50a..82f04996be3c 100644 --- a/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr +++ b/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr @@ -13,6 +13,7 @@ use crate::{ event::{event_interface::EventInterface, EventMessage}, hash::{compute_secret_hash, hash_args}, messages::{ + delivery::OnchainDeliveryMode, discovery::{ ComputeNoteHash, ComputeNoteNullifier, CustomMessageHandler, process_message::process_message_plaintext, }, @@ -23,11 +24,7 @@ use crate::{ }, note::{note_interface::{NoteHash, NoteType}, NoteMessage}, oracle::version::assert_compatible_oracle_version, - test::helpers::{ - tagging_secret_strategy::{TaggingSecretStrategiesByDeliveryMode, TaggingSecretStrategy}, - txe_oracles, - utils::ContractDeployment, - }, + test::helpers::{tagging_secret_strategy::TaggingSecretStrategy, txe_oracles, utils::ContractDeployment}, utils::array::subarray, }; @@ -96,14 +93,17 @@ pub struct TestEnvironment { /// ); /// ``` pub struct TestEnvironmentOptions { - tagging_secret_strategy: Option, - tagging_secret_strategies_by_delivery_mode: Option, + unconstrained_tagging_secret_strategy: Option, + constrained_tagging_secret_strategy: Option, } impl TestEnvironmentOptions { /// Creates a new `TestEnvironmentOptions` with default values. pub fn new() -> Self { - Self { tagging_secret_strategy: Option::none(), tagging_secret_strategies_by_delivery_mode: Option::none() } + Self { + unconstrained_tagging_secret_strategy: Option::none(), + constrained_tagging_secret_strategy: Option::none(), + } } /// Sets the [`TaggingSecretStrategy`] the wallet reports through the @@ -112,8 +112,8 @@ impl TestEnvironmentOptions { /// /// If not set, the wallet hook is left unconfigured and the private execution environment applies its own default. pub fn with_tagging_secret_strategy(&mut self, strategy: TaggingSecretStrategy) -> Self { - self.tagging_secret_strategy = Option::some(strategy); - self.tagging_secret_strategies_by_delivery_mode = Option::none(); + self.unconstrained_tagging_secret_strategy = Option::some(strategy); + self.constrained_tagging_secret_strategy = Option::some(strategy); *self } @@ -124,11 +124,8 @@ impl TestEnvironmentOptions { unconstrained_strategy: TaggingSecretStrategy, constrained_strategy: TaggingSecretStrategy, ) -> Self { - self.tagging_secret_strategy = Option::none(); - self.tagging_secret_strategies_by_delivery_mode = Option::some(TaggingSecretStrategiesByDeliveryMode::new( - unconstrained_strategy, - constrained_strategy, - )); + self.unconstrained_tagging_secret_strategy = Option::some(unconstrained_strategy); + self.constrained_tagging_secret_strategy = Option::some(constrained_strategy); *self } } @@ -638,16 +635,14 @@ impl TestEnvironment { assert_compatible_oracle_version(); txe_oracles::assert_compatible_txe_oracle_version(); - // Forward the configured strategy to the wallet. - if options.tagging_secret_strategies_by_delivery_mode.is_some() { - let strategies = options.tagging_secret_strategies_by_delivery_mode.unwrap_unchecked(); - txe_oracles::set_tagging_secret_strategies_by_delivery_mode( - strategies.unconstrained_strategy, - strategies.constrained_strategy, - ); - } else { - txe_oracles::set_tagging_secret_strategy(options.tagging_secret_strategy); - } + txe_oracles::set_tagging_secret_strategy( + OnchainDeliveryMode::onchain_unconstrained(), + options.unconstrained_tagging_secret_strategy, + ); + txe_oracles::set_tagging_secret_strategy( + OnchainDeliveryMode::onchain_constrained(), + options.constrained_tagging_secret_strategy, + ); Self { // Use an offset to avoid secret collision with account secrets. Without this, when diff --git a/noir-projects/aztec-nr/aztec/src/test/helpers/txe_oracles.nr b/noir-projects/aztec-nr/aztec/src/test/helpers/txe_oracles.nr index 8f93b10d5c05..682ca216d05e 100644 --- a/noir-projects/aztec-nr/aztec/src/test/helpers/txe_oracles.nr +++ b/noir-projects/aztec-nr/aztec/src/test/helpers/txe_oracles.nr @@ -1,7 +1,7 @@ use crate::{ context::inputs::PrivateContextInputs, event::EventSelector, - messages::encoding::MESSAGE_CIPHERTEXT_LEN, + messages::{delivery::OnchainDeliveryMode, encoding::MESSAGE_CIPHERTEXT_LEN}, test::helpers::{tagging_secret_strategy::TaggingSecretStrategy, utils::TestAccount}, }; @@ -21,8 +21,8 @@ use crate::protocol::{ /// [`oracle::version`](crate::oracle::version), which covers oracles used during contract execution by PXE. /// /// The TypeScript counterparts are in `yarn-project/txe/src/txe_oracle_version.ts`. -pub global TXE_ORACLE_VERSION_MAJOR: Field = 2; -pub global TXE_ORACLE_VERSION_MINOR: Field = 3; +pub global TXE_ORACLE_VERSION_MAJOR: Field = 3; +pub global TXE_ORACLE_VERSION_MINOR: Field = 0; /// Asserts that the TXE oracle interface version is compatible. pub unconstrained fn assert_compatible_txe_oracle_version() { @@ -266,12 +266,9 @@ pub unconstrained fn send_l1_to_l2_message( ) -> Field {} #[oracle(aztec_txe_setTaggingSecretStrategy)] -pub unconstrained fn set_tagging_secret_strategy(strategy: Option) {} - -#[oracle(aztec_txe_setTaggingSecretStrategiesByDeliveryMode)] -pub unconstrained fn set_tagging_secret_strategies_by_delivery_mode( - unconstrained_strategy: TaggingSecretStrategy, - constrained_strategy: TaggingSecretStrategy, +pub unconstrained fn set_tagging_secret_strategy( + delivery_mode: OnchainDeliveryMode, + strategy: Option, ) {} #[oracle(aztec_txe_privateCallNewFlow)] diff --git a/yarn-project/txe/src/oracle/interfaces.ts b/yarn-project/txe/src/oracle/interfaces.ts index 939b86e76d18..c8e2fe9d7ba4 100644 --- a/yarn-project/txe/src/oracle/interfaces.ts +++ b/yarn-project/txe/src/oracle/interfaces.ts @@ -8,7 +8,7 @@ import type { Option } from '@aztec/pxe/simulator'; import type { EventSelector, FunctionSelector } from '@aztec/stdlib/abi'; import type { AztecAddress } from '@aztec/stdlib/aztec-address'; import type { GasSettings } from '@aztec/stdlib/gas'; -import type { PrivateLog } from '@aztec/stdlib/logs'; +import type { AppTaggingSecretKind, PrivateLog } from '@aztec/stdlib/logs'; import type { UInt64 } from '@aztec/stdlib/types'; // These interfaces complement the ones defined in PXE, and combined with those contain the full list of oracles used by @@ -73,11 +73,7 @@ export interface ITxeExecutionOracle { addAccount(secret: Fr): Promise; addAuthWitness(address: AztecAddress, messageHash: Fr): Promise; sendL1ToL2Message(content: Fr, secretHash: Fr, sender: EthAddress, recipient: AztecAddress): Promise; - setTaggingSecretStrategy(strategy: Option): void; - setTaggingSecretStrategiesByDeliveryMode( - unconstrained: TaggingSecretStrategy, - constrained: TaggingSecretStrategy, - ): void; + setTaggingSecretStrategy(deliveryMode: AppTaggingSecretKind, strategy: Option): void; getLastBlockTimestamp(): Promise; getLastTxEffects(): Promise<{ txHash: TxHash; diff --git a/yarn-project/txe/src/oracle/tagging_secret_strategy.test.ts b/yarn-project/txe/src/oracle/tagging_secret_strategy.test.ts index f7bf48e09a36..b708f2e693cd 100644 --- a/yarn-project/txe/src/oracle/tagging_secret_strategy.test.ts +++ b/yarn-project/txe/src/oracle/tagging_secret_strategy.test.ts @@ -1,5 +1,6 @@ import { Fr } from '@aztec/foundation/curves/bn254'; import { Point } from '@aztec/foundation/curves/grumpkin'; +import type { TaggingSecretStrategy } from '@aztec/pxe/server'; import { AztecAddress } from '@aztec/stdlib/aztec-address'; import { AppTaggingSecretKind } from '@aztec/stdlib/logs'; @@ -8,40 +9,60 @@ import { callTxeHandler } from './txe_oracle_registry.js'; describe('makeResolveTaggingSecretStrategyHook', () => { it('returns undefined when no TXE strategy is configured', () => { - expect(makeResolveTaggingSecretStrategyHook(undefined)).toBeUndefined(); + expect(makeResolveTaggingSecretStrategyHook(new Map())).toBeUndefined(); }); it('returns a static strategy for every delivery mode', async () => { const strategy = { type: 'non-interactive-handshake' as const }; - const hook = makeResolveTaggingSecretStrategyHook(strategy); + const hook = makeResolveTaggingSecretStrategyHook( + new Map([ + [AppTaggingSecretKind.UNCONSTRAINED, strategy], + [AppTaggingSecretKind.CONSTRAINED, strategy], + ]), + ); await expect(hook?.(makeRequest(AppTaggingSecretKind.UNCONSTRAINED))).resolves.toBe(strategy); await expect(hook?.(makeRequest(AppTaggingSecretKind.CONSTRAINED))).resolves.toBe(strategy); }); it('selects a strategy by delivery mode', async () => { - const unconstrained = { type: 'address-derived' as const }; - const constrained = { type: 'arbitrary-secret' as const, secret: await Point.random() }; - const hook = makeResolveTaggingSecretStrategyHook({ - type: 'by-delivery-mode', - unconstrained, - constrained, - }); + const unconstrained: TaggingSecretStrategy = { type: 'address-derived' }; + const constrained: TaggingSecretStrategy = { type: 'arbitrary-secret', secret: await Point.random() }; + const hook = makeResolveTaggingSecretStrategyHook( + new Map([ + [AppTaggingSecretKind.UNCONSTRAINED, unconstrained], + [AppTaggingSecretKind.CONSTRAINED, constrained], + ]), + ); await expect(hook?.(makeRequest(AppTaggingSecretKind.UNCONSTRAINED))).resolves.toBe(unconstrained); await expect(hook?.(makeRequest(AppTaggingSecretKind.CONSTRAINED))).resolves.toBe(constrained); }); + it('defaults an unset mode to PXE default strategy when another mode is configured', async () => { + const unconstrained = { type: 'address-derived' as const }; + const hook = makeResolveTaggingSecretStrategyHook(new Map([[AppTaggingSecretKind.UNCONSTRAINED, unconstrained]])); + + await expect(hook?.(makeRequest(AppTaggingSecretKind.UNCONSTRAINED))).resolves.toBe(unconstrained); + await expect(hook?.(makeRequest(AppTaggingSecretKind.CONSTRAINED))).resolves.toEqual({ + type: 'non-interactive-handshake', + }); + }); + it('deserializes the mode-aware TXE oracle setter', async () => { const received = await callTxeHandler({ - oracle: 'aztec_txe_setTaggingSecretStrategiesByDeliveryMode', - inputs: [field(2), field(5), field(6), field(1), field(0), field(0)], - handler: ([unconstrained, constrained]) => { - expect(unconstrained.type).toBe('arbitrary-secret'); + oracle: 'aztec_txe_setTaggingSecretStrategy', + inputs: [field(2), field(1), field(2), field(5), field(6)], + handler: ([deliveryMode, strategy]) => { + expect(deliveryMode).toBe(AppTaggingSecretKind.UNCONSTRAINED); + expect(strategy.isSome()).toBe(true); + if (!strategy.isSome()) { + throw new Error('Expected tagging secret strategy'); + } + expect(strategy.value.type).toBe('arbitrary-secret'); expect( - unconstrained.type === 'arbitrary-secret' && unconstrained.secret.equals(new Point(new Fr(5), new Fr(6))), + strategy.value.type === 'arbitrary-secret' && strategy.value.secret.equals(new Point(new Fr(5), new Fr(6))), ).toBeTruthy(); - expect(constrained).toEqual({ type: 'non-interactive-handshake' }); }, }); diff --git a/yarn-project/txe/src/oracle/tagging_secret_strategy.ts b/yarn-project/txe/src/oracle/tagging_secret_strategy.ts index 70a1f0794312..1febbedece2d 100644 --- a/yarn-project/txe/src/oracle/tagging_secret_strategy.ts +++ b/yarn-project/txe/src/oracle/tagging_secret_strategy.ts @@ -1,27 +1,14 @@ import type { ResolveTaggingSecretStrategy, TaggingSecretStrategy } from '@aztec/pxe/server'; import { AppTaggingSecretKind } from '@aztec/stdlib/logs'; -export type TXETaggingSecretStrategy = - | TaggingSecretStrategy - | { - type: 'by-delivery-mode'; - unconstrained: TaggingSecretStrategy; - constrained: TaggingSecretStrategy; - }; +export type TXETaggingSecretStrategies = Map; export function makeResolveTaggingSecretStrategyHook( - strategy: TXETaggingSecretStrategy | undefined, + strategies: TXETaggingSecretStrategies, ): ResolveTaggingSecretStrategy | undefined { - if (strategy === undefined) { + if (strategies.size === 0) { return undefined; } - if (strategy.type !== 'by-delivery-mode') { - return () => Promise.resolve(strategy); - } - - return ({ deliveryMode }) => - Promise.resolve( - deliveryMode === AppTaggingSecretKind.UNCONSTRAINED ? strategy.unconstrained : strategy.constrained, - ); + return ({ deliveryMode }) => Promise.resolve(strategies.get(deliveryMode) ?? { type: 'non-interactive-handshake' }); } diff --git a/yarn-project/txe/src/oracle/txe_oracle_registry.ts b/yarn-project/txe/src/oracle/txe_oracle_registry.ts index 95ec203d29f4..ffdaf6aca0a0 100644 --- a/yarn-project/txe/src/oracle/txe_oracle_registry.ts +++ b/yarn-project/txe/src/oracle/txe_oracle_registry.ts @@ -17,6 +17,7 @@ import { BIGINT, BLOCK_NUMBER, BOOL, + DELIVERY_MODE, ETH_ADDRESS, FIELD, FIXED_ARRAY, @@ -316,13 +317,9 @@ export const TXE_ORACLE_REGISTRY = { }), aztec_txe_setTaggingSecretStrategy: makeEntry({ - params: [{ name: 'strategy', type: OPTION(TAGGING_SECRET_STRATEGY) }], - }), - - aztec_txe_setTaggingSecretStrategiesByDeliveryMode: makeEntry({ params: [ - { name: 'unconstrained', type: TAGGING_SECRET_STRATEGY }, - { name: 'constrained', type: TAGGING_SECRET_STRATEGY }, + { name: 'deliveryMode', type: DELIVERY_MODE }, + { name: 'strategy', type: OPTION(TAGGING_SECRET_STRATEGY) }, ], }), diff --git a/yarn-project/txe/src/oracle/txe_oracle_top_level_context.ts b/yarn-project/txe/src/oracle/txe_oracle_top_level_context.ts index 5d3972df7597..7c9b0f57ad52 100644 --- a/yarn-project/txe/src/oracle/txe_oracle_top_level_context.ts +++ b/yarn-project/txe/src/oracle/txe_oracle_top_level_context.ts @@ -73,6 +73,7 @@ import { } from '@aztec/stdlib/kernel'; import { deriveKeys, hashPublicKey } from '@aztec/stdlib/keys'; import type { PrivateLog } from '@aztec/stdlib/logs'; +import { AppTaggingSecretKind } from '@aztec/stdlib/logs'; import { L1Actor, L1ToL2Message, L2Actor } from '@aztec/stdlib/messaging'; import { ChonkProof } from '@aztec/stdlib/proofs'; import { makeGlobalVariables } from '@aztec/stdlib/testing'; @@ -99,7 +100,7 @@ import type { TXEAccountStore } from '../utils/txe_account_store.js'; import type { TXEArtifactResolver } from '../utils/txe_artifact_resolver.js'; import { TXEPublicContractDataSource } from '../utils/txe_public_contract_data_source.js'; import type { ITxeExecutionOracle } from './interfaces.js'; -import { type TXETaggingSecretStrategy, makeResolveTaggingSecretStrategyHook } from './tagging_secret_strategy.js'; +import { type TXETaggingSecretStrategies, makeResolveTaggingSecretStrategyHook } from './tagging_secret_strategy.js'; export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracle { isMisc = true as const; @@ -124,7 +125,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl private version: Fr, private chainId: Fr, private authwits: Map, - private taggingSecretStrategy: TXETaggingSecretStrategy | undefined, + private taggingSecretStrategies: TXETaggingSecretStrategies, private readonly artifactResolver: TXEArtifactResolver, private readonly rootPath: string, private readonly packageName: string, @@ -357,15 +358,12 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl this.authwits.set(authWitness.requestHash.toString(), authWitness); } - setTaggingSecretStrategy(strategy: Option): void { - this.taggingSecretStrategy = strategy.value; - } - - setTaggingSecretStrategiesByDeliveryMode( - unconstrained: TaggingSecretStrategy, - constrained: TaggingSecretStrategy, - ): void { - this.taggingSecretStrategy = { type: 'by-delivery-mode', unconstrained, constrained }; + setTaggingSecretStrategy(deliveryMode: AppTaggingSecretKind, strategy: Option): void { + if (strategy.isSome()) { + this.taggingSecretStrategies.set(deliveryMode, strategy.value); + } else { + this.taggingSecretStrategies.delete(deliveryMode); + } } async sendL1ToL2Message(content: Fr, secretHash: Fr, sender: EthAddress, recipient: AztecAddress): Promise { @@ -479,7 +477,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl const simulator = new WASMSimulator(); const transientArrayService = new TransientArrayService(); - const taggingSecretStrategy = this.taggingSecretStrategy; + const taggingSecretStrategies = this.taggingSecretStrategies; const privateExecutionOracle = new PrivateExecutionOracle({ argsHash, txContext, @@ -520,7 +518,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl ), // Only configure the hook when a strategy was explicitly set, so that otherwise the default tagging secret // strategy is exercised. - resolveTaggingSecretStrategy: makeResolveTaggingSecretStrategyHook(taggingSecretStrategy), + resolveTaggingSecretStrategy: makeResolveTaggingSecretStrategyHook(taggingSecretStrategies), }), transientArrayService, }); @@ -972,9 +970,9 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl } } - close(): [bigint, Map, TXETaggingSecretStrategy | undefined] { + close(): [bigint, Map, TXETaggingSecretStrategies] { this.logger.debug('Exiting Top Level Context'); - return [this.nextBlockTimestamp, this.authwits, this.taggingSecretStrategy]; + return [this.nextBlockTimestamp, this.authwits, this.taggingSecretStrategies]; } private async getLastBlockNumber(): Promise { diff --git a/yarn-project/txe/src/oracle/txe_oracle_version.ts b/yarn-project/txe/src/oracle/txe_oracle_version.ts index f50649d09b47..d87fcad45704 100644 --- a/yarn-project/txe/src/oracle/txe_oracle_version.ts +++ b/yarn-project/txe/src/oracle/txe_oracle_version.ts @@ -5,8 +5,8 @@ * * The Noir counterparts are in `noir-projects/aztec-nr/aztec/src/test/helpers/txe_oracles.nr`. */ -export const TXE_ORACLE_VERSION_MAJOR = 2; -export const TXE_ORACLE_VERSION_MINOR = 3; +export const TXE_ORACLE_VERSION_MAJOR = 3; +export const TXE_ORACLE_VERSION_MINOR = 0; /** * This hash is computed from the TXE oracle interfaces (IAvmExecutionOracle and ITxeExecutionOracle) and is used to @@ -14,4 +14,4 @@ export const TXE_ORACLE_VERSION_MINOR = 3; * - TXE_ORACLE_VERSION_MAJOR (and reset MINOR to 0) for breaking changes, or * - TXE_ORACLE_VERSION_MINOR for additive changes (new oracle method added). */ -export const TXE_ORACLE_INTERFACE_HASH = '67af6782f0f848a81508b44d619f01e6492b5883b30ed97cbed5bcf8bad00529'; +export const TXE_ORACLE_INTERFACE_HASH = 'c611d6d0d9709755207719864a630a8bc7b8e53efa1be7f86088fcea4b48f1b3'; diff --git a/yarn-project/txe/src/rpc_translator.ts b/yarn-project/txe/src/rpc_translator.ts index 7d7ef663eaf5..ce3f01f15f4e 100644 --- a/yarn-project/txe/src/rpc_translator.ts +++ b/yarn-project/txe/src/rpc_translator.ts @@ -225,17 +225,7 @@ export class RPCTranslator { return callTxeHandler({ oracle: 'aztec_txe_setTaggingSecretStrategy', inputs, - handler: ([strategy]) => this.handlerAsTxe().setTaggingSecretStrategy(strategy), - }); - } - - // eslint-disable-next-line camelcase - aztec_txe_setTaggingSecretStrategiesByDeliveryMode(...inputs: ForeignCallArgs) { - return callTxeHandler({ - oracle: 'aztec_txe_setTaggingSecretStrategiesByDeliveryMode', - inputs, - handler: ([unconstrained, constrained]) => - this.handlerAsTxe().setTaggingSecretStrategiesByDeliveryMode(unconstrained, constrained), + handler: ([deliveryMode, strategy]) => this.handlerAsTxe().setTaggingSecretStrategy(deliveryMode, strategy), }); } diff --git a/yarn-project/txe/src/txe_session.ts b/yarn-project/txe/src/txe_session.ts index 7fb8d67a4b8c..27cfe78b388d 100644 --- a/yarn-project/txe/src/txe_session.ts +++ b/yarn-project/txe/src/txe_session.ts @@ -58,7 +58,7 @@ import { z } from 'zod'; import { DEFAULT_ADDRESS, MAX_OFFCHAIN_EFFECTS_PER_TXE_QUERY, MAX_OFFCHAIN_EFFECT_LEN } from './constants.js'; import type { IAvmExecutionOracle, ITxeExecutionOracle } from './oracle/interfaces.js'; import { - type TXETaggingSecretStrategy, + type TXETaggingSecretStrategies, makeResolveTaggingSecretStrategyHook, } from './oracle/tagging_secret_strategy.js'; import { TXEOraclePublicContext } from './oracle/txe_oracle_public_context.js'; @@ -240,7 +240,7 @@ function emptyLastCallState(): LastCallState { export class TXESession implements TXESessionStateHandler { private state: SessionState = { name: 'TOP_LEVEL' }; private authwits: Map = new Map(); - private taggingSecretStrategy: TXETaggingSecretStrategy | undefined = undefined; + private taggingSecretStrategies: TXETaggingSecretStrategies = new Map(); private lastCallInfo: LastCallState = emptyLastCallState(); private txeOracleVersion: { major: number; minor: number } | undefined; @@ -360,7 +360,7 @@ export class TXESession implements TXESessionStateHandler { version, chainId, new Map(), - undefined, + new Map(), artifactResolver, rootPath, packageName, @@ -690,7 +690,7 @@ export class TXESession implements TXESessionStateHandler { this.version, this.chainId, this.authwits, - this.taggingSecretStrategy, + this.taggingSecretStrategies, this.artifactResolver, this.rootPath, this.packageName, @@ -740,7 +740,7 @@ export class TXESession implements TXESessionStateHandler { this.stateMachine.contractClassService, anchorBlock!, ); - const taggingSecretStrategy = this.taggingSecretStrategy; + const taggingSecretStrategies = this.taggingSecretStrategies; this.oracleHandler = new TXEPrivateExecutionOracle({ argsHash: Fr.ZERO, txContext: new TxContext(this.chainId, this.version, gasSettings), @@ -770,7 +770,7 @@ export class TXESession implements TXESessionStateHandler { txResolver: this.stateMachine.txResolver, simulator: new WASMSimulator(), hooks: composeHooks({ - resolveTaggingSecretStrategy: makeResolveTaggingSecretStrategyHook(taggingSecretStrategy), + resolveTaggingSecretStrategy: makeResolveTaggingSecretStrategyHook(taggingSecretStrategies), }), transientArrayService, }); @@ -899,7 +899,7 @@ export class TXESession implements TXESessionStateHandler { // TODO: persisting authwits this way is quite unfortunate: they create a temporary utility context that would // otherwise reset them, so we'd not be able to pass more than one per execution. Ideally authwits would be passed // alongside a contract call instead of pre-seeded. - [this.nextBlockTimestamp, this.authwits, this.taggingSecretStrategy] = ( + [this.nextBlockTimestamp, this.authwits, this.taggingSecretStrategies] = ( this.oracleHandler as TXEOracleTopLevelContext ).close(); } From b8a3a01a96219c41d3311d69c5b7ff588cd4957a Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Mon, 6 Jul 2026 20:02:12 -0400 Subject: [PATCH 03/21] refactor(txe): per-mode tagging secret strategy options Review cleanup for the unconstrained delivery coverage: - TestEnvironmentOptions now exposes a keyed with_tag_secret_strategy(mode, strategy) setter plus with_tag_secret_strategy_all_modes; a mode left unset falls back to the PXE default (non-interactive handshake), matching a hook that hardcodes the default for modes it does not customize. - Share DEFAULT_TAGGING_SECRET_STRATEGY between PXE and the TXE hook fallback. - Add compute_directional_app_secret so tests derive expected secrets instead of inlining the poseidon2 derivation. - Merge the per-mode next-index test wrappers into next_index_for_secret(secret, mode). - Drop dead locals, reuse toSingle in the oracle unit test, reword the test contract module doc. --- .../test/helpers/tagging_secret_strategy.nr | 21 +++++- .../src/test/helpers/test_environment.nr | 42 ++++++------ .../test/resolve_tagging_strategy.nr | 43 ++++++++---- .../src/main.nr | 16 ++--- .../src/test.nr | 66 +++++++++++++------ .../oracle/private_execution_oracle.ts | 10 +-- yarn-project/pxe/src/hooks/index.ts | 1 + .../hooks/resolve_tagging_secret_strategy.ts | 3 + .../oracle/tagging_secret_strategy.test.ts | 7 +- .../txe/src/oracle/tagging_secret_strategy.ts | 17 ++++- .../oracle/txe_oracle_top_level_context.ts | 3 +- yarn-project/txe/src/txe_session.ts | 3 +- 12 files changed, 148 insertions(+), 84 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/test/helpers/tagging_secret_strategy.nr b/noir-projects/aztec-nr/aztec/src/test/helpers/tagging_secret_strategy.nr index a4f7b52d91ba..71c6bfeb8f41 100644 --- a/noir-projects/aztec-nr/aztec/src/test/helpers/tagging_secret_strategy.nr +++ b/noir-projects/aztec-nr/aztec/src/test/helpers/tagging_secret_strategy.nr @@ -1,4 +1,11 @@ -use crate::protocol::{point::EmbeddedCurvePoint, traits::{Deserialize, Serialize}, utils::reader::Reader}; +use crate::keys::ecdh_shared_secret::compute_app_siloed_shared_secret; +use crate::protocol::{ + address::AztecAddress, + hash::poseidon2_hash, + point::EmbeddedCurvePoint, + traits::{Deserialize, Serialize, ToField}, + utils::reader::Reader, +}; global NON_INTERACTIVE_HANDSHAKE: u8 = 1; global ARBITRARY_SECRET: u8 = 2; @@ -6,7 +13,7 @@ global ADDRESS_DERIVED: u8 = 3; /// How a message's tagging secret is chosen: the wallet's strategy. /// -/// This type only exists for tests: a Noir test sets it via `with_tagging_secret_strategy` on +/// This type only exists for tests: a Noir test sets it via `with_tag_secret_strategy` on /// [`TestEnvironmentOptions`](crate::test::helpers::test_environment::TestEnvironmentOptions). /// Production wallets express the strategy in PXE; the Noir path only consumes the resolved /// [`ResolvedTaggingStrategy`](crate::messages::delivery::ResolvedTaggingStrategy). @@ -48,6 +55,16 @@ impl TaggingSecretStrategy { } } +/// Computes the directional tagging secret PXE derives from an arbitrary shared secret point: the point is app-siloed +/// and the recipient is folded in for direction. Mirrors `AppTaggingSecret.computeDirectional` on the PXE side; tests +/// use it to derive the secret they expect an [`arbitrary_secret`](TaggingSecretStrategy::arbitrary_secret) strategy +/// to produce. +pub fn compute_directional_app_secret(secret: EmbeddedCurvePoint, app: AztecAddress, recipient: AztecAddress) -> Field { + poseidon2_hash( + [compute_app_siloed_shared_secret(secret, app), recipient.to_field()], + ) +} + impl Deserialize for TaggingSecretStrategy { let N: u32 = 3; diff --git a/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr b/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr index 82f04996be3c..12702415b33d 100644 --- a/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr +++ b/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr @@ -89,7 +89,7 @@ pub struct TestEnvironment { /// methods setting each value, e.g.: /// ```noir /// let env = TestEnvironment::new_opts( -/// TestEnvironmentOptions::new().with_tagging_secret_strategy(TaggingSecretStrategy::non_interactive_handshake()), +/// TestEnvironmentOptions::new().with_tag_secret_strategy_all_modes(strategy), /// ); /// ``` pub struct TestEnvironmentOptions { @@ -106,27 +106,32 @@ impl TestEnvironmentOptions { } } - /// Sets the [`TaggingSecretStrategy`] the wallet reports through the + /// Sets the [`TaggingSecretStrategy`] the wallet reports for `mode` through the /// [`resolve_tagging_strategy`](crate::oracle::resolve_tagging_strategy) oracle, affecting message delivery in /// private executions. /// - /// If not set, the wallet hook is left unconfigured and the private execution environment applies its own default. - pub fn with_tagging_secret_strategy(&mut self, strategy: TaggingSecretStrategy) -> Self { - self.unconstrained_tagging_secret_strategy = Option::some(strategy); - self.constrained_tagging_secret_strategy = Option::some(strategy); + /// If no strategy is set for either mode, the wallet hook is left unconfigured and the private execution + /// environment applies its own default. A mode left unset while the other is configured falls back to the default + /// [`TaggingSecretStrategy::non_interactive_handshake`], the strategy a PXE `resolveTaggingSecretStrategy` hook + /// typically hardcodes for modes it does not customize. + pub fn with_tag_secret_strategy(&mut self, mode: M, strategy: TaggingSecretStrategy) -> Self + where + M: Into, + { + if mode.into() == OnchainDeliveryMode::onchain_unconstrained() { + self.unconstrained_tagging_secret_strategy = Option::some(strategy); + } else { + self.constrained_tagging_secret_strategy = Option::some(strategy); + } *self } - /// Sets the [`TaggingSecretStrategy`] the wallet reports by delivery mode through the - /// [`resolve_tagging_strategy`](crate::oracle::resolve_tagging_strategy) oracle. - pub fn with_tagging_secret_strategies_by_delivery_mode( - &mut self, - unconstrained_strategy: TaggingSecretStrategy, - constrained_strategy: TaggingSecretStrategy, - ) -> Self { - self.unconstrained_tagging_secret_strategy = Option::some(unconstrained_strategy); - self.constrained_tagging_secret_strategy = Option::some(constrained_strategy); - *self + /// Sets `strategy` for both delivery modes: the equivalent of a PXE `resolveTaggingSecretStrategy` hook that + /// ignores the request's mode. See [`with_tag_secret_strategy`](Self::with_tag_secret_strategy) to configure a + /// single mode. + pub fn with_tag_secret_strategy_all_modes(&mut self, strategy: TaggingSecretStrategy) -> Self { + let _ = self.with_tag_secret_strategy(OnchainDeliveryMode::onchain_unconstrained(), strategy); + self.with_tag_secret_strategy(OnchainDeliveryMode::onchain_constrained(), strategy) } } @@ -625,10 +630,9 @@ impl TestEnvironment { /// /// ### Sample usage /// ```noir + /// let strategy = TaggingSecretStrategy::non_interactive_handshake(); /// let env = TestEnvironment::new_opts( - /// TestEnvironmentOptions::new().with_tagging_secret_strategy( - /// TaggingSecretStrategy::non_interactive_handshake(), - /// ), + /// TestEnvironmentOptions::new().with_tag_secret_strategy_all_modes(strategy), /// ); /// ``` pub unconstrained fn new_opts(options: TestEnvironmentOptions) -> Self { diff --git a/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment/test/resolve_tagging_strategy.nr b/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment/test/resolve_tagging_strategy.nr index 2d08d3e96664..63090e1018fd 100644 --- a/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment/test/resolve_tagging_strategy.nr +++ b/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment/test/resolve_tagging_strategy.nr @@ -1,14 +1,35 @@ use crate::{ - keys::ecdh_shared_secret::compute_app_siloed_shared_secret, messages::delivery::{OnchainDeliveryMode, ResolvedTaggingStrategy}, oracle::resolve_tagging_strategy::resolve_tagging_strategy, - protocol::{address::AztecAddress, hash::poseidon2_hash, point::EmbeddedCurvePoint, traits::{FromField, ToField}}, + protocol::{address::AztecAddress, point::EmbeddedCurvePoint, traits::FromField}, test::helpers::{ - tagging_secret_strategy::TaggingSecretStrategy, + tagging_secret_strategy::{compute_directional_app_secret, TaggingSecretStrategy}, test_environment::{TestEnvironment, TestEnvironmentOptions}, }, }; +#[test] +unconstrained fn an_unset_mode_falls_back_to_the_default_strategy() { + // Only the unconstrained mode is configured: the constrained mode falls back to the default non-interactive + // handshake strategy. + let mut env = TestEnvironment::new_opts(TestEnvironmentOptions::new().with_tag_secret_strategy( + OnchainDeliveryMode::onchain_unconstrained(), + TaggingSecretStrategy::arbitrary_secret(EmbeddedCurvePoint { x: 7, y: 11 }), + )); + // The hook path looks up the executing contract's class ID, so run in a deployed contract's context. + let app = env.create_contract_account(); + let recipient = env.create_light_account(); + + env.private_context_at(app, |_| { + let resolved = resolve_tagging_strategy( + AztecAddress::from_field(1), + recipient, + OnchainDeliveryMode::onchain_constrained(), + ); + assert_eq(resolved, ResolvedTaggingStrategy::non_interactive_handshake()); + }); +} + #[test] unconstrained fn defaults_an_unconstrained_self_send_to_the_address_derived_shared_secret() { let mut env = TestEnvironment::new(); @@ -69,7 +90,7 @@ unconstrained fn defaults_constrained_delivery_to_a_non_interactive_handshake() #[test] unconstrained fn constrained_delivery_succeeds_with_a_configured_strategy() { - let mut env = TestEnvironment::new_opts(TestEnvironmentOptions::new().with_tagging_secret_strategy( + let mut env = TestEnvironment::new_opts(TestEnvironmentOptions::new().with_tag_secret_strategy_all_modes( TaggingSecretStrategy::non_interactive_handshake(), )); // The hook path looks up the executing contract's class ID, so run in a deployed contract's context. @@ -88,7 +109,7 @@ unconstrained fn constrained_delivery_succeeds_with_a_configured_strategy() { #[test] unconstrained fn applies_the_strategy_set_in_the_options() { - let mut env = TestEnvironment::new_opts(TestEnvironmentOptions::new().with_tagging_secret_strategy( + let mut env = TestEnvironment::new_opts(TestEnvironmentOptions::new().with_tag_secret_strategy_all_modes( TaggingSecretStrategy::arbitrary_secret(EmbeddedCurvePoint { x: 7, y: 11 }), )); // The hook path looks up the executing contract's class ID, so run in a deployed contract's context. @@ -108,7 +129,7 @@ unconstrained fn applies_the_strategy_set_in_the_options() { #[test] unconstrained fn app_silos_an_arbitrary_secret_point() { let point = EmbeddedCurvePoint { x: 7, y: 11 }; - let mut env = TestEnvironment::new_opts(TestEnvironmentOptions::new().with_tagging_secret_strategy( + let mut env = TestEnvironment::new_opts(TestEnvironmentOptions::new().with_tag_secret_strategy_all_modes( TaggingSecretStrategy::arbitrary_secret(point), )); // The hook path looks up the executing contract's class ID, so run in a deployed contract's context. @@ -122,10 +143,7 @@ unconstrained fn app_silos_an_arbitrary_secret_point() { OnchainDeliveryMode::onchain_unconstrained(), ); - // PXE app-silos the raw point before handing it over, then folds in the recipient for direction. This mirrors - // `AppTaggingSecret.computeDirectional` on the PXE side. - let app_secret = compute_app_siloed_shared_secret(point, app); - let expected_secret = poseidon2_hash([app_secret, recipient.to_field()]); + let expected_secret = compute_directional_app_secret(point, app, recipient); assert_eq(resolved, ResolvedTaggingStrategy::unconstrained_secret(expected_secret)); }); } @@ -133,7 +151,7 @@ unconstrained fn app_silos_an_arbitrary_secret_point() { #[test] unconstrained fn overrides_a_configured_arbitrary_secret_for_an_unconstrained_self_send() { let point = EmbeddedCurvePoint { x: 7, y: 11 }; - let mut env = TestEnvironment::new_opts(TestEnvironmentOptions::new().with_tagging_secret_strategy( + let mut env = TestEnvironment::new_opts(TestEnvironmentOptions::new().with_tag_secret_strategy_all_modes( TaggingSecretStrategy::arbitrary_secret(point), )); let app = env.create_contract_account(); @@ -149,8 +167,7 @@ unconstrained fn overrides_a_configured_arbitrary_secret_for_an_unconstrained_se // A self-send forces an address-derived secret even with an arbitrary secret configured. The resolved // secret is unconstrained, but not the app-siloed arbitrary point the configured secret would have produced. - let app_secret = compute_app_siloed_shared_secret(point, app); - let arbitrary_secret = poseidon2_hash([app_secret, recipient.to_field()]); + let arbitrary_secret = compute_directional_app_secret(point, app, recipient); assert(resolved.is_unconstrained_secret()); assert(resolved != ResolvedTaggingStrategy::unconstrained_secret(arbitrary_secret)); }); diff --git a/noir-projects/noir-contracts/contracts/test/constrained_delivery_test_contract/src/main.nr b/noir-projects/noir-contracts/contracts/test/constrained_delivery_test_contract/src/main.nr index 6f316707f260..f3cd379f6e42 100644 --- a/noir-projects/noir-contracts/contracts/test/constrained_delivery_test_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/test/constrained_delivery_test_contract/src/main.nr @@ -1,4 +1,4 @@ -//! Thin wrappers around constrained delivery for TXE tests. +//! Thin wrappers around onchain message delivery for TXE tests. use aztec::macros::aztec; mod test; @@ -7,7 +7,7 @@ mod test; pub contract ConstrainedDeliveryTest { use aztec::{ macros::{events::event, functions::external, storage::storage}, - messages::delivery::MessageDelivery, + messages::delivery::{MessageDelivery, OnchainDeliveryMode}, note::note_viewer_options::NoteViewerOptions, oracle::notes::{get_app_tagging_secret, get_next_tagging_index}, protocol::{ @@ -34,18 +34,10 @@ pub contract ConstrainedDeliveryTest { } #[external("private")] - fn next_index_for_secret(secret: Field) -> u32 { + fn next_index_for_secret(secret: Field, mode: OnchainDeliveryMode) -> u32 { // Safety: test-only observation of the index the PXE hands out next for this secret. unsafe { - get_next_tagging_index(secret, MessageDelivery::onchain_constrained()) - } - } - - #[external("private")] - fn next_unconstrained_index_for_secret(secret: Field) -> u32 { - // Safety: test-only observation of the index the PXE hands out next for this secret. - unsafe { - get_next_tagging_index(secret, MessageDelivery::onchain_unconstrained()) + get_next_tagging_index(secret, mode) } } diff --git a/noir-projects/noir-contracts/contracts/test/constrained_delivery_test_contract/src/test.nr b/noir-projects/noir-contracts/contracts/test/constrained_delivery_test_contract/src/test.nr index 78d9bfb4bb64..91055784d233 100644 --- a/noir-projects/noir-contracts/contracts/test/constrained_delivery_test_contract/src/test.nr +++ b/noir-projects/noir-contracts/contracts/test/constrained_delivery_test_contract/src/test.nr @@ -7,11 +7,11 @@ use crate::ConstrainedDeliveryTest; use aztec::{ - keys::ecdh_shared_secret::compute_app_siloed_shared_secret, - protocol::{address::AztecAddress, hash::poseidon2_hash, point::EmbeddedCurvePoint, traits::{FromField, ToField}}, + messages::delivery::MessageDelivery, + protocol::{address::AztecAddress, point::EmbeddedCurvePoint, traits::FromField}, standard_addresses::STANDARD_HANDSHAKE_REGISTRY_ADDRESS, test::helpers::{ - tagging_secret_strategy::TaggingSecretStrategy, + tagging_secret_strategy::{compute_directional_app_secret, TaggingSecretStrategy}, test_environment::{ CallPrivateOptions, DeployOptions, ExecuteUtilityOptions, TestEnvironment, TestEnvironmentOptions, }, @@ -62,8 +62,9 @@ unconstrained fn arbitrary_secret_point() -> EmbeddedCurvePoint { #[test] unconstrained fn unconstrained_delivery_uses_configured_arbitrary_secret() { let point = arbitrary_secret_point(); - let options = - TestEnvironmentOptions::new().with_tagging_secret_strategy(TaggingSecretStrategy::arbitrary_secret(point)); + let options = TestEnvironmentOptions::new().with_tag_secret_strategy_all_modes( + TaggingSecretStrategy::arbitrary_secret(point), + ); let (env, registry_address, test_address, sender, _) = setup_opts(options); let recipient = external_recipient(); let test_contract = ConstrainedDeliveryTest::at(test_address); @@ -74,11 +75,15 @@ unconstrained fn unconstrained_delivery_uses_configured_arbitrary_secret() { let effects = txe_oracles::get_last_tx_effects(); assert_eq(effects.private_logs.len(), 1); - let app_secret = compute_app_siloed_shared_secret(point, test_address); - let expected_secret = poseidon2_hash([app_secret, recipient.to_field()]); - let next_unconstrained_index = - env.call_private(sender, test_contract.next_unconstrained_index_for_secret(expected_secret)); - let next_constrained_index = env.call_private(sender, test_contract.next_index_for_secret(expected_secret)); + let expected_secret = compute_directional_app_secret(point, test_address, recipient); + let next_unconstrained_index = env.call_private( + sender, + test_contract.next_index_for_secret(expected_secret, MessageDelivery::onchain_unconstrained().into()), + ); + let next_constrained_index = env.call_private( + sender, + test_contract.next_index_for_secret(expected_secret, MessageDelivery::onchain_constrained().into()), + ); assert_eq(next_unconstrained_index, 1); assert_eq(next_constrained_index, 0); @@ -91,7 +96,8 @@ unconstrained fn unconstrained_delivery_uses_configured_arbitrary_secret() { #[test] unconstrained fn unconstrained_delivery_uses_address_derived_secret() { - let options = TestEnvironmentOptions::new().with_tagging_secret_strategy(TaggingSecretStrategy::address_derived()); + let options = + TestEnvironmentOptions::new().with_tag_secret_strategy_all_modes(TaggingSecretStrategy::address_derived()); let (env, _, test_address, sender, _) = setup_opts(options); let recipient = external_recipient(); let test_contract = ConstrainedDeliveryTest::at(test_address); @@ -102,8 +108,14 @@ unconstrained fn unconstrained_delivery_uses_address_derived_secret() { let private_logs = txe_oracles::get_last_tx_effects().private_logs; assert_eq(private_logs.len(), 1); - let next_unconstrained_index = env.call_private(sender, test_contract.next_unconstrained_index_for_secret(secret)); - let next_constrained_index = env.call_private(sender, test_contract.next_index_for_secret(secret)); + let next_unconstrained_index = env.call_private( + sender, + test_contract.next_index_for_secret(secret, MessageDelivery::onchain_unconstrained().into()), + ); + let next_constrained_index = env.call_private( + sender, + test_contract.next_index_for_secret(secret, MessageDelivery::onchain_constrained().into()), + ); assert_eq(next_unconstrained_index, 1); assert_eq(next_constrained_index, 0); } @@ -111,9 +123,11 @@ unconstrained fn unconstrained_delivery_uses_address_derived_secret() { #[test] unconstrained fn tagging_secret_strategy_can_differ_by_delivery_mode() { let point = arbitrary_secret_point(); - let options = TestEnvironmentOptions::new().with_tagging_secret_strategies_by_delivery_mode( + // Only the unconstrained mode is configured: the constrained mode falls back to the default non-interactive + // handshake strategy, so the constrained delivery below still bootstraps a registry handshake. + let options = TestEnvironmentOptions::new().with_tag_secret_strategy( + MessageDelivery::onchain_unconstrained(), TaggingSecretStrategy::arbitrary_secret(point), - TaggingSecretStrategy::non_interactive_handshake(), ); let (env, registry_address, test_address, sender, _) = setup_opts(options); let recipient = external_recipient(); @@ -122,10 +136,14 @@ unconstrained fn tagging_secret_strategy_can_differ_by_delivery_mode() { env.call_private(sender, test_contract.emit_unconstrained_event(recipient, 1)); - let app_secret = compute_app_siloed_shared_secret(point, test_address); - let expected_unconstrained_secret = poseidon2_hash([app_secret, recipient.to_field()]); - let next_unconstrained_index = - env.call_private(sender, test_contract.next_unconstrained_index_for_secret(expected_unconstrained_secret)); + let expected_unconstrained_secret = compute_directional_app_secret(point, test_address, recipient); + let next_unconstrained_index = env.call_private( + sender, + test_contract.next_index_for_secret( + expected_unconstrained_secret, + MessageDelivery::onchain_unconstrained().into(), + ), + ); assert_eq(next_unconstrained_index, 1); let maybe_handshake = env.execute_utility_opts( @@ -144,7 +162,10 @@ unconstrained fn tagging_secret_strategy_can_differ_by_delivery_mode() { .expect(f"constrained delivery should have stored a handshake"); assert(handshake.shared != expected_unconstrained_secret); - let next_constrained_index = env.call_private(sender, test_contract.next_index_for_secret(handshake.shared)); + let next_constrained_index = env.call_private( + sender, + test_contract.next_index_for_secret(handshake.shared, MessageDelivery::onchain_constrained().into()), + ); assert_eq(next_constrained_index, 1); } @@ -175,7 +196,10 @@ unconstrained fn rehandshake_replaces_registry_secret_for_future_delivery() { env.call_private_opts(sender, authorizing(registry_address), test_contract.emit_event(recipient, 1)); - let next_index = env.call_private(sender, test_contract.next_index_for_secret(second_secret)); + let next_index = env.call_private( + sender, + test_contract.next_index_for_secret(second_secret, MessageDelivery::onchain_constrained().into()), + ); assert_eq(next_index, 1); } diff --git a/yarn-project/pxe/src/contract_function_simulator/oracle/private_execution_oracle.ts b/yarn-project/pxe/src/contract_function_simulator/oracle/private_execution_oracle.ts index cc08e3b25e2e..da88ce0da9fe 100644 --- a/yarn-project/pxe/src/contract_function_simulator/oracle/private_execution_oracle.ts +++ b/yarn-project/pxe/src/contract_function_simulator/oracle/private_execution_oracle.ts @@ -30,9 +30,10 @@ import { } from '@aztec/stdlib/tx'; import type { ResolveCustomRequest } from '../../hooks/resolve_custom_request.js'; -import type { - ResolveTaggingSecretStrategy, - TaggingSecretStrategy, +import { + DEFAULT_TAGGING_SECRET_STRATEGY, + type ResolveTaggingSecretStrategy, + type TaggingSecretStrategy, } from '../../hooks/resolve_tagging_secret_strategy.js'; import { NoteService } from '../../notes/note_service.js'; import type { SenderTaggingStore } from '../../storage/tagging_store/sender_tagging_store.js'; @@ -233,8 +234,7 @@ export class PrivateExecutionOracle extends UtilityExecutionOracle implements IP ): Promise { const hook: ResolveTaggingSecretStrategy | undefined = this.hooks?.resolveTaggingSecretStrategy; if (!hook) { - // With no hook, both delivery modes default to a non-interactive handshake - return { type: 'non-interactive-handshake' }; + return DEFAULT_TAGGING_SECRET_STRATEGY; } const contractClassId = await this.#getCurrentContractClassId(this.contractAddress); diff --git a/yarn-project/pxe/src/hooks/index.ts b/yarn-project/pxe/src/hooks/index.ts index 360cae3e904f..029b85c5e000 100644 --- a/yarn-project/pxe/src/hooks/index.ts +++ b/yarn-project/pxe/src/hooks/index.ts @@ -6,6 +6,7 @@ export type { export { type ExecutionHooks, composeHooks } from './execution_hooks.js'; export { type CustomRequest, type ResolveCustomRequest } from './resolve_custom_request.js'; export { + DEFAULT_TAGGING_SECRET_STRATEGY, type ResolveTaggingSecretStrategy, type TaggingSecretStrategy, type TaggingSecretStrategyRequest, diff --git a/yarn-project/pxe/src/hooks/resolve_tagging_secret_strategy.ts b/yarn-project/pxe/src/hooks/resolve_tagging_secret_strategy.ts index aac3e9ce1fbd..50e6dbcf23dc 100644 --- a/yarn-project/pxe/src/hooks/resolve_tagging_secret_strategy.ts +++ b/yarn-project/pxe/src/hooks/resolve_tagging_secret_strategy.ts @@ -28,6 +28,9 @@ export type TaggingSecretStrategy = secret: Point; }; +/** The strategy PXE applies to both delivery modes when no `resolveTaggingSecretStrategy` hook is configured. */ +export const DEFAULT_TAGGING_SECRET_STRATEGY: TaggingSecretStrategy = { type: 'non-interactive-handshake' }; + /** Information about the message delivery requesting a tagging secret strategy. */ export type TaggingSecretStrategyRequest = { contractAddress: AztecAddress; diff --git a/yarn-project/txe/src/oracle/tagging_secret_strategy.test.ts b/yarn-project/txe/src/oracle/tagging_secret_strategy.test.ts index b708f2e693cd..c86d2ec72754 100644 --- a/yarn-project/txe/src/oracle/tagging_secret_strategy.test.ts +++ b/yarn-project/txe/src/oracle/tagging_secret_strategy.test.ts @@ -4,6 +4,7 @@ import type { TaggingSecretStrategy } from '@aztec/pxe/server'; import { AztecAddress } from '@aztec/stdlib/aztec-address'; import { AppTaggingSecretKind } from '@aztec/stdlib/logs'; +import { toSingle } from '../utils/encoding.js'; import { makeResolveTaggingSecretStrategyHook } from './tagging_secret_strategy.js'; import { callTxeHandler } from './txe_oracle_registry.js'; @@ -52,7 +53,7 @@ describe('makeResolveTaggingSecretStrategyHook', () => { it('deserializes the mode-aware TXE oracle setter', async () => { const received = await callTxeHandler({ oracle: 'aztec_txe_setTaggingSecretStrategy', - inputs: [field(2), field(1), field(2), field(5), field(6)], + inputs: [toSingle(2), toSingle(1), toSingle(2), toSingle(5), toSingle(6)], handler: ([deliveryMode, strategy]) => { expect(deliveryMode).toBe(AppTaggingSecretKind.UNCONSTRAINED); expect(strategy.isSome()).toBe(true); @@ -79,7 +80,3 @@ function makeRequest(deliveryMode: AppTaggingSecretKind) { deliveryMode, }; } - -function field(value: number) { - return new Fr(value).toString().replace(/^0x/, ''); -} diff --git a/yarn-project/txe/src/oracle/tagging_secret_strategy.ts b/yarn-project/txe/src/oracle/tagging_secret_strategy.ts index 1febbedece2d..593db4d43729 100644 --- a/yarn-project/txe/src/oracle/tagging_secret_strategy.ts +++ b/yarn-project/txe/src/oracle/tagging_secret_strategy.ts @@ -1,8 +1,19 @@ -import type { ResolveTaggingSecretStrategy, TaggingSecretStrategy } from '@aztec/pxe/server'; -import { AppTaggingSecretKind } from '@aztec/stdlib/logs'; +import { + DEFAULT_TAGGING_SECRET_STRATEGY, + type ResolveTaggingSecretStrategy, + type TaggingSecretStrategy, +} from '@aztec/pxe/server'; +import type { AppTaggingSecretKind } from '@aztec/stdlib/logs'; +/** The tagging secret strategies a TXE test has configured, keyed by delivery mode. Absence means "not configured". */ export type TXETaggingSecretStrategies = Map; +/** + * Builds the `resolveTaggingSecretStrategy` hook backing the `aztec_txe_setTaggingSecretStrategy` oracle. Returns + * `undefined` when no strategy is configured, so PXE's own no-hook default path is exercised. When at least one mode + * is configured, modes without an entry resolve to {@link DEFAULT_TAGGING_SECRET_STRATEGY}, matching what PXE would + * apply without a hook. + */ export function makeResolveTaggingSecretStrategyHook( strategies: TXETaggingSecretStrategies, ): ResolveTaggingSecretStrategy | undefined { @@ -10,5 +21,5 @@ export function makeResolveTaggingSecretStrategyHook( return undefined; } - return ({ deliveryMode }) => Promise.resolve(strategies.get(deliveryMode) ?? { type: 'non-interactive-handshake' }); + return ({ deliveryMode }) => Promise.resolve(strategies.get(deliveryMode) ?? DEFAULT_TAGGING_SECRET_STRATEGY); } diff --git a/yarn-project/txe/src/oracle/txe_oracle_top_level_context.ts b/yarn-project/txe/src/oracle/txe_oracle_top_level_context.ts index 7c9b0f57ad52..7af81ddab73b 100644 --- a/yarn-project/txe/src/oracle/txe_oracle_top_level_context.ts +++ b/yarn-project/txe/src/oracle/txe_oracle_top_level_context.ts @@ -477,7 +477,6 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl const simulator = new WASMSimulator(); const transientArrayService = new TransientArrayService(); - const taggingSecretStrategies = this.taggingSecretStrategies; const privateExecutionOracle = new PrivateExecutionOracle({ argsHash, txContext, @@ -518,7 +517,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl ), // Only configure the hook when a strategy was explicitly set, so that otherwise the default tagging secret // strategy is exercised. - resolveTaggingSecretStrategy: makeResolveTaggingSecretStrategyHook(taggingSecretStrategies), + resolveTaggingSecretStrategy: makeResolveTaggingSecretStrategyHook(this.taggingSecretStrategies), }), transientArrayService, }); diff --git a/yarn-project/txe/src/txe_session.ts b/yarn-project/txe/src/txe_session.ts index 27cfe78b388d..e84b4b6a31df 100644 --- a/yarn-project/txe/src/txe_session.ts +++ b/yarn-project/txe/src/txe_session.ts @@ -740,7 +740,6 @@ export class TXESession implements TXESessionStateHandler { this.stateMachine.contractClassService, anchorBlock!, ); - const taggingSecretStrategies = this.taggingSecretStrategies; this.oracleHandler = new TXEPrivateExecutionOracle({ argsHash: Fr.ZERO, txContext: new TxContext(this.chainId, this.version, gasSettings), @@ -770,7 +769,7 @@ export class TXESession implements TXESessionStateHandler { txResolver: this.stateMachine.txResolver, simulator: new WASMSimulator(), hooks: composeHooks({ - resolveTaggingSecretStrategy: makeResolveTaggingSecretStrategyHook(taggingSecretStrategies), + resolveTaggingSecretStrategy: makeResolveTaggingSecretStrategyHook(this.taggingSecretStrategies), }), transientArrayService, }); From 1ed50ec4379eb19ce8240f247461976f65e429c1 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Mon, 6 Jul 2026 23:24:20 -0400 Subject: [PATCH 04/21] fix(e2e): pass delivery mode to next_index_for_secret --- .../src/e2e_constrained_delivery.test.ts | 23 +++++++++++++++---- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/yarn-project/end-to-end/src/e2e_constrained_delivery.test.ts b/yarn-project/end-to-end/src/e2e_constrained_delivery.test.ts index 5ee4f32ef33a..5dd8b4a019ed 100644 --- a/yarn-project/end-to-end/src/e2e_constrained_delivery.test.ts +++ b/yarn-project/end-to-end/src/e2e_constrained_delivery.test.ts @@ -19,6 +19,9 @@ import { AUTOMINE_E2E_OPTS } from './fixtures/fixtures.js'; import { ensureHandshakeRegistryPublished, setup, setupPXEAndGetWallet } from './fixtures/setup.js'; import { TestWallet } from './test-wallet/test_wallet.js'; +// Keep in sync with aztec::messages::delivery::OnchainDeliveryMode. +const ONCHAIN_CONSTRAINED_DELIVERY_MODE = { inner: 3 }; + describe('constrained delivery', () => { jest.setTimeout(300_000); @@ -63,7 +66,9 @@ describe('constrained delivery', () => { // The second send reuses the handshake rather than bootstrapping a new one: the secret is unchanged. expect(secret).toEqual(secretAfterFirstSend); - const { result: index } = await contract.methods.next_index_for_secret(secret).simulate({ from: sender }); + const { result: index } = await contract.methods + .next_index_for_secret(secret, ONCHAIN_CONSTRAINED_DELIVERY_MODE) + .simulate({ from: sender }); expect(index).toEqual(2n); }); @@ -99,7 +104,9 @@ describe('constrained delivery', () => { .simulate({ from: sender }); expect(secret).toBeDefined(); - const { result: index } = await contract.methods.next_index_for_secret(secret).simulate({ from: sender }); + const { result: index } = await contract.methods + .next_index_for_secret(secret, ONCHAIN_CONSTRAINED_DELIVERY_MODE) + .simulate({ from: sender }); expect(index).toEqual(2n); }); @@ -118,7 +125,9 @@ describe('constrained delivery', () => { .simulate({ from: sender }); expect(secret).toBeDefined(); - const { result: index } = await contract.methods.next_index_for_secret(secret).simulate({ from: sender }); + const { result: index } = await contract.methods + .next_index_for_secret(secret, ONCHAIN_CONSTRAINED_DELIVERY_MODE) + .simulate({ from: sender }); expect(index).toEqual(2n); }); @@ -135,7 +144,9 @@ describe('constrained delivery', () => { .simulate({ from: sender }); expect(secret).toBeDefined(); - const { result: index } = await contract.methods.next_index_for_secret(secret).simulate({ from: sender }); + const { result: index } = await contract.methods + .next_index_for_secret(secret, ONCHAIN_CONSTRAINED_DELIVERY_MODE) + .simulate({ from: sender }); expect(index).toEqual(1n); }); @@ -153,7 +164,9 @@ describe('constrained delivery', () => { .simulate({ from: sender }); expect(secret).toBeDefined(); - const { result: index } = await contract.methods.next_index_for_secret(secret).simulate({ from: sender }); + const { result: index } = await contract.methods + .next_index_for_secret(secret, ONCHAIN_CONSTRAINED_DELIVERY_MODE) + .simulate({ from: sender }); expect(index).toEqual(1n); }); }); From 3bc97323e6e405de2f80d711a4216c51f3c4fc09 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Tue, 7 Jul 2026 12:29:17 -0400 Subject: [PATCH 05/21] test(txe): tighten tagging strategy coverage --- .../aztec/src/messages/delivery/tag.nr | 23 +++++++ .../src/test/helpers/test_environment.nr | 5 +- .../src/main.nr | 12 +--- .../src/test.nr | 64 +------------------ .../oracle/tagging_secret_strategy.test.ts | 25 ++------ 5 files changed, 35 insertions(+), 94 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/messages/delivery/tag.nr b/noir-projects/aztec-nr/aztec/src/messages/delivery/tag.nr index 056e5479782e..93ee3b2b3041 100644 --- a/noir-projects/aztec-nr/aztec/src/messages/delivery/tag.nr +++ b/noir-projects/aztec-nr/aztec/src/messages/delivery/tag.nr @@ -180,6 +180,29 @@ mod test { }); } + #[test(should_fail_with = "an unconstrained tagging secret cannot back constrained delivery")] + unconstrained fn constrained_delivery_rejects_a_resolved_unconstrained_secret() { + let env = TestEnvironment::new(); + let secret: Field = 7; + let index: u32 = 0; + + env.private_context(|context| { + mock_existing_handshake_secrets(Option::none()); + let _ = OracleMock::mock("aztec_prv_resolveTaggingStrategy").returns( + ResolvedTaggingStrategy::unconstrained_secret(secret), + ); + let _ = OracleMock::mock("aztec_prv_getNextTaggingIndex").returns(index); + + let _ = derive_log_tag( + context, + OnchainDeliveryMode::onchain_constrained(), + SENDER, + RECIPIENT, + Option::none(), + ); + }); + } + #[test] unconstrained fn constrained_delivery_emits_the_sequence_nullifier_and_constrained_tag() { let env = TestEnvironment::new(); diff --git a/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr b/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr index 12702415b33d..6fae6b34032b 100644 --- a/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr +++ b/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr @@ -89,7 +89,10 @@ pub struct TestEnvironment { /// methods setting each value, e.g.: /// ```noir /// let env = TestEnvironment::new_opts( -/// TestEnvironmentOptions::new().with_tag_secret_strategy_all_modes(strategy), +/// TestEnvironmentOptions::new().with_tag_secret_strategy( +/// MessageDelivery::onchain_unconstrained(), +/// TaggingSecretStrategy::non_interactive_handshake(), +/// ), /// ); /// ``` pub struct TestEnvironmentOptions { diff --git a/noir-projects/noir-contracts/contracts/test/constrained_delivery_test_contract/src/main.nr b/noir-projects/noir-contracts/contracts/test/constrained_delivery_test_contract/src/main.nr index f3cd379f6e42..3afb1ff0c520 100644 --- a/noir-projects/noir-contracts/contracts/test/constrained_delivery_test_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/test/constrained_delivery_test_contract/src/main.nr @@ -9,7 +9,7 @@ pub contract ConstrainedDeliveryTest { macros::{events::event, functions::external, storage::storage}, messages::delivery::{MessageDelivery, OnchainDeliveryMode}, note::note_viewer_options::NoteViewerOptions, - oracle::notes::{get_app_tagging_secret, get_next_tagging_index}, + oracle::notes::get_next_tagging_index, protocol::{ address::AztecAddress, constants::DOM_SEP__CONSTRAINED_MSG_LOG_TAG, @@ -41,16 +41,6 @@ pub contract ConstrainedDeliveryTest { } } - #[external("private")] - fn address_derived_secret(sender: AztecAddress, recipient: AztecAddress) -> Field { - // Safety: test-only observation of the PXE's address-derived tagging secret. - unsafe { - let secret: Field = - get_app_tagging_secret(sender, recipient).expect(f"expected an address-derived tagging secret"); - secret - } - } - #[external("private")] fn emit_note(recipient: AztecAddress, value: Field) { self.storage.notes.at(recipient).insert(FieldNote { value }).deliver(MessageDelivery::onchain_constrained()); diff --git a/noir-projects/noir-contracts/contracts/test/constrained_delivery_test_contract/src/test.nr b/noir-projects/noir-contracts/contracts/test/constrained_delivery_test_contract/src/test.nr index 91055784d233..964ac33f3eb8 100644 --- a/noir-projects/noir-contracts/contracts/test/constrained_delivery_test_contract/src/test.nr +++ b/noir-projects/noir-contracts/contracts/test/constrained_delivery_test_contract/src/test.nr @@ -15,7 +15,6 @@ use aztec::{ test_environment::{ CallPrivateOptions, DeployOptions, ExecuteUtilityOptions, TestEnvironment, TestEnvironmentOptions, }, - txe_oracles, }, }; use handshake_registry_contract::HandshakeRegistry; @@ -60,68 +59,7 @@ unconstrained fn arbitrary_secret_point() -> EmbeddedCurvePoint { } #[test] -unconstrained fn unconstrained_delivery_uses_configured_arbitrary_secret() { - let point = arbitrary_secret_point(); - let options = TestEnvironmentOptions::new().with_tag_secret_strategy_all_modes( - TaggingSecretStrategy::arbitrary_secret(point), - ); - let (env, registry_address, test_address, sender, _) = setup_opts(options); - let recipient = external_recipient(); - let test_contract = ConstrainedDeliveryTest::at(test_address); - let registry = HandshakeRegistry::at(registry_address); - - env.call_private(sender, test_contract.emit_unconstrained_event(recipient, 1)); - - let effects = txe_oracles::get_last_tx_effects(); - assert_eq(effects.private_logs.len(), 1); - - let expected_secret = compute_directional_app_secret(point, test_address, recipient); - let next_unconstrained_index = env.call_private( - sender, - test_contract.next_index_for_secret(expected_secret, MessageDelivery::onchain_unconstrained().into()), - ); - let next_constrained_index = env.call_private( - sender, - test_contract.next_index_for_secret(expected_secret, MessageDelivery::onchain_constrained().into()), - ); - assert_eq(next_unconstrained_index, 1); - assert_eq(next_constrained_index, 0); - - let maybe_handshake = env.execute_utility_opts( - ExecuteUtilityOptions::new().with_from(test_address), - registry.get_app_siloed_secrets(sender, recipient), - ); - assert(maybe_handshake.is_none()); -} - -#[test] -unconstrained fn unconstrained_delivery_uses_address_derived_secret() { - let options = - TestEnvironmentOptions::new().with_tag_secret_strategy_all_modes(TaggingSecretStrategy::address_derived()); - let (env, _, test_address, sender, _) = setup_opts(options); - let recipient = external_recipient(); - let test_contract = ConstrainedDeliveryTest::at(test_address); - let secret = env.call_private(sender, test_contract.address_derived_secret(sender, recipient)); - - env.call_private(sender, test_contract.emit_unconstrained_event(recipient, 1)); - - let private_logs = txe_oracles::get_last_tx_effects().private_logs; - assert_eq(private_logs.len(), 1); - - let next_unconstrained_index = env.call_private( - sender, - test_contract.next_index_for_secret(secret, MessageDelivery::onchain_unconstrained().into()), - ); - let next_constrained_index = env.call_private( - sender, - test_contract.next_index_for_secret(secret, MessageDelivery::onchain_constrained().into()), - ); - assert_eq(next_unconstrained_index, 1); - assert_eq(next_constrained_index, 0); -} - -#[test] -unconstrained fn tagging_secret_strategy_can_differ_by_delivery_mode() { +unconstrained fn unconstrained_delivery_works_alongside_constrained_delivery() { let point = arbitrary_secret_point(); // Only the unconstrained mode is configured: the constrained mode falls back to the default non-interactive // handshake strategy, so the constrained delivery below still bootstraps a registry handshake. diff --git a/yarn-project/txe/src/oracle/tagging_secret_strategy.test.ts b/yarn-project/txe/src/oracle/tagging_secret_strategy.test.ts index c86d2ec72754..05ef3b4f71bd 100644 --- a/yarn-project/txe/src/oracle/tagging_secret_strategy.test.ts +++ b/yarn-project/txe/src/oracle/tagging_secret_strategy.test.ts @@ -1,6 +1,6 @@ import { Fr } from '@aztec/foundation/curves/bn254'; import { Point } from '@aztec/foundation/curves/grumpkin'; -import type { TaggingSecretStrategy } from '@aztec/pxe/server'; +import { DEFAULT_TAGGING_SECRET_STRATEGY, type TaggingSecretStrategy } from '@aztec/pxe/server'; import { AztecAddress } from '@aztec/stdlib/aztec-address'; import { AppTaggingSecretKind } from '@aztec/stdlib/logs'; @@ -13,22 +13,9 @@ describe('makeResolveTaggingSecretStrategyHook', () => { expect(makeResolveTaggingSecretStrategyHook(new Map())).toBeUndefined(); }); - it('returns a static strategy for every delivery mode', async () => { - const strategy = { type: 'non-interactive-handshake' as const }; - const hook = makeResolveTaggingSecretStrategyHook( - new Map([ - [AppTaggingSecretKind.UNCONSTRAINED, strategy], - [AppTaggingSecretKind.CONSTRAINED, strategy], - ]), - ); - - await expect(hook?.(makeRequest(AppTaggingSecretKind.UNCONSTRAINED))).resolves.toBe(strategy); - await expect(hook?.(makeRequest(AppTaggingSecretKind.CONSTRAINED))).resolves.toBe(strategy); - }); - it('selects a strategy by delivery mode', async () => { - const unconstrained: TaggingSecretStrategy = { type: 'address-derived' }; - const constrained: TaggingSecretStrategy = { type: 'arbitrary-secret', secret: await Point.random() }; + const unconstrained: TaggingSecretStrategy = { type: 'arbitrary-secret', secret: await Point.random() }; + const constrained: TaggingSecretStrategy = { type: 'non-interactive-handshake' }; const hook = makeResolveTaggingSecretStrategyHook( new Map([ [AppTaggingSecretKind.UNCONSTRAINED, unconstrained], @@ -45,9 +32,9 @@ describe('makeResolveTaggingSecretStrategyHook', () => { const hook = makeResolveTaggingSecretStrategyHook(new Map([[AppTaggingSecretKind.UNCONSTRAINED, unconstrained]])); await expect(hook?.(makeRequest(AppTaggingSecretKind.UNCONSTRAINED))).resolves.toBe(unconstrained); - await expect(hook?.(makeRequest(AppTaggingSecretKind.CONSTRAINED))).resolves.toEqual({ - type: 'non-interactive-handshake', - }); + await expect(hook?.(makeRequest(AppTaggingSecretKind.CONSTRAINED))).resolves.toEqual( + DEFAULT_TAGGING_SECRET_STRATEGY, + ); }); it('deserializes the mode-aware TXE oracle setter', async () => { From 2aa0cdbd212bb3669c65cad0595968d91de8af7d Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Tue, 7 Jul 2026 12:56:56 -0400 Subject: [PATCH 06/21] rename --- .../contracts/test/onchain_delivery_test_contract/src/test.nr | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/noir-projects/noir-contracts/contracts/test/onchain_delivery_test_contract/src/test.nr b/noir-projects/noir-contracts/contracts/test/onchain_delivery_test_contract/src/test.nr index 96376d126f85..f2eab1fa333c 100644 --- a/noir-projects/noir-contracts/contracts/test/onchain_delivery_test_contract/src/test.nr +++ b/noir-projects/noir-contracts/contracts/test/onchain_delivery_test_contract/src/test.nr @@ -69,10 +69,10 @@ unconstrained fn unconstrained_delivery_works_alongside_constrained_delivery() { ); let (env, registry_address, test_address, sender, _) = setup_opts(options); let recipient = external_recipient(); - let test_contract = ConstrainedDeliveryTest::at(test_address); + let test_contract = OnchainDeliveryTest::at(test_address); let registry = HandshakeRegistry::at(registry_address); - env.call_private(sender, test_contract.emit_unconstrained_event(recipient, 1)); + env.call_private(sender, test_contract.emit_event_unconstrained(recipient, 1)); let expected_unconstrained_secret = compute_directional_app_secret(point, test_address, recipient); let next_unconstrained_index = env.call_private( From 36c473e4c315dda1d5e86c0d90e2192bafb01aba Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Tue, 7 Jul 2026 13:11:01 -0400 Subject: [PATCH 07/21] test(txe): set both tagging secret strategies in one oracle call Replaces the per-mode aztec_txe_setTaggingSecretStrategy oracle (called twice per TestEnvironment creation) with a single aztec_txe_setTaggingSecretStrategies call carrying an option per delivery mode, halving the setup round-trips every TXE test pays. Also updates the stale execution_hooks doc example to the current TestEnvironmentOptions API, refreshes the onchain delivery test module header to cover the unconstrained tests, and tidies minor conventions in the TXE tagging strategy code (JSDoc, stale comment, compound test assertion). --- .../pxe/execution_hooks.md | 8 ++--- .../aztec-nr/aztec/src/test/helpers/mod.nr | 2 +- .../src/test/helpers/test_environment.nr | 6 +--- .../aztec/src/test/helpers/txe_oracles.nr | 10 +++--- .../src/test.nr | 7 ++-- yarn-project/txe/src/oracle/interfaces.ts | 11 ++++-- .../oracle/tagging_secret_strategy.test.ts | 35 ++++++++++++------- .../txe/src/oracle/tagging_secret_strategy.ts | 2 +- .../txe/src/oracle/txe_oracle_registry.ts | 9 +++-- .../oracle/txe_oracle_top_level_context.ts | 21 ++++++----- yarn-project/txe/src/rpc_translator.ts | 7 ++-- yarn-project/txe/src/txe_session.ts | 2 +- 12 files changed, 70 insertions(+), 50 deletions(-) diff --git a/docs/docs-developers/docs/foundational-topics/pxe/execution_hooks.md b/docs/docs-developers/docs/foundational-topics/pxe/execution_hooks.md index dbdc302c3d4c..cd462ffa7f12 100644 --- a/docs/docs-developers/docs/foundational-topics/pxe/execution_hooks.md +++ b/docs/docs-developers/docs/foundational-topics/pxe/execution_hooks.md @@ -82,12 +82,12 @@ For an unconstrained self-send (the recipient is one of the wallet's own account ### In Noir tests -When testing in Noir, leaving the strategy unset makes `TestEnvironment` fall back to the bare PXE default. Set a strategy when creating the environment to exercise a specific one; it affects message delivery in private executions: +When testing in Noir, leaving the strategy unset makes `TestEnvironment` fall back to the bare PXE default. Set a strategy when creating the environment to exercise a specific one; it affects message delivery in private executions. Use `with_tag_secret_strategy` to configure a single delivery mode, or `with_tag_secret_strategy_all_modes` for both at once: ```rust -let env = TestEnvironment::new_opts( - TestEnvironmentOptions::new().with_tagging_secret_strategy(TaggingSecretStrategy::non_interactive_handshake()), -); +let env = TestEnvironment::new_opts(TestEnvironmentOptions::new().with_tag_secret_strategy_all_modes( + TaggingSecretStrategy::non_interactive_handshake(), +)); ``` ### In production diff --git a/noir-projects/aztec-nr/aztec/src/test/helpers/mod.nr b/noir-projects/aztec-nr/aztec/src/test/helpers/mod.nr index 6cd7f18ac34f..1732db45a308 100644 --- a/noir-projects/aztec-nr/aztec/src/test/helpers/mod.nr +++ b/noir-projects/aztec-nr/aztec/src/test/helpers/mod.nr @@ -13,7 +13,7 @@ pub mod test_environment; quote { private_call_new_flow_oracle }, // TODO: implement once we support more complex types quote { public_call_new_flow_oracle }, // TODO: implement once we support more complex types quote { set_private_txe_context_oracle }, // TODO: implement once we support more complex types - quote { set_tagging_secret_strategy }, // TODO: implement once we support more complex types + quote { set_tagging_secret_strategies }, // TODO: implement once we support more complex types ], )] pub mod txe_oracles; diff --git a/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr b/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr index f63bc7b76bbd..e3fd0bd676e6 100644 --- a/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr +++ b/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr @@ -642,12 +642,8 @@ impl TestEnvironment { assert_compatible_oracle_version(); txe_oracles::assert_compatible_txe_oracle_version(); - txe_oracles::set_tagging_secret_strategy( - OnchainDeliveryMode::onchain_unconstrained(), + txe_oracles::set_tagging_secret_strategies( options.unconstrained_tagging_secret_strategy, - ); - txe_oracles::set_tagging_secret_strategy( - OnchainDeliveryMode::onchain_constrained(), options.constrained_tagging_secret_strategy, ); diff --git a/noir-projects/aztec-nr/aztec/src/test/helpers/txe_oracles.nr b/noir-projects/aztec-nr/aztec/src/test/helpers/txe_oracles.nr index 682ca216d05e..55bb5c56fdd8 100644 --- a/noir-projects/aztec-nr/aztec/src/test/helpers/txe_oracles.nr +++ b/noir-projects/aztec-nr/aztec/src/test/helpers/txe_oracles.nr @@ -1,7 +1,7 @@ use crate::{ context::inputs::PrivateContextInputs, event::EventSelector, - messages::{delivery::OnchainDeliveryMode, encoding::MESSAGE_CIPHERTEXT_LEN}, + messages::encoding::MESSAGE_CIPHERTEXT_LEN, test::helpers::{tagging_secret_strategy::TaggingSecretStrategy, utils::TestAccount}, }; @@ -265,10 +265,10 @@ pub unconstrained fn send_l1_to_l2_message( recipient: AztecAddress, ) -> Field {} -#[oracle(aztec_txe_setTaggingSecretStrategy)] -pub unconstrained fn set_tagging_secret_strategy( - delivery_mode: OnchainDeliveryMode, - strategy: Option, +#[oracle(aztec_txe_setTaggingSecretStrategies)] +pub unconstrained fn set_tagging_secret_strategies( + unconstrained_strategy: Option, + constrained_strategy: Option, ) {} #[oracle(aztec_txe_privateCallNewFlow)] diff --git a/noir-projects/noir-contracts/contracts/test/onchain_delivery_test_contract/src/test.nr b/noir-projects/noir-contracts/contracts/test/onchain_delivery_test_contract/src/test.nr index f2eab1fa333c..943eee7ac6d0 100644 --- a/noir-projects/noir-contracts/contracts/test/onchain_delivery_test_contract/src/test.nr +++ b/noir-projects/noir-contracts/contracts/test/onchain_delivery_test_contract/src/test.nr @@ -1,9 +1,10 @@ -//! Tests for constrained delivery through the public message-delivery API. +//! Tests for onchain message delivery through the public message-delivery API. //! -//! These tests exercise the sender-side helper flow indirectly via constrained note/event delivery: resolve or +//! The constrained tests exercise the sender-side helper flow indirectly via note/event delivery: resolve or //! bootstrap the app-siloed handshake secret, reserve the next per-secret index, constrain that index, emit the //! sequence nullifier, then emit the tagged private log. The malformed-log test lands a constrained tag without its -//! sequence nullifier so the next real delivery must reject the broken sequence. +//! sequence nullifier so the next real delivery must reject the broken sequence. The unconstrained tests cover +//! delivery in the mode without sequence nullifiers, alongside and independent of the constrained flow. use crate::OnchainDeliveryTest; use aztec::{ diff --git a/yarn-project/txe/src/oracle/interfaces.ts b/yarn-project/txe/src/oracle/interfaces.ts index c8e2fe9d7ba4..50eca629ea36 100644 --- a/yarn-project/txe/src/oracle/interfaces.ts +++ b/yarn-project/txe/src/oracle/interfaces.ts @@ -8,7 +8,7 @@ import type { Option } from '@aztec/pxe/simulator'; import type { EventSelector, FunctionSelector } from '@aztec/stdlib/abi'; import type { AztecAddress } from '@aztec/stdlib/aztec-address'; import type { GasSettings } from '@aztec/stdlib/gas'; -import type { AppTaggingSecretKind, PrivateLog } from '@aztec/stdlib/logs'; +import type { PrivateLog } from '@aztec/stdlib/logs'; import type { UInt64 } from '@aztec/stdlib/types'; // These interfaces complement the ones defined in PXE, and combined with those contain the full list of oracles used by @@ -73,7 +73,14 @@ export interface ITxeExecutionOracle { addAccount(secret: Fr): Promise; addAuthWitness(address: AztecAddress, messageHash: Fr): Promise; sendL1ToL2Message(content: Fr, secretHash: Fr, sender: EthAddress, recipient: AztecAddress): Promise; - setTaggingSecretStrategy(deliveryMode: AppTaggingSecretKind, strategy: Option): void; + /** + * Configures the tagging secret strategy the test's simulated wallet resolves for each delivery mode. A `none` + * clears that mode, so it falls back to the default strategy (or, when both modes end up unset, to no hook at all). + */ + setTaggingSecretStrategies( + unconstrainedStrategy: Option, + constrainedStrategy: Option, + ): void; getLastBlockTimestamp(): Promise; getLastTxEffects(): Promise<{ txHash: TxHash; diff --git a/yarn-project/txe/src/oracle/tagging_secret_strategy.test.ts b/yarn-project/txe/src/oracle/tagging_secret_strategy.test.ts index 05ef3b4f71bd..15c735415544 100644 --- a/yarn-project/txe/src/oracle/tagging_secret_strategy.test.ts +++ b/yarn-project/txe/src/oracle/tagging_secret_strategy.test.ts @@ -37,20 +37,31 @@ describe('makeResolveTaggingSecretStrategyHook', () => { ); }); - it('deserializes the mode-aware TXE oracle setter', async () => { + it('deserializes the per-mode TXE oracle setter', async () => { const received = await callTxeHandler({ - oracle: 'aztec_txe_setTaggingSecretStrategy', - inputs: [toSingle(2), toSingle(1), toSingle(2), toSingle(5), toSingle(6)], - handler: ([deliveryMode, strategy]) => { - expect(deliveryMode).toBe(AppTaggingSecretKind.UNCONSTRAINED); - expect(strategy.isSome()).toBe(true); - if (!strategy.isSome()) { - throw new Error('Expected tagging secret strategy'); + oracle: 'aztec_txe_setTaggingSecretStrategies', + inputs: [ + // Unconstrained mode: some(arbitrary-secret with point (5, 6)). + toSingle(1), + toSingle(2), + toSingle(5), + toSingle(6), + // Constrained mode: none, zero-padded to the option's full width. + toSingle(0), + toSingle(0), + toSingle(0), + toSingle(0), + ], + handler: ([unconstrainedStrategy, constrainedStrategy]) => { + expect(constrainedStrategy.isSome()).toBe(false); + if (!unconstrainedStrategy.isSome()) { + throw new Error('Expected an unconstrained-mode tagging secret strategy'); } - expect(strategy.value.type).toBe('arbitrary-secret'); - expect( - strategy.value.type === 'arbitrary-secret' && strategy.value.secret.equals(new Point(new Fr(5), new Fr(6))), - ).toBeTruthy(); + const strategy = unconstrainedStrategy.value; + if (strategy.type !== 'arbitrary-secret') { + throw new Error(`Expected an arbitrary-secret strategy, got '${strategy.type}'`); + } + expect(strategy.secret.equals(new Point(new Fr(5), new Fr(6)))).toBe(true); }, }); diff --git a/yarn-project/txe/src/oracle/tagging_secret_strategy.ts b/yarn-project/txe/src/oracle/tagging_secret_strategy.ts index 593db4d43729..089ae0d78ff8 100644 --- a/yarn-project/txe/src/oracle/tagging_secret_strategy.ts +++ b/yarn-project/txe/src/oracle/tagging_secret_strategy.ts @@ -9,7 +9,7 @@ import type { AppTaggingSecretKind } from '@aztec/stdlib/logs'; export type TXETaggingSecretStrategies = Map; /** - * Builds the `resolveTaggingSecretStrategy` hook backing the `aztec_txe_setTaggingSecretStrategy` oracle. Returns + * Builds the `resolveTaggingSecretStrategy` hook backing the `aztec_txe_setTaggingSecretStrategies` oracle. Returns * `undefined` when no strategy is configured, so PXE's own no-hook default path is exercised. When at least one mode * is configured, modes without an entry resolve to {@link DEFAULT_TAGGING_SECRET_STRATEGY}, matching what PXE would * apply without a hook. diff --git a/yarn-project/txe/src/oracle/txe_oracle_registry.ts b/yarn-project/txe/src/oracle/txe_oracle_registry.ts index ffdaf6aca0a0..160a012681ff 100644 --- a/yarn-project/txe/src/oracle/txe_oracle_registry.ts +++ b/yarn-project/txe/src/oracle/txe_oracle_registry.ts @@ -17,7 +17,6 @@ import { BIGINT, BLOCK_NUMBER, BOOL, - DELIVERY_MODE, ETH_ADDRESS, FIELD, FIXED_ARRAY, @@ -68,7 +67,7 @@ const GAS_SETTINGS: TypeMapping = { }; // Tagging secret strategy discriminants. Must match the Noir test helper `TaggingSecretStrategy` in -// aztec-nr `test/helpers/tagging_secret_strategy.nr`. This is a test-only oracle (only `set_tagging_secret_strategy` +// aztec-nr `test/helpers/tagging_secret_strategy.nr`. This is a test-only oracle (only `set_tagging_secret_strategies` // reads it), so the mapping lives here on the TXE side rather than in the production oracle type mappings. const STRATEGY_NON_INTERACTIVE_HANDSHAKE = 1; const STRATEGY_ARBITRARY_SECRET = 2; @@ -316,10 +315,10 @@ export const TXE_ORACLE_REGISTRY = { returnType: FIELD, }), - aztec_txe_setTaggingSecretStrategy: makeEntry({ + aztec_txe_setTaggingSecretStrategies: makeEntry({ params: [ - { name: 'deliveryMode', type: DELIVERY_MODE }, - { name: 'strategy', type: OPTION(TAGGING_SECRET_STRATEGY) }, + { name: 'unconstrainedStrategy', type: OPTION(TAGGING_SECRET_STRATEGY) }, + { name: 'constrainedStrategy', type: OPTION(TAGGING_SECRET_STRATEGY) }, ], }), diff --git a/yarn-project/txe/src/oracle/txe_oracle_top_level_context.ts b/yarn-project/txe/src/oracle/txe_oracle_top_level_context.ts index 7af81ddab73b..6bd2b4e31d74 100644 --- a/yarn-project/txe/src/oracle/txe_oracle_top_level_context.ts +++ b/yarn-project/txe/src/oracle/txe_oracle_top_level_context.ts @@ -358,12 +358,19 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl this.authwits.set(authWitness.requestHash.toString(), authWitness); } - setTaggingSecretStrategy(deliveryMode: AppTaggingSecretKind, strategy: Option): void { - if (strategy.isSome()) { - this.taggingSecretStrategies.set(deliveryMode, strategy.value); - } else { - this.taggingSecretStrategies.delete(deliveryMode); - } + setTaggingSecretStrategies( + unconstrainedStrategy: Option, + constrainedStrategy: Option, + ): void { + const apply = (mode: AppTaggingSecretKind, strategy: Option) => { + if (strategy.isSome()) { + this.taggingSecretStrategies.set(mode, strategy.value); + } else { + this.taggingSecretStrategies.delete(mode); + } + }; + apply(AppTaggingSecretKind.UNCONSTRAINED, unconstrainedStrategy); + apply(AppTaggingSecretKind.CONSTRAINED, constrainedStrategy); } async sendL1ToL2Message(content: Fr, secretHash: Fr, sender: EthAddress, recipient: AztecAddress): Promise { @@ -515,8 +522,6 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl isStaticCall ? 'private view' : 'private', authorizedUtilityCallTargets, ), - // Only configure the hook when a strategy was explicitly set, so that otherwise the default tagging secret - // strategy is exercised. resolveTaggingSecretStrategy: makeResolveTaggingSecretStrategyHook(this.taggingSecretStrategies), }), transientArrayService, diff --git a/yarn-project/txe/src/rpc_translator.ts b/yarn-project/txe/src/rpc_translator.ts index 889395415a5a..16ae802cca0c 100644 --- a/yarn-project/txe/src/rpc_translator.ts +++ b/yarn-project/txe/src/rpc_translator.ts @@ -221,11 +221,12 @@ export class RPCTranslator { } // eslint-disable-next-line camelcase - aztec_txe_setTaggingSecretStrategy(...inputs: ForeignCallArgs) { + aztec_txe_setTaggingSecretStrategies(...inputs: ForeignCallArgs) { return callTxeHandler({ - oracle: 'aztec_txe_setTaggingSecretStrategy', + oracle: 'aztec_txe_setTaggingSecretStrategies', inputs, - handler: ([deliveryMode, strategy]) => this.handlerAsTxe().setTaggingSecretStrategy(deliveryMode, strategy), + handler: ([unconstrainedStrategy, constrainedStrategy]) => + this.handlerAsTxe().setTaggingSecretStrategies(unconstrainedStrategy, constrainedStrategy), }); } diff --git a/yarn-project/txe/src/txe_session.ts b/yarn-project/txe/src/txe_session.ts index e84b4b6a31df..f571dfae57f1 100644 --- a/yarn-project/txe/src/txe_session.ts +++ b/yarn-project/txe/src/txe_session.ts @@ -892,7 +892,7 @@ export class TXESession implements TXESessionStateHandler { // accounts to PXE (via `addAccount`), etc. This is a slight inconsistency in the working model of this class, but // is not too bad. The `close` call below therefore only hands back the session-scoped values that a test // sets directly at the top level, outside any contract execution (e.g. via `advanceTimestampBy`, - // `addAuthWitness`, `setTaggingSecretStrategy`). The oracle handler is discarded on every state transition, + // `addAuthWitness`, `setTaggingSecretStrategies`). The oracle handler is discarded on every state transition, // so the session must seed these values into the contexts it creates later. // TODO: persisting authwits this way is quite unfortunate: they create a temporary utility context that would From 1daee1c14622873356e4e7c02effc02700b5ae3d Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Tue, 7 Jul 2026 15:05:20 -0400 Subject: [PATCH 08/21] additioanl default test --- .../test/resolve_tagging_strategy.nr | 62 ++++++++++++++++--- 1 file changed, 54 insertions(+), 8 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment/test/resolve_tagging_strategy.nr b/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment/test/resolve_tagging_strategy.nr index 63090e1018fd..0cbeaa305f99 100644 --- a/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment/test/resolve_tagging_strategy.nr +++ b/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment/test/resolve_tagging_strategy.nr @@ -9,14 +9,13 @@ use crate::{ }; #[test] -unconstrained fn an_unset_mode_falls_back_to_the_default_strategy() { +unconstrained fn an_unset_constrained_mode_falls_back_to_the_default_strategy() { // Only the unconstrained mode is configured: the constrained mode falls back to the default non-interactive // handshake strategy. let mut env = TestEnvironment::new_opts(TestEnvironmentOptions::new().with_tag_secret_strategy( OnchainDeliveryMode::onchain_unconstrained(), TaggingSecretStrategy::arbitrary_secret(EmbeddedCurvePoint { x: 7, y: 11 }), )); - // The hook path looks up the executing contract's class ID, so run in a deployed contract's context. let app = env.create_contract_account(); let recipient = env.create_light_account(); @@ -30,6 +29,28 @@ unconstrained fn an_unset_mode_falls_back_to_the_default_strategy() { }); } +#[test] +unconstrained fn an_unset_unconstrained_mode_falls_back_to_the_default_strategy() { + // Only the constrained mode is configured: the unconstrained mode falls back to the default non-interactive + // handshake strategy for an external recipient. + let mut env = TestEnvironment::new_opts(TestEnvironmentOptions::new().with_tag_secret_strategy( + OnchainDeliveryMode::onchain_constrained(), + TaggingSecretStrategy::non_interactive_handshake(), + )); + let app = env.create_contract_account(); + let sender = env.create_light_account(); + let recipient = AztecAddress::from_field(8); + + env.private_context_at(app, |_| { + let resolved = resolve_tagging_strategy( + sender, + recipient, + OnchainDeliveryMode::onchain_unconstrained(), + ); + assert_eq(resolved, ResolvedTaggingStrategy::non_interactive_handshake()); + }); +} + #[test] unconstrained fn defaults_an_unconstrained_self_send_to_the_address_derived_shared_secret() { let mut env = TestEnvironment::new(); @@ -93,7 +114,6 @@ unconstrained fn constrained_delivery_succeeds_with_a_configured_strategy() { let mut env = TestEnvironment::new_opts(TestEnvironmentOptions::new().with_tag_secret_strategy_all_modes( TaggingSecretStrategy::non_interactive_handshake(), )); - // The hook path looks up the executing contract's class ID, so run in a deployed contract's context. let app = env.create_contract_account(); let recipient = env.create_light_account(); @@ -107,12 +127,37 @@ unconstrained fn constrained_delivery_succeeds_with_a_configured_strategy() { }); } +#[test] +unconstrained fn constrained_mode_arbitrary_secret_resolves_without_rejecting() { + // Mirrors PXE hooks: strategies are resolved without delivery-mode validation. Runtime rejection is covered by + // `tag_secret_source::test::unconstrained_secret_cannot_back_constrained_delivery` and + // `tag::test::constrained_delivery_rejects_a_resolved_unconstrained_secret`. + let point = EmbeddedCurvePoint { x: 7, y: 11 }; + let mut env = TestEnvironment::new_opts(TestEnvironmentOptions::new().with_tag_secret_strategy( + OnchainDeliveryMode::onchain_constrained(), + TaggingSecretStrategy::arbitrary_secret(point), + )); + let app = env.create_contract_account(); + let recipient = AztecAddress::from_field(8); + + env.private_context_at(app, |_| { + let resolved = resolve_tagging_strategy( + AztecAddress::from_field(1), + recipient, + OnchainDeliveryMode::onchain_constrained(), + ); + + let expected_secret = compute_directional_app_secret(point, app, recipient); + assert_eq(resolved, ResolvedTaggingStrategy::unconstrained_secret(expected_secret)); + }); +} + #[test] unconstrained fn applies_the_strategy_set_in_the_options() { - let mut env = TestEnvironment::new_opts(TestEnvironmentOptions::new().with_tag_secret_strategy_all_modes( + let mut env = TestEnvironment::new_opts(TestEnvironmentOptions::new().with_tag_secret_strategy( + OnchainDeliveryMode::onchain_unconstrained(), TaggingSecretStrategy::arbitrary_secret(EmbeddedCurvePoint { x: 7, y: 11 }), )); - // The hook path looks up the executing contract's class ID, so run in a deployed contract's context. let app = env.create_contract_account(); let recipient = AztecAddress::from_field(8); @@ -129,10 +174,10 @@ unconstrained fn applies_the_strategy_set_in_the_options() { #[test] unconstrained fn app_silos_an_arbitrary_secret_point() { let point = EmbeddedCurvePoint { x: 7, y: 11 }; - let mut env = TestEnvironment::new_opts(TestEnvironmentOptions::new().with_tag_secret_strategy_all_modes( + let mut env = TestEnvironment::new_opts(TestEnvironmentOptions::new().with_tag_secret_strategy( + OnchainDeliveryMode::onchain_unconstrained(), TaggingSecretStrategy::arbitrary_secret(point), )); - // The hook path looks up the executing contract's class ID, so run in a deployed contract's context. let app = env.create_contract_account(); let recipient = AztecAddress::from_field(8); @@ -151,7 +196,8 @@ unconstrained fn app_silos_an_arbitrary_secret_point() { #[test] unconstrained fn overrides_a_configured_arbitrary_secret_for_an_unconstrained_self_send() { let point = EmbeddedCurvePoint { x: 7, y: 11 }; - let mut env = TestEnvironment::new_opts(TestEnvironmentOptions::new().with_tag_secret_strategy_all_modes( + let mut env = TestEnvironment::new_opts(TestEnvironmentOptions::new().with_tag_secret_strategy( + OnchainDeliveryMode::onchain_unconstrained(), TaggingSecretStrategy::arbitrary_secret(point), )); let app = env.create_contract_account(); From 34950513dc0574fc6cc0685bfdc754600674d24f Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Tue, 7 Jul 2026 15:13:58 -0400 Subject: [PATCH 09/21] non-interactive in txe resolve tagging secret strat --- .../test/helpers/tagging_secret_strategy.nr | 18 ++++++++++++++++-- .../oracle/private_execution_oracle.ts | 2 ++ .../hooks/resolve_tagging_secret_strategy.ts | 4 ++++ .../src/oracle/tagging_secret_strategy.test.ts | 13 ++++++++----- .../txe/src/oracle/txe_oracle_registry.ts | 5 +++++ 5 files changed, 35 insertions(+), 7 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/test/helpers/tagging_secret_strategy.nr b/noir-projects/aztec-nr/aztec/src/test/helpers/tagging_secret_strategy.nr index 71c6bfeb8f41..cf788e2912ad 100644 --- a/noir-projects/aztec-nr/aztec/src/test/helpers/tagging_secret_strategy.nr +++ b/noir-projects/aztec-nr/aztec/src/test/helpers/tagging_secret_strategy.nr @@ -10,6 +10,7 @@ use crate::protocol::{ global NON_INTERACTIVE_HANDSHAKE: u8 = 1; global ARBITRARY_SECRET: u8 = 2; global ADDRESS_DERIVED: u8 = 3; +global INTERACTIVE_HANDSHAKE: u8 = 4; /// How a message's tagging secret is chosen: the wallet's strategy. /// @@ -29,6 +30,11 @@ impl TaggingSecretStrategy { Self { kind: NON_INTERACTIVE_HANDSHAKE, secret: EmbeddedCurvePoint { x: 0, y: 0 } } } + /// A secret established through an interactive handshake. + pub fn interactive_handshake() -> Self { + Self { kind: INTERACTIVE_HANDSHAKE, secret: EmbeddedCurvePoint { x: 0, y: 0 } } + } + /// A secret derived from the sender's and recipient's address keys via Diffie-Hellman. /// /// The PXE performs the key exchange, so the wallet supplies no material. @@ -46,9 +52,10 @@ impl TaggingSecretStrategy { /// Validates a raw discriminant, as deserialization must always reject unknown values. fn from_parts(kind: u8, secret: EmbeddedCurvePoint) -> Self { let strategy = Self { kind, secret }; + let is_no_payload_strategy = + (kind == NON_INTERACTIVE_HANDSHAKE) | (kind == INTERACTIVE_HANDSHAKE) | (kind == ADDRESS_DERIVED); assert( - (((kind == NON_INTERACTIVE_HANDSHAKE) | (kind == ADDRESS_DERIVED)) & secret.is_infinite()) - | (kind == ARBITRARY_SECRET), + (is_no_payload_strategy & secret.is_infinite()) | (kind == ARBITRARY_SECRET), f"unrecognized tagging secret strategy kind: {kind}", ); strategy @@ -89,10 +96,12 @@ mod test { #[test] fn strategy_roundtrips_through_serialization() { let non_interactive = TaggingSecretStrategy::non_interactive_handshake(); + let interactive = TaggingSecretStrategy::interactive_handshake(); let address = TaggingSecretStrategy::address_derived(); let provided = TaggingSecretStrategy::arbitrary_secret(EmbeddedCurvePoint { x: 7, y: 11 }); assert(TaggingSecretStrategy::deserialize(non_interactive.serialize()) == non_interactive); + assert(TaggingSecretStrategy::deserialize(interactive.serialize()) == interactive); assert(TaggingSecretStrategy::deserialize(address.serialize()) == address); assert(TaggingSecretStrategy::deserialize(provided.serialize()) == provided); } @@ -107,6 +116,11 @@ mod test { let _ = TaggingSecretStrategy::deserialize([1, 7, 11]); } + #[test(should_fail_with = "unrecognized tagging secret strategy kind")] + fn deserializing_interactive_handshake_with_noninfinite_point_fails() { + let _ = TaggingSecretStrategy::deserialize([4, 7, 11]); + } + #[test(should_fail_with = "unrecognized tagging secret strategy kind")] fn deserializing_address_derived_with_noninfinite_point_fails() { let _ = TaggingSecretStrategy::deserialize([3, 7, 11]); diff --git a/yarn-project/pxe/src/contract_function_simulator/oracle/private_execution_oracle.ts b/yarn-project/pxe/src/contract_function_simulator/oracle/private_execution_oracle.ts index da88ce0da9fe..f0bbfb01b9ed 100644 --- a/yarn-project/pxe/src/contract_function_simulator/oracle/private_execution_oracle.ts +++ b/yarn-project/pxe/src/contract_function_simulator/oracle/private_execution_oracle.ts @@ -278,6 +278,8 @@ export class PrivateExecutionOracle extends UtilityExecutionOracle implements IP switch (strategy.type) { case 'non-interactive-handshake': return { type: 'non-interactive-handshake' }; + case 'interactive-handshake': + throw new Error('Interactive handshake tagging strategies are not supported by PXE resolution yet'); case 'address-derived': return this.#addressDerivedSecret(sender, recipient); case 'arbitrary-secret': { diff --git a/yarn-project/pxe/src/hooks/resolve_tagging_secret_strategy.ts b/yarn-project/pxe/src/hooks/resolve_tagging_secret_strategy.ts index 50e6dbcf23dc..c00e41beb353 100644 --- a/yarn-project/pxe/src/hooks/resolve_tagging_secret_strategy.ts +++ b/yarn-project/pxe/src/hooks/resolve_tagging_secret_strategy.ts @@ -13,6 +13,10 @@ export type TaggingSecretStrategy = /** Establish a fresh non-interactive handshake via the onchain registry; reveals the recipient onchain. */ type: 'non-interactive-handshake'; } + | { + /** Establish a recipient-authorized interactive handshake via the onchain registry. */ + type: 'interactive-handshake'; + } | { /** Derive the secret from the sender's and recipient's address keys via ECDH. PXE computes and silos it. */ type: 'address-derived'; diff --git a/yarn-project/txe/src/oracle/tagging_secret_strategy.test.ts b/yarn-project/txe/src/oracle/tagging_secret_strategy.test.ts index 15c735415544..ccb75a6eba1d 100644 --- a/yarn-project/txe/src/oracle/tagging_secret_strategy.test.ts +++ b/yarn-project/txe/src/oracle/tagging_secret_strategy.test.ts @@ -15,7 +15,7 @@ describe('makeResolveTaggingSecretStrategyHook', () => { it('selects a strategy by delivery mode', async () => { const unconstrained: TaggingSecretStrategy = { type: 'arbitrary-secret', secret: await Point.random() }; - const constrained: TaggingSecretStrategy = { type: 'non-interactive-handshake' }; + const constrained: TaggingSecretStrategy = { type: 'interactive-handshake' }; const hook = makeResolveTaggingSecretStrategyHook( new Map([ [AppTaggingSecretKind.UNCONSTRAINED, unconstrained], @@ -46,14 +46,17 @@ describe('makeResolveTaggingSecretStrategyHook', () => { toSingle(2), toSingle(5), toSingle(6), - // Constrained mode: none, zero-padded to the option's full width. - toSingle(0), - toSingle(0), + // Constrained mode: some(interactive-handshake). + toSingle(1), + toSingle(4), toSingle(0), toSingle(0), ], handler: ([unconstrainedStrategy, constrainedStrategy]) => { - expect(constrainedStrategy.isSome()).toBe(false); + if (!constrainedStrategy.isSome()) { + throw new Error('Expected a constrained-mode tagging secret strategy'); + } + expect(constrainedStrategy.value).toEqual({ type: 'interactive-handshake' }); if (!unconstrainedStrategy.isSome()) { throw new Error('Expected an unconstrained-mode tagging secret strategy'); } diff --git a/yarn-project/txe/src/oracle/txe_oracle_registry.ts b/yarn-project/txe/src/oracle/txe_oracle_registry.ts index 160a012681ff..47a6135603b4 100644 --- a/yarn-project/txe/src/oracle/txe_oracle_registry.ts +++ b/yarn-project/txe/src/oracle/txe_oracle_registry.ts @@ -72,6 +72,7 @@ const GAS_SETTINGS: TypeMapping = { const STRATEGY_NON_INTERACTIVE_HANDSHAKE = 1; const STRATEGY_ARBITRARY_SECRET = 2; const STRATEGY_ADDRESS_DERIVED = 3; +const STRATEGY_INTERACTIVE_HANDSHAKE = 4; const TAGGING_SECRET_STRATEGY: TypeMapping = { serialization: { @@ -79,6 +80,8 @@ const TAGGING_SECRET_STRATEGY: TypeMapping = { switch (strategy.type) { case 'non-interactive-handshake': return [new Fr(STRATEGY_NON_INTERACTIVE_HANDSHAKE), Fr.ZERO, Fr.ZERO]; + case 'interactive-handshake': + return [new Fr(STRATEGY_INTERACTIVE_HANDSHAKE), Fr.ZERO, Fr.ZERO]; case 'address-derived': return [new Fr(STRATEGY_ADDRESS_DERIVED), Fr.ZERO, Fr.ZERO]; case 'arbitrary-secret': @@ -93,6 +96,8 @@ const TAGGING_SECRET_STRATEGY: TypeMapping = { switch (kind) { case STRATEGY_NON_INTERACTIVE_HANDSHAKE: return { type: 'non-interactive-handshake' }; + case STRATEGY_INTERACTIVE_HANDSHAKE: + return { type: 'interactive-handshake' }; case STRATEGY_ADDRESS_DERIVED: return { type: 'address-derived' }; case STRATEGY_ARBITRARY_SECRET: From 62c76d36276b776134ed6482b572de503ae3f70a Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Tue, 7 Jul 2026 15:27:54 -0400 Subject: [PATCH 10/21] remove deserde test --- pied! | 54 +++++++++++++++++++ yarn-project/txe/.codex-txe-server.mjs | 19 +++++++ .../oracle/tagging_secret_strategy.test.ts | 36 ------------- 3 files changed, 73 insertions(+), 36 deletions(-) create mode 100644 pied! create mode 100644 yarn-project/txe/.codex-txe-server.mjs diff --git a/pied! b/pied! new file mode 100644 index 000000000000..623ea1551e50 --- /dev/null +++ b/pied! @@ -0,0 +1,54 @@ +diff --git a/yarn-project/txe/src/oracle/tagging_secret_strategy.test.ts b/yarn-project/txe/src/oracle/tagging_secret_strategy.test.ts +index fdd24cacfc..8a38811e6c 100644 +--- a/yarn-project/txe/src/oracle/tagging_secret_strategy.test.ts ++++ b/yarn-project/txe/src/oracle/tagging_secret_strategy.test.ts +@@ -4,9 +4,7 @@ import { DEFAULT_TAGGING_SECRET_STRATEGY, type TaggingSecretStrategy } from '@az + import { AztecAddress } from '@aztec/stdlib/aztec-address'; + import { AppTaggingSecretKind } from '@aztec/stdlib/logs'; +  +-import { toSingle } from '../utils/encoding.js'; + import { makeResolveTaggingSecretStrategyHook } from './tagging_secret_strategy.js'; +-import { callTxeHandler } from './txe_oracle_registry.js'; +  + describe('makeResolveTaggingSecretStrategyHook', () => { + it('returns undefined when no TXE strategy is configured', () => { +@@ -37,39 +35,6 @@ describe('makeResolveTaggingSecretStrategyHook', () => { + ); + }); +  +- it('deserializes the per-mode TXE oracle setter', async () => { +- const received = await callTxeHandler({ +- oracle: 'aztec_txe_setTaggingSecretStrategies', +- inputs: [ +- // Unconstrained mode: some(arbitrary-secret with point (5, 6)). +- toSingle(1), +- toSingle(2), +- toSingle(5), +- toSingle(6), +- // Constrained mode: some(interactive-handshake). +- toSingle(1), +- toSingle(4), +- toSingle(0), +- toSingle(0), +- ], +- handler: ([unconstrainedStrategy, constrainedStrategy]) => { +- if (!constrainedStrategy.isSome()) { +- throw new Error('Expected a constrained-mode tagging secret strategy'); +- } +- expect(constrainedStrategy.value).toEqual({ type: 'interactive-handshake' }); +- if (!unconstrainedStrategy.isSome()) { +- throw new Error('Expected an unconstrained-mode tagging secret strategy'); +- } +- const strategy = unconstrainedStrategy.value; +- if (strategy.type !== 'arbitrary-secret') { +- throw new Error(`Expected an arbitrary-secret strategy, got '${strategy.type}'`); +- } +- expect(strategy.secret.equals(new Point(new Fr(5), new Fr(6)))).toBe(true); +- }, +- }); +- +- expect(received.values).toEqual([]); +- }); + }); +  + function makeRequest(deliveryMode: AppTaggingSecretKind) { diff --git a/yarn-project/txe/.codex-txe-server.mjs b/yarn-project/txe/.codex-txe-server.mjs new file mode 100644 index 000000000000..27f8e6a654bc --- /dev/null +++ b/yarn-project/txe/.codex-txe-server.mjs @@ -0,0 +1,19 @@ +process.env.HARDWARE_CONCURRENCY ??= '2'; + +const { createLogger } = await import('@aztec/aztec.js/log'); +const { startHttpRpcServer } = await import('@aztec/foundation/json-rpc/server'); +const { createTXERpcServer } = await import('./dest/server.bundle.js'); + +const logger = createLogger('txe:rpc'); +const server = await createTXERpcServer(logger); +const { port } = await startHttpRpcServer(server, { + host: '127.0.0.1', + port: Number(process.env.TXE_PORT ?? 14730), + timeoutMs: 300000, +}); + +logger.info('TXE listening', { port }); +process.on('SIGTERM', () => process.exit(0)); +process.on('SIGINT', () => process.exit(0)); + +await new Promise(() => {}); diff --git a/yarn-project/txe/src/oracle/tagging_secret_strategy.test.ts b/yarn-project/txe/src/oracle/tagging_secret_strategy.test.ts index fdd24cacfca0..c75bced9b4c6 100644 --- a/yarn-project/txe/src/oracle/tagging_secret_strategy.test.ts +++ b/yarn-project/txe/src/oracle/tagging_secret_strategy.test.ts @@ -4,9 +4,7 @@ import { DEFAULT_TAGGING_SECRET_STRATEGY, type TaggingSecretStrategy } from '@az import { AztecAddress } from '@aztec/stdlib/aztec-address'; import { AppTaggingSecretKind } from '@aztec/stdlib/logs'; -import { toSingle } from '../utils/encoding.js'; import { makeResolveTaggingSecretStrategyHook } from './tagging_secret_strategy.js'; -import { callTxeHandler } from './txe_oracle_registry.js'; describe('makeResolveTaggingSecretStrategyHook', () => { it('returns undefined when no TXE strategy is configured', () => { @@ -36,40 +34,6 @@ describe('makeResolveTaggingSecretStrategyHook', () => { DEFAULT_TAGGING_SECRET_STRATEGY, ); }); - - it('deserializes the per-mode TXE oracle setter', async () => { - const received = await callTxeHandler({ - oracle: 'aztec_txe_setTaggingSecretStrategies', - inputs: [ - // Unconstrained mode: some(arbitrary-secret with point (5, 6)). - toSingle(1), - toSingle(2), - toSingle(5), - toSingle(6), - // Constrained mode: some(interactive-handshake). - toSingle(1), - toSingle(4), - toSingle(0), - toSingle(0), - ], - handler: ([unconstrainedStrategy, constrainedStrategy]) => { - if (!constrainedStrategy.isSome()) { - throw new Error('Expected a constrained-mode tagging secret strategy'); - } - expect(constrainedStrategy.value).toEqual({ type: 'interactive-handshake' }); - if (!unconstrainedStrategy.isSome()) { - throw new Error('Expected an unconstrained-mode tagging secret strategy'); - } - const strategy = unconstrainedStrategy.value; - if (strategy.type !== 'arbitrary-secret') { - throw new Error(`Expected an arbitrary-secret strategy, got '${strategy.type}'`); - } - expect(strategy.secret.equals(new Point(new Fr(5), new Fr(6)))).toBe(true); - }, - }); - - expect(received.values).toEqual([]); - }); }); function makeRequest(deliveryMode: AppTaggingSecretKind) { From 7d64a2651afd132193531d0a8730fb06f9285b6d Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Tue, 7 Jul 2026 15:28:02 -0400 Subject: [PATCH 11/21] .' --- pied! | 54 ------------------------------------------------------ 1 file changed, 54 deletions(-) delete mode 100644 pied! diff --git a/pied! b/pied! deleted file mode 100644 index 623ea1551e50..000000000000 --- a/pied! +++ /dev/null @@ -1,54 +0,0 @@ -diff --git a/yarn-project/txe/src/oracle/tagging_secret_strategy.test.ts b/yarn-project/txe/src/oracle/tagging_secret_strategy.test.ts -index fdd24cacfc..8a38811e6c 100644 ---- a/yarn-project/txe/src/oracle/tagging_secret_strategy.test.ts -+++ b/yarn-project/txe/src/oracle/tagging_secret_strategy.test.ts -@@ -4,9 +4,7 @@ import { DEFAULT_TAGGING_SECRET_STRATEGY, type TaggingSecretStrategy } from '@az - import { AztecAddress } from '@aztec/stdlib/aztec-address'; - import { AppTaggingSecretKind } from '@aztec/stdlib/logs'; -  --import { toSingle } from '../utils/encoding.js'; - import { makeResolveTaggingSecretStrategyHook } from './tagging_secret_strategy.js'; --import { callTxeHandler } from './txe_oracle_registry.js'; -  - describe('makeResolveTaggingSecretStrategyHook', () => { - it('returns undefined when no TXE strategy is configured', () => { -@@ -37,39 +35,6 @@ describe('makeResolveTaggingSecretStrategyHook', () => { - ); - }); -  -- it('deserializes the per-mode TXE oracle setter', async () => { -- const received = await callTxeHandler({ -- oracle: 'aztec_txe_setTaggingSecretStrategies', -- inputs: [ -- // Unconstrained mode: some(arbitrary-secret with point (5, 6)). -- toSingle(1), -- toSingle(2), -- toSingle(5), -- toSingle(6), -- // Constrained mode: some(interactive-handshake). -- toSingle(1), -- toSingle(4), -- toSingle(0), -- toSingle(0), -- ], -- handler: ([unconstrainedStrategy, constrainedStrategy]) => { -- if (!constrainedStrategy.isSome()) { -- throw new Error('Expected a constrained-mode tagging secret strategy'); -- } -- expect(constrainedStrategy.value).toEqual({ type: 'interactive-handshake' }); -- if (!unconstrainedStrategy.isSome()) { -- throw new Error('Expected an unconstrained-mode tagging secret strategy'); -- } -- const strategy = unconstrainedStrategy.value; -- if (strategy.type !== 'arbitrary-secret') { -- throw new Error(`Expected an arbitrary-secret strategy, got '${strategy.type}'`); -- } -- expect(strategy.secret.equals(new Point(new Fr(5), new Fr(6)))).toBe(true); -- }, -- }); -- -- expect(received.values).toEqual([]); -- }); - }); -  - function makeRequest(deliveryMode: AppTaggingSecretKind) { From da2d4848d8765d3cf86b9526df05831d2f0b139a Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Tue, 7 Jul 2026 15:29:47 -0400 Subject: [PATCH 12/21] reduce diff --- .../aztec/src/test/helpers/tagging_secret_strategy.nr | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/test/helpers/tagging_secret_strategy.nr b/noir-projects/aztec-nr/aztec/src/test/helpers/tagging_secret_strategy.nr index cf788e2912ad..c14bdb8b6d6c 100644 --- a/noir-projects/aztec-nr/aztec/src/test/helpers/tagging_secret_strategy.nr +++ b/noir-projects/aztec-nr/aztec/src/test/helpers/tagging_secret_strategy.nr @@ -117,12 +117,12 @@ mod test { } #[test(should_fail_with = "unrecognized tagging secret strategy kind")] - fn deserializing_interactive_handshake_with_noninfinite_point_fails() { + fn deserializing_address_derived_with_noninfinite_point_fails() { let _ = TaggingSecretStrategy::deserialize([4, 7, 11]); } #[test(should_fail_with = "unrecognized tagging secret strategy kind")] - fn deserializing_address_derived_with_noninfinite_point_fails() { + fn deserializing_interactive_handshake_with_noninfinite_point_fails() { let _ = TaggingSecretStrategy::deserialize([3, 7, 11]); } } From ad28bcf32af39d32212fbf31674fd079dce23290 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Tue, 7 Jul 2026 15:42:40 -0400 Subject: [PATCH 13/21] improve deserialization/serialization coverage --- .../test/resolve_tagging_strategy.nr | 2 +- .../noir-structs/resolved_tagging_strategy.ts | 14 +++++++- .../oracle/oracle_type_mappings.test.ts | 36 ++++++++++++++----- .../oracle/tagging_secret_strategy.test.ts | 2 +- 4 files changed, 42 insertions(+), 12 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment/test/resolve_tagging_strategy.nr b/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment/test/resolve_tagging_strategy.nr index 648768fb8436..5a8c0be138e3 100644 --- a/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment/test/resolve_tagging_strategy.nr +++ b/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment/test/resolve_tagging_strategy.nr @@ -35,7 +35,7 @@ unconstrained fn an_unset_unconstrained_mode_falls_back_to_the_default_strategy( // handshake strategy for an external recipient. let mut env = TestEnvironment::new_opts(TestEnvironmentOptions::new().with_tag_secret_strategy( OnchainDeliveryMode::onchain_constrained(), - TaggingSecretStrategy::non_interactive_handshake(), + TaggingSecretStrategy::interactive_handshake(), )); let app = env.create_contract_account(); let sender = env.create_light_account(); diff --git a/yarn-project/pxe/src/contract_function_simulator/noir-structs/resolved_tagging_strategy.ts b/yarn-project/pxe/src/contract_function_simulator/noir-structs/resolved_tagging_strategy.ts index 8b1930281d3e..5cf811dc3e2f 100644 --- a/yarn-project/pxe/src/contract_function_simulator/noir-structs/resolved_tagging_strategy.ts +++ b/yarn-project/pxe/src/contract_function_simulator/noir-structs/resolved_tagging_strategy.ts @@ -47,12 +47,24 @@ export function resolvedTaggingStrategyToFields(resolved: ResolvedTaggingStrateg export function resolvedTaggingStrategyFromFields(kind: number, secret: Fr): ResolvedTaggingStrategy { switch (kind) { case NON_INTERACTIVE_HANDSHAKE: + assertAbsentSecret(kind, secret); return { type: 'non-interactive-handshake' }; case INTERACTIVE_HANDSHAKE: + assertAbsentSecret(kind, secret); return { type: 'interactive-handshake' }; case UNCONSTRAINED_SECRET: return { type: 'unconstrained-secret', secret }; default: - throw new Error(`Unrecognized resolved tagging strategy kind: ${kind}`); + throw unrecognizedResolvedTaggingStrategy(kind); } } + +function assertAbsentSecret(kind: number, secret: Fr): void { + if (!secret.isZero()) { + throw unrecognizedResolvedTaggingStrategy(kind); + } +} + +function unrecognizedResolvedTaggingStrategy(kind: number): Error { + return new Error(`Unrecognized resolved tagging strategy kind: ${kind}`); +} diff --git a/yarn-project/pxe/src/contract_function_simulator/oracle/oracle_type_mappings.test.ts b/yarn-project/pxe/src/contract_function_simulator/oracle/oracle_type_mappings.test.ts index fcabc5e9f9b8..29a1c57ab184 100644 --- a/yarn-project/pxe/src/contract_function_simulator/oracle/oracle_type_mappings.test.ts +++ b/yarn-project/pxe/src/contract_function_simulator/oracle/oracle_type_mappings.test.ts @@ -8,6 +8,7 @@ import { EphemeralArrayService } from '../ephemeral_array_service.js'; import { BoundedVec } from '../noir-structs/bounded_vec.js'; import { type LogRetrievalRequest, LogSource } from '../noir-structs/log_retrieval_request.js'; import { Option } from '../noir-structs/option.js'; +import type { ResolvedTaggingStrategy } from '../noir-structs/resolved_tagging_strategy.js'; import { ARRAY, AZTEC_ADDRESS, @@ -109,18 +110,35 @@ describe('oracle type mappings', () => { }); describe('RESOLVED_TAGGING_STRATEGY', () => { - it('serializes an interactive handshake to the Noir discriminant', () => { - expect(RESOLVED_TAGGING_STRATEGY.serialization!.fn({ type: 'interactive-handshake' })).toEqual([ - new Fr(3), - Fr.ZERO, - ]); + const secret = new Fr(42); + const strategyCases: [string, ResolvedTaggingStrategy, Fr[]][] = [ + ['non-interactive handshake', { type: 'non-interactive-handshake' }, [new Fr(1), Fr.ZERO]], + ['unconstrained secret', { type: 'unconstrained-secret', secret }, [new Fr(2), secret]], + ['interactive handshake', { type: 'interactive-handshake' }, [new Fr(3), Fr.ZERO]], + ]; + + it.each(strategyCases)('serializes %s to the Noir fields', (_name, strategy, fields) => { + expect(RESOLVED_TAGGING_STRATEGY.serialization!.fn(strategy)).toEqual(fields); }); - it('round-trips an interactive handshake', () => { - expect(roundTrip(RESOLVED_TAGGING_STRATEGY, { type: 'interactive-handshake' })).toEqual({ - type: 'interactive-handshake', - }); + it.each(strategyCases)('deserializes %s from the Noir fields', (_name, strategy, [kind, strategySecret]) => { + expect(deserializeStrategy(kind, strategySecret)).toEqual(strategy); + }); + + it('rejects an unknown strategy kind', () => { + expect(() => deserializeStrategy(new Fr(99), Fr.ZERO)).toThrow('Unrecognized resolved tagging strategy kind'); + }); + + it.each([ + ['non-interactive handshake', new Fr(1)], + ['interactive handshake', new Fr(3)], + ])('rejects %s with a nonzero secret', (_name, kind) => { + expect(() => deserializeStrategy(kind, secret)).toThrow('Unrecognized resolved tagging strategy kind'); }); + + function deserializeStrategy(kind: Fr, secret: Fr): ResolvedTaggingStrategy { + return RESOLVED_TAGGING_STRATEGY.deserialization!.fn([new FieldReader([kind]), new FieldReader([secret])]); + } }); describe('AZTEC_ADDRESS', () => { diff --git a/yarn-project/txe/src/oracle/tagging_secret_strategy.test.ts b/yarn-project/txe/src/oracle/tagging_secret_strategy.test.ts index c75bced9b4c6..ef2f03e2b97a 100644 --- a/yarn-project/txe/src/oracle/tagging_secret_strategy.test.ts +++ b/yarn-project/txe/src/oracle/tagging_secret_strategy.test.ts @@ -25,7 +25,7 @@ describe('makeResolveTaggingSecretStrategyHook', () => { await expect(hook?.(makeRequest(AppTaggingSecretKind.CONSTRAINED))).resolves.toBe(constrained); }); - it('defaults an unset mode wto PXE default strategy when another mode is configured', async () => { + it('defaults an unset mode to PXE default strategy when another mode is configured', async () => { const unconstrained = { type: 'address-derived' as const }; const hook = makeResolveTaggingSecretStrategyHook(new Map([[AppTaggingSecretKind.UNCONSTRAINED, unconstrained]])); From 51d3d4aec91f4c5c67e5b9dd21ee0d917c5a8b94 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Tue, 7 Jul 2026 15:46:15 -0400 Subject: [PATCH 14/21] cleanup --- .../src/test.nr | 12 ++++-------- yarn-project/txe/.codex-txe-server.mjs | 19 ------------------- 2 files changed, 4 insertions(+), 27 deletions(-) delete mode 100644 yarn-project/txe/.codex-txe-server.mjs diff --git a/noir-projects/noir-contracts/contracts/test/onchain_delivery_test_contract/src/test.nr b/noir-projects/noir-contracts/contracts/test/onchain_delivery_test_contract/src/test.nr index fb1f2e14cb3b..cc6db01f9b06 100644 --- a/noir-projects/noir-contracts/contracts/test/onchain_delivery_test_contract/src/test.nr +++ b/noir-projects/noir-contracts/contracts/test/onchain_delivery_test_contract/src/test.nr @@ -52,16 +52,12 @@ unconstrained fn external_recipient() -> AztecAddress { AztecAddress::from_field(8) } -unconstrained fn arbitrary_secret_point() -> EmbeddedCurvePoint { - EmbeddedCurvePoint { - x: 0x15d55a5b3b2caa6a6207f313f05c5113deba5da9927d6421bcaa164822b911bc, - y: 0x0974c3d0825031ae933243d653ebb1a0b08b90ee7f228f94c5c74739ea3c871e, - } -} - #[test] unconstrained fn unconstrained_delivery_works_alongside_constrained_delivery() { - let point = arbitrary_secret_point(); + let point = EmbeddedCurvePoint { + x: 0x15d55a5b3b2caa6a6207f313f05c5113deba5da9927d6421bcaa164822b911bc, + y: 0x0974c3d0825031ae933243d653ebb1a0b08b90ee7f228f94c5c74739ea3c871e, + }; // Only the unconstrained mode is configured: the constrained mode falls back to the default non-interactive // handshake strategy, so the constrained delivery below still bootstraps a registry handshake. let options = TestEnvironmentOptions::new().with_tag_secret_strategy( diff --git a/yarn-project/txe/.codex-txe-server.mjs b/yarn-project/txe/.codex-txe-server.mjs deleted file mode 100644 index 27f8e6a654bc..000000000000 --- a/yarn-project/txe/.codex-txe-server.mjs +++ /dev/null @@ -1,19 +0,0 @@ -process.env.HARDWARE_CONCURRENCY ??= '2'; - -const { createLogger } = await import('@aztec/aztec.js/log'); -const { startHttpRpcServer } = await import('@aztec/foundation/json-rpc/server'); -const { createTXERpcServer } = await import('./dest/server.bundle.js'); - -const logger = createLogger('txe:rpc'); -const server = await createTXERpcServer(logger); -const { port } = await startHttpRpcServer(server, { - host: '127.0.0.1', - port: Number(process.env.TXE_PORT ?? 14730), - timeoutMs: 300000, -}); - -logger.info('TXE listening', { port }); -process.on('SIGTERM', () => process.exit(0)); -process.on('SIGINT', () => process.exit(0)); - -await new Promise(() => {}); From deb3c2f0567908a06a3072832cc6699cad489097 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Tue, 7 Jul 2026 15:53:39 -0400 Subject: [PATCH 15/21] test fix --- .../src/test/helpers/tagging_secret_strategy.nr | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/test/helpers/tagging_secret_strategy.nr b/noir-projects/aztec-nr/aztec/src/test/helpers/tagging_secret_strategy.nr index c14bdb8b6d6c..5d01b215a6e6 100644 --- a/noir-projects/aztec-nr/aztec/src/test/helpers/tagging_secret_strategy.nr +++ b/noir-projects/aztec-nr/aztec/src/test/helpers/tagging_secret_strategy.nr @@ -113,16 +113,22 @@ mod test { #[test(should_fail_with = "unrecognized tagging secret strategy kind")] fn deserializing_handshake_with_noninfinite_point_fails() { - let _ = TaggingSecretStrategy::deserialize([1, 7, 11]); + let _ = TaggingSecretStrategy::deserialize([ + TaggingSecretStrategy::non_interactive_handshake().kind as Field, + 7, + 11, + ]); } #[test(should_fail_with = "unrecognized tagging secret strategy kind")] fn deserializing_address_derived_with_noninfinite_point_fails() { - let _ = TaggingSecretStrategy::deserialize([4, 7, 11]); + let _ = TaggingSecretStrategy::deserialize( + [TaggingSecretStrategy::address_derived().kind as Field, 7, 11], + ); } #[test(should_fail_with = "unrecognized tagging secret strategy kind")] fn deserializing_interactive_handshake_with_noninfinite_point_fails() { - let _ = TaggingSecretStrategy::deserialize([3, 7, 11]); + let _ = TaggingSecretStrategy::deserialize([TaggingSecretStrategy::interactive_handshake().kind as Field, 7, 11]); } } From 91933f0e958332df15384f7a39088c9a46b8ab6d Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Tue, 7 Jul 2026 15:56:17 -0400 Subject: [PATCH 16/21] test fix and docs --- .../docs/foundational-topics/pxe/execution_hooks.md | 13 +++++++++---- .../test/onchain_delivery_test_contract/src/test.nr | 5 ++++- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/docs/docs-developers/docs/foundational-topics/pxe/execution_hooks.md b/docs/docs-developers/docs/foundational-topics/pxe/execution_hooks.md index e1c738b84f1f..0aa25d29da26 100644 --- a/docs/docs-developers/docs/foundational-topics/pxe/execution_hooks.md +++ b/docs/docs-developers/docs/foundational-topics/pxe/execution_hooks.md @@ -82,14 +82,19 @@ For an unconstrained self-send (the recipient is one of the wallet's own account ### In Noir tests -When testing in Noir, leaving the strategy unset makes `TestEnvironment` fall back to the bare PXE default. Set a strategy when creating the environment to exercise a specific one; it affects message delivery in private executions. Use `with_tag_secret_strategy` to configure a single delivery mode, or `with_tag_secret_strategy_all_modes` for both at once: +When testing in Noir, leaving the strategy unset makes `TestEnvironment` fall back to the bare PXE default. Set a strategy when creating the environment to exercise a specific one; it affects message delivery in private executions. Use `with_tag_secret_strategy` to configure the strategy for a specific delivery mode: ```rust -let env = TestEnvironment::new_opts(TestEnvironmentOptions::new().with_tag_secret_strategy_all_modes( - TaggingSecretStrategy::non_interactive_handshake(), -)); +let env = TestEnvironment::new_opts( + TestEnvironmentOptions::new().with_tag_secret_strategy( + OnchainDeliveryMode::onchain_unconstrained(), + TaggingSecretStrategy::non_interactive_handshake(), + ), +); ``` +Use `with_tag_secret_strategy_all_modes` only when the same strategy should apply to both constrained and unconstrained delivery. + ### In production Pass a `resolveTaggingSecretStrategy` hook when [creating the PXE](#configuring-hooks). It receives a `TaggingSecretStrategyRequest` with the executing contract's address and the message's sender, recipient, and delivery mode (`'constrained'` or `'unconstrained'`), so a wallet can apply per-application or per-recipient policies, or surface the decision to the user, instead of returning a fixed value. diff --git a/noir-projects/noir-contracts/contracts/test/onchain_delivery_test_contract/src/test.nr b/noir-projects/noir-contracts/contracts/test/onchain_delivery_test_contract/src/test.nr index cc6db01f9b06..fe8f0f94e8c2 100644 --- a/noir-projects/noir-contracts/contracts/test/onchain_delivery_test_contract/src/test.nr +++ b/noir-projects/noir-contracts/contracts/test/onchain_delivery_test_contract/src/test.nr @@ -176,7 +176,10 @@ unconstrained fn interactive_handshake_reuses_an_existing_registry_handshake() { test_contract.emit_event_via_interactive_handshake(recipient, 1), ); - let next_index = env.call_private(sender, test_contract.next_index_for_secret(secret)); + let next_index = env.call_private( + sender, + test_contract.next_index_for_secret(secret, MessageDelivery::onchain_constrained().into()), + ); assert_eq(next_index, 1); } From 32bad6b252b0136ff85fd5bb41c7c4caf586723b Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Tue, 7 Jul 2026 16:04:40 -0400 Subject: [PATCH 17/21] chore(txe): update oracle interface hash --- yarn-project/txe/src/oracle/txe_oracle_version.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yarn-project/txe/src/oracle/txe_oracle_version.ts b/yarn-project/txe/src/oracle/txe_oracle_version.ts index d87fcad45704..4b221cac6814 100644 --- a/yarn-project/txe/src/oracle/txe_oracle_version.ts +++ b/yarn-project/txe/src/oracle/txe_oracle_version.ts @@ -14,4 +14,4 @@ export const TXE_ORACLE_VERSION_MINOR = 0; * - TXE_ORACLE_VERSION_MAJOR (and reset MINOR to 0) for breaking changes, or * - TXE_ORACLE_VERSION_MINOR for additive changes (new oracle method added). */ -export const TXE_ORACLE_INTERFACE_HASH = 'c611d6d0d9709755207719864a630a8bc7b8e53efa1be7f86088fcea4b48f1b3'; +export const TXE_ORACLE_INTERFACE_HASH = 'bea75bd85baebd7aa1438efb1defab496d27540bc805a8d854bb74b8d45fd859'; From 0801ac410046e00b813f68a33dcd731a0a4cbd58 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Tue, 7 Jul 2026 16:19:25 -0400 Subject: [PATCH 18/21] more test cleanup --- .../foundational-topics/pxe/execution_hooks.md | 2 +- .../test/resolve_tagging_strategy.nr | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/docs-developers/docs/foundational-topics/pxe/execution_hooks.md b/docs/docs-developers/docs/foundational-topics/pxe/execution_hooks.md index 0aa25d29da26..2eb8662a4a58 100644 --- a/docs/docs-developers/docs/foundational-topics/pxe/execution_hooks.md +++ b/docs/docs-developers/docs/foundational-topics/pxe/execution_hooks.md @@ -87,7 +87,7 @@ When testing in Noir, leaving the strategy unset makes `TestEnvironment` fall ba ```rust let env = TestEnvironment::new_opts( TestEnvironmentOptions::new().with_tag_secret_strategy( - OnchainDeliveryMode::onchain_unconstrained(), + MessageDelivery::onchain_unconstrained(), TaggingSecretStrategy::non_interactive_handshake(), ), ); diff --git a/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment/test/resolve_tagging_strategy.nr b/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment/test/resolve_tagging_strategy.nr index 5a8c0be138e3..a78f7f773718 100644 --- a/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment/test/resolve_tagging_strategy.nr +++ b/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment/test/resolve_tagging_strategy.nr @@ -1,5 +1,5 @@ use crate::{ - messages::delivery::{OnchainDeliveryMode, ResolvedTaggingStrategy}, + messages::delivery::{MessageDelivery, OnchainDeliveryMode, ResolvedTaggingStrategy}, oracle::resolve_tagging_strategy::resolve_tagging_strategy, protocol::{address::AztecAddress, point::EmbeddedCurvePoint, traits::FromField}, test::helpers::{ @@ -13,7 +13,7 @@ unconstrained fn an_unset_constrained_mode_falls_back_to_the_default_strategy() // Only the unconstrained mode is configured: the constrained mode falls back to the default non-interactive // handshake strategy. let mut env = TestEnvironment::new_opts(TestEnvironmentOptions::new().with_tag_secret_strategy( - OnchainDeliveryMode::onchain_unconstrained(), + MessageDelivery::onchain_unconstrained(), TaggingSecretStrategy::arbitrary_secret(EmbeddedCurvePoint { x: 7, y: 11 }), )); let app = env.create_contract_account(); @@ -34,7 +34,7 @@ unconstrained fn an_unset_unconstrained_mode_falls_back_to_the_default_strategy( // Only the constrained mode is configured: the unconstrained mode falls back to the default non-interactive // handshake strategy for an external recipient. let mut env = TestEnvironment::new_opts(TestEnvironmentOptions::new().with_tag_secret_strategy( - OnchainDeliveryMode::onchain_constrained(), + MessageDelivery::onchain_constrained(), TaggingSecretStrategy::interactive_handshake(), )); let app = env.create_contract_account(); @@ -128,9 +128,9 @@ unconstrained fn constrained_delivery_succeeds_with_a_configured_strategy() { } #[test] -unconstrained fn constrained_delivery_succeeds_with_a_configured_interactive_handshake() { +unconstrained fn applies_a_configured_interactive_handshake_strategy() { let mut env = TestEnvironment::new_opts(TestEnvironmentOptions::new().with_tag_secret_strategy( - OnchainDeliveryMode::onchain_constrained(), + MessageDelivery::onchain_constrained(), TaggingSecretStrategy::interactive_handshake(), )); let app = env.create_contract_account(); @@ -153,7 +153,7 @@ unconstrained fn constrained_mode_arbitrary_secret_resolves_without_rejecting() // `tag::test::constrained_delivery_rejects_a_resolved_unconstrained_secret`. let point = EmbeddedCurvePoint { x: 7, y: 11 }; let mut env = TestEnvironment::new_opts(TestEnvironmentOptions::new().with_tag_secret_strategy( - OnchainDeliveryMode::onchain_constrained(), + MessageDelivery::onchain_constrained(), TaggingSecretStrategy::arbitrary_secret(point), )); let app = env.create_contract_account(); @@ -174,7 +174,7 @@ unconstrained fn constrained_mode_arbitrary_secret_resolves_without_rejecting() #[test] unconstrained fn applies_the_strategy_set_in_the_options() { let mut env = TestEnvironment::new_opts(TestEnvironmentOptions::new().with_tag_secret_strategy( - OnchainDeliveryMode::onchain_unconstrained(), + MessageDelivery::onchain_unconstrained(), TaggingSecretStrategy::arbitrary_secret(EmbeddedCurvePoint { x: 7, y: 11 }), )); let app = env.create_contract_account(); @@ -194,7 +194,7 @@ unconstrained fn applies_the_strategy_set_in_the_options() { unconstrained fn app_silos_an_arbitrary_secret_point() { let point = EmbeddedCurvePoint { x: 7, y: 11 }; let mut env = TestEnvironment::new_opts(TestEnvironmentOptions::new().with_tag_secret_strategy( - OnchainDeliveryMode::onchain_unconstrained(), + MessageDelivery::onchain_unconstrained(), TaggingSecretStrategy::arbitrary_secret(point), )); let app = env.create_contract_account(); @@ -216,7 +216,7 @@ unconstrained fn app_silos_an_arbitrary_secret_point() { unconstrained fn overrides_a_configured_arbitrary_secret_for_an_unconstrained_self_send() { let point = EmbeddedCurvePoint { x: 7, y: 11 }; let mut env = TestEnvironment::new_opts(TestEnvironmentOptions::new().with_tag_secret_strategy( - OnchainDeliveryMode::onchain_unconstrained(), + MessageDelivery::onchain_unconstrained(), TaggingSecretStrategy::arbitrary_secret(point), )); let app = env.create_contract_account(); From 6a1a6fdf27414a34bc169fb0cae67e3aa9134166 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Wed, 8 Jul 2026 16:12:33 -0400 Subject: [PATCH 19/21] fix: clarify resolved tagging strategy helpers --- .../pxe/execution_hooks.md | 10 +++++--- .../docs/resources/migration_notes.md | 20 ++++++++++++++++ .../test/helpers/tagging_secret_strategy.nr | 2 +- .../src/test/helpers/test_environment.nr | 20 ++++++++-------- .../test/resolve_tagging_strategy.nr | 23 +++++++++---------- .../src/test.nr | 2 +- .../noir-structs/resolved_tagging_strategy.ts | 2 +- .../oracle/oracle_type_mappings.test.ts | 15 +----------- 8 files changed, 52 insertions(+), 42 deletions(-) diff --git a/docs/docs-developers/docs/foundational-topics/pxe/execution_hooks.md b/docs/docs-developers/docs/foundational-topics/pxe/execution_hooks.md index 2eb8662a4a58..6b3dc2c43775 100644 --- a/docs/docs-developers/docs/foundational-topics/pxe/execution_hooks.md +++ b/docs/docs-developers/docs/foundational-topics/pxe/execution_hooks.md @@ -82,18 +82,22 @@ For an unconstrained self-send (the recipient is one of the wallet's own account ### In Noir tests -When testing in Noir, leaving the strategy unset makes `TestEnvironment` fall back to the bare PXE default. Set a strategy when creating the environment to exercise a specific one; it affects message delivery in private executions. Use `with_tag_secret_strategy` to configure the strategy for a specific delivery mode: +When testing in Noir, leaving the strategy unset makes `TestEnvironment` fall back to the bare PXE default. Set a strategy +when creating the environment to exercise a specific one; it affects message delivery in private executions that use the +default wallet strategy hook. Use `with_default_tag_secret_strategy` to configure the strategy for a specific delivery +mode: ```rust let env = TestEnvironment::new_opts( - TestEnvironmentOptions::new().with_tag_secret_strategy( + TestEnvironmentOptions::new().with_default_tag_secret_strategy( MessageDelivery::onchain_unconstrained(), TaggingSecretStrategy::non_interactive_handshake(), ), ); ``` -Use `with_tag_secret_strategy_all_modes` only when the same strategy should apply to both constrained and unconstrained delivery. +Use `with_default_tag_secret_strategy_all_modes` only when the same strategy should apply to both constrained and +unconstrained delivery. Contract-fixed delivery derivations bypass this default strategy. ### In production diff --git a/docs/docs-developers/docs/resources/migration_notes.md b/docs/docs-developers/docs/resources/migration_notes.md index 41b06758fcec..340e2121dfc7 100644 --- a/docs/docs-developers/docs/resources/migration_notes.md +++ b/docs/docs-developers/docs/resources/migration_notes.md @@ -134,6 +134,26 @@ When no `resolveTaggingSecretStrategy` hook is configured, onchain unconstrained **Impact**: An external recipient can now discover unconstrained-delivered messages without having registered the sender in advance, but establishing the handshake publishes an onchain marker derived from the recipient's address (anyone who knows that address can tell a handshake was created for them, though not by whom nor the contents). Wallets that want the previous behavior can configure a `resolveTaggingSecretStrategy` hook that returns an `address-derived` strategy. +### [Aztec.nr] `TestEnvironmentOptions::with_tagging_secret_strategy` replaced + +`TestEnvironmentOptions::with_tagging_secret_strategy` is now `with_default_tag_secret_strategy_all_modes` for tests +that want the same default wallet strategy for both onchain delivery modes. The new naming reflects that these helpers +configure the TXE default wallet strategy hook; contract-fixed delivery derivations bypass that default. + +For mode-specific defaults and hook semantics, see the +[`resolveTaggingSecretStrategy` test helper docs](../foundational-topics/pxe/execution_hooks.md#resolvetaggingsecretstrategy). + +**Migration:** + +```diff +- TestEnvironmentOptions::new().with_tagging_secret_strategy(TaggingSecretStrategy::address_derived()) ++ TestEnvironmentOptions::new().with_default_tag_secret_strategy_all_modes( ++ TaggingSecretStrategy::address_derived(), ++ ) +``` + +**Impact**: Noir tests using the old helper name no longer compile until renamed. Message delivery behavior is unchanged. + ### [Aztec.nr] `PrivateContext` data fields are no longer public `PrivateContext`'s data fields are now private (or crate-internal): its public API is now exclusively its methods. Contracts that read these fields directly must switch to the corresponding getter. A new `get_side_effect_counter()` getter exposes the side-effect counter, and a new `is_static_call()` getter replaces reaching into `inputs.call_context`. The `get_anchor_block_header()` getter already existed. diff --git a/noir-projects/aztec-nr/aztec/src/test/helpers/tagging_secret_strategy.nr b/noir-projects/aztec-nr/aztec/src/test/helpers/tagging_secret_strategy.nr index 5d01b215a6e6..8a694b5b3592 100644 --- a/noir-projects/aztec-nr/aztec/src/test/helpers/tagging_secret_strategy.nr +++ b/noir-projects/aztec-nr/aztec/src/test/helpers/tagging_secret_strategy.nr @@ -14,7 +14,7 @@ global INTERACTIVE_HANDSHAKE: u8 = 4; /// How a message's tagging secret is chosen: the wallet's strategy. /// -/// This type only exists for tests: a Noir test sets it via `with_tag_secret_strategy` on +/// This type only exists for tests: a Noir test sets it via `with_default_tag_secret_strategy` on /// [`TestEnvironmentOptions`](crate::test::helpers::test_environment::TestEnvironmentOptions). /// Production wallets express the strategy in PXE; the Noir path only consumes the resolved /// [`ResolvedTaggingStrategy`](crate::messages::delivery::ResolvedTaggingStrategy). diff --git a/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr b/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr index e3fd0bd676e6..0757c162495d 100644 --- a/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr +++ b/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr @@ -89,7 +89,7 @@ pub struct TestEnvironment { /// methods setting each value, e.g.: /// ```noir /// let env = TestEnvironment::new_opts( -/// TestEnvironmentOptions::new().with_tag_secret_strategy( +/// TestEnvironmentOptions::new().with_default_tag_secret_strategy( /// MessageDelivery::onchain_unconstrained(), /// TaggingSecretStrategy::non_interactive_handshake(), /// ), @@ -109,15 +109,15 @@ impl TestEnvironmentOptions { } } - /// Sets the [`TaggingSecretStrategy`] the wallet reports for `mode` through the + /// Sets the default [`TaggingSecretStrategy`] the wallet reports for `mode` through the /// [`resolve_tagging_strategy`](crate::oracle::resolve_tagging_strategy) oracle, affecting message delivery in - /// private executions. + /// private executions that do not fix their own delivery derivation. /// /// If no strategy is set for either mode, the wallet hook is left unconfigured and the private execution /// environment applies its own default. A mode left unset while the other is configured falls back to the default /// [`TaggingSecretStrategy::non_interactive_handshake`], the strategy a PXE `resolveTaggingSecretStrategy` hook /// typically hardcodes for modes it does not customize. - pub fn with_tag_secret_strategy(&mut self, mode: M, strategy: TaggingSecretStrategy) -> Self + pub fn with_default_tag_secret_strategy(&mut self, mode: M, strategy: TaggingSecretStrategy) -> Self where M: Into, { @@ -130,11 +130,11 @@ impl TestEnvironmentOptions { } /// Sets `strategy` for both delivery modes: the equivalent of a PXE `resolveTaggingSecretStrategy` hook that - /// ignores the request's mode. See [`with_tag_secret_strategy`](Self::with_tag_secret_strategy) to configure a - /// single mode. - pub fn with_tag_secret_strategy_all_modes(&mut self, strategy: TaggingSecretStrategy) -> Self { - let _ = self.with_tag_secret_strategy(OnchainDeliveryMode::onchain_unconstrained(), strategy); - self.with_tag_secret_strategy(OnchainDeliveryMode::onchain_constrained(), strategy) + /// ignores the request's mode. See + /// [`with_default_tag_secret_strategy`](Self::with_default_tag_secret_strategy) to configure a single mode. + pub fn with_default_tag_secret_strategy_all_modes(&mut self, strategy: TaggingSecretStrategy) -> Self { + let _ = self.with_default_tag_secret_strategy(OnchainDeliveryMode::onchain_unconstrained(), strategy); + self.with_default_tag_secret_strategy(OnchainDeliveryMode::onchain_constrained(), strategy) } } @@ -635,7 +635,7 @@ impl TestEnvironment { /// ```noir /// let strategy = TaggingSecretStrategy::non_interactive_handshake(); /// let env = TestEnvironment::new_opts( - /// TestEnvironmentOptions::new().with_tag_secret_strategy_all_modes(strategy), + /// TestEnvironmentOptions::new().with_default_tag_secret_strategy_all_modes(strategy), /// ); /// ``` pub unconstrained fn new_opts(options: TestEnvironmentOptions) -> Self { diff --git a/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment/test/resolve_tagging_strategy.nr b/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment/test/resolve_tagging_strategy.nr index a78f7f773718..7537e08a5201 100644 --- a/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment/test/resolve_tagging_strategy.nr +++ b/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment/test/resolve_tagging_strategy.nr @@ -12,7 +12,7 @@ use crate::{ unconstrained fn an_unset_constrained_mode_falls_back_to_the_default_strategy() { // Only the unconstrained mode is configured: the constrained mode falls back to the default non-interactive // handshake strategy. - let mut env = TestEnvironment::new_opts(TestEnvironmentOptions::new().with_tag_secret_strategy( + let mut env = TestEnvironment::new_opts(TestEnvironmentOptions::new().with_default_tag_secret_strategy( MessageDelivery::onchain_unconstrained(), TaggingSecretStrategy::arbitrary_secret(EmbeddedCurvePoint { x: 7, y: 11 }), )); @@ -33,7 +33,7 @@ unconstrained fn an_unset_constrained_mode_falls_back_to_the_default_strategy() unconstrained fn an_unset_unconstrained_mode_falls_back_to_the_default_strategy() { // Only the constrained mode is configured: the unconstrained mode falls back to the default non-interactive // handshake strategy for an external recipient. - let mut env = TestEnvironment::new_opts(TestEnvironmentOptions::new().with_tag_secret_strategy( + let mut env = TestEnvironment::new_opts(TestEnvironmentOptions::new().with_default_tag_secret_strategy( MessageDelivery::onchain_constrained(), TaggingSecretStrategy::interactive_handshake(), )); @@ -111,7 +111,7 @@ unconstrained fn defaults_constrained_delivery_to_a_non_interactive_handshake() #[test] unconstrained fn constrained_delivery_succeeds_with_a_configured_strategy() { - let mut env = TestEnvironment::new_opts(TestEnvironmentOptions::new().with_tag_secret_strategy_all_modes( + let mut env = TestEnvironment::new_opts(TestEnvironmentOptions::new().with_default_tag_secret_strategy_all_modes( TaggingSecretStrategy::non_interactive_handshake(), )); let app = env.create_contract_account(); @@ -129,7 +129,7 @@ unconstrained fn constrained_delivery_succeeds_with_a_configured_strategy() { #[test] unconstrained fn applies_a_configured_interactive_handshake_strategy() { - let mut env = TestEnvironment::new_opts(TestEnvironmentOptions::new().with_tag_secret_strategy( + let mut env = TestEnvironment::new_opts(TestEnvironmentOptions::new().with_default_tag_secret_strategy( MessageDelivery::onchain_constrained(), TaggingSecretStrategy::interactive_handshake(), )); @@ -147,12 +147,11 @@ unconstrained fn applies_a_configured_interactive_handshake_strategy() { } #[test] -unconstrained fn constrained_mode_arbitrary_secret_resolves_without_rejecting() { - // Mirrors PXE hooks: strategies are resolved without delivery-mode validation. Runtime rejection is covered by - // `tag_secret_source::test::unconstrained_secret_cannot_back_constrained_delivery` and - // `tag::test::constrained_delivery_rejects_a_resolved_unconstrained_secret`. +unconstrained fn defers_constrained_arbitrary_secret_validation_to_delivery() { + // Resolution derives the wallet strategy without delivery-mode validation; constrained delivery later rejects + // unsound unconstrained secrets. let point = EmbeddedCurvePoint { x: 7, y: 11 }; - let mut env = TestEnvironment::new_opts(TestEnvironmentOptions::new().with_tag_secret_strategy( + let mut env = TestEnvironment::new_opts(TestEnvironmentOptions::new().with_default_tag_secret_strategy( MessageDelivery::onchain_constrained(), TaggingSecretStrategy::arbitrary_secret(point), )); @@ -173,7 +172,7 @@ unconstrained fn constrained_mode_arbitrary_secret_resolves_without_rejecting() #[test] unconstrained fn applies_the_strategy_set_in_the_options() { - let mut env = TestEnvironment::new_opts(TestEnvironmentOptions::new().with_tag_secret_strategy( + let mut env = TestEnvironment::new_opts(TestEnvironmentOptions::new().with_default_tag_secret_strategy( MessageDelivery::onchain_unconstrained(), TaggingSecretStrategy::arbitrary_secret(EmbeddedCurvePoint { x: 7, y: 11 }), )); @@ -193,7 +192,7 @@ unconstrained fn applies_the_strategy_set_in_the_options() { #[test] unconstrained fn app_silos_an_arbitrary_secret_point() { let point = EmbeddedCurvePoint { x: 7, y: 11 }; - let mut env = TestEnvironment::new_opts(TestEnvironmentOptions::new().with_tag_secret_strategy( + let mut env = TestEnvironment::new_opts(TestEnvironmentOptions::new().with_default_tag_secret_strategy( MessageDelivery::onchain_unconstrained(), TaggingSecretStrategy::arbitrary_secret(point), )); @@ -215,7 +214,7 @@ unconstrained fn app_silos_an_arbitrary_secret_point() { #[test] unconstrained fn overrides_a_configured_arbitrary_secret_for_an_unconstrained_self_send() { let point = EmbeddedCurvePoint { x: 7, y: 11 }; - let mut env = TestEnvironment::new_opts(TestEnvironmentOptions::new().with_tag_secret_strategy( + let mut env = TestEnvironment::new_opts(TestEnvironmentOptions::new().with_default_tag_secret_strategy( MessageDelivery::onchain_unconstrained(), TaggingSecretStrategy::arbitrary_secret(point), )); diff --git a/noir-projects/noir-contracts/contracts/test/onchain_delivery_test_contract/src/test.nr b/noir-projects/noir-contracts/contracts/test/onchain_delivery_test_contract/src/test.nr index fe8f0f94e8c2..8a0fa818b7c5 100644 --- a/noir-projects/noir-contracts/contracts/test/onchain_delivery_test_contract/src/test.nr +++ b/noir-projects/noir-contracts/contracts/test/onchain_delivery_test_contract/src/test.nr @@ -60,7 +60,7 @@ unconstrained fn unconstrained_delivery_works_alongside_constrained_delivery() { }; // Only the unconstrained mode is configured: the constrained mode falls back to the default non-interactive // handshake strategy, so the constrained delivery below still bootstraps a registry handshake. - let options = TestEnvironmentOptions::new().with_tag_secret_strategy( + let options = TestEnvironmentOptions::new().with_default_tag_secret_strategy( MessageDelivery::onchain_unconstrained(), TaggingSecretStrategy::arbitrary_secret(point), ); diff --git a/yarn-project/pxe/src/contract_function_simulator/noir-structs/resolved_tagging_strategy.ts b/yarn-project/pxe/src/contract_function_simulator/noir-structs/resolved_tagging_strategy.ts index 5cf811dc3e2f..02ec409ad545 100644 --- a/yarn-project/pxe/src/contract_function_simulator/noir-structs/resolved_tagging_strategy.ts +++ b/yarn-project/pxe/src/contract_function_simulator/noir-structs/resolved_tagging_strategy.ts @@ -61,7 +61,7 @@ export function resolvedTaggingStrategyFromFields(kind: number, secret: Fr): Res function assertAbsentSecret(kind: number, secret: Fr): void { if (!secret.isZero()) { - throw unrecognizedResolvedTaggingStrategy(kind); + throw new Error(`Resolved tagging strategy ${kind} must not include a secret`); } } diff --git a/yarn-project/pxe/src/contract_function_simulator/oracle/oracle_type_mappings.test.ts b/yarn-project/pxe/src/contract_function_simulator/oracle/oracle_type_mappings.test.ts index 29a1c57ab184..23ca2fbc3108 100644 --- a/yarn-project/pxe/src/contract_function_simulator/oracle/oracle_type_mappings.test.ts +++ b/yarn-project/pxe/src/contract_function_simulator/oracle/oracle_type_mappings.test.ts @@ -111,19 +111,6 @@ describe('oracle type mappings', () => { describe('RESOLVED_TAGGING_STRATEGY', () => { const secret = new Fr(42); - const strategyCases: [string, ResolvedTaggingStrategy, Fr[]][] = [ - ['non-interactive handshake', { type: 'non-interactive-handshake' }, [new Fr(1), Fr.ZERO]], - ['unconstrained secret', { type: 'unconstrained-secret', secret }, [new Fr(2), secret]], - ['interactive handshake', { type: 'interactive-handshake' }, [new Fr(3), Fr.ZERO]], - ]; - - it.each(strategyCases)('serializes %s to the Noir fields', (_name, strategy, fields) => { - expect(RESOLVED_TAGGING_STRATEGY.serialization!.fn(strategy)).toEqual(fields); - }); - - it.each(strategyCases)('deserializes %s from the Noir fields', (_name, strategy, [kind, strategySecret]) => { - expect(deserializeStrategy(kind, strategySecret)).toEqual(strategy); - }); it('rejects an unknown strategy kind', () => { expect(() => deserializeStrategy(new Fr(99), Fr.ZERO)).toThrow('Unrecognized resolved tagging strategy kind'); @@ -133,7 +120,7 @@ describe('oracle type mappings', () => { ['non-interactive handshake', new Fr(1)], ['interactive handshake', new Fr(3)], ])('rejects %s with a nonzero secret', (_name, kind) => { - expect(() => deserializeStrategy(kind, secret)).toThrow('Unrecognized resolved tagging strategy kind'); + expect(() => deserializeStrategy(kind, secret)).toThrow(`Resolved tagging strategy ${kind.toNumber()}`); }); function deserializeStrategy(kind: Fr, secret: Fr): ResolvedTaggingStrategy { From 45958102a3a729f80c34e752d1be487361bae667 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Wed, 8 Jul 2026 16:50:18 -0400 Subject: [PATCH 20/21] merge tests --- .../test/resolve_tagging_strategy.nr | 43 ++++++------------- 1 file changed, 14 insertions(+), 29 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment/test/resolve_tagging_strategy.nr b/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment/test/resolve_tagging_strategy.nr index 7537e08a5201..e2aed6337206 100644 --- a/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment/test/resolve_tagging_strategy.nr +++ b/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment/test/resolve_tagging_strategy.nr @@ -146,30 +146,6 @@ unconstrained fn applies_a_configured_interactive_handshake_strategy() { }); } -#[test] -unconstrained fn defers_constrained_arbitrary_secret_validation_to_delivery() { - // Resolution derives the wallet strategy without delivery-mode validation; constrained delivery later rejects - // unsound unconstrained secrets. - let point = EmbeddedCurvePoint { x: 7, y: 11 }; - let mut env = TestEnvironment::new_opts(TestEnvironmentOptions::new().with_default_tag_secret_strategy( - MessageDelivery::onchain_constrained(), - TaggingSecretStrategy::arbitrary_secret(point), - )); - let app = env.create_contract_account(); - let recipient = AztecAddress::from_field(8); - - env.private_context_at(app, |_| { - let resolved = resolve_tagging_strategy( - AztecAddress::from_field(1), - recipient, - OnchainDeliveryMode::onchain_constrained(), - ); - - let expected_secret = compute_directional_app_secret(point, app, recipient); - assert_eq(resolved, ResolvedTaggingStrategy::unconstrained_secret(expected_secret)); - }); -} - #[test] unconstrained fn applies_the_strategy_set_in_the_options() { let mut env = TestEnvironment::new_opts(TestEnvironmentOptions::new().with_default_tag_secret_strategy( @@ -191,23 +167,32 @@ unconstrained fn applies_the_strategy_set_in_the_options() { #[test] unconstrained fn app_silos_an_arbitrary_secret_point() { + // Both delivery modes resolve the configured arbitrary secret to the same app-siloed point. Mode-blindness + // is deliberate: PXE's tagging-strategy hook resolves whatever the wallet configured regardless of mode, and + // the TXE oracle must match that behavior. Constrained delivery rejects unsound secrets later, not here. let point = EmbeddedCurvePoint { x: 7, y: 11 }; - let mut env = TestEnvironment::new_opts(TestEnvironmentOptions::new().with_default_tag_secret_strategy( - MessageDelivery::onchain_unconstrained(), + let mut env = TestEnvironment::new_opts(TestEnvironmentOptions::new().with_default_tag_secret_strategy_all_modes( TaggingSecretStrategy::arbitrary_secret(point), )); let app = env.create_contract_account(); let recipient = AztecAddress::from_field(8); env.private_context_at(app, |_| { - let resolved = resolve_tagging_strategy( + let expected_secret = compute_directional_app_secret(point, app, recipient); + + let resolved_unconstrained = resolve_tagging_strategy( AztecAddress::from_field(1), recipient, OnchainDeliveryMode::onchain_unconstrained(), ); + assert_eq(resolved_unconstrained, ResolvedTaggingStrategy::unconstrained_secret(expected_secret)); - let expected_secret = compute_directional_app_secret(point, app, recipient); - assert_eq(resolved, ResolvedTaggingStrategy::unconstrained_secret(expected_secret)); + let resolved_constrained = resolve_tagging_strategy( + AztecAddress::from_field(1), + recipient, + OnchainDeliveryMode::onchain_constrained(), + ); + assert_eq(resolved_constrained, ResolvedTaggingStrategy::unconstrained_secret(expected_secret)); }); } From a250dc77bf48268200d71e418b3b2c310f83cd83 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Wed, 8 Jul 2026 17:20:40 -0400 Subject: [PATCH 21/21] pr review cleanup --- .../docs/resources/migration_notes.md | 40 +++++++++---------- .../src/test/helpers/test_environment.nr | 16 ++++---- .../noir-structs/resolved_tagging_strategy.ts | 6 +-- 3 files changed, 29 insertions(+), 33 deletions(-) diff --git a/docs/docs-developers/docs/resources/migration_notes.md b/docs/docs-developers/docs/resources/migration_notes.md index 340e2121dfc7..a68b2f17afdc 100644 --- a/docs/docs-developers/docs/resources/migration_notes.md +++ b/docs/docs-developers/docs/resources/migration_notes.md @@ -9,6 +9,26 @@ Aztec is in active development. Each version may introduce breaking changes that ## TBD +### [Aztec.nr] `TestEnvironmentOptions::with_tagging_secret_strategy` replaced + +`TestEnvironmentOptions::with_tagging_secret_strategy` is now `with_default_tag_secret_strategy_all_modes` for tests +that want the same default wallet strategy for both onchain delivery modes. The new naming reflects that these helpers +configure the TXE default wallet strategy hook; contract-fixed delivery derivations bypass that default. + +For mode-specific defaults and hook semantics, see the +[`resolveTaggingSecretStrategy` test helper docs](../foundational-topics/pxe/execution_hooks.md#resolvetaggingsecretstrategy). + +**Migration:** + +```diff +- TestEnvironmentOptions::new().with_tagging_secret_strategy(TaggingSecretStrategy::address_derived()) ++ TestEnvironmentOptions::new().with_default_tag_secret_strategy_all_modes( ++ TaggingSecretStrategy::address_derived(), ++ ) +``` + +**Impact**: Noir tests using the old helper name no longer compile until renamed. Message delivery behavior is unchanged. + ### [Aztec.js] Account signing keys are no longer derived from the privacy secret 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`). @@ -134,26 +154,6 @@ When no `resolveTaggingSecretStrategy` hook is configured, onchain unconstrained **Impact**: An external recipient can now discover unconstrained-delivered messages without having registered the sender in advance, but establishing the handshake publishes an onchain marker derived from the recipient's address (anyone who knows that address can tell a handshake was created for them, though not by whom nor the contents). Wallets that want the previous behavior can configure a `resolveTaggingSecretStrategy` hook that returns an `address-derived` strategy. -### [Aztec.nr] `TestEnvironmentOptions::with_tagging_secret_strategy` replaced - -`TestEnvironmentOptions::with_tagging_secret_strategy` is now `with_default_tag_secret_strategy_all_modes` for tests -that want the same default wallet strategy for both onchain delivery modes. The new naming reflects that these helpers -configure the TXE default wallet strategy hook; contract-fixed delivery derivations bypass that default. - -For mode-specific defaults and hook semantics, see the -[`resolveTaggingSecretStrategy` test helper docs](../foundational-topics/pxe/execution_hooks.md#resolvetaggingsecretstrategy). - -**Migration:** - -```diff -- TestEnvironmentOptions::new().with_tagging_secret_strategy(TaggingSecretStrategy::address_derived()) -+ TestEnvironmentOptions::new().with_default_tag_secret_strategy_all_modes( -+ TaggingSecretStrategy::address_derived(), -+ ) -``` - -**Impact**: Noir tests using the old helper name no longer compile until renamed. Message delivery behavior is unchanged. - ### [Aztec.nr] `PrivateContext` data fields are no longer public `PrivateContext`'s data fields are now private (or crate-internal): its public API is now exclusively its methods. Contracts that read these fields directly must switch to the corresponding getter. A new `get_side_effect_counter()` getter exposes the side-effect counter, and a new `is_static_call()` getter replaces reaching into `inputs.call_context`. The `get_anchor_block_header()` getter already existed. diff --git a/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr b/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr index 0757c162495d..65204cd2466d 100644 --- a/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr +++ b/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr @@ -96,16 +96,16 @@ pub struct TestEnvironment { /// ); /// ``` pub struct TestEnvironmentOptions { - unconstrained_tagging_secret_strategy: Option, - constrained_tagging_secret_strategy: Option, + default_unconstrained_tagging_secret_strategy: Option, + default_constrained_tagging_secret_strategy: Option, } impl TestEnvironmentOptions { /// Creates a new `TestEnvironmentOptions` with default values. pub fn new() -> Self { Self { - unconstrained_tagging_secret_strategy: Option::none(), - constrained_tagging_secret_strategy: Option::none(), + default_unconstrained_tagging_secret_strategy: Option::none(), + default_constrained_tagging_secret_strategy: Option::none(), } } @@ -122,9 +122,9 @@ impl TestEnvironmentOptions { M: Into, { if mode.into() == OnchainDeliveryMode::onchain_unconstrained() { - self.unconstrained_tagging_secret_strategy = Option::some(strategy); + self.default_unconstrained_tagging_secret_strategy = Option::some(strategy); } else { - self.constrained_tagging_secret_strategy = Option::some(strategy); + self.default_constrained_tagging_secret_strategy = Option::some(strategy); } *self } @@ -643,8 +643,8 @@ impl TestEnvironment { txe_oracles::assert_compatible_txe_oracle_version(); txe_oracles::set_tagging_secret_strategies( - options.unconstrained_tagging_secret_strategy, - options.constrained_tagging_secret_strategy, + options.default_unconstrained_tagging_secret_strategy, + options.default_constrained_tagging_secret_strategy, ); Self { diff --git a/yarn-project/pxe/src/contract_function_simulator/noir-structs/resolved_tagging_strategy.ts b/yarn-project/pxe/src/contract_function_simulator/noir-structs/resolved_tagging_strategy.ts index 02ec409ad545..578ec805c6dd 100644 --- a/yarn-project/pxe/src/contract_function_simulator/noir-structs/resolved_tagging_strategy.ts +++ b/yarn-project/pxe/src/contract_function_simulator/noir-structs/resolved_tagging_strategy.ts @@ -55,7 +55,7 @@ export function resolvedTaggingStrategyFromFields(kind: number, secret: Fr): Res case UNCONSTRAINED_SECRET: return { type: 'unconstrained-secret', secret }; default: - throw unrecognizedResolvedTaggingStrategy(kind); + throw new Error(`Unrecognized resolved tagging strategy kind: ${kind}`); } } @@ -64,7 +64,3 @@ function assertAbsentSecret(kind: number, secret: Fr): void { throw new Error(`Resolved tagging strategy ${kind} must not include a secret`); } } - -function unrecognizedResolvedTaggingStrategy(kind: number): Error { - return new Error(`Unrecognized resolved tagging strategy kind: ${kind}`); -}