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 6be213eae194..6221c51ee106 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 @@ -32,18 +32,12 @@ describe('Logs', () => { it('emits multiple events as private logs and decodes them', async () => { const preimages = makeTuple(5, makeTuple.bind(undefined, 4, Fr.random)) as Tuple, 5>; - // TODO(benesjan): Sending the txs is sequence here instead of in parallel as they were sent before because - // with the processing of events in Aztec.nr this revealed a bug in log processing. - // const txs = await Promise.all( - // preimages.map(preimage => - // testLogContract.methods.emit_encrypted_events(wallets[1].getAddress(), preimage).send().wait(), - // ), - // ); - const txs = []; - for (const preimage of preimages) { - const tx = await testLogContract.methods.emit_encrypted_events(wallets[1].getAddress(), preimage).send().wait(); - txs.push(tx); - } + const txs = await Promise.all( + preimages.map(preimage => + testLogContract.methods.emit_encrypted_events(wallets[1].getAddress(), preimage).send().wait(), + ), + ); + const firstBlockNumber = Math.min(...txs.map(tx => tx.blockNumber!)); const lastBlockNumber = Math.max(...txs.map(tx => tx.blockNumber!)); const numBlocks = lastBlockNumber - firstBlockNumber + 1; diff --git a/yarn-project/pxe/src/pxe_oracle_interface/pxe_oracle_interface.test.ts b/yarn-project/pxe/src/pxe_oracle_interface/pxe_oracle_interface.test.ts index 0d37975016bc..17dfaca00b38 100644 --- a/yarn-project/pxe/src/pxe_oracle_interface/pxe_oracle_interface.test.ts +++ b/yarn-project/pxe/src/pxe_oracle_interface/pxe_oracle_interface.test.ts @@ -181,7 +181,7 @@ describe('PXEOracleInterface', () => { // First sender should have 2 logs, but keep index 1 since they were built using the same tag // Next 4 senders should also have index 1 = offset + 1 // Last 5 senders should have index 2 = offset + 2 - const indexes = await taggingDataProvider.getTaggingSecretsIndexesAsRecipient(secrets); + const indexes = await taggingDataProvider.getTaggingSecretsIndexesAsRecipient(secrets, recipient.address); expect(indexes).toHaveLength(NUM_SENDERS); expect(indexes).toEqual([1, 1, 1, 1, 1, 2, 2, 2, 2, 2]); @@ -202,14 +202,25 @@ describe('PXEOracleInterface', () => { // Recompute the secrets (as recipient) to ensure indexes are updated const ivsk = await keyStore.getMasterIncomingViewingSecretKey(recipient.address); + // An array of direction-less secrets for each sender-recipient pair const secrets = await Promise.all( senders.map(sender => computeAppTaggingSecret(recipient, ivsk, sender.completeAddress.address, contractAddress), ), ); - const indexesAsSender = await taggingDataProvider.getTaggingSecretsIndexesAsSender(secrets); - expect(indexesAsSender).toStrictEqual([0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); + // We only get the tagging secret at index `index` for each sender because each sender only needs to track + // their own tagging secret with the recipient. The secrets array contains all sender-recipient pairs, so + // secrets[index] corresponds to the tagging secret between sender[index] and the recipient. + const getTaggingSecretsIndexesAsSenderForSenders = () => + Promise.all( + senders.map((sender, index) => + taggingDataProvider.getTaggingSecretsIndexesAsSender([secrets[index]], sender.completeAddress.address), + ), + ); + + const indexesAsSender = await getTaggingSecretsIndexesAsSenderForSenders(); + expect(indexesAsSender).toStrictEqual([[0], [0], [0], [0], [0], [0], [0], [0], [0], [0]]); expect(aztecNode.getLogsByTags.mock.calls.length).toBe(0); @@ -221,8 +232,8 @@ describe('PXEOracleInterface', () => { ); } - let indexesAsSenderAfterSync = await taggingDataProvider.getTaggingSecretsIndexesAsSender(secrets); - expect(indexesAsSenderAfterSync).toStrictEqual([1, 1, 1, 1, 1, 2, 2, 2, 2, 2]); + let indexesAsSenderAfterSync = await getTaggingSecretsIndexesAsSenderForSenders(); + expect(indexesAsSenderAfterSync).toStrictEqual([[1], [1], [1], [1], [1], [2], [2], [2], [2], [2]]); // Only 1 window is obtained for each sender expect(aztecNode.getLogsByTags.mock.calls.length).toBe(NUM_SENDERS); @@ -240,8 +251,8 @@ describe('PXEOracleInterface', () => { ); } - indexesAsSenderAfterSync = await taggingDataProvider.getTaggingSecretsIndexesAsSender(secrets); - expect(indexesAsSenderAfterSync).toStrictEqual([12, 12, 12, 12, 12, 13, 13, 13, 13, 13]); + indexesAsSenderAfterSync = await getTaggingSecretsIndexesAsSenderForSenders(); + expect(indexesAsSenderAfterSync).toStrictEqual([[12], [12], [12], [12], [12], [13], [13], [13], [13], [13]]); expect(aztecNode.getLogsByTags.mock.calls.length).toBe(NUM_SENDERS * 2); }); @@ -264,7 +275,7 @@ describe('PXEOracleInterface', () => { // First sender should have 2 logs, but keep index 1 since they were built using the same tag // Next 4 senders should also have index 6 = offset + 1 // Last 5 senders should have index 7 = offset + 2 - const indexes = await taggingDataProvider.getTaggingSecretsIndexesAsRecipient(secrets); + const indexes = await taggingDataProvider.getTaggingSecretsIndexesAsRecipient(secrets, recipient.address); expect(indexes).toHaveLength(NUM_SENDERS); expect(indexes).toEqual([6, 6, 6, 6, 6, 7, 7, 7, 7, 7]); @@ -289,6 +300,7 @@ describe('PXEOracleInterface', () => { // Increase our indexes to 2 await taggingDataProvider.setTaggingSecretsIndexesAsRecipient( secrets.map(secret => new IndexedTaggingSecret(secret, 2)), + recipient.address, ); const syncedLogs = await pxeOracleInterface.syncTaggedLogs(contractAddress, 3); @@ -300,7 +312,7 @@ describe('PXEOracleInterface', () => { // First sender should have 2 logs, but keep index 2 since they were built using the same tag // Next 4 senders should also have index 2 = tagIndex + 1 // Last 5 senders should have index 3 = tagIndex + 2 - const indexes = await taggingDataProvider.getTaggingSecretsIndexesAsRecipient(secrets); + const indexes = await taggingDataProvider.getTaggingSecretsIndexesAsRecipient(secrets, recipient.address); expect(indexes).toHaveLength(NUM_SENDERS); expect(indexes).toEqual([2, 2, 2, 2, 2, 3, 3, 3, 3, 3]); @@ -324,8 +336,10 @@ describe('PXEOracleInterface', () => { // We set the indexes to WINDOW_HALF_SIZE + 1 so that it's outside the window and for this reason no updates // should be triggered. + const index = WINDOW_HALF_SIZE + 1; await taggingDataProvider.setTaggingSecretsIndexesAsRecipient( - secrets.map(secret => new IndexedTaggingSecret(secret, WINDOW_HALF_SIZE + 1)), + secrets.map(secret => new IndexedTaggingSecret(secret, index)), + recipient.address, ); const syncedLogs = await pxeOracleInterface.syncTaggedLogs(contractAddress, 3); @@ -334,10 +348,10 @@ describe('PXEOracleInterface', () => { expect(syncedLogs.get(recipient.address.toString())).toHaveLength(NUM_SENDERS / 2); // Indexes should remain where we set them (window_size + 1) - const indexes = await taggingDataProvider.getTaggingSecretsIndexesAsRecipient(secrets); + const indexes = await taggingDataProvider.getTaggingSecretsIndexesAsRecipient(secrets, recipient.address); expect(indexes).toHaveLength(NUM_SENDERS); - expect(indexes).toEqual([11, 11, 11, 11, 11, 11, 11, 11, 11, 11]); + expect(indexes).toEqual([index, index, index, index, index, index, index, index, index, index]); // We should have called the node once and that is only for the first window expect(aztecNode.getLogsByTags.mock.calls.length).toBe(1); @@ -357,6 +371,7 @@ describe('PXEOracleInterface', () => { await taggingDataProvider.setTaggingSecretsIndexesAsRecipient( secrets.map(secret => new IndexedTaggingSecret(secret, WINDOW_HALF_SIZE + 2)), + recipient.address, ); let syncedLogs = await pxeOracleInterface.syncTaggedLogs(contractAddress, 3); @@ -377,7 +392,7 @@ describe('PXEOracleInterface', () => { // First sender should have 2 logs, but keep index 1 since they were built using the same tag // Next 4 senders should also have index 1 = offset + 1 // Last 5 senders should have index 2 = offset + 2 - const indexes = await taggingDataProvider.getTaggingSecretsIndexesAsRecipient(secrets); + const indexes = await taggingDataProvider.getTaggingSecretsIndexesAsRecipient(secrets, recipient.address); expect(indexes).toHaveLength(NUM_SENDERS); expect(indexes).toEqual([1, 1, 1, 1, 1, 2, 2, 2, 2, 2]); diff --git a/yarn-project/pxe/src/pxe_oracle_interface/pxe_oracle_interface.ts b/yarn-project/pxe/src/pxe_oracle_interface/pxe_oracle_interface.ts index 6b6670c5c7ef..07d3d59c64a0 100644 --- a/yarn-project/pxe/src/pxe_oracle_interface/pxe_oracle_interface.ts +++ b/yarn-project/pxe/src/pxe_oracle_interface/pxe_oracle_interface.ts @@ -293,7 +293,7 @@ export class PXEOracleInterface implements ExecutionDataProvider { await this.syncTaggedLogsAsSender(contractAddress, sender, recipient); const appTaggingSecret = await this.#calculateAppTaggingSecret(contractAddress, sender, recipient); - const [index] = await this.taggingDataProvider.getTaggingSecretsIndexesAsSender([appTaggingSecret]); + const [index] = await this.taggingDataProvider.getTaggingSecretsIndexesAsSender([appTaggingSecret], sender); return new IndexedTaggingSecret(appTaggingSecret, index); } @@ -319,8 +319,11 @@ export class PXEOracleInterface implements ExecutionDataProvider { contractAddress, }); - const [index] = await this.taggingDataProvider.getTaggingSecretsIndexesAsSender([secret]); - await this.taggingDataProvider.setTaggingSecretsIndexesAsSender([new IndexedTaggingSecret(secret, index + 1)]); + const [index] = await this.taggingDataProvider.getTaggingSecretsIndexesAsSender([secret], sender); + await this.taggingDataProvider.setTaggingSecretsIndexesAsSender( + [new IndexedTaggingSecret(secret, index + 1)], + sender, + ); } async #calculateAppTaggingSecret(contractAddress: AztecAddress, sender: AztecAddress, recipient: AztecAddress) { @@ -356,7 +359,7 @@ export class PXEOracleInterface implements ExecutionDataProvider { computeAppTaggingSecret(recipientCompleteAddress, recipientIvsk, contact, contractAddress), ), ); - const indexes = await this.taggingDataProvider.getTaggingSecretsIndexesAsRecipient(appTaggingSecrets); + const indexes = await this.taggingDataProvider.getTaggingSecretsIndexesAsRecipient(appTaggingSecrets, recipient); return appTaggingSecrets.map((secret, i) => new IndexedTaggingSecret(secret, indexes[i])); } @@ -373,7 +376,7 @@ export class PXEOracleInterface implements ExecutionDataProvider { recipient: AztecAddress, ): Promise { const appTaggingSecret = await this.#calculateAppTaggingSecret(contractAddress, sender, recipient); - const [oldIndex] = await this.taggingDataProvider.getTaggingSecretsIndexesAsSender([appTaggingSecret]); + const [oldIndex] = await this.taggingDataProvider.getTaggingSecretsIndexesAsSender([appTaggingSecret], sender); // This algorithm works such that: // 1. If we find minimum consecutive empty logs in a window of logs we set the index to the index of the last log @@ -411,9 +414,10 @@ export class PXEOracleInterface implements ExecutionDataProvider { const contractName = await this.contractDataProvider.getDebugContractName(contractAddress); if (currentIndex !== oldIndex) { - await this.taggingDataProvider.setTaggingSecretsIndexesAsSender([ - new IndexedTaggingSecret(appTaggingSecret, currentIndex), - ]); + await this.taggingDataProvider.setTaggingSecretsIndexesAsSender( + [new IndexedTaggingSecret(appTaggingSecret, currentIndex)], + sender, + ); this.log.debug(`Syncing logs for sender ${sender} at contract ${contractName}(${contractAddress})`, { sender, @@ -576,6 +580,7 @@ export class PXEOracleInterface implements ExecutionDataProvider { Object.entries(newLargestIndexMapToStore).map( ([appTaggingSecret, index]) => new IndexedTaggingSecret(Fr.fromHexString(appTaggingSecret), index), ), + recipient, ); } return logsMap; diff --git a/yarn-project/pxe/src/storage/tagging_data_provider/tagging_data_provider.ts b/yarn-project/pxe/src/storage/tagging_data_provider/tagging_data_provider.ts index 2d121a284463..739002280272 100644 --- a/yarn-project/pxe/src/storage/tagging_data_provider/tagging_data_provider.ts +++ b/yarn-project/pxe/src/storage/tagging_data_provider/tagging_data_provider.ts @@ -23,32 +23,63 @@ export class TaggingDataProvider { this.#taggingSecretIndexesForRecipients = this.#store.openMap('tagging_secret_indexes_for_recipients'); } - async setTaggingSecretsIndexesAsSender(indexedSecrets: IndexedTaggingSecret[]): Promise { - await this.#setTaggingSecretsIndexes(indexedSecrets, this.#taggingSecretIndexesForSenders); + setTaggingSecretsIndexesAsSender(indexedSecrets: IndexedTaggingSecret[], sender: AztecAddress) { + return this.#setTaggingSecretsIndexes(indexedSecrets, this.#taggingSecretIndexesForSenders, sender); } - async setTaggingSecretsIndexesAsRecipient(indexedSecrets: IndexedTaggingSecret[]): Promise { - await this.#setTaggingSecretsIndexes(indexedSecrets, this.#taggingSecretIndexesForRecipients); + setTaggingSecretsIndexesAsRecipient(indexedSecrets: IndexedTaggingSecret[], recipient: AztecAddress) { + return this.#setTaggingSecretsIndexes(indexedSecrets, this.#taggingSecretIndexesForRecipients, recipient); } - async #setTaggingSecretsIndexes(indexedSecrets: IndexedTaggingSecret[], storageMap: AztecAsyncMap) { - await Promise.all( + /** + * Sets the indexes of the tagging secrets for the given app tagging secrets in the direction of the given address. + * @dev We need to specify the direction because app tagging secrets are direction-less due to the way they are generated + * but we need to guarantee that the index is stored under a uni-directional key because the tags are themselves + * uni-directional. + * @param indexedSecrets - The app tagging secrets and indexes to set. + * @param storageMap - The storage map to set the indexes in. + * @param inDirectionOf - The address that the secrets are in the direction of. + */ + #setTaggingSecretsIndexes( + indexedSecrets: IndexedTaggingSecret[], + storageMap: AztecAsyncMap, + inDirectionOf: AztecAddress, + ) { + return Promise.all( indexedSecrets.map(indexedSecret => - storageMap.set(indexedSecret.appTaggingSecret.toString(), indexedSecret.index), + storageMap.set(`${indexedSecret.appTaggingSecret.toString()}_${inDirectionOf.toString()}`, indexedSecret.index), ), ); } - async getTaggingSecretsIndexesAsRecipient(appTaggingSecrets: Fr[]) { - return await this.#getTaggingSecretsIndexes(appTaggingSecrets, this.#taggingSecretIndexesForRecipients); + getTaggingSecretsIndexesAsRecipient(appTaggingSecrets: Fr[], recipient: AztecAddress) { + return this.#getTaggingSecretsIndexes(appTaggingSecrets, this.#taggingSecretIndexesForRecipients, recipient); } - async getTaggingSecretsIndexesAsSender(appTaggingSecrets: Fr[]) { - return await this.#getTaggingSecretsIndexes(appTaggingSecrets, this.#taggingSecretIndexesForSenders); + getTaggingSecretsIndexesAsSender(appTaggingSecrets: Fr[], sender: AztecAddress) { + return this.#getTaggingSecretsIndexes(appTaggingSecrets, this.#taggingSecretIndexesForSenders, sender); } - #getTaggingSecretsIndexes(appTaggingSecrets: Fr[], storageMap: AztecAsyncMap): Promise { - return Promise.all(appTaggingSecrets.map(async secret => (await storageMap.getAsync(`${secret.toString()}`)) ?? 0)); + /** + * Returns the indexes of the tagging secrets for the given app tagging secrets in the direction of the given address. + * @dev We need to specify the direction because app tagging secrets are direction-less due to the way they are generated + * but we need to guarantee that the index is stored under a uni-directional key because the tags are themselves + * uni-directional. + * @param appTaggingSecrets - The app tagging secrets to get the indexes for. + * @param storageMap - The storage map to get the indexes from. + * @param inDirectionOf - The address that the secrets are in the direction of. + * @returns The indexes of the tagging secrets. + */ + #getTaggingSecretsIndexes( + appTaggingSecrets: Fr[], + storageMap: AztecAsyncMap, + inDirectionOf: AztecAddress, + ): Promise { + return Promise.all( + appTaggingSecrets.map( + async secret => (await storageMap.getAsync(`${secret.toString()}_${inDirectionOf.toString()}`)) ?? 0, + ), + ); } resetNoteSyncData(): Promise {