Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -82,14 +82,23 @@ 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 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_tagging_secret_strategy(TaggingSecretStrategy::non_interactive_handshake()),
TestEnvironmentOptions::new().with_default_tag_secret_strategy(
MessageDelivery::onchain_unconstrained(),
TaggingSecretStrategy::non_interactive_handshake(),
),
);
```

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

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.
Expand Down
3 changes: 3 additions & 0 deletions docs/docs-developers/docs/resources/migration_notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ Aztec is in active development. Each version may introduce breaking changes that

## TBD

<<<<<<< HEAD
## 5.0.1

### [Aztec.nr] History note nullification helpers renamed and restricted to own-contract notes
Expand Down Expand Up @@ -54,6 +55,8 @@ The persisted tagging stores now key every entry by the self-describing `<kind>:

**Impact**: On upgrade your local PXE state is reset. You must re-register accounts and re-sync from genesis. Wallets should surface a "your local state was reset, please re-register accounts and re-sync" path.

=======
>>>>>>> 6dd127124d (fix(txe): align tagging strategy oracle with PXE (#24561))
### [Aztec.nr] `TestEnvironmentOptions::with_tagging_secret_strategy` replaced

`TestEnvironmentOptions::with_tagging_secret_strategy` is now `with_default_tag_secret_strategy_all_modes` for tests
Expand Down
23 changes: 23 additions & 0 deletions noir-projects/aztec-nr/aztec/src/messages/delivery/tag.nr
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
2 changes: 1 addition & 1 deletion noir-projects/aztec-nr/aztec/src/test/helpers/mod.nr
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -7,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_tagging_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).
Expand Down Expand Up @@ -45,18 +52,26 @@ 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) | (kind == INTERACTIVE_HANDSHAKE))
& secret.is_infinite()
)
| (kind == ARBITRARY_SECRET),
(is_no_payload_strategy & secret.is_infinite()) | (kind == ARBITRARY_SECRET),
f"unrecognized tagging secret strategy kind: {kind}",
);
strategy
}
}

/// 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;

Expand All @@ -81,14 +96,14 @@ 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 });
let interactive = TaggingSecretStrategy::interactive_handshake();

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);
assert(TaggingSecretStrategy::deserialize(interactive.serialize()) == interactive);
}

#[test(should_fail_with = "unrecognized tagging secret strategy kind")]
Expand All @@ -98,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([3, 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([4, 7, 11]);
let _ = TaggingSecretStrategy::deserialize([TaggingSecretStrategy::interactive_handshake().kind as Field, 7, 11]);
}
}
53 changes: 40 additions & 13 deletions noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr
Original file line number Diff line number Diff line change
Expand Up @@ -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,
},
Expand Down Expand Up @@ -88,28 +89,53 @@ 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_default_tag_secret_strategy(
/// MessageDelivery::onchain_unconstrained(),
/// TaggingSecretStrategy::non_interactive_handshake(),
/// ),
/// );
/// ```
pub struct TestEnvironmentOptions {
tagging_secret_strategy: Option<TaggingSecretStrategy>,
default_unconstrained_tagging_secret_strategy: Option<TaggingSecretStrategy>,
default_constrained_tagging_secret_strategy: Option<TaggingSecretStrategy>,
}

impl TestEnvironmentOptions {
/// Creates a new `TestEnvironmentOptions` with default values.
pub fn new() -> Self {
Self { tagging_secret_strategy: Option::none() }
Self {
default_unconstrained_tagging_secret_strategy: Option::none(),
default_constrained_tagging_secret_strategy: Option::none(),
}
}

/// Sets the [`TaggingSecretStrategy`] the wallet reports 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 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);
/// 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_default_tag_secret_strategy<M>(&mut self, mode: M, strategy: TaggingSecretStrategy) -> Self
where
M: Into<OnchainDeliveryMode>,
{
if mode.into() == OnchainDeliveryMode::onchain_unconstrained() {
self.default_unconstrained_tagging_secret_strategy = Option::some(strategy);
} else {
self.default_constrained_tagging_secret_strategy = Option::some(strategy);
}
*self
}

/// Sets `strategy` for both delivery modes: the equivalent of a PXE `resolveTaggingSecretStrategy` hook that
/// 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)
}
}

/// Configuration values for [`TestEnvironment::private_context_opts`]. Meant to be used by calling `new` and then
Expand Down Expand Up @@ -607,18 +633,19 @@ 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_default_tag_secret_strategy_all_modes(strategy),
/// );
/// ```
pub unconstrained fn new_opts(options: TestEnvironmentOptions) -> Self {
assert_compatible_oracle_version();
txe_oracles::assert_compatible_txe_oracle_version();

// Forward the configured strategy to the wallet.
txe_oracles::set_tagging_secret_strategy(options.tagging_secret_strategy);
txe_oracles::set_tagging_secret_strategies(
options.default_unconstrained_tagging_secret_strategy,
options.default_constrained_tagging_secret_strategy,
);

Self {
// Use an offset to avoid secret collision with account secrets. Without this, when
Expand Down
Loading
Loading