From ff2fd17c4ef459548d4db818304206f84082acd4 Mon Sep 17 00:00:00 2001 From: sklppy88 Date: Thu, 17 Oct 2024 09:12:31 +0000 Subject: [PATCH 1/5] init --- .../aztec/src/encrypted_logs/payload.nr | 64 ++++----- .../aztec-nr/aztec/src/keys/getters/mod.nr | 4 +- .../aztec-nr/aztec/src/keys/getters/test.nr | 12 +- .../aztec-nr/aztec/src/utils/point.nr | 2 +- .../src/main.nr | 2 +- .../src/util.nr | 2 +- .../validate_contract_address.nr | 2 +- .../validate_contract_address.nr | 4 +- .../crates/types/src/address/aztec_address.nr | 122 +++++++++++++++--- .../crates/types/src/contract_instance.nr | 2 +- .../types/src/tests/fixtures/contracts.nr | 2 +- .../l1_payload/encrypted_log_payload.test.ts | 63 ++++++--- .../src/contract/contract_address.ts | 5 +- .../__snapshots__/derivation.test.ts.snap | 3 +- .../circuits.js/src/keys/derivation.test.ts | 37 +++++- .../circuits.js/src/keys/derivation.ts | 44 ++++++- .../src/structs/complete_address.test.ts | 4 +- .../src/structs/complete_address.ts | 28 +++- .../end-to-end/src/e2e_block_building.test.ts | 14 +- .../end-to-end/src/e2e_event_logs.test.ts | 12 +- yarn-project/end-to-end/src/shared/browser.ts | 4 +- yarn-project/key-store/src/key_store.test.ts | 4 +- .../src/protocol_contract_data.ts | 14 +- .../src/note_processor/note_processor.test.ts | 40 ++++-- .../pxe/src/note_processor/note_processor.ts | 12 +- .../pxe/src/pxe_service/pxe_service.ts | 21 ++- 26 files changed, 384 insertions(+), 139 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/encrypted_logs/payload.nr b/noir-projects/aztec-nr/aztec/src/encrypted_logs/payload.nr index 60dad2821f09..0a341524d309 100644 --- a/noir-projects/aztec-nr/aztec/src/encrypted_logs/payload.nr +++ b/noir-projects/aztec-nr/aztec/src/encrypted_logs/payload.nr @@ -25,11 +25,16 @@ pub fn compute_encrypted_log( let header = EncryptedLogHeader::new(contract_address); - let incoming_header_ciphertext: [u8; 48] = header.compute_ciphertext(eph_sk, ivpk); + let incoming_header_ciphertext: [u8; 48] = header.compute_ciphertext(eph_sk, recipient); let outgoing_header_ciphertext: [u8; 48] = header.compute_ciphertext(eph_sk, ovpk); - let incoming_body_ciphertext = compute_incoming_body_ciphertext(plaintext, eph_sk, ivpk); - let outgoing_body_ciphertext: [u8; 144] = - compute_outgoing_body_ciphertext(recipient, ivpk, fr_to_fq(ovsk_app), eph_sk, eph_pk); + let incoming_body_ciphertext = compute_incoming_body_ciphertext(plaintext, eph_sk, IvpkM { inner: recipient.to_point() }); + let outgoing_body_ciphertext: [u8; 144] = compute_outgoing_body_ciphertext( + recipient, + IvpkM { inner: recipient.to_point() }, + fr_to_fq(ovsk_app), + eph_sk, + eph_pk + ); let mut encrypted_bytes: [u8; M] = [0; M]; // @todo We ignore the tags for now @@ -161,23 +166,22 @@ mod test { #[test] unconstrained fn test_encrypted_log_matches_typescript() { // All the values in this test were copied over from `tagged_log.test.ts` - let contract_address = AztecAddress::from_field( - 0x10f48cd9eff7ae5b209c557c70de2e657ee79166868676b787e9417e19260e04, - ); - let ovsk_app = 0x03a6513d6def49f41d20373d2cec894c23e7492794b08fc50c0e8a1bd2512612; + let contract_address = AztecAddress::from_field(0x10f48cd9eff7ae5b209c557c70de2e657ee79166868676b787e9417e19260e04); + let ovsk_app = 0x191ac5e29bbc8f80f29ed06b75eaf30c036ed7952d844833860c527077c8c3b4; let ovpk_m = OvpkM { inner: Point { - x: 0x1961448682803198631f299340e4206bb12809d4bebbf012b30f59af73ba1a15, - y: 0x133674060c3925142aceb4f1dcd9f9137d0217d37ff8729ee5ceaa6e2790353d, - is_infinite: false, - }, + x: 0x07f696b8b233de2c1935e43c793399586f532da5ff7c0356636a75acb862e964, + y: 0x156e8a3e42bfca3663936ba98c7fd26386a14657c23b5f5146f1a94b6c465154, + is_infinite: false + } }; + let ivpk_m = IvpkM { inner: Point { - x: 0x260cd3904f6df16e974c29944fdc839e40fb5cf293f03df2eb370851d3a527bc, - y: 0x0eef2964fe6640e84c82b5d2915892409b38e9e25d39f68dd79edb725c55387f, - is_infinite: false, - }, + x: 0x18dd22d6a4032eefe3a7a55703f583396596235f7c186e450c92981186ee7404, + y: 0x2e49e00996565114016a1a478309842ecbaf930fb716c3f498e7e10370631d75, + is_infinite: false + } }; let plaintext = [ @@ -192,9 +196,7 @@ mod test { let eph_sk = 0x1358d15019d4639393d62b97e1588c095957ce74a1c32d6ec7d62fe6705d9538; let _ = OracleMock::mock("getRandomField").returns(eph_sk); - let recipient = AztecAddress::from_field( - 0x10ee41ee4b62703b16f61e03cb0d88c4b306a9eb4a6ceeb2aff13428541689a2, - ); + let recipient = AztecAddress::from_field(0x25afb798ea6d0b8c1618e50fdeafa463059415013d3b7c75d46abf5e242be70c); let log: [u8; 448] = compute_encrypted_log( contract_address, @@ -208,29 +210,7 @@ mod test { // The following value was generated by `tagged_log.test.ts` // --> Run the test with AZTEC_GENERATE_TEST_DATA=1 flag to update test data. let encrypted_log_from_typescript = [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 141, 70, 12, 14, 67, 77, 132, 110, 193, 234, 40, 110, 64, 144, 235, - 86, 55, 111, 242, 123, 221, 193, 170, 202, 225, 216, 86, 84, 159, 112, 31, 167, 126, 79, - 51, 186, 47, 71, 253, 172, 99, 112, 241, 59, 197, 241, 107, 186, 232, 87, 187, 230, 171, - 62, 228, 234, 42, 51, 145, 146, 238, 242, 42, 71, 206, 13, 244, 66, 111, 195, 20, 203, - 98, 148, 204, 242, 145, 183, 156, 29, 141, 54, 44, 220, 194, 35, 229, 16, 32, 204, 211, - 49, 142, 112, 82, 202, 116, 241, 254, 146, 42, 217, 20, 189, 70, 228, 182, 171, 205, - 104, 27, 99, 171, 28, 91, 244, 21, 30, 130, 240, 5, 72, 174, 124, 97, 197, 157, 248, - 193, 23, 193, 76, 46, 141, 144, 70, 211, 45, 67, 167, 218, 129, 140, 104, 190, 41, 110, - 249, 209, 68, 106, 135, 164, 80, 235, 63, 101, 80, 32, 13, 38, 99, 145, 91, 11, 173, - 151, 231, 247, 65, 153, 117, 229, 167, 64, 239, 182, 126, 235, 83, 4, 169, 8, 8, 160, 4, - 235, 252, 21, 96, 84, 161, 69, 145, 145, 215, 254, 161, 117, 246, 198, 65, 89, 179, 194, - 90, 19, 121, 12, 202, 114, 80, 195, 14, 60, 128, 105, 142, 100, 86, 90, 108, 157, 219, - 22, 172, 20, 121, 195, 25, 159, 236, 2, 70, 75, 42, 37, 34, 2, 17, 149, 20, 176, 32, 18, - 204, 56, 117, 121, 34, 15, 3, 88, 123, 64, 68, 74, 233, 63, 59, 131, 222, 194, 192, 167, - 110, 217, 10, 128, 73, 129, 172, 61, 43, 12, 98, 165, 203, 191, 154, 161, 150, 4, 239, - 95, 48, 60, 159, 33, 222, 142, 102, 73, 193, 236, 145, 197, 160, 216, 254, 113, 243, 25, - 244, 251, 192, 222, 35, 7, 114, 101, 35, 152, 151, 112, 24, 32, 94, 138, 71, 160, 91, - 68, 131, 217, 117, 140, 19, 147, 37, 197, 192, 21, 43, 172, 239, 239, 205, 15, 110, 76, - 26, 211, 42, 117, 4, 15, 135, 145, 247, 37, 73, 84, 164, 149, 250, 35, 0, 205, 105, 178, - 143, 104, 98, 100, 250, 193, 154, 136, 175, 177, 109, 225, 207, 252, 147, 250, 250, 189, - 117, 147, 101, 230, 132, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 141, 70, 12, 14, 67, 77, 132, 110, 193, 234, 40, 110, 64, 144, 235, 86, 55, 111, 242, 123, 221, 193, 170, 202, 225, 216, 86, 84, 159, 112, 31, 167, 5, 119, 121, 10, 234, 188, 194, 216, 30, 200, 208, 201, 158, 127, 93, 43, 242, 241, 69, 32, 37, 220, 119, 122, 23, 132, 4, 248, 81, 217, 61, 232, 24, 146, 63, 133, 24, 120, 113, 217, 155, 223, 149, 214, 149, 239, 240, 169, 224, 155, 161, 81, 83, 252, 155, 77, 34, 75, 110, 30, 113, 223, 189, 202, 171, 6, 192, 157, 91, 60, 116, 155, 254, 190, 28, 4, 7, 236, 205, 4, 245, 27, 187, 89, 20, 38, 128, 200, 160, 145, 185, 127, 198, 203, 207, 97, 246, 194, 175, 155, 142, 188, 143, 120, 83, 122, 178, 63, 208, 197, 232, 24, 228, 212, 45, 69, 157, 38, 90, 219, 119, 194, 239, 130, 155, 246, 143, 135, 242, 196, 123, 71, 139, 181, 122, 231, 228, 26, 7, 100, 63, 101, 195, 83, 8, 61, 85, 123, 148, 227, 29, 164, 162, 161, 49, 39, 73, 141, 46, 179, 240, 52, 109, 165, 238, 210, 233, 188, 36, 90, 175, 2, 42, 149, 78, 208, 176, 145, 50, 180, 152, 245, 55, 112, 40, 153, 180, 78, 54, 102, 119, 98, 56, 235, 246, 51, 179, 86, 45, 127, 18, 77, 187, 168, 41, 24, 232, 113, 149, 138, 148, 33, 143, 215, 150, 188, 105, 131, 254, 236, 199, 206, 56, 44, 130, 134, 29, 99, 254, 69, 153, 146, 68, 234, 148, 148, 178, 38, 221, 182, 148, 178, 100, 13, 206, 0, 91, 71, 58, 207, 26, 227, 190, 21, 143, 85, 138, 209, 202, 34, 142, 159, 121, 61, 9, 57, 2, 48, 162, 89, 126, 14, 83, 173, 40, 247, 170, 154, 112, 12, 204, 48, 38, 7, 173, 108, 38, 234, 20, 16, 115, 91, 106, 140, 121, 63, 99, 23, 247, 0, 148, 9, 163, 145, 43, 21, 238, 47, 40, 204, 241, 124, 246, 201, 75, 114, 3, 1, 229, 197, 130, 109, 227, 158, 133, 188, 125, 179, 220, 51, 170, 121, 175, 202, 243, 37, 103, 13, 27, 53, 157, 8, 177, 11, 208, 120, 64, 211, 148, 201, 240, 56 ]; assert_eq(encrypted_log_from_typescript, log); } diff --git a/noir-projects/aztec-nr/aztec/src/keys/getters/mod.nr b/noir-projects/aztec-nr/aztec/src/keys/getters/mod.nr index 52502eed6bfc..6d85a125478d 100644 --- a/noir-projects/aztec-nr/aztec/src/keys/getters/mod.nr +++ b/noir-projects/aztec-nr/aztec/src/keys/getters/mod.nr @@ -26,9 +26,7 @@ pub fn get_public_keys(account: AztecAddress) -> PublicKeys { let (hinted_canonical_public_keys, partial_address) = unsafe { get_public_keys_and_partial_address(account) }; assert_eq( - account, - AztecAddress::compute(hinted_canonical_public_keys.hash(), partial_address), - "Invalid public keys hint for address", + account, AztecAddress::compute(hinted_canonical_public_keys, partial_address), "Invalid public keys hint for address" ); hinted_canonical_public_keys diff --git a/noir-projects/aztec-nr/aztec/src/keys/getters/test.nr b/noir-projects/aztec-nr/aztec/src/keys/getters/test.nr index 9a5ee36603cd..c988bc398d98 100644 --- a/noir-projects/aztec-nr/aztec/src/keys/getters/test.nr +++ b/noir-projects/aztec-nr/aztec/src/keys/getters/test.nr @@ -18,9 +18,15 @@ unconstrained fn test_get_public_keys_unknown() { // Instead of querying for some unknown account, which would result in the oracle erroring out, we mock a bad oracle // response to check that the circuit properly checks the address derivation. - let _ = OracleMock::mock("getPublicKeysAndPartialAddress") - .returns([0; KEY_ORACLE_RESPONSE_LENGTH]) - .times(1); + let original_keys = get_public_keys(account.address).serialize(); + + // We fill in all the keys but we leave partial address = 0 + let mut bad_response = [0; KEY_ORACLE_RESPONSE_LENGTH]; + for i in 0..12 { + bad_response[i] = original_keys[i]; + } + + let _ = OracleMock::mock("getPublicKeysAndPartialAddress").returns(bad_response.serialize()).times(1); let _ = get_public_keys(account.address); } diff --git a/noir-projects/aztec-nr/aztec/src/utils/point.nr b/noir-projects/aztec-nr/aztec/src/utils/point.nr index ecfb802dd480..70da5e73d28e 100644 --- a/noir-projects/aztec-nr/aztec/src/utils/point.nr +++ b/noir-projects/aztec-nr/aztec/src/utils/point.nr @@ -1,6 +1,6 @@ use dep::protocol_types::point::Point; -// I am storing the modulus divided by 2 plus 1 here because full modulus would throw "String literal too large" error +// I am storing the modulus minus 1 divided by 2 here because full modulus would throw "String literal too large" error // Full modulus is 21888242871839275222246405745257275088548364400416034343698204186575808495617 global BN254_FR_MODULUS_DIV_2: Field = 10944121435919637611123202872628637544274182200208017171849102093287904247808; diff --git a/noir-projects/noir-contracts/contracts/contract_instance_deployer_contract/src/main.nr b/noir-projects/noir-contracts/contracts/contract_instance_deployer_contract/src/main.nr index 6ffb083f2645..48da9bbbcbfc 100644 --- a/noir-projects/noir-contracts/contracts/contract_instance_deployer_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/contract_instance_deployer_contract/src/main.nr @@ -94,7 +94,7 @@ contract ContractInstanceDeployer { let partial_address = PartialAddress::compute(contract_class_id, salt, initialization_hash, deployer); - let address = AztecAddress::compute(public_keys.hash(), partial_address); + let address = AztecAddress::compute(public_keys, partial_address); // Emit the address as a nullifier to be able to prove that this instance has been (not) deployed context.push_nullifier(address.to_field()); diff --git a/noir-projects/noir-contracts/contracts/schnorr_single_key_account_contract/src/util.nr b/noir-projects/noir-contracts/contracts/schnorr_single_key_account_contract/src/util.nr index 82e839cda08e..c8bf3891ad18 100644 --- a/noir-projects/noir-contracts/contracts/schnorr_single_key_account_contract/src/util.nr +++ b/noir-projects/noir-contracts/contracts/schnorr_single_key_account_contract/src/util.nr @@ -13,5 +13,5 @@ pub fn recover_address(message_hash: Field, witness: AuthWitness) -> AztecAddres ); assert(verification == true); - AztecAddress::compute(witness.keys.hash(), witness.partial_address) + AztecAddress::compute(witness.keys, witness.partial_address) } diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/private_call_data_validator/validate_contract_address.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/private_call_data_validator/validate_contract_address.nr index 8d0eec386ed2..b4b4e20086e6 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/private_call_data_validator/validate_contract_address.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/private_call_data_validator/validate_contract_address.nr @@ -22,7 +22,7 @@ pub fn validate_contract_address( private_call_data.contract_class_artifact_hash, private_call_data.contract_class_public_bytecode_commitment, private_call_data.salted_initialization_hash, - private_call_data.public_keys.hash(), + private_call_data.public_keys, ); let protocol_contract_index = contract_address.to_field(); diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/private_call_data_validator_builder/validate_contract_address.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/private_call_data_validator_builder/validate_contract_address.nr index aba970eb4049..fd82f88f6a8b 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/private_call_data_validator_builder/validate_contract_address.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/private_call_data_validator_builder/validate_contract_address.nr @@ -1,5 +1,6 @@ use crate::tests::private_call_data_validator_builder::PrivateCallDataValidatorBuilder; use dep::types::address::AztecAddress; +use std::embedded_curve_ops::{EmbeddedCurvePoint, EmbeddedCurveScalar, fixed_base_scalar_mul as derive_public_key}; impl PrivateCallDataValidatorBuilder { pub fn new_with_regular_contract() -> Self { @@ -71,8 +72,7 @@ fn validate_contract_address_incorrect_partial_address_preimage_fails() { fn validate_contract_address_incorrect_address_preimage_fails() { let mut builder = PrivateCallDataValidatorBuilder::new_with_regular_contract(); - builder.private_call.public_keys.ivpk_m.inner.x = - builder.private_call.public_keys.ivpk_m.inner.x + 1; + builder.private_call.public_keys.ivpk_m.inner = derive_public_key(EmbeddedCurveScalar::from_field(69)); builder.validate(); } diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/address/aztec_address.nr b/noir-projects/noir-protocol-circuits/crates/types/src/address/aztec_address.nr index c1515da11b00..0acd0934af33 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/address/aztec_address.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/address/aztec_address.nr @@ -1,16 +1,30 @@ use crate::{ abis::function_selector::FunctionSelector, + public_keys::{ToPoint, PublicKeys, NpkM, IvpkM, OvpkM, TpkM}, address::{ - partial_address::PartialAddress, public_keys_hash::PublicKeysHash, - salted_initialization_hash::SaltedInitializationHash, - }, - constants::{AZTEC_ADDRESS_LENGTH, FUNCTION_TREE_HEIGHT, GENERATOR_INDEX__CONTRACT_ADDRESS_V1}, + partial_address::PartialAddress, public_keys_hash::PublicKeysHash, + salted_initialization_hash::SaltedInitializationHash +}, + constants::{ + AZTEC_ADDRESS_LENGTH, FUNCTION_TREE_HEIGHT, GENERATOR_INDEX__PUBLIC_KEYS_HASH, + GENERATOR_INDEX__CONTRACT_ADDRESS_V1 +}, contract_class_id::ContractClassId, hash::{poseidon2_hash_with_separator, private_functions_root_from_siblings}, merkle_tree::membership::MembershipWitness, traits::{Empty, FromField, ToField, Serialize, Deserialize}, utils, }; +global BN254_FR_MODULUS_DIV_2: Field = 10944121435919637611123202872628637544274182200208017171849102093287904247808; + +// We do below because `use crate::point::Point;` does not work +use dep::std::embedded_curve_ops::EmbeddedCurvePoint as Point; + +use std::{ + ec::{sqrt, pow}, + embedded_curve_ops::{fixed_base_scalar_mul as derive_public_key, EmbeddedCurveScalar} +}; + // Aztec address pub struct AztecAddress { inner: Field, @@ -52,16 +66,53 @@ impl Deserialize for AztecAddress { } } +impl ToPoint for AztecAddress { + fn to_point(self) -> Point { + // Calculate y^2 = x^3 - 17 + let y_squared = pow(self.inner, 3) - 17; + + // TODO (#8970): Failure validation + // We can see if y is square first, or we can soft fail with just sqrt(y_squared); + // If y is not square, the x-coordinate is not on the curve + // Do we throw here or can we soft continue and this is fine ? Test this with points not on curve to see what happens. + // let y_is_square = is_square(y_squared); + // assert(y_is_square); + + let mut y = sqrt(y_squared); + + // If we get a negative y coordinate, we pin it to the positive one by subtracting it from the Field modulus + if (!(y.lt(BN254_FR_MODULUS_DIV_2) | y.eq(BN254_FR_MODULUS_DIV_2))) { + y = (BN254_FR_MODULUS_DIV_2 + BN254_FR_MODULUS_DIV_2 + 1) - y; + } + + Point { x: self.inner, y, is_infinite: false } + } +} + impl AztecAddress { pub fn zero() -> Self { Self { inner: 0 } } - pub fn compute(pub_keys_hash: PublicKeysHash, partial_address: PartialAddress) -> AztecAddress { - AztecAddress::from_field(poseidon2_hash_with_separator( - [pub_keys_hash.to_field(), partial_address.to_field()], - GENERATOR_INDEX__CONTRACT_ADDRESS_V1, - )) + pub fn compute_preaddress(pub_keys_hash: PublicKeysHash, partial_address: PartialAddress) -> AztecAddress { + AztecAddress::from_field( + poseidon2_hash_with_separator( + [pub_keys_hash.to_field(), partial_address.to_field()], + GENERATOR_INDEX__CONTRACT_ADDRESS_V1 + ) + ) + } + + pub fn compute(public_keys: PublicKeys, partial_address: PartialAddress) -> AztecAddress { + let public_keys_hash = public_keys.hash(); + + let pre_address = poseidon2_hash_with_separator( + [public_keys_hash.to_field(), partial_address.to_field()], + GENERATOR_INDEX__CONTRACT_ADDRESS_V1 + ); + + let address_point = derive_public_key(EmbeddedCurveScalar::from_field(pre_address)).add(public_keys.ivpk_m.to_point()); + AztecAddress::from_field(address_point.x) } pub fn compute_from_private_function( @@ -71,7 +122,7 @@ impl AztecAddress { contract_class_artifact_hash: Field, contract_class_public_bytecode_commitment: Field, salted_initialization_hash: SaltedInitializationHash, - public_keys_hash: PublicKeysHash, + public_keys: PublicKeys ) -> Self { let private_functions_root = private_functions_root_from_siblings( function_selector, @@ -92,7 +143,7 @@ impl AztecAddress { salted_initialization_hash, ); - AztecAddress::compute(public_keys_hash, partial_address) + AztecAddress::compute(public_keys, partial_address) } pub fn is_zero(self) -> bool { @@ -110,14 +161,53 @@ impl AztecAddress { } #[test] -fn compute_address_from_partial_and_pub_keys_hash() { +fn compute_address_from_partial_and_pub_keys() { + let public_keys = PublicKeys { + npk_m: NpkM { + inner: Point { + x: 0x22f7fcddfa3ce3e8f0cc8e82d7b94cdd740afa3e77f8e4a63ea78a239432dcab, + y: 0x0471657de2b6216ade6c506d28fbc22ba8b8ed95c871ad9f3e3984e90d9723a7, + is_infinite: false + } + }, + ivpk_m: IvpkM { + inner: Point { + x: 0x111223493147f6785514b1c195bb37a2589f22a6596d30bb2bb145fdc9ca8f1e, + y: 0x273bbffd678edce8fe30e0deafc4f66d58357c06fd4a820285294b9746c3be95, + is_infinite: false + } + }, + ovpk_m: OvpkM { + inner: Point { + x: 0x09115c96e962322ffed6522f57194627136b8d03ac7469109707f5e44190c484, + y: 0x0c49773308a13d740a7f0d4f0e6163b02c5a408b6f965856b6a491002d073d5b, + is_infinite: false + } + }, + tpk_m: TpkM { + inner: Point { + x: 0x00d3d81beb009873eb7116327cf47c612d5758ef083d4fda78e9b63980b2a762, + y: 0x2f567d22d2b02fe1f4ad42db9d58a36afd1983e7e2909d1cab61cafedad6193a, + is_infinite: false + } + } + }; + + let partial_address = PartialAddress::from_field(0x0a7c585381b10f4666044266a02405bf6e01fa564c8517d4ad5823493abd31de); + + let address = AztecAddress::compute(public_keys, partial_address); + let expected_computed_address_from_partial_and_pubkey = 0x24e4646f58b9fbe7d38e317db8d5636c423fbbdfbe119fc190fe9c64747e0c62; + assert(address.to_field() == expected_computed_address_from_partial_and_pubkey); +} + +#[test] +fn compute_preaddress_from_partial_and_pub_keys_hash() { let pub_keys_hash = PublicKeysHash::from_field(1); let partial_address = PartialAddress::from_field(2); - let address = AztecAddress::compute(pub_keys_hash, partial_address); - let expected_computed_address_from_partial_and_pubkey = - 0x23ce9be3fa3c846b0f9245cc796902e731d04f086e8a42473bb29e405fc98075; - assert(address.to_field() == expected_computed_address_from_partial_and_pubkey); + let address = AztecAddress::compute_preaddress(pub_keys_hash, partial_address); + let expected_computed_preaddress_from_partial_and_pubkey = 0x23ce9be3fa3c846b0f9245cc796902e731d04f086e8a42473bb29e405fc98075; + assert(address.to_field() == expected_computed_preaddress_from_partial_and_pubkey); } #[test] diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/contract_instance.nr b/noir-projects/noir-protocol-circuits/crates/types/src/contract_instance.nr index dfe75822abac..dee9b16f0821 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/contract_instance.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/contract_instance.nr @@ -81,7 +81,7 @@ impl Hash for ContractInstance { impl ContractInstance { fn to_address(self) -> AztecAddress { AztecAddress::compute( - self.public_keys.hash(), + self.public_keys, PartialAddress::compute( self.contract_class_id, self.salt, diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/tests/fixtures/contracts.nr b/noir-projects/noir-protocol-circuits/crates/types/src/tests/fixtures/contracts.nr index c854adeec7fc..6adece0766db 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/tests/fixtures/contracts.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/tests/fixtures/contracts.nr @@ -92,7 +92,7 @@ pub fn get_protocol_contract(index: u32) -> ContractData { salted_initialization_hash, ); - let address = AztecAddress::compute(public_keys.hash(), partial_address); + let address = AztecAddress::compute(public_keys, partial_address); ContractData { contract_address_salt: 1, diff --git a/yarn-project/circuit-types/src/logs/l1_payload/encrypted_log_payload.test.ts b/yarn-project/circuit-types/src/logs/l1_payload/encrypted_log_payload.test.ts index 2c17b670f58e..74785a323041 100644 --- a/yarn-project/circuit-types/src/logs/l1_payload/encrypted_log_payload.test.ts +++ b/yarn-project/circuit-types/src/logs/l1_payload/encrypted_log_payload.test.ts @@ -1,6 +1,15 @@ -import { AztecAddress, KeyValidationRequest, computeOvskApp, derivePublicKeyFromSecretKey } from '@aztec/circuits.js'; +import { + AztecAddress, + CompleteAddress, + KeyValidationRequest, + computeAddressSecret, + computeOvskApp, + computePoint, + deriveKeys, + derivePublicKeyFromSecretKey, +} from '@aztec/circuits.js'; import { randomBytes } from '@aztec/foundation/crypto'; -import { Fr, GrumpkinScalar } from '@aztec/foundation/fields'; +import { Fr, GrumpkinScalar, Point } from '@aztec/foundation/fields'; import { updateInlineTestData } from '@aztec/foundation/testing'; import { EncryptedLogPayload } from './encrypted_log_payload.js'; @@ -10,6 +19,7 @@ const PLACEHOLDER_TAG = new Fr(33); describe('EncryptedLogPayload', () => { describe('encrypt and decrypt a full log', () => { + let completeAddress: CompleteAddress; let ovskM: GrumpkinScalar; let ivskM: GrumpkinScalar; @@ -21,21 +31,28 @@ describe('EncryptedLogPayload', () => { const contract = AztecAddress.random(); original = new EncryptedLogPayload(PLACEHOLDER_TAG, PLACEHOLDER_TAG, contract, incomingBodyPlaintext); - ovskM = GrumpkinScalar.random(); - ivskM = GrumpkinScalar.random(); + const secretKey = Fr.random(); + const partialAddress = Fr.random(); + ({ masterOutgoingViewingSecretKey: ovskM, masterIncomingViewingSecretKey: ivskM } = deriveKeys(secretKey)); + + completeAddress = CompleteAddress.fromSecretKeyAndPartialAddress(secretKey, partialAddress); const ovKeys = getKeyValidationRequest(ovskM, contract); const ephSk = GrumpkinScalar.random(); - const recipientAddress = AztecAddress.random(); - const ivpk = derivePublicKeyFromSecretKey(ivskM); - - encrypted = original.encrypt(ephSk, recipientAddress, ivpk, ovKeys); + encrypted = original.encrypt( + ephSk, + completeAddress.address, + computePoint(completeAddress.address), + ovKeys, + ); }); it('decrypt a log as incoming', () => { - const recreated = EncryptedLogPayload.decryptAsIncoming(encrypted, ivskM); + const addressSecret = computeAddressSecret(completeAddress.getPreaddress(), ivskM); + + const recreated = EncryptedLogPayload.decryptAsIncoming(encrypted, addressSecret); expect(recreated?.toBuffer()).toEqual(original.toBuffer()); }); @@ -56,21 +73,25 @@ describe('EncryptedLogPayload', () => { ); const log = new EncryptedLogPayload(new Fr(0), new Fr(0), contract, plaintext); - const ovskM = new GrumpkinScalar(0x06b76394ac57b8a18ceb08b14ed15b5b778d5c506b4cfb7edc203324eab27c05n); - const ivskM = new GrumpkinScalar(0x03fd94b1101e834e829cda4f227043f60490b5c7b3073875f5bfbe5028ed05ccn); + const ovskM = new GrumpkinScalar(0x1d7f6b3c491e99f32aad05c433301f3a2b4ed68de661ff8255d275ff94de6fc4n); const ovKeys = getKeyValidationRequest(ovskM, contract); + const ephSk = new GrumpkinScalar(0x1358d15019d4639393d62b97e1588c095957ce74a1c32d6ec7d62fe6705d9538n); - const recipientAddress = AztecAddress.fromString( - '0x10ee41ee4b62703b16f61e03cb0d88c4b306a9eb4a6ceeb2aff13428541689a2', + const recipientCompleteAddress = CompleteAddress.fromString( + '0x25afb798ea6d0b8c1618e50fdeafa463059415013d3b7c75d46abf5e242be70c138af8799f2fba962549802469e12e3b7ba4c5f9c999c6421e05c73f45ec68481970dd8ce0250b677759dfc040f6edaf77c5827a7bcd425e66bcdec3fa7e59bc18dd22d6a4032eefe3a7a55703f583396596235f7c186e450c92981186ee74042e49e00996565114016a1a478309842ecbaf930fb716c3f498e7e10370631d7507f696b8b233de2c1935e43c793399586f532da5ff7c0356636a75acb862e964156e8a3e42bfca3663936ba98c7fd26386a14657c23b5f5146f1a94b6c4651542685ea16f17c580a7cc7c8ff2688dce9bde8bf1f50475f4c3281e1c33404ee0025f50db0733f719462b22eff03cec746bb9e3829ae3636c84fbccd2754b5a5a92087a5f41ccf94a03a2671cd341ba3264c45147e75d4ea96e3b1a58498550b89', ); - const ivpk = derivePublicKeyFromSecretKey(ivskM); - - const encrypted = log.encrypt(ephSk, recipientAddress, ivpk, ovKeys).toString('hex'); - + const encrypted = log + .encrypt( + ephSk, + recipientCompleteAddress.address, + computePoint(recipientCompleteAddress.address), + ovKeys, + ) + .toString('hex'); expect(encrypted).toMatchInlineSnapshot( - `"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008d460c0e434d846ec1ea286e4090eb56376ff27bddc1aacae1d856549f701fa77e4f33ba2f47fdac6370f13bc5f16bbae857bbe6ab3ee4ea2a339192eef22a47ce0df4426fc314cb6294ccf291b79c1d8d362cdcc223e51020ccd3318e7052ca74f1fe922ad914bd46e4b6abcd681b63ab1c5bf4151e82f00548ae7c61c59df8c117c14c2e8d9046d32d43a7da818c68be296ef9d1446a87a450eb3f6550200d2663915b0bad97e7f7419975e5a740efb67eeb5304a90808a004ebfc156054a1459191d7fea175f6c64159b3c25a13790cca7250c30e3c80698e64565a6c9ddb16ac1479c3199fec02464b2a252202119514b02012cc387579220f03587b40444ae93f3b83dec2c0a76ed90a804981ac3d2b0c62a5cbbf9aa19604ef5f303c9f21de8e6649c1ec91c5a0d8fe71f319f4fbc0de230772652398977018205e8a47a05b4483d9758c139325c5c0152bacefefcd0f6e4c1ad32a75040f8791f7254954a495fa2300cd69b28f686264fac19a88afb16de1cffc93fafabd759365e684"`, + `"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008d460c0e434d846ec1ea286e4090eb56376ff27bddc1aacae1d856549f701fa70577790aeabcc2d81ec8d0c99e7f5d2bf2f1452025dc777a178404f851d93de818923f85187871d99bdf95d695eff0a9e09ba15153fc9b4d224b6e1e71dfbdcaab06c09d5b3c749bfebe1c0407eccd04f51bbb59142680c8a091b97fc6cbcf61f6c2af9b8ebc8f78537ab23fd0c5e818e4d42d459d265adb77c2ef829bf68f87f2c47b478bb57ae7e41a07643f65c353083d557b94e31da4a2a13127498d2eb3f0346da5eed2e9bc245aaf022a954ed0b09132b498f537702899b44e3666776238ebf633b3562d7f124dbba82918e871958a94218fd796bc6983feecc7ce382c82861d63fe45999244ea9494b226ddb694b2640dce005b473acf1ae3be158f558ad1ca228e9f793d09390230a2597e0e53ad28f7aa9a700ccc302607ad6c26ea1410735b6a8c793f6317f7009409a3912b15ee2f28ccf17cf6c94b720301e5c5826de39e85bc7db3dc33aa79afcaf325670d1b359d08b10bd07840d394c9f038"`, ); const byteArrayString = `[${encrypted.match(/.{1,2}/g)!.map(byte => parseInt(byte, 16))}]`; @@ -81,6 +102,12 @@ describe('EncryptedLogPayload', () => { 'encrypted_log_from_typescript', byteArrayString, ); + + const ivskM = new GrumpkinScalar(0x0d6e27b21c89a7632f7766e35cc280d43f75bea3898d7328400a5fefc804d462n); + + const addressSecret = computeAddressSecret(recipientCompleteAddress.getPreaddress(), ivskM); + const recreated = EncryptedLogPayload.decryptAsIncoming(Buffer.from(encrypted, 'hex'), addressSecret); + expect(recreated?.toBuffer()).toEqual(log.toBuffer()); }); const getKeyValidationRequest = (ovskM: GrumpkinScalar, app: AztecAddress) => { diff --git a/yarn-project/circuits.js/src/contract/contract_address.ts b/yarn-project/circuits.js/src/contract/contract_address.ts index 571d8a33f74c..a250fecf39b0 100644 --- a/yarn-project/circuits.js/src/contract/contract_address.ts +++ b/yarn-project/circuits.js/src/contract/contract_address.ts @@ -15,7 +15,7 @@ import { type ContractInstance } from './interfaces/contract_instance.js'; * ``` * salted_initialization_hash = pedersen([salt, initialization_hash, deployer], GENERATOR__SALTED_INITIALIZATION_HASH) * partial_address = pedersen([contract_class_id, salted_initialization_hash], GENERATOR__CONTRACT_PARTIAL_ADDRESS_V1) - * address = poseidon2Hash([public_keys_hash, partial_address, GENERATOR__CONTRACT_ADDRESS_V1]) + * address = (poseidon2Hash([public_keys_hash, partial_address, GENERATOR__CONTRACT_ADDRESS_V1]) * G) + ivpk_m * ``` * @param instance - A contract instance for which to calculate the deployment address. */ @@ -25,8 +25,7 @@ export function computeContractAddressFromInstance( | ({ contractClassId: Fr; saltedInitializationHash: Fr } & Pick), ): AztecAddress { const partialAddress = computePartialAddress(instance); - const publicKeysHash = instance.publicKeys.hash(); - return computeAddress(publicKeysHash, partialAddress); + return computeAddress(instance.publicKeys, partialAddress); } /** diff --git a/yarn-project/circuits.js/src/keys/__snapshots__/derivation.test.ts.snap b/yarn-project/circuits.js/src/keys/__snapshots__/derivation.test.ts.snap index 54555005a5c7..ccf574c1a37e 100644 --- a/yarn-project/circuits.js/src/keys/__snapshots__/derivation.test.ts.snap +++ b/yarn-project/circuits.js/src/keys/__snapshots__/derivation.test.ts.snap @@ -1,3 +1,4 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`๐Ÿ”‘ Address from partial matches Noir 1`] = `"0x23ce9be3fa3c846b0f9245cc796902e731d04f086e8a42473bb29e405fc98075"`; +exports[`๐Ÿ”‘ Pre address from partial matches Noir 1`] = `"0x23ce9be3fa3c846b0f9245cc796902e731d04f086e8a42473bb29e405fc98075"`; +exports[`๐Ÿ”‘ Address matches Noir 1`] = `"0x24e4646f58b9fbe7d38e317db8d5636c423fbbdfbe119fc190fe9c64747e0c62"`; diff --git a/yarn-project/circuits.js/src/keys/derivation.test.ts b/yarn-project/circuits.js/src/keys/derivation.test.ts index d4999149c5a3..bc84cb8b17fc 100644 --- a/yarn-project/circuits.js/src/keys/derivation.test.ts +++ b/yarn-project/circuits.js/src/keys/derivation.test.ts @@ -2,7 +2,7 @@ import { Fr, Point } from '@aztec/foundation/fields'; import { updateInlineTestData } from '@aztec/foundation/testing'; import { PublicKeys } from '../types/public_keys.js'; -import { computeAddress } from './derivation.js'; +import { computeAddress, computePreaddress } from './derivation.js'; describe('๐Ÿ”‘', () => { it('computing public keys hash matches Noir', () => { @@ -29,16 +29,45 @@ describe('๐Ÿ”‘', () => { ); }); - it('Address from partial matches Noir', () => { + it('Pre address from partial matches Noir', () => { const publicKeysHash = new Fr(1n); const partialAddress = new Fr(2n); - const address = computeAddress(publicKeysHash, partialAddress).toString(); + const address = computePreaddress(publicKeysHash, partialAddress).toString(); expect(address).toMatchSnapshot(); // Run with AZTEC_GENERATE_TEST_DATA=1 to update noir test data updateInlineTestData( 'noir-projects/noir-protocol-circuits/crates/types/src/address/aztec_address.nr', - 'expected_computed_address_from_partial_and_pubkey', + 'expected_computed_preaddress_from_partial_and_pubkey', + address.toString(), + ); + }); + + it('Address matches Noir', () => { + const npkM = Point.fromString( + '0x22f7fcddfa3ce3e8f0cc8e82d7b94cdd740afa3e77f8e4a63ea78a239432dcab0471657de2b6216ade6c506d28fbc22ba8b8ed95c871ad9f3e3984e90d9723a7', + ); + const ivpkM = Point.fromString( + '0x111223493147f6785514b1c195bb37a2589f22a6596d30bb2bb145fdc9ca8f1e273bbffd678edce8fe30e0deafc4f66d58357c06fd4a820285294b9746c3be95', + ); + const ovpkM = Point.fromString( + '0x09115c96e962322ffed6522f57194627136b8d03ac7469109707f5e44190c4840c49773308a13d740a7f0d4f0e6163b02c5a408b6f965856b6a491002d073d5b', + ); + const tpkM = Point.fromString( + '0x00d3d81beb009873eb7116327cf47c612d5758ef083d4fda78e9b63980b2a7622f567d22d2b02fe1f4ad42db9d58a36afd1983e7e2909d1cab61cafedad6193a', + ); + + const publicKeys = new PublicKeys(npkM, ivpkM, ovpkM, tpkM); + + const partialAddress = Fr.fromString('0x0a7c585381b10f4666044266a02405bf6e01fa564c8517d4ad5823493abd31de'); + + const address = computeAddress(publicKeys, partialAddress).toString(); + expect(address).toMatchSnapshot(); + + // Run with AZTEC_GENERATE_TEST_DATA=1 to update noir test data + updateInlineTestData( + 'noir-projects/noir-protocol-circuits/crates/types/src/address/aztec_address.nr', + 'expected_computed_address_from_partial_and_pubkeys', address.toString(), ); }); diff --git a/yarn-project/circuits.js/src/keys/derivation.ts b/yarn-project/circuits.js/src/keys/derivation.ts index c8eb037c167f..f76b82f84484 100644 --- a/yarn-project/circuits.js/src/keys/derivation.ts +++ b/yarn-project/circuits.js/src/keys/derivation.ts @@ -1,6 +1,6 @@ import { AztecAddress } from '@aztec/foundation/aztec-address'; import { poseidon2HashWithSeparator, sha512ToGrumpkinScalar } from '@aztec/foundation/crypto'; -import { type Fq, type Fr, GrumpkinScalar } from '@aztec/foundation/fields'; +import { Fq, Fr, GrumpkinScalar, Point } from '@aztec/foundation/fields'; import { Grumpkin } from '../barretenberg/crypto/grumpkin/index.js'; import { GeneratorIndex } from '../constants.gen.js'; @@ -41,9 +41,45 @@ export function deriveSigningKey(secretKey: Fr): GrumpkinScalar { return sha512ToGrumpkinScalar([secretKey, GeneratorIndex.IVSK_M]); } -export function computeAddress(publicKeysHash: Fr, partialAddress: Fr) { - const addressFr = poseidon2HashWithSeparator([publicKeysHash, partialAddress], GeneratorIndex.CONTRACT_ADDRESS_V1); - return AztecAddress.fromField(addressFr); +export function computePreaddress(publicKeysHash: Fr, partialAddress: Fr) { + return poseidon2HashWithSeparator([publicKeysHash, partialAddress], GeneratorIndex.CONTRACT_ADDRESS_V1); +} + +export function computeAddress(publicKeys: PublicKeys, partialAddress: Fr) { + const preaddress = computePreaddress(publicKeys.hash(), partialAddress); + const address = computeAddressFromPreaddressAndIvpkM(preaddress, publicKeys.masterIncomingViewingPublicKey); + + return address; +} + +export function computeAddressFromPreaddressAndIvpkM(preaddress: Fr, ivpkM: Point) { + const addressPoint = computeAddressPointFromPreaddressAndIvpkM(preaddress, ivpkM); + + return AztecAddress.fromField(addressPoint.x); +} + +export function computeAddressPointFromPreaddressAndIvpkM(preaddress: Fr, ivpkM: Point) { + const preaddressPoint = derivePublicKeyFromSecretKey(new Fq(preaddress.toBigInt())); + const addressPoint = new Grumpkin().add(preaddressPoint, ivpkM); + + return addressPoint; +} + +export function computeAddressSecret(preaddress: Fr, ivsk: Fq) { + const addressSecretCandidate = ivsk.add(new Fq(preaddress.toBigInt())); + const addressPointCandidate = derivePublicKeyFromSecretKey(addressSecretCandidate); + + // If our secret computes a point with a negative y-coordinate, we then negate the secret to produce the secret + // that can decrypt payloads encrypted with the point having a positive y-coordinate. + if (!addressPointCandidate.y.lt(new Fr((Fr.MODULUS - 1n) / 2n))) { + return new Fq(Fq.MODULUS - addressSecretCandidate.toBigInt()); + } + + return addressSecretCandidate; +} + +export function computePoint(address: AztecAddress) { + return Point.fromXAndSign(address, true); } export function derivePublicKeyFromSecretKey(secretKey: Fq) { diff --git a/yarn-project/circuits.js/src/structs/complete_address.test.ts b/yarn-project/circuits.js/src/structs/complete_address.test.ts index 50a7b071c9f4..d531e2e344a2 100644 --- a/yarn-project/circuits.js/src/structs/complete_address.test.ts +++ b/yarn-project/circuits.js/src/structs/complete_address.test.ts @@ -38,11 +38,11 @@ describe('CompleteAddress', () => { // docs:start:instantiate-complete-address // Typically a recipient would share their complete address with the sender const completeAddressFromString = CompleteAddress.fromString( - '0x23d95e303879a5d0bbef78ecbc335e559da37431f6dcd11da54ed375c284681322f7fcddfa3ce3e8f0cc8e82d7b94cdd740afa3e77f8e4a63ea78a239432dcab0471657de2b6216ade6c506d28fbc22ba8b8ed95c871ad9f3e3984e90d9723a7111223493147f6785514b1c195bb37a2589f22a6596d30bb2bb145fdc9ca8f1e273bbffd678edce8fe30e0deafc4f66d58357c06fd4a820285294b9746c3be9509115c96e962322ffed6522f57194627136b8d03ac7469109707f5e44190c4840c49773308a13d740a7f0d4f0e6163b02c5a408b6f965856b6a491002d073d5b00d3d81beb009873eb7116327cf47c612d5758ef083d4fda78e9b63980b2a7622f567d22d2b02fe1f4ad42db9d58a36afd1983e7e2909d1cab61cafedad6193a0a7c585381b10f4666044266a02405bf6e01fa564c8517d4ad5823493abd31de', + '0x24e4646f58b9fbe7d38e317db8d5636c423fbbdfbe119fc190fe9c64747e0c6222f7fcddfa3ce3e8f0cc8e82d7b94cdd740afa3e77f8e4a63ea78a239432dcab0471657de2b6216ade6c506d28fbc22ba8b8ed95c871ad9f3e3984e90d9723a7111223493147f6785514b1c195bb37a2589f22a6596d30bb2bb145fdc9ca8f1e273bbffd678edce8fe30e0deafc4f66d58357c06fd4a820285294b9746c3be9509115c96e962322ffed6522f57194627136b8d03ac7469109707f5e44190c4840c49773308a13d740a7f0d4f0e6163b02c5a408b6f965856b6a491002d073d5b00d3d81beb009873eb7116327cf47c612d5758ef083d4fda78e9b63980b2a7622f567d22d2b02fe1f4ad42db9d58a36afd1983e7e2909d1cab61cafedad6193a0a7c585381b10f4666044266a02405bf6e01fa564c8517d4ad5823493abd31de', ); // Alternatively, a recipient could share the individual components with the sender - const address = Fr.fromString('0x23d95e303879a5d0bbef78ecbc335e559da37431f6dcd11da54ed375c2846813'); + const address = Fr.fromString('0x24e4646f58b9fbe7d38e317db8d5636c423fbbdfbe119fc190fe9c64747e0c62'); const npkM = Point.fromString( '0x22f7fcddfa3ce3e8f0cc8e82d7b94cdd740afa3e77f8e4a63ea78a239432dcab0471657de2b6216ade6c506d28fbc22ba8b8ed95c871ad9f3e3984e90d9723a7', ); diff --git a/yarn-project/circuits.js/src/structs/complete_address.ts b/yarn-project/circuits.js/src/structs/complete_address.ts index 31c36f15698d..28bb9dd24e08 100644 --- a/yarn-project/circuits.js/src/structs/complete_address.ts +++ b/yarn-project/circuits.js/src/structs/complete_address.ts @@ -1,9 +1,10 @@ import { AztecAddress } from '@aztec/foundation/aztec-address'; -import { Fr } from '@aztec/foundation/fields'; +import { Fq, Fr } from '@aztec/foundation/fields'; import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize'; +import { Grumpkin } from '../barretenberg/index.js'; import { computePartialAddress } from '../contract/contract_address.js'; -import { computeAddress, deriveKeys } from '../keys/index.js'; +import { computePreaddress, deriveKeys, derivePublicKeyFromSecretKey } from '../keys/index.js'; import { type PartialAddress } from '../types/partial_address.js'; import { PublicKeys } from '../types/public_keys.js'; @@ -35,9 +36,18 @@ export class CompleteAddress { } static fromSecretKeyAndPartialAddress(secretKey: Fr, partialAddress: Fr): CompleteAddress { - const { publicKeys } = deriveKeys(secretKey); - const address = computeAddress(publicKeys.hash(), partialAddress); - return new CompleteAddress(address, publicKeys, partialAddress); + const { publicKeys, masterIncomingViewingSecretKey } = deriveKeys(secretKey); + const preaddress = computePreaddress(publicKeys.hash(), partialAddress); + + const addressSecret = masterIncomingViewingSecretKey.add(new Fq(preaddress.toBigInt())); + + const addressPoint = derivePublicKeyFromSecretKey(addressSecret); + + return new CompleteAddress(AztecAddress.fromField(addressPoint.x), publicKeys, partialAddress); + } + + getPreaddress() { + return computePreaddress(this.publicKeys.hash(), this.partialAddress); } static fromSecretKeyAndInstance( @@ -50,8 +60,12 @@ export class CompleteAddress { /** Throws if the address is not correctly derived from the public key and partial address.*/ public validate() { - const expectedAddress = computeAddress(this.publicKeys.hash(), this.partialAddress); - if (!expectedAddress.equals(this.address)) { + const expectedPreAddress = computePreaddress(this.publicKeys.hash(), this.partialAddress); + const expectedPreAddressPoint = derivePublicKeyFromSecretKey(new Fq(expectedPreAddress.toBigInt())); + + const expectedAddress = new Grumpkin().add(expectedPreAddressPoint, this.publicKeys.masterIncomingViewingPublicKey); + + if (!AztecAddress.fromField(expectedAddress.x).equals(this.address)) { throw new Error( `Address cannot be derived from public keys and partial address (received ${this.address.toString()}, derived ${expectedAddress.toString()})`, ); diff --git a/yarn-project/end-to-end/src/e2e_block_building.test.ts b/yarn-project/end-to-end/src/e2e_block_building.test.ts index a89fd40dacd2..4760f4492a2a 100644 --- a/yarn-project/end-to-end/src/e2e_block_building.test.ts +++ b/yarn-project/end-to-end/src/e2e_block_building.test.ts @@ -16,7 +16,11 @@ import { retryUntil, sleep, } from '@aztec/aztec.js'; -import { AZTEC_EPOCH_PROOF_CLAIM_WINDOW_IN_L2_SLOTS } from '@aztec/circuits.js'; +import { + AZTEC_EPOCH_PROOF_CLAIM_WINDOW_IN_L2_SLOTS, + computeAddressSecret, + deriveMasterIncomingViewingSecretKey, +} from '@aztec/circuits.js'; import { times } from '@aztec/foundation/collection'; import { poseidon2HashWithSeparator } from '@aztec/foundation/crypto'; import { StatefulTestContract, StatefulTestContractArtifact } from '@aztec/noir-contracts.js'; @@ -299,7 +303,13 @@ describe('e2e_block_building', () => { // compare logs expect(rct.status).toEqual('success'); const noteValues = tx.noteEncryptedLogs.unrollLogs().map(l => { - const notePayload = L1NotePayload.decryptAsIncoming(l, keys.masterIncomingViewingSecretKey); + const notePayload = L1NotePayload.decryptAsIncoming( + l, + computeAddressSecret( + thisWallet.getCompleteAddress().getPreaddress(), + deriveMasterIncomingViewingSecretKey(thisWallet.getSecretKey()), + ), + ); return notePayload?.note.items[0]; }); expect(noteValues[0]).toEqual(new Fr(10)); diff --git a/yarn-project/end-to-end/src/e2e_event_logs.test.ts b/yarn-project/end-to-end/src/e2e_event_logs.test.ts index d15b19d740c9..d800610e513a 100644 --- a/yarn-project/end-to-end/src/e2e_event_logs.test.ts +++ b/yarn-project/end-to-end/src/e2e_event_logs.test.ts @@ -6,7 +6,7 @@ import { L1EventPayload, type PXE, } from '@aztec/aztec.js'; -import { deriveMasterIncomingViewingSecretKey } from '@aztec/circuits.js'; +import { computeAddressSecret, deriveMasterIncomingViewingSecretKey } from '@aztec/circuits.js'; import { EventSelector } from '@aztec/foundation/abi'; import { makeTuple } from '@aztec/foundation/array'; import { type Tuple } from '@aztec/foundation/serialize'; @@ -57,7 +57,10 @@ describe('Logs', () => { const decryptedEvent0 = L1EventPayload.decryptAsIncoming( encryptedLogs[0], - deriveMasterIncomingViewingSecretKey(wallets[0].getSecretKey()), + computeAddressSecret( + wallets[0].getCompleteAddress().getPreaddress(), + deriveMasterIncomingViewingSecretKey(wallets[0].getSecretKey()), + ), )!; expect(decryptedEvent0.contractAddress).toStrictEqual(testLogContract.address); @@ -77,7 +80,10 @@ describe('Logs', () => { const decryptedEvent1 = L1EventPayload.decryptAsIncoming( encryptedLogs[2], - deriveMasterIncomingViewingSecretKey(wallets[0].getSecretKey()), + computeAddressSecret( + wallets[0].getCompleteAddress().getPreaddress(), + deriveMasterIncomingViewingSecretKey(wallets[0].getSecretKey()), + ), )!; expect(decryptedEvent1.contractAddress).toStrictEqual(testLogContract.address); diff --git a/yarn-project/end-to-end/src/shared/browser.ts b/yarn-project/end-to-end/src/shared/browser.ts index 65d6a150aefb..5f2ee0a153eb 100644 --- a/yarn-project/end-to-end/src/shared/browser.ts +++ b/yarn-project/end-to-end/src/shared/browser.ts @@ -145,14 +145,14 @@ export const browserTestSuite = ( it('Can access CompleteAddress class in browser', async () => { const result: string = await page.evaluate(() => { const completeAddress = window.AztecJs.CompleteAddress.fromString( - '0x05eb10b0bda78b5d8dc91de3d62bed7b55d73b5509521a9f8ef7269133e58a8c2c93b9572b35f9c9e07e9003ae1ca444442a165f927bce00e347dab57cc19391148730d0deec722eb6c54747df7345bc2ab3bd8e81f438b17b81ccabd9e6a3ac0708920251ccaf6664d769cbc47c8d767f64912639e13d9f9e441b225066161900c48a65eea83f1dbf217c43daf1be6ba9cefd2754f07e3cc13e81e5432e47f30dfb47c8b1e11368bec638fd9d22c696bf9c323a0fd09050745f4b7cf150bfa529a9f3062ee5f9d0a099ac53b4e1130653fb797ed2b59914a8915951d13ad8252521211957a854707af85ad40e9ab4d474a4fcbdcbe7a47866cae0db4fd86ed2261669d85a9cfbd09365a6db5d7acfe5560104a0cb893a375d6c08ffb9cbb8270be446a16361f271ac11899ee19f990c68035da18703ba00c8e9773dfe6a784a', + '0x2401bfdad7ac9282bd612e8a6bb0f6ce125b08e317e24dc04ddbba24694ac2e7261249d8b3ad8ad9ed075257eede1dcd8356bfd55e1518f07717c47609194b6500c926582f07fda6a53e3600251f2aa1401c0cd377cef064f3f59045222194541acc5f62d8907a6dc98b85e32f8097a152c3c795bb3981c64e576b014f23005e0891d109aa087560cf8720ae94098827aa009a0bcee09f98fd2a05a7cbc6185402a53516a379d7856d26e3bb5542f1fe57f1ee5a0e4c60f7a463205aa19e2f8e00bce110b9a89857b79e3f70777e38a262b04cf80c56bd833a3c4b58dde7dbdc25c807c4012229e08651fd0d48cf9d966d9ab18826692f48a4cf934bef78614023e9cb95711f532786c7c78e72c3752f03f2a4cafc1846ad9df47324e2b7683f0faaa2e6fe44f3ff68646ce7d8538cb6935ce25472c4c75a244ab0c5d2e3b74d', ); // NOTE: browser does not know how to serialize CompleteAddress for return, so return a string // otherwise returning a CompleteAddress makes result undefined. return completeAddress.toString(); }); expect(result).toBe( - '0x05eb10b0bda78b5d8dc91de3d62bed7b55d73b5509521a9f8ef7269133e58a8c2c93b9572b35f9c9e07e9003ae1ca444442a165f927bce00e347dab57cc19391148730d0deec722eb6c54747df7345bc2ab3bd8e81f438b17b81ccabd9e6a3ac0708920251ccaf6664d769cbc47c8d767f64912639e13d9f9e441b225066161900c48a65eea83f1dbf217c43daf1be6ba9cefd2754f07e3cc13e81e5432e47f30dfb47c8b1e11368bec638fd9d22c696bf9c323a0fd09050745f4b7cf150bfa529a9f3062ee5f9d0a099ac53b4e1130653fb797ed2b59914a8915951d13ad8252521211957a854707af85ad40e9ab4d474a4fcbdcbe7a47866cae0db4fd86ed2261669d85a9cfbd09365a6db5d7acfe5560104a0cb893a375d6c08ffb9cbb8270be446a16361f271ac11899ee19f990c68035da18703ba00c8e9773dfe6a784a', + '0x2401bfdad7ac9282bd612e8a6bb0f6ce125b08e317e24dc04ddbba24694ac2e7261249d8b3ad8ad9ed075257eede1dcd8356bfd55e1518f07717c47609194b6500c926582f07fda6a53e3600251f2aa1401c0cd377cef064f3f59045222194541acc5f62d8907a6dc98b85e32f8097a152c3c795bb3981c64e576b014f23005e0891d109aa087560cf8720ae94098827aa009a0bcee09f98fd2a05a7cbc6185402a53516a379d7856d26e3bb5542f1fe57f1ee5a0e4c60f7a463205aa19e2f8e00bce110b9a89857b79e3f70777e38a262b04cf80c56bd833a3c4b58dde7dbdc25c807c4012229e08651fd0d48cf9d966d9ab18826692f48a4cf934bef78614023e9cb95711f532786c7c78e72c3752f03f2a4cafc1846ad9df47324e2b7683f0faaa2e6fe44f3ff68646ce7d8538cb6935ce25472c4c75a244ab0c5d2e3b74d', ); }); diff --git a/yarn-project/key-store/src/key_store.test.ts b/yarn-project/key-store/src/key_store.test.ts index 25fcdea0a559..7c1e4ce68b5a 100644 --- a/yarn-project/key-store/src/key_store.test.ts +++ b/yarn-project/key-store/src/key_store.test.ts @@ -17,7 +17,7 @@ describe('KeyStore', () => { const { address: accountAddress } = await keyStore.addAccount(sk, partialAddress); expect(accountAddress.toString()).toMatchInlineSnapshot( - `"0x2321fcb3bd7447b178138746ee78f6fbb1e2a2aa8ff542f51420b884bab641cc"`, + `"0x13c731a2c339964488f847ca0dac49ac71dafc3f34bab6ec3e5d83e7468885ab"`, ); const { pkM: masterNullifierPublicKey } = await keyStore.getKeyValidationRequest( @@ -66,7 +66,7 @@ describe('KeyStore', () => { // Returned accounts are as expected const accounts = await keyStore.getAccounts(); expect(accounts.toString()).toMatchInlineSnapshot( - `"0x2321fcb3bd7447b178138746ee78f6fbb1e2a2aa8ff542f51420b884bab641cc"`, + `"0x13c731a2c339964488f847ca0dac49ac71dafc3f34bab6ec3e5d83e7468885ab"`, ); // Manages to find master nullifer secret key for pub key diff --git a/yarn-project/protocol-contracts/src/protocol_contract_data.ts b/yarn-project/protocol-contracts/src/protocol_contract_data.ts index f05089798eb8..48ce95ad950c 100644 --- a/yarn-project/protocol-contracts/src/protocol_contract_data.ts +++ b/yarn-project/protocol-contracts/src/protocol_contract_data.ts @@ -50,14 +50,14 @@ export const ProtocolContractAddress: Record }; export const ProtocolContractLeaf = { - AuthRegistry: Fr.fromString('0x0d7c69bbd50f9da911b5ab26e313dafc7f66b0e03a52d6d98270c54ed751c4e2'), - ContractInstanceDeployer: Fr.fromString('0x0f91b27c8187748d9710cc579b3fc08b56e5a391b9f7818b71feadd39816bc6d'), - ContractClassRegisterer: Fr.fromString('0x17f57693577de778fa6f4d83c280597ac659acfbce64175aacc390d289f97953'), - MultiCallEntrypoint: Fr.fromString('0x054b0c9a77a2fcaf1c3a1519205a21d4627cb950245a462d6a9bfc52892d6b7a'), - FeeJuice: Fr.fromString('0x03c51587dc12449bd85f33627111224910a934b3c79fcf720fd1c6ccac0f34e1'), - Router: Fr.fromString('0x250937e9172b417f70fb2d0bdefdf69268e1134f1612418cf58fb80ff4278fe9'), + AuthRegistry: Fr.fromString('0x087d102766af335cf7654eb5d946dccf114d0eb1e86dc788cbc10d87e1c84fd0'), + ContractInstanceDeployer: Fr.fromString('0x10141a7093a1050d38fc0467c6c81ca7beb158ef47095145ce4edd52be7ef661'), + ContractClassRegisterer: Fr.fromString('0x074de3b4ee658fd695a1be6c4f9f10fe3042b948955436e0e20c31c7027cfaef'), + MultiCallEntrypoint: Fr.fromString('0x19bc0ab26e84d8b2551cb3c83000a5da0f6958056fcaa91f63c14c35e266ef0d'), + FeeJuice: Fr.fromString('0x033a034d778d077970ae6108253a4971e37af6bd163d80b3be184c5838045eda'), + Router: Fr.fromString('0x24eed1db8f33a2db1611412c16157a28a57c7bef4794844c4ef228d538438ac8'), }; export const protocolContractTreeRoot = Fr.fromString( - '0x1010ec5ed35322c9adf0ef6573d9f4bdb43bb971ddfe8dfa7776c5d4254eb2ab', + '0x1299197d756a2e86830d877cb2c5c71ab20aa4f97726fc7ccd2d468ce20d81a6', ); diff --git a/yarn-project/pxe/src/note_processor/note_processor.test.ts b/yarn-project/pxe/src/note_processor/note_processor.test.ts index 686fd09c7f85..831957c109bb 100644 --- a/yarn-project/pxe/src/note_processor/note_processor.test.ts +++ b/yarn-project/pxe/src/note_processor/note_processor.test.ts @@ -8,6 +8,7 @@ import { MAX_NOTE_HASHES_PER_TX, type PublicKey, computeOvskApp, + computePoint, deriveKeys, } from '@aztec/circuits.js'; import { pedersenHash } from '@aztec/foundation/crypto'; @@ -184,7 +185,7 @@ describe('Note Processor', () => { 4, 0, 2, - ownerIvpkM, + computePoint(account.address), KeyValidationRequest.random(), ); @@ -217,11 +218,32 @@ describe('Note Processor', () => { it('should store multiple notes that belong to us', async () => { const requests = [ - new MockNoteRequest(getRandomNoteLogPayload(app), 1, 1, 1, ownerIvpkM, ownerOvKeys), + new MockNoteRequest( + getRandomNoteLogPayload(app), + 1, + 1, + 1, + computePoint(account.address), + ownerOvKeys, + ), new MockNoteRequest(getRandomNoteLogPayload(app), 2, 3, 0, Point.random(), ownerOvKeys), - new MockNoteRequest(getRandomNoteLogPayload(app), 6, 3, 2, ownerIvpkM, KeyValidationRequest.random()), + new MockNoteRequest( + getRandomNoteLogPayload(app), + 6, + 3, + 2, + computePoint(account.address), + KeyValidationRequest.random(), + ), new MockNoteRequest(getRandomNoteLogPayload(app), 9, 3, 2, Point.random(), KeyValidationRequest.random()), - new MockNoteRequest(getRandomNoteLogPayload(app), 12, 3, 2, ownerIvpkM, ownerOvKeys), + new MockNoteRequest( + getRandomNoteLogPayload(app), + 12, + 3, + 2, + computePoint(account.address), + ownerOvKeys, + ), ]; const blocks = mockBlocks(requests); @@ -270,11 +292,11 @@ describe('Note Processor', () => { const note2 = getRandomNoteLogPayload(app); // All note payloads except one have the same contract address, storage slot, and the actual note. const requests = [ - new MockNoteRequest(note, 3, 0, 0, ownerIvpkM, ownerOvKeys), - new MockNoteRequest(note, 4, 0, 2, ownerIvpkM, ownerOvKeys), - new MockNoteRequest(note, 4, 2, 0, ownerIvpkM, ownerOvKeys), - new MockNoteRequest(note2, 5, 2, 1, ownerIvpkM, ownerOvKeys), - new MockNoteRequest(note, 6, 2, 3, ownerIvpkM, ownerOvKeys), + new MockNoteRequest(note, 3, 0, 0, computePoint(account.address), ownerOvKeys), + new MockNoteRequest(note, 4, 0, 2, computePoint(account.address), ownerOvKeys), + new MockNoteRequest(note, 4, 2, 0, computePoint(account.address), ownerOvKeys), + new MockNoteRequest(note2, 5, 2, 1, computePoint(account.address), ownerOvKeys), + new MockNoteRequest(note, 6, 2, 3, computePoint(account.address), ownerOvKeys), ]; const blocks = mockBlocks(requests); diff --git a/yarn-project/pxe/src/note_processor/note_processor.ts b/yarn-project/pxe/src/note_processor/note_processor.ts index b5bd723dc6d1..b3bfa94e53ea 100644 --- a/yarn-project/pxe/src/note_processor/note_processor.ts +++ b/yarn-project/pxe/src/note_processor/note_processor.ts @@ -1,6 +1,12 @@ import { type AztecNode, L1NotePayload, type L2Block } from '@aztec/circuit-types'; import { type NoteProcessorStats } from '@aztec/circuit-types/stats'; -import { type CompleteAddress, INITIAL_L2_BLOCK_NUM, MAX_NOTE_HASHES_PER_TX, type PublicKey } from '@aztec/circuits.js'; +import { + type CompleteAddress, + INITIAL_L2_BLOCK_NUM, + MAX_NOTE_HASHES_PER_TX, + type PublicKey, + computeAddressSecret, +} from '@aztec/circuits.js'; import { type Fr } from '@aztec/foundation/fields'; import { type Logger, createDebugLogger } from '@aztec/foundation/log'; import { Timer } from '@aztec/foundation/timer'; @@ -115,6 +121,8 @@ export class NoteProcessor { const deferredOutgoingNotes: DeferredNoteDao[] = []; const ivskM = await this.keyStore.getMasterSecretKey(this.ivpkM); + const addressSecret = computeAddressSecret(this.account.getPreaddress(), ivskM); + const ovskM = await this.keyStore.getMasterSecretKey(this.ovpkM); // Iterate over both blocks and encrypted logs. @@ -142,7 +150,7 @@ export class NoteProcessor { for (const functionLogs of txFunctionLogs) { for (const log of functionLogs.logs) { this.stats.seen++; - const incomingNotePayload = L1NotePayload.decryptAsIncoming(log, ivskM); + const incomingNotePayload = L1NotePayload.decryptAsIncoming(log, addressSecret); const outgoingNotePayload = L1NotePayload.decryptAsOutgoing(log, ovskM); if (incomingNotePayload || outgoingNotePayload) { diff --git a/yarn-project/pxe/src/pxe_service/pxe_service.ts b/yarn-project/pxe/src/pxe_service/pxe_service.ts index 7bacee26001b..b598e7f5464a 100644 --- a/yarn-project/pxe/src/pxe_service/pxe_service.ts +++ b/yarn-project/pxe/src/pxe_service/pxe_service.ts @@ -41,6 +41,7 @@ import { type NodeInfo, type PartialAddress, type PrivateKernelTailCircuitPublicInputs, + computeAddressSecret, computeContractAddressFromInstance, computeContractClassId, getContractClassFromArtifact, @@ -858,6 +859,7 @@ export class PXEService implements PXE { from: number, limit: number, eventMetadata: EventMetadata, + // TODO: Fix this by allow it to pass only an address vpks: Point[], ): Promise { if (vpks.length === 0) { @@ -871,7 +873,24 @@ export class PXEService implements PXE { const encryptedLogs = encryptedTxLogs.flatMap(encryptedTxLog => encryptedTxLog.unrollLogs()); - const vsks = await Promise.all(vpks.map(vpk => this.keyStore.getMasterSecretKey(vpk))); + const vsks = await Promise.all( + vpks.map(async vpk => { + const [keyPrefix, account] = this.keyStore.getKeyPrefixAndAccount(vpk); + let secretKey = await this.keyStore.getMasterSecretKey(vpk); + if (keyPrefix === 'iv') { + const registeredAccount = await this.getRegisteredAccount(account); + if (!registeredAccount) { + throw new Error('No registered account'); + } + + const preaddress = registeredAccount.getPreaddress(); + + secretKey = computeAddressSecret(preaddress, secretKey); + } + + return secretKey; + }), + ); const visibleEvents = encryptedLogs.flatMap(encryptedLog => { for (const sk of vsks) { From 9e4d684d26fa881e04412eae5ce29adf7bc22059 Mon Sep 17 00:00:00 2001 From: sklppy88 Date: Thu, 24 Oct 2024 00:00:17 +0000 Subject: [PATCH 2/5] addressing feedback --- .../aztec/src/encrypted_logs/payload.nr | 45 +++++++++--- .../aztec-nr/aztec/src/keys/getters/mod.nr | 4 +- .../aztec-nr/aztec/src/keys/getters/test.nr | 4 +- .../validate_contract_address.nr | 7 +- .../crates/types/src/address/aztec_address.nr | 72 ++++++++++--------- .../l1_payload/encrypted_log_payload.test.ts | 16 +---- .../contract_address.test.ts.snap | 2 +- .../circuits.js/src/keys/derivation.ts | 2 +- .../__snapshots__/noir_test_gen.test.ts.snap | 4 +- .../src/note_processor/note_processor.test.ts | 18 +---- 10 files changed, 95 insertions(+), 79 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/encrypted_logs/payload.nr b/noir-projects/aztec-nr/aztec/src/encrypted_logs/payload.nr index 0a341524d309..ed3e81bddb7d 100644 --- a/noir-projects/aztec-nr/aztec/src/encrypted_logs/payload.nr +++ b/noir-projects/aztec-nr/aztec/src/encrypted_logs/payload.nr @@ -27,13 +27,14 @@ pub fn compute_encrypted_log( let incoming_header_ciphertext: [u8; 48] = header.compute_ciphertext(eph_sk, recipient); let outgoing_header_ciphertext: [u8; 48] = header.compute_ciphertext(eph_sk, ovpk); - let incoming_body_ciphertext = compute_incoming_body_ciphertext(plaintext, eph_sk, IvpkM { inner: recipient.to_point() }); + let incoming_body_ciphertext = + compute_incoming_body_ciphertext(plaintext, eph_sk, IvpkM { inner: recipient.to_point() }); let outgoing_body_ciphertext: [u8; 144] = compute_outgoing_body_ciphertext( recipient, IvpkM { inner: recipient.to_point() }, fr_to_fq(ovsk_app), eph_sk, - eph_pk + eph_pk, ); let mut encrypted_bytes: [u8; M] = [0; M]; @@ -166,22 +167,24 @@ mod test { #[test] unconstrained fn test_encrypted_log_matches_typescript() { // All the values in this test were copied over from `tagged_log.test.ts` - let contract_address = AztecAddress::from_field(0x10f48cd9eff7ae5b209c557c70de2e657ee79166868676b787e9417e19260e04); + let contract_address = AztecAddress::from_field( + 0x10f48cd9eff7ae5b209c557c70de2e657ee79166868676b787e9417e19260e04, + ); let ovsk_app = 0x191ac5e29bbc8f80f29ed06b75eaf30c036ed7952d844833860c527077c8c3b4; let ovpk_m = OvpkM { inner: Point { x: 0x07f696b8b233de2c1935e43c793399586f532da5ff7c0356636a75acb862e964, y: 0x156e8a3e42bfca3663936ba98c7fd26386a14657c23b5f5146f1a94b6c465154, - is_infinite: false - } + is_infinite: false, + }, }; let ivpk_m = IvpkM { inner: Point { x: 0x18dd22d6a4032eefe3a7a55703f583396596235f7c186e450c92981186ee7404, y: 0x2e49e00996565114016a1a478309842ecbaf930fb716c3f498e7e10370631d75, - is_infinite: false - } + is_infinite: false, + }, }; let plaintext = [ @@ -196,7 +199,9 @@ mod test { let eph_sk = 0x1358d15019d4639393d62b97e1588c095957ce74a1c32d6ec7d62fe6705d9538; let _ = OracleMock::mock("getRandomField").returns(eph_sk); - let recipient = AztecAddress::from_field(0x25afb798ea6d0b8c1618e50fdeafa463059415013d3b7c75d46abf5e242be70c); + let recipient = AztecAddress::from_field( + 0x25afb798ea6d0b8c1618e50fdeafa463059415013d3b7c75d46abf5e242be70c, + ); let log: [u8; 448] = compute_encrypted_log( contract_address, @@ -210,7 +215,29 @@ mod test { // The following value was generated by `tagged_log.test.ts` // --> Run the test with AZTEC_GENERATE_TEST_DATA=1 flag to update test data. let encrypted_log_from_typescript = [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 141, 70, 12, 14, 67, 77, 132, 110, 193, 234, 40, 110, 64, 144, 235, 86, 55, 111, 242, 123, 221, 193, 170, 202, 225, 216, 86, 84, 159, 112, 31, 167, 5, 119, 121, 10, 234, 188, 194, 216, 30, 200, 208, 201, 158, 127, 93, 43, 242, 241, 69, 32, 37, 220, 119, 122, 23, 132, 4, 248, 81, 217, 61, 232, 24, 146, 63, 133, 24, 120, 113, 217, 155, 223, 149, 214, 149, 239, 240, 169, 224, 155, 161, 81, 83, 252, 155, 77, 34, 75, 110, 30, 113, 223, 189, 202, 171, 6, 192, 157, 91, 60, 116, 155, 254, 190, 28, 4, 7, 236, 205, 4, 245, 27, 187, 89, 20, 38, 128, 200, 160, 145, 185, 127, 198, 203, 207, 97, 246, 194, 175, 155, 142, 188, 143, 120, 83, 122, 178, 63, 208, 197, 232, 24, 228, 212, 45, 69, 157, 38, 90, 219, 119, 194, 239, 130, 155, 246, 143, 135, 242, 196, 123, 71, 139, 181, 122, 231, 228, 26, 7, 100, 63, 101, 195, 83, 8, 61, 85, 123, 148, 227, 29, 164, 162, 161, 49, 39, 73, 141, 46, 179, 240, 52, 109, 165, 238, 210, 233, 188, 36, 90, 175, 2, 42, 149, 78, 208, 176, 145, 50, 180, 152, 245, 55, 112, 40, 153, 180, 78, 54, 102, 119, 98, 56, 235, 246, 51, 179, 86, 45, 127, 18, 77, 187, 168, 41, 24, 232, 113, 149, 138, 148, 33, 143, 215, 150, 188, 105, 131, 254, 236, 199, 206, 56, 44, 130, 134, 29, 99, 254, 69, 153, 146, 68, 234, 148, 148, 178, 38, 221, 182, 148, 178, 100, 13, 206, 0, 91, 71, 58, 207, 26, 227, 190, 21, 143, 85, 138, 209, 202, 34, 142, 159, 121, 61, 9, 57, 2, 48, 162, 89, 126, 14, 83, 173, 40, 247, 170, 154, 112, 12, 204, 48, 38, 7, 173, 108, 38, 234, 20, 16, 115, 91, 106, 140, 121, 63, 99, 23, 247, 0, 148, 9, 163, 145, 43, 21, 238, 47, 40, 204, 241, 124, 246, 201, 75, 114, 3, 1, 229, 197, 130, 109, 227, 158, 133, 188, 125, 179, 220, 51, 170, 121, 175, 202, 243, 37, 103, 13, 27, 53, 157, 8, 177, 11, 208, 120, 64, 211, 148, 201, 240, 56 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 141, 70, 12, 14, 67, 77, 132, 110, 193, 234, 40, 110, 64, 144, 235, + 86, 55, 111, 242, 123, 221, 193, 170, 202, 225, 216, 86, 84, 159, 112, 31, 167, 5, 119, + 121, 10, 234, 188, 194, 216, 30, 200, 208, 201, 158, 127, 93, 43, 242, 241, 69, 32, 37, + 220, 119, 122, 23, 132, 4, 248, 81, 217, 61, 232, 24, 146, 63, 133, 24, 120, 113, 217, + 155, 223, 149, 214, 149, 239, 240, 169, 224, 155, 161, 81, 83, 252, 155, 77, 34, 75, + 110, 30, 113, 223, 189, 202, 171, 6, 192, 157, 91, 60, 116, 155, 254, 190, 28, 4, 7, + 236, 205, 4, 245, 27, 187, 89, 20, 38, 128, 200, 160, 145, 185, 127, 198, 203, 207, 97, + 246, 194, 175, 155, 142, 188, 143, 120, 83, 122, 178, 63, 208, 197, 232, 24, 228, 212, + 45, 69, 157, 38, 90, 219, 119, 194, 239, 130, 155, 246, 143, 135, 242, 196, 123, 71, + 139, 181, 122, 231, 228, 26, 7, 100, 63, 101, 195, 83, 8, 61, 85, 123, 148, 227, 29, + 164, 162, 161, 49, 39, 73, 141, 46, 179, 240, 52, 109, 165, 238, 210, 233, 188, 36, 90, + 175, 2, 42, 149, 78, 208, 176, 145, 50, 180, 152, 245, 55, 112, 40, 153, 180, 78, 54, + 102, 119, 98, 56, 235, 246, 51, 179, 86, 45, 127, 18, 77, 187, 168, 41, 24, 232, 113, + 149, 138, 148, 33, 143, 215, 150, 188, 105, 131, 254, 236, 199, 206, 56, 44, 130, 134, + 29, 99, 254, 69, 153, 146, 68, 234, 148, 148, 178, 38, 221, 182, 148, 178, 100, 13, 206, + 0, 91, 71, 58, 207, 26, 227, 190, 21, 143, 85, 138, 209, 202, 34, 142, 159, 121, 61, 9, + 57, 2, 48, 162, 89, 126, 14, 83, 173, 40, 247, 170, 154, 112, 12, 204, 48, 38, 7, 173, + 108, 38, 234, 20, 16, 115, 91, 106, 140, 121, 63, 99, 23, 247, 0, 148, 9, 163, 145, 43, + 21, 238, 47, 40, 204, 241, 124, 246, 201, 75, 114, 3, 1, 229, 197, 130, 109, 227, 158, + 133, 188, 125, 179, 220, 51, 170, 121, 175, 202, 243, 37, 103, 13, 27, 53, 157, 8, 177, + 11, 208, 120, 64, 211, 148, 201, 240, 56, ]; assert_eq(encrypted_log_from_typescript, log); } diff --git a/noir-projects/aztec-nr/aztec/src/keys/getters/mod.nr b/noir-projects/aztec-nr/aztec/src/keys/getters/mod.nr index 6d85a125478d..75ed3452c1f1 100644 --- a/noir-projects/aztec-nr/aztec/src/keys/getters/mod.nr +++ b/noir-projects/aztec-nr/aztec/src/keys/getters/mod.nr @@ -26,7 +26,9 @@ pub fn get_public_keys(account: AztecAddress) -> PublicKeys { let (hinted_canonical_public_keys, partial_address) = unsafe { get_public_keys_and_partial_address(account) }; assert_eq( - account, AztecAddress::compute(hinted_canonical_public_keys, partial_address), "Invalid public keys hint for address" + account, + AztecAddress::compute(hinted_canonical_public_keys, partial_address), + "Invalid public keys hint for address", ); hinted_canonical_public_keys diff --git a/noir-projects/aztec-nr/aztec/src/keys/getters/test.nr b/noir-projects/aztec-nr/aztec/src/keys/getters/test.nr index c988bc398d98..d90a857378a3 100644 --- a/noir-projects/aztec-nr/aztec/src/keys/getters/test.nr +++ b/noir-projects/aztec-nr/aztec/src/keys/getters/test.nr @@ -26,7 +26,9 @@ unconstrained fn test_get_public_keys_unknown() { bad_response[i] = original_keys[i]; } - let _ = OracleMock::mock("getPublicKeysAndPartialAddress").returns(bad_response.serialize()).times(1); + let _ = OracleMock::mock("getPublicKeysAndPartialAddress") + .returns(bad_response.serialize()) + .times(1); let _ = get_public_keys(account.address); } diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/private_call_data_validator_builder/validate_contract_address.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/private_call_data_validator_builder/validate_contract_address.nr index fd82f88f6a8b..7928b8e889b6 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/private_call_data_validator_builder/validate_contract_address.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/private_call_data_validator_builder/validate_contract_address.nr @@ -1,6 +1,8 @@ use crate::tests::private_call_data_validator_builder::PrivateCallDataValidatorBuilder; use dep::types::address::AztecAddress; -use std::embedded_curve_ops::{EmbeddedCurvePoint, EmbeddedCurveScalar, fixed_base_scalar_mul as derive_public_key}; +use std::embedded_curve_ops::{ + EmbeddedCurvePoint, EmbeddedCurveScalar, fixed_base_scalar_mul as derive_public_key, +}; impl PrivateCallDataValidatorBuilder { pub fn new_with_regular_contract() -> Self { @@ -72,7 +74,8 @@ fn validate_contract_address_incorrect_partial_address_preimage_fails() { fn validate_contract_address_incorrect_address_preimage_fails() { let mut builder = PrivateCallDataValidatorBuilder::new_with_regular_contract(); - builder.private_call.public_keys.ivpk_m.inner = derive_public_key(EmbeddedCurveScalar::from_field(69)); + builder.private_call.public_keys.ivpk_m.inner = + derive_public_key(EmbeddedCurveScalar::from_field(69)); builder.validate(); } diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/address/aztec_address.nr b/noir-projects/noir-protocol-circuits/crates/types/src/address/aztec_address.nr index 0acd0934af33..6edbd0f973cb 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/address/aztec_address.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/address/aztec_address.nr @@ -2,27 +2,27 @@ use crate::{ abis::function_selector::FunctionSelector, public_keys::{ToPoint, PublicKeys, NpkM, IvpkM, OvpkM, TpkM}, address::{ - partial_address::PartialAddress, public_keys_hash::PublicKeysHash, - salted_initialization_hash::SaltedInitializationHash -}, + partial_address::PartialAddress, public_keys_hash::PublicKeysHash, + salted_initialization_hash::SaltedInitializationHash, + }, constants::{ - AZTEC_ADDRESS_LENGTH, FUNCTION_TREE_HEIGHT, GENERATOR_INDEX__PUBLIC_KEYS_HASH, - GENERATOR_INDEX__CONTRACT_ADDRESS_V1 -}, - contract_class_id::ContractClassId, + AZTEC_ADDRESS_LENGTH, FUNCTION_TREE_HEIGHT, GENERATOR_INDEX__PUBLIC_KEYS_HASH, + GENERATOR_INDEX__CONTRACT_ADDRESS_V1, + }, contract_class_id::ContractClassId, hash::{poseidon2_hash_with_separator, private_functions_root_from_siblings}, merkle_tree::membership::MembershipWitness, traits::{Empty, FromField, ToField, Serialize, Deserialize}, utils, }; -global BN254_FR_MODULUS_DIV_2: Field = 10944121435919637611123202872628637544274182200208017171849102093287904247808; +global BN254_FR_MODULUS_DIV_2: Field = + 10944121435919637611123202872628637544274182200208017171849102093287904247808; // We do below because `use crate::point::Point;` does not work use dep::std::embedded_curve_ops::EmbeddedCurvePoint as Point; use std::{ ec::{sqrt, pow}, - embedded_curve_ops::{fixed_base_scalar_mul as derive_public_key, EmbeddedCurveScalar} + embedded_curve_ops::{fixed_base_scalar_mul as derive_public_key, EmbeddedCurveScalar}, }; // Aztec address @@ -77,7 +77,6 @@ impl ToPoint for AztecAddress { // Do we throw here or can we soft continue and this is fine ? Test this with points not on curve to see what happens. // let y_is_square = is_square(y_squared); // assert(y_is_square); - let mut y = sqrt(y_squared); // If we get a negative y coordinate, we pin it to the positive one by subtracting it from the Field modulus @@ -94,12 +93,13 @@ impl AztecAddress { Self { inner: 0 } } - pub fn compute_preaddress(pub_keys_hash: PublicKeysHash, partial_address: PartialAddress) -> AztecAddress { - AztecAddress::from_field( - poseidon2_hash_with_separator( - [pub_keys_hash.to_field(), partial_address.to_field()], - GENERATOR_INDEX__CONTRACT_ADDRESS_V1 - ) + pub fn compute_preaddress( + pub_keys_hash: PublicKeysHash, + partial_address: PartialAddress, + ) -> Field { + poseidon2_hash_with_separator( + [pub_keys_hash.to_field(), partial_address.to_field()], + GENERATOR_INDEX__CONTRACT_ADDRESS_V1, ) } @@ -108,10 +108,12 @@ impl AztecAddress { let pre_address = poseidon2_hash_with_separator( [public_keys_hash.to_field(), partial_address.to_field()], - GENERATOR_INDEX__CONTRACT_ADDRESS_V1 + GENERATOR_INDEX__CONTRACT_ADDRESS_V1, ); - let address_point = derive_public_key(EmbeddedCurveScalar::from_field(pre_address)).add(public_keys.ivpk_m.to_point()); + let address_point = derive_public_key(EmbeddedCurveScalar::from_field(pre_address)).add( + public_keys.ivpk_m.to_point(), + ); AztecAddress::from_field(address_point.x) } @@ -122,7 +124,7 @@ impl AztecAddress { contract_class_artifact_hash: Field, contract_class_public_bytecode_commitment: Field, salted_initialization_hash: SaltedInitializationHash, - public_keys: PublicKeys + public_keys: PublicKeys, ) -> Self { let private_functions_root = private_functions_root_from_siblings( function_selector, @@ -167,36 +169,39 @@ fn compute_address_from_partial_and_pub_keys() { inner: Point { x: 0x22f7fcddfa3ce3e8f0cc8e82d7b94cdd740afa3e77f8e4a63ea78a239432dcab, y: 0x0471657de2b6216ade6c506d28fbc22ba8b8ed95c871ad9f3e3984e90d9723a7, - is_infinite: false - } + is_infinite: false, + }, }, ivpk_m: IvpkM { inner: Point { x: 0x111223493147f6785514b1c195bb37a2589f22a6596d30bb2bb145fdc9ca8f1e, y: 0x273bbffd678edce8fe30e0deafc4f66d58357c06fd4a820285294b9746c3be95, - is_infinite: false - } + is_infinite: false, + }, }, ovpk_m: OvpkM { inner: Point { x: 0x09115c96e962322ffed6522f57194627136b8d03ac7469109707f5e44190c484, y: 0x0c49773308a13d740a7f0d4f0e6163b02c5a408b6f965856b6a491002d073d5b, - is_infinite: false - } + is_infinite: false, + }, }, tpk_m: TpkM { inner: Point { x: 0x00d3d81beb009873eb7116327cf47c612d5758ef083d4fda78e9b63980b2a762, y: 0x2f567d22d2b02fe1f4ad42db9d58a36afd1983e7e2909d1cab61cafedad6193a, - is_infinite: false - } - } + is_infinite: false, + }, + }, }; - let partial_address = PartialAddress::from_field(0x0a7c585381b10f4666044266a02405bf6e01fa564c8517d4ad5823493abd31de); + let partial_address = PartialAddress::from_field( + 0x0a7c585381b10f4666044266a02405bf6e01fa564c8517d4ad5823493abd31de, + ); let address = AztecAddress::compute(public_keys, partial_address); - let expected_computed_address_from_partial_and_pubkey = 0x24e4646f58b9fbe7d38e317db8d5636c423fbbdfbe119fc190fe9c64747e0c62; + let expected_computed_address_from_partial_and_pubkey = + 0x24e4646f58b9fbe7d38e317db8d5636c423fbbdfbe119fc190fe9c64747e0c62; assert(address.to_field() == expected_computed_address_from_partial_and_pubkey); } @@ -205,9 +210,10 @@ fn compute_preaddress_from_partial_and_pub_keys_hash() { let pub_keys_hash = PublicKeysHash::from_field(1); let partial_address = PartialAddress::from_field(2); - let address = AztecAddress::compute_preaddress(pub_keys_hash, partial_address); - let expected_computed_preaddress_from_partial_and_pubkey = 0x23ce9be3fa3c846b0f9245cc796902e731d04f086e8a42473bb29e405fc98075; - assert(address.to_field() == expected_computed_preaddress_from_partial_and_pubkey); + let preaddress = AztecAddress::compute_preaddress(pub_keys_hash, partial_address); + let expected_computed_preaddress_from_partial_and_pubkey = + 0x23ce9be3fa3c846b0f9245cc796902e731d04f086e8a42473bb29e405fc98075; + assert(preaddress == expected_computed_preaddress_from_partial_and_pubkey); } #[test] diff --git a/yarn-project/circuit-types/src/logs/l1_payload/encrypted_log_payload.test.ts b/yarn-project/circuit-types/src/logs/l1_payload/encrypted_log_payload.test.ts index 74785a323041..2003e253b2b3 100644 --- a/yarn-project/circuit-types/src/logs/l1_payload/encrypted_log_payload.test.ts +++ b/yarn-project/circuit-types/src/logs/l1_payload/encrypted_log_payload.test.ts @@ -9,7 +9,7 @@ import { derivePublicKeyFromSecretKey, } from '@aztec/circuits.js'; import { randomBytes } from '@aztec/foundation/crypto'; -import { Fr, GrumpkinScalar, Point } from '@aztec/foundation/fields'; +import { Fr, GrumpkinScalar } from '@aztec/foundation/fields'; import { updateInlineTestData } from '@aztec/foundation/testing'; import { EncryptedLogPayload } from './encrypted_log_payload.js'; @@ -41,12 +41,7 @@ describe('EncryptedLogPayload', () => { const ephSk = GrumpkinScalar.random(); - encrypted = original.encrypt( - ephSk, - completeAddress.address, - computePoint(completeAddress.address), - ovKeys, - ); + encrypted = original.encrypt(ephSk, completeAddress.address, computePoint(completeAddress.address), ovKeys); }); it('decrypt a log as incoming', () => { @@ -83,12 +78,7 @@ describe('EncryptedLogPayload', () => { ); const encrypted = log - .encrypt( - ephSk, - recipientCompleteAddress.address, - computePoint(recipientCompleteAddress.address), - ovKeys, - ) + .encrypt(ephSk, recipientCompleteAddress.address, computePoint(recipientCompleteAddress.address), ovKeys) .toString('hex'); expect(encrypted).toMatchInlineSnapshot( `"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008d460c0e434d846ec1ea286e4090eb56376ff27bddc1aacae1d856549f701fa70577790aeabcc2d81ec8d0c99e7f5d2bf2f1452025dc777a178404f851d93de818923f85187871d99bdf95d695eff0a9e09ba15153fc9b4d224b6e1e71dfbdcaab06c09d5b3c749bfebe1c0407eccd04f51bbb59142680c8a091b97fc6cbcf61f6c2af9b8ebc8f78537ab23fd0c5e818e4d42d459d265adb77c2ef829bf68f87f2c47b478bb57ae7e41a07643f65c353083d557b94e31da4a2a13127498d2eb3f0346da5eed2e9bc245aaf022a954ed0b09132b498f537702899b44e3666776238ebf633b3562d7f124dbba82918e871958a94218fd796bc6983feecc7ce382c82861d63fe45999244ea9494b226ddb694b2640dce005b473acf1ae3be158f558ad1ca228e9f793d09390230a2597e0e53ad28f7aa9a700ccc302607ad6c26ea1410735b6a8c793f6317f7009409a3912b15ee2f28ccf17cf6c94b720301e5c5826de39e85bc7db3dc33aa79afcaf325670d1b359d08b10bd07840d394c9f038"`, diff --git a/yarn-project/circuits.js/src/contract/__snapshots__/contract_address.test.ts.snap b/yarn-project/circuits.js/src/contract/__snapshots__/contract_address.test.ts.snap index 17a995704b80..4cdcd836059a 100644 --- a/yarn-project/circuits.js/src/contract/__snapshots__/contract_address.test.ts.snap +++ b/yarn-project/circuits.js/src/contract/__snapshots__/contract_address.test.ts.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`ContractAddress computeContractAddressFromInstance 1`] = `"0x0ea56faa48431d99cc2d073463d1b718c11174bb551a3d1d6f296b0096089dbb"`; +exports[`ContractAddress computeContractAddressFromInstance 1`] = `"0x226f73921336a2893fd3748d317de107ab1cdd8fa7d9ddc235e8a189236cd265"`; exports[`ContractAddress computeInitializationHash 1`] = `Fr<0x153329c6098512a53cb449057bb5adade93e360575b71192b7b304ace35b2b6b>`; diff --git a/yarn-project/circuits.js/src/keys/derivation.ts b/yarn-project/circuits.js/src/keys/derivation.ts index f76b82f84484..5b3ecde93fd6 100644 --- a/yarn-project/circuits.js/src/keys/derivation.ts +++ b/yarn-project/circuits.js/src/keys/derivation.ts @@ -71,7 +71,7 @@ export function computeAddressSecret(preaddress: Fr, ivsk: Fq) { // If our secret computes a point with a negative y-coordinate, we then negate the secret to produce the secret // that can decrypt payloads encrypted with the point having a positive y-coordinate. - if (!addressPointCandidate.y.lt(new Fr((Fr.MODULUS - 1n) / 2n))) { + if (!(addressPointCandidate.y.toBigInt() <= (Fr.MODULUS - 1n) / 2n)) { return new Fq(Fq.MODULUS - addressSecretCandidate.toBigInt()); } diff --git a/yarn-project/noir-protocol-circuits-types/src/__snapshots__/noir_test_gen.test.ts.snap b/yarn-project/noir-protocol-circuits-types/src/__snapshots__/noir_test_gen.test.ts.snap index 6ba80a51baaf..0ccb39080a94 100644 --- a/yarn-project/noir-protocol-circuits-types/src/__snapshots__/noir_test_gen.test.ts.snap +++ b/yarn-project/noir-protocol-circuits-types/src/__snapshots__/noir_test_gen.test.ts.snap @@ -6,7 +6,7 @@ exports[`Data generation for noir tests Computes contract info for defaultContra artifact_hash: 0x0000000000000000000000000000000000000000000000000000000000003039, public_bytecode_commitment: 0x0000000000000000000000000000000000000000000000000000000000000005, private_functions_root: 0x25d76df45434ec75a83321daf941cfc667ff3a9027942e17105da4f50d1d13f9, - address: AztecAddress { inner: 0x26a0c3dba9cc7290ebf910fe8a8ea00b99b51817ae686fdd7c35392860133410 }, + address: AztecAddress { inner: 0x07a6b468dff245b1d9482de2b5048383549766d3641760aed17727525cd9baa1 }, partial_address: PartialAddress { inner: 0x0cf203c94c91bed28440b00ecd888d88cce1f86ddf2aa8d33acbb9b6fc06d382 }, contract_class_id: ContractClassId { inner: 0x28e91aaf764bc6083e2796ff884079ad895d4b948d6ce8f37f01b29d0bc95a21 }, public_keys: PublicKeys { inner: 01498945581e0eb9f8427ad6021184c700ef091d570892c437d12c7d90364bbd170ae506787c5c43d6ca9255d571c10fa9ffa9d141666e290c347c5c9ab7e34400c044b05b6ca83b9c2dbae79cc1135155956a64e136819136e9947fe5e5866c1c1f0ca244c7cd46b682552bff8ae77dea40b966a71de076ec3b7678f2bdb1511b00316144359e9a3ec8e49c1cdb7eeb0cedd190dfd9dc90eea5115aa779e287080ffc74d7a8b0bccb88ac11f45874172f3847eb8b92654aaa58a3d2b8dc7833019c111f36ad3fc1d9b7a7a14344314d2864b94f030594cd67f753ef774a1efb2039907fe37f08d10739255141bb066c506a12f7d1e8dfec21abc58494705b6f }, @@ -21,7 +21,7 @@ exports[`Data generation for noir tests Computes contract info for parentContrac artifact_hash: 0x00000000000000000000000000000000000000000000000000000000000004bc, public_bytecode_commitment: 0x0000000000000000000000000000000000000000000000000000000000000005, private_functions_root: 0x1228b39ba6702af03e595300e8484c6373f00790d0148cc3d4ff0fd1c778a83a, - address: AztecAddress { inner: 0x00d81b0b87d6776f68155d9f2b92469fdcb461c1366b9635ab0030bbf6ec86cf }, + address: AztecAddress { inner: 0x030a8041016cd781040145827932d85ef3c9211204aafd6cf033f91295fc4946 }, partial_address: PartialAddress { inner: 0x245df9f519d616473880260dd64b19a838081bb44dc17cd6ea5d870a63d2bf57 }, contract_class_id: ContractClassId { inner: 0x00236b0dc6c537d5106543053c5b85c4cbe95b0474f8238b094bae63f1cbcfee }, public_keys: PublicKeys { inner: 01498945581e0eb9f8427ad6021184c700ef091d570892c437d12c7d90364bbd170ae506787c5c43d6ca9255d571c10fa9ffa9d141666e290c347c5c9ab7e34400c044b05b6ca83b9c2dbae79cc1135155956a64e136819136e9947fe5e5866c1c1f0ca244c7cd46b682552bff8ae77dea40b966a71de076ec3b7678f2bdb1511b00316144359e9a3ec8e49c1cdb7eeb0cedd190dfd9dc90eea5115aa779e287080ffc74d7a8b0bccb88ac11f45874172f3847eb8b92654aaa58a3d2b8dc7833019c111f36ad3fc1d9b7a7a14344314d2864b94f030594cd67f753ef774a1efb2039907fe37f08d10739255141bb066c506a12f7d1e8dfec21abc58494705b6f }, diff --git a/yarn-project/pxe/src/note_processor/note_processor.test.ts b/yarn-project/pxe/src/note_processor/note_processor.test.ts index 831957c109bb..8b231c67bf57 100644 --- a/yarn-project/pxe/src/note_processor/note_processor.test.ts +++ b/yarn-project/pxe/src/note_processor/note_processor.test.ts @@ -218,14 +218,7 @@ describe('Note Processor', () => { it('should store multiple notes that belong to us', async () => { const requests = [ - new MockNoteRequest( - getRandomNoteLogPayload(app), - 1, - 1, - 1, - computePoint(account.address), - ownerOvKeys, - ), + new MockNoteRequest(getRandomNoteLogPayload(app), 1, 1, 1, computePoint(account.address), ownerOvKeys), new MockNoteRequest(getRandomNoteLogPayload(app), 2, 3, 0, Point.random(), ownerOvKeys), new MockNoteRequest( getRandomNoteLogPayload(app), @@ -236,14 +229,7 @@ describe('Note Processor', () => { KeyValidationRequest.random(), ), new MockNoteRequest(getRandomNoteLogPayload(app), 9, 3, 2, Point.random(), KeyValidationRequest.random()), - new MockNoteRequest( - getRandomNoteLogPayload(app), - 12, - 3, - 2, - computePoint(account.address), - ownerOvKeys, - ), + new MockNoteRequest(getRandomNoteLogPayload(app), 12, 3, 2, computePoint(account.address), ownerOvKeys), ]; const blocks = mockBlocks(requests); From 7c70f22e25bc6ba2abf22e462d18ad2d6ce98df9 Mon Sep 17 00:00:00 2001 From: sklppy88 Date: Thu, 24 Oct 2024 14:28:35 +0000 Subject: [PATCH 3/5] addressing comments --- .../circuits.js/src/keys/derivation.ts | 17 ++++++++++++-- .../src/structs/complete_address.ts | 22 ++++++------------- .../src/protocol_contract_data.ts | 14 ++++++------ .../pxe/src/pxe_service/pxe_service.ts | 2 +- 4 files changed, 30 insertions(+), 25 deletions(-) diff --git a/yarn-project/circuits.js/src/keys/derivation.ts b/yarn-project/circuits.js/src/keys/derivation.ts index 5b3ecde93fd6..e7126f89411f 100644 --- a/yarn-project/circuits.js/src/keys/derivation.ts +++ b/yarn-project/circuits.js/src/keys/derivation.ts @@ -59,18 +59,31 @@ export function computeAddressFromPreaddressAndIvpkM(preaddress: Fr, ivpkM: Poin } export function computeAddressPointFromPreaddressAndIvpkM(preaddress: Fr, ivpkM: Point) { + // Given h, our preaddress, and our ivpk_m, we can derive our address point. + // All we need to do is: + // First, take our preaddress, and multiply it by the generator G const preaddressPoint = derivePublicKeyFromSecretKey(new Fq(preaddress.toBigInt())); + // Then we add our ivpk_m to it. Tada ! const addressPoint = new Grumpkin().add(preaddressPoint, ivpkM); return addressPoint; } export function computeAddressSecret(preaddress: Fr, ivsk: Fq) { + // TLDR; (h + ivsk) * G = P1 + // if P1.y is pos + // S = (h + ivsk) + // else + // S = Fq.MODULUS - (h + ivsk) + // + // Given h, our preaddress, and our ivsk, we have two different addressSecret candidates. One encodes to a point with a positive y-coordinate + // and the other encodes to a point with a negative y-coordinate. We take the addressSecret candidate that is a simple addition of the two Scalars. const addressSecretCandidate = ivsk.add(new Fq(preaddress.toBigInt())); + // We then multiply this secretCandidate by the generator G to create an addressPoint candidate. const addressPointCandidate = derivePublicKeyFromSecretKey(addressSecretCandidate); - // If our secret computes a point with a negative y-coordinate, we then negate the secret to produce the secret - // that can decrypt payloads encrypted with the point having a positive y-coordinate. + // Because all encryption to addresses is done using a point with the positive y-coordinate, if our addressSecret candidate derives a point with a + // negative y-coordinate, we use the other candidate by negating the secret. This transformation of the secret simply flips the y-coordinate of the derived point while keeping the x-coordinate the same. if (!(addressPointCandidate.y.toBigInt() <= (Fr.MODULUS - 1n) / 2n)) { return new Fq(Fq.MODULUS - addressSecretCandidate.toBigInt()); } diff --git a/yarn-project/circuits.js/src/structs/complete_address.ts b/yarn-project/circuits.js/src/structs/complete_address.ts index 28bb9dd24e08..68140d7d02bc 100644 --- a/yarn-project/circuits.js/src/structs/complete_address.ts +++ b/yarn-project/circuits.js/src/structs/complete_address.ts @@ -1,10 +1,9 @@ import { AztecAddress } from '@aztec/foundation/aztec-address'; -import { Fq, Fr } from '@aztec/foundation/fields'; +import { Fr } from '@aztec/foundation/fields'; import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize'; -import { Grumpkin } from '../barretenberg/index.js'; import { computePartialAddress } from '../contract/contract_address.js'; -import { computePreaddress, deriveKeys, derivePublicKeyFromSecretKey } from '../keys/index.js'; +import { computeAddress, computePreaddress, deriveKeys } from '../keys/index.js'; import { type PartialAddress } from '../types/partial_address.js'; import { PublicKeys } from '../types/public_keys.js'; @@ -36,14 +35,10 @@ export class CompleteAddress { } static fromSecretKeyAndPartialAddress(secretKey: Fr, partialAddress: Fr): CompleteAddress { - const { publicKeys, masterIncomingViewingSecretKey } = deriveKeys(secretKey); - const preaddress = computePreaddress(publicKeys.hash(), partialAddress); + const { publicKeys } = deriveKeys(secretKey); + const address = computeAddress(publicKeys, partialAddress); - const addressSecret = masterIncomingViewingSecretKey.add(new Fq(preaddress.toBigInt())); - - const addressPoint = derivePublicKeyFromSecretKey(addressSecret); - - return new CompleteAddress(AztecAddress.fromField(addressPoint.x), publicKeys, partialAddress); + return new CompleteAddress(address, publicKeys, partialAddress); } getPreaddress() { @@ -60,12 +55,9 @@ export class CompleteAddress { /** Throws if the address is not correctly derived from the public key and partial address.*/ public validate() { - const expectedPreAddress = computePreaddress(this.publicKeys.hash(), this.partialAddress); - const expectedPreAddressPoint = derivePublicKeyFromSecretKey(new Fq(expectedPreAddress.toBigInt())); - - const expectedAddress = new Grumpkin().add(expectedPreAddressPoint, this.publicKeys.masterIncomingViewingPublicKey); + const expectedAddress = computeAddress(this.publicKeys, this.partialAddress); - if (!AztecAddress.fromField(expectedAddress.x).equals(this.address)) { + if (!AztecAddress.fromField(expectedAddress).equals(this.address)) { throw new Error( `Address cannot be derived from public keys and partial address (received ${this.address.toString()}, derived ${expectedAddress.toString()})`, ); diff --git a/yarn-project/protocol-contracts/src/protocol_contract_data.ts b/yarn-project/protocol-contracts/src/protocol_contract_data.ts index 48ce95ad950c..4096a51b1659 100644 --- a/yarn-project/protocol-contracts/src/protocol_contract_data.ts +++ b/yarn-project/protocol-contracts/src/protocol_contract_data.ts @@ -50,14 +50,14 @@ export const ProtocolContractAddress: Record }; export const ProtocolContractLeaf = { - AuthRegistry: Fr.fromString('0x087d102766af335cf7654eb5d946dccf114d0eb1e86dc788cbc10d87e1c84fd0'), - ContractInstanceDeployer: Fr.fromString('0x10141a7093a1050d38fc0467c6c81ca7beb158ef47095145ce4edd52be7ef661'), - ContractClassRegisterer: Fr.fromString('0x074de3b4ee658fd695a1be6c4f9f10fe3042b948955436e0e20c31c7027cfaef'), - MultiCallEntrypoint: Fr.fromString('0x19bc0ab26e84d8b2551cb3c83000a5da0f6958056fcaa91f63c14c35e266ef0d'), - FeeJuice: Fr.fromString('0x033a034d778d077970ae6108253a4971e37af6bd163d80b3be184c5838045eda'), - Router: Fr.fromString('0x24eed1db8f33a2db1611412c16157a28a57c7bef4794844c4ef228d538438ac8'), + AuthRegistry: Fr.fromString('0x25287d474d84b6ecb5d4f5e90b8efe5969a35ddb9d605077e5df17dac0f2aa58'), + ContractInstanceDeployer: Fr.fromString('0x1b92794557c9c6694ede81d4ea8909f786fe37ec51018d673f9ed6d8df09fcb2'), + ContractClassRegisterer: Fr.fromString('0x034af167c41eeb46cb695f9fc56824b3339b23aa670ebfef7bf5d3c8dd5c13d4'), + MultiCallEntrypoint: Fr.fromString('0x009d1fc8ca80534c2de293ce6eedc71cc145e0562fd1af0826c734c77b1543a5'), + FeeJuice: Fr.fromString('0x07c4d7db5027bcdb7b71a60186a5e137d22bd8412d11fee0676d070c68d0f7ee'), + Router: Fr.fromString('0x12df324157fddc5be72e52e527416e3f7c70240deab1c953613d904232e1eb78'), }; export const protocolContractTreeRoot = Fr.fromString( - '0x1299197d756a2e86830d877cb2c5c71ab20aa4f97726fc7ccd2d468ce20d81a6', + '0x04180f14bbf7d65ec020746102a118a58c4aa4016cdd85a41614fc4d972607cf', ); diff --git a/yarn-project/pxe/src/pxe_service/pxe_service.ts b/yarn-project/pxe/src/pxe_service/pxe_service.ts index b598e7f5464a..3fdc1843ae85 100644 --- a/yarn-project/pxe/src/pxe_service/pxe_service.ts +++ b/yarn-project/pxe/src/pxe_service/pxe_service.ts @@ -859,7 +859,7 @@ export class PXEService implements PXE { from: number, limit: number, eventMetadata: EventMetadata, - // TODO: Fix this by allow it to pass only an address + // TODO (#9272): Make this better, we should be able to only pass an address now vpks: Point[], ): Promise { if (vpks.length === 0) { From 9afe19972c6e5019e5c02320ee0a9ead492a9db3 Mon Sep 17 00:00:00 2001 From: sklppy88 Date: Thu, 24 Oct 2024 18:56:52 +0000 Subject: [PATCH 4/5] addressing more comments --- .../aztec-nr/aztec/src/keys/getters/test.nr | 43 +++++++++++--- .../crates/types/src/address/aztec_address.nr | 57 ++++++------------- .../wallet/account_wallet_with_private_key.ts | 12 +++- .../src/contract/contract_address.ts | 2 +- .../__snapshots__/derivation.test.ts.snap | 3 +- .../circuits.js/src/keys/derivation.ts | 34 ++++------- .../end-to-end/src/e2e_block_building.test.ts | 14 +---- .../end-to-end/src/e2e_event_logs.test.ts | 17 +----- yarn-project/ethereum/tsconfig.json | 4 +- 9 files changed, 84 insertions(+), 102 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/keys/getters/test.nr b/noir-projects/aztec-nr/aztec/src/keys/getters/test.nr index d90a857378a3..565ca62b47e4 100644 --- a/noir-projects/aztec-nr/aztec/src/keys/getters/test.nr +++ b/noir-projects/aztec-nr/aztec/src/keys/getters/test.nr @@ -18,16 +18,43 @@ unconstrained fn test_get_public_keys_unknown() { // Instead of querying for some unknown account, which would result in the oracle erroring out, we mock a bad oracle // response to check that the circuit properly checks the address derivation. - let original_keys = get_public_keys(account.address).serialize(); - - // We fill in all the keys but we leave partial address = 0 - let mut bad_response = [0; KEY_ORACLE_RESPONSE_LENGTH]; - for i in 0..12 { - bad_response[i] = original_keys[i]; - } + let mut random_keys_and_partial_address = [0; KEY_ORACLE_RESPONSE_LENGTH]; + // We use randomly generated points on the curve, and a random partial address to ensure that + // this combination does not derive the address and we should see the assertion fail. + // npk_m + random_keys_and_partial_address[0] = + 0x292364b852c6c6f01472951e76a39cbcf074591fd0e063a81965e7b51ad868a5; + random_keys_and_partial_address[1] = + 0x0a687b46cdc9238f1c311f126aaaa4acbd7a737bff2efd7aeabdb8d805843a27; + random_keys_and_partial_address[2] = + 0x0000000000000000000000000000000000000000000000000000000000000000; + // ivpk_m + random_keys_and_partial_address[3] = + 0x173c5229a00c5425255680dd6edc27e278c48883991f348fe6985de43b4ec25f; + random_keys_and_partial_address[4] = + 0x1698608e23b5f6c2f43c49a559108bb64e2247b8fc2da842296a416817f40b7f; + random_keys_and_partial_address[5] = + 0x0000000000000000000000000000000000000000000000000000000000000000; + // ovpk_m + random_keys_and_partial_address[6] = + 0x1bad2f7d1ad960a1bd0fe4d2c8d17f5ab4a86ef8b103e0a9e7f67ec0d3b4795e; + random_keys_and_partial_address[7] = + 0x206db87110abbecc9fbaef2c865189d94ef2c106202f734ee4eba9257fd28bf1; + random_keys_and_partial_address[8] = + 0x0000000000000000000000000000000000000000000000000000000000000000; + // tpk_m + random_keys_and_partial_address[9] = + 0x05e3bd9cfe6b47daa139613619cf7d7fd8bb0112b6f2908caa6d9b536ed948ed; + random_keys_and_partial_address[10] = + 0x051066f877c9df47552d02e7dc32127ff4edefc8498e813bca1cbd3f5d1be429; + random_keys_and_partial_address[11] = + 0x0000000000000000000000000000000000000000000000000000000000000000; + // partial address + random_keys_and_partial_address[12] = + 0x236703e2cb00a182e024e98e9f759231b556d25ff19f98896cebb69e9e678cc9; let _ = OracleMock::mock("getPublicKeysAndPartialAddress") - .returns(bad_response.serialize()) + .returns(random_keys_and_partial_address.serialize()) .times(1); let _ = get_public_keys(account.address); } diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/address/aztec_address.nr b/noir-projects/noir-protocol-circuits/crates/types/src/address/aztec_address.nr index 6edbd0f973cb..9a1d37edbe2e 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/address/aztec_address.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/address/aztec_address.nr @@ -7,16 +7,13 @@ use crate::{ }, constants::{ AZTEC_ADDRESS_LENGTH, FUNCTION_TREE_HEIGHT, GENERATOR_INDEX__PUBLIC_KEYS_HASH, - GENERATOR_INDEX__CONTRACT_ADDRESS_V1, + GENERATOR_INDEX__CONTRACT_ADDRESS_V1, MAX_FIELD_VALUE, }, contract_class_id::ContractClassId, hash::{poseidon2_hash_with_separator, private_functions_root_from_siblings}, merkle_tree::membership::MembershipWitness, traits::{Empty, FromField, ToField, Serialize, Deserialize}, utils, }; -global BN254_FR_MODULUS_DIV_2: Field = - 10944121435919637611123202872628637544274182200208017171849102093287904247808; - // We do below because `use crate::point::Point;` does not work use dep::std::embedded_curve_ops::EmbeddedCurvePoint as Point; @@ -68,20 +65,20 @@ impl Deserialize for AztecAddress { impl ToPoint for AztecAddress { fn to_point(self) -> Point { - // Calculate y^2 = x^3 - 17 - let y_squared = pow(self.inner, 3) - 17; - - // TODO (#8970): Failure validation - // We can see if y is square first, or we can soft fail with just sqrt(y_squared); - // If y is not square, the x-coordinate is not on the curve - // Do we throw here or can we soft continue and this is fine ? Test this with points not on curve to see what happens. - // let y_is_square = is_square(y_squared); - // assert(y_is_square); + // We compute the address point by taking our address, setting it to x, and then solving for y in the + // equation which defines our bn curve: + // y^2 = x^3 - 17; x = address + let x = self.inner; + let y_squared = pow(x, 3) - 17; + + // TODO (#8970): Handle cases where we cannot recover a point from an address let mut y = sqrt(y_squared); - // If we get a negative y coordinate, we pin it to the positive one by subtracting it from the Field modulus - if (!(y.lt(BN254_FR_MODULUS_DIV_2) | y.eq(BN254_FR_MODULUS_DIV_2))) { - y = (BN254_FR_MODULUS_DIV_2 + BN254_FR_MODULUS_DIV_2 + 1) - y; + // If we get a negative y coordinate (any y where y > MAX_FIELD_VALUE / 2), we pin it to the + // positive one (any value where y <= MAX_FIELD_VALUE / 2) by subtracting it from the Field modulus + // note: The field modulus is MAX_FIELD_VALUE + 1 + if (!(y.lt(MAX_FIELD_VALUE / 2) | y.eq(MAX_FIELD_VALUE / 2))) { + y = (MAX_FIELD_VALUE + 1) - y; } Point { x: self.inner, y, is_infinite: false } @@ -93,16 +90,6 @@ impl AztecAddress { Self { inner: 0 } } - pub fn compute_preaddress( - pub_keys_hash: PublicKeysHash, - partial_address: PartialAddress, - ) -> Field { - poseidon2_hash_with_separator( - [pub_keys_hash.to_field(), partial_address.to_field()], - GENERATOR_INDEX__CONTRACT_ADDRESS_V1, - ) - } - pub fn compute(public_keys: PublicKeys, partial_address: PartialAddress) -> AztecAddress { let public_keys_hash = public_keys.hash(); @@ -200,20 +187,12 @@ fn compute_address_from_partial_and_pub_keys() { ); let address = AztecAddress::compute(public_keys, partial_address); - let expected_computed_address_from_partial_and_pubkey = - 0x24e4646f58b9fbe7d38e317db8d5636c423fbbdfbe119fc190fe9c64747e0c62; - assert(address.to_field() == expected_computed_address_from_partial_and_pubkey); -} -#[test] -fn compute_preaddress_from_partial_and_pub_keys_hash() { - let pub_keys_hash = PublicKeysHash::from_field(1); - let partial_address = PartialAddress::from_field(2); - - let preaddress = AztecAddress::compute_preaddress(pub_keys_hash, partial_address); - let expected_computed_preaddress_from_partial_and_pubkey = - 0x23ce9be3fa3c846b0f9245cc796902e731d04f086e8a42473bb29e405fc98075; - assert(preaddress == expected_computed_preaddress_from_partial_and_pubkey); + // The following value was generated by `derivation.test.ts`. + // --> Run the test with AZTEC_GENERATE_TEST_DATA=1 flag to update test data. + let expected_computed_address_from_partial_and_pubkeys = + 0x24e4646f58b9fbe7d38e317db8d5636c423fbbdfbe119fc190fe9c64747e0c62; + assert(address.to_field() == expected_computed_address_from_partial_and_pubkeys); } #[test] diff --git a/yarn-project/aztec.js/src/wallet/account_wallet_with_private_key.ts b/yarn-project/aztec.js/src/wallet/account_wallet_with_private_key.ts index 792b44e60d94..ec1c387c7f9f 100644 --- a/yarn-project/aztec.js/src/wallet/account_wallet_with_private_key.ts +++ b/yarn-project/aztec.js/src/wallet/account_wallet_with_private_key.ts @@ -1,5 +1,5 @@ import { type PXE } from '@aztec/circuit-types'; -import { type Fr } from '@aztec/circuits.js'; +import { type Fr, computeAddressSecret, deriveMasterIncomingViewingSecretKey } from '@aztec/circuits.js'; import { type Salt } from '../account/index.js'; import { type AccountInterface } from '../account/interface.js'; @@ -25,4 +25,14 @@ export class AccountWalletWithSecretKey extends AccountWallet { public getSecretKey() { return this.secretKey; } + + /** Returns the address secret, the secret of the address pointโ€”the point that others use to encrypt messages to this account + * note - this ensures that the address secret always corresponds to an address point with y being positive + */ + public getAddressSecret() { + return computeAddressSecret( + this.getCompleteAddress().getPreaddress(), + deriveMasterIncomingViewingSecretKey(this.getSecretKey()), + ); + } } diff --git a/yarn-project/circuits.js/src/contract/contract_address.ts b/yarn-project/circuits.js/src/contract/contract_address.ts index a250fecf39b0..25238a1d381e 100644 --- a/yarn-project/circuits.js/src/contract/contract_address.ts +++ b/yarn-project/circuits.js/src/contract/contract_address.ts @@ -15,7 +15,7 @@ import { type ContractInstance } from './interfaces/contract_instance.js'; * ``` * salted_initialization_hash = pedersen([salt, initialization_hash, deployer], GENERATOR__SALTED_INITIALIZATION_HASH) * partial_address = pedersen([contract_class_id, salted_initialization_hash], GENERATOR__CONTRACT_PARTIAL_ADDRESS_V1) - * address = (poseidon2Hash([public_keys_hash, partial_address, GENERATOR__CONTRACT_ADDRESS_V1]) * G) + ivpk_m + * address = ((poseidon2Hash([public_keys_hash, partial_address, GENERATOR__CONTRACT_ADDRESS_V1]) * G) + ivpk_m).x <- the x-coordinate of the address point * ``` * @param instance - A contract instance for which to calculate the deployment address. */ diff --git a/yarn-project/circuits.js/src/keys/__snapshots__/derivation.test.ts.snap b/yarn-project/circuits.js/src/keys/__snapshots__/derivation.test.ts.snap index ccf574c1a37e..8e7a5e4ad29c 100644 --- a/yarn-project/circuits.js/src/keys/__snapshots__/derivation.test.ts.snap +++ b/yarn-project/circuits.js/src/keys/__snapshots__/derivation.test.ts.snap @@ -1,4 +1,5 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`๐Ÿ”‘ Pre address from partial matches Noir 1`] = `"0x23ce9be3fa3c846b0f9245cc796902e731d04f086e8a42473bb29e405fc98075"`; exports[`๐Ÿ”‘ Address matches Noir 1`] = `"0x24e4646f58b9fbe7d38e317db8d5636c423fbbdfbe119fc190fe9c64747e0c62"`; + +exports[`๐Ÿ”‘ Pre address from partial matches Noir 1`] = `"0x23ce9be3fa3c846b0f9245cc796902e731d04f086e8a42473bb29e405fc98075"`; diff --git a/yarn-project/circuits.js/src/keys/derivation.ts b/yarn-project/circuits.js/src/keys/derivation.ts index e7126f89411f..1caff8bff86d 100644 --- a/yarn-project/circuits.js/src/keys/derivation.ts +++ b/yarn-project/circuits.js/src/keys/derivation.ts @@ -1,4 +1,4 @@ -import { AztecAddress } from '@aztec/foundation/aztec-address'; +import { type AztecAddress } from '@aztec/foundation/aztec-address'; import { poseidon2HashWithSeparator, sha512ToGrumpkinScalar } from '@aztec/foundation/crypto'; import { Fq, Fr, GrumpkinScalar, Point } from '@aztec/foundation/fields'; @@ -46,37 +46,27 @@ export function computePreaddress(publicKeysHash: Fr, partialAddress: Fr) { } export function computeAddress(publicKeys: PublicKeys, partialAddress: Fr) { + // Given public keys and a partial address, we can compute our address in the following steps. + // 1. preaddress = poseidon2([publicKeysHash, partialAddress], GeneratorIndex.CONTRACT_ADDRESS_V1); + // 2. addressPoint = (preaddress * G) + ivpk_m + // 3. address = addressPoint.x const preaddress = computePreaddress(publicKeys.hash(), partialAddress); - const address = computeAddressFromPreaddressAndIvpkM(preaddress, publicKeys.masterIncomingViewingPublicKey); - - return address; -} - -export function computeAddressFromPreaddressAndIvpkM(preaddress: Fr, ivpkM: Point) { - const addressPoint = computeAddressPointFromPreaddressAndIvpkM(preaddress, ivpkM); - - return AztecAddress.fromField(addressPoint.x); -} - -export function computeAddressPointFromPreaddressAndIvpkM(preaddress: Fr, ivpkM: Point) { - // Given h, our preaddress, and our ivpk_m, we can derive our address point. - // All we need to do is: - // First, take our preaddress, and multiply it by the generator G - const preaddressPoint = derivePublicKeyFromSecretKey(new Fq(preaddress.toBigInt())); - // Then we add our ivpk_m to it. Tada ! - const addressPoint = new Grumpkin().add(preaddressPoint, ivpkM); + const address = new Grumpkin().add( + derivePublicKeyFromSecretKey(new Fq(preaddress.toBigInt())), + publicKeys.masterIncomingViewingPublicKey, + ); - return addressPoint; + return address.x; } export function computeAddressSecret(preaddress: Fr, ivsk: Fq) { - // TLDR; (h + ivsk) * G = P1 + // TLDR; P1 = (h + ivsk) * G // if P1.y is pos // S = (h + ivsk) // else // S = Fq.MODULUS - (h + ivsk) // - // Given h, our preaddress, and our ivsk, we have two different addressSecret candidates. One encodes to a point with a positive y-coordinate + // Given h (our preaddress) and our ivsk, we have two different addressSecret candidates. One encodes to a point with a positive y-coordinate // and the other encodes to a point with a negative y-coordinate. We take the addressSecret candidate that is a simple addition of the two Scalars. const addressSecretCandidate = ivsk.add(new Fq(preaddress.toBigInt())); // We then multiply this secretCandidate by the generator G to create an addressPoint candidate. diff --git a/yarn-project/end-to-end/src/e2e_block_building.test.ts b/yarn-project/end-to-end/src/e2e_block_building.test.ts index 4760f4492a2a..eb09f286842d 100644 --- a/yarn-project/end-to-end/src/e2e_block_building.test.ts +++ b/yarn-project/end-to-end/src/e2e_block_building.test.ts @@ -16,11 +16,7 @@ import { retryUntil, sleep, } from '@aztec/aztec.js'; -import { - AZTEC_EPOCH_PROOF_CLAIM_WINDOW_IN_L2_SLOTS, - computeAddressSecret, - deriveMasterIncomingViewingSecretKey, -} from '@aztec/circuits.js'; +import { AZTEC_EPOCH_PROOF_CLAIM_WINDOW_IN_L2_SLOTS } from '@aztec/circuits.js'; import { times } from '@aztec/foundation/collection'; import { poseidon2HashWithSeparator } from '@aztec/foundation/crypto'; import { StatefulTestContract, StatefulTestContractArtifact } from '@aztec/noir-contracts.js'; @@ -303,13 +299,7 @@ describe('e2e_block_building', () => { // compare logs expect(rct.status).toEqual('success'); const noteValues = tx.noteEncryptedLogs.unrollLogs().map(l => { - const notePayload = L1NotePayload.decryptAsIncoming( - l, - computeAddressSecret( - thisWallet.getCompleteAddress().getPreaddress(), - deriveMasterIncomingViewingSecretKey(thisWallet.getSecretKey()), - ), - ); + const notePayload = L1NotePayload.decryptAsIncoming(l, thisWallet.getAddressSecret()); return notePayload?.note.items[0]; }); expect(noteValues[0]).toEqual(new Fr(10)); diff --git a/yarn-project/end-to-end/src/e2e_event_logs.test.ts b/yarn-project/end-to-end/src/e2e_event_logs.test.ts index d800610e513a..0b8738ab66e9 100644 --- a/yarn-project/end-to-end/src/e2e_event_logs.test.ts +++ b/yarn-project/end-to-end/src/e2e_event_logs.test.ts @@ -6,7 +6,6 @@ import { L1EventPayload, type PXE, } from '@aztec/aztec.js'; -import { computeAddressSecret, deriveMasterIncomingViewingSecretKey } from '@aztec/circuits.js'; import { EventSelector } from '@aztec/foundation/abi'; import { makeTuple } from '@aztec/foundation/array'; import { type Tuple } from '@aztec/foundation/serialize'; @@ -55,13 +54,7 @@ describe('Logs', () => { const encryptedLogs = txEffect!.encryptedLogs.unrollLogs(); expect(encryptedLogs.length).toBe(3); - const decryptedEvent0 = L1EventPayload.decryptAsIncoming( - encryptedLogs[0], - computeAddressSecret( - wallets[0].getCompleteAddress().getPreaddress(), - deriveMasterIncomingViewingSecretKey(wallets[0].getSecretKey()), - ), - )!; + const decryptedEvent0 = L1EventPayload.decryptAsIncoming(encryptedLogs[0], wallets[0].getAddressSecret())!; expect(decryptedEvent0.contractAddress).toStrictEqual(testLogContract.address); expect(decryptedEvent0.randomness).toStrictEqual(randomness[0]); @@ -78,13 +71,7 @@ describe('Logs', () => { const badEvent0 = TestLogContract.events.ExampleEvent1.decode(decryptedEvent0); expect(badEvent0).toBe(undefined); - const decryptedEvent1 = L1EventPayload.decryptAsIncoming( - encryptedLogs[2], - computeAddressSecret( - wallets[0].getCompleteAddress().getPreaddress(), - deriveMasterIncomingViewingSecretKey(wallets[0].getSecretKey()), - ), - )!; + const decryptedEvent1 = L1EventPayload.decryptAsIncoming(encryptedLogs[2], wallets[0].getAddressSecret())!; expect(decryptedEvent1.contractAddress).toStrictEqual(testLogContract.address); expect(decryptedEvent1.randomness).toStrictEqual(randomness[1]); diff --git a/yarn-project/ethereum/tsconfig.json b/yarn-project/ethereum/tsconfig.json index fe5dd1c13edd..b12e902b9463 100644 --- a/yarn-project/ethereum/tsconfig.json +++ b/yarn-project/ethereum/tsconfig.json @@ -5,9 +5,7 @@ "rootDir": "src", "tsBuildInfoFile": ".tsbuildinfo" }, - "include": [ - "src" - ], + "include": ["src"], "references": [ { "path": "../foundation" From cdd161b7ec670f3d5abd7dfda44bfdc7df53b7e2 Mon Sep 17 00:00:00 2001 From: sklppy88 Date: Fri, 25 Oct 2024 04:54:49 +0000 Subject: [PATCH 5/5] addressing more more feedback --- .../crates/types/src/address/aztec_address.nr | 8 ++++++++ .../src/wallet/account_wallet_with_private_key.ts | 5 +++-- yarn-project/end-to-end/src/e2e_block_building.test.ts | 2 +- yarn-project/end-to-end/src/e2e_event_logs.test.ts | 4 ++-- 4 files changed, 14 insertions(+), 5 deletions(-) diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/address/aztec_address.nr b/noir-projects/noir-protocol-circuits/crates/types/src/address/aztec_address.nr index 9a1d37edbe2e..073551520690 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/address/aztec_address.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/address/aztec_address.nr @@ -101,6 +101,14 @@ impl AztecAddress { let address_point = derive_public_key(EmbeddedCurveScalar::from_field(pre_address)).add( public_keys.ivpk_m.to_point(), ); + + // Note that our address is only the x-coordinate of the full address_point. This is okay because when people want to encrypt something and send it to us + // they can recover our full point using the x-coordinate (our address itself). To do this, they recompute the y-coordinate according to the equation y^2 = x^3 - 17. + // When they do this, they may get a positive y-coordinate (a value that is less than or equal to MAX_FIELD_VALUE / 2) or + // a negative y-coordinate (a value that is more than MAX_FIELD_VALUE), and we cannot dictate which one they get and hence the recovered point may sometimes be different than the one + // our secrect can decrypt. Regardless though, they should and will always encrypt using point with the positive y-coordinate by convention. + // This ensures that everyone encrypts to the same point given an arbitrary x-coordinate (address). This is allowed because even though our original point may not have a positive y-coordinate, + // with our original secret, we will be able to derive the secret to the point with the flipped (and now positive) y-coordinate that everyone encrypts to. AztecAddress::from_field(address_point.x) } diff --git a/yarn-project/aztec.js/src/wallet/account_wallet_with_private_key.ts b/yarn-project/aztec.js/src/wallet/account_wallet_with_private_key.ts index ec1c387c7f9f..135d06ce339e 100644 --- a/yarn-project/aztec.js/src/wallet/account_wallet_with_private_key.ts +++ b/yarn-project/aztec.js/src/wallet/account_wallet_with_private_key.ts @@ -26,10 +26,11 @@ export class AccountWalletWithSecretKey extends AccountWallet { return this.secretKey; } - /** Returns the address secret, the secret of the address pointโ€”the point that others use to encrypt messages to this account + /** Returns the encryption secret, the secret of the encryption pointโ€”the point that others use to encrypt messages to this account * note - this ensures that the address secret always corresponds to an address point with y being positive + * dev - this is also referred to as the address secret, which decrypts payloads encrypted to an address point */ - public getAddressSecret() { + public getEncryptionSecret() { return computeAddressSecret( this.getCompleteAddress().getPreaddress(), deriveMasterIncomingViewingSecretKey(this.getSecretKey()), diff --git a/yarn-project/end-to-end/src/e2e_block_building.test.ts b/yarn-project/end-to-end/src/e2e_block_building.test.ts index eb09f286842d..d93f42e3ea08 100644 --- a/yarn-project/end-to-end/src/e2e_block_building.test.ts +++ b/yarn-project/end-to-end/src/e2e_block_building.test.ts @@ -299,7 +299,7 @@ describe('e2e_block_building', () => { // compare logs expect(rct.status).toEqual('success'); const noteValues = tx.noteEncryptedLogs.unrollLogs().map(l => { - const notePayload = L1NotePayload.decryptAsIncoming(l, thisWallet.getAddressSecret()); + const notePayload = L1NotePayload.decryptAsIncoming(l, thisWallet.getEncryptionSecret()); return notePayload?.note.items[0]; }); expect(noteValues[0]).toEqual(new Fr(10)); diff --git a/yarn-project/end-to-end/src/e2e_event_logs.test.ts b/yarn-project/end-to-end/src/e2e_event_logs.test.ts index 0b8738ab66e9..e508c603affd 100644 --- a/yarn-project/end-to-end/src/e2e_event_logs.test.ts +++ b/yarn-project/end-to-end/src/e2e_event_logs.test.ts @@ -54,7 +54,7 @@ describe('Logs', () => { const encryptedLogs = txEffect!.encryptedLogs.unrollLogs(); expect(encryptedLogs.length).toBe(3); - const decryptedEvent0 = L1EventPayload.decryptAsIncoming(encryptedLogs[0], wallets[0].getAddressSecret())!; + const decryptedEvent0 = L1EventPayload.decryptAsIncoming(encryptedLogs[0], wallets[0].getEncryptionSecret())!; expect(decryptedEvent0.contractAddress).toStrictEqual(testLogContract.address); expect(decryptedEvent0.randomness).toStrictEqual(randomness[0]); @@ -71,7 +71,7 @@ describe('Logs', () => { const badEvent0 = TestLogContract.events.ExampleEvent1.decode(decryptedEvent0); expect(badEvent0).toBe(undefined); - const decryptedEvent1 = L1EventPayload.decryptAsIncoming(encryptedLogs[2], wallets[0].getAddressSecret())!; + const decryptedEvent1 = L1EventPayload.decryptAsIncoming(encryptedLogs[2], wallets[0].getEncryptionSecret())!; expect(decryptedEvent1.contractAddress).toStrictEqual(testLogContract.address); expect(decryptedEvent1.randomness).toStrictEqual(randomness[1]);