Skip to content

Commit 18b40a1

Browse files
nchamoaztec-bot
authored andcommitted
refactor(pxe): batch log RPC calls in LogService.fetchLogsByTag (#23088)
1 parent 6690ae3 commit 18b40a1

2 files changed

Lines changed: 69 additions & 45 deletions

File tree

yarn-project/pxe/src/logs/log_service.test.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,5 +147,40 @@ describe('LogService', () => {
147147
/Got a log retrieval request from/,
148148
);
149149
});
150+
151+
it('batches multiple requests into single RPC calls', async () => {
152+
const tag1 = Tag.random();
153+
const tag2 = Tag.random();
154+
const tag3 = Tag.random();
155+
156+
const publicLog1 = randomTxScopedPrivateL2Log();
157+
const privateLog2 = randomTxScopedPrivateL2Log();
158+
159+
aztecNode.getPublicLogsByTagsFromContract.mockResolvedValue([[publicLog1], [], []]);
160+
aztecNode.getPrivateLogsByTags.mockResolvedValue([[], [privateLog2], []]);
161+
162+
const requests = [
163+
new LogRetrievalRequest(contractAddress, tag1),
164+
new LogRetrievalRequest(contractAddress, tag2),
165+
new LogRetrievalRequest(contractAddress, tag3),
166+
];
167+
168+
const responses = await logService.fetchLogsByTag(contractAddress, requests);
169+
170+
expect(responses).toHaveLength(3);
171+
expect(responses[0]).toEqual(expect.objectContaining({ txHash: publicLog1.txHash }));
172+
expect(responses[1]).toEqual(expect.objectContaining({ txHash: privateLog2.txHash }));
173+
expect(responses[2]).toBeNull();
174+
175+
expect(aztecNode.getPublicLogsByTagsFromContract).toHaveBeenCalledTimes(1);
176+
expect(aztecNode.getPrivateLogsByTags).toHaveBeenCalledTimes(1);
177+
});
178+
179+
it('returns empty array for empty requests', async () => {
180+
const responses = await logService.fetchLogsByTag(contractAddress, []);
181+
expect(responses).toEqual([]);
182+
expect(aztecNode.getPublicLogsByTagsFromContract).not.toHaveBeenCalled();
183+
expect(aztecNode.getPrivateLogsByTags).not.toHaveBeenCalled();
184+
});
150185
});
151186
});

yarn-project/pxe/src/logs/log_service.ts

Lines changed: 34 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,12 @@ import type { KeyStore } from '@aztec/key-store';
33
import { AztecAddress } from '@aztec/stdlib/aztec-address';
44
import type { L2TipsProvider } from '@aztec/stdlib/block';
55
import type { AztecNode } from '@aztec/stdlib/interfaces/server';
6-
import { ExtendedDirectionalAppTaggingSecret, PendingTaggedLog, SiloedTag, Tag } from '@aztec/stdlib/logs';
6+
import {
7+
ExtendedDirectionalAppTaggingSecret,
8+
PendingTaggedLog,
9+
SiloedTag,
10+
type TxScopedL2Log,
11+
} from '@aztec/stdlib/logs';
712
import type { BlockHeader } from '@aztec/stdlib/tx';
813

914
import type { LogRetrievalRequest } from '../contract_function_simulator/noir-structs/log_retrieval_request.js';
@@ -44,62 +49,46 @@ export class LogService {
4449
}
4550
}
4651

47-
return await Promise.all(
48-
logRetrievalRequests.map(async request => {
49-
const [publicLog, privateLog] = await Promise.all([
50-
this.#getPublicLogByTag(request.tag, request.contractAddress),
51-
this.#getPrivateLogByTag(await SiloedTag.computeFromTagAndApp(request.tag, request.contractAddress)),
52-
]);
53-
54-
if (publicLog !== null && privateLog !== null) {
55-
this.log.warn(
56-
`Found both a public and private log for tag ${request.tag} from contract ${request.contractAddress}. This may indicate a contract bug. Returning the public log.`,
57-
);
58-
}
59-
60-
return publicLog ?? privateLog;
61-
}),
62-
);
63-
}
52+
if (logRetrievalRequests.length === 0) {
53+
return [];
54+
}
6455

65-
async #getPublicLogByTag(tag: Tag, contractAddress: AztecAddress): Promise<LogRetrievalResponse | null> {
6656
const anchorBlockHash = await this.anchorBlockHeader.hash();
67-
const allLogsPerTag = await getAllPublicLogsByTagsFromContract(
68-
this.aztecNode,
69-
contractAddress,
70-
[tag],
71-
anchorBlockHash,
57+
const tags = logRetrievalRequests.map(r => r.tag);
58+
const siloedTags = await Promise.all(
59+
logRetrievalRequests.map(r => SiloedTag.computeFromTagAndApp(r.tag, r.contractAddress)),
7260
);
73-
const logsForTag = allLogsPerTag[0];
7461

75-
if (logsForTag.length === 0) {
76-
return null;
77-
} else if (logsForTag.length > 1) {
78-
this.log.warn(
79-
`Expected at most 1 public log for tag ${tag} and contract ${contractAddress.toString()}, got ${logsForTag.length}. This may indicate a contract bug. Returning the first log.`,
62+
const [allPublicLogsPerTag, allPrivateLogsPerTag] = await Promise.all([
63+
getAllPublicLogsByTagsFromContract(this.aztecNode, contractAddress, tags, anchorBlockHash),
64+
getAllPrivateLogsByTags(this.aztecNode, siloedTags, anchorBlockHash),
65+
]);
66+
67+
return logRetrievalRequests.map((request, i) => {
68+
const publicLog = this.#extractSingleLog(
69+
allPublicLogsPerTag[i],
70+
`public log for tag ${request.tag} and contract ${request.contractAddress.toString()}`,
8071
);
81-
}
72+
const privateLog = this.#extractSingleLog(allPrivateLogsPerTag[i], `private log for tag ${siloedTags[i]}`);
8273

83-
const scopedLog = logsForTag[0];
74+
if (publicLog !== null && privateLog !== null) {
75+
this.log.warn(
76+
`Found both a public and private log for tag ${request.tag} from contract ${request.contractAddress}. This may indicate a contract bug. Returning the public log.`,
77+
);
78+
}
8479

85-
return new LogRetrievalResponse(
86-
scopedLog.logData.slice(1), // Skip the tag
87-
scopedLog.txHash,
88-
scopedLog.noteHashes,
89-
scopedLog.firstNullifier,
90-
);
80+
return publicLog ?? privateLog;
81+
});
9182
}
9283

93-
async #getPrivateLogByTag(siloedTag: SiloedTag): Promise<LogRetrievalResponse | null> {
94-
const anchorBlockHash = await this.anchorBlockHeader.hash();
95-
const allLogsPerTag = await getAllPrivateLogsByTags(this.aztecNode, [siloedTag], anchorBlockHash);
96-
const logsForTag = allLogsPerTag[0];
97-
84+
#extractSingleLog(logsForTag: TxScopedL2Log[], description: string): LogRetrievalResponse | null {
9885
if (logsForTag.length === 0) {
9986
return null;
100-
} else if (logsForTag.length > 1) {
87+
}
88+
89+
if (logsForTag.length > 1) {
10190
this.log.warn(
102-
`Expected at most 1 private log for tag ${siloedTag}, got ${logsForTag.length}. This may indicate a contract bug. Returning the first log.`,
91+
`Expected at most 1 ${description}, got ${logsForTag.length}. This may indicate a contract bug. Returning the first log.`,
10392
);
10493
}
10594

0 commit comments

Comments
 (0)