From c4347a209ee790fa75b790834272d8aa0c82edf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Venturo?= Date: Fri, 13 Mar 2026 22:22:43 +0000 Subject: [PATCH 1/2] feat: filtering public logs by tag Co-Authored-By: Claude Opus 4.6 (1M context) --- .../src/store/kv_archiver_store.test.ts | 18 ++++++++++++++++++ yarn-project/archiver/src/store/log_store.ts | 5 ++++- yarn-project/stdlib/src/logs/log_filter.ts | 5 +++++ 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/yarn-project/archiver/src/store/kv_archiver_store.test.ts b/yarn-project/archiver/src/store/kv_archiver_store.test.ts index e84fa3d43710..6c56e63140be 100644 --- a/yarn-project/archiver/src/store/kv_archiver_store.test.ts +++ b/yarn-project/archiver/src/store/kv_archiver_store.test.ts @@ -2782,6 +2782,24 @@ describe('KVArchiverDataStore', () => { } }); + it('"tag" filter param is respected', async () => { + // Get a random tag from the logs + const targetBlockIndex = randomInt(numBlocksForPublicLogs); + const targetBlock = publishedCheckpoints[targetBlockIndex].checkpoint.blocks[0]; + const targetTxIndex = randomInt(getTxsPerBlock(targetBlock)); + const targetLogIndex = randomInt(getPublicLogsPerTx(targetBlock, targetTxIndex)); + const targetTag = targetBlock.body.txEffects[targetTxIndex].publicLogs[targetLogIndex].fields[0]; + + const response = await store.getPublicLogs({ tag: targetTag }); + + expect(response.maxLogsHit).toBeFalsy(); + expect(response.logs.length).toBeGreaterThan(0); + + for (const extendedLog of response.logs) { + expect(extendedLog.log.fields[0].equals(targetTag)).toBeTruthy(); + } + }); + it('"afterLog" filter param is respected', async () => { // Get a random log as reference const targetBlockIndex = randomInt(numBlocksForPublicLogs); diff --git a/yarn-project/archiver/src/store/log_store.ts b/yarn-project/archiver/src/store/log_store.ts index e389cba458e2..133b74ca84c9 100644 --- a/yarn-project/archiver/src/store/log_store.ts +++ b/yarn-project/archiver/src/store/log_store.ts @@ -592,7 +592,10 @@ export class LogStore { let logIndex = typeof filter.afterLog?.logIndex === 'number' ? filter.afterLog.logIndex + 1 : 0; for (; logIndex < txLogs.length; logIndex++) { const log = txLogs[logIndex]; - if (!filter.contractAddress || log.contractAddress.equals(filter.contractAddress)) { + if ( + (!filter.contractAddress || log.contractAddress.equals(filter.contractAddress)) && + (!filter.tag || log.fields[0]?.equals(filter.tag)) + ) { results.push( new ExtendedPublicLog(new LogId(BlockNumber(blockNumber), blockHash, txHash, txIndex, logIndex), log), ); diff --git a/yarn-project/stdlib/src/logs/log_filter.ts b/yarn-project/stdlib/src/logs/log_filter.ts index d2c191a803a9..75f97cd1ee20 100644 --- a/yarn-project/stdlib/src/logs/log_filter.ts +++ b/yarn-project/stdlib/src/logs/log_filter.ts @@ -1,3 +1,5 @@ +import type { Fr } from '@aztec/foundation/curves/bn254'; + import { z } from 'zod'; import type { AztecAddress } from '../aztec-address/index.js'; @@ -20,6 +22,8 @@ export type LogFilter = { afterLog?: LogId; /** The contract address to filter logs by. */ contractAddress?: AztecAddress; + /** The tag (first field of the log) to filter logs by. */ + tag?: Fr; }; export const LogFilterSchema: ZodFor = z.object({ @@ -28,4 +32,5 @@ export const LogFilterSchema: ZodFor = z.object({ toBlock: schemas.Integer.optional(), afterLog: LogId.schema.optional(), contractAddress: schemas.AztecAddress.optional(), + tag: schemas.Fr.optional(), }); From 8c3e4079bf4808c54ea9e8db2f696707e7e660ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Venturo?= Date: Fri, 13 Mar 2026 22:31:05 +0000 Subject: [PATCH 2/2] fix: enforce all log filter params in accumulate methods The accumulate methods for public and contract class logs did not check txHash, fromBlock, or toBlock, relying on callers to handle them. This meant that certain filter combinations (e.g. txHash + afterLog) would silently ignore params. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../src/store/kv_archiver_store.test.ts | 6 +++--- yarn-project/archiver/src/store/log_store.ts | 20 +++++++++++++++++++ 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/yarn-project/archiver/src/store/kv_archiver_store.test.ts b/yarn-project/archiver/src/store/kv_archiver_store.test.ts index 6c56e63140be..d2057ed482d1 100644 --- a/yarn-project/archiver/src/store/kv_archiver_store.test.ts +++ b/yarn-project/archiver/src/store/kv_archiver_store.test.ts @@ -2835,13 +2835,13 @@ describe('KVArchiverDataStore', () => { } }); - it('"txHash" filter param is ignored when "afterLog" is set', async () => { - // Get random txHash + it('"txHash" filter param is respected when "afterLog" is set', async () => { + // A random txHash should match nothing, even with afterLog set const txHash = TxHash.random(); const afterLog = new LogId(BlockNumber(1), BlockHash.random(), TxHash.random(), 0, 0); const response = await store.getPublicLogs({ txHash, afterLog }); - expect(response.logs.length).toBeGreaterThan(1); + expect(response.logs.length).toBe(0); }); it('intersecting works', async () => { diff --git a/yarn-project/archiver/src/store/log_store.ts b/yarn-project/archiver/src/store/log_store.ts index 133b74ca84c9..e2230e7c2847 100644 --- a/yarn-project/archiver/src/store/log_store.ts +++ b/yarn-project/archiver/src/store/log_store.ts @@ -588,6 +588,16 @@ export class LogStore { txLogs: PublicLog[], filter: LogFilter = {}, ): boolean { + if (filter.fromBlock && blockNumber < filter.fromBlock) { + return false; + } + if (filter.toBlock && blockNumber >= filter.toBlock) { + return false; + } + if (filter.txHash && !txHash.equals(filter.txHash)) { + return false; + } + let maxLogsHit = false; let logIndex = typeof filter.afterLog?.logIndex === 'number' ? filter.afterLog.logIndex + 1 : 0; for (; logIndex < txLogs.length; logIndex++) { @@ -619,6 +629,16 @@ export class LogStore { txLogs: ContractClassLog[], filter: LogFilter = {}, ): boolean { + if (filter.fromBlock && blockNumber < filter.fromBlock) { + return false; + } + if (filter.toBlock && blockNumber >= filter.toBlock) { + return false; + } + if (filter.txHash && !txHash.equals(filter.txHash)) { + return false; + } + let maxLogsHit = false; let logIndex = typeof filter.afterLog?.logIndex === 'number' ? filter.afterLog.logIndex + 1 : 0; for (; logIndex < txLogs.length; logIndex++) {