@aztec/archiver 4.0.0-nightly.20260127 → 4.0.0-nightly.20260129

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 (47) hide show
  1. package/dest/archiver.d.ts +3 -2
  2. package/dest/archiver.d.ts.map +1 -1
  3. package/dest/archiver.js +15 -2
  4. package/dest/factory.d.ts +3 -1
  5. package/dest/factory.d.ts.map +1 -1
  6. package/dest/factory.js +2 -3
  7. package/dest/l1/validate_trace.d.ts +6 -3
  8. package/dest/l1/validate_trace.d.ts.map +1 -1
  9. package/dest/l1/validate_trace.js +13 -9
  10. package/dest/modules/instrumentation.d.ts +1 -1
  11. package/dest/modules/instrumentation.d.ts.map +1 -1
  12. package/dest/modules/instrumentation.js +17 -10
  13. package/dest/modules/l1_synchronizer.d.ts +1 -1
  14. package/dest/modules/l1_synchronizer.d.ts.map +1 -1
  15. package/dest/modules/l1_synchronizer.js +2 -3
  16. package/dest/store/block_store.d.ts +4 -4
  17. package/dest/store/block_store.d.ts.map +1 -1
  18. package/dest/store/block_store.js +2 -2
  19. package/dest/store/kv_archiver_store.d.ts +1 -1
  20. package/dest/store/kv_archiver_store.d.ts.map +1 -1
  21. package/dest/store/kv_archiver_store.js +3 -3
  22. package/dest/store/log_store.d.ts +1 -1
  23. package/dest/store/log_store.d.ts.map +1 -1
  24. package/dest/store/log_store.js +2 -2
  25. package/dest/test/index.js +3 -1
  26. package/dest/test/mock_l2_block_source.d.ts +3 -3
  27. package/dest/test/mock_l2_block_source.d.ts.map +1 -1
  28. package/dest/test/mock_l2_block_source.js +3 -3
  29. package/dest/test/mock_structs.d.ts +3 -2
  30. package/dest/test/mock_structs.d.ts.map +1 -1
  31. package/dest/test/mock_structs.js +7 -5
  32. package/dest/test/noop_l1_archiver.d.ts +23 -0
  33. package/dest/test/noop_l1_archiver.d.ts.map +1 -0
  34. package/dest/test/noop_l1_archiver.js +68 -0
  35. package/package.json +14 -13
  36. package/src/archiver.ts +22 -2
  37. package/src/factory.ts +3 -3
  38. package/src/l1/validate_trace.ts +24 -6
  39. package/src/modules/instrumentation.ts +15 -10
  40. package/src/modules/l1_synchronizer.ts +2 -3
  41. package/src/store/block_store.ts +4 -4
  42. package/src/store/kv_archiver_store.ts +3 -3
  43. package/src/store/log_store.ts +4 -4
  44. package/src/test/index.ts +3 -0
  45. package/src/test/mock_l2_block_source.ts +3 -3
  46. package/src/test/mock_structs.ts +22 -6
  47. package/src/test/noop_l1_archiver.ts +109 -0
@@ -10,6 +10,7 @@ import {
10
10
  type TelemetryClient,
11
11
  type Tracer,
12
12
  type UpDownCounter,
13
+ createUpDownCounterWithDefault,
13
14
  } from '@aztec/telemetry-client';
14
15
 
15
16
  export class ArchiverInstrumentation {
@@ -48,15 +49,17 @@ export class ArchiverInstrumentation {
48
49
 
49
50
  this.l1BlockHeight = meter.createGauge(Metrics.ARCHIVER_L1_BLOCK_HEIGHT);
50
51
 
51
- this.txCount = meter.createUpDownCounter(Metrics.ARCHIVER_TOTAL_TXS);
52
+ this.txCount = createUpDownCounterWithDefault(meter, Metrics.ARCHIVER_TOTAL_TXS);
52
53
 
53
- this.proofsSubmittedCount = meter.createUpDownCounter(Metrics.ARCHIVER_ROLLUP_PROOF_COUNT);
54
+ this.proofsSubmittedCount = createUpDownCounterWithDefault(meter, Metrics.ARCHIVER_ROLLUP_PROOF_COUNT, {
55
+ [Attributes.PROOF_TIMED_OUT]: [true, false],
56
+ });
54
57
 
55
58
  this.proofsSubmittedDelay = meter.createHistogram(Metrics.ARCHIVER_ROLLUP_PROOF_DELAY);
56
59
 
57
60
  this.syncDurationPerBlock = meter.createHistogram(Metrics.ARCHIVER_SYNC_PER_BLOCK);
58
61
 
59
- this.syncBlockCount = meter.createUpDownCounter(Metrics.ARCHIVER_SYNC_BLOCK_COUNT);
62
+ this.syncBlockCount = createUpDownCounterWithDefault(meter, Metrics.ARCHIVER_SYNC_BLOCK_COUNT);
60
63
 
61
64
  this.manaPerBlock = meter.createHistogram(Metrics.ARCHIVER_MANA_PER_BLOCK);
62
65
 
@@ -64,13 +67,19 @@ export class ArchiverInstrumentation {
64
67
 
65
68
  this.syncDurationPerMessage = meter.createHistogram(Metrics.ARCHIVER_SYNC_PER_MESSAGE);
66
69
 
67
- this.syncMessageCount = meter.createUpDownCounter(Metrics.ARCHIVER_SYNC_MESSAGE_COUNT);
70
+ this.syncMessageCount = createUpDownCounterWithDefault(meter, Metrics.ARCHIVER_SYNC_MESSAGE_COUNT);
68
71
 
69
72
  this.pruneDuration = meter.createHistogram(Metrics.ARCHIVER_PRUNE_DURATION);
70
73
 
71
- this.pruneCount = meter.createUpDownCounter(Metrics.ARCHIVER_PRUNE_COUNT);
74
+ this.pruneCount = createUpDownCounterWithDefault(meter, Metrics.ARCHIVER_PRUNE_COUNT);
72
75
 
73
- this.blockProposalTxTargetCount = meter.createUpDownCounter(Metrics.ARCHIVER_BLOCK_PROPOSAL_TX_TARGET_COUNT);
76
+ this.blockProposalTxTargetCount = createUpDownCounterWithDefault(
77
+ meter,
78
+ Metrics.ARCHIVER_BLOCK_PROPOSAL_TX_TARGET_COUNT,
79
+ {
80
+ [Attributes.L1_BLOCK_PROPOSAL_USED_TRACE]: [true, false],
81
+ },
82
+ );
74
83
 
75
84
  this.dbMetrics = new LmdbMetrics(
76
85
  meter,
@@ -84,10 +93,6 @@ export class ArchiverInstrumentation {
84
93
  public static async new(telemetry: TelemetryClient, lmdbStats?: LmdbStatsCallback) {
85
94
  const instance = new ArchiverInstrumentation(telemetry, lmdbStats);
86
95
 
87
- instance.syncBlockCount.add(0);
88
- instance.syncMessageCount.add(0);
89
- instance.pruneCount.add(0);
90
-
91
96
  await instance.telemetry.flush();
92
97
 
93
98
  return instance;
@@ -16,7 +16,7 @@ import { DateProvider, Timer, elapsed } from '@aztec/foundation/timer';
16
16
  import { isDefined } from '@aztec/foundation/types';
17
17
  import { type ArchiverEmitter, L2BlockSourceEvents, type ValidateCheckpointResult } from '@aztec/stdlib/block';
18
18
  import { PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
19
- import { type L1RollupConstants, getEpochAtSlot, getSlotAtTimestamp } from '@aztec/stdlib/epoch-helpers';
19
+ import { type L1RollupConstants, getEpochAtSlot, getSlotAtNextL1Block } from '@aztec/stdlib/epoch-helpers';
20
20
  import { computeInHashFromL1ToL2Messages } from '@aztec/stdlib/messaging';
21
21
  import { type Traceable, type Tracer, execInSpan, trackSpan } from '@aztec/telemetry-client';
22
22
 
@@ -249,8 +249,7 @@ export class ArchiverL1Synchronizer implements Traceable {
249
249
  const firstUncheckpointedBlockSlot = firstUncheckpointedBlockHeader?.getSlot();
250
250
 
251
251
  // What's the slot at the next L1 block? All blocks for slots strictly before this one should've been checkpointed by now.
252
- const nextL1BlockTimestamp = currentL1Timestamp + BigInt(this.l1Constants.ethereumSlotDuration);
253
- const slotAtNextL1Block = getSlotAtTimestamp(nextL1BlockTimestamp, this.l1Constants);
252
+ const slotAtNextL1Block = getSlotAtNextL1Block(currentL1Timestamp, this.l1Constants);
254
253
 
255
254
  // Prune provisional blocks from slots that have ended without being checkpointed
256
255
  if (firstUncheckpointedBlockSlot !== undefined && firstUncheckpointedBlockSlot < slotAtNextL1Block) {
@@ -9,11 +9,11 @@ import { isDefined } from '@aztec/foundation/types';
9
9
  import type { AztecAsyncKVStore, AztecAsyncMap, AztecAsyncSingleton, Range } from '@aztec/kv-store';
10
10
  import type { AztecAddress } from '@aztec/stdlib/aztec-address';
11
11
  import {
12
+ BlockHash,
12
13
  Body,
13
14
  CheckpointedL2Block,
14
15
  CommitteeAttestation,
15
16
  L2Block,
16
- L2BlockHash,
17
17
  type ValidateCheckpointResult,
18
18
  deserializeValidateCheckpointResult,
19
19
  serializeValidateCheckpointResult,
@@ -351,7 +351,7 @@ export class BlockStore {
351
351
  }
352
352
 
353
353
  private async addBlockToDatabase(block: L2Block, checkpointNumber: number, indexWithinCheckpoint: number) {
354
- const blockHash = L2BlockHash.fromField(await block.hash());
354
+ const blockHash = BlockHash.fromField(await block.hash());
355
355
 
356
356
  await this.#blocks.set(block.number, {
357
357
  header: block.header.toBuffer(),
@@ -673,7 +673,7 @@ export class BlockStore {
673
673
  * @param blockHash - The hash of the block to return.
674
674
  * @returns The requested L2 block.
675
675
  */
676
- async getBlockByHash(blockHash: L2BlockHash): Promise<L2Block | undefined> {
676
+ async getBlockByHash(blockHash: BlockHash): Promise<L2Block | undefined> {
677
677
  const blockNumber = await this.#blockHashIndex.getAsync(blockHash.toString());
678
678
  if (blockNumber === undefined) {
679
679
  return undefined;
@@ -699,7 +699,7 @@ export class BlockStore {
699
699
  * @param blockHash - The hash of the block to return.
700
700
  * @returns The requested block header.
701
701
  */
702
- async getBlockHeaderByHash(blockHash: L2BlockHash): Promise<BlockHeader | undefined> {
702
+ async getBlockHeaderByHash(blockHash: BlockHash): Promise<BlockHeader | undefined> {
703
703
  const blockNumber = await this.#blockHashIndex.getAsync(blockHash.toString());
704
704
  if (blockNumber === undefined) {
705
705
  return undefined;
@@ -6,7 +6,7 @@ import { createLogger } from '@aztec/foundation/log';
6
6
  import type { AztecAsyncKVStore, CustomRange, StoreSize } from '@aztec/kv-store';
7
7
  import { FunctionSelector } from '@aztec/stdlib/abi';
8
8
  import type { AztecAddress } from '@aztec/stdlib/aztec-address';
9
- import { CheckpointedL2Block, L2Block, L2BlockHash, type ValidateCheckpointResult } from '@aztec/stdlib/block';
9
+ import { BlockHash, CheckpointedL2Block, L2Block, type ValidateCheckpointResult } from '@aztec/stdlib/block';
10
10
  import type { PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
11
11
  import type {
12
12
  ContractClassPublic,
@@ -313,7 +313,7 @@ export class KVArchiverDataStore implements ContractDataSource {
313
313
  * @param blockHash - The block hash to return.
314
314
  */
315
315
  getBlockByHash(blockHash: Fr): Promise<L2Block | undefined> {
316
- return this.#blockStore.getBlockByHash(L2BlockHash.fromField(blockHash));
316
+ return this.#blockStore.getBlockByHash(BlockHash.fromField(blockHash));
317
317
  }
318
318
  /**
319
319
  * Returns the block for the given archive root, or undefined if not exists.
@@ -358,7 +358,7 @@ export class KVArchiverDataStore implements ContractDataSource {
358
358
  * @param blockHash - The block hash to return.
359
359
  */
360
360
  getBlockHeaderByHash(blockHash: Fr): Promise<BlockHeader | undefined> {
361
- return this.#blockStore.getBlockHeaderByHash(L2BlockHash.fromField(blockHash));
361
+ return this.#blockStore.getBlockHeaderByHash(BlockHash.fromField(blockHash));
362
362
  }
363
363
 
364
364
  /**
@@ -6,7 +6,7 @@ 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 {
@@ -275,14 +275,14 @@ export class LogStore {
275
275
  return Buffer.concat([blockHash.toBuffer(), ...data]);
276
276
  }
277
277
 
278
- #unpackBlockHash(reader: BufferReader): L2BlockHash {
278
+ #unpackBlockHash(reader: BufferReader): BlockHash {
279
279
  const blockHash = reader.remainingBytes() > 0 ? reader.readObject(Fr) : undefined;
280
280
 
281
281
  if (!blockHash) {
282
282
  throw new Error('Failed to read block hash from log entry buffer');
283
283
  }
284
284
 
285
- return L2BlockHash.fromField(blockHash);
285
+ return BlockHash.fromField(blockHash);
286
286
  }
287
287
 
288
288
  deleteLogs(blocks: L2Block[]): Promise<boolean> {
@@ -543,7 +543,7 @@ export class LogStore {
543
543
  #accumulateLogs(
544
544
  results: (ExtendedContractClassLog | ExtendedPublicLog)[],
545
545
  blockNumber: number,
546
- blockHash: L2BlockHash,
546
+ blockHash: BlockHash,
547
547
  txIndex: number,
548
548
  txLogs: (ContractClassLog | PublicLog)[],
549
549
  filter: LogFilter = {},
package/src/test/index.ts CHANGED
@@ -2,3 +2,6 @@ export * from './mock_structs.js';
2
2
  export * from './mock_l2_block_source.js';
3
3
  export * from './mock_l1_to_l2_message_source.js';
4
4
  export * from './mock_archiver.js';
5
+ // NOTE: noop_l1_archiver.js is intentionally NOT exported here because it imports
6
+ // jest-mock-extended, which depends on @jest/globals and can only run inside Jest.
7
+ // Import it directly: import { NoopL1Archiver } from '@aztec/archiver/test/noop-l1';
@@ -8,9 +8,9 @@ import { createLogger } from '@aztec/foundation/log';
8
8
  import type { FunctionSelector } from '@aztec/stdlib/abi';
9
9
  import type { AztecAddress } from '@aztec/stdlib/aztec-address';
10
10
  import {
11
+ BlockHash,
11
12
  CheckpointedL2Block,
12
13
  L2Block,
13
- L2BlockHash,
14
14
  type L2BlockSource,
15
15
  type L2Tips,
16
16
  type ValidateCheckpointResult,
@@ -322,7 +322,7 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
322
322
  return {
323
323
  data: txEffect,
324
324
  l2BlockNumber: block.number,
325
- l2BlockHash: L2BlockHash.fromField(await block.hash()),
325
+ l2BlockHash: BlockHash.fromField(await block.hash()),
326
326
  txIndexInBlock: block.body.txEffects.indexOf(txEffect),
327
327
  };
328
328
  }
@@ -343,7 +343,7 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
343
343
  TxExecutionResult.SUCCESS,
344
344
  undefined,
345
345
  txEffect.transactionFee.toBigInt(),
346
- L2BlockHash.fromField(await block.hash()),
346
+ BlockHash.fromField(await block.hash()),
347
347
  block.number,
348
348
  );
349
349
  }
@@ -46,24 +46,40 @@ export function makeInboxMessage(
46
46
  }
47
47
 
48
48
  export function makeInboxMessages(
49
- count: number,
49
+ totalCount: number,
50
50
  opts: {
51
51
  initialHash?: Buffer16;
52
52
  initialCheckpointNumber?: CheckpointNumber;
53
+ messagesPerCheckpoint?: number;
53
54
  overrideFn?: (msg: InboxMessage, index: number) => InboxMessage;
54
55
  } = {},
55
56
  ): InboxMessage[] {
56
- const { initialHash = Buffer16.ZERO, overrideFn = msg => msg, initialCheckpointNumber = 1 } = opts;
57
+ const {
58
+ initialHash = Buffer16.ZERO,
59
+ overrideFn = msg => msg,
60
+ initialCheckpointNumber = CheckpointNumber(1),
61
+ messagesPerCheckpoint = 1,
62
+ } = opts;
63
+
57
64
  const messages: InboxMessage[] = [];
58
65
  let rollingHash = initialHash;
59
- for (let i = 0; i < count; i++) {
66
+ for (let i = 0; i < totalCount; i++) {
67
+ const msgIndex = i % messagesPerCheckpoint;
68
+ const checkpointNumber = CheckpointNumber.fromBigInt(
69
+ BigInt(initialCheckpointNumber) + BigInt(i) / BigInt(messagesPerCheckpoint),
70
+ );
60
71
  const leaf = Fr.random();
61
- const checkpointNumber = CheckpointNumber(i + initialCheckpointNumber);
62
- const message = overrideFn(makeInboxMessage(rollingHash, { leaf, checkpointNumber }), i);
72
+ const message = overrideFn(
73
+ makeInboxMessage(rollingHash, {
74
+ leaf,
75
+ checkpointNumber,
76
+ index: InboxLeaf.smallestIndexForCheckpoint(checkpointNumber) + BigInt(msgIndex),
77
+ }),
78
+ i,
79
+ );
63
80
  rollingHash = message.rollingHash;
64
81
  messages.push(message);
65
82
  }
66
-
67
83
  return messages;
68
84
  }
69
85
 
@@ -0,0 +1,109 @@
1
+ import type { BlobClientInterface } from '@aztec/blob-client/client';
2
+ import type { RollupContract } from '@aztec/ethereum/contracts';
3
+ import type { ViemPublicClient, ViemPublicDebugClient } from '@aztec/ethereum/types';
4
+ import { Buffer32 } from '@aztec/foundation/buffer';
5
+ import { Fr } from '@aztec/foundation/curves/bn254';
6
+ import { EthAddress } from '@aztec/foundation/eth-address';
7
+ import type { FunctionsOf } from '@aztec/foundation/types';
8
+ import type { ArchiverEmitter } from '@aztec/stdlib/block';
9
+ import type { L1RollupConstants } from '@aztec/stdlib/epoch-helpers';
10
+ import { type TelemetryClient, type Tracer, getTelemetryClient } from '@aztec/telemetry-client';
11
+
12
+ import { mock } from 'jest-mock-extended';
13
+ import { EventEmitter } from 'node:events';
14
+
15
+ import { Archiver } from '../archiver.js';
16
+ import { ArchiverInstrumentation } from '../modules/instrumentation.js';
17
+ import type { ArchiverL1Synchronizer } from '../modules/l1_synchronizer.js';
18
+ import type { KVArchiverDataStore } from '../store/kv_archiver_store.js';
19
+
20
+ /** Noop L1 synchronizer for testing without L1 connectivity. */
21
+ class NoopL1Synchronizer implements FunctionsOf<ArchiverL1Synchronizer> {
22
+ public readonly tracer: Tracer;
23
+
24
+ constructor(tracer: Tracer) {
25
+ this.tracer = tracer;
26
+ }
27
+
28
+ setConfig(_config: unknown) {}
29
+ getL1BlockNumber(): bigint | undefined {
30
+ return 0n;
31
+ }
32
+ getL1Timestamp(): bigint | undefined {
33
+ return 0n;
34
+ }
35
+ testEthereumNodeSynced(): Promise<void> {
36
+ return Promise.resolve();
37
+ }
38
+ syncFromL1(_initialSyncComplete: boolean): Promise<void> {
39
+ return Promise.resolve();
40
+ }
41
+ }
42
+
43
+ /**
44
+ * Archiver with mocked L1 connectivity for testing.
45
+ * Uses mock L1 clients and a noop synchronizer, enabling tests that
46
+ * don't require real Ethereum connectivity.
47
+ */
48
+ export class NoopL1Archiver extends Archiver {
49
+ constructor(
50
+ dataStore: KVArchiverDataStore,
51
+ l1Constants: L1RollupConstants & { genesisArchiveRoot: Fr },
52
+ instrumentation: ArchiverInstrumentation,
53
+ ) {
54
+ // Create mocks for L1 clients
55
+ const publicClient = mock<ViemPublicClient>();
56
+ const debugClient = mock<ViemPublicDebugClient>();
57
+ const rollup = mock<RollupContract>();
58
+ const blobClient = mock<BlobClientInterface>();
59
+
60
+ // Mock methods called during start()
61
+ blobClient.testSources.mockResolvedValue();
62
+ publicClient.getBlockNumber.mockResolvedValue(1n);
63
+
64
+ const events = new EventEmitter() as ArchiverEmitter;
65
+ const synchronizer = new NoopL1Synchronizer(instrumentation.tracer);
66
+
67
+ super(
68
+ publicClient,
69
+ debugClient,
70
+ rollup,
71
+ {
72
+ registryAddress: EthAddress.ZERO,
73
+ governanceProposerAddress: EthAddress.ZERO,
74
+ slashFactoryAddress: EthAddress.ZERO,
75
+ slashingProposerAddress: EthAddress.ZERO,
76
+ },
77
+ dataStore,
78
+ {
79
+ pollingIntervalMs: 1000,
80
+ batchSize: 100,
81
+ skipValidateCheckpointAttestations: true,
82
+ maxAllowedEthClientDriftSeconds: 300,
83
+ ethereumAllowNoDebugHosts: true, // Skip trace validation
84
+ },
85
+ blobClient,
86
+ instrumentation,
87
+ { ...l1Constants, l1StartBlockHash: Buffer32.random() },
88
+ synchronizer as ArchiverL1Synchronizer,
89
+ events,
90
+ );
91
+ }
92
+
93
+ /** Override start to skip L1 validation checks. */
94
+ public override start(_blockUntilSynced?: boolean): Promise<void> {
95
+ // Just start the running promise without L1 checks
96
+ this.runningPromise.start();
97
+ return Promise.resolve();
98
+ }
99
+ }
100
+
101
+ /** Creates an archiver with mocked L1 connectivity for testing. */
102
+ export async function createNoopL1Archiver(
103
+ dataStore: KVArchiverDataStore,
104
+ l1Constants: L1RollupConstants & { genesisArchiveRoot: Fr },
105
+ telemetry: TelemetryClient = getTelemetryClient(),
106
+ ): Promise<NoopL1Archiver> {
107
+ const instrumentation = await ArchiverInstrumentation.new(telemetry, () => dataStore.estimateSize());
108
+ return new NoopL1Archiver(dataStore, l1Constants, instrumentation);
109
+ }