|
| 1 | +use crate::messages::delivery::OnchainDeliveryMode; |
| 2 | +use crate::protocol::point::EmbeddedCurvePoint; |
| 3 | +use crate::protocol::traits::{Deserialize, Serialize}; |
| 4 | + |
| 5 | +use crate::{ |
| 6 | + ephemeral::EphemeralArray, |
| 7 | + messages::processing::provided_secret::ProvidedSecret, |
| 8 | + oracle::{call_utility_function::call_utility_function, shared_secret::get_shared_secrets}, |
| 9 | + protocol::{ |
| 10 | + abis::function_selector::FunctionSelector, address::AztecAddress, hash::sha256_to_field, traits::ToField, |
| 11 | + }, |
| 12 | + standard_addresses::STANDARD_HANDSHAKE_REGISTRY_ADDRESS, |
| 13 | +}; |
| 14 | + |
| 15 | +/// Page size for handshake discovery pagination. |
| 16 | +pub global MAX_HANDSHAKES_PER_PAGE: u32 = 32; |
| 17 | + |
| 18 | +/// A handshake discovered during sync: the sender's ephemeral public key and the delivery mode. |
| 19 | +#[derive(Deserialize, Eq, Serialize)] |
| 20 | +pub struct DiscoveredHandshake { |
| 21 | + pub eph_pk: EmbeddedCurvePoint, |
| 22 | + pub mode: OnchainDeliveryMode, |
| 23 | +} |
| 24 | + |
| 25 | +/// A paginated response of discovered handshakes. |
| 26 | +#[derive(Deserialize, Serialize)] |
| 27 | +pub struct HandshakePage { |
| 28 | + pub items: BoundedVec<DiscoveredHandshake, MAX_HANDSHAKES_PER_PAGE>, |
| 29 | + pub total_count: u32, |
| 30 | +} |
| 31 | + |
| 32 | +pub(crate) global PROVIDED_SECRETS_ARRAY_BASE_SLOT: Field = |
| 33 | + sha256_to_field("AZTEC_NR::PROVIDED_SECRETS_ARRAY_BASE_SLOT".as_bytes()); |
| 34 | + |
| 35 | +global HANDSHAKE_EPH_PKS_SLOT: Field = sha256_to_field("AZTEC_NR::HANDSHAKE_EPH_PKS_SLOT".as_bytes()); |
| 36 | + |
| 37 | +global HANDSHAKE_MODES_SLOT: Field = sha256_to_field("AZTEC_NR::HANDSHAKE_MODES_SLOT".as_bytes()); |
| 38 | + |
| 39 | +/// Fetches discovered handshakes from the HandshakeRegistry and derives app-siloed tagging secrets for each, |
| 40 | +/// returning them so that [`get_pending_tagged_logs`](crate::oracle::message_processing::get_pending_tagged_logs) |
| 41 | +/// searches for logs tagged with these secrets. |
| 42 | +pub(crate) unconstrained fn get_handshake_secrets( |
| 43 | + contract_address: AztecAddress, |
| 44 | + scope: AztecAddress, |
| 45 | +) -> EphemeralArray<ProvidedSecret> { |
| 46 | + let provided_secrets = EphemeralArray::<ProvidedSecret>::empty_at(PROVIDED_SECRETS_ARRAY_BASE_SLOT); |
| 47 | + |
| 48 | + let eph_pks: EphemeralArray<EmbeddedCurvePoint> = EphemeralArray::empty_at(HANDSHAKE_EPH_PKS_SLOT); |
| 49 | + let modes: EphemeralArray<OnchainDeliveryMode> = EphemeralArray::empty_at(HANDSHAKE_MODES_SLOT); |
| 50 | + |
| 51 | + let mut page_offset: u32 = 0; |
| 52 | + let mut has_more = true; |
| 53 | + while has_more { |
| 54 | + let page = fetch_handshake_page(scope, page_offset); |
| 55 | + |
| 56 | + for j in 0..page.items.len() { |
| 57 | + let handshake = page.items.get(j); |
| 58 | + eph_pks.push(handshake.eph_pk); |
| 59 | + modes.push(handshake.mode); |
| 60 | + } |
| 61 | + |
| 62 | + page_offset += page.items.len(); |
| 63 | + has_more = page_offset < page.total_count; |
| 64 | + } |
| 65 | + |
| 66 | + if eph_pks.len() > 0 { |
| 67 | + let secrets = get_shared_secrets(scope, eph_pks, contract_address); |
| 68 | + for j in 0..secrets.len() { |
| 69 | + provided_secrets.push(ProvidedSecret { secret: secrets.get(j), mode: modes.get(j) }); |
| 70 | + } |
| 71 | + } |
| 72 | + |
| 73 | + provided_secrets |
| 74 | +} |
| 75 | + |
| 76 | +/// Calls the HandshakeRegistry's `get_handshakes` utility function and deserializes the response. |
| 77 | +unconstrained fn fetch_handshake_page(recipient: AztecAddress, page_offset: u32) -> HandshakePage { |
| 78 | + let selector = comptime { FunctionSelector::from_signature("get_handshakes((Field),u32)") }; |
| 79 | + let args: [Field; 2] = [recipient.to_field(), page_offset as Field]; |
| 80 | + let response = call_utility_function(STANDARD_HANDSHAKE_REGISTRY_ADDRESS, selector, args); |
| 81 | + HandshakePage::deserialize(response) |
| 82 | +} |
| 83 | + |
| 84 | +mod test { |
| 85 | + use crate::ephemeral::EphemeralArray; |
| 86 | + use crate::messages::delivery::handshake::{ |
| 87 | + DiscoveredHandshake, get_handshake_secrets, HandshakePage, MAX_HANDSHAKES_PER_PAGE, |
| 88 | + }; |
| 89 | + use crate::messages::delivery::OnchainDeliveryMode; |
| 90 | + use crate::protocol::{address::AztecAddress, traits::Serialize}; |
| 91 | + use crate::test::helpers::test_environment::TestEnvironment; |
| 92 | + use crate::utils::point::point_from_x_coord; |
| 93 | + use std::test::OracleMock; |
| 94 | + |
| 95 | + global UNCONSTRAINED: OnchainDeliveryMode = OnchainDeliveryMode::onchain_unconstrained(); |
| 96 | + global CONSTRAINED: OnchainDeliveryMode = OnchainDeliveryMode::onchain_constrained(); |
| 97 | + |
| 98 | + #[test] |
| 99 | + unconstrained fn get_handshake_secrets_returns_secrets_from_single_page() { |
| 100 | + let mut env = TestEnvironment::new(); |
| 101 | + let scope = env.create_light_account(); |
| 102 | + let contract_address = AztecAddress { inner: 0xdeadbeef }; |
| 103 | + |
| 104 | + env.utility_context_at(contract_address, |_| { |
| 105 | + let pk_a = point_from_x_coord(1).unwrap(); |
| 106 | + let pk_b = point_from_x_coord(2).unwrap(); |
| 107 | + |
| 108 | + let mut items: BoundedVec<DiscoveredHandshake, MAX_HANDSHAKES_PER_PAGE> = BoundedVec::new(); |
| 109 | + items.push(DiscoveredHandshake { eph_pk: pk_a, mode: UNCONSTRAINED }); |
| 110 | + items.push(DiscoveredHandshake { eph_pk: pk_b, mode: CONSTRAINED }); |
| 111 | + let page = HandshakePage { items, total_count: 2 }; |
| 112 | + let _ = OracleMock::mock("aztec_utl_callUtilityFunction").returns(page.serialize()); |
| 113 | + |
| 114 | + let secret_a: Field = 111; |
| 115 | + let secret_b: Field = 222; |
| 116 | + mock_get_shared_secrets([secret_a, secret_b]); |
| 117 | + |
| 118 | + let secrets = get_handshake_secrets(contract_address, scope); |
| 119 | + |
| 120 | + assert_eq(secrets.len(), 2); |
| 121 | + assert_eq(secrets.get(0).secret, secret_a); |
| 122 | + assert_eq(secrets.get(0).mode, UNCONSTRAINED); |
| 123 | + assert_eq(secrets.get(1).secret, secret_b); |
| 124 | + assert_eq(secrets.get(1).mode, CONSTRAINED); |
| 125 | + }); |
| 126 | + } |
| 127 | + |
| 128 | + #[test] |
| 129 | + unconstrained fn get_handshake_secrets_fetches_multiple_pages() { |
| 130 | + let mut env = TestEnvironment::new(); |
| 131 | + let scope = env.create_light_account(); |
| 132 | + let contract_address = AztecAddress { inner: 0xdeadbeef }; |
| 133 | + |
| 134 | + env.utility_context_at(contract_address, |_| { |
| 135 | + let pk_a = point_from_x_coord(1).unwrap(); |
| 136 | + let pk_b = point_from_x_coord(2).unwrap(); |
| 137 | + let pk_c = point_from_x_coord(8).unwrap(); |
| 138 | + |
| 139 | + let mut page_1_items: BoundedVec<DiscoveredHandshake, MAX_HANDSHAKES_PER_PAGE> = BoundedVec::new(); |
| 140 | + page_1_items.push(DiscoveredHandshake { eph_pk: pk_a, mode: UNCONSTRAINED }); |
| 141 | + page_1_items.push(DiscoveredHandshake { eph_pk: pk_b, mode: CONSTRAINED }); |
| 142 | + let page_1 = HandshakePage { items: page_1_items, total_count: 3 }; |
| 143 | + |
| 144 | + let mut page_2_items: BoundedVec<DiscoveredHandshake, MAX_HANDSHAKES_PER_PAGE> = BoundedVec::new(); |
| 145 | + page_2_items.push(DiscoveredHandshake { eph_pk: pk_c, mode: UNCONSTRAINED }); |
| 146 | + let page_2 = HandshakePage { items: page_2_items, total_count: 3 }; |
| 147 | + |
| 148 | + let _ = OracleMock::mock("aztec_utl_callUtilityFunction").returns(page_1.serialize()).times(1); |
| 149 | + let _ = OracleMock::mock("aztec_utl_callUtilityFunction").returns(page_2.serialize()).times(1); |
| 150 | + |
| 151 | + mock_get_shared_secrets([111, 222, 333]); |
| 152 | + |
| 153 | + let secrets = get_handshake_secrets(contract_address, scope); |
| 154 | + assert_eq(secrets.len(), 3); |
| 155 | + assert_eq(secrets.get(0).secret, 111); |
| 156 | + assert_eq(secrets.get(0).mode, UNCONSTRAINED); |
| 157 | + assert_eq(secrets.get(1).secret, 222); |
| 158 | + assert_eq(secrets.get(1).mode, CONSTRAINED); |
| 159 | + assert_eq(secrets.get(2).secret, 333); |
| 160 | + assert_eq(secrets.get(2).mode, UNCONSTRAINED); |
| 161 | + }); |
| 162 | + } |
| 163 | + |
| 164 | + unconstrained fn mock_get_shared_secrets<let N: u32>(response_values: [Field; N]) { |
| 165 | + let response_slot: Field = 99; |
| 166 | + let response_array: EphemeralArray<Field> = EphemeralArray::at(response_slot); |
| 167 | + for value in response_values { |
| 168 | + response_array.push(value); |
| 169 | + } |
| 170 | + let _ = OracleMock::mock("aztec_utl_getSharedSecrets").returns(response_slot); |
| 171 | + } |
| 172 | +} |
0 commit comments