Skip to content

Commit 5e4b46d

Browse files
nventuroThunkar
andauthored
fix: always remove nullified notes (#10722)
We used to remove nullified notes as we process tagged logs, but this is incomplete: it may happen that a recipient got no new logs but they still need to nullify things. I imagine we never caught this because either our tests are not comprehensive enough, of because all in all scenarios we tried there was always a new note (e.g. a transfer for less than the full balance resulted in a change note). I changed things so that the note syncing process also triggers a full search of all nullifiers for all recipients always, instead of only doing it for those that got notes. This is perhaps not ideal long-term, but it is a simple fix for the issue we currently have. --------- Co-authored-by: thunkar <[email protected]>
1 parent 381b0b8 commit 5e4b46d

File tree

6 files changed

+50
-46
lines changed

6 files changed

+50
-46
lines changed

yarn-project/pxe/src/simulator_oracle/index.ts

Lines changed: 24 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -624,27 +624,30 @@ export class SimulatorOracle implements DBOracle {
624624
});
625625
});
626626
}
627-
const nullifiedNotes: IncomingNoteDao[] = [];
628-
const currentNotesForRecipient = await this.db.getIncomingNotes({ owner: recipient });
629-
const nullifiersToCheck = currentNotesForRecipient.map(note => note.siloedNullifier);
630-
const currentBlockNumber = await this.getBlockNumber();
631-
const nullifierIndexes = await this.aztecNode.findNullifiersIndexesWithBlock(currentBlockNumber, nullifiersToCheck);
632-
633-
const foundNullifiers = nullifiersToCheck
634-
.map((nullifier, i) => {
635-
if (nullifierIndexes[i] !== undefined) {
636-
return { ...nullifierIndexes[i], ...{ data: nullifier } } as InBlock<Fr>;
637-
}
638-
})
639-
.filter(nullifier => nullifier !== undefined) as InBlock<Fr>[];
640-
641-
await this.db.removeNullifiedNotes(foundNullifiers, recipient.toAddressPoint());
642-
nullifiedNotes.forEach(noteDao => {
643-
this.log.verbose(`Removed note for contract ${noteDao.contractAddress} at slot ${noteDao.storageSlot}`, {
644-
contract: noteDao.contractAddress,
645-
slot: noteDao.storageSlot,
646-
nullifier: noteDao.siloedNullifier.toString(),
627+
}
628+
629+
public async removeNullifiedNotes(contractAddress: AztecAddress) {
630+
for (const recipient of await this.keyStore.getAccounts()) {
631+
const currentNotesForRecipient = await this.db.getIncomingNotes({ contractAddress, owner: recipient });
632+
const nullifiersToCheck = currentNotesForRecipient.map(note => note.siloedNullifier);
633+
const nullifierIndexes = await this.aztecNode.findNullifiersIndexesWithBlock('latest', nullifiersToCheck);
634+
635+
const foundNullifiers = nullifiersToCheck
636+
.map((nullifier, i) => {
637+
if (nullifierIndexes[i] !== undefined) {
638+
return { ...nullifierIndexes[i], ...{ data: nullifier } } as InBlock<Fr>;
639+
}
640+
})
641+
.filter(nullifier => nullifier !== undefined) as InBlock<Fr>[];
642+
643+
const nullifiedNotes = await this.db.removeNullifiedNotes(foundNullifiers, recipient.toAddressPoint());
644+
nullifiedNotes.forEach(noteDao => {
645+
this.log.verbose(`Removed note for contract ${noteDao.contractAddress} at slot ${noteDao.storageSlot}`, {
646+
contract: noteDao.contractAddress,
647+
slot: noteDao.storageSlot,
648+
nullifier: noteDao.siloedNullifier.toString(),
649+
});
647650
});
648-
});
651+
}
649652
}
650653
}

yarn-project/pxe/src/simulator_oracle/simulator_oracle.test.ts

Lines changed: 14 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,13 @@ import {
22
type AztecNode,
33
EncryptedLogPayload,
44
L1NotePayload,
5+
L2Block,
56
Note,
67
type TxEffect,
78
TxHash,
89
TxScopedL2Log,
910
randomInBlock,
11+
wrapInBlock,
1012
} from '@aztec/circuit-types';
1113
import {
1214
AztecAddress,
@@ -654,40 +656,27 @@ describe('Simulator oracle', () => {
654656
}
655657
});
656658

657-
it('should not store nullified notes', async () => {
659+
it('should remove nullified notes', async () => {
658660
const requests = [
659661
new MockNoteRequest(getRandomNoteLogPayload(Fr.random(), contractAddress), 1, 1, 1, recipient.address),
660662
new MockNoteRequest(getRandomNoteLogPayload(Fr.random(), contractAddress), 6, 3, 2, recipient.address),
661663
new MockNoteRequest(getRandomNoteLogPayload(Fr.random(), contractAddress), 12, 3, 2, recipient.address),
662664
];
663665

664-
const taggedLogs = mockTaggedLogs(requests, 2);
665-
666-
getIncomingNotesSpy.mockResolvedValueOnce(Promise.resolve(requests.map(request => request.snippetOfNoteDao)));
667-
668-
await simulatorOracle.processTaggedLogs(taggedLogs, recipient.address, simulator);
669-
670-
expect(addNotesSpy).toHaveBeenCalledTimes(1);
671-
expect(addNotesSpy).toHaveBeenCalledWith(
672-
// Incoming should contain notes from requests 0, 1, 2 because in those requests we set owner address point.
673-
[
674-
expect.objectContaining({
675-
...requests[0].snippetOfNoteDao,
676-
index: requests[0].indexWithinNoteHashTree,
677-
}),
678-
expect.objectContaining({
679-
...requests[1].snippetOfNoteDao,
680-
index: requests[1].indexWithinNoteHashTree,
681-
}),
682-
expect.objectContaining({
683-
...requests[2].snippetOfNoteDao,
684-
index: requests[2].indexWithinNoteHashTree,
685-
}),
686-
],
687-
recipient.address,
666+
getIncomingNotesSpy.mockResolvedValueOnce(
667+
Promise.resolve(requests.map(request => ({ siloedNullifier: Fr.random(), ...request.snippetOfNoteDao }))),
688668
);
669+
let requestedNullifier;
670+
aztecNode.findNullifiersIndexesWithBlock.mockImplementationOnce((_blockNumber, nullifiers) => {
671+
const block = L2Block.random(2);
672+
requestedNullifier = wrapInBlock(nullifiers[0], block);
673+
return Promise.resolve([wrapInBlock(1n, L2Block.random(2)), undefined, undefined]);
674+
});
675+
676+
await simulatorOracle.removeNullifiedNotes(contractAddress);
689677

690678
expect(removeNullifiedNotesSpy).toHaveBeenCalledTimes(1);
679+
expect(removeNullifiedNotesSpy).toHaveBeenCalledWith([requestedNullifier], recipient.address.toAddressPoint());
691680
}, 30_000);
692681
});
693682
});

yarn-project/simulator/src/client/client_execution_context.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -565,5 +565,7 @@ export class ClientExecutionContext extends ViewDataOracle {
565565
for (const [recipient, taggedLogs] of taggedLogsByRecipient.entries()) {
566566
await this.db.processTaggedLogs(taggedLogs, AztecAddress.fromString(recipient));
567567
}
568+
569+
await this.db.removeNullifiedNotes(this.contractAddress);
568570
}
569571
}

yarn-project/simulator/src/client/db_oracle.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,4 +242,9 @@ export interface DBOracle extends CommitmentsDB {
242242
* @param recipient - The recipient of the logs.
243243
*/
244244
processTaggedLogs(logs: TxScopedL2Log[], recipient: AztecAddress): Promise<void>;
245+
246+
/**
247+
* Removes all of a contract's notes that have been nullified from the note database.
248+
*/
249+
removeNullifiedNotes(contractAddress: AztecAddress): Promise<void>;
245250
}

yarn-project/simulator/src/client/view_data_oracle.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,8 +314,11 @@ export class ViewDataOracle extends TypedOracle {
314314
await this.aztecNode.getBlockNumber(),
315315
this.scopes,
316316
);
317+
317318
for (const [recipient, taggedLogs] of taggedLogsByRecipient.entries()) {
318319
await this.db.processTaggedLogs(taggedLogs, AztecAddress.fromString(recipient));
319320
}
321+
322+
await this.db.removeNullifiedNotes(this.contractAddress);
320323
}
321324
}

yarn-project/txe/src/oracle/txe_oracle.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -921,6 +921,8 @@ export class TXE implements TypedOracle {
921921
await this.simulatorOracle.processTaggedLogs(taggedLogs, AztecAddress.fromString(recipient));
922922
}
923923

924+
await this.simulatorOracle.removeNullifiedNotes(this.contractAddress);
925+
924926
return Promise.resolve();
925927
}
926928

0 commit comments

Comments
 (0)