@aztec/archiver 0.0.1-commit.993d240 → 0.0.1-commit.9a89641

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 (86) hide show
  1. package/dest/archiver.d.ts +44 -8
  2. package/dest/archiver.d.ts.map +1 -1
  3. package/dest/archiver.js +103 -35
  4. package/dest/config.d.ts +5 -4
  5. package/dest/config.d.ts.map +1 -1
  6. package/dest/config.js +28 -7
  7. package/dest/factory.d.ts +8 -1
  8. package/dest/factory.d.ts.map +1 -1
  9. package/dest/factory.js +41 -5
  10. package/dest/l1/calldata_retriever.d.ts +8 -1
  11. package/dest/l1/calldata_retriever.d.ts.map +1 -1
  12. package/dest/l1/calldata_retriever.js +7 -6
  13. package/dest/l1/data_retrieval.d.ts +7 -2
  14. package/dest/l1/data_retrieval.d.ts.map +1 -1
  15. package/dest/modules/contract_data_source_adapter.d.ts +2 -2
  16. package/dest/modules/contract_data_source_adapter.d.ts.map +1 -1
  17. package/dest/modules/contract_data_source_adapter.js +1 -9
  18. package/dest/modules/data_source_base.d.ts +5 -6
  19. package/dest/modules/data_source_base.d.ts.map +1 -1
  20. package/dest/modules/data_source_base.js +9 -38
  21. package/dest/modules/data_store_updater.d.ts +1 -1
  22. package/dest/modules/data_store_updater.d.ts.map +1 -1
  23. package/dest/modules/data_store_updater.js +4 -3
  24. package/dest/modules/instrumentation.d.ts +9 -1
  25. package/dest/modules/instrumentation.d.ts.map +1 -1
  26. package/dest/modules/instrumentation.js +23 -2
  27. package/dest/modules/l1_synchronizer.d.ts +3 -3
  28. package/dest/modules/l1_synchronizer.d.ts.map +1 -1
  29. package/dest/modules/l1_synchronizer.js +78 -41
  30. package/dest/modules/outbox_trees_resolver.d.ts +64 -0
  31. package/dest/modules/outbox_trees_resolver.d.ts.map +1 -0
  32. package/dest/modules/outbox_trees_resolver.js +184 -0
  33. package/dest/modules/validation.d.ts +29 -7
  34. package/dest/modules/validation.d.ts.map +1 -1
  35. package/dest/modules/validation.js +31 -13
  36. package/dest/store/block_store.d.ts +26 -22
  37. package/dest/store/block_store.d.ts.map +1 -1
  38. package/dest/store/block_store.js +105 -78
  39. package/dest/store/contract_class_store.d.ts +1 -1
  40. package/dest/store/contract_class_store.d.ts.map +1 -1
  41. package/dest/store/contract_class_store.js +19 -1
  42. package/dest/store/contract_instance_store.d.ts +1 -1
  43. package/dest/store/contract_instance_store.d.ts.map +1 -1
  44. package/dest/store/contract_instance_store.js +18 -1
  45. package/dest/store/log_store.d.ts +1 -1
  46. package/dest/store/log_store.d.ts.map +1 -1
  47. package/dest/store/log_store.js +3 -3
  48. package/dest/store/log_store_codec.d.ts +9 -1
  49. package/dest/store/log_store_codec.d.ts.map +1 -1
  50. package/dest/store/log_store_codec.js +9 -0
  51. package/dest/test/fake_l1_state.js +3 -3
  52. package/dest/test/mock_l1_to_l2_message_source.d.ts +1 -1
  53. package/dest/test/mock_l1_to_l2_message_source.d.ts.map +1 -1
  54. package/dest/test/mock_l1_to_l2_message_source.js +1 -2
  55. package/dest/test/mock_l2_block_source.d.ts +4 -11
  56. package/dest/test/mock_l2_block_source.d.ts.map +1 -1
  57. package/dest/test/mock_l2_block_source.js +15 -39
  58. package/dest/test/mock_structs.d.ts +1 -1
  59. package/dest/test/mock_structs.d.ts.map +1 -1
  60. package/dest/test/mock_structs.js +2 -2
  61. package/dest/test/noop_l1_archiver.d.ts +8 -3
  62. package/dest/test/noop_l1_archiver.d.ts.map +1 -1
  63. package/dest/test/noop_l1_archiver.js +12 -9
  64. package/package.json +13 -13
  65. package/src/archiver.ts +147 -42
  66. package/src/config.ts +34 -16
  67. package/src/factory.ts +48 -4
  68. package/src/l1/calldata_retriever.ts +13 -6
  69. package/src/l1/data_retrieval.ts +8 -1
  70. package/src/modules/contract_data_source_adapter.ts +1 -10
  71. package/src/modules/data_source_base.ts +17 -44
  72. package/src/modules/data_store_updater.ts +4 -3
  73. package/src/modules/instrumentation.ts +15 -2
  74. package/src/modules/l1_synchronizer.ts +120 -57
  75. package/src/modules/outbox_trees_resolver.ts +220 -0
  76. package/src/modules/validation.ts +71 -15
  77. package/src/store/block_store.ts +108 -101
  78. package/src/store/contract_class_store.ts +19 -1
  79. package/src/store/contract_instance_store.ts +18 -1
  80. package/src/store/log_store.ts +3 -2
  81. package/src/store/log_store_codec.ts +11 -0
  82. package/src/test/fake_l1_state.ts +3 -3
  83. package/src/test/mock_l1_to_l2_message_source.ts +0 -1
  84. package/src/test/mock_l2_block_source.ts +14 -53
  85. package/src/test/mock_structs.ts +5 -2
  86. package/src/test/noop_l1_archiver.ts +30 -11
@@ -1 +1 @@
1
- {"version":3,"file":"instrumentation.d.ts","sourceRoot":"","sources":["../../src/modules/instrumentation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAC;AAElE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC/D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAErE,OAAO,EAKL,KAAK,iBAAiB,EAEtB,KAAK,eAAe,EACpB,KAAK,MAAM,EAGZ,MAAM,yBAAyB,CAAC;AAEjC,qBAAa,uBAAuB;IA+BhC,OAAO,CAAC,SAAS;IA9BnB,SAAgB,MAAM,EAAE,MAAM,CAAC;IAE/B,OAAO,CAAC,WAAW,CAAQ;IAC3B,OAAO,CAAC,gBAAgB,CAAQ;IAChC,OAAO,CAAC,OAAO,CAAgB;IAC/B,OAAO,CAAC,aAAa,CAAQ;IAC7B,OAAO,CAAC,oBAAoB,CAAY;IACxC,OAAO,CAAC,oBAAoB,CAAgB;IAC5C,OAAO,CAAC,SAAS,CAAc;IAE/B,OAAO,CAAC,aAAa,CAAY;IACjC,OAAO,CAAC,UAAU,CAAgB;IAElC,OAAO,CAAC,oBAAoB,CAAY;IACxC,OAAO,CAAC,yBAAyB,CAAY;IAC7C,OAAO,CAAC,cAAc,CAAgB;IACtC,OAAO,CAAC,YAAY,CAAY;IAChC,OAAO,CAAC,WAAW,CAAY;IAE/B,OAAO,CAAC,sBAAsB,CAAY;IAC1C,OAAO,CAAC,gBAAgB,CAAgB;IAExC,OAAO,CAAC,0BAA0B,CAAgB;IAElD,OAAO,CAAC,0BAA0B,CAAY;IAC9C,OAAO,CAAC,uBAAuB,CAAgB;IAE/C,OAAO,CAAC,GAAG,CAA4C;IAEvD,OAAO,eA0DN;IAED,OAAoB,GAAG,CAAC,SAAS,EAAE,eAAe,EAAE,SAAS,CAAC,EAAE,iBAAiB,oCAMhF;IAEM,SAAS,IAAI,OAAO,CAE1B;IAEM,uBAAuB,CAAC,gBAAgB,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,QAStE;IAEM,4BAA4B,CAAC,qBAAqB,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,QASnF;IAEM,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,QAMhE;IAEM,YAAY,CAAC,QAAQ,EAAE,MAAM,QAGnC;IAEM,0BAA0B,CAAC,UAAU,EAAE,cAAc,QAI3D;IAEM,qBAAqB,CAAC,IAAI,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,QAW9F;IAEM,mBAAmB,CAAC,WAAW,EAAE,MAAM,QAE7C;IAEM,2BAA2B,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,QAKpE;IAED,wEAAwE;IACjE,yBAAyB,SAE/B;IAED;;OAEG;IACI,yBAAyB,CAAC,IAAI,EAAE;QACrC,UAAU,EAAE,UAAU,CAAC;QACvB,WAAW,EAAE,MAAM,CAAC;QACpB,WAAW,EAAE,IAAI,CAAC,iBAAiB,EAAE,eAAe,GAAG,cAAc,CAAC,CAAC;KACxE,GAAG,IAAI,CAIP;CACF"}
1
+ {"version":3,"file":"instrumentation.d.ts","sourceRoot":"","sources":["../../src/modules/instrumentation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAC;AAElE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC/D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAErE,OAAO,EAKL,KAAK,iBAAiB,EAEtB,KAAK,eAAe,EACpB,KAAK,MAAM,EAGZ,MAAM,yBAAyB,CAAC;AAEjC,qBAAa,uBAAuB;IA+BhC,OAAO,CAAC,SAAS;IA9BnB,SAAgB,MAAM,EAAE,MAAM,CAAC;IAE/B,OAAO,CAAC,WAAW,CAAQ;IAC3B,OAAO,CAAC,gBAAgB,CAAQ;IAChC,OAAO,CAAC,OAAO,CAAgB;IAC/B,OAAO,CAAC,aAAa,CAAQ;IAC7B,OAAO,CAAC,oBAAoB,CAAY;IACxC,OAAO,CAAC,oBAAoB,CAAgB;IAC5C,OAAO,CAAC,SAAS,CAAc;IAE/B,OAAO,CAAC,aAAa,CAAY;IACjC,OAAO,CAAC,UAAU,CAAgB;IAElC,OAAO,CAAC,oBAAoB,CAAY;IACxC,OAAO,CAAC,yBAAyB,CAAY;IAC7C,OAAO,CAAC,cAAc,CAAgB;IACtC,OAAO,CAAC,YAAY,CAAY;IAChC,OAAO,CAAC,WAAW,CAAY;IAE/B,OAAO,CAAC,sBAAsB,CAAY;IAC1C,OAAO,CAAC,gBAAgB,CAAgB;IAExC,OAAO,CAAC,0BAA0B,CAAgB;IAElD,OAAO,CAAC,0BAA0B,CAAY;IAC9C,OAAO,CAAC,uBAAuB,CAAgB;IAE/C,OAAO,CAAC,GAAG,CAA4C;IAEvD,OAAO,eA4DN;IAED,OAAoB,GAAG,CAAC,SAAS,EAAE,eAAe,EAAE,SAAS,CAAC,EAAE,iBAAiB,oCAMhF;IAEM,SAAS,IAAI,OAAO,CAE1B;IAEM,uBAAuB,CAAC,gBAAgB,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,QAStE;IAEM,4BAA4B,CAAC,qBAAqB,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,QASnF;IAEM,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,QAMhE;IAEM,YAAY,CAAC,QAAQ,EAAE,MAAM,QAGnC;IAED;;;;;;OAMG;IACI,WAAW,CAAC,SAAS,EAAE,gBAAgB,GAAG,aAAa,GAAG,QAAQ,GAAG,aAAa,QAExF;IAEM,0BAA0B,CAAC,UAAU,EAAE,cAAc,QAI3D;IAEM,qBAAqB,CAAC,IAAI,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,QAW9F;IAEM,mBAAmB,CAAC,WAAW,EAAE,MAAM,QAE7C;IAEM,2BAA2B,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,QAKpE;IAED,wEAAwE;IACjE,yBAAyB,SAE/B;IAED;;OAEG;IACI,yBAAyB,CAAC,IAAI,EAAE;QACrC,UAAU,EAAE,UAAU,CAAC;QACvB,WAAW,EAAE,MAAM,CAAC;QACpB,WAAW,EAAE,IAAI,CAAC,iBAAiB,EAAE,eAAe,GAAG,cAAc,CAAC,CAAC;KACxE,GAAG,IAAI,CAIP;CACF"}
@@ -48,7 +48,15 @@ export class ArchiverInstrumentation {
48
48
  this.syncDurationPerMessage = meter.createHistogram(Metrics.ARCHIVER_SYNC_PER_MESSAGE);
49
49
  this.syncMessageCount = createUpDownCounterWithDefault(meter, Metrics.ARCHIVER_SYNC_MESSAGE_COUNT);
50
50
  this.pruneDuration = meter.createHistogram(Metrics.ARCHIVER_PRUNE_DURATION);
51
- this.pruneCount = createUpDownCounterWithDefault(meter, Metrics.ARCHIVER_PRUNE_COUNT);
51
+ this.pruneCount = createUpDownCounterWithDefault(meter, Metrics.ARCHIVER_PRUNE_COUNT, {
52
+ [Attributes.PRUNE_TYPE]: [
53
+ 'unproven',
54
+ 'uncheckpointed',
55
+ 'l1_conflict',
56
+ 'orphan',
57
+ 'l1_mismatch'
58
+ ]
59
+ });
52
60
  this.blockProposalTxTargetCount = createUpDownCounterWithDefault(meter, Metrics.ARCHIVER_BLOCK_PROPOSAL_TX_TARGET_COUNT, {
53
61
  [Attributes.L1_BLOCK_PROPOSAL_USED_TRACE]: [
54
62
  true,
@@ -99,9 +107,22 @@ export class ArchiverInstrumentation {
99
107
  this.syncDurationPerMessage.record(Math.ceil(syncPerMessageMs));
100
108
  }
101
109
  processPrune(duration) {
102
- this.pruneCount.add(1);
110
+ this.pruneCount.add(1, {
111
+ [Attributes.PRUNE_TYPE]: 'unproven'
112
+ });
103
113
  this.pruneDuration.record(Math.ceil(duration));
104
114
  }
115
+ /**
116
+ * Records a pending-chain reorg, where the archiver dropped proposed blocks (and world-state follows by pruning). The
117
+ * type distinguishes the cause: 'uncheckpointed' (slot ended without a checkpoint), 'l1_conflict' (proposed blocks
118
+ * conflicting with an L1 checkpoint), 'orphan' (no matching proposed checkpoint arrived before the deadline), or
119
+ * 'l1_mismatch' (the local checkpointed tip diverged from L1 — an L1 reorg or a pruned/missed-proof checkpoint — so
120
+ * already-checkpointed blocks were rewound).
121
+ */ recordPrune(pruneType) {
122
+ this.pruneCount.add(1, {
123
+ [Attributes.PRUNE_TYPE]: pruneType
124
+ });
125
+ }
105
126
  updateLastProvenCheckpoint(checkpoint) {
106
127
  const lastBlockNumberInCheckpoint = checkpoint.startBlock + checkpoint.blockCount - 1;
107
128
  this.blockHeight.record(lastBlockNumberInCheckpoint, {
@@ -6,7 +6,7 @@ import { Buffer32 } from '@aztec/foundation/buffer';
6
6
  import { Fr } from '@aztec/foundation/curves/bn254';
7
7
  import { type Logger } from '@aztec/foundation/log';
8
8
  import { DateProvider } from '@aztec/foundation/timer';
9
- import { type ArchiverEmitter } from '@aztec/stdlib/block';
9
+ import { type ArchiverEmitter, type L2Block } from '@aztec/stdlib/block';
10
10
  import { type L1RollupConstants } from '@aztec/stdlib/epoch-helpers';
11
11
  import { type Traceable, type Tracer } from '@aztec/telemetry-client';
12
12
  import { type ArchiverDataStores } from '../store/data_stores.js';
@@ -58,7 +58,7 @@ export declare class ArchiverL1Synchronizer implements Traceable {
58
58
  private getSignatureContext;
59
59
  /** Checks that the ethereum node we are connected to has a latest timestamp no more than the allowed drift. Throw if not. */
60
60
  testEthereumNodeSynced(): Promise<void>;
61
- syncFromL1(initialSyncComplete: boolean): Promise<void>;
61
+ syncFromL1(initialSyncComplete: boolean): Promise<L2Block[]>;
62
62
  private updateFinalizedCheckpoint;
63
63
  private pruneUncheckpointedBlocks;
64
64
  private canPrune;
@@ -74,4 +74,4 @@ export declare class ArchiverL1Synchronizer implements Traceable {
74
74
  private checkForNewCheckpointsBeforeL1SyncPoint;
75
75
  private getCheckpointHeader;
76
76
  }
77
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibDFfc3luY2hyb25pemVyLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvbW9kdWxlcy9sMV9zeW5jaHJvbml6ZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLEVBQUUsbUJBQW1CLEVBQUUsTUFBTSwyQkFBMkIsQ0FBQztBQUNyRSxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sb0JBQW9CLENBQUM7QUFDaEQsT0FBTyxFQUFFLGFBQWEsRUFBMkIsY0FBYyxFQUFFLE1BQU0sMkJBQTJCLENBQUM7QUFHbkcsT0FBTyxLQUFLLEVBQUUsZ0JBQWdCLEVBQUUscUJBQXFCLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQUlyRixPQUFPLEVBQVksUUFBUSxFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFFOUQsT0FBTyxFQUFFLEVBQUUsRUFBRSxNQUFNLGdDQUFnQyxDQUFDO0FBRXBELE9BQU8sRUFBRSxLQUFLLE1BQU0sRUFBZ0IsTUFBTSx1QkFBdUIsQ0FBQztBQUdsRSxPQUFPLEVBQUUsWUFBWSxFQUFrQixNQUFNLHlCQUF5QixDQUFDO0FBRXZFLE9BQU8sRUFBRSxLQUFLLGVBQWUsRUFBc0QsTUFBTSxxQkFBcUIsQ0FBQztBQUUvRyxPQUFPLEVBQUUsS0FBSyxpQkFBaUIsRUFBd0MsTUFBTSw2QkFBNkIsQ0FBQztBQUczRyxPQUFPLEVBQUUsS0FBSyxTQUFTLEVBQUUsS0FBSyxNQUFNLEVBQXlCLE1BQU0seUJBQXlCLENBQUM7QUFZN0YsT0FBTyxFQUFFLEtBQUssa0JBQWtCLEVBQXlCLE1BQU0seUJBQXlCLENBQUM7QUFDekYsT0FBTyxLQUFLLEVBQUUsV0FBVyxFQUFFLE1BQU0sMkJBQTJCLENBQUM7QUFJN0QsT0FBTyxLQUFLLEVBQUUsdUJBQXVCLEVBQUUsTUFBTSxzQkFBc0IsQ0FBQztBQWVwRTs7O0dBR0c7QUFDSCxxQkFBYSxzQkFBdUIsWUFBVyxTQUFTO0lBU3BELE9BQU8sQ0FBQyxRQUFRLENBQUMs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
77
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibDFfc3luY2hyb25pemVyLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvbW9kdWxlcy9sMV9zeW5jaHJvbml6ZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLEVBQUUsbUJBQW1CLEVBQUUsTUFBTSwyQkFBMkIsQ0FBQztBQUNyRSxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sb0JBQW9CLENBQUM7QUFDaEQsT0FBTyxFQUFFLGFBQWEsRUFBMkIsY0FBYyxFQUFFLE1BQU0sMkJBQTJCLENBQUM7QUFHbkcsT0FBTyxLQUFLLEVBQUUsZ0JBQWdCLEVBQUUscUJBQXFCLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQUlyRixPQUFPLEVBQVksUUFBUSxFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFFOUQsT0FBTyxFQUFFLEVBQUUsRUFBRSxNQUFNLGdDQUFnQyxDQUFDO0FBRXBELE9BQU8sRUFBRSxLQUFLLE1BQU0sRUFBZ0IsTUFBTSx1QkFBdUIsQ0FBQztBQUdsRSxPQUFPLEVBQUUsWUFBWSxFQUFrQixNQUFNLHlCQUF5QixDQUFDO0FBRXZFLE9BQU8sRUFDTCxLQUFLLGVBQWUsRUFDcEIsS0FBSyxPQUFPLEVBR2IsTUFBTSxxQkFBcUIsQ0FBQztBQVE3QixPQUFPLEVBQUUsS0FBSyxpQkFBaUIsRUFBd0MsTUFBTSw2QkFBNkIsQ0FBQztBQUczRyxPQUFPLEVBQUUsS0FBSyxTQUFTLEVBQUUsS0FBSyxNQUFNLEVBQXlCLE1BQU0seUJBQXlCLENBQUM7QUFZN0YsT0FBTyxFQUFFLEtBQUssa0JBQWtCLEVBQXlCLE1BQU0seUJBQXlCLENBQUM7QUFDekYsT0FBTyxLQUFLLEVBQUUsV0FBVyxFQUFFLE1BQU0sMkJBQTJCLENBQUM7QUFJN0QsT0FBTyxLQUFLLEVBQUUsdUJBQXVCLEVBQUUsTUFBTSxzQkFBc0IsQ0FBQztBQWlCcEU7OztHQUdHO0FBQ0gscUJBQWEsc0JBQXVCLFlBQVcsU0FBUztJQVNwRCxPQUFP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
@@ -1 +1 @@
1
- {"version":3,"file":"l1_synchronizer.d.ts","sourceRoot":"","sources":["../../src/modules/l1_synchronizer.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"l1_synchronizer.d.ts","sourceRoot":"","sources":["../../src/modules/l1_synchronizer.ts"],"names":[],"mappings":"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"}
@@ -393,7 +393,7 @@ import { getCheckpointBlobDataFromBlobs, retrieveCheckpointCalldataFromRollup, r
393
393
  import { getArchiverSynchPoint } from '../store/data_stores.js';
394
394
  import { MessageStoreError } from '../store/message_store.js';
395
395
  import { ArchiverDataStoreUpdater } from './data_store_updater.js';
396
- import { validateCheckpointAttestations } from './validation.js';
396
+ import { validateCheckpointAttestationsFromCalldata } from './validation.js';
397
397
  _dec = trackSpan('Archiver.syncFromL1'), _dec1 = trackSpan('Archiver.handleEpochPrune'), _dec2 = trackSpan('Archiver.handleL1ToL2Messages'), _dec3 = trackSpan('Archiver.handleCheckpoints');
398
398
  /**
399
399
  * Handles L1 synchronization for the archiver.
@@ -490,6 +490,7 @@ _dec = trackSpan('Archiver.syncFromL1'), _dec1 = trackSpan('Archiver.handleEpoch
490
490
  }
491
491
  }
492
492
  async syncFromL1(initialSyncComplete) {
493
+ const blocksAdded = [];
493
494
  // In between the various calls to L1, the block number can move meaning some of the following
494
495
  // calls will return data for blocks that were not present during earlier calls. To combat this
495
496
  // we ensure that all data retrieval methods only retrieve data up to the currentBlockNumber
@@ -506,7 +507,7 @@ _dec = trackSpan('Archiver.syncFromL1'), _dec1 = trackSpan('Archiver.handleEpoch
506
507
  };
507
508
  if (this.l1BlockHash && currentL1BlockHash.equals(this.l1BlockHash)) {
508
509
  this.log.trace(`No new L1 blocks since last sync at L1 block ${this.l1BlockNumber}`);
509
- return;
510
+ return blocksAdded;
510
511
  }
511
512
  // Log at error if the latest L1 block timestamp is too old
512
513
  const maxAllowedDelay = this.config.maxAllowedEthClientDriftSeconds;
@@ -540,6 +541,7 @@ _dec = trackSpan('Archiver.syncFromL1'), _dec1 = trackSpan('Archiver.handleEpoch
540
541
  // First we retrieve new checkpoints and L2 blocks and store them in the DB. This will also update the
541
542
  // pending chain validation status, proven checkpoint number, and synched L1 block number.
542
543
  const rollupStatus = await this.handleCheckpoints(blocksSynchedTo, currentL1BlockNumber, initialSyncComplete);
544
+ blocksAdded.push(...rollupStatus.blocksAdded);
543
545
  // Then we try pruning uncheckpointed blocks if a new slot was mined without checkpoints
544
546
  await this.pruneUncheckpointedBlocks(currentL1Timestamp);
545
547
  // Then we prune the current epoch if it'd reorg on next submission.
@@ -570,6 +572,7 @@ _dec = trackSpan('Archiver.syncFromL1'), _dec1 = trackSpan('Archiver.handleEpoch
570
572
  l1TimestampAtStart: currentL1Timestamp,
571
573
  l1BlockNumberAtEnd
572
574
  });
575
+ return blocksAdded;
573
576
  }
574
577
  /** Updates the finalized checkpoint using the pre-fetched finalized L1 block from the current sync iteration. */ async updateFinalizedCheckpoint(finalizedL1Block) {
575
578
  try {
@@ -625,6 +628,7 @@ _dec = trackSpan('Archiver.syncFromL1'), _dec1 = trackSpan('Archiver.handleEpoch
625
628
  });
626
629
  const prunedBlocks = await this.updater.removeUncheckpointedBlocksAfter(lastCheckpointedBlockNumber);
627
630
  if (prunedBlocks.length > 0) {
631
+ this.instrumentation.recordPrune('uncheckpointed');
628
632
  this.events.emit(L2BlockSourceEvents.L2PruneUncheckpointed, {
629
633
  type: L2BlockSourceEvents.L2PruneUncheckpointed,
630
634
  slotNumber: firstUncheckpointedBlockSlot,
@@ -885,6 +889,7 @@ _dec = trackSpan('Archiver.syncFromL1'), _dec1 = trackSpan('Archiver.handleEpoch
885
889
  return Buffer32.fromString(block.hash);
886
890
  }
887
891
  async handleCheckpoints(blocksSynchedTo, currentL1BlockNumber, initialSyncComplete) {
892
+ const blocksAdded = [];
888
893
  const localPendingCheckpointNumber = await this.stores.blocks.getLatestCheckpointNumber();
889
894
  const initialValidationResult = await this.stores.blocks.getPendingChainValidationStatus();
890
895
  const { provenCheckpointNumber, provenArchive, pendingCheckpointNumber, pendingArchive, archiveOfMyCheckpoint: archiveForLocalPendingCheckpointNumber } = await execInSpan(this.tracer, 'Archiver.getRollupStatus', ()=>this.rollup.status(localPendingCheckpointNumber, {
@@ -895,7 +900,8 @@ _dec = trackSpan('Archiver.syncFromL1'), _dec1 = trackSpan('Archiver.handleEpoch
895
900
  provenArchive: provenArchive.toString(),
896
901
  pendingCheckpointNumber,
897
902
  pendingArchive: pendingArchive.toString(),
898
- validationResult: initialValidationResult
903
+ validationResult: initialValidationResult,
904
+ blocksAdded
899
905
  };
900
906
  this.log.trace(`Retrieved rollup status at current L1 block ${currentL1BlockNumber}.`, {
901
907
  localPendingCheckpointNumber,
@@ -1006,6 +1012,9 @@ _dec = trackSpan('Archiver.syncFromL1'), _dec1 = trackSpan('Archiver.handleEpoch
1006
1012
  }
1007
1013
  const checkpointsToRemove = localPendingCheckpointNumber - tipAfterUnwind;
1008
1014
  await this.updater.removeCheckpointsAfter(CheckpointNumber(tipAfterUnwind));
1015
+ if (checkpointsToRemove > 0) {
1016
+ this.instrumentation.recordPrune('l1_mismatch');
1017
+ }
1009
1018
  this.log.warn(`Removed ${count(checkpointsToRemove, 'checkpoint')} after checkpoint ${tipAfterUnwind} ` + `due to mismatched checkpoint hashes at L1 block ${currentL1BlockNumber}. ` + `Updated L2 latest checkpoint is ${await this.stores.blocks.getLatestCheckpointNumber()}.`);
1010
1019
  }
1011
1020
  }
@@ -1038,31 +1047,24 @@ _dec = trackSpan('Archiver.syncFromL1'), _dec1 = trackSpan('Archiver.handleEpoch
1038
1047
  const promoteResult = await this.tryBuildPublishedCheckpointFromProposed(lastCalldataCheckpoint);
1039
1048
  const checkpointToPromote = promoteResult && !('diverged' in promoteResult) ? promoteResult : undefined;
1040
1049
  const evictProposedFrom = promoteResult && 'diverged' in promoteResult ? promoteResult.fromCheckpointNumber : undefined;
1041
- // Then fetch blobs in parallel and build the full published checkpoints
1042
- const toFetchBlobs = checkpointToPromote ? calldataCheckpoints.slice(0, -1) : calldataCheckpoints;
1043
- const blobFetched = await asyncPool(10, toFetchBlobs, async (checkpoint)=>retrievedToPublishedCheckpoint({
1044
- ...checkpoint,
1045
- checkpointBlobData: await getCheckpointBlobDataFromBlobs(this.blobClient, checkpoint.l1.blockHash, checkpoint.blobHashes, checkpoint.checkpointNumber, this.log, !initialSyncComplete, checkpoint.parentBeaconBlockRoot, checkpoint.l1.timestamp)
1046
- }));
1047
- // And add the promoted checkpoint to the list of all checkpoints
1048
- const publishedCheckpoints = checkpointToPromote ? [
1049
- ...blobFetched,
1050
- checkpointToPromote
1051
- ] : blobFetched;
1052
- const validCheckpoints = [];
1053
- // Now loop through all checkpoints and validate their attestations
1054
- for (const published of publishedCheckpoints){
1055
- // Check the attestations uploaded by the publisher to L1 are correct
1050
+ // Validate attestations from CALLDATA before fetching any blobs. A checkpoint with invalid
1051
+ // attestations (or one descending from a rejected ancestor) is rejected here without fetching its
1052
+ // blobs, so a malformed blob does not throw during decode before the rejection path runs and
1053
+ // stall sync. The signed consensus payload (header, archive root, fee asset price
1054
+ // modifier) is fully available from calldata.
1055
+ const checkpointsToIngest = [];
1056
+ for (const calldataCheckpoint of calldataCheckpoints){
1057
+ // Check the attestations uploaded by the publisher to L1 are correct.
1056
1058
  // Rollup contract does not validate attestations to save on gas, so this
1057
1059
  // falls on the nodes to verify offchain and skip those checkpoints.
1058
1060
  const validationResult = this.config.skipValidateCheckpointAttestations ? {
1059
1061
  valid: true
1060
- } : await validateCheckpointAttestations(published, this.epochCache, this.l1Constants, this.getSignatureContext(), this.log);
1062
+ } : await validateCheckpointAttestationsFromCalldata(calldataCheckpoint, this.epochCache, this.l1Constants, this.getSignatureContext(), this.log);
1061
1063
  // Also skip the checkpoint if it builds on a previously-rejected ancestor. Without
1062
1064
  // this, addCheckpoints would throw InitialCheckpointNumberNotSequentialError when the
1063
1065
  // ancestor was skipped earlier (e.g. due to invalid attestations), the catch handler
1064
1066
  // would roll back the L1 sync point, and the next iteration would re-fetch and re-throw.
1065
- const rejectedAncestor = await this.stores.blocks.getRejectedCheckpointByArchiveRoot(published.checkpoint.header.lastArchiveRoot);
1067
+ const rejectedAncestor = await this.stores.blocks.getRejectedCheckpointByArchiveRoot(calldataCheckpoint.header.lastArchiveRoot);
1066
1068
  // Update the validation result if it has changed, so we can keep track of the first invalid checkpoint
1067
1069
  // in case there is a sequence of more than one invalid checkpoint, as we need to invalidate the first one.
1068
1070
  // There is an exception though: if a checkpoint is invalidated and replaced with another invalid checkpoint,
@@ -1076,9 +1078,9 @@ _dec = trackSpan('Archiver.syncFromL1'), _dec1 = trackSpan('Archiver.handleEpoch
1076
1078
  rollupStatus.validationResult = validationResult;
1077
1079
  }
1078
1080
  if (!validationResult.valid) {
1079
- this.log.warn(`Skipping checkpoint ${published.checkpoint.number} due to invalid attestations`, {
1080
- checkpointHash: published.checkpoint.hash(),
1081
- l1BlockNumber: published.l1.blockNumber,
1081
+ this.log.warn(`Skipping checkpoint ${calldataCheckpoint.checkpointNumber} due to invalid attestations`, {
1082
+ checkpointNumber: calldataCheckpoint.checkpointNumber,
1083
+ l1BlockNumber: calldataCheckpoint.l1.blockNumber,
1082
1084
  ...pick(validationResult, 'reason')
1083
1085
  });
1084
1086
  // Emit event for invalid checkpoint detection
@@ -1090,22 +1092,27 @@ _dec = trackSpan('Archiver.syncFromL1'), _dec1 = trackSpan('Archiver.handleEpoch
1090
1092
  // is detected and skipped (rather than tripping the addCheckpoints consecutive-number
1091
1093
  // check and causing the sync point to roll back in a loop).
1092
1094
  await this.stores.blocks.addRejectedCheckpoint({
1093
- checkpointNumber: published.checkpoint.number,
1094
- archiveRoot: published.checkpoint.archive.root,
1095
- parentArchiveRoot: published.checkpoint.header.lastArchiveRoot,
1096
- slotNumber: published.checkpoint.header.slotNumber,
1097
- l1: published.l1,
1095
+ checkpointNumber: calldataCheckpoint.checkpointNumber,
1096
+ archiveRoot: calldataCheckpoint.archiveRoot,
1097
+ parentArchiveRoot: calldataCheckpoint.header.lastArchiveRoot,
1098
+ slotNumber: calldataCheckpoint.header.slotNumber,
1099
+ l1: calldataCheckpoint.l1,
1098
1100
  reason: 'invalid-attestations'
1099
1101
  });
1100
1102
  continue;
1101
1103
  }
1102
1104
  if (rejectedAncestor) {
1103
- const descendantInfo = published.checkpoint.toCheckpointInfo();
1104
- this.log.warn(`Skipping checkpoint ${published.checkpoint.number} as it is a descendant of ` + `rejected checkpoint ${rejectedAncestor.checkpointNumber} (${rejectedAncestor.reason})`, {
1105
- checkpointNumber: published.checkpoint.number,
1106
- checkpointHash: published.checkpoint.hash(),
1107
- l1BlockNumber: published.l1.blockNumber,
1108
- l1BlockHash: published.l1.blockHash,
1105
+ const descendantInfo = {
1106
+ archive: calldataCheckpoint.archiveRoot,
1107
+ lastArchive: calldataCheckpoint.header.lastArchiveRoot,
1108
+ slotNumber: calldataCheckpoint.header.slotNumber,
1109
+ checkpointNumber: calldataCheckpoint.checkpointNumber,
1110
+ timestamp: calldataCheckpoint.header.timestamp
1111
+ };
1112
+ this.log.warn(`Skipping checkpoint ${calldataCheckpoint.checkpointNumber} as it is a descendant of ` + `rejected checkpoint ${rejectedAncestor.checkpointNumber} (${rejectedAncestor.reason})`, {
1113
+ checkpointNumber: calldataCheckpoint.checkpointNumber,
1114
+ l1BlockNumber: calldataCheckpoint.l1.blockNumber,
1115
+ l1BlockHash: calldataCheckpoint.l1.blockHash,
1109
1116
  ancestorCheckpointNumber: rejectedAncestor.checkpointNumber,
1110
1117
  ancestorArchiveRoot: rejectedAncestor.archiveRoot.toString(),
1111
1118
  ancestorReason: rejectedAncestor.reason
@@ -1119,15 +1126,38 @@ _dec = trackSpan('Archiver.syncFromL1'), _dec1 = trackSpan('Archiver.handleEpoch
1119
1126
  // Persist this chainpoint as rejected as well, so we can construct a chain of
1120
1127
  // skipped checkpoints starting from the first one with invalid attestations.
1121
1128
  await this.stores.blocks.addRejectedCheckpoint({
1122
- checkpointNumber: published.checkpoint.number,
1123
- archiveRoot: published.checkpoint.archive.root,
1124
- parentArchiveRoot: published.checkpoint.header.lastArchiveRoot,
1125
- slotNumber: published.checkpoint.header.slotNumber,
1126
- l1: published.l1,
1129
+ checkpointNumber: calldataCheckpoint.checkpointNumber,
1130
+ archiveRoot: calldataCheckpoint.archiveRoot,
1131
+ parentArchiveRoot: calldataCheckpoint.header.lastArchiveRoot,
1132
+ slotNumber: calldataCheckpoint.header.slotNumber,
1133
+ l1: calldataCheckpoint.l1,
1127
1134
  reason: 'descends-from-invalid-attestations'
1128
1135
  });
1129
1136
  continue;
1130
1137
  }
1138
+ checkpointsToIngest.push(calldataCheckpoint);
1139
+ }
1140
+ // Fetch blobs in parallel only for the surviving (attestation-valid, non-descendant) checkpoints,
1141
+ // then build the full published checkpoints. The last calldata checkpoint may be promotable from a
1142
+ // local proposed block (checkpointToPromote), in which case it carries no blob to fetch. A missing or
1143
+ // undecodable blob throws and propagates, rolling back the L1 sync point so the fetch is retried.
1144
+ const toFetchBlobs = checkpointToPromote ? checkpointsToIngest.filter((c)=>c.checkpointNumber !== checkpointToPromote.checkpoint.number) : checkpointsToIngest;
1145
+ const blobFetched = await asyncPool(10, toFetchBlobs, async (checkpoint)=>retrievedToPublishedCheckpoint({
1146
+ ...checkpoint,
1147
+ checkpointBlobData: await getCheckpointBlobDataFromBlobs(this.blobClient, checkpoint.l1.blockHash, checkpoint.blobHashes, checkpoint.checkpointNumber, this.log, !initialSyncComplete, checkpoint.parentBeaconBlockRoot, checkpoint.l1.timestamp)
1148
+ }));
1149
+ // Index the built checkpoints by number so we can ingest them in calldata order, slotting in the
1150
+ // promoted checkpoint (built from a local proposed block rather than blobs).
1151
+ const publishedByNumber = new Map(blobFetched.map((published)=>[
1152
+ published.checkpoint.number,
1153
+ published
1154
+ ]));
1155
+ if (checkpointToPromote) {
1156
+ publishedByNumber.set(checkpointToPromote.checkpoint.number, checkpointToPromote);
1157
+ }
1158
+ const validCheckpoints = [];
1159
+ for (const calldataCheckpoint of checkpointsToIngest){
1160
+ const published = publishedByNumber.get(calldataCheckpoint.checkpointNumber);
1131
1161
  // Check the inHash of the checkpoint against the l1->l2 messages.
1132
1162
  // The messages should've been synced up to the currentL1BlockNumber and must be available for the published
1133
1163
  // checkpoints we just retrieved.
@@ -1172,6 +1202,10 @@ _dec = trackSpan('Archiver.syncFromL1'), _dec1 = trackSpan('Archiver.handleEpoch
1172
1202
  if (validCheckpoints.length > 0) {
1173
1203
  this.instrumentation.processNewCheckpointedBlocks(processDuration / validCheckpoints.length, validCheckpoints.flatMap((c)=>c.checkpoint.blocks));
1174
1204
  }
1205
+ // Record blocks newly fetched from L1 checkpoint payloads as added. The promoted checkpoint (if any) is
1206
+ // excluded: its blocks were already in local archiver storage (added via the proposed-block path) so the
1207
+ // block stream does not need them re-downloaded.
1208
+ blocksAdded.push(...checkpointsToAdd.flatMap((c)=>c.checkpoint.blocks));
1175
1209
  // If blocks were pruned due to conflict with L1 checkpoints, emit event
1176
1210
  if (result.prunedBlocks && result.prunedBlocks.length > 0) {
1177
1211
  const prunedCheckpointNumber = result.prunedBlocks[0].checkpointNumber;
@@ -1181,6 +1215,7 @@ _dec = trackSpan('Archiver.syncFromL1'), _dec1 = trackSpan('Archiver.handleEpoch
1181
1215
  prunedSlotNumber,
1182
1216
  prunedCheckpointNumber
1183
1217
  });
1218
+ this.instrumentation.recordPrune('l1_conflict');
1184
1219
  // Emit event for listening services to react to the prune.
1185
1220
  // Note: slotNumber comes from the first pruned block. If pruned blocks theoretically spanned multiple slots,
1186
1221
  // only one slot number would be reported (though in practice all blocks in a checkpoint span a single slot).
@@ -1220,7 +1255,9 @@ _dec = trackSpan('Archiver.syncFromL1'), _dec1 = trackSpan('Archiver.handleEpoch
1220
1255
  });
1221
1256
  }
1222
1257
  lastRetrievedCheckpoint = validCheckpoints.at(-1) ?? lastRetrievedCheckpoint;
1223
- lastSeenCheckpoint = publishedCheckpoints.at(-1) ?? lastSeenCheckpoint;
1258
+ // The last checkpoint seen on L1 this batch (valid or rejected), tracked from calldata since
1259
+ // rejected checkpoints are no longer built into PublishedCheckpoints.
1260
+ lastSeenCheckpoint = lastCalldataCheckpoint;
1224
1261
  }while (searchEndBlock < currentL1BlockNumber)
1225
1262
  // Important that we update AFTER inserting the blocks.
1226
1263
  await updateProvenCheckpoint();
@@ -1310,7 +1347,7 @@ _dec = trackSpan('Archiver.syncFromL1'), _dec1 = trackSpan('Archiver.handleEpoch
1310
1347
  const { lastSeenCheckpoint, pendingCheckpointNumber } = status;
1311
1348
  // Compare the last checkpoint (valid or not) we have (either retrieved in this round or loaded from store)
1312
1349
  // with what the rollup contract told us was the latest one (pinned at the currentL1BlockNumber).
1313
- const latestLocalCheckpointNumber = lastSeenCheckpoint?.checkpoint.number ?? CheckpointNumber.max(await this.stores.blocks.getLatestCheckpointNumber(), await this.stores.blocks.getLatestRejectedCheckpointNumber()) ?? CheckpointNumber.ZERO;
1350
+ const latestLocalCheckpointNumber = lastSeenCheckpoint?.checkpointNumber ?? CheckpointNumber.max(await this.stores.blocks.getLatestCheckpointNumber(), await this.stores.blocks.getLatestRejectedCheckpointNumber()) ?? CheckpointNumber.ZERO;
1314
1351
  if (latestLocalCheckpointNumber < pendingCheckpointNumber) {
1315
1352
  // Here we have consumed all logs until the `currentL1Block` we pinned at the beginning of the archiver loop,
1316
1353
  // but still haven't reached the pending checkpoint according to the call to the rollup contract.
@@ -0,0 +1,64 @@
1
+ import type { OutboxContract } from '@aztec/ethereum/contracts';
2
+ import type { BlockNumber, EpochNumber } from '@aztec/foundation/branded-types';
3
+ import type { Fr } from '@aztec/foundation/curves/bn254';
4
+ import { type Logger } from '@aztec/foundation/log';
5
+ import type { AztecAsyncKVStore } from '@aztec/kv-store';
6
+ import type { L2Block } from '@aztec/stdlib/block';
7
+ import type { CheckpointData } from '@aztec/stdlib/checkpoint';
8
+ import { type L2ToL1MembershipWitness } from '@aztec/stdlib/messaging';
9
+ import type { IndexedTxEffect, TxHash } from '@aztec/stdlib/tx';
10
+ /**
11
+ * Archiver-side view of the data the resolver needs to assemble a witness. The archiver implements
12
+ * each of these natively, so no L2 RPC plumbing is required.
13
+ */
14
+ export interface OutboxTreesArchiverView {
15
+ getBlocks(query: {
16
+ epoch: EpochNumber;
17
+ onlyCheckpointed: true;
18
+ }): Promise<L2Block[]>;
19
+ getBlock(query: {
20
+ number: BlockNumber;
21
+ }): Promise<L2Block | undefined>;
22
+ getCheckpointsData(query: {
23
+ epoch: EpochNumber;
24
+ }): Promise<CheckpointData[]>;
25
+ getTxEffect(txHash: TxHash): Promise<IndexedTxEffect | undefined>;
26
+ }
27
+ /**
28
+ * Holds an in-memory cache of per-epoch Outbox roots used to build L2-to-L1 message membership
29
+ * witnesses, plus the single-flight registry that dedupes concurrent fetches.
30
+ *
31
+ * Roots are fetched lazily on request and cached against the node's synced L1 block: a cached entry
32
+ * is reused only while the synced L1 block is unchanged, and re-fetched from L1 once the node
33
+ * advances. The cache is intentionally not persisted — entries are valid for at most one L1 slot, so
34
+ * there is nothing worth carrying across restarts. The four merkle-tree levels are rebuilt per
35
+ * request from the archiver's raw L2 data via `computeL2ToL1MembershipWitness`; no trees or
36
+ * intermediate out-hashes are cached.
37
+ *
38
+ * TODO: Re-add permanent caching for sealed-and-L1-finalized epochs so their roots can be frozen
39
+ * and served without any L1 read once they can no longer change. That logic was dropped here to keep
40
+ * the cache simple; it would likely want a persisted store again.
41
+ */
42
+ export declare class OutboxTreesResolver {
43
+ #private;
44
+ private readonly outbox;
45
+ private readonly archiver;
46
+ private readonly store;
47
+ private readonly getSyncedL1BlockNumber;
48
+ private readonly epochDuration;
49
+ private readonly log;
50
+ constructor(outbox: OutboxContract, archiver: OutboxTreesArchiverView, store: AztecAsyncKVStore, getSyncedL1BlockNumber: () => Promise<bigint | undefined>, epochDuration: number, log?: Logger);
51
+ /**
52
+ * Builds the L2-to-L1 membership witness for `message` in tx `txHash`. Returns `undefined` if the
53
+ * tx isn't yet in a block/epoch, or if the Outbox holds no covering partial-proof root for the
54
+ * tx's checkpoint as of the node's synced L1 block — callers should retry in that case.
55
+ */
56
+ getL2ToL1MembershipWitness(txHash: TxHash, message: Fr, messageIndexInTx?: number): Promise<L2ToL1MembershipWitness | undefined>;
57
+ /**
58
+ * Builds the node-shaped adapter handed to `computeL2ToL1MembershipWitness`. The helper invokes
59
+ * `getL2ToL1Messages` / `getBlock` / `getCheckpointsData`; `getTxReceipt` is part of the helper's
60
+ * node type but is never invoked because we always pass an already-resolved receipt.
61
+ */
62
+ private makeNodeView;
63
+ }
64
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib3V0Ym94X3RyZWVzX3Jlc29sdmVyLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvbW9kdWxlcy9vdXRib3hfdHJlZXNfcmVzb2x2ZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLEVBQUUsY0FBYyxFQUFFLE1BQU0sMkJBQTJCLENBQUM7QUFDaEUsT0FBTyxLQUFLLEVBQUUsV0FBVyxFQUFFLFdBQVcsRUFBRSxNQUFNLGlDQUFpQyxDQUFDO0FBRWhGLE9BQU8sS0FBSyxFQUFFLEVBQUUsRUFBRSxNQUFNLGdDQUFnQyxDQUFDO0FBQ3pELE9BQU8sRUFBRSxLQUFLLE1BQU0sRUFBZ0IsTUFBTSx1QkFBdUIsQ0FBQztBQUNsRSxPQUFPLEtBQUssRUFBRSxpQkFBaUIsRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBQ3pELE9BQU8sS0FBSyxFQUFFLE9BQU8sRUFBRSxNQUFNLHFCQUFxQixDQUFDO0FBQ25ELE9BQU8sS0FBSyxFQUFFLGNBQWMsRUFBRSxNQUFNLDBCQUEwQixDQUFDO0FBRS9ELE9BQU8sRUFBRSxLQUFLLHVCQUF1QixFQUFrQyxNQUFNLHlCQUF5QixDQUFDO0FBQ3ZHLE9BQU8sS0FBSyxFQUFFLGVBQWUsRUFBRSxNQUFNLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUVoRTs7O0dBR0c7QUFDSCxNQUFNLFdBQVcsdUJBQXVCO0lBQ3RDLFNBQVMsQ0FBQyxLQUFLLEVBQUU7UUFBRSxLQUFLLEVBQUUsV0FBVyxDQUFDO1FBQUMsZ0JBQWdCLEVBQUUsSUFBSSxDQUFBO0tBQUUsR0FBRyxPQUFPLENBQUMsT0FBTyxFQUFFLENBQUMsQ0FBQztJQUNyRixRQUFRLENBQUMsS0FBSyxFQUFFO1FBQUUsTUFBTSxFQUFFLFdBQVcsQ0FBQTtLQUFFLEdBQUcsT0FBTyxDQUFDLE9BQU8sR0FBRyxTQUFTLENBQUMsQ0FBQztJQUN2RSxrQkFBa0IsQ0FBQyxLQUFLLEVBQUU7UUFBRSxLQUFLLEVBQUUsV0FBVyxDQUFBO0tBQUUsR0FBRyxPQUFPLENBQUMsY0FBYyxFQUFFLENBQUMsQ0FBQztJQUM3RSxXQUFXLENBQUMsTUFBTSxFQUFFLE1BQU0sR0FBRyxPQUFPLENBQUMsZUFBZSxHQUFHLFNBQVMsQ0FBQyxDQUFDO0NBQ25FO0FBVUQ7Ozs7Ozs7Ozs7Ozs7O0dBY0c7QUFDSCxxQkFBYSxtQkFBbUI7O0lBVzVCLE9BQU8sQ0FBQyxRQUFRLENBQUMsTUFBTTtJQUN2QixPQUFPLENBQUMsUUFBUSxDQUFDLFFBQVE7SUFDekIsT0FBTyxDQUFDLFFBQVEsQ0FBQyxLQUFLO0lBQ3RCLE9BQU8sQ0FBQyxRQUFRLENBQUMsc0JBQXNCO0lBQ3ZDLE9BQU8sQ0FBQyxRQUFRLENBQUMsYUFBYTtJQUM5QixPQUFPLENBQUMsUUFBUSxDQUFDLEdBQUc7SUFOdEIsWUFDbUIsTUFBTSxFQUFFLGNBQWMsRUFDdEIsUUFBUSxFQUFFLHVCQUF1QixFQUNqQyxLQUFLLEVBQUUsaUJBQWlCLEVBQ3hCLHNCQUFzQixFQUFFLE1BQU0sT0FBTyxDQUFDLE1BQU0sR0FBRyxTQUFTLENBQUMsRUFDekQsYUFBYSxFQUFFLE1BQU0sRUFDckIsR0FBRyxHQUFFLE1BQXdDLEVBRy9EO0lBRUQ7Ozs7T0FJRztJQUNVLDBCQUEwQixDQUNyQyxNQUFNLEVBQUUsTUFBTSxFQUNkLE9BQU8sRUFBRSxFQUFFLEVBQ1gsZ0JBQWdCLENBQUMsRUFBRSxNQUFNLEdBQ3hCLE9BQU8sQ0FBQyx1QkFBdUIsR0FBRyxTQUFTLENBQUMsQ0E2RDlDO0lBaUVEOzs7O09BSUc7SUFDSCxPQUFPLENBQUMsWUFBWTtDQVlyQiJ9
@@ -0,0 +1 @@
1
+ {"version":3,"file":"outbox_trees_resolver.d.ts","sourceRoot":"","sources":["../../src/modules/outbox_trees_resolver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAEhF,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,gCAAgC,CAAC;AACzD,OAAO,EAAE,KAAK,MAAM,EAAgB,MAAM,uBAAuB,CAAC;AAClE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAE/D,OAAO,EAAE,KAAK,uBAAuB,EAAkC,MAAM,yBAAyB,CAAC;AACvG,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAEhE;;;GAGG;AACH,MAAM,WAAW,uBAAuB;IACtC,SAAS,CAAC,KAAK,EAAE;QAAE,KAAK,EAAE,WAAW,CAAC;QAAC,gBAAgB,EAAE,IAAI,CAAA;KAAE,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;IACrF,QAAQ,CAAC,KAAK,EAAE;QAAE,MAAM,EAAE,WAAW,CAAA;KAAE,GAAG,OAAO,CAAC,OAAO,GAAG,SAAS,CAAC,CAAC;IACvE,kBAAkB,CAAC,KAAK,EAAE;QAAE,KAAK,EAAE,WAAW,CAAA;KAAE,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,CAAC;IAC7E,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,GAAG,SAAS,CAAC,CAAC;CACnE;AAUD;;;;;;;;;;;;;;GAcG;AACH,qBAAa,mBAAmB;;IAW5B,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,sBAAsB;IACvC,OAAO,CAAC,QAAQ,CAAC,aAAa;IAC9B,OAAO,CAAC,QAAQ,CAAC,GAAG;IANtB,YACmB,MAAM,EAAE,cAAc,EACtB,QAAQ,EAAE,uBAAuB,EACjC,KAAK,EAAE,iBAAiB,EACxB,sBAAsB,EAAE,MAAM,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,EACzD,aAAa,EAAE,MAAM,EACrB,GAAG,GAAE,MAAwC,EAG/D;IAED;;;;OAIG;IACU,0BAA0B,CACrC,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,EAAE,EACX,gBAAgB,CAAC,EAAE,MAAM,GACxB,OAAO,CAAC,uBAAuB,GAAG,SAAS,CAAC,CA6D9C;IAiED;;;;OAIG;IACH,OAAO,CAAC,YAAY;CAYrB"}
@@ -0,0 +1,184 @@
1
+ import { chunkBy } from '@aztec/foundation/collection';
2
+ import { createLogger } from '@aztec/foundation/log';
3
+ import { getEpochAtSlot } from '@aztec/stdlib/epoch-helpers';
4
+ import { computeL2ToL1MembershipWitness } from '@aztec/stdlib/messaging';
5
+ /**
6
+ * Holds an in-memory cache of per-epoch Outbox roots used to build L2-to-L1 message membership
7
+ * witnesses, plus the single-flight registry that dedupes concurrent fetches.
8
+ *
9
+ * Roots are fetched lazily on request and cached against the node's synced L1 block: a cached entry
10
+ * is reused only while the synced L1 block is unchanged, and re-fetched from L1 once the node
11
+ * advances. The cache is intentionally not persisted — entries are valid for at most one L1 slot, so
12
+ * there is nothing worth carrying across restarts. The four merkle-tree levels are rebuilt per
13
+ * request from the archiver's raw L2 data via `computeL2ToL1MembershipWitness`; no trees or
14
+ * intermediate out-hashes are cached.
15
+ *
16
+ * TODO: Re-add permanent caching for sealed-and-L1-finalized epochs so their roots can be frozen
17
+ * and served without any L1 read once they can no longer change. That logic was dropped here to keep
18
+ * the cache simple; it would likely want a persisted store again.
19
+ */ export class OutboxTreesResolver {
20
+ outbox;
21
+ archiver;
22
+ store;
23
+ getSyncedL1BlockNumber;
24
+ epochDuration;
25
+ log;
26
+ /** In-memory cache of per-epoch Outbox roots, keyed by epoch number. */ #cache;
27
+ /** Single-flight registry for in-flight roots fetches, keyed by epoch number. */ #pending;
28
+ /** Adapter exposing the slice of the node API that `computeL2ToL1MembershipWitness` requires. */ #node;
29
+ constructor(outbox, archiver, store, getSyncedL1BlockNumber, epochDuration, log = createLogger('archiver:outbox')){
30
+ this.outbox = outbox;
31
+ this.archiver = archiver;
32
+ this.store = store;
33
+ this.getSyncedL1BlockNumber = getSyncedL1BlockNumber;
34
+ this.epochDuration = epochDuration;
35
+ this.log = log;
36
+ this.#cache = new Map();
37
+ this.#pending = new Map();
38
+ this.#node = this.makeNodeView();
39
+ }
40
+ /**
41
+ * Builds the L2-to-L1 membership witness for `message` in tx `txHash`. Returns `undefined` if the
42
+ * tx isn't yet in a block/epoch, or if the Outbox holds no covering partial-proof root for the
43
+ * tx's checkpoint as of the node's synced L1 block — callers should retry in that case.
44
+ */ async getL2ToL1MembershipWitness(txHash, message, messageIndexInTx) {
45
+ // Read the tx effect once up front only to learn which epoch's Outbox roots to fetch. The roots
46
+ // read hits L1, so it stays outside the store transaction below: holding the archiver's writer
47
+ // lock across a network round-trip would stall block sync.
48
+ const indexed = await this.archiver.getTxEffect(txHash);
49
+ if (!indexed) {
50
+ this.log.trace(`No tx effect for tx, no witness available`, {
51
+ txHash
52
+ });
53
+ return undefined;
54
+ }
55
+ // Derive the epoch from the tx's slot the same way the node assembles a mined receipt, so the
56
+ // witness helper sees the same view a `getTxReceipt` caller would.
57
+ const epochNumber = getEpochAtSlot(indexed.slotNumber, {
58
+ epochDuration: this.epochDuration
59
+ });
60
+ const roots = await this.#getRoots(epochNumber);
61
+ if (roots === undefined) {
62
+ return undefined;
63
+ }
64
+ const syncedBefore = await this.getSyncedL1BlockNumber();
65
+ try {
66
+ // Assemble the witness inside a single store transaction so every archiver read it depends on
67
+ // (the tx effect, the epoch's blocks, the target block and the checkpoint metadata) observes one
68
+ // consistent snapshot. `transactionAsync` serializes against writers, and the substore reads
69
+ // below bind to it automatically, so no store commit can land mid-assembly and splice together
70
+ // two different chain states — which would otherwise surface as a spurious "message does not
71
+ // exist" error even when the tx-effect index is healthy.
72
+ return await this.store.transactionAsync(async ()=>{
73
+ // Re-read the tx effect within the snapshot so the receipt's block number and tx index stay
74
+ // consistent with the block and checkpoint data read while building the witness. The epoch is
75
+ // reused from the pre-transaction read (it selected the roots above); if a reorg moved the tx
76
+ // to a different epoch in between, the block lookups simply miss and the helper returns
77
+ // undefined for the caller to retry.
78
+ const indexedInSnapshot = await this.archiver.getTxEffect(txHash);
79
+ if (!indexedInSnapshot) {
80
+ return undefined;
81
+ }
82
+ const receipt = {
83
+ txHash,
84
+ epochNumber,
85
+ blockNumber: indexedInSnapshot.l2BlockNumber,
86
+ txIndexInBlock: indexedInSnapshot.txIndexInBlock
87
+ };
88
+ return await computeL2ToL1MembershipWitness(this.#node, roots, message, receipt, messageIndexInTx);
89
+ });
90
+ } catch (err) {
91
+ // The helper throws if the locally-assembled epoch root disagrees with the cached Outbox root.
92
+ // The cached roots are pinned to the node's synced L1 block, but the block / checkpoint data is
93
+ // read from a store snapshot taken after that fetch, so if the archiver advanced in between the
94
+ // two can reflect different heights and produce a transient mismatch. When the synced block moved
95
+ // during assembly we treat the mismatch as transient and return undefined so the caller can
96
+ // retry; a mismatch at a stable synced block is a genuine node/L1 disagreement and must surface.
97
+ const syncedAfter = await this.getSyncedL1BlockNumber();
98
+ if (syncedBefore !== syncedAfter && err instanceof Error && err.message.includes('does not match Outbox')) {
99
+ this.log.debug(`Transient outbox root mismatch during sync, returning no witness`, {
100
+ txHash,
101
+ epoch: epochNumber
102
+ });
103
+ return undefined;
104
+ }
105
+ throw err;
106
+ }
107
+ }
108
+ /**
109
+ * Lazily returns the per-epoch Outbox roots, reusing the cached entry while the node's synced L1
110
+ * block is unchanged and re-fetching from L1 once it advances. Returns `undefined` if the node has
111
+ * not established a synced L1 block yet. Concurrent requests for the same epoch share a single
112
+ * in-flight fetch via {@link #pending}.
113
+ */ #getRoots(epoch) {
114
+ const inFlight = this.#pending.get(epoch);
115
+ if (inFlight) {
116
+ return inFlight;
117
+ }
118
+ const promise = this.#refreshRoots(epoch).finally(()=>{
119
+ this.#pending.delete(epoch);
120
+ });
121
+ this.#pending.set(epoch, promise);
122
+ return promise;
123
+ }
124
+ /**
125
+ * Single-flight body for {@link #getRoots}. Re-reads the cached entry and synced block here —
126
+ * rather than capturing them before winning the {@link #pending} slot — so a late entrant that
127
+ * missed the slot cannot overwrite a fresher entry the previous winner just wrote.
128
+ */ async #refreshRoots(epoch) {
129
+ const synced = await this.getSyncedL1BlockNumber();
130
+ if (synced === undefined) {
131
+ // The archiver has no synced L1 block yet, so there is no consistent height to read the Outbox
132
+ // at. The caller retries once the node has synced.
133
+ return undefined;
134
+ }
135
+ const cached = this.#cache.get(epoch);
136
+ if (cached && cached.fetchedAtL1Block === synced) {
137
+ return cached.roots;
138
+ }
139
+ // TODO: Re-add permanent caching for sealed-and-L1-finalized epochs so we can skip the L1 read
140
+ // entirely once an epoch's roots can no longer change. For now the cache is valid only for the
141
+ // current synced L1 block and we re-query the Outbox whenever the node advances.
142
+ const roots = await this.outbox.getRoots(epoch, {
143
+ blockNumber: synced
144
+ });
145
+ this.#cache.set(epoch, {
146
+ roots,
147
+ fetchedAtL1Block: synced
148
+ });
149
+ this.log.trace(`Fetched outbox roots for epoch ${epoch}`, {
150
+ epoch,
151
+ syncedL1Block: synced,
152
+ nonZeroRoots: roots.filter((r)=>!r.isZero()).length
153
+ });
154
+ return roots;
155
+ }
156
+ /**
157
+ * Returns the L2-to-L1 messages in `epoch`, organized as `checkpoint[] → block[] → tx[] →
158
+ * message[]`. Mirrors `AztecNodeService.getL2ToL1Messages` so the resolver can run off the
159
+ * archiver directly without going through the node RPC surface.
160
+ */ async #getL2ToL1Messages(epoch) {
161
+ const blocks = await this.archiver.getBlocks({
162
+ epoch,
163
+ onlyCheckpointed: true
164
+ });
165
+ const blocksInCheckpoints = chunkBy(blocks, (block)=>block.header.globalVariables.slotNumber);
166
+ return blocksInCheckpoints.map((slotBlocks)=>slotBlocks.map((block)=>block.body.txEffects.map((txEffect)=>txEffect.l2ToL1Msgs)));
167
+ }
168
+ /**
169
+ * Builds the node-shaped adapter handed to `computeL2ToL1MembershipWitness`. The helper invokes
170
+ * `getL2ToL1Messages` / `getBlock` / `getCheckpointsData`; `getTxReceipt` is part of the helper's
171
+ * node type but is never invoked because we always pass an already-resolved receipt.
172
+ */ makeNodeView() {
173
+ return {
174
+ getL2ToL1Messages: (epoch)=>this.#getL2ToL1Messages(epoch),
175
+ getBlock: (param)=>this.archiver.getBlock({
176
+ number: param
177
+ }),
178
+ getCheckpointsData: (query)=>this.archiver.getCheckpointsData(query),
179
+ getTxReceipt: ()=>{
180
+ throw new Error('OutboxTreesResolver always passes a resolved receipt; getTxReceipt must not be called');
181
+ }
182
+ };
183
+ }
184
+ }