Skip to content
Merged
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
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
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
use crate::protocol::{
abis::block_header::BlockHeader,
constants::{ARCHIVE_HEIGHT, NOTE_HASH_TREE_HEIGHT},
merkle_tree::MembershipWitness,
traits::Hash,
use crate::{
ephemeral::EphemeralArray,
protocol::{
abis::block_header::BlockHeader,
constants::{ARCHIVE_HEIGHT, NOTE_HASH_TREE_HEIGHT},
merkle_tree::MembershipWitness,
traits::Hash,
},
};

#[oracle(aztec_utl_getNoteHashMembershipWitness)]
Expand All @@ -17,6 +20,12 @@ unconstrained fn get_block_hash_membership_witness_oracle(
block_hash: Field,
) -> Option<MembershipWitness<ARCHIVE_HEIGHT>> {}

#[oracle(aztec_utl_areBlockHashesInArchive)]
unconstrained fn are_block_hashes_in_archive_oracle(
anchor_block_hash: Field,
block_hashes: EphemeralArray<Field>,
) -> EphemeralArray<bool> {}

// Note: get_nullifier_membership_witness function is implemented in get_nullifier_membership_witness.nr

/// Returns a membership witness for a `note_hash` in the note hash tree whose root is defined in
Expand Down Expand Up @@ -53,6 +62,15 @@ pub unconstrained fn get_maybe_block_hash_membership_witness(
get_block_hash_membership_witness_oracle(anchor_block_hash, block_hash)
}

/// Returns whether each block hash is present in the archive tree whose root is defined in `anchor_block_header`.
pub unconstrained fn are_block_hashes_in_archive(
anchor_block_header: BlockHeader,
block_hashes: EphemeralArray<Field>,
) -> EphemeralArray<bool> {
let anchor_block_hash = anchor_block_header.hash();
are_block_hashes_in_archive_oracle(anchor_block_hash, block_hashes)
}

mod test {
use crate::history::test::{create_note, NOTE_CREATED_AT};
use crate::note::note_interface::NoteHash;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,3 +66,11 @@ pub unconstrained fn get_tx_effect(tx_hash: Field) -> Option<TxEffect> {

#[oracle(aztec_utl_getTxEffect)]
unconstrained fn get_tx_effect_oracle(tx_hash: Field) -> Option<TxEffect> {}

/// Fetches all effects of settled transactions by hash, preserving request order.
pub unconstrained fn get_tx_effects(tx_hashes: EphemeralArray<Field>) -> EphemeralArray<Option<TxEffect>> {
get_tx_effects_oracle(tx_hashes)
}

#[oracle(aztec_utl_getTxEffects)]
unconstrained fn get_tx_effects_oracle(tx_hashes: EphemeralArray<Field>) -> EphemeralArray<Option<TxEffect>> {}
15 changes: 7 additions & 8 deletions noir-projects/aztec-nr/aztec/src/oracle/mod.nr
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,7 @@ use crate::macros::oracle_testing::{generate_oracle_tests, generate_oracle_tests
],
)]
pub mod avm;
#[generate_oracle_tests_excluding(
@[
// TODO: cover once the iv/sym_key BUFFER mapping is expressible as a plain fixed array of bytes, so the
// fixture synthesizer does not need to special-case it.
quote { aes128_decrypt_oracle },
],
)]
#[generate_oracle_tests]
pub mod aes128_decrypt;
#[generate_oracle_tests]
pub mod auth_witness;
Expand Down Expand Up @@ -99,7 +93,12 @@ pub mod get_nullifier_membership_witness;
],
)]
pub mod get_public_data_witness;
#[generate_oracle_tests]
#[generate_oracle_tests_excluding(
@[
// TODO: cover once the test resolver can serve EphemeralArray contents.
quote { are_block_hashes_in_archive_oracle },
],
)]
pub mod get_membership_witness;
#[generate_oracle_tests]
pub mod keys;
Expand Down
101 changes: 0 additions & 101 deletions noir-projects/aztec-nr/aztec/src/oracle/tx_resolution.nr
Original file line number Diff line number Diff line change
Expand Up @@ -18,104 +18,3 @@ pub(crate) unconstrained fn get_resolved_txs(requests: EphemeralArray<Field>) ->

#[oracle(aztec_utl_getResolvedTxs)]
unconstrained fn get_resolved_txs_oracle(requests: EphemeralArray<Field>) -> EphemeralArray<Option<ResolvedTx>> {}

mod test {
use crate::oracle::tx_resolution::ResolvedTx;
use crate::protocol::traits::Deserialize;

#[test]
unconstrained fn resolved_tx_serialization_matches_typescript() {
// Setup test data
let tx_hash = 123;
let unique_note_hashes = BoundedVec::from_array([4, 5]);
let first_nullifier = 6;
let block_number: u32 = 7;
let block_hash = 8;

// Create a ResolvedTx instance
let resolved_tx = ResolvedTx {
tx_hash,
unique_note_hashes_in_tx: unique_note_hashes,
first_nullifier_in_tx: first_nullifier,
block_number,
block_hash,
};

// Expected output generated from TypeScript's `ResolvedTx.toFields()`
let serialized_resolved_tx_from_typescript = [
0x000000000000000000000000000000000000000000000000000000000000007b,
0x0000000000000000000000000000000000000000000000000000000000000004,
0x0000000000000000000000000000000000000000000000000000000000000005,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000002,
0x0000000000000000000000000000000000000000000000000000000000000006,
0x0000000000000000000000000000000000000000000000000000000000000007,
0x0000000000000000000000000000000000000000000000000000000000000008,
];

let deserialized = ResolvedTx::deserialize(serialized_resolved_tx_from_typescript);

assert_eq(deserialized, resolved_tx);
}
}
2 changes: 1 addition & 1 deletion noir-projects/aztec-nr/aztec/src/oracle/version.nr
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
/// immediately if AZTEC_NR_MINOR > PXE_MINOR because if a contract is updated to use a newer Aztec.nr dependency
/// without actually using any of the new oracles then there is no reason to throw.
pub global ORACLE_VERSION_MAJOR: Field = 30;
pub global ORACLE_VERSION_MINOR: Field = 6;
pub global ORACLE_VERSION_MINOR: Field = 8;

/// Asserts that the version of the oracle is compatible with the version expected by the contract.
pub fn assert_compatible_oracle_version() {
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]);
}
}
Loading
Loading