@aztec/archiver 0.0.1-commit.fffb133c → 0.0.1-dev

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. package/dest/archiver.d.ts +9 -5
  2. package/dest/archiver.d.ts.map +1 -1
  3. package/dest/archiver.js +87 -111
  4. package/dest/errors.d.ts +7 -9
  5. package/dest/errors.d.ts.map +1 -1
  6. package/dest/errors.js +9 -14
  7. package/dest/factory.d.ts +4 -3
  8. package/dest/factory.d.ts.map +1 -1
  9. package/dest/factory.js +32 -26
  10. package/dest/index.d.ts +2 -1
  11. package/dest/index.d.ts.map +1 -1
  12. package/dest/index.js +1 -0
  13. package/dest/l1/bin/retrieve-calldata.js +36 -33
  14. package/dest/l1/calldata_retriever.d.ts +73 -50
  15. package/dest/l1/calldata_retriever.d.ts.map +1 -1
  16. package/dest/l1/calldata_retriever.js +190 -259
  17. package/dest/l1/data_retrieval.d.ts +4 -7
  18. package/dest/l1/data_retrieval.d.ts.map +1 -1
  19. package/dest/l1/data_retrieval.js +10 -14
  20. package/dest/l1/spire_proposer.d.ts +5 -5
  21. package/dest/l1/spire_proposer.d.ts.map +1 -1
  22. package/dest/l1/spire_proposer.js +9 -17
  23. package/dest/l1/validate_trace.d.ts +6 -3
  24. package/dest/l1/validate_trace.d.ts.map +1 -1
  25. package/dest/l1/validate_trace.js +13 -9
  26. package/dest/modules/data_source_base.d.ts +13 -8
  27. package/dest/modules/data_source_base.d.ts.map +1 -1
  28. package/dest/modules/data_source_base.js +29 -73
  29. package/dest/modules/data_store_updater.d.ts +22 -7
  30. package/dest/modules/data_store_updater.d.ts.map +1 -1
  31. package/dest/modules/data_store_updater.js +114 -40
  32. package/dest/modules/instrumentation.d.ts +4 -2
  33. package/dest/modules/instrumentation.d.ts.map +1 -1
  34. package/dest/modules/instrumentation.js +26 -12
  35. package/dest/modules/l1_synchronizer.d.ts +5 -8
  36. package/dest/modules/l1_synchronizer.d.ts.map +1 -1
  37. package/dest/modules/l1_synchronizer.js +42 -13
  38. package/dest/store/block_store.d.ts +38 -29
  39. package/dest/store/block_store.d.ts.map +1 -1
  40. package/dest/store/block_store.js +294 -85
  41. package/dest/store/contract_class_store.d.ts +1 -1
  42. package/dest/store/contract_class_store.d.ts.map +1 -1
  43. package/dest/store/contract_class_store.js +27 -7
  44. package/dest/store/contract_instance_store.d.ts +1 -1
  45. package/dest/store/contract_instance_store.d.ts.map +1 -1
  46. package/dest/store/contract_instance_store.js +17 -2
  47. package/dest/store/kv_archiver_store.d.ts +40 -18
  48. package/dest/store/kv_archiver_store.d.ts.map +1 -1
  49. package/dest/store/kv_archiver_store.js +44 -16
  50. package/dest/store/l2_tips_cache.d.ts +18 -0
  51. package/dest/store/l2_tips_cache.d.ts.map +1 -0
  52. package/dest/store/l2_tips_cache.js +20 -0
  53. package/dest/store/log_store.d.ts +1 -1
  54. package/dest/store/log_store.d.ts.map +1 -1
  55. package/dest/store/log_store.js +125 -56
  56. package/dest/store/message_store.js +1 -1
  57. package/dest/test/fake_l1_state.d.ts +20 -1
  58. package/dest/test/fake_l1_state.d.ts.map +1 -1
  59. package/dest/test/fake_l1_state.js +97 -20
  60. package/dest/test/index.js +3 -1
  61. package/dest/test/mock_archiver.d.ts +1 -1
  62. package/dest/test/mock_archiver.d.ts.map +1 -1
  63. package/dest/test/mock_archiver.js +3 -2
  64. package/dest/test/mock_l2_block_source.d.ts +25 -9
  65. package/dest/test/mock_l2_block_source.d.ts.map +1 -1
  66. package/dest/test/mock_l2_block_source.js +133 -87
  67. package/dest/test/mock_structs.d.ts +6 -2
  68. package/dest/test/mock_structs.d.ts.map +1 -1
  69. package/dest/test/mock_structs.js +20 -6
  70. package/dest/test/noop_l1_archiver.d.ts +26 -0
  71. package/dest/test/noop_l1_archiver.d.ts.map +1 -0
  72. package/dest/test/noop_l1_archiver.js +72 -0
  73. package/package.json +14 -13
  74. package/src/archiver.ts +110 -132
  75. package/src/errors.ts +10 -24
  76. package/src/factory.ts +47 -23
  77. package/src/index.ts +1 -0
  78. package/src/l1/README.md +25 -68
  79. package/src/l1/bin/retrieve-calldata.ts +46 -39
  80. package/src/l1/calldata_retriever.ts +249 -379
  81. package/src/l1/data_retrieval.ts +7 -17
  82. package/src/l1/spire_proposer.ts +7 -15
  83. package/src/l1/validate_trace.ts +24 -6
  84. package/src/modules/data_source_base.ts +59 -98
  85. package/src/modules/data_store_updater.ts +124 -43
  86. package/src/modules/instrumentation.ts +24 -12
  87. package/src/modules/l1_synchronizer.ts +49 -17
  88. package/src/store/block_store.ts +359 -115
  89. package/src/store/contract_class_store.ts +27 -7
  90. package/src/store/contract_instance_store.ts +19 -5
  91. package/src/store/kv_archiver_store.ts +71 -25
  92. package/src/store/l2_tips_cache.ts +27 -0
  93. package/src/store/log_store.ts +186 -57
  94. package/src/store/message_store.ts +1 -1
  95. package/src/test/fake_l1_state.ts +125 -21
  96. package/src/test/index.ts +3 -0
  97. package/src/test/mock_archiver.ts +3 -2
  98. package/src/test/mock_l2_block_source.ts +179 -87
  99. package/src/test/mock_structs.ts +42 -12
  100. package/src/test/noop_l1_archiver.ts +115 -0
@@ -1,12 +1,12 @@
1
1
  import { INITIAL_L2_BLOCK_NUM } from '@aztec/constants';
2
2
  import { BlockNumber } from '@aztec/foundation/branded-types';
3
- import { filterAsync } from '@aztec/foundation/collection';
3
+ import { compactArray, filterAsync } from '@aztec/foundation/collection';
4
4
  import { Fr } from '@aztec/foundation/curves/bn254';
5
5
  import { createLogger } from '@aztec/foundation/log';
6
6
  import { BufferReader, numToUInt32BE } from '@aztec/foundation/serialize';
7
7
  import type { AztecAsyncKVStore, AztecAsyncMap } from '@aztec/kv-store';
8
8
  import type { AztecAddress } from '@aztec/stdlib/aztec-address';
9
- import { L2Block, L2BlockHash } from '@aztec/stdlib/block';
9
+ import { BlockHash, L2Block } from '@aztec/stdlib/block';
10
10
  import { MAX_LOGS_PER_TAG } from '@aztec/stdlib/interfaces/api-limit';
11
11
  import type { GetContractClassLogsResponse, GetPublicLogsResponse } from '@aztec/stdlib/interfaces/client';
12
12
  import {
@@ -20,9 +20,21 @@ import {
20
20
  Tag,
21
21
  TxScopedL2Log,
22
22
  } from '@aztec/stdlib/logs';
23
+ import { TxHash } from '@aztec/stdlib/tx';
23
24
 
24
25
  import type { BlockStore } from './block_store.js';
25
26
 
27
+ /**
28
+ * Tag key for a log: the string form of its first field, or the empty string when the log carries no
29
+ * fields. A protocol-valid public log can carry zero fields (e.g. a raw AVM `EMITPUBLICLOG` with
30
+ * `logSize = 0`), which has no tag to index by. Keying it under the empty tag keeps it retrievable via
31
+ * the per-block read (and droppable on reorg) while never matching a real tag query — instead of reading
32
+ * `fields[0]` off an empty array and aborting the whole block-ingestion transaction.
33
+ */
34
+ function tagKeyForLog(fields: Fr[]): string {
35
+ return fields.length > 0 ? fields[0].toString() : '';
36
+ }
37
+
26
38
  /**
27
39
  * A store for logs
28
40
  */
@@ -69,11 +81,12 @@ export class LogStore {
69
81
  const txHash = txEffect.txHash;
70
82
 
71
83
  txEffect.privateLogs.forEach(log => {
72
- // Private logs use SiloedTag (already siloed by kernel)
73
- const tag = log.fields[0];
74
- this.#log.debug(`Found private log with tag ${tag.toString()} in block ${block.number}`);
84
+ // Private logs use SiloedTag (already siloed by kernel). A zero-field log has no tag and is keyed
85
+ // under the empty tag rather than throwing on log.fields[0].
86
+ const tagKey = tagKeyForLog(log.fields);
87
+ this.#log.debug(`Found private log with tag ${tagKey} in block ${block.number}`);
75
88
 
76
- const currentLogs = privateTaggedLogs.get(tag.toString()) ?? [];
89
+ const currentLogs = privateTaggedLogs.get(tagKey) ?? [];
77
90
  currentLogs.push(
78
91
  new TxScopedL2Log(
79
92
  txHash,
@@ -84,16 +97,17 @@ export class LogStore {
84
97
  txEffect.nullifiers[0],
85
98
  ).toBuffer(),
86
99
  );
87
- privateTaggedLogs.set(tag.toString(), currentLogs);
100
+ privateTaggedLogs.set(tagKey, currentLogs);
88
101
  });
89
102
 
90
103
  txEffect.publicLogs.forEach(log => {
91
- // Public logs use Tag directly (not siloed) and are stored with contract address
92
- const tag = log.fields[0];
104
+ // Public logs use Tag directly (not siloed) and are stored with contract address. A zero-field
105
+ // log has no tag and is keyed under the empty tag rather than throwing on log.fields[0].
106
+ const tagKey = tagKeyForLog(log.fields);
93
107
  const contractAddress = log.contractAddress;
94
- const key = `${contractAddress.toString()}_${tag.toString()}`;
108
+ const key = `${contractAddress.toString()}_${tagKey}`;
95
109
  this.#log.debug(
96
- `Found public log with tag ${tag.toString()} from contract ${contractAddress.toString()} in block ${block.number}`,
110
+ `Found public log with tag ${tagKey} from contract ${contractAddress.toString()} in block ${block.number}`,
97
111
  );
98
112
 
99
113
  const currentLogs = publicTaggedLogs.get(key) ?? [];
@@ -219,6 +233,7 @@ export class LogStore {
219
233
  .map((txEffect, txIndex) =>
220
234
  [
221
235
  numToUInt32BE(txIndex),
236
+ txEffect.txHash.toBuffer(),
222
237
  numToUInt32BE(txEffect.publicLogs.length),
223
238
  txEffect.publicLogs.map(log => log.toBuffer()),
224
239
  ].flat(),
@@ -242,6 +257,7 @@ export class LogStore {
242
257
  .map((txEffect, txIndex) =>
243
258
  [
244
259
  numToUInt32BE(txIndex),
260
+ txEffect.txHash.toBuffer(),
245
261
  numToUInt32BE(txEffect.contractClassLogs.length),
246
262
  txEffect.contractClassLogs.map(log => log.toBuffer()),
247
263
  ].flat(),
@@ -271,34 +287,65 @@ export class LogStore {
271
287
  });
272
288
  }
273
289
 
274
- #packWithBlockHash(blockHash: Fr, data: Buffer<ArrayBufferLike>[]): Buffer<ArrayBufferLike> {
290
+ #packWithBlockHash(blockHash: BlockHash, data: Buffer<ArrayBufferLike>[]): Buffer<ArrayBufferLike> {
275
291
  return Buffer.concat([blockHash.toBuffer(), ...data]);
276
292
  }
277
293
 
278
- #unpackBlockHash(reader: BufferReader): L2BlockHash {
294
+ #unpackBlockHash(reader: BufferReader): BlockHash {
279
295
  const blockHash = reader.remainingBytes() > 0 ? reader.readObject(Fr) : undefined;
280
296
 
281
297
  if (!blockHash) {
282
298
  throw new Error('Failed to read block hash from log entry buffer');
283
299
  }
284
300
 
285
- return L2BlockHash.fromField(blockHash);
301
+ return new BlockHash(blockHash);
286
302
  }
287
303
 
288
304
  deleteLogs(blocks: L2Block[]): Promise<boolean> {
289
305
  return this.db.transactionAsync(async () => {
290
- await Promise.all(
291
- blocks.map(async block => {
292
- // Delete private logs
293
- const privateKeys = (await this.#privateLogKeysByBlock.getAsync(block.number)) ?? [];
294
- await Promise.all(privateKeys.map(tag => this.#privateLogsByTag.delete(tag)));
295
-
296
- // Delete public logs
297
- const publicKeys = (await this.#publicLogKeysByBlock.getAsync(block.number)) ?? [];
298
- await Promise.all(publicKeys.map(key => this.#publicLogsByContractAndTag.delete(key)));
299
- }),
306
+ const blockNumbers = new Set(blocks.map(block => block.number));
307
+ const firstBlockToDelete = Math.min(...blockNumbers);
308
+
309
+ // Collect all unique private tags across all blocks being deleted
310
+ const allPrivateTags = new Set(
311
+ compactArray(await Promise.all(blocks.map(block => this.#privateLogKeysByBlock.getAsync(block.number)))).flat(),
300
312
  );
301
313
 
314
+ // Trim private logs: for each tag, delete all instances including and after the first block being deleted.
315
+ // This hinges on the invariant that logs for a given tag are always inserted in order of block number, which is enforced in #addPrivateLogs.
316
+ for (const tag of allPrivateTags) {
317
+ const existing = await this.#privateLogsByTag.getAsync(tag);
318
+ if (existing === undefined || existing.length === 0) {
319
+ continue;
320
+ }
321
+ const lastIndexToKeep = existing.findLastIndex(
322
+ buf => TxScopedL2Log.getBlockNumberFromBuffer(buf) < firstBlockToDelete,
323
+ );
324
+ const remaining = existing.slice(0, lastIndexToKeep + 1);
325
+ await (remaining.length > 0 ? this.#privateLogsByTag.set(tag, remaining) : this.#privateLogsByTag.delete(tag));
326
+ }
327
+
328
+ // Collect all unique public keys across all blocks being deleted
329
+ const allPublicKeys = new Set(
330
+ compactArray(await Promise.all(blocks.map(block => this.#publicLogKeysByBlock.getAsync(block.number)))).flat(),
331
+ );
332
+
333
+ // And do the same as we did with private logs
334
+ for (const key of allPublicKeys) {
335
+ const existing = await this.#publicLogsByContractAndTag.getAsync(key);
336
+ if (existing === undefined || existing.length === 0) {
337
+ continue;
338
+ }
339
+ const lastIndexToKeep = existing.findLastIndex(
340
+ buf => TxScopedL2Log.getBlockNumberFromBuffer(buf) < firstBlockToDelete,
341
+ );
342
+ const remaining = existing.slice(0, lastIndexToKeep + 1);
343
+ await (remaining.length > 0
344
+ ? this.#publicLogsByContractAndTag.set(key, remaining)
345
+ : this.#publicLogsByContractAndTag.delete(key));
346
+ }
347
+
348
+ // After trimming the tagged logs, we can delete the block-level keys that track which tags are in which blocks.
302
349
  await Promise.all(
303
350
  blocks.map(block =>
304
351
  Promise.all([
@@ -386,24 +433,33 @@ export class LogStore {
386
433
  }
387
434
 
388
435
  const buffer = (await this.#publicLogsByBlock.getAsync(blockNumber)) ?? Buffer.alloc(0);
389
- const publicLogsInBlock: [PublicLog[]] = [[]];
436
+ const publicLogsInBlock: { txHash: TxHash; logs: PublicLog[] }[] = [];
390
437
  const reader = new BufferReader(buffer);
391
438
 
392
439
  const blockHash = this.#unpackBlockHash(reader);
393
440
 
394
441
  while (reader.remainingBytes() > 0) {
395
442
  const indexOfTx = reader.readNumber();
443
+ const txHash = reader.readObject(TxHash);
396
444
  const numLogsInTx = reader.readNumber();
397
- publicLogsInBlock[indexOfTx] = [];
445
+ publicLogsInBlock[indexOfTx] = { txHash, logs: [] };
398
446
  for (let i = 0; i < numLogsInTx; i++) {
399
- publicLogsInBlock[indexOfTx].push(reader.readObject(PublicLog));
447
+ publicLogsInBlock[indexOfTx].logs.push(reader.readObject(PublicLog));
400
448
  }
401
449
  }
402
450
 
403
- const txLogs = publicLogsInBlock[txIndex];
451
+ const txData = publicLogsInBlock[txIndex];
404
452
 
405
453
  const logs: ExtendedPublicLog[] = [];
406
- const maxLogsHit = this.#accumulateLogs(logs, blockNumber, blockHash, txIndex, txLogs, filter);
454
+ const maxLogsHit = this.#accumulatePublicLogs(
455
+ logs,
456
+ blockNumber,
457
+ blockHash,
458
+ txIndex,
459
+ txData.txHash,
460
+ txData.logs,
461
+ filter,
462
+ );
407
463
 
408
464
  return { logs, maxLogsHit };
409
465
  }
@@ -424,22 +480,31 @@ export class LogStore {
424
480
 
425
481
  let maxLogsHit = false;
426
482
  loopOverBlocks: for await (const [blockNumber, logBuffer] of this.#publicLogsByBlock.entriesAsync({ start, end })) {
427
- const publicLogsInBlock: [PublicLog[]] = [[]];
483
+ const publicLogsInBlock: { txHash: TxHash; logs: PublicLog[] }[] = [];
428
484
  const reader = new BufferReader(logBuffer);
429
485
 
430
486
  const blockHash = this.#unpackBlockHash(reader);
431
487
 
432
488
  while (reader.remainingBytes() > 0) {
433
489
  const indexOfTx = reader.readNumber();
490
+ const txHash = reader.readObject(TxHash);
434
491
  const numLogsInTx = reader.readNumber();
435
- publicLogsInBlock[indexOfTx] = [];
492
+ publicLogsInBlock[indexOfTx] = { txHash, logs: [] };
436
493
  for (let i = 0; i < numLogsInTx; i++) {
437
- publicLogsInBlock[indexOfTx].push(reader.readObject(PublicLog));
494
+ publicLogsInBlock[indexOfTx].logs.push(reader.readObject(PublicLog));
438
495
  }
439
496
  }
440
497
  for (let txIndex = filter.afterLog?.txIndex ?? 0; txIndex < publicLogsInBlock.length; txIndex++) {
441
- const txLogs = publicLogsInBlock[txIndex];
442
- maxLogsHit = this.#accumulateLogs(logs, blockNumber, blockHash, txIndex, txLogs, filter);
498
+ const txData = publicLogsInBlock[txIndex];
499
+ maxLogsHit = this.#accumulatePublicLogs(
500
+ logs,
501
+ blockNumber,
502
+ blockHash,
503
+ txIndex,
504
+ txData.txHash,
505
+ txData.logs,
506
+ filter,
507
+ );
443
508
  if (maxLogsHit) {
444
509
  this.#log.debug(`Max logs hit at block ${blockNumber}`);
445
510
  break loopOverBlocks;
@@ -475,24 +540,33 @@ export class LogStore {
475
540
  return { logs: [], maxLogsHit: false };
476
541
  }
477
542
  const contractClassLogsBuffer = (await this.#contractClassLogsByBlock.getAsync(blockNumber)) ?? Buffer.alloc(0);
478
- const contractClassLogsInBlock: [ContractClassLog[]] = [[]];
543
+ const contractClassLogsInBlock: { txHash: TxHash; logs: ContractClassLog[] }[] = [];
479
544
 
480
545
  const reader = new BufferReader(contractClassLogsBuffer);
481
546
  const blockHash = this.#unpackBlockHash(reader);
482
547
 
483
548
  while (reader.remainingBytes() > 0) {
484
549
  const indexOfTx = reader.readNumber();
550
+ const txHash = reader.readObject(TxHash);
485
551
  const numLogsInTx = reader.readNumber();
486
- contractClassLogsInBlock[indexOfTx] = [];
552
+ contractClassLogsInBlock[indexOfTx] = { txHash, logs: [] };
487
553
  for (let i = 0; i < numLogsInTx; i++) {
488
- contractClassLogsInBlock[indexOfTx].push(reader.readObject(ContractClassLog));
554
+ contractClassLogsInBlock[indexOfTx].logs.push(reader.readObject(ContractClassLog));
489
555
  }
490
556
  }
491
557
 
492
- const txLogs = contractClassLogsInBlock[txIndex];
558
+ const txData = contractClassLogsInBlock[txIndex];
493
559
 
494
560
  const logs: ExtendedContractClassLog[] = [];
495
- const maxLogsHit = this.#accumulateLogs(logs, blockNumber, blockHash, txIndex, txLogs, filter);
561
+ const maxLogsHit = this.#accumulateContractClassLogs(
562
+ logs,
563
+ blockNumber,
564
+ blockHash,
565
+ txIndex,
566
+ txData.txHash,
567
+ txData.logs,
568
+ filter,
569
+ );
496
570
 
497
571
  return { logs, maxLogsHit };
498
572
  }
@@ -516,20 +590,29 @@ export class LogStore {
516
590
  start,
517
591
  end,
518
592
  })) {
519
- const contractClassLogsInBlock: [ContractClassLog[]] = [[]];
593
+ const contractClassLogsInBlock: { txHash: TxHash; logs: ContractClassLog[] }[] = [];
520
594
  const reader = new BufferReader(logBuffer);
521
595
  const blockHash = this.#unpackBlockHash(reader);
522
596
  while (reader.remainingBytes() > 0) {
523
597
  const indexOfTx = reader.readNumber();
598
+ const txHash = reader.readObject(TxHash);
524
599
  const numLogsInTx = reader.readNumber();
525
- contractClassLogsInBlock[indexOfTx] = [];
600
+ contractClassLogsInBlock[indexOfTx] = { txHash, logs: [] };
526
601
  for (let i = 0; i < numLogsInTx; i++) {
527
- contractClassLogsInBlock[indexOfTx].push(reader.readObject(ContractClassLog));
602
+ contractClassLogsInBlock[indexOfTx].logs.push(reader.readObject(ContractClassLog));
528
603
  }
529
604
  }
530
605
  for (let txIndex = filter.afterLog?.txIndex ?? 0; txIndex < contractClassLogsInBlock.length; txIndex++) {
531
- const txLogs = contractClassLogsInBlock[txIndex];
532
- maxLogsHit = this.#accumulateLogs(logs, blockNumber, blockHash, txIndex, txLogs, filter);
606
+ const txData = contractClassLogsInBlock[txIndex];
607
+ maxLogsHit = this.#accumulateContractClassLogs(
608
+ logs,
609
+ blockNumber,
610
+ blockHash,
611
+ txIndex,
612
+ txData.txHash,
613
+ txData.logs,
614
+ filter,
615
+ );
533
616
  if (maxLogsHit) {
534
617
  this.#log.debug(`Max logs hit at block ${blockNumber}`);
535
618
  break loopOverBlocks;
@@ -540,28 +623,74 @@ export class LogStore {
540
623
  return { logs, maxLogsHit };
541
624
  }
542
625
 
543
- #accumulateLogs(
544
- results: (ExtendedContractClassLog | ExtendedPublicLog)[],
626
+ #accumulatePublicLogs(
627
+ results: ExtendedPublicLog[],
545
628
  blockNumber: number,
546
- blockHash: L2BlockHash,
629
+ blockHash: BlockHash,
547
630
  txIndex: number,
548
- txLogs: (ContractClassLog | PublicLog)[],
631
+ txHash: TxHash,
632
+ txLogs: PublicLog[],
549
633
  filter: LogFilter = {},
550
634
  ): boolean {
635
+ if (filter.fromBlock && blockNumber < filter.fromBlock) {
636
+ return false;
637
+ }
638
+ if (filter.toBlock && blockNumber >= filter.toBlock) {
639
+ return false;
640
+ }
641
+ if (filter.txHash && !txHash.equals(filter.txHash)) {
642
+ return false;
643
+ }
644
+
551
645
  let maxLogsHit = false;
552
646
  let logIndex = typeof filter.afterLog?.logIndex === 'number' ? filter.afterLog.logIndex + 1 : 0;
553
647
  for (; logIndex < txLogs.length; logIndex++) {
554
648
  const log = txLogs[logIndex];
555
- if (!filter.contractAddress || log.contractAddress.equals(filter.contractAddress)) {
556
- if (log instanceof ContractClassLog) {
557
- results.push(
558
- new ExtendedContractClassLog(new LogId(BlockNumber(blockNumber), blockHash, txIndex, logIndex), log),
559
- );
560
- } else if (log instanceof PublicLog) {
561
- results.push(new ExtendedPublicLog(new LogId(BlockNumber(blockNumber), blockHash, txIndex, logIndex), log));
562
- } else {
563
- throw new Error('Unknown log type');
649
+ if (
650
+ (!filter.contractAddress || log.contractAddress.equals(filter.contractAddress)) &&
651
+ (!filter.tag || log.fields[0]?.equals(filter.tag))
652
+ ) {
653
+ results.push(
654
+ new ExtendedPublicLog(new LogId(BlockNumber(blockNumber), blockHash, txHash, txIndex, logIndex), log),
655
+ );
656
+
657
+ if (results.length >= this.#logsMaxPageSize) {
658
+ maxLogsHit = true;
659
+ break;
564
660
  }
661
+ }
662
+ }
663
+
664
+ return maxLogsHit;
665
+ }
666
+
667
+ #accumulateContractClassLogs(
668
+ results: ExtendedContractClassLog[],
669
+ blockNumber: number,
670
+ blockHash: BlockHash,
671
+ txIndex: number,
672
+ txHash: TxHash,
673
+ txLogs: ContractClassLog[],
674
+ filter: LogFilter = {},
675
+ ): boolean {
676
+ if (filter.fromBlock && blockNumber < filter.fromBlock) {
677
+ return false;
678
+ }
679
+ if (filter.toBlock && blockNumber >= filter.toBlock) {
680
+ return false;
681
+ }
682
+ if (filter.txHash && !txHash.equals(filter.txHash)) {
683
+ return false;
684
+ }
685
+
686
+ let maxLogsHit = false;
687
+ let logIndex = typeof filter.afterLog?.logIndex === 'number' ? filter.afterLog.logIndex + 1 : 0;
688
+ for (; logIndex < txLogs.length; logIndex++) {
689
+ const log = txLogs[logIndex];
690
+ if (!filter.contractAddress || log.contractAddress.equals(filter.contractAddress)) {
691
+ results.push(
692
+ new ExtendedContractClassLog(new LogId(BlockNumber(blockNumber), blockHash, txHash, txIndex, logIndex), log),
693
+ );
565
694
 
566
695
  if (results.length >= this.#logsMaxPageSize) {
567
696
  maxLogsHit = true;
@@ -137,7 +137,7 @@ export class MessageStore {
137
137
  );
138
138
  }
139
139
 
140
- // Check the first message in a block has the correct index.
140
+ // Check the first message in a checkpoint has the correct index.
141
141
  if (
142
142
  (!lastMessage || message.checkpointNumber > lastMessage.checkpointNumber) &&
143
143
  message.index !== expectedStart