diff --git a/noir-projects/aztec-nr/aztec/src/oracle/notes.nr b/noir-projects/aztec-nr/aztec/src/oracle/notes.nr index 00bb2f14ab1..39ed994516f 100644 --- a/noir-projects/aztec-nr/aztec/src/oracle/notes.nr +++ b/noir-projects/aztec-nr/aztec/src/oracle/notes.nr @@ -231,11 +231,11 @@ pub unconstrained fn get_app_tagging_secrets_for_senders( let results = get_app_tagging_secrets_for_senders_oracle(recipient); let mut indexed_tagging_secrets = &[]; for i in 0..results.len() { - if i % 2 != 0 { + if i % 3 != 0 { continue; } indexed_tagging_secrets = indexed_tagging_secrets.push_back( - IndexedTaggingSecret::deserialize([results[i], results[i + 1]]), + IndexedTaggingSecret::deserialize([results[i], results[i + 1], results[i + 2]]), ); } indexed_tagging_secrets diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/indexed_tagging_secret.nr b/noir-projects/noir-protocol-circuits/crates/types/src/indexed_tagging_secret.nr index 1685fdf38e8..c3191eb9e08 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/indexed_tagging_secret.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/indexed_tagging_secret.nr @@ -1,10 +1,12 @@ use crate::traits::Deserialize; +use super::address::aztec_address::AztecAddress; use std::meta::derive; -pub global INDEXED_TAGGING_SECRET_LENGTH: u32 = 2; +pub global INDEXED_TAGGING_SECRET_LENGTH: u32 = 3; #[derive(Deserialize)] pub struct IndexedTaggingSecret { secret: Field, + recipient: AztecAddress, index: u32, } diff --git a/yarn-project/aztec.js/src/utils/account.ts b/yarn-project/aztec.js/src/utils/account.ts index cfc1c5bf479..5f11c192ede 100644 --- a/yarn-project/aztec.js/src/utils/account.ts +++ b/yarn-project/aztec.js/src/utils/account.ts @@ -1,4 +1,5 @@ -import { type CompleteAddress, type PXE } from '@aztec/circuit-types'; +import { type PXE } from '@aztec/circuit-types'; +import { type CompleteAddress } from '@aztec/circuits.js'; import { retryUntil } from '@aztec/foundation/retry'; import { DefaultWaitOpts, type WaitOpts } from '../contract/sent_tx.js'; diff --git a/yarn-project/circuit-types/src/logs/encrypted_l2_note_log.ts b/yarn-project/circuit-types/src/logs/encrypted_l2_note_log.ts index 3202155a858..095fe870b29 100644 --- a/yarn-project/circuit-types/src/logs/encrypted_l2_note_log.ts +++ b/yarn-project/circuit-types/src/logs/encrypted_l2_note_log.ts @@ -60,10 +60,10 @@ export class EncryptedL2NoteLog { * Crates a random log. * @returns A random log. */ - public static random(): EncryptedL2NoteLog { + public static random(tag: Fr = Fr.random()): EncryptedL2NoteLog { const randomEphPubKey = Point.random(); const randomLogContent = randomBytes(144 - Point.COMPRESSED_SIZE_IN_BYTES); - const data = Buffer.concat([Fr.random().toBuffer(), randomLogContent, randomEphPubKey.toCompressedBuffer()]); + const data = Buffer.concat([tag.toBuffer(), randomLogContent, randomEphPubKey.toCompressedBuffer()]); return new EncryptedL2NoteLog(data); } diff --git a/yarn-project/circuits.js/src/keys/derivation.ts b/yarn-project/circuits.js/src/keys/derivation.ts index 1fa9276e3f3..337091c197a 100644 --- a/yarn-project/circuits.js/src/keys/derivation.ts +++ b/yarn-project/circuits.js/src/keys/derivation.ts @@ -134,5 +134,8 @@ export function computeTaggingSecret(knownAddress: CompleteAddress, ivsk: Fq, ex const curve = new Grumpkin(); // Given A (known complete address) -> B (external address) and h == preaddress // Compute shared secret as S = (h_A + ivsk_A) * Addr_Point_B - return curve.mul(externalAddressPoint, ivsk.add(new Fq(knownPreaddress.toBigInt()))); + + // Beware! h_a + ivsk_a (also known as the address secret) can lead to an address point with a negative y-coordinate, since there's two possible candidates + // computeAddressSecret takes care of selecting the one that leads to a positive y-coordinate, which is the only valid address point + return curve.mul(externalAddressPoint, computeAddressSecret(knownPreaddress, ivsk)); } diff --git a/yarn-project/circuits.js/src/structs/index.ts b/yarn-project/circuits.js/src/structs/index.ts index 4e047dfc520..7bc2702e0a5 100644 --- a/yarn-project/circuits.js/src/structs/index.ts +++ b/yarn-project/circuits.js/src/structs/index.ts @@ -13,7 +13,7 @@ export * from './gas_fees.js'; export * from './gas_settings.js'; export * from './global_variables.js'; export * from './header.js'; -export * from './indexed_tagging_secret.js'; +export * from './tagging_secret.js'; export * from './kernel/combined_accumulated_data.js'; export * from './kernel/combined_constant_data.js'; export * from './kernel/enqueued_call_data.js'; diff --git a/yarn-project/circuits.js/src/structs/indexed_tagging_secret.ts b/yarn-project/circuits.js/src/structs/indexed_tagging_secret.ts deleted file mode 100644 index ab218b5b7ee..00000000000 --- a/yarn-project/circuits.js/src/structs/indexed_tagging_secret.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { Fr } from '@aztec/foundation/fields'; - -export class IndexedTaggingSecret { - constructor(public secret: Fr, public index: number) {} - - toFields(): Fr[] { - return [this.secret, new Fr(this.index)]; - } -} diff --git a/yarn-project/circuits.js/src/structs/tagging_secret.ts b/yarn-project/circuits.js/src/structs/tagging_secret.ts new file mode 100644 index 00000000000..b43e8cb8030 --- /dev/null +++ b/yarn-project/circuits.js/src/structs/tagging_secret.ts @@ -0,0 +1,20 @@ +import { type AztecAddress } from '@aztec/foundation/aztec-address'; +import { Fr } from '@aztec/foundation/fields'; + +export class TaggingSecret { + constructor(public secret: Fr, public recipient: AztecAddress) {} + + toFields(): Fr[] { + return [this.secret, this.recipient]; + } +} + +export class IndexedTaggingSecret extends TaggingSecret { + constructor(secret: Fr, recipient: AztecAddress, public index: number) { + super(secret, recipient); + } + + override toFields(): Fr[] { + return [this.secret, this.recipient, new Fr(this.index)]; + } +} diff --git a/yarn-project/pxe/package.json b/yarn-project/pxe/package.json index b36c544458d..2d84ed81f13 100644 --- a/yarn-project/pxe/package.json +++ b/yarn-project/pxe/package.json @@ -87,9 +87,11 @@ "@jest/globals": "^29.5.0", "@types/jest": "^29.5.0", "@types/lodash.omit": "^4.5.7", + "@types/lodash.times": "^4.3.9", "@types/node": "^18.7.23", "jest": "^29.5.0", "jest-mock-extended": "^3.0.3", + "lodash.times": "^4.3.2", "ts-node": "^10.9.1", "typescript": "^5.0.4" }, diff --git a/yarn-project/pxe/src/database/kv_pxe_database.ts b/yarn-project/pxe/src/database/kv_pxe_database.ts index 95a5da82e45..6d84f65c567 100644 --- a/yarn-project/pxe/src/database/kv_pxe_database.ts +++ b/yarn-project/pxe/src/database/kv_pxe_database.ts @@ -1,16 +1,12 @@ -import { - type IncomingNotesFilter, - MerkleTreeId, - NoteStatus, - type OutgoingNotesFilter, - type PublicKey, -} from '@aztec/circuit-types'; +import { type IncomingNotesFilter, MerkleTreeId, NoteStatus, type OutgoingNotesFilter } from '@aztec/circuit-types'; import { AztecAddress, CompleteAddress, type ContractInstanceWithAddress, Header, + type PublicKey, SerializableContractInstance, + type TaggingSecret, computePoint, } from '@aztec/circuits.js'; import { type ContractArtifact } from '@aztec/foundation/abi'; @@ -576,19 +572,23 @@ export class KVPxeDatabase implements PxeDatabase { return incomingNotesSize + outgoingNotesSize + treeRootsSize + authWitsSize + addressesSize; } - async incrementTaggingSecretsIndexes(appTaggingSecrets: Fr[]): Promise { - const indexes = await this.getTaggingSecretsIndexes(appTaggingSecrets); + async incrementTaggingSecretsIndexes(appTaggingSecretsWithRecipient: TaggingSecret[]): Promise { + const indexes = await this.getTaggingSecretsIndexes(appTaggingSecretsWithRecipient); await this.db.transaction(() => { - indexes.forEach(index => { - const nextIndex = index ? index + 1 : 1; - void this.#taggingSecretIndexes.set(appTaggingSecrets.toString(), nextIndex); + indexes.forEach((taggingSecretIndex, listIndex) => { + const nextIndex = taggingSecretIndex ? taggingSecretIndex + 1 : 1; + const { secret, recipient } = appTaggingSecretsWithRecipient[listIndex]; + const key = `${secret.toString()}-${recipient.toString()}`; + void this.#taggingSecretIndexes.set(key, nextIndex); }); }); } - getTaggingSecretsIndexes(appTaggingSecrets: Fr[]): Promise { + getTaggingSecretsIndexes(appTaggingSecretsWithRecipient: TaggingSecret[]): Promise { return this.db.transaction(() => - appTaggingSecrets.map(secret => this.#taggingSecretIndexes.get(secret.toString()) ?? 0), + appTaggingSecretsWithRecipient.map( + ({ secret, recipient }) => this.#taggingSecretIndexes.get(`${secret.toString()}-${recipient.toString()}`) ?? 0, + ), ); } } diff --git a/yarn-project/pxe/src/database/pxe_database.ts b/yarn-project/pxe/src/database/pxe_database.ts index db845d06828..961301501de 100644 --- a/yarn-project/pxe/src/database/pxe_database.ts +++ b/yarn-project/pxe/src/database/pxe_database.ts @@ -4,6 +4,7 @@ import { type ContractInstanceWithAddress, type Header, type PublicKey, + type TaggingSecret, } from '@aztec/circuits.js'; import { type ContractArtifact } from '@aztec/foundation/abi'; import { type AztecAddress } from '@aztec/foundation/aztec-address'; @@ -186,7 +187,20 @@ export interface PxeDatabase extends ContractArtifactDatabase, ContractInstanceD */ estimateSize(): Promise; - getTaggingSecretsIndexes(appTaggingSecrets: Fr[]): Promise; + /** + * Returns the last seen indexes for the provided app siloed tagging secrets or 0 if they've never been seen. + * The recipient must also be provided to convey "directionality" of the secret and index pair, or in other words + * whether the index was used to tag a sent or received note. + * @param appTaggingSecrets - The app siloed tagging secrets. + * @returns The indexes for the provided secrets, 0 if they've never been seen. + */ + getTaggingSecretsIndexes(appTaggingSecretsWithRecipient: TaggingSecret[]): Promise; - incrementTaggingSecretsIndexes(appTaggingSecrets: Fr[]): Promise; + /** + * Increments the index for the provided app siloed tagging secrets. + * The recipient must also be provided to convey "directionality" of the secret and index pair, or in other words + * whether the index was used to tag a sent or received note. + * @param appTaggingSecrets - The app siloed tagging secrets. + */ + incrementTaggingSecretsIndexes(appTaggingSecretsWithRecipient: TaggingSecret[]): Promise; } diff --git a/yarn-project/pxe/src/index.ts b/yarn-project/pxe/src/index.ts index 86b3f1205e7..d6e46f5ad84 100644 --- a/yarn-project/pxe/src/index.ts +++ b/yarn-project/pxe/src/index.ts @@ -4,7 +4,7 @@ export * from './config/index.js'; export { Tx, TxHash } from '@aztec/circuit-types'; -export { TxRequest, PartialAddress } from '@aztec/circuits.js'; +export { TxRequest } from '@aztec/circuits.js'; export * from '@aztec/foundation/fields'; export * from '@aztec/foundation/eth-address'; export * from '@aztec/foundation/aztec-address'; diff --git a/yarn-project/pxe/src/simulator_oracle/index.ts b/yarn-project/pxe/src/simulator_oracle/index.ts index 4295bc1e555..9f4b278ecba 100644 --- a/yarn-project/pxe/src/simulator_oracle/index.ts +++ b/yarn-project/pxe/src/simulator_oracle/index.ts @@ -1,5 +1,6 @@ import { type AztecNode, + type EncryptedL2NoteLog, type L2Block, MerkleTreeId, type NoteStatus, @@ -17,6 +18,7 @@ import { IndexedTaggingSecret, type KeyValidationRequest, type L1_TO_L2_MSG_TREE_HEIGHT, + TaggingSecret, computeTaggingSecret, } from '@aztec/circuits.js'; import { type FunctionArtifact, getFunctionArtifact } from '@aztec/foundation/abi'; @@ -247,9 +249,11 @@ export class SimulatorOracle implements DBOracle { const senderIvsk = await this.keyStore.getMasterIncomingViewingSecretKey(sender); const sharedSecret = computeTaggingSecret(senderCompleteAddress, senderIvsk, recipient); // Silo the secret to the app so it can't be used to track other app's notes - const secret = poseidon2Hash([sharedSecret.x, sharedSecret.y, contractAddress]); - const [index] = await this.db.getTaggingSecretsIndexes([secret]); - return new IndexedTaggingSecret(secret, index); + const siloedSecret = poseidon2Hash([sharedSecret.x, sharedSecret.y, contractAddress]); + // Get the index of the secret, ensuring the directionality (sender -> recipient) + const directionalSecret = new TaggingSecret(siloedSecret, recipient); + const [index] = await this.db.getTaggingSecretsIndexes([directionalSecret]); + return new IndexedTaggingSecret(siloedSecret, recipient, index); } /** @@ -274,7 +278,55 @@ export class SimulatorOracle implements DBOracle { const sharedSecret = computeTaggingSecret(recipientCompleteAddress, recipientIvsk, sender); return poseidon2Hash([sharedSecret.x, sharedSecret.y, contractAddress]); }); - const indexes = await this.db.getTaggingSecretsIndexes(secrets); - return secrets.map((secret, i) => new IndexedTaggingSecret(secret, indexes[i])); + // Ensure the directionality (sender -> recipient) + const directionalSecrets = secrets.map(secret => new TaggingSecret(secret, recipient)); + const indexes = await this.db.getTaggingSecretsIndexes(directionalSecrets); + return directionalSecrets.map( + ({ secret, recipient }, i) => new IndexedTaggingSecret(secret, recipient, indexes[i]), + ); + } + + /** + * Synchronizes the logs tagged with the recipient's address and all the senders in the addressbook. + * Returns the unsynched logs and updates the indexes of the secrets used to tag them until there are no more logs to sync. + * @param contractAddress - The address of the contract that the logs are tagged for + * @param recipient - The address of the recipient + * @returns A list of encrypted logs tagged with the recipient's address + */ + public async syncTaggedLogs(contractAddress: AztecAddress, recipient: AztecAddress): Promise { + // Ideally this algorithm would be implemented in noir, exposing its building blocks as oracles. + // However it is impossible at the moment due to the language not supporting nested slices. + // This nesting is necessary because for a given set of tags we don't + // know how many logs we will get back. Furthermore, these logs are of undetermined + // length, since we don't really know the note they correspond to until we decrypt them. + + // 1. Get all the secrets for the recipient and sender pairs (#9365) + let appTaggingSecrets = await this.getAppTaggingSecretsForSenders(contractAddress, recipient); + + const logs: EncryptedL2NoteLog[] = []; + while (appTaggingSecrets.length > 0) { + // 2. Compute tags using the secrets, recipient and index. Obtain logs for each tag (#9380) + const currentTags = appTaggingSecrets.map(({ secret, recipient, index }) => + poseidon2Hash([secret, recipient, index]), + ); + const logsByTags = await this.aztecNode.getLogsByTags(currentTags); + const newTaggingSecrets: IndexedTaggingSecret[] = []; + logsByTags.forEach((logsByTag, index) => { + // 3.1. Append logs to the list and increment the index for the tags that have logs (#9380) + if (logsByTag.length > 0) { + logs.push(...logsByTag); + // 3.2. Increment the index for the tags that have logs (#9380) + newTaggingSecrets.push( + new IndexedTaggingSecret(appTaggingSecrets[index].secret, recipient, appTaggingSecrets[index].index + 1), + ); + } + }); + // 4. Consolidate in db and replace initial appTaggingSecrets with the new ones (updated indexes) + await this.db.incrementTaggingSecretsIndexes( + newTaggingSecrets.map(secret => new TaggingSecret(secret.secret, recipient)), + ); + appTaggingSecrets = newTaggingSecrets; + } + return logs; } } diff --git a/yarn-project/pxe/src/simulator_oracle/simulator_oracle.test.ts b/yarn-project/pxe/src/simulator_oracle/simulator_oracle.test.ts new file mode 100644 index 00000000000..d9e8728ad95 --- /dev/null +++ b/yarn-project/pxe/src/simulator_oracle/simulator_oracle.test.ts @@ -0,0 +1,165 @@ +import { type AztecNode, EncryptedL2NoteLog } from '@aztec/circuit-types'; +import { + AztecAddress, + CompleteAddress, + type Fq, + Fr, + TaggingSecret, + computeAddress, + computeTaggingSecret, + deriveKeys, +} from '@aztec/circuits.js'; +import { poseidon2Hash } from '@aztec/foundation/crypto'; +import { KeyStore } from '@aztec/key-store'; +import { openTmpStore } from '@aztec/kv-store/utils'; + +import { type MockProxy, mock } from 'jest-mock-extended'; +import times from 'lodash.times'; + +import { type PxeDatabase } from '../database/index.js'; +import { KVPxeDatabase } from '../database/kv_pxe_database.js'; +import { ContractDataOracle } from '../index.js'; +import { SimulatorOracle } from './index.js'; + +function computeTagForIndex( + sender: { completeAddress: CompleteAddress; ivsk: Fq }, + recipient: AztecAddress, + contractAddress: AztecAddress, + index: number, +) { + const sharedSecret = computeTaggingSecret(sender.completeAddress, sender.ivsk, recipient); + const siloedSecret = poseidon2Hash([sharedSecret.x, sharedSecret.y, contractAddress]); + return poseidon2Hash([siloedSecret, recipient, index]); +} + +describe('Simulator oracle', () => { + let aztecNode: MockProxy; + let database: PxeDatabase; + let contractDataOracle: ContractDataOracle; + let simulatorOracle: SimulatorOracle; + let keyStore: KeyStore; + + let recipient: CompleteAddress; + let contractAddress: AztecAddress; + + const NUM_SENDERS = 10; + let senders: { completeAddress: CompleteAddress; ivsk: Fq }[]; + + beforeEach(async () => { + const db = openTmpStore(); + aztecNode = mock(); + database = new KVPxeDatabase(db); + contractDataOracle = new ContractDataOracle(database); + keyStore = new KeyStore(db); + simulatorOracle = new SimulatorOracle(contractDataOracle, database, keyStore, aztecNode); + // Set up contract address + contractAddress = AztecAddress.random(); + // Set up recipient account + recipient = await keyStore.addAccount(new Fr(69), Fr.random()); + await database.addCompleteAddress(recipient); + // Set up the address book + senders = times(NUM_SENDERS).map((_, index) => { + const keys = deriveKeys(new Fr(index)); + const partialAddress = Fr.random(); + const address = computeAddress(keys.publicKeys, partialAddress); + const completeAddress = new CompleteAddress(address, keys.publicKeys, partialAddress); + return { completeAddress, ivsk: keys.masterIncomingViewingSecretKey }; + }); + for (const sender of senders) { + await database.addCompleteAddress(sender.completeAddress); + } + + const logs: { [k: string]: EncryptedL2NoteLog[] } = {}; + + // Add a random note from every address in the address book for our account with index 0 + // Compute the tag as sender (knowledge of preaddress and ivsk) + for (const sender of senders) { + const tag = computeTagForIndex(sender, recipient.address, contractAddress, 0); + const log = EncryptedL2NoteLog.random(tag); + logs[tag.toString()] = [log]; + } + // Accumulated logs intended for recipient: NUM_SENDERS + + // Add a random note from the first sender in the address book, repeating the tag + // Compute the tag as sender (knowledge of preaddress and ivsk) + const firstSender = senders[0]; + const tag = computeTagForIndex(firstSender, recipient.address, contractAddress, 0); + const log = EncryptedL2NoteLog.random(tag); + logs[tag.toString()].push(log); + // Accumulated logs intended for recipient: NUM_SENDERS + 1 + + // Add a random note from half the address book for our account with index 1 + // Compute the tag as sender (knowledge of preaddress and ivsk) + for (let i = NUM_SENDERS / 2; i < NUM_SENDERS; i++) { + const sender = senders[i]; + const tag = computeTagForIndex(sender, recipient.address, contractAddress, 1); + const log = EncryptedL2NoteLog.random(tag); + logs[tag.toString()] = [log]; + } + // Accumulated logs intended for recipient: NUM_SENDERS + 1 + NUM_SENDERS / 2 + + // Add a random note from every address in the address book for a random recipient with index 0 + // Compute the tag as sender (knowledge of preaddress and ivsk) + for (const sender of senders) { + const keys = deriveKeys(Fr.random()); + const partialAddress = Fr.random(); + const randomRecipient = computeAddress(keys.publicKeys, partialAddress); + const tag = computeTagForIndex(sender, randomRecipient, contractAddress, 0); + const log = EncryptedL2NoteLog.random(tag); + logs[tag.toString()] = [log]; + } + // Accumulated logs intended for recipient: NUM_SENDERS + 1 + NUM_SENDERS / 2 + + // Set up the getTaggedLogs mock + + aztecNode.getLogsByTags.mockImplementation(tags => { + return Promise.resolve(tags.map(tag => logs[tag.toString()] ?? [])); + }); + }); + + describe('sync tagged logs', () => { + it('should sync tagged logs', async () => { + const syncedLogs = await simulatorOracle.syncTaggedLogs(contractAddress, recipient.address); + // We expect to have all logs intended for the recipient, one per sender + 1 with a duplicated tag for the first one + half of the logs for the second index + expect(syncedLogs).toHaveLength(NUM_SENDERS + 1 + NUM_SENDERS / 2); + + // Recompute the secrets (as recipient) to ensure indexes are updated + + const ivsk = await keyStore.getMasterIncomingViewingSecretKey(recipient.address); + const directionalSecrets = senders.map(sender => { + const firstSenderSharedSecret = computeTaggingSecret(recipient, ivsk, sender.completeAddress.address); + const siloedSecret = poseidon2Hash([firstSenderSharedSecret.x, firstSenderSharedSecret.y, contractAddress]); + return new TaggingSecret(siloedSecret, recipient.address); + }); + + // First sender should have 2 logs, but keep index 1 since they were built using the same tag + // Next 4 senders hould also have index 1 + // Last 5 senders should have index 2 + const indexes = await database.getTaggingSecretsIndexes(directionalSecrets); + + expect(indexes).toHaveLength(NUM_SENDERS); + expect(indexes).toEqual([1, 1, 1, 1, 1, 2, 2, 2, 2, 2]); + }); + + it('should only sync tagged logs for which indexes are not updated', async () => { + // Recompute the secrets (as recipient) to update indexes + + const ivsk = await keyStore.getMasterIncomingViewingSecretKey(recipient.address); + const directionalSecrets = senders.map(sender => { + const firstSenderSharedSecret = computeTaggingSecret(recipient, ivsk, sender.completeAddress.address); + const siloedSecret = poseidon2Hash([firstSenderSharedSecret.x, firstSenderSharedSecret.y, contractAddress]); + return new TaggingSecret(siloedSecret, recipient.address); + }); + + await database.incrementTaggingSecretsIndexes(directionalSecrets); + + const syncedLogs = await simulatorOracle.syncTaggedLogs(contractAddress, recipient.address); + + // Only half of the logs should be synced since we start from index 1, the other half should be skipped + expect(syncedLogs).toHaveLength(NUM_SENDERS / 2); + + // We should have called the node twice, once for index 1 and once for index 2 (which should return no logs) + expect(aztecNode.getLogsByTags.mock.calls.length).toBe(2); + }); + }); +}); diff --git a/yarn-project/txe/src/oracle/txe_oracle.ts b/yarn-project/txe/src/oracle/txe_oracle.ts index 1078e6e329e..1754fa8e8c1 100644 --- a/yarn-project/txe/src/oracle/txe_oracle.ts +++ b/yarn-project/txe/src/oracle/txe_oracle.ts @@ -1,5 +1,6 @@ import { AuthWitness, + type EncryptedL2NoteLog, MerkleTreeId, Note, type NoteStatus, @@ -29,6 +30,7 @@ import { PrivateContextInputs, PublicDataTreeLeaf, type PublicDataTreeLeafPreimage, + TaggingSecret, TxContext, computeContractClassId, computeTaggingSecret, @@ -90,6 +92,8 @@ export class TXE implements TypedOracle { private version: Fr = Fr.ONE; private chainId: Fr = Fr.ONE; + private logsByTags = new Map(); + constructor( private logger: Logger, private trees: MerkleTrees, @@ -758,8 +762,8 @@ export class TXE implements TypedOracle { const sharedSecret = computeTaggingSecret(senderCompleteAddress, senderIvsk, recipient); // Silo the secret to the app so it can't be used to track other app's notes const secret = poseidon2Hash([sharedSecret.x, sharedSecret.y, this.contractAddress]); - const [index] = await this.txeDatabase.getTaggingSecretsIndexes([secret]); - return new IndexedTaggingSecret(secret, index); + const [index] = await this.txeDatabase.getTaggingSecretsIndexes([new TaggingSecret(secret, recipient)]); + return new IndexedTaggingSecret(secret, recipient, index); } async getAppTaggingSecretsForSenders(recipient: AztecAddress): Promise { @@ -775,8 +779,9 @@ export class TXE implements TypedOracle { const sharedSecret = computeTaggingSecret(recipientCompleteAddress, recipientIvsk, sender); return poseidon2Hash([sharedSecret.x, sharedSecret.y, this.contractAddress]); }); - const indexes = await this.txeDatabase.getTaggingSecretsIndexes(secrets); - return secrets.map((secret, i) => new IndexedTaggingSecret(secret, indexes[i])); + const directionalSecrets = secrets.map(secret => new TaggingSecret(secret, recipient)); + const indexes = await this.txeDatabase.getTaggingSecretsIndexes(directionalSecrets); + return secrets.map((secret, i) => new IndexedTaggingSecret(secret, recipient, indexes[i])); } // AVM oracles diff --git a/yarn-project/yarn.lock b/yarn-project/yarn.lock index a20dfb4d126..d31f814b645 100644 --- a/yarn-project/yarn.lock +++ b/yarn-project/yarn.lock @@ -1022,12 +1022,14 @@ __metadata: "@noir-lang/types": "workspace:*" "@types/jest": ^29.5.0 "@types/lodash.omit": ^4.5.7 + "@types/lodash.times": ^4.3.9 "@types/node": ^18.7.23 jest: ^29.5.0 jest-mock-extended: ^3.0.3 koa: ^2.14.2 koa-router: ^12.0.0 lodash.omit: ^4.5.0 + lodash.times: ^4.3.2 sha3: ^2.1.4 ts-node: ^10.9.1 tslib: ^2.4.0 @@ -4718,7 +4720,7 @@ __metadata: languageName: node linkType: hard -"@types/lodash.times@npm:^4.3.7": +"@types/lodash.times@npm:^4.3.7, @types/lodash.times@npm:^4.3.9": version: 4.3.9 resolution: "@types/lodash.times@npm:4.3.9" dependencies: