Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 6 additions & 12 deletions yarn-project/end-to-end/src/e2e_event_logs.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<Tuple<Fr, 4>, 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);
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The original workaround was to not send the txs "at once" but instead one by one.

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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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]);
Expand All @@ -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);

Expand All @@ -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);
Expand All @@ -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);
});
Expand All @@ -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]);
Expand All @@ -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);
Expand All @@ -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]);
Expand All @@ -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)),
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Introduced this change when I initially bumped the window size to 20 to fix the test. Then I reverted it back to 10 but decided to keep this change around as it improves the test (it makes it robust against window changes).

recipient.address,
);

const syncedLogs = await pxeOracleInterface.syncTaggedLogs(contractAddress, 3);
Expand All @@ -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]);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above comment. This is an unrelated change.


// We should have called the node once and that is only for the first window
expect(aztecNode.getLogsByTags.mock.calls.length).toBe(1);
Expand All @@ -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);
Expand All @@ -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]);
Expand Down
21 changes: 13 additions & 8 deletions yarn-project/pxe/src/pxe_oracle_interface/pxe_oracle_interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand All @@ -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) {
Expand Down Expand Up @@ -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]));
}

Expand All @@ -373,7 +376,7 @@ export class PXEOracleInterface implements ExecutionDataProvider {
recipient: AztecAddress,
): Promise<void> {
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
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -576,6 +580,7 @@ export class PXEOracleInterface implements ExecutionDataProvider {
Object.entries(newLargestIndexMapToStore).map(
([appTaggingSecret, index]) => new IndexedTaggingSecret(Fr.fromHexString(appTaggingSecret), index),
),
recipient,
);
}
return logsMap;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,32 +23,63 @@ export class TaggingDataProvider {
this.#taggingSecretIndexesForRecipients = this.#store.openMap('tagging_secret_indexes_for_recipients');
}

async setTaggingSecretsIndexesAsSender(indexedSecrets: IndexedTaggingSecret[]): Promise<void> {
await this.#setTaggingSecretsIndexes(indexedSecrets, this.#taggingSecretIndexesForSenders);
setTaggingSecretsIndexesAsSender(indexedSecrets: IndexedTaggingSecret[], sender: AztecAddress) {
return this.#setTaggingSecretsIndexes(indexedSecrets, this.#taggingSecretIndexesForSenders, sender);
}

async setTaggingSecretsIndexesAsRecipient(indexedSecrets: IndexedTaggingSecret[]): Promise<void> {
await this.#setTaggingSecretsIndexes(indexedSecrets, this.#taggingSecretIndexesForRecipients);
setTaggingSecretsIndexesAsRecipient(indexedSecrets: IndexedTaggingSecret[], recipient: AztecAddress) {
return this.#setTaggingSecretsIndexes(indexedSecrets, this.#taggingSecretIndexesForRecipients, recipient);
}

async #setTaggingSecretsIndexes(indexedSecrets: IndexedTaggingSecret[], storageMap: AztecAsyncMap<string, number>) {
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<string, number>,
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<string, number>): Promise<number[]> {
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<string, number>,
inDirectionOf: AztecAddress,
): Promise<number[]> {
return Promise.all(
appTaggingSecrets.map(
async secret => (await storageMap.getAsync(`${secret.toString()}_${inDirectionOf.toString()}`)) ?? 0,
),
);
}

resetNoteSyncData(): Promise<void> {
Expand Down