Skip to content

Commit ceabd1c

Browse files
committed
refactor!: expanding return value of getLogsByTags
Fixes F-231 Fixes #9789 In this PR I modify the the return value of our "get logs by tags" endpoints such that we don't need to perform another request for tx hash in PXE's log service. Since I was already modifying it I also dropped unused values from the type and made it a bit less messy. I added here migration notes for all the changes I did to the endpoint not only in this PR but also in PRs down the stack.
1 parent 45ab5b6 commit ceabd1c

File tree

19 files changed

+248
-479
lines changed

19 files changed

+248
-479
lines changed

docs/docs-developers/docs/resources/migration_notes.md

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,26 +9,34 @@ Aztec is in full-speed development. Literally every version breaks compatibility
99

1010
## TBD
1111

12+
### [Aztec Node] changes to `getLogsByTags` endpoint
13+
14+
`getLogsByTags` endpoint has been optimized for our new log sync algorithm and these are the changes:
15+
16+
- The `logsPerTag` pagination argument has been removed. Pagination was unnecessary here, since multiple logs per tag typically only occur if several devices are sending logs from the same sender to a recipient, which is unlikely to generate enough logs to require pagination.
17+
- The structure of `TxScopedL2Log` has been revised to meet the requirements of our new log sync algorithm.
18+
- The endpoint has been separated into two versions: `getPrivateLogsByTags` and `getPublicLogsByTagsFromContract`. This change was made because it was never desirable in PXE to mix public and private logs. The public version requires both a `Tag` and a contract address as input. In contrast to the private version—which uses `SiloedTag` (a tag that hashes the raw tag with the emitting contract's address)—the public version uses the raw `Tag` type, since kernels do not hash the tag with the contract address for public logs.
19+
1220
### [AVM] Gas cost multipliers for public execution to reach simulation/proving parity
1321

1422
Gas costs for several AVM opcodes have been adjusted with multipliers to better align public simulation costs with actual proving costs.
1523

16-
| Opcode | Multiplier | Previous Cost | New Cost |
17-
|--------|------------|---------------|----------|
18-
| FDIV | 25x | 9 | 225 |
19-
| SLOAD | 10x | 129 | 1,290 |
20-
| SSTORE | 20x | 1,657 | 33,140 |
21-
| NOTEHASHEXISTS | 4x | 126 | 504 |
22-
| EMITNOTEHASH | 15x | 1,285 | 19,275 |
23-
| NULLIFIEREXISTS | 7x | 132 | 924 |
24-
| EMITNULLIFIER | 20x | 1,540 | 30,800 |
25-
| L1TOL2MSGEXISTS | 5x | 108 | 540 |
26-
| SENDL2TOL1MSG | 2x | 209 | 418 |
27-
| CALL | 3x | 3,312 | 9,936 |
28-
| STATICCALL | 3x | 3,312 | 9,936 |
29-
| GETCONTRACTINSTANCE | 4x | 1,527 | 6,108 |
30-
| POSEIDON2 | 15x | 24 | 360 |
31-
| ECADD | 10x | 27 | 270 |
24+
| Opcode | Multiplier | Previous Cost | New Cost |
25+
| ------------------- | ---------- | ------------- | -------- |
26+
| FDIV | 25x | 9 | 225 |
27+
| SLOAD | 10x | 129 | 1,290 |
28+
| SSTORE | 20x | 1,657 | 33,140 |
29+
| NOTEHASHEXISTS | 4x | 126 | 504 |
30+
| EMITNOTEHASH | 15x | 1,285 | 19,275 |
31+
| NULLIFIEREXISTS | 7x | 132 | 924 |
32+
| EMITNULLIFIER | 20x | 1,540 | 30,800 |
33+
| L1TOL2MSGEXISTS | 5x | 108 | 540 |
34+
| SENDL2TOL1MSG | 2x | 209 | 418 |
35+
| CALL | 3x | 3,312 | 9,936 |
36+
| STATICCALL | 3x | 3,312 | 9,936 |
37+
| GETCONTRACTINSTANCE | 4x | 1,527 | 6,108 |
38+
| POSEIDON2 | 15x | 24 | 360 |
39+
| ECADD | 10x | 27 | 270 |
3240

3341
**Impact**: Contracts with public bytecode performing any of these operations will see increased gas consumption.
3442

yarn-project/archiver/src/archiver/archiver_store_test_suite.ts

Lines changed: 10 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2276,17 +2276,13 @@ export function describeArchiverDataStore(
22762276
[
22772277
expect.objectContaining({
22782278
blockNumber: 2,
2279-
blockHash: L2BlockHash.fromField(await logsCheckpoints[2 - 1].checkpoint.blocks[0].header.hash()),
2280-
log: makePrivateLog(tags[0]),
2281-
isFromPublic: false,
2279+
logData: makePrivateLog(tags[0]).getEmittedFields(),
22822280
}),
22832281
],
22842282
[
22852283
expect.objectContaining({
22862284
blockNumber: 1,
2287-
blockHash: L2BlockHash.fromField(await logsCheckpoints[1 - 1].checkpoint.blocks[0].header.hash()),
2288-
log: makePrivateLog(tags[1]),
2289-
isFromPublic: false,
2285+
logData: makePrivateLog(tags[1]).getEmittedFields(),
22902286
}),
22912287
],
22922288
]);
@@ -2312,15 +2308,11 @@ export function describeArchiverDataStore(
23122308
[
23132309
expect.objectContaining({
23142310
blockNumber: 1,
2315-
blockHash: L2BlockHash.fromField(await logsCheckpoints[1 - 1].checkpoint.blocks[0].header.hash()),
2316-
log: makePrivateLog(tags[0]),
2317-
isFromPublic: false,
2311+
logData: makePrivateLog(tags[0]).getEmittedFields(),
23182312
}),
23192313
expect.objectContaining({
23202314
blockNumber: newBlockNumber,
2321-
blockHash: L2BlockHash.fromField(await newCheckpoint.checkpoint.blocks[0].header.hash()),
2322-
log: newLog,
2323-
isFromPublic: false,
2315+
logData: newLog.getEmittedFields(),
23242316
}),
23252317
],
23262318
]);
@@ -2338,9 +2330,7 @@ export function describeArchiverDataStore(
23382330
[
23392331
expect.objectContaining({
23402332
blockNumber: 1,
2341-
blockHash: L2BlockHash.fromField(await logsCheckpoints[1 - 1].checkpoint.blocks[0].header.hash()),
2342-
log: makePrivateLog(tags[1]),
2343-
isFromPublic: false,
2333+
logData: makePrivateLog(tags[1]).getEmittedFields(),
23442334
}),
23452335
],
23462336
]);
@@ -2425,17 +2415,13 @@ export function describeArchiverDataStore(
24252415
[
24262416
expect.objectContaining({
24272417
blockNumber: 2,
2428-
blockHash: L2BlockHash.fromField(await logsCheckpoints[2 - 1].checkpoint.blocks[0].header.hash()),
2429-
log: makePublicLog(tags[0]),
2430-
isFromPublic: true,
2418+
logData: makePublicLog(tags[0]).getEmittedFields(),
24312419
}),
24322420
],
24332421
[
24342422
expect.objectContaining({
24352423
blockNumber: 1,
2436-
blockHash: L2BlockHash.fromField(await logsCheckpoints[1 - 1].checkpoint.blocks[0].header.hash()),
2437-
log: makePublicLog(tags[1]),
2438-
isFromPublic: true,
2424+
logData: makePublicLog(tags[1]).getEmittedFields(),
24392425
}),
24402426
],
24412427
]);
@@ -2461,15 +2447,11 @@ export function describeArchiverDataStore(
24612447
[
24622448
expect.objectContaining({
24632449
blockNumber: 1,
2464-
blockHash: L2BlockHash.fromField(await logsCheckpoints[1 - 1].checkpoint.blocks[0].header.hash()),
2465-
log: makePublicLog(tags[0]),
2466-
isFromPublic: true,
2450+
logData: makePublicLog(tags[0]).getEmittedFields(),
24672451
}),
24682452
expect.objectContaining({
24692453
blockNumber: newBlockNumber,
2470-
blockHash: L2BlockHash.fromField(await newCheckpoint.checkpoint.blocks[0].header.hash()),
2471-
log: newLog,
2472-
isFromPublic: true,
2454+
logData: newLog.getEmittedFields(),
24732455
}),
24742456
],
24752457
]);
@@ -2487,9 +2469,7 @@ export function describeArchiverDataStore(
24872469
[
24882470
expect.objectContaining({
24892471
blockNumber: 1,
2490-
blockHash: L2BlockHash.fromField(await logsCheckpoints[1 - 1].checkpoint.blocks[0].header.hash()),
2491-
log: makePublicLog(tags[1]),
2492-
isFromPublic: true,
2472+
logData: makePublicLog(tags[1]).getEmittedFields(),
24932473
}),
24942474
],
24952475
]);

yarn-project/archiver/src/archiver/kv_archiver_store/log_store.ts

Lines changed: 18 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { INITIAL_L2_BLOCK_NUM, MAX_NOTE_HASHES_PER_TX } from '@aztec/constants';
1+
import { INITIAL_L2_BLOCK_NUM } from '@aztec/constants';
22
import { BlockNumber } from '@aztec/foundation/branded-types';
33
import { Fr } from '@aztec/foundation/curves/bn254';
44
import { createLogger } from '@aztec/foundation/log';
@@ -57,21 +57,16 @@ export class LogStore {
5757
* @param block - The L2 block to extract logs from.
5858
* @returns An object containing the private and public tagged logs for the block.
5959
*/
60-
async #extractTaggedLogsFromBlock(block: L2BlockNew) {
61-
const blockHash = L2BlockHash.fromField(await block.hash());
60+
#extractTaggedLogsFromBlock(block: L2BlockNew) {
6261
// SiloedTag (as string) -> array of log buffers.
6362
const privateTaggedLogs = new Map<string, Buffer[]>();
6463
// "{contractAddress}_{tag}" (as string) -> array of log buffers.
6564
const publicTaggedLogs = new Map<string, Buffer[]>();
66-
const dataStartIndexForBlock =
67-
block.header.state.partial.noteHashTree.nextAvailableLeafIndex -
68-
block.body.txEffects.length * MAX_NOTE_HASHES_PER_TX;
6965

70-
block.body.txEffects.forEach((txEffect, txIndex) => {
66+
block.body.txEffects.forEach(txEffect => {
7167
const txHash = txEffect.txHash;
72-
const dataStartIndexForTx = dataStartIndexForBlock + txIndex * MAX_NOTE_HASHES_PER_TX;
7368

74-
txEffect.privateLogs.forEach((log, logIndex) => {
69+
txEffect.privateLogs.forEach(log => {
7570
// Private logs use SiloedTag (already siloed by kernel)
7671
const tag = log.fields[0];
7772
this.#log.debug(`Found private log with tag ${tag.toString()} in block ${block.number}`);
@@ -80,18 +75,17 @@ export class LogStore {
8075
currentLogs.push(
8176
new TxScopedL2Log(
8277
txHash,
83-
dataStartIndexForTx,
84-
logIndex,
8578
block.number,
86-
blockHash,
8779
block.timestamp,
88-
log,
80+
log.getEmittedFields(),
81+
txEffect.noteHashes,
82+
txEffect.nullifiers[0],
8983
).toBuffer(),
9084
);
9185
privateTaggedLogs.set(tag.toString(), currentLogs);
9286
});
9387

94-
txEffect.publicLogs.forEach((log, logIndex) => {
88+
txEffect.publicLogs.forEach(log => {
9589
// Public logs use Tag directly (not siloed) and are stored with contract address
9690
const tag = log.fields[0];
9791
const contractAddress = log.contractAddress;
@@ -104,12 +98,11 @@ export class LogStore {
10498
currentLogs.push(
10599
new TxScopedL2Log(
106100
txHash,
107-
dataStartIndexForTx,
108-
logIndex,
109101
block.number,
110-
blockHash,
111102
block.timestamp,
112-
log,
103+
log.getEmittedFields(),
104+
txEffect.noteHashes,
105+
txEffect.nullifiers[0],
113106
).toBuffer(),
114107
);
115108
publicTaggedLogs.set(key, currentLogs);
@@ -125,10 +118,11 @@ export class LogStore {
125118
* @returns A map from tag (as string) to an array of serialized private logs belonging to that tag, and a map from
126119
* "{contractAddress}_{tag}" (as string) to an array of serialized public logs belonging to that key.
127120
*/
128-
async #extractTaggedLogs(
129-
blocks: L2BlockNew[],
130-
): Promise<{ privateTaggedLogs: Map<string, Buffer[]>; publicTaggedLogs: Map<string, Buffer[]> }> {
131-
const taggedLogsInBlocks = await Promise.all(blocks.map(block => this.#extractTaggedLogsFromBlock(block)));
121+
#extractTaggedLogs(blocks: L2BlockNew[]): {
122+
privateTaggedLogs: Map<string, Buffer[]>;
123+
publicTaggedLogs: Map<string, Buffer[]>;
124+
} {
125+
const taggedLogsInBlocks = blocks.map(block => this.#extractTaggedLogsFromBlock(block));
132126

133127
// Now we merge the maps from each block into a single map.
134128
const privateTaggedLogs = taggedLogsInBlocks.reduce((acc, { privateTaggedLogs }) => {
@@ -155,8 +149,8 @@ export class LogStore {
155149
* @param blocks - The blocks for which to add the logs.
156150
* @returns True if the operation is successful.
157151
*/
158-
async addLogs(blocks: L2BlockNew[]): Promise<boolean> {
159-
const { privateTaggedLogs, publicTaggedLogs } = await this.#extractTaggedLogs(blocks);
152+
addLogs(blocks: L2BlockNew[]): Promise<boolean> {
153+
const { privateTaggedLogs, publicTaggedLogs } = this.#extractTaggedLogs(blocks);
160154

161155
const keysOfPrivateLogsToUpdate = Array.from(privateTaggedLogs.keys());
162156
const keysOfPublicLogsToUpdate = Array.from(publicTaggedLogs.keys());

yarn-project/end-to-end/src/composed/e2e_token_bridge_tutorial_test.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,5 +229,5 @@ describe('e2e_cross_chain_messaging token_bridge_tutorial_test', () => {
229229
logger.info(`New L1 balance of ${ownerEthAddress} is ${newL1Balance}`);
230230
// docs:end:l1-withdraw
231231
expect(newL1Balance).toBe(withdrawAmount);
232-
}, 90000);
232+
}, 110_000);
233233
});
Lines changed: 6 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,10 @@
1-
import { BlockNumber } from '@aztec/foundation/branded-types';
2-
import { randomInt } from '@aztec/foundation/crypto/random';
31
import { Fr } from '@aztec/foundation/curves/bn254';
42
import { KeyStore } from '@aztec/key-store';
53
import { openTmpStore } from '@aztec/kv-store/lmdb-v2';
64
import { AztecAddress } from '@aztec/stdlib/aztec-address';
7-
import { L2BlockHash } from '@aztec/stdlib/block';
85
import type { AztecNode } from '@aztec/stdlib/interfaces/server';
9-
import { PublicLog, Tag, TxScopedL2Log } from '@aztec/stdlib/logs';
10-
import { TxHash, randomIndexedTxEffect } from '@aztec/stdlib/tx';
6+
import { Tag } from '@aztec/stdlib/logs';
7+
import { randomTxScopedPrivateL2Log } from '@aztec/stdlib/testing';
118

129
import { type MockProxy, mock } from 'jest-mock-extended';
1310

@@ -70,18 +67,12 @@ describe('LogService', () => {
7067
});
7168

7269
it('returns a public log if one is found', async () => {
73-
const scopedLog = await TxScopedL2Log.random(true);
74-
(scopedLog.log as PublicLog).contractAddress = contractAddress;
70+
const scopedLog = randomTxScopedPrivateL2Log();
7571

7672
aztecNode.getPublicLogsByTagsFromContract.mockResolvedValue([[scopedLog]]);
7773
aztecNode.getPrivateLogsByTags.mockResolvedValue([[]]);
78-
const indexedTxEffect = await randomIndexedTxEffect();
7974

80-
aztecNode.getTxEffect.mockImplementation((txHash: TxHash) =>
81-
txHash.equals(scopedLog.txHash) ? Promise.resolve(indexedTxEffect) : Promise.resolve(undefined),
82-
);
83-
84-
const request = new LogRetrievalRequest(contractAddress, new Tag(scopedLog.log.fields[0]));
75+
const request = new LogRetrievalRequest(contractAddress, new Tag(scopedLog.logData[0]));
8576

8677
const responses = await logService.bulkRetrieveLogs([request]);
8778

@@ -90,106 +81,17 @@ describe('LogService', () => {
9081
});
9182

9283
it('returns a private log if one is found', async () => {
93-
const scopedLog = await TxScopedL2Log.random(false);
84+
const scopedLog = randomTxScopedPrivateL2Log();
9485

9586
aztecNode.getPrivateLogsByTags.mockResolvedValue([[scopedLog]]);
9687
aztecNode.getPublicLogsByTagsFromContract.mockResolvedValue([[]]);
97-
const indexedTxEffect = await randomIndexedTxEffect();
98-
aztecNode.getTxEffect.mockResolvedValue(indexedTxEffect);
9988

100-
aztecNode.getTxEffect.mockImplementation((txHash: TxHash) =>
101-
txHash.equals(scopedLog.txHash) ? Promise.resolve(indexedTxEffect) : Promise.resolve(undefined),
102-
);
103-
104-
const request = new LogRetrievalRequest(contractAddress, new Tag(scopedLog.log.fields[0]));
89+
const request = new LogRetrievalRequest(contractAddress, new Tag(scopedLog.logData[0]));
10590

10691
const responses = await logService.bulkRetrieveLogs([request]);
10792

10893
expect(responses.length).toEqual(1);
10994
expect(responses[0]).not.toBeNull();
11095
});
11196
});
112-
113-
describe('getPublicLogByTag', () => {
114-
const tag = new Tag(Fr.random());
115-
116-
beforeEach(() => {
117-
aztecNode.getPublicLogsByTagsFromContract.mockReset();
118-
aztecNode.getTxEffect.mockReset();
119-
});
120-
121-
it('returns null if no logs found for tag', async () => {
122-
aztecNode.getPublicLogsByTagsFromContract.mockResolvedValue([[]]);
123-
124-
const result = await logService.getPublicLogByTag(tag, contractAddress);
125-
expect(result).toBeNull();
126-
});
127-
128-
it('returns log data when single log found', async () => {
129-
const scopedLog = await TxScopedL2Log.random(true);
130-
const logContractAddress = (scopedLog.log as PublicLog).contractAddress;
131-
132-
aztecNode.getPublicLogsByTagsFromContract.mockResolvedValue([[scopedLog]]);
133-
const indexedTxEffect = await randomIndexedTxEffect();
134-
aztecNode.getTxEffect.mockImplementation((txHash: TxHash) =>
135-
txHash.equals(scopedLog.txHash) ? Promise.resolve(indexedTxEffect) : Promise.resolve(undefined),
136-
);
137-
138-
const result = (await logService.getPublicLogByTag(tag, logContractAddress))!;
139-
140-
expect(result.logPayload).toEqual(scopedLog.log.getEmittedFieldsWithoutTag());
141-
expect(result.uniqueNoteHashesInTx).toEqual(indexedTxEffect.data.noteHashes);
142-
expect(result.txHash).toEqual(scopedLog.txHash);
143-
expect(result.firstNullifierInTx).toEqual(indexedTxEffect.data.nullifiers[0]);
144-
145-
expect(aztecNode.getPublicLogsByTagsFromContract).toHaveBeenCalledWith(logContractAddress, [tag]);
146-
expect(aztecNode.getTxEffect).toHaveBeenCalledWith(scopedLog.txHash);
147-
});
148-
149-
it('throws if multiple logs found for tag', async () => {
150-
const scopedLog = await TxScopedL2Log.random(true);
151-
aztecNode.getPublicLogsByTagsFromContract.mockResolvedValue([[scopedLog, scopedLog]]);
152-
const logContractAddress = (scopedLog.log as PublicLog).contractAddress;
153-
154-
await expect(logService.getPublicLogByTag(tag, logContractAddress)).rejects.toThrow(/Got 2 logs for tag/);
155-
});
156-
157-
it('throws if tx effect not found', async () => {
158-
const scopedLog = await TxScopedL2Log.random(true);
159-
aztecNode.getPublicLogsByTagsFromContract.mockResolvedValue([[scopedLog]]);
160-
aztecNode.getTxEffect.mockResolvedValue(undefined);
161-
const logContractAddress = (scopedLog.log as PublicLog).contractAddress;
162-
163-
await expect(logService.getPublicLogByTag(tag, logContractAddress)).rejects.toThrow(
164-
/failed to retrieve tx effects/,
165-
);
166-
});
167-
168-
it('returns log fields that are actually emitted', async () => {
169-
const logContractAddress = await AztecAddress.random();
170-
const logPlaintext = [Fr.random()];
171-
const logContent = [tag.value, ...logPlaintext];
172-
173-
const log = PublicLog.from({
174-
contractAddress: logContractAddress,
175-
fields: logContent,
176-
});
177-
const scopedLogWithPadding = new TxScopedL2Log(
178-
TxHash.random(),
179-
randomInt(100),
180-
randomInt(100),
181-
BlockNumber(randomInt(100)),
182-
L2BlockHash.random(),
183-
0n,
184-
log,
185-
);
186-
187-
aztecNode.getPublicLogsByTagsFromContract.mockResolvedValue([[scopedLogWithPadding]]);
188-
aztecNode.getTxEffect.mockResolvedValue(await randomIndexedTxEffect());
189-
190-
const result = await logService.getPublicLogByTag(tag, logContractAddress);
191-
192-
expect(result?.logPayload).toEqual(logPlaintext);
193-
});
194-
});
19597
});

0 commit comments

Comments
 (0)