diff --git a/yarn-project/pxe/src/contract_function_simulator/execution_data_provider.ts b/yarn-project/pxe/src/contract_function_simulator/execution_data_provider.ts index 25ebefbef080..244b344054cf 100644 --- a/yarn-project/pxe/src/contract_function_simulator/execution_data_provider.ts +++ b/yarn-project/pxe/src/contract_function_simulator/execution_data_provider.ts @@ -239,11 +239,12 @@ export interface ExecutionDataProvider { syncTaggedLogsAsSender(secret: DirectionalAppTaggingSecret, contractAddress: AztecAddress): Promise; /** - * Returns the next index to be used to compute a tag when sending a log. + * Returns the last used index when sending a log with a given secret. * @param secret - The directional app tagging secret. - * @returns The next index to be used to compute a tag for the given directional app tagging secret. + * @returns The last used index for the given directional app tagging secret, or undefined if we never sent a log + * from this sender to a recipient in a given contract (implicitly included in the secret). */ - getNextIndexAsSender(secret: DirectionalAppTaggingSecret): Promise; + getLastUsedIndexAsSender(secret: DirectionalAppTaggingSecret): Promise; /** * Synchronizes the private logs tagged with scoped addresses and all the senders in the address book. Stores the found diff --git a/yarn-project/pxe/src/contract_function_simulator/execution_tagging_index_cache.ts b/yarn-project/pxe/src/contract_function_simulator/execution_tagging_index_cache.ts index 8523b125ee35..5ca48ee0cf2a 100644 --- a/yarn-project/pxe/src/contract_function_simulator/execution_tagging_index_cache.ts +++ b/yarn-project/pxe/src/contract_function_simulator/execution_tagging_index_cache.ts @@ -8,11 +8,11 @@ import { DirectionalAppTaggingSecret, type IndexedTaggingSecret } from '@aztec/s export class ExecutionTaggingIndexCache { private taggingIndexMap: Map = new Map(); - public getTaggingIndex(secret: DirectionalAppTaggingSecret): number | undefined { + public getLastUsedIndex(secret: DirectionalAppTaggingSecret): number | undefined { return this.taggingIndexMap.get(secret.toString()); } - public setTaggingIndex(secret: DirectionalAppTaggingSecret, index: number) { + public setLastUsedIndex(secret: DirectionalAppTaggingSecret, index: number) { const currentValue = this.taggingIndexMap.get(secret.toString()); if (currentValue !== undefined && currentValue !== index - 1) { throw new Error(`Invalid tagging index update. Current value: ${currentValue}, new value: ${index}`); @@ -20,7 +20,10 @@ export class ExecutionTaggingIndexCache { this.taggingIndexMap.set(secret.toString(), index); } - public getIndexedTaggingSecrets(): IndexedTaggingSecret[] { + /** + * Returns the indexed tagging secrets that were used in this execution. + */ + public getUsedIndexedTaggingSecrets(): IndexedTaggingSecret[] { return Array.from(this.taggingIndexMap.entries()).map(([secret, index]) => ({ secret: DirectionalAppTaggingSecret.fromString(secret), index, diff --git a/yarn-project/pxe/src/contract_function_simulator/oracle/private_execution.test.ts b/yarn-project/pxe/src/contract_function_simulator/oracle/private_execution.test.ts index 820adacb3b47..489fe88bb012 100644 --- a/yarn-project/pxe/src/contract_function_simulator/oracle/private_execution.test.ts +++ b/yarn-project/pxe/src/contract_function_simulator/oracle/private_execution.test.ts @@ -302,8 +302,8 @@ describe('Private Execution test suite', () => { throw new Error(`Unknown address: ${address}. Recipient: ${recipient}, Owner: ${owner}`); }); - executionDataProvider.getNextIndexAsSender.mockImplementation((_secret: DirectionalAppTaggingSecret) => { - return Promise.resolve(0); + executionDataProvider.getLastUsedIndexAsSender.mockImplementation((_secret: DirectionalAppTaggingSecret) => { + return Promise.resolve(undefined); }); executionDataProvider.getFunctionArtifact.mockImplementation(async (address, selector) => { const contract = contracts[address.toString()]; diff --git a/yarn-project/pxe/src/contract_function_simulator/oracle/private_execution.ts b/yarn-project/pxe/src/contract_function_simulator/oracle/private_execution.ts index f065ed5237f1..413b6bf65c74 100644 --- a/yarn-project/pxe/src/contract_function_simulator/oracle/private_execution.ts +++ b/yarn-project/pxe/src/contract_function_simulator/oracle/private_execution.ts @@ -88,7 +88,7 @@ export async function executePrivateFunction( const newNotes = privateExecutionOracle.getNewNotes(); const noteHashNullifierCounterMap = privateExecutionOracle.getNoteHashNullifierCounterMap(); const offchainEffects = privateExecutionOracle.getOffchainEffects(); - const indexedTaggingSecrets = privateExecutionOracle.getIndexedTaggingSecrets(); + const indexedTaggingSecrets = privateExecutionOracle.getUsedIndexedTaggingSecrets(); const nestedExecutionResults = privateExecutionOracle.getNestedExecutionResults(); let timerSubtractionList = nestedExecutionResults; diff --git a/yarn-project/pxe/src/contract_function_simulator/oracle/private_execution_oracle.ts b/yarn-project/pxe/src/contract_function_simulator/oracle/private_execution_oracle.ts index fc659be9276a..9d62b43c8dae 100644 --- a/yarn-project/pxe/src/contract_function_simulator/oracle/private_execution_oracle.ts +++ b/yarn-project/pxe/src/contract_function_simulator/oracle/private_execution_oracle.ts @@ -14,7 +14,7 @@ import type { AuthWitness } from '@aztec/stdlib/auth-witness'; import { AztecAddress } from '@aztec/stdlib/aztec-address'; import { computeUniqueNoteHash, siloNoteHash, siloNullifier } from '@aztec/stdlib/hash'; import { PrivateContextInputs } from '@aztec/stdlib/kernel'; -import type { ContractClassLog, IndexedTaggingSecret } from '@aztec/stdlib/logs'; +import type { ContractClassLog, DirectionalAppTaggingSecret, IndexedTaggingSecret } from '@aztec/stdlib/logs'; import { Note, type NoteStatus } from '@aztec/stdlib/note'; import { type BlockHeader, @@ -152,10 +152,10 @@ export class PrivateExecutionOracle extends UtilityExecutionOracle implements IP } /** - * Return the tagging indexes incremented by this execution along with the directional app tagging secrets. + * Returns the indexed tagging secrets that were used in this execution. */ - public getIndexedTaggingSecrets(): IndexedTaggingSecret[] { - return this.taggingIndexCache.getIndexedTaggingSecrets(); + public getUsedIndexedTaggingSecrets(): IndexedTaggingSecret[] { + return this.taggingIndexCache.getUsedIndexedTaggingSecrets(); } /** @@ -208,34 +208,31 @@ export class PrivateExecutionOracle extends UtilityExecutionOracle implements IP recipient, ); + const index = await this.#getIndexToUseForSecret(secret); + this.log.debug( + `Incrementing tagging index for sender: ${sender}, recipient: ${recipient}, contract: ${this.contractAddress} to ${index}`, + ); + this.taggingIndexCache.setLastUsedIndex(secret, index); + + return Tag.compute({ secret, index }); + } + + async #getIndexToUseForSecret(secret: DirectionalAppTaggingSecret): Promise { // If we have the tagging index in the cache, we use it. If not we obtain it from the execution data provider. - // TODO(benesjan): Make this be `getLastUsedIndex` and refactor this function to look as proposed in this comment: - // https://github.com/AztecProtocol/aztec-packages/pull/17445#discussion_r2400365845 - const maybeTaggingIndex = this.taggingIndexCache.getTaggingIndex(secret); - let taggingIndex: number; + const lastUsedIndexInTx = this.taggingIndexCache.getLastUsedIndex(secret); - if (maybeTaggingIndex !== undefined) { - taggingIndex = maybeTaggingIndex; + if (lastUsedIndexInTx !== undefined) { + return lastUsedIndexInTx + 1; } else { // This is a tagging secret we've not yet used in this tx, so first sync our store to make sure its indices // are up to date. We do this here because this store is not synced as part of the global sync because // that'd be wasteful as most tagging secrets are not used in each tx. - this.log.debug(`Syncing tagged logs as sender ${sender} for contract ${this.contractAddress}`, { - directionalAppTaggingSecret: secret, - recipient, - }); await this.executionDataProvider.syncTaggedLogsAsSender(secret, this.contractAddress); - taggingIndex = await this.executionDataProvider.getNextIndexAsSender(secret); + const lastUsedIndex = await this.executionDataProvider.getLastUsedIndexAsSender(secret); + // If lastUsedIndex is undefined, we've never used this secret, so start from 0 + // Otherwise, the next index to use is one past the last used index + return lastUsedIndex === undefined ? 0 : lastUsedIndex + 1; } - - // Now we increment the index by 1 and store it in the cache. - const nextTaggingIndex = taggingIndex + 1; - this.log.debug( - `Incrementing tagging index for sender: ${sender}, recipient: ${recipient}, contract: ${this.contractAddress} to ${nextTaggingIndex}`, - ); - this.taggingIndexCache.setTaggingIndex(secret, nextTaggingIndex); - - return Tag.compute({ secret, index: taggingIndex }); } /** diff --git a/yarn-project/pxe/src/contract_function_simulator/pxe_oracle_interface.test.ts b/yarn-project/pxe/src/contract_function_simulator/pxe_oracle_interface.test.ts index d696aba6fcb4..4e4dd8c69a99 100644 --- a/yarn-project/pxe/src/contract_function_simulator/pxe_oracle_interface.test.ts +++ b/yarn-project/pxe/src/contract_function_simulator/pxe_oracle_interface.test.ts @@ -206,13 +206,13 @@ 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.getNextIndexesAsRecipient(secrets); + // First sender should have 2 logs, but keep index 0 since they were built using the same tag + // Next 4 senders should also have index 0 = offset + 0 + // Last 5 senders should have index 1 = offset + 1 + const indexes = await taggingDataProvider.getLastUsedIndexesAsRecipient(secrets); expect(indexes).toHaveLength(NUM_SENDERS); - expect(indexes).toEqual([1, 1, 1, 1, 1, 2, 2, 2, 2, 2]); + expect(indexes).toEqual([0, 0, 0, 0, 0, 1, 1, 1, 1, 1]); // We should have called the node 2 times: // 2 times: first time during initial request, second time after pushing the edge of the window once @@ -244,10 +244,21 @@ describe('PXEOracleInterface', () => { ); const getTaggingSecretsIndexesAsSenderForSenders = () => - Promise.all(secrets.map(secret => taggingDataProvider.getNextIndexAsSender(secret))); + Promise.all(secrets.map(secret => taggingDataProvider.getLastUsedIndexesAsSender(secret))); const indexesAsSender = await getTaggingSecretsIndexesAsSenderForSenders(); - expect(indexesAsSender).toStrictEqual([0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); + expect(indexesAsSender).toStrictEqual([ + undefined, + undefined, + undefined, + undefined, + undefined, + undefined, + undefined, + undefined, + undefined, + undefined, + ]); expect(aztecNode.getLogsByTags.mock.calls.length).toBe(0); @@ -263,7 +274,7 @@ describe('PXEOracleInterface', () => { } let indexesAsSenderAfterSync = await getTaggingSecretsIndexesAsSenderForSenders(); - expect(indexesAsSenderAfterSync).toStrictEqual([1, 1, 1, 1, 1, 2, 2, 2, 2, 2]); + expect(indexesAsSenderAfterSync).toStrictEqual([0, 0, 0, 0, 0, 1, 1, 1, 1, 1]); // Only 1 window is obtained for each sender expect(aztecNode.getLogsByTags.mock.calls.length).toBe(NUM_SENDERS); @@ -285,7 +296,7 @@ describe('PXEOracleInterface', () => { } indexesAsSenderAfterSync = await getTaggingSecretsIndexesAsSenderForSenders(); - expect(indexesAsSenderAfterSync).toStrictEqual([12, 12, 12, 12, 12, 13, 13, 13, 13, 13]); + expect(indexesAsSenderAfterSync).toStrictEqual([10, 10, 10, 10, 10, 11, 11, 11, 11, 11]); expect(aztecNode.getLogsByTags.mock.calls.length).toBe(NUM_SENDERS * 2); }); @@ -313,13 +324,13 @@ describe('PXEOracleInterface', () => { ), ); - // First sender should have 2 logs, but keep index 6 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.getNextIndexesAsRecipient(secrets); + // First sender should have 2 logs, but keep index 5 since they were built using the same tag + // Next 4 senders should also have index 5 = offset + // Last 5 senders should have index 6 = offset + 1 + const indexes = await taggingDataProvider.getLastUsedIndexesAsRecipient(secrets); expect(indexes).toHaveLength(NUM_SENDERS); - expect(indexes).toEqual([6, 6, 6, 6, 6, 7, 7, 7, 7, 7]); + expect(indexes).toEqual([5, 5, 5, 5, 5, 6, 6, 6, 6, 6]); // We should have called the node 2 times: // 2 times: first time during initial request, second time after pushing the edge of the window once @@ -344,8 +355,8 @@ describe('PXEOracleInterface', () => { ), ); - // Increase our indexes to 2 - await taggingDataProvider.setNextIndexesAsRecipient(secrets.map(secret => ({ secret, index: 2 }))); + // Set last used indexes to 1 (so next scan starts at 2) + await taggingDataProvider.setLastUsedIndexesAsRecipient(secrets.map(secret => ({ secret, index: 1 }))); await pxeOracleInterface.syncTaggedLogs(contractAddress, PENDING_TAGGED_LOG_ARRAY_BASE_SLOT); @@ -353,13 +364,13 @@ describe('PXEOracleInterface', () => { // since the window starts at Math.max(0, 2 - window_size) = 0 await expectPendingTaggedLogArrayLengthToBe(contractAddress, NUM_SENDERS + 1 + NUM_SENDERS / 2); - // 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.getNextIndexesAsRecipient(secrets); + // 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 = tagIndex + // Last 5 senders should have index 2 = tagIndex + 1 + const indexes = await taggingDataProvider.getLastUsedIndexesAsRecipient(secrets); expect(indexes).toHaveLength(NUM_SENDERS); - expect(indexes).toEqual([2, 2, 2, 2, 2, 3, 3, 3, 3, 3]); + expect(indexes).toEqual([1, 1, 1, 1, 1, 2, 2, 2, 2, 2]); // We should have called the node 2 times: // first time during initial request, second time after pushing the edge of the window once @@ -384,10 +395,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. + // We set the last used indexes to WINDOW_HALF_SIZE so that next scan starts at WINDOW_HALF_SIZE + 1, + // which is outside the window, and for this reason no updates should be triggered. const index = WINDOW_HALF_SIZE + 1; - await taggingDataProvider.setNextIndexesAsRecipient(secrets.map(secret => ({ secret, index }))); + await taggingDataProvider.setLastUsedIndexesAsRecipient(secrets.map(secret => ({ secret, index }))); await pxeOracleInterface.syncTaggedLogs(contractAddress, PENDING_TAGGED_LOG_ARRAY_BASE_SLOT); @@ -395,8 +406,8 @@ describe('PXEOracleInterface', () => { // be skipped await expectPendingTaggedLogArrayLengthToBe(contractAddress, NUM_SENDERS / 2); - // Indexes should remain where we set them (window_size + 1) - const indexes = await taggingDataProvider.getNextIndexesAsRecipient(secrets); + // Indexes should remain where we set them (window_size) + const indexes = await taggingDataProvider.getLastUsedIndexesAsRecipient(secrets); expect(indexes).toHaveLength(NUM_SENDERS); expect(indexes).toEqual([index, index, index, index, index, index, index, index, index, index]); @@ -423,7 +434,7 @@ describe('PXEOracleInterface', () => { ), ); - await taggingDataProvider.setNextIndexesAsRecipient( + await taggingDataProvider.setLastUsedIndexesAsRecipient( secrets.map(secret => ({ secret, index: WINDOW_HALF_SIZE + 2 })), ); @@ -443,13 +454,13 @@ describe('PXEOracleInterface', () => { await pxeOracleInterface.syncTaggedLogs(contractAddress, PENDING_TAGGED_LOG_ARRAY_BASE_SLOT); - // 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.getNextIndexesAsRecipient(secrets); + // First sender should have 2 logs, but keep index 0 since they were built using the same tag + // Next 4 senders should also have index 0 = offset + // Last 5 senders should have index 1 = offset + 1 + const indexes = await taggingDataProvider.getLastUsedIndexesAsRecipient(secrets); expect(indexes).toHaveLength(NUM_SENDERS); - expect(indexes).toEqual([1, 1, 1, 1, 1, 2, 2, 2, 2, 2]); + expect(indexes).toEqual([0, 0, 0, 0, 0, 1, 1, 1, 1, 1]); // We should have called the node 2 times: // first time during initial request, second time after pushing the edge of the window once diff --git a/yarn-project/pxe/src/contract_function_simulator/pxe_oracle_interface.ts b/yarn-project/pxe/src/contract_function_simulator/pxe_oracle_interface.ts index 789a3de28b76..a64dbfa071a3 100644 --- a/yarn-project/pxe/src/contract_function_simulator/pxe_oracle_interface.ts +++ b/yarn-project/pxe/src/contract_function_simulator/pxe_oracle_interface.ts @@ -24,7 +24,6 @@ import { TxScopedL2Log, deriveEcdhSharedSecret, } from '@aztec/stdlib/logs'; -import type { IndexedTaggingSecret } from '@aztec/stdlib/logs'; import { getNonNullifiedL1ToL2MessageWitness } from '@aztec/stdlib/messaging'; import { Note, type NoteStatus } from '@aztec/stdlib/note'; import { MerkleTreeId, type NullifierMembershipWitness, PublicDataWitness } from '@aztec/stdlib/trees'; @@ -269,8 +268,8 @@ export class PXEOracleInterface implements ExecutionDataProvider { return this.taggingDataProvider.getSenderAddresses(); } - public getNextIndexAsSender(secret: DirectionalAppTaggingSecret): Promise { - return this.taggingDataProvider.getNextIndexAsSender(secret); + public getLastUsedIndexAsSender(secret: DirectionalAppTaggingSecret): Promise { + return this.taggingDataProvider.getLastUsedIndexesAsSender(secret); } public async calculateDirectionalAppTaggingSecret( @@ -296,12 +295,12 @@ export class PXEOracleInterface implements ExecutionDataProvider { * so we're keeping it private for now. * @param contractAddress - The contract address to silo the secret for * @param recipient - The address receiving the notes - * @returns A list of indexed tagging secrets + * @returns A list of indexed tagging secrets. If the corresponding secret was never used, the index is undefined. */ - async #getIndexedTaggingSecretsForSenders( + async #getLastUsedIndexedTaggingSecretsForSenders( contractAddress: AztecAddress, recipient: AztecAddress, - ): Promise { + ): Promise<{ secret: DirectionalAppTaggingSecret; index: number | undefined }[]> { const recipientCompleteAddress = await this.getCompleteAddress(recipient); const recipientIvsk = await this.keyStore.getMasterIncomingViewingSecretKey(recipient); @@ -322,7 +321,7 @@ export class PXEOracleInterface implements ExecutionDataProvider { ); }), ); - const indexes = await this.taggingDataProvider.getNextIndexesAsRecipient(secrets); + const indexes = await this.taggingDataProvider.getLastUsedIndexesAsRecipient(secrets); if (indexes.length !== secrets.length) { throw new Error('Indexes and directional app tagging secrets have different lengths'); } @@ -337,7 +336,10 @@ export class PXEOracleInterface implements ExecutionDataProvider { secret: DirectionalAppTaggingSecret, contractAddress: AztecAddress, ): Promise { - const oldIndex = await this.taggingDataProvider.getNextIndexAsSender(secret); + const lastUsedIndex = await this.taggingDataProvider.getLastUsedIndexesAsSender(secret); + // If lastUsedIndex is undefined, we've never used this secret, so start from 0 + // Otherwise, start from one past the last used index + const startIndex = lastUsedIndex === undefined ? 0 : lastUsedIndex + 1; // 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 @@ -347,7 +349,8 @@ export class PXEOracleInterface implements ExecutionDataProvider { const MIN_CONSECUTIVE_EMPTY_LOGS = 10; const WINDOW_SIZE = MIN_CONSECUTIVE_EMPTY_LOGS * 2; - let [numConsecutiveEmptyLogs, currentIndex] = [0, oldIndex]; + let [numConsecutiveEmptyLogs, currentIndex] = [0, startIndex]; + let lastFoundLogIndex: number | undefined = undefined; do { // We compute the tags for the current window of indexes const currentTags = await timesParallel(WINDOW_SIZE, async i => { @@ -361,23 +364,26 @@ export class PXEOracleInterface implements ExecutionDataProvider { const possibleLogs = await this.#getPrivateLogsByTags(tagsAsFr); // We find the index of the last log in the window that is not empty - const indexOfLastLog = possibleLogs.findLastIndex(possibleLog => possibleLog.length !== 0); + const indexOfLastLogWithinArray = possibleLogs.findLastIndex(possibleLog => possibleLog.length !== 0); - if (indexOfLastLog === -1) { + if (indexOfLastLogWithinArray === -1) { // We haven't found any logs in the current window so we stop looking break; } - // We move the current index to that of the last log we found - currentIndex += indexOfLastLog + 1; + // We've found logs so we update the last found log index + lastFoundLogIndex = (lastFoundLogIndex ?? 0) + indexOfLastLogWithinArray; + // We move the current index to that of the log right after the last found log + currentIndex = lastFoundLogIndex + 1; // We compute the number of consecutive empty logs we found and repeat the process if we haven't found enough. - numConsecutiveEmptyLogs = WINDOW_SIZE - indexOfLastLog - 1; + numConsecutiveEmptyLogs = WINDOW_SIZE - indexOfLastLogWithinArray - 1; } while (numConsecutiveEmptyLogs < MIN_CONSECUTIVE_EMPTY_LOGS); const contractName = await this.contractDataProvider.getDebugContractName(contractAddress); - if (currentIndex !== oldIndex) { - await this.taggingDataProvider.setNextIndexesAsSender([{ secret, index: currentIndex }]); + if (lastFoundLogIndex !== undefined) { + // Last found index is defined meaning we have actually found logs so we update the last used index + await this.taggingDataProvider.setLastUsedIndexesAsSender([{ secret, index: lastFoundLogIndex }]); this.log.debug(`Syncing logs for secret ${secret.toString()} at contract ${contractName}(${contractAddress})`, { index: currentIndex, @@ -419,7 +425,7 @@ export class PXEOracleInterface implements ExecutionDataProvider { const contractName = await this.contractDataProvider.getDebugContractName(contractAddress); for (const recipient of recipients) { // Get all the secrets for the recipient and sender pairs (#9365) - const secrets = await this.#getIndexedTaggingSecretsForSenders(contractAddress, recipient); + const indexedSecrets = await this.#getLastUsedIndexedTaggingSecretsForSenders(contractAddress, recipient); // We fetch logs for a window of indexes in a range: // . @@ -429,19 +435,28 @@ export class PXEOracleInterface implements ExecutionDataProvider { // for logs the first time we don't receive any logs for a tag, we might never receive anything from that sender again. // Also there's a possibility that we have advanced our index, but the sender has reused it, so we might have missed // some logs. For these reasons, we have to look both back and ahead of the stored index. - let secretsAndWindows = secrets.map(indexedSecret => { - return { - secret: indexedSecret.secret, - leftMostIndex: Math.max(0, indexedSecret.index - WINDOW_HALF_SIZE), - rightMostIndex: indexedSecret.index + WINDOW_HALF_SIZE, - }; + let secretsAndWindows = indexedSecrets.map(indexedSecret => { + if (indexedSecret.index === undefined) { + return { + secret: indexedSecret.secret, + leftMostIndex: 0, + rightMostIndex: WINDOW_HALF_SIZE, + }; + } else { + return { + secret: indexedSecret.secret, + leftMostIndex: Math.max(0, indexedSecret.index - WINDOW_HALF_SIZE), + rightMostIndex: indexedSecret.index + WINDOW_HALF_SIZE, + }; + } }); // As we iterate we store the largest index we have seen for a given secret to later on store it in the db. const newLargestIndexMapToStore: { [k: string]: number } = {}; - // The initial/unmodified indexes of the secrets stored in a key-value map where key is the app tagging secret. - const initialIndexesMap = getInitialIndexesMap(secrets); + // The initial/unmodified indexes of the secrets stored in a key-value map where key is the directional app + // tagging secret. + const initialIndexesMap = getInitialIndexesMap(indexedSecrets); while (secretsAndWindows.length > 0) { const secretsForTheWholeWindow = getIndexedTaggingSecretsForTheWindow(secretsAndWindows); @@ -510,7 +525,7 @@ export class PXEOracleInterface implements ExecutionDataProvider { // so we fetch the logs only for the difference of the window sets. const newSecretsAndWindows = []; for (const [directionalAppTaggingSecret, newIndex] of Object.entries(newLargestIndexMapForIteration)) { - const maybeIndexedSecret = secrets.find( + const maybeIndexedSecret = indexedSecrets.find( indexedSecret => indexedSecret.secret.toString() === directionalAppTaggingSecret, ); if (maybeIndexedSecret) { @@ -534,11 +549,13 @@ export class PXEOracleInterface implements ExecutionDataProvider { secretsAndWindows = newSecretsAndWindows; } - // At this point we have processed all the logs for the recipient so we store the new largest indexes in the db. - await this.taggingDataProvider.setNextIndexesAsRecipient( + // At this point we have processed all the logs for the recipient so we store the last used indexes in the db. + // newLargestIndexMapToStore contains "next" indexes to look for (one past the last found), so subtract 1 to get + // last used. + await this.taggingDataProvider.setLastUsedIndexesAsRecipient( Object.entries(newLargestIndexMapToStore).map(([directionalAppTaggingSecret, index]) => ({ secret: DirectionalAppTaggingSecret.fromString(directionalAppTaggingSecret), - index, + index: index - 1, })), ); } diff --git a/yarn-project/pxe/src/pxe.ts b/yarn-project/pxe/src/pxe.ts index 39769e41dc6e..94042a894505 100644 --- a/yarn-project/pxe/src/pxe.ts +++ b/yarn-project/pxe/src/pxe.ts @@ -744,12 +744,12 @@ export class PXE { const indexedTaggingSecretsIncrementedInTheTx = privateExecutionResult.entrypoint.indexedTaggingSecrets; if (indexedTaggingSecretsIncrementedInTheTx.length > 0) { - await this.taggingDataProvider.setNextIndexesAsSender(indexedTaggingSecretsIncrementedInTheTx); - this.log.debug(`Incremented next tagging secret indexes as sender for the tx`, { + await this.taggingDataProvider.setLastUsedIndexesAsSender(indexedTaggingSecretsIncrementedInTheTx); + this.log.debug(`Stored last used tagging secret indexes as sender for the tx`, { indexedTaggingSecretsIncrementedInTheTx, }); } else { - this.log.debug(`No next tagging secret indexes incremented in the tx`); + this.log.debug(`No tagging secret indexes incremented in the tx`); } return txProvingResult; 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 2e5a97bab484..f7d86d701d2e 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 @@ -7,43 +7,43 @@ export class TaggingDataProvider { #store: AztecAsyncKVStore; #addressBook: AztecAsyncMap; - // Stores the next index to be used for each directional app tagging secret. Taking into account whether we are + // Stores the last used index for each directional app tagging secret. Taking into account whether we are // requesting the index as a sender or as a recipient because the sender and recipient can be in the same PXE. - #nextIndexesAsSenders: AztecAsyncMap; - #nextIndexesAsRecipients: AztecAsyncMap; + #lastUsedIndexesAsSenders: AztecAsyncMap; + #lastUsedIndexesAsRecipients: AztecAsyncMap; constructor(store: AztecAsyncKVStore) { this.#store = store; this.#addressBook = this.#store.openMap('address_book'); - this.#nextIndexesAsSenders = this.#store.openMap('next_indexes_as_senders'); - this.#nextIndexesAsRecipients = this.#store.openMap('next_indexes_as_recipients'); + this.#lastUsedIndexesAsSenders = this.#store.openMap('last_used_indexes_as_senders'); + this.#lastUsedIndexesAsRecipients = this.#store.openMap('last_used_indexes_as_recipients'); } /** - * Sets the next indexes to be used to compute tags when sending a log. - * @param indexedSecrets - The indexed secrets to set the next indexes for. + * Sets the last used indexes when sending a log. + * @param indexedSecrets - The indexed secrets to set the last used indexes for. * @throws If there are duplicate secrets in the input array */ - setNextIndexesAsSender(indexedSecrets: IndexedTaggingSecret[]) { + setLastUsedIndexesAsSender(indexedSecrets: IndexedTaggingSecret[]) { this.#assertUniqueSecrets(indexedSecrets, 'sender'); return Promise.all( - indexedSecrets.map(({ secret, index }) => this.#nextIndexesAsSenders.set(secret.toString(), index)), + indexedSecrets.map(({ secret, index }) => this.#lastUsedIndexesAsSenders.set(secret.toString(), index)), ); } /** - * Sets the next indexes to be used to compute tags when looking for logs. - * @param indexedSecrets - The indexed secrets to set the next indexes for. + * Sets the last used indexes when looking for logs. + * @param indexedSecrets - The indexed secrets to set the last used indexes for. * @throws If there are duplicate secrets in the input array */ - setNextIndexesAsRecipient(indexedSecrets: IndexedTaggingSecret[]) { + setLastUsedIndexesAsRecipient(indexedSecrets: IndexedTaggingSecret[]) { this.#assertUniqueSecrets(indexedSecrets, 'recipient'); return Promise.all( - indexedSecrets.map(({ secret, index }) => this.#nextIndexesAsRecipients.set(secret.toString(), index)), + indexedSecrets.map(({ secret, index }) => this.#lastUsedIndexesAsRecipients.set(secret.toString(), index)), ); } @@ -53,36 +53,35 @@ export class TaggingDataProvider { const secretStrings = indexedSecrets.map(({ secret }) => secret.toString()); const uniqueSecrets = new Set(secretStrings); if (uniqueSecrets.size !== secretStrings.length) { - throw new Error(`Duplicate secrets found when setting next indexes as ${role}`); + throw new Error(`Duplicate secrets found when setting last used indexes as ${role}`); } } /** - * Returns the next index to be used to compute a tag when sending a log. + * Returns the last used index when sending a log with a given secret. * @param secret - The directional app tagging secret. - * @returns The next index to be used to compute a tag for the given directional app tagging secret. + * @returns The last used index for the given directional app tagging secret, or undefined if not found. */ - async getNextIndexAsSender(secret: DirectionalAppTaggingSecret): Promise { - return (await this.#nextIndexesAsSenders.getAsync(secret.toString())) ?? 0; + async getLastUsedIndexesAsSender(secret: DirectionalAppTaggingSecret): Promise { + return await this.#lastUsedIndexesAsSenders.getAsync(secret.toString()); } /** - * Returns the next indexes to be used to compute tags when looking for logs. + * Returns the last used indexes when looking for logs as a recipient. * @param secrets - The directional app tagging secrets to obtain the indexes for. - * @returns The next indexes to be used to compute tags for the given directional app tagging secrets. + * @returns The last used indexes for the given directional app tagging secrets, or undefined if have never yet found + * a log for a given secret. */ - getNextIndexesAsRecipient(secrets: DirectionalAppTaggingSecret[]): Promise { - return Promise.all( - secrets.map(async secret => (await this.#nextIndexesAsRecipients.getAsync(secret.toString())) ?? 0), - ); + getLastUsedIndexesAsRecipient(secrets: DirectionalAppTaggingSecret[]): Promise<(number | undefined)[]> { + return Promise.all(secrets.map(secret => this.#lastUsedIndexesAsRecipients.getAsync(secret.toString()))); } resetNoteSyncData(): Promise { return this.#store.transactionAsync(async () => { - const keysForSenders = await toArray(this.#nextIndexesAsSenders.keysAsync()); - await Promise.all(keysForSenders.map(secret => this.#nextIndexesAsSenders.delete(secret))); - const keysForRecipients = await toArray(this.#nextIndexesAsRecipients.keysAsync()); - await Promise.all(keysForRecipients.map(secret => this.#nextIndexesAsRecipients.delete(secret))); + const keysForSenders = await toArray(this.#lastUsedIndexesAsSenders.keysAsync()); + await Promise.all(keysForSenders.map(secret => this.#lastUsedIndexesAsSenders.delete(secret))); + const keysForRecipients = await toArray(this.#lastUsedIndexesAsRecipients.keysAsync()); + await Promise.all(keysForRecipients.map(secret => this.#lastUsedIndexesAsRecipients.delete(secret))); }); } diff --git a/yarn-project/pxe/src/tagging/utils.ts b/yarn-project/pxe/src/tagging/utils.ts index c99847e8dd01..fb3f32a91e36 100644 --- a/yarn-project/pxe/src/tagging/utils.ts +++ b/yarn-project/pxe/src/tagging/utils.ts @@ -18,13 +18,15 @@ export function getIndexedTaggingSecretsForTheWindow( * @param indexedTaggingSecrets - The indexed tagging secrets to get the initial indexes from. * @returns The map from directional app tagging secret to initial index. */ -export function getInitialIndexesMap(indexedTaggingSecrets: IndexedTaggingSecret[]): { +export function getInitialIndexesMap( + indexedTaggingSecrets: { secret: DirectionalAppTaggingSecret; index: number | undefined }[], +): { [k: string]: number; } { const initialIndexes: { [k: string]: number } = {}; for (const indexedTaggingSecret of indexedTaggingSecrets) { - initialIndexes[indexedTaggingSecret.secret.toString()] = indexedTaggingSecret.index; + initialIndexes[indexedTaggingSecret.secret.toString()] = indexedTaggingSecret.index ?? 0; } return initialIndexes;