@aztec/archiver 3.0.0-nightly.20251209 → 3.0.0-nightly.20251211

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 (108) hide show
  1. package/dest/archiver/archiver.d.ts +16 -14
  2. package/dest/archiver/archiver.d.ts.map +1 -1
  3. package/dest/archiver/archiver.js +59 -28
  4. package/dest/archiver/archiver_store.d.ts +9 -9
  5. package/dest/archiver/archiver_store.d.ts.map +1 -1
  6. package/dest/archiver/archiver_store_test_suite.d.ts +1 -1
  7. package/dest/archiver/archiver_store_test_suite.d.ts.map +1 -1
  8. package/dest/archiver/archiver_store_test_suite.js +47 -46
  9. package/dest/archiver/config.d.ts +3 -2
  10. package/dest/archiver/config.d.ts.map +1 -1
  11. package/dest/archiver/config.js +8 -1
  12. package/dest/archiver/instrumentation.d.ts +3 -1
  13. package/dest/archiver/instrumentation.d.ts.map +1 -1
  14. package/dest/archiver/instrumentation.js +11 -0
  15. package/dest/archiver/kv_archiver_store/block_store.d.ts +2 -2
  16. package/dest/archiver/kv_archiver_store/block_store.d.ts.map +1 -1
  17. package/dest/archiver/kv_archiver_store/block_store.js +1 -1
  18. package/dest/archiver/kv_archiver_store/contract_class_store.d.ts +2 -2
  19. package/dest/archiver/kv_archiver_store/contract_class_store.d.ts.map +1 -1
  20. package/dest/archiver/kv_archiver_store/contract_class_store.js +1 -1
  21. package/dest/archiver/kv_archiver_store/contract_instance_store.d.ts +2 -2
  22. package/dest/archiver/kv_archiver_store/contract_instance_store.d.ts.map +1 -1
  23. package/dest/archiver/kv_archiver_store/kv_archiver_store.d.ts +8 -8
  24. package/dest/archiver/kv_archiver_store/kv_archiver_store.d.ts.map +1 -1
  25. package/dest/archiver/kv_archiver_store/kv_archiver_store.js +6 -6
  26. package/dest/archiver/kv_archiver_store/log_store.d.ts +2 -2
  27. package/dest/archiver/kv_archiver_store/log_store.d.ts.map +1 -1
  28. package/dest/archiver/kv_archiver_store/message_store.d.ts +6 -5
  29. package/dest/archiver/kv_archiver_store/message_store.d.ts.map +1 -1
  30. package/dest/archiver/kv_archiver_store/message_store.js +15 -14
  31. package/dest/archiver/l1/bin/retrieve-calldata.d.ts +3 -0
  32. package/dest/archiver/l1/bin/retrieve-calldata.d.ts.map +1 -0
  33. package/dest/archiver/l1/bin/retrieve-calldata.js +147 -0
  34. package/dest/archiver/l1/calldata_retriever.d.ts +98 -0
  35. package/dest/archiver/l1/calldata_retriever.d.ts.map +1 -0
  36. package/dest/archiver/l1/calldata_retriever.js +403 -0
  37. package/dest/archiver/l1/data_retrieval.d.ts +87 -0
  38. package/dest/archiver/l1/data_retrieval.d.ts.map +1 -0
  39. package/dest/archiver/{data_retrieval.js → l1/data_retrieval.js} +26 -95
  40. package/dest/archiver/l1/debug_tx.d.ts +19 -0
  41. package/dest/archiver/l1/debug_tx.d.ts.map +1 -0
  42. package/dest/archiver/l1/debug_tx.js +73 -0
  43. package/dest/archiver/l1/spire_proposer.d.ts +70 -0
  44. package/dest/archiver/l1/spire_proposer.d.ts.map +1 -0
  45. package/dest/archiver/l1/spire_proposer.js +157 -0
  46. package/dest/archiver/l1/trace_tx.d.ts +97 -0
  47. package/dest/archiver/l1/trace_tx.d.ts.map +1 -0
  48. package/dest/archiver/l1/trace_tx.js +91 -0
  49. package/dest/archiver/l1/types.d.ts +12 -0
  50. package/dest/archiver/l1/types.d.ts.map +1 -0
  51. package/dest/archiver/l1/types.js +3 -0
  52. package/dest/archiver/l1/validate_trace.d.ts +29 -0
  53. package/dest/archiver/l1/validate_trace.d.ts.map +1 -0
  54. package/dest/archiver/l1/validate_trace.js +150 -0
  55. package/dest/archiver/structs/inbox_message.d.ts +4 -4
  56. package/dest/archiver/structs/inbox_message.d.ts.map +1 -1
  57. package/dest/archiver/structs/inbox_message.js +6 -6
  58. package/dest/index.d.ts +2 -2
  59. package/dest/index.d.ts.map +1 -1
  60. package/dest/index.js +1 -1
  61. package/dest/test/mock_archiver.d.ts +4 -5
  62. package/dest/test/mock_archiver.d.ts.map +1 -1
  63. package/dest/test/mock_archiver.js +5 -9
  64. package/dest/test/mock_l1_to_l2_message_source.d.ts +5 -6
  65. package/dest/test/mock_l1_to_l2_message_source.d.ts.map +1 -1
  66. package/dest/test/mock_l1_to_l2_message_source.js +7 -11
  67. package/dest/test/mock_l2_block_source.d.ts +2 -2
  68. package/dest/test/mock_l2_block_source.d.ts.map +1 -1
  69. package/dest/test/mock_l2_block_source.js +2 -2
  70. package/dest/test/mock_structs.d.ts +3 -2
  71. package/dest/test/mock_structs.d.ts.map +1 -1
  72. package/dest/test/mock_structs.js +9 -9
  73. package/package.json +15 -14
  74. package/src/archiver/archiver.ts +79 -40
  75. package/src/archiver/archiver_store.ts +8 -8
  76. package/src/archiver/archiver_store_test_suite.ts +56 -48
  77. package/src/archiver/config.ts +8 -7
  78. package/src/archiver/instrumentation.ts +14 -0
  79. package/src/archiver/kv_archiver_store/block_store.ts +1 -1
  80. package/src/archiver/kv_archiver_store/contract_class_store.ts +1 -1
  81. package/src/archiver/kv_archiver_store/contract_instance_store.ts +1 -1
  82. package/src/archiver/kv_archiver_store/kv_archiver_store.ts +9 -9
  83. package/src/archiver/kv_archiver_store/log_store.ts +1 -1
  84. package/src/archiver/kv_archiver_store/message_store.ts +21 -18
  85. package/src/archiver/l1/README.md +98 -0
  86. package/src/archiver/l1/bin/retrieve-calldata.ts +182 -0
  87. package/src/archiver/l1/calldata_retriever.ts +531 -0
  88. package/src/archiver/{data_retrieval.ts → l1/data_retrieval.ts} +57 -143
  89. package/src/archiver/l1/debug_tx.ts +99 -0
  90. package/src/archiver/l1/spire_proposer.ts +160 -0
  91. package/src/archiver/l1/trace_tx.ts +128 -0
  92. package/src/archiver/l1/types.ts +13 -0
  93. package/src/archiver/l1/validate_trace.ts +211 -0
  94. package/src/archiver/structs/inbox_message.ts +7 -8
  95. package/src/index.ts +1 -1
  96. package/src/test/fixtures/debug_traceTransaction-multicall3.json +88 -0
  97. package/src/test/fixtures/debug_traceTransaction-multiplePropose.json +153 -0
  98. package/src/test/fixtures/debug_traceTransaction-proxied.json +122 -0
  99. package/src/test/fixtures/trace_transaction-multicall3.json +65 -0
  100. package/src/test/fixtures/trace_transaction-multiplePropose.json +319 -0
  101. package/src/test/fixtures/trace_transaction-proxied.json +128 -0
  102. package/src/test/fixtures/trace_transaction-randomRevert.json +216 -0
  103. package/src/test/mock_archiver.ts +6 -11
  104. package/src/test/mock_l1_to_l2_message_source.ts +6 -11
  105. package/src/test/mock_l2_block_source.ts +2 -2
  106. package/src/test/mock_structs.ts +10 -10
  107. package/dest/archiver/data_retrieval.d.ts +0 -80
  108. package/dest/archiver/data_retrieval.d.ts.map +0 -1
@@ -4,11 +4,11 @@ import {
4
4
  PRIVATE_LOG_SIZE_IN_FIELDS,
5
5
  } from '@aztec/constants';
6
6
  import { makeTuple } from '@aztec/foundation/array';
7
- import { BlockNumber, EpochNumber } from '@aztec/foundation/branded-types';
7
+ import { BlockNumber, CheckpointNumber, EpochNumber } from '@aztec/foundation/branded-types';
8
8
  import { Buffer16, Buffer32 } from '@aztec/foundation/buffer';
9
9
  import { times, timesParallel } from '@aztec/foundation/collection';
10
- import { randomInt } from '@aztec/foundation/crypto';
11
- import { Fr } from '@aztec/foundation/fields';
10
+ import { randomInt } from '@aztec/foundation/crypto/random';
11
+ import { Fr } from '@aztec/foundation/curves/bn254';
12
12
  import { toArray } from '@aztec/foundation/iterable';
13
13
  import { sleep } from '@aztec/foundation/sleep';
14
14
  import { AztecAddress } from '@aztec/stdlib/aztec-address';
@@ -446,7 +446,7 @@ export function describeArchiverDataStore(
446
446
  });
447
447
 
448
448
  describe('L1 to L2 Messages', () => {
449
- const initialL2BlockNumber = 13;
449
+ const initialCheckpointNumber = CheckpointNumber(13);
450
450
 
451
451
  const checkMessages = async (msgs: InboxMessage[]) => {
452
452
  expect(await store.getLastL1ToL2Message()).toEqual(msgs.at(-1));
@@ -454,46 +454,50 @@ export function describeArchiverDataStore(
454
454
  expect(await store.getTotalL1ToL2MessageCount()).toEqual(BigInt(msgs.length));
455
455
  };
456
456
 
457
- const makeInboxMessagesWithFullBlocks = (blockCount: number, opts: { initialL2BlockNumber?: number } = {}) =>
457
+ const makeInboxMessagesWithFullBlocks = (
458
+ blockCount: number,
459
+ opts: { initialCheckpointNumber?: CheckpointNumber } = {},
460
+ ) =>
458
461
  makeInboxMessages(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP * blockCount, {
459
462
  overrideFn: (msg, i) => {
460
- const l2BlockNumber = BlockNumber(
461
- (opts.initialL2BlockNumber ?? initialL2BlockNumber) + Math.floor(i / NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP),
463
+ const checkpointNumber = CheckpointNumber(
464
+ (opts.initialCheckpointNumber ?? initialCheckpointNumber) +
465
+ Math.floor(i / NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP),
462
466
  );
463
467
  const index =
464
- InboxLeaf.smallestIndexFromL2Block(l2BlockNumber) + BigInt(i % NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP);
465
- return { ...msg, l2BlockNumber, index };
468
+ InboxLeaf.smallestIndexForCheckpoint(checkpointNumber) + BigInt(i % NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP);
469
+ return { ...msg, checkpointNumber, index };
466
470
  },
467
471
  });
468
472
 
469
473
  it('stores first message ever', async () => {
470
- const msg = makeInboxMessage(Buffer16.ZERO, { index: 0n, l2BlockNumber: BlockNumber(1) });
474
+ const msg = makeInboxMessage(Buffer16.ZERO, { index: 0n, checkpointNumber: CheckpointNumber(1) });
471
475
  await store.addL1ToL2Messages([msg]);
472
476
 
473
477
  await checkMessages([msg]);
474
- expect(await store.getL1ToL2Messages(BlockNumber(1))).toEqual([msg.leaf]);
478
+ expect(await store.getL1ToL2Messages(CheckpointNumber(1))).toEqual([msg.leaf]);
475
479
  });
476
480
 
477
481
  it('stores single message', async () => {
478
- const msg = makeInboxMessage(Buffer16.ZERO, { l2BlockNumber: BlockNumber(2) });
482
+ const msg = makeInboxMessage(Buffer16.ZERO, { checkpointNumber: CheckpointNumber(2) });
479
483
  await store.addL1ToL2Messages([msg]);
480
484
 
481
485
  await checkMessages([msg]);
482
- expect(await store.getL1ToL2Messages(BlockNumber(2))).toEqual([msg.leaf]);
486
+ expect(await store.getL1ToL2Messages(CheckpointNumber(2))).toEqual([msg.leaf]);
483
487
  });
484
488
 
485
489
  it('stores and returns messages across different blocks', async () => {
486
- const msgs = makeInboxMessages(5, { initialL2BlockNumber });
490
+ const msgs = makeInboxMessages(5, { initialCheckpointNumber });
487
491
  await store.addL1ToL2Messages(msgs);
488
492
 
489
493
  await checkMessages(msgs);
490
- expect(await store.getL1ToL2Messages(BlockNumber(initialL2BlockNumber + 2))).toEqual(
494
+ expect(await store.getL1ToL2Messages(CheckpointNumber(initialCheckpointNumber + 2))).toEqual(
491
495
  [msgs[2]].map(m => m.leaf),
492
496
  );
493
497
  });
494
498
 
495
499
  it('stores the same messages again', async () => {
496
- const msgs = makeInboxMessages(5, { initialL2BlockNumber });
500
+ const msgs = makeInboxMessages(5, { initialCheckpointNumber });
497
501
  await store.addL1ToL2Messages(msgs);
498
502
  await store.addL1ToL2Messages(msgs.slice(2));
499
503
 
@@ -501,26 +505,29 @@ export function describeArchiverDataStore(
501
505
  });
502
506
 
503
507
  it('stores and returns messages across different blocks with gaps', async () => {
504
- const msgs1 = makeInboxMessages(3, { initialL2BlockNumber: 1 });
505
- const msgs2 = makeInboxMessages(3, { initialL2BlockNumber: 20, initialHash: msgs1.at(-1)!.rollingHash });
508
+ const msgs1 = makeInboxMessages(3, { initialCheckpointNumber: CheckpointNumber(1) });
509
+ const msgs2 = makeInboxMessages(3, {
510
+ initialCheckpointNumber: CheckpointNumber(20),
511
+ initialHash: msgs1.at(-1)!.rollingHash,
512
+ });
506
513
 
507
514
  await store.addL1ToL2Messages(msgs1);
508
515
  await store.addL1ToL2Messages(msgs2);
509
516
 
510
517
  await checkMessages([...msgs1, ...msgs2]);
511
518
 
512
- expect(await store.getL1ToL2Messages(BlockNumber(1))).toEqual([msgs1[0].leaf]);
513
- expect(await store.getL1ToL2Messages(BlockNumber(4))).toEqual([]);
514
- expect(await store.getL1ToL2Messages(BlockNumber(20))).toEqual([msgs2[0].leaf]);
515
- expect(await store.getL1ToL2Messages(BlockNumber(24))).toEqual([]);
519
+ expect(await store.getL1ToL2Messages(CheckpointNumber(1))).toEqual([msgs1[0].leaf]);
520
+ expect(await store.getL1ToL2Messages(CheckpointNumber(4))).toEqual([]);
521
+ expect(await store.getL1ToL2Messages(CheckpointNumber(20))).toEqual([msgs2[0].leaf]);
522
+ expect(await store.getL1ToL2Messages(CheckpointNumber(24))).toEqual([]);
516
523
  });
517
524
 
518
525
  it('stores and returns messages with block numbers larger than a byte', async () => {
519
- const msgs = makeInboxMessages(5, { initialL2BlockNumber: 1000 });
526
+ const msgs = makeInboxMessages(5, { initialCheckpointNumber: CheckpointNumber(1000) });
520
527
  await store.addL1ToL2Messages(msgs);
521
528
 
522
529
  await checkMessages(msgs);
523
- expect(await store.getL1ToL2Messages(BlockNumber(1002))).toEqual([msgs[2]].map(m => m.leaf));
530
+ expect(await store.getL1ToL2Messages(CheckpointNumber(1002))).toEqual([msgs[2]].map(m => m.leaf));
524
531
  });
525
532
 
526
533
  it('stores and returns multiple messages per block', async () => {
@@ -528,7 +535,7 @@ export function describeArchiverDataStore(
528
535
  await store.addL1ToL2Messages(msgs);
529
536
 
530
537
  await checkMessages(msgs);
531
- const blockMessages = await store.getL1ToL2Messages(BlockNumber(initialL2BlockNumber + 1));
538
+ const blockMessages = await store.getL1ToL2Messages(CheckpointNumber(initialCheckpointNumber + 1));
532
539
  expect(blockMessages).toHaveLength(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP);
533
540
  expect(blockMessages).toEqual(
534
541
  msgs.slice(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP * 2).map(m => m.leaf),
@@ -536,21 +543,21 @@ export function describeArchiverDataStore(
536
543
  });
537
544
 
538
545
  it('stores messages in multiple operations', async () => {
539
- const msgs = makeInboxMessages(20, { initialL2BlockNumber });
546
+ const msgs = makeInboxMessages(20, { initialCheckpointNumber });
540
547
  await store.addL1ToL2Messages(msgs.slice(0, 10));
541
548
  await store.addL1ToL2Messages(msgs.slice(10, 20));
542
549
 
543
- expect(await store.getL1ToL2Messages(BlockNumber(initialL2BlockNumber + 2))).toEqual(
550
+ expect(await store.getL1ToL2Messages(CheckpointNumber(initialCheckpointNumber + 2))).toEqual(
544
551
  [msgs[2]].map(m => m.leaf),
545
552
  );
546
- expect(await store.getL1ToL2Messages(BlockNumber(initialL2BlockNumber + 12))).toEqual(
553
+ expect(await store.getL1ToL2Messages(CheckpointNumber(initialCheckpointNumber + 12))).toEqual(
547
554
  [msgs[12]].map(m => m.leaf),
548
555
  );
549
556
  await checkMessages(msgs);
550
557
  });
551
558
 
552
559
  it('iterates over messages from start index', async () => {
553
- const msgs = makeInboxMessages(10, { initialL2BlockNumber });
560
+ const msgs = makeInboxMessages(10, { initialCheckpointNumber });
554
561
  await store.addL1ToL2Messages(msgs);
555
562
 
556
563
  const iterated = await toArray(store.iterateL1ToL2Messages({ start: msgs[3].index }));
@@ -558,8 +565,9 @@ export function describeArchiverDataStore(
558
565
  });
559
566
 
560
567
  it('iterates over messages in reverse', async () => {
561
- const msgs = makeInboxMessages(10, { initialL2BlockNumber });
568
+ const msgs = makeInboxMessages(10, { initialCheckpointNumber });
562
569
  await store.addL1ToL2Messages(msgs);
570
+ initialCheckpointNumber;
563
571
 
564
572
  const iterated = await toArray(store.iterateL1ToL2Messages({ reverse: true, end: msgs[3].index }));
565
573
  expect(iterated).toEqual(msgs.slice(0, 4).reverse());
@@ -571,8 +579,8 @@ export function describeArchiverDataStore(
571
579
  });
572
580
 
573
581
  it('throws if block number for the first message is out of order', async () => {
574
- const msgs = makeInboxMessages(4, { initialL2BlockNumber });
575
- msgs[2].l2BlockNumber = BlockNumber(initialL2BlockNumber - 1);
582
+ const msgs = makeInboxMessages(4, { initialCheckpointNumber });
583
+ msgs[2].checkpointNumber = CheckpointNumber(initialCheckpointNumber - 1);
576
584
  await store.addL1ToL2Messages(msgs.slice(0, 2));
577
585
  await expect(store.addL1ToL2Messages(msgs.slice(2, 4))).rejects.toThrow(MessageStoreError);
578
586
  });
@@ -586,28 +594,28 @@ export function describeArchiverDataStore(
586
594
  it('throws if rolling hash for first message is not correct', async () => {
587
595
  const msgs = makeInboxMessages(4);
588
596
  msgs[2].rollingHash = Buffer16.random();
589
- await store.addL1ToL2Messages(msgs.slice(0, BlockNumber(2)));
597
+ await store.addL1ToL2Messages(msgs.slice(0, CheckpointNumber(2)));
590
598
  await expect(store.addL1ToL2Messages(msgs.slice(2, 4))).rejects.toThrow(MessageStoreError);
591
599
  });
592
600
 
593
601
  it('throws if index is not in the correct range', async () => {
594
- const msgs = makeInboxMessages(5, { initialL2BlockNumber });
602
+ const msgs = makeInboxMessages(5, { initialCheckpointNumber });
595
603
  msgs.at(-1)!.index += 100n;
596
604
  await expect(store.addL1ToL2Messages(msgs)).rejects.toThrow(MessageStoreError);
597
605
  });
598
606
 
599
607
  it('throws if first index in block has gaps', async () => {
600
- const msgs = makeInboxMessages(4, { initialL2BlockNumber });
608
+ const msgs = makeInboxMessages(4, { initialCheckpointNumber });
601
609
  msgs[2].index++;
602
610
  await expect(store.addL1ToL2Messages(msgs)).rejects.toThrow(MessageStoreError);
603
611
  });
604
612
 
605
613
  it('throws if index does not follow previous one', async () => {
606
614
  const msgs = makeInboxMessages(2, {
607
- initialL2BlockNumber,
615
+ initialCheckpointNumber,
608
616
  overrideFn: (msg, i) => ({
609
617
  ...msg,
610
- l2BlockNumber: BlockNumber(2),
618
+ checkpointNumber: CheckpointNumber(2),
611
619
  index: BigInt(i + NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP * 2),
612
620
  }),
613
621
  });
@@ -616,28 +624,28 @@ export function describeArchiverDataStore(
616
624
  });
617
625
 
618
626
  it('removes messages up to the given block number', async () => {
619
- const msgs = makeInboxMessagesWithFullBlocks(4, { initialL2BlockNumber: 1 });
627
+ const msgs = makeInboxMessagesWithFullBlocks(4, { initialCheckpointNumber: CheckpointNumber(1) });
620
628
 
621
629
  await store.addL1ToL2Messages(msgs);
622
630
  await checkMessages(msgs);
623
631
 
624
- expect(await store.getL1ToL2Messages(BlockNumber(1))).toHaveLength(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP);
625
- expect(await store.getL1ToL2Messages(BlockNumber(2))).toHaveLength(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP);
626
- expect(await store.getL1ToL2Messages(BlockNumber(3))).toHaveLength(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP);
627
- expect(await store.getL1ToL2Messages(BlockNumber(4))).toHaveLength(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP);
632
+ expect(await store.getL1ToL2Messages(CheckpointNumber(1))).toHaveLength(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP);
633
+ expect(await store.getL1ToL2Messages(CheckpointNumber(2))).toHaveLength(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP);
634
+ expect(await store.getL1ToL2Messages(CheckpointNumber(3))).toHaveLength(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP);
635
+ expect(await store.getL1ToL2Messages(CheckpointNumber(4))).toHaveLength(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP);
628
636
 
629
- await store.rollbackL1ToL2MessagesToL2Block(BlockNumber(2));
637
+ await store.rollbackL1ToL2MessagesToCheckpoint(CheckpointNumber(2));
630
638
 
631
- expect(await store.getL1ToL2Messages(BlockNumber(1))).toHaveLength(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP);
632
- expect(await store.getL1ToL2Messages(BlockNumber(2))).toHaveLength(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP);
633
- expect(await store.getL1ToL2Messages(BlockNumber(3))).toHaveLength(0);
634
- expect(await store.getL1ToL2Messages(BlockNumber(4))).toHaveLength(0);
639
+ expect(await store.getL1ToL2Messages(CheckpointNumber(1))).toHaveLength(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP);
640
+ expect(await store.getL1ToL2Messages(CheckpointNumber(2))).toHaveLength(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP);
641
+ expect(await store.getL1ToL2Messages(CheckpointNumber(3))).toHaveLength(0);
642
+ expect(await store.getL1ToL2Messages(CheckpointNumber(4))).toHaveLength(0);
635
643
 
636
644
  await checkMessages(msgs.slice(0, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP * 2));
637
645
  });
638
646
 
639
647
  it('removes messages starting with the given index', async () => {
640
- const msgs = makeInboxMessagesWithFullBlocks(4, { initialL2BlockNumber: 1 });
648
+ const msgs = makeInboxMessagesWithFullBlocks(4, { initialCheckpointNumber: CheckpointNumber(1) });
641
649
  await store.addL1ToL2Messages(msgs);
642
650
 
643
651
  await store.removeL1ToL2Messages(msgs[13].index);
@@ -1,11 +1,7 @@
1
1
  import { type BlobSinkConfig, blobSinkConfigMapping } from '@aztec/blob-sink/client';
2
- import {
3
- type L1ContractsConfig,
4
- type L1ReaderConfig,
5
- l1ContractAddressesMapping,
6
- l1ContractsConfigMappings,
7
- l1ReaderConfigMappings,
8
- } from '@aztec/ethereum';
2
+ import { type L1ContractsConfig, l1ContractsConfigMappings } from '@aztec/ethereum/config';
3
+ import { l1ContractAddressesMapping } from '@aztec/ethereum/l1-contract-addresses';
4
+ import { type L1ReaderConfig, l1ReaderConfigMappings } from '@aztec/ethereum/l1-reader';
9
5
  import {
10
6
  type ConfigMappingsType,
11
7
  booleanConfigHelper,
@@ -55,6 +51,11 @@ export const archiverConfigMappings: ConfigMappingsType<ArchiverConfig> = {
55
51
  description: 'Maximum allowed drift in seconds between the Ethereum client and current time.',
56
52
  ...numberConfigHelper(300),
57
53
  },
54
+ ethereumAllowNoDebugHosts: {
55
+ env: 'ETHEREUM_ALLOW_NO_DEBUG_HOSTS',
56
+ description: 'Whether to allow starting the archiver without debug/trace method support on Ethereum hosts',
57
+ ...booleanConfigHelper(true),
58
+ },
58
59
  ...chainConfigMappings,
59
60
  ...l1ReaderConfigMappings,
60
61
  viemPollingIntervalMS: {
@@ -34,6 +34,8 @@ export class ArchiverInstrumentation {
34
34
  private syncDurationPerMessage: Histogram;
35
35
  private syncMessageCount: UpDownCounter;
36
36
 
37
+ private blockProposalTxTargetCount: UpDownCounter;
38
+
37
39
  private log = createLogger('archiver:instrumentation');
38
40
 
39
41
  private constructor(
@@ -114,6 +116,11 @@ export class ArchiverInstrumentation {
114
116
  valueType: ValueType.INT,
115
117
  });
116
118
 
119
+ this.blockProposalTxTargetCount = meter.createUpDownCounter(Metrics.ARCHIVER_BLOCK_PROPOSAL_TX_TARGET_COUNT, {
120
+ description: 'Number of block proposals by tx target',
121
+ valueType: ValueType.INT,
122
+ });
123
+
117
124
  this.dbMetrics = new LmdbMetrics(
118
125
  meter,
119
126
  {
@@ -184,4 +191,11 @@ export class ArchiverInstrumentation {
184
191
  public updateL1BlockHeight(blockNumber: bigint) {
185
192
  this.l1BlockHeight.record(Number(blockNumber));
186
193
  }
194
+
195
+ public recordBlockProposalTxTarget(target: string, usedTrace: boolean) {
196
+ this.blockProposalTxTargetCount.add(1, {
197
+ [Attributes.L1_BLOCK_PROPOSAL_TX_TARGET]: target.toLowerCase(),
198
+ [Attributes.L1_BLOCK_PROPOSAL_USED_TRACE]: usedTrace,
199
+ });
200
+ }
187
201
  }
@@ -1,6 +1,6 @@
1
1
  import { INITIAL_L2_BLOCK_NUM } from '@aztec/constants';
2
2
  import { BlockNumber } from '@aztec/foundation/branded-types';
3
- import { Fr } from '@aztec/foundation/fields';
3
+ import { Fr } from '@aztec/foundation/curves/bn254';
4
4
  import { toArray } from '@aztec/foundation/iterable';
5
5
  import { createLogger } from '@aztec/foundation/log';
6
6
  import { BufferReader } from '@aztec/foundation/serialize';
@@ -1,4 +1,4 @@
1
- import { Fr } from '@aztec/foundation/fields';
1
+ import { Fr } from '@aztec/foundation/curves/bn254';
2
2
  import { toArray } from '@aztec/foundation/iterable';
3
3
  import { BufferReader, numToUInt8, serializeToBuffer } from '@aztec/foundation/serialize';
4
4
  import type { AztecAsyncKVStore, AztecAsyncMap } from '@aztec/kv-store';
@@ -1,4 +1,4 @@
1
- import type { Fr } from '@aztec/foundation/fields';
1
+ import type { Fr } from '@aztec/foundation/curves/bn254';
2
2
  import type { AztecAsyncKVStore, AztecAsyncMap } from '@aztec/kv-store';
3
3
  import type { AztecAddress } from '@aztec/stdlib/aztec-address';
4
4
  import {
@@ -1,6 +1,6 @@
1
- import type { L1BlockId } from '@aztec/ethereum';
2
- import { BlockNumber } from '@aztec/foundation/branded-types';
3
- import type { Fr } from '@aztec/foundation/fields';
1
+ import type { L1BlockId } from '@aztec/ethereum/l1-types';
2
+ import type { BlockNumber, CheckpointNumber } from '@aztec/foundation/branded-types';
3
+ import type { Fr } from '@aztec/foundation/curves/bn254';
4
4
  import { toArray } from '@aztec/foundation/iterable';
5
5
  import { createLogger } from '@aztec/foundation/log';
6
6
  import type { AztecAsyncKVStore, CustomRange, StoreSize } from '@aztec/kv-store';
@@ -300,12 +300,12 @@ export class KVArchiverDataStore implements ArchiverDataStore, ContractDataSourc
300
300
  }
301
301
 
302
302
  /**
303
- * Gets L1 to L2 message (to be) included in a given block.
304
- * @param blockNumber - L2 block number to get messages for.
303
+ * Gets L1 to L2 message (to be) included in a given checkpoint.
304
+ * @param checkpointNumber - Checkpoint number to get messages for.
305
305
  * @returns The L1 to L2 messages/leaves of the messages subtree (throws if not found).
306
306
  */
307
- getL1ToL2Messages(blockNumber: BlockNumber): Promise<Fr[]> {
308
- return this.#messageStore.getL1ToL2Messages(blockNumber);
307
+ getL1ToL2Messages(checkpointNumber: CheckpointNumber): Promise<Fr[]> {
308
+ return this.#messageStore.getL1ToL2Messages(checkpointNumber);
309
309
  }
310
310
 
311
311
  /**
@@ -401,8 +401,8 @@ export class KVArchiverDataStore implements ArchiverDataStore, ContractDataSourc
401
401
  return this.db.estimateSize();
402
402
  }
403
403
 
404
- public rollbackL1ToL2MessagesToL2Block(targetBlockNumber: BlockNumber): Promise<void> {
405
- return this.#messageStore.rollbackL1ToL2MessagesToL2Block(targetBlockNumber);
404
+ public rollbackL1ToL2MessagesToCheckpoint(targetCheckpointNumber: CheckpointNumber): Promise<void> {
405
+ return this.#messageStore.rollbackL1ToL2MessagesToCheckpoint(targetCheckpointNumber);
406
406
  }
407
407
 
408
408
  public iterateL1ToL2Messages(range: CustomRange<bigint> = {}): AsyncIterableIterator<InboxMessage> {
@@ -1,6 +1,6 @@
1
1
  import { INITIAL_L2_BLOCK_NUM, MAX_NOTE_HASHES_PER_TX } from '@aztec/constants';
2
2
  import { BlockNumber } from '@aztec/foundation/branded-types';
3
- import type { Fr } from '@aztec/foundation/fields';
3
+ import type { Fr } from '@aztec/foundation/curves/bn254';
4
4
  import { createLogger } from '@aztec/foundation/log';
5
5
  import { BufferReader, numToUInt32BE } from '@aztec/foundation/serialize';
6
6
  import type { AztecAsyncKVStore, AztecAsyncMap } from '@aztec/kv-store';
@@ -1,6 +1,7 @@
1
- import type { L1BlockId } from '@aztec/ethereum';
1
+ import type { L1BlockId } from '@aztec/ethereum/l1-types';
2
+ import { CheckpointNumber } from '@aztec/foundation/branded-types';
2
3
  import { Buffer16, Buffer32 } from '@aztec/foundation/buffer';
3
- import { Fr } from '@aztec/foundation/fields';
4
+ import { Fr } from '@aztec/foundation/curves/bn254';
4
5
  import { toArray } from '@aztec/foundation/iterable';
5
6
  import { createLogger } from '@aztec/foundation/log';
6
7
  import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize';
@@ -113,20 +114,20 @@ export class MessageStore {
113
114
  );
114
115
  }
115
116
 
116
- // Check index corresponds to the L2 block number.
117
- const [expectedStart, expectedEnd] = InboxLeaf.indexRangeFromL2Block(message.l2BlockNumber);
117
+ // Check index corresponds to the checkpoint number.
118
+ const [expectedStart, expectedEnd] = InboxLeaf.indexRangeForCheckpoint(message.checkpointNumber);
118
119
  if (message.index < expectedStart || message.index >= expectedEnd) {
119
120
  throw new MessageStoreError(
120
121
  `Invalid index ${message.index} for incoming L1 to L2 message ${message.leaf.toString()} ` +
121
- `at block ${message.l2BlockNumber} (expected value in range [${expectedStart}, ${expectedEnd}))`,
122
+ `at checkpoint ${message.checkpointNumber} (expected value in range [${expectedStart}, ${expectedEnd}))`,
122
123
  message,
123
124
  );
124
125
  }
125
126
 
126
- // Check there are no gaps in the indices within the same block.
127
+ // Check there are no gaps in the indices within the same checkpoint.
127
128
  if (
128
129
  lastMessage &&
129
- message.l2BlockNumber === lastMessage.l2BlockNumber &&
130
+ message.checkpointNumber === lastMessage.checkpointNumber &&
130
131
  message.index !== lastMessage.index + 1n
131
132
  ) {
132
133
  throw new MessageStoreError(
@@ -138,12 +139,12 @@ export class MessageStore {
138
139
 
139
140
  // Check the first message in a block has the correct index.
140
141
  if (
141
- (!lastMessage || message.l2BlockNumber > lastMessage.l2BlockNumber) &&
142
- message.index !== InboxLeaf.smallestIndexFromL2Block(message.l2BlockNumber)
142
+ (!lastMessage || message.checkpointNumber > lastMessage.checkpointNumber) &&
143
+ message.index !== expectedStart
143
144
  ) {
144
145
  throw new MessageStoreError(
145
- `Message ${message.leaf.toString()} for L2 block ${message.l2BlockNumber} has wrong index ` +
146
- `${message.index} (expected ${InboxLeaf.smallestIndexFromL2Block(message.l2BlockNumber)})`,
146
+ `Message ${message.leaf.toString()} for checkpoint ${message.checkpointNumber} has wrong index ` +
147
+ `${message.index} (expected ${expectedStart})`,
147
148
  message,
148
149
  );
149
150
  }
@@ -184,10 +185,10 @@ export class MessageStore {
184
185
  return msg ? deserializeInboxMessage(msg) : undefined;
185
186
  }
186
187
 
187
- public async getL1ToL2Messages(blockNumber: number): Promise<Fr[]> {
188
+ public async getL1ToL2Messages(checkpointNumber: CheckpointNumber): Promise<Fr[]> {
188
189
  const messages: Fr[] = [];
189
190
 
190
- const [startIndex, endIndex] = InboxLeaf.indexRangeFromL2Block(blockNumber);
191
+ const [startIndex, endIndex] = InboxLeaf.indexRangeForCheckpoint(checkpointNumber);
191
192
  let lastIndex = startIndex - 1n;
192
193
 
193
194
  for await (const msgBuffer of this.#l1ToL2Messages.valuesAsync({
@@ -195,8 +196,10 @@ export class MessageStore {
195
196
  end: this.indexToKey(endIndex),
196
197
  })) {
197
198
  const msg = deserializeInboxMessage(msgBuffer);
198
- if (msg.l2BlockNumber !== blockNumber) {
199
- throw new Error(`L1 to L2 message with index ${msg.index} has invalid block number ${msg.l2BlockNumber}`);
199
+ if (msg.checkpointNumber !== checkpointNumber) {
200
+ throw new Error(
201
+ `L1 to L2 message with index ${msg.index} has invalid checkpoint number ${msg.checkpointNumber}`,
202
+ );
200
203
  } else if (msg.index !== lastIndex + 1n) {
201
204
  throw new Error(`Expected L1 to L2 message with index ${lastIndex + 1n} but got ${msg.index}`);
202
205
  }
@@ -232,9 +235,9 @@ export class MessageStore {
232
235
  });
233
236
  }
234
237
 
235
- public rollbackL1ToL2MessagesToL2Block(targetBlockNumber: number): Promise<void> {
236
- this.#log.debug(`Deleting L1 to L2 messages up to target L2 block ${targetBlockNumber}`);
237
- const startIndex = InboxLeaf.smallestIndexFromL2Block(targetBlockNumber + 1);
238
+ public rollbackL1ToL2MessagesToCheckpoint(targetCheckpointNumber: CheckpointNumber): Promise<void> {
239
+ this.#log.debug(`Deleting L1 to L2 messages up to target checkpoint ${targetCheckpointNumber}`);
240
+ const startIndex = InboxLeaf.smallestIndexForCheckpoint(CheckpointNumber(targetCheckpointNumber + 1));
238
241
  return this.removeL1ToL2Messages(startIndex);
239
242
  }
240
243
 
@@ -0,0 +1,98 @@
1
+ # Archiver L1 Data Retrieval
2
+
3
+ Modules and classes to handle data retrieval from L1 for the archiver.
4
+
5
+ ## Calldata Retriever
6
+
7
+ The sequencer publisher bundles multiple operations into a single multicall3 transaction for gas
8
+ efficiency. A typical transaction includes:
9
+
10
+ 1. Attestation invalidations (if needed): `invalidateBadAttestation`, `invalidateInsufficientAttestations`
11
+ 2. Block proposal: `propose` (exactly one per transaction to the rollup contract)
12
+ 3. Governance and slashing (if needed): votes, payload creation/execution
13
+
14
+ The archiver needs to extract the `propose` calldata from these bundled transactions to reconstruct
15
+ L2 blocks. This class needs to handle scenarios where the transaction was submitted via multicall3,
16
+ as well as alternative ways for submitting the `propose` call that other clients might use.
17
+
18
+ ### Multicall3 Validation and Decoding
19
+
20
+ First attempt to decode the transaction as a multicall3 `aggregate3` call with validation:
21
+
22
+ - Check if transaction is to multicall3 address (`0xcA11bde05977b3631167028862bE2a173976CA11`)
23
+ - Decode as `aggregate3(Call3[] calldata calls)`
24
+ - Allow calls to known addresses and methods (rollup, governance, slashing contracts, etc.)
25
+ - Find the single `propose` call to the rollup contract
26
+ - Verify exactly one `propose` call exists
27
+ - Extract and return the propose calldata
28
+
29
+ This step handles the common case efficiently without requiring expensive trace or debug RPC calls.
30
+ Any validation failure triggers fallback to the next step.
31
+
32
+ ### Direct Propose Call
33
+
34
+ Second attempt to decode the transaction as a direct `propose` call to the rollup contract:
35
+
36
+ - Check if transaction is to the rollup address
37
+ - Decode as `propose` function call
38
+ - Verify the function is indeed `propose`
39
+ - Return the transaction input as the propose calldata
40
+
41
+ This handles scenarios where clients submit transactions directly to the rollup contract without
42
+ using multicall3 for bundling. Any validation failure triggers fallback to the next step.
43
+
44
+ ### Spire Proposer Call
45
+
46
+ Given existing attempts to route the call via the Spire proposer, we also check if the tx is `to` the
47
+ proposer known address, and if so, we try decoding it as either a multicall3 or a direct call to the
48
+ rollup contract.
49
+
50
+ Similar as with the multicall3 check, we check that there are no other calls in the Spire proposer, so
51
+ we are absolutely sure that the only call is the successful one to the rollup. Any extraneous call would
52
+ imply an unexpected path to calling `propose` in the rollup contract, and since we cannot verify if the
53
+ calldata arguments we extracted are the correct ones (see the section below), we cannot know for sure which
54
+ one is the call that succeeded, so we don't know which calldata to process.
55
+
56
+ Furthermore, since the Spire proposer is upgradeable, we check if the implementation has not changed in
57
+ order to decode. As usual, any validation failure triggers fallback to the next step.
58
+
59
+ ### Verifying Multicall3 Arguments
60
+
61
+ **This is NOT implemented for simplicity's sake**
62
+
63
+ If the checks above don't hold, such as when there are multiple calls to `propose`, then we cannot
64
+ reliably extract the `propose` calldata from the multicall3 arguments alone. We can try a best-effort
65
+ where we try all `propose` calls we see and validate them against on-chain data. Note that we can use these
66
+ same strategies if we were to obtain the calldata from another source.
67
+
68
+ #### TempBlockLog Verification
69
+
70
+ Read the stored `TempBlockLog` for the L2 block number from L1 and verify it matches our decoded header hash,
71
+ since the `TempBlockLog` stores the hash of the proposed block header, the payload commitment, and the attestations.
72
+
73
+ However, `TempBlockLog` is only stored temporarily and deleted after proven, so this method only works for recent
74
+ blocks, not for historical data syncing.
75
+
76
+ #### Archive Verification
77
+
78
+ Verify that the archive root in the decoded propose is correct with regard to the block header. This requires
79
+ hashing the block header we have retrieved, inserting it into the archive tree, and checking the resulting root
80
+ against the one we got from L1.
81
+
82
+ However, this requires that the archive keeps a reference to world-state, which is not the case in the current
83
+ system.
84
+
85
+ #### Emit Commitments in Rollup Contract
86
+
87
+ Modify rollup contract to emit commitments to the block header in the `L2BlockProposed` event, allowing us to easily
88
+ verify the calldata we obtained vs the emitted event.
89
+
90
+ However, modifying the rollup contract is out of scope for this change. But we can implement this approach in `v2`.
91
+
92
+ ### Debug and Trace Transaction Fallback
93
+
94
+ Last, we use L1 node's trace/debug RPC methods to definitively identify the one successful `propose` call within the tx.
95
+ We can then extract the exact calldata that hit the `propose` function in the rollup contract.
96
+
97
+ This approach requires access to a debug-enabled L1 node, which may be more resource-intensive, so we only
98
+ use it as a fallback when the first step fails, which should be rare in practice.