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 @@ -52,8 +52,8 @@ pub global GET_APP_SILOED_SECRETS_SELECTOR: FunctionSelector =
comptime { FunctionSelector::from_signature("get_app_siloed_secrets((Field),(Field))") };
pub global NON_INTERACTIVE_HANDSHAKE_SELECTOR: FunctionSelector =
comptime { FunctionSelector::from_signature("non_interactive_handshake((Field),(Field))") };
pub global GET_HANDSHAKES_SELECTOR: FunctionSelector =
comptime { FunctionSelector::from_signature("get_handshakes((Field),u32)") };
pub global GET_NON_INTERACTIVE_HANDSHAKES_SELECTOR: FunctionSelector =
comptime { FunctionSelector::from_signature("get_non_interactive_handshakes((Field),u32)") };

/// Reads the existing app-siloed handshake secrets for `(sender, recipient)` from the registry.
///
Expand Down Expand Up @@ -135,12 +135,12 @@ pub(crate) unconstrained fn get_handshake_secrets(
provided_secrets
}

/// Calls the HandshakeRegistry's `get_handshakes` utility function and deserializes the response.
/// Calls the HandshakeRegistry's `get_non_interactive_handshakes` utility function and deserializes the response.
unconstrained fn fetch_handshake_page(recipient: AztecAddress, page_offset: u32) -> HandshakePage {
let args: [Field; 2] = [recipient.to_field(), page_offset as Field];
let response = call_utility_function(
STANDARD_HANDSHAKE_REGISTRY_ADDRESS,
GET_HANDSHAKES_SELECTOR,
GET_NON_INTERACTIVE_HANDSHAKES_SELECTOR,
args,
);
HandshakePage::deserialize(response)
Expand Down
8 changes: 4 additions & 4 deletions noir-projects/aztec-nr/aztec/src/standard_addresses.nr
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,17 @@
use protocol_types::{address::AztecAddress, traits::FromField};

pub global STANDARD_AUTH_REGISTRY_ADDRESS: AztecAddress = AztecAddress::from_field(
0x0564d5361fd6d7501baa1706a59bbebd2035d1c2565c9e5daf8f1567da547a23,
0x0bbe366bb57059b03929228f7e886b17210cf268e021608392f58cb8f2574f43,
);

pub global STANDARD_MULTI_CALL_ENTRYPOINT_ADDRESS: AztecAddress = AztecAddress::from_field(
0x126ab69e8161771b52273d00dbf5d7252ef6a92724d15f1ec2dcc967118a0c7b,
0x252e4f53e6bef46e70a37168c52a9eba7501944a40c63f0aa807cf2f823260d9,
);

pub global STANDARD_PUBLIC_CHECKS_ADDRESS: AztecAddress = AztecAddress::from_field(
0x077341cf79b91db9c440c49786fc4d4508ef3ab623c30fa3be7031ffcad4754c,
0x05c207d39f7a51de9de6cfd37ab7764bbc9d0434481c4448fc6ddbeac2f67636,
);

pub global STANDARD_HANDSHAKE_REGISTRY_ADDRESS: AztecAddress = AztecAddress::from_field(
0x2f8592fb5b1620f33d21d9b67a34002f0e5392d93807a684584af388e0d70741,
0x0fe4285eada74ef13ea09468db7fcb69861161276f81f24a99e152e5871c5081,
);
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ global INTERACTIVE_HANDSHAKE: u8 = 2;
pub contract HandshakeRegistry {
use crate::{
handshake_note::HandshakeNote, INTERACTIVE_HANDSHAKE, NON_INTERACTIVE_HANDSHAKE,
recipient_signature::RecipientSignature, sync::discovered_handshakes::get_all_handshakes,
recipient_signature::RecipientSignature, sync::discovered_handshakes::get_all_discovered_handshakes,
};
use aztec::{
keys::{ecdh_shared_secret::derive_ecdh_shared_secret, ephemeral::generate_positive_ephemeral_key_pair},
Expand Down Expand Up @@ -166,12 +166,21 @@ pub contract HandshakeRegistry {
}
}

/// Returns a page of discovered handshakes addressed to `recipient`.
/// Returns a page of discovered non-interactive handshakes addressed to `recipient`.
///
/// Only non-interactive handshakes appear here: they are the ones announced in a log the recipient's sync scan
/// discovers.
///
/// `total_count` is the full list length, so callers can determine whether more pages remain.
///
/// # Privacy
/// PXE authorizes this read for any calling contract without consulting the wallet's `authorizeUtilityCall`
/// hook, so any contract executing for `recipient` can see that account's discovered handshakes: their count and
/// their ephemeral public keys. This does not weaken message secrecy, as deriving the shared secret from an
/// ephemeral key requires the recipient's secret keys.
#[external("utility")]
unconstrained fn get_handshakes(recipient: AztecAddress, page_offset: u32) -> HandshakePage {
let handshakes = get_all_handshakes(self.context.this_address(), recipient);
unconstrained fn get_non_interactive_handshakes(recipient: AztecAddress, page_offset: u32) -> HandshakePage {
let handshakes = get_all_discovered_handshakes(self.context.this_address(), recipient);
let total_count = handshakes.len();
let offset = std::cmp::min(page_offset, total_count);
let page_len = std::cmp::min(MAX_HANDSHAKES_PER_PAGE, total_count - offset);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ use aztec::{
};

/// Fact-collection type id of the per-recipient discovered handshakes populated by [`record_discovered_handshake`]
/// and read by [`get_all_handshakes`]. Each recipient (the fact scope) has a single collection of this type holding
/// one fact per discovered handshake, in discovery order.
/// and read by [`get_all_discovered_handshakes`]. Each recipient (the fact scope) has a single collection of this
/// type holding one fact per discovered handshake, in discovery order.
global DISCOVERED_HANDSHAKES_TYPE_ID: Field = sha256_to_field("HANDSHAKE_REGISTRY::DISCOVERED_HANDSHAKES".as_bytes());

/// Collection id of the singleton discovered-handshakes collection.
Expand Down Expand Up @@ -39,11 +39,11 @@ pub(crate) unconstrained fn record_discovered_handshake(
);
}

/// Returns the discovered handshakes for `recipient`.
/// Returns the discovered handshakes for `recipient`; only non-interactive handshakes are ever discovered.
///
/// The order of facts within a collection is not specified, but it is stable, keeping
/// [`HandshakeRegistry::get_handshakes`] pagination consistent across pages.
pub(crate) unconstrained fn get_all_handshakes(
/// [`HandshakeRegistry::get_non_interactive_handshakes`] pagination consistent across pages.
pub(crate) unconstrained fn get_all_discovered_handshakes(
contract_address: AztecAddress,
recipient: AztecAddress,
) -> EphemeralArray<DiscoveredHandshake> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use aztec::{
messages::delivery::{
constrained_delivery::VALIDATE_HANDSHAKE_SELECTOR,
handshake::{
AppSiloedHandshakeSecrets, GET_APP_SILOED_SECRETS_SELECTOR, GET_HANDSHAKES_SELECTOR,
AppSiloedHandshakeSecrets, GET_APP_SILOED_SECRETS_SELECTOR, GET_NON_INTERACTIVE_HANDSHAKES_SELECTOR,
MAX_HANDSHAKES_PER_PAGE, NON_INTERACTIVE_HANDSHAKE_SELECTOR,
},
},
Expand Down Expand Up @@ -59,7 +59,7 @@ unconstrained fn selectors_match_the_constrained_delivery_helper() {
assert_eq(registry.get_app_siloed_secrets(sender, recipient).selector, GET_APP_SILOED_SECRETS_SELECTOR);
assert_eq(registry.non_interactive_handshake(sender, recipient).selector, NON_INTERACTIVE_HANDSHAKE_SELECTOR);
assert_eq(registry.validate_handshake(sender, recipient, secrets).selector, VALIDATE_HANDSHAKE_SELECTOR);
assert_eq(registry.get_handshakes(recipient, 0).selector, GET_HANDSHAKES_SELECTOR);
assert_eq(registry.get_non_interactive_handshakes(recipient, 0).selector, GET_NON_INTERACTIVE_HANDSHAKES_SELECTOR);
}

#[test]
Expand Down Expand Up @@ -460,7 +460,7 @@ unconstrained fn non_interactive_handshake_is_discovered_by_recipient() {

let returned_secrets = env.call_private(sender, registry.non_interactive_handshake(sender, recipient));

let discovered = env.execute_utility(registry.get_handshakes(recipient, 0));
let discovered = env.execute_utility(registry.get_non_interactive_handshakes(recipient, 0));

assert_eq(discovered.items.len(), 1);
assert_eq(discovered.total_count, 1);
Expand Down Expand Up @@ -488,8 +488,8 @@ unconstrained fn handshake_to_one_recipient_not_discovered_by_another() {

let _ = env.call_private(sender, registry.non_interactive_handshake(sender, recipient_a));

let discovered_a = env.execute_utility(registry.get_handshakes(recipient_a, 0));
let discovered_b = env.execute_utility(registry.get_handshakes(recipient_b, 0));
let discovered_a = env.execute_utility(registry.get_non_interactive_handshakes(recipient_a, 0));
let discovered_b = env.execute_utility(registry.get_non_interactive_handshakes(recipient_b, 0));

assert_eq(discovered_a.items.len(), 1);
assert_eq(discovered_b.items.len(), 0);
Expand All @@ -502,12 +502,12 @@ unconstrained fn repeated_sync_does_not_refetch_logs() {

let _ = env.call_private(sender, registry.non_interactive_handshake(sender, recipient));

let after_first = env.execute_utility(registry.get_handshakes(recipient, 0));
let after_first = env.execute_utility(registry.get_non_interactive_handshakes(recipient, 0));
assert_eq(after_first.items.len(), 1);

// Mock the log-retrieval oracle so we can assert it is never called on re-sync.
let log_oracle = OracleMock::mock("aztec_utl_getLogsByTagV2");
let _ = env.execute_utility(registry.get_handshakes(recipient, 0));
let _ = env.execute_utility(registry.get_non_interactive_handshakes(recipient, 0));
assert_eq(log_oracle.times_called(), 0);
log_oracle.clear();
}
Expand All @@ -519,11 +519,11 @@ unconstrained fn later_handshake_appends_after_cursor_advances() {

let _ = env.call_private(sender, registry.non_interactive_handshake(sender, recipient));

let after_first = env.execute_utility(registry.get_handshakes(recipient, 0));
let after_first = env.execute_utility(registry.get_non_interactive_handshakes(recipient, 0));
assert_eq(after_first.items.len(), 1);

let _ = env.call_private(other_sender, registry.non_interactive_handshake(other_sender, recipient));
let after_second = env.execute_utility(registry.get_handshakes(recipient, 0));
let after_second = env.execute_utility(registry.get_non_interactive_handshakes(recipient, 0));
assert_eq(after_second.items.len(), 2);
}

Expand All @@ -536,7 +536,7 @@ unconstrained fn multiple_senders_discovered_for_same_recipient() {
let _ = env.call_private(sender, registry.non_interactive_handshake(sender, recipient));
let _ = env.call_private(other_sender, registry.non_interactive_handshake(other_sender, recipient));

let discovered = env.execute_utility(registry.get_handshakes(recipient, 0));
let discovered = env.execute_utility(registry.get_non_interactive_handshakes(recipient, 0));

assert_eq(discovered.items.len(), 2);
assert(
Expand All @@ -546,7 +546,7 @@ unconstrained fn multiple_senders_discovered_for_same_recipient() {
}

#[test]
unconstrained fn get_handshakes_paginates_across_full_pages() {
unconstrained fn get_non_interactive_handshakes_paginates_across_full_pages() {
let (env, registry_address, sender, _, recipient) = setup();
let registry = HandshakeRegistry::at(registry_address);

Expand All @@ -555,8 +555,8 @@ unconstrained fn get_handshakes_paginates_across_full_pages() {
let _ = env.call_private(sender, registry.non_interactive_handshake(sender, recipient));
}

let page_0 = env.execute_utility(registry.get_handshakes(recipient, 0));
let page_1 = env.execute_utility(registry.get_handshakes(recipient, MAX_HANDSHAKES_PER_PAGE));
let page_0 = env.execute_utility(registry.get_non_interactive_handshakes(recipient, 0));
let page_1 = env.execute_utility(registry.get_non_interactive_handshakes(recipient, MAX_HANDSHAKES_PER_PAGE));

assert_eq(page_0.items.len(), MAX_HANDSHAKES_PER_PAGE);
assert_eq(page_0.total_count, total_handshakes);
Expand All @@ -565,13 +565,13 @@ unconstrained fn get_handshakes_paginates_across_full_pages() {
}

#[test]
unconstrained fn get_handshakes_out_of_bounds_offset_returns_empty() {
unconstrained fn get_non_interactive_handshakes_out_of_bounds_offset_returns_empty() {
let (env, registry_address, sender, _, recipient) = setup();
let registry = HandshakeRegistry::at(registry_address);

let _ = env.call_private(sender, registry.non_interactive_handshake(sender, recipient));

let page = env.execute_utility(registry.get_handshakes(recipient, 999));
let page = env.execute_utility(registry.get_non_interactive_handshakes(recipient, 999));
assert_eq(page.items.len(), 0);
assert_eq(page.total_count, 1);
}
Expand All @@ -583,7 +583,7 @@ unconstrained fn rescan_after_cursor_retraction_does_not_duplicate_handshakes()

let _ = env.call_private(sender, registry.non_interactive_handshake(sender, recipient));

let after_first = env.execute_utility(registry.get_handshakes(recipient, 0));
let after_first = env.execute_utility(registry.get_non_interactive_handshakes(recipient, 0));
assert_eq(after_first.items.len(), 1);

// Emulate a reorg that pruned back past every recorded scan but not past the handshake log: scans anchor at the
Expand All @@ -593,7 +593,7 @@ unconstrained fn rescan_after_cursor_retraction_does_not_duplicate_handshakes()
env.mine_block();

// With no cursor the rescan covers the full range again; re-recording the surviving handshake is a no-op.
let after_rescan = env.execute_utility(registry.get_handshakes(recipient, 0));
let after_rescan = env.execute_utility(registry.get_non_interactive_handshakes(recipient, 0));
assert_eq(after_rescan.items.len(), 1);
assert_eq(after_rescan.items.get(0).eph_pk.x, after_first.items.get(0).eph_pk.x);
}
Expand All @@ -610,7 +610,7 @@ unconstrained fn handshake_fact_originates_at_the_log_block() {
env.mine_block();
env.mine_block();

let discovered = env.execute_utility(registry.get_handshakes(recipient, 0));
let discovered = env.execute_utility(registry.get_non_interactive_handshakes(recipient, 0));
assert_eq(discovered.items.len(), 1);

// Ephemeral arrays are frame-scoped, so read the origins and extract the value inside the registry's context.
Expand Down
Binary file modified noir-projects/noir-contracts/pinned-standard-contracts.tar.gz
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -449,8 +449,8 @@ describe('Utility Execution test suite', () => {
});

// Pins the production oracle's default-authorization allowlist for cross-contract utility reads of the
// standard HandshakeRegistry: only get_handshakes and get_app_siloed_secrets are allowed, everything else is
// denied.
// standard HandshakeRegistry: only get_non_interactive_handshakes and get_app_siloed_secrets are allowed,
// everything else is denied.
describe('cross-contract utility authorization', () => {
const prepareNestedUtilityCall = async (
targetContractAddress: AztecAddress,
Expand Down Expand Up @@ -493,7 +493,7 @@ describe('Utility Execution test suite', () => {
nestedSimulator = makeNestedSimulator();
utilityExecutionOracle = makeOracle({ simulator: nestedSimulator });
defaultAuthorizedHandshakeRegistryReads = new Map<string, Fr[]>([
['get_handshakes', []],
['get_non_interactive_handshakes', []],
['get_app_siloed_secrets', [Fr.random(), Fr.random()]],
]);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1142,10 +1142,11 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
}

// Registry reads that any contract may issue without an `authorizeUtilityCall` hook. The constrained-delivery
// library calls these implicitly for the app, and they are safe to default-authorize because the registry siloes
// every returned secret to `msg_sender`, so a caller only ever learns values siloed to its own address.
// library calls these implicitly for the app, and they are safe to default-authorize: `get_app_siloed_secrets`
// siloes every returned secret to `msg_sender`, and `get_non_interactive_handshakes` only exposes ephemeral public
// keys, from which the shared secret cannot be derived without the recipient's secret keys.
const STANDARD_HANDSHAKE_REGISTRY_DEFAULT_AUTHORIZED_READ_SIGNATURES = [
'get_handshakes((Field),u32)',
'get_non_interactive_handshakes((Field),u32)',
'get_app_siloed_secrets((Field),(Field))',
];

Expand Down
24 changes: 12 additions & 12 deletions yarn-project/standard-contracts/src/standard_contract_data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,44 +20,44 @@ export const StandardContractSalt: Record<StandardContractName, Fr> = {
};

export const StandardContractAddress: Record<StandardContractName, AztecAddress> = {
AuthRegistry: AztecAddress.fromStringUnsafe('0x0564d5361fd6d7501baa1706a59bbebd2035d1c2565c9e5daf8f1567da547a23'),
AuthRegistry: AztecAddress.fromStringUnsafe('0x0bbe366bb57059b03929228f7e886b17210cf268e021608392f58cb8f2574f43'),
MultiCallEntrypoint: AztecAddress.fromStringUnsafe(
'0x126ab69e8161771b52273d00dbf5d7252ef6a92724d15f1ec2dcc967118a0c7b',
'0x252e4f53e6bef46e70a37168c52a9eba7501944a40c63f0aa807cf2f823260d9',
),
PublicChecks: AztecAddress.fromStringUnsafe('0x077341cf79b91db9c440c49786fc4d4508ef3ab623c30fa3be7031ffcad4754c'),
PublicChecks: AztecAddress.fromStringUnsafe('0x05c207d39f7a51de9de6cfd37ab7764bbc9d0434481c4448fc6ddbeac2f67636'),
HandshakeRegistry: AztecAddress.fromStringUnsafe(
'0x2f8592fb5b1620f33d21d9b67a34002f0e5392d93807a684584af388e0d70741',
'0x0fe4285eada74ef13ea09468db7fcb69861161276f81f24a99e152e5871c5081',
),
};

export const StandardContractClassId: Record<StandardContractName, Fr> = {
AuthRegistry: Fr.fromString('0x197cba16b38c5408694cec7188b9128e80d247596050e31a0d12249b60b360e1'),
MultiCallEntrypoint: Fr.fromString('0x24e025d7b0d3158d69197dbabf2baf18db0b1dd4ff3c87ec2ed418fecb0f192d'),
PublicChecks: Fr.fromString('0x000a3012fd5d88921976565891e19c0b9a6a753eaf773e9493f47c890089bd08'),
HandshakeRegistry: Fr.fromString('0x20e14f4a6ada38f27144ceedbfbb7417aca6c679ee1ea09b9435886ed7faf3d2'),
AuthRegistry: Fr.fromString('0x2f4a272a86482af67aa5beb9db9ea648080f0e140baf55a0af153421ab19e12d'),
MultiCallEntrypoint: Fr.fromString('0x2b2d3d423944a96cc01b3d332ba25e8a53aa486740d68047ca4df1b1c849a8e5'),
PublicChecks: Fr.fromString('0x1c14245c8afceb64326b033fd1841e78e844124e40c49a36712f57dfffd258d6'),
HandshakeRegistry: Fr.fromString('0x250d9a5696197bc0aaa73bd6fe01b89e33a796d91b18b847cc419e433a5cfaf9'),
};

export const StandardContractClassIdPreimage: Record<
StandardContractName,
{ artifactHash: Fr; privateFunctionsRoot: Fr; publicBytecodeCommitment: Fr }
> = {
AuthRegistry: {
artifactHash: Fr.fromString('0x17bd6a48fb22fb700a16d5767f93e08b2b21bf83c280cb61abe763567179601f'),
artifactHash: Fr.fromString('0x01f4d890c55a73e9164007dfe9833d7fc9daaf29f49886d412188ba02b4c4b81'),
privateFunctionsRoot: Fr.fromString('0x17b584350f4c3ccafd8f688729afb9feab8976114fb40012e9dee65022c072a4'),
publicBytecodeCommitment: Fr.fromString('0x2545f39893766508ce37bb5cea5e4dcab04c6f7f79f3089b1c076876e9d268b2'),
},
MultiCallEntrypoint: {
artifactHash: Fr.fromString('0x13dfb7aaca5d32bb876650f6afa5531d7c2c946f1cf927bde230755017af91d3'),
artifactHash: Fr.fromString('0x158b25c22e41ecc134945b6f7fed7b0d523841a6e0ac78be1bdac989eb1ae6c0'),
privateFunctionsRoot: Fr.fromString('0x0e68dfbb256e80b08b3aef47aca1f2669e97a9c6259787893c1223ac083ad5d5'),
publicBytecodeCommitment: Fr.fromString('0x0ce4c618c3ed7f3a20410e618c06bb701e150af7fe28a3e92f68e7733809f33e'),
},
PublicChecks: {
artifactHash: Fr.fromString('0x212730544c282ed52485a69d91a2d46adb5a5e6cd23e8c26c3abc6de4bcc3714'),
artifactHash: Fr.fromString('0x18fbef2f3a4f54681c07f1eb9474851e43d5dce9a52cf1ebc0ca4bbebb96527f'),
privateFunctionsRoot: Fr.fromString('0x202860adb1b8975971eeaf571aaaa88a27f4035290d58532ae7d60b0dfaad54c'),
publicBytecodeCommitment: Fr.fromString('0x013c4f854a5c87c9daf86c5f9bc07a42c2a061f1d924a5b3564ec7edc8e18cb7'),
},
HandshakeRegistry: {
artifactHash: Fr.fromString('0x01b39b7d8576cbea4e4eae792c4538a710d0ac2ea0361364c57d0e2b4b1a3474'),
artifactHash: Fr.fromString('0x0e781457b14303befa6080e70bcec27e82a0081191063b9128561a4183722c59'),
privateFunctionsRoot: Fr.fromString('0x02a4dba36389845b8ef0108562f7536d3284f07ca678558fc3c3bce3b24ee821'),
publicBytecodeCommitment: Fr.fromString('0x0ce4c618c3ed7f3a20410e618c06bb701e150af7fe28a3e92f68e7733809f33e'),
},
Expand Down
Loading