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 a34e62a1e8cc..47fc4ec847e5 100644 --- a/noir-projects/aztec-nr/aztec/src/encrypted_logs/payload.nr +++ b/noir-projects/aztec-nr/aztec/src/encrypted_logs/payload.nr @@ -24,7 +24,7 @@ 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); diff --git a/noir-projects/aztec-nr/aztec/src/utils/point.nr b/noir-projects/aztec-nr/aztec/src/utils/point.nr index 40b555c5655c..75a3f07179d2 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-protocol-circuits/crates/types/src/address/aztec_address.nr b/noir-projects/noir-protocol-circuits/crates/types/src/address/aztec_address.nr index 4fe04afcb268..ab1bf6c2d929 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 @@ -4,13 +4,25 @@ use crate::{ partial_address::PartialAddress, public_keys_hash::PublicKeysHash, salted_initialization_hash::SaltedInitializationHash }, - constants::{AZTEC_ADDRESS_LENGTH, FUNCTION_TREE_HEIGHT, GENERATOR_INDEX__CONTRACT_ADDRESS_V1}, + 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} +}; +use crate::public_keys::ToPoint; +use crate::public_keys::PublicKeys; + // Aztec address pub struct AztecAddress { inner : Field @@ -52,6 +64,31 @@ impl Deserialize for AztecAddress { } } +impl ToPoint for AztecAddress { + pub fn to_point(self) -> Point { + // Calculate y^2 = x^3 - 17 + let y_squared = pow(self.inner, 3) - 17; + + // 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 soft continue ? + // let y_is_square = is_square(y_squared); + // assert(y_is_square); + + let mut y = sqrt(y_squared); + + // We can NOT do a check like the below. We do not have access to the sign, and this derivation produces "both" points + // assert(y.lt(BN254_FR_MODULUS_DIV_2) | y.eq(BN254_FR_MODULUS_DIV_2)); + + // 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 } @@ -66,6 +103,18 @@ impl AztecAddress { ) } + pub fn compute_from_public_keys(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( function_selector: FunctionSelector, functino_vk_hash: Field, diff --git a/yarn-project/circuits.js/src/keys/derivation.ts b/yarn-project/circuits.js/src/keys/derivation.ts index c8eb037c167f..dbeec36dcaa5 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, type Point } from '@aztec/foundation/fields'; import { Grumpkin } from '../barretenberg/crypto/grumpkin/index.js'; import { GeneratorIndex } from '../constants.gen.js'; @@ -46,6 +46,38 @@ export function computeAddress(publicKeysHash: Fr, partialAddress: Fr) { return AztecAddress.fromField(addressFr); } +export function computeNewAddress(publicKeys: PublicKeys, partialAddress: Fr) { + const preaddress = poseidon2HashWithSeparator([publicKeys.hash(), partialAddress], GeneratorIndex.CONTRACT_ADDRESS_V1); + 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 curve = new Grumpkin(); + const preaddressPoint = derivePublicKeyFromSecretKey(new Fq(preaddress.toBigInt())); + const addressPoint = curve.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 (!addressPointCandidate.y.lt(new Fr((Fr.MODULUS - 1n)/ 2n))) { + return new Fq(Fq.MODULUS - addressSecretCandidate.toBigInt()); + } + + return addressSecretCandidate; +} + export function derivePublicKeyFromSecretKey(secretKey: Fq) { const curve = new Grumpkin(); return curve.mul(curve.generator(), secretKey); diff --git a/yarn-project/circuits.js/src/structs/complete_address.ts b/yarn-project/circuits.js/src/structs/complete_address.ts index 31c36f15698d..c26a9c13a601 100644 --- a/yarn-project/circuits.js/src/structs/complete_address.ts +++ b/yarn-project/circuits.js/src/structs/complete_address.ts @@ -58,6 +58,10 @@ export class CompleteAddress { } } + public toPreaddress() { + return computeAddress(this.publicKeys.hash(), this.partialAddress).toField(); + } + /** * Gets a readable string representation of the complete address. * @returns A readable string representation of the complete address. diff --git a/yarn-project/foundation/src/fields/fields.ts b/yarn-project/foundation/src/fields/fields.ts index 8939b7adb7c8..08443b004687 100644 --- a/yarn-project/foundation/src/fields/fields.ts +++ b/yarn-project/foundation/src/fields/fields.ts @@ -381,6 +381,10 @@ export class Fq extends BaseField { return new Fq((high.toBigInt() << Fq.HIGH_SHIFT) + low.toBigInt()); } + add(rhs: Fq) { + return new Fq((this.toBigInt() + rhs.toBigInt()) % Fq.MODULUS); + } + toJSON() { return { type: 'Fq', diff --git a/yarn-project/pxe/src/note_processor/note_processor.ts b/yarn-project/pxe/src/note_processor/note_processor.ts index b5bd723dc6d1..d47552927618 100644 --- a/yarn-project/pxe/src/note_processor/note_processor.ts +++ b/yarn-project/pxe/src/note_processor/note_processor.ts @@ -1,6 +1,6 @@ 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, computeAddressSecret, INITIAL_L2_BLOCK_NUM, MAX_NOTE_HASHES_PER_TX, type PublicKey } 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 +115,7 @@ export class NoteProcessor { const deferredOutgoingNotes: DeferredNoteDao[] = []; const ivskM = await this.keyStore.getMasterSecretKey(this.ivpkM); + const addressSecret = computeAddressSecret(this.account.toPreaddress(), ivskM); const ovskM = await this.keyStore.getMasterSecretKey(this.ovpkM); // Iterate over both blocks and encrypted logs. @@ -142,7 +143,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) {