@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,5 +1,6 @@
1
1
  import { SlotNumber } from '@aztec/foundation/branded-types';
2
2
  import { Fr } from '@aztec/foundation/curves/bn254';
3
+ import { DateProvider } from '@aztec/foundation/timer';
3
4
  import type { BlockHash } from '@aztec/stdlib/block';
4
5
  import type { L1RollupConstants } from '@aztec/stdlib/epoch-helpers';
5
6
  import type { BlockHeader } from '@aztec/stdlib/tx';
@@ -8,6 +9,9 @@ import { Archiver } from '../archiver.js';
8
9
  import { ArchiverInstrumentation } from '../modules/instrumentation.js';
9
10
  import type { ArchiverDataStores } from '../store/data_stores.js';
10
11
  import { L2TipsCache } from '../store/l2_tips_cache.js';
12
+ type NoopL1ArchiverConfigOverrides = Partial<{
13
+ skipOrphanProposedBlockPruning: boolean;
14
+ }>;
11
15
  /**
12
16
  * Archiver with mocked L1 connectivity for testing.
13
17
  * Uses mock L1 clients and a noop synchronizer, enabling tests that
@@ -16,7 +20,7 @@ import { L2TipsCache } from '../store/l2_tips_cache.js';
16
20
  export declare class NoopL1Archiver extends Archiver {
17
21
  constructor(dataStores: ArchiverDataStores, l1Constants: L1RollupConstants & {
18
22
  genesisArchiveRoot: Fr;
19
- }, instrumentation: ArchiverInstrumentation, initialHeader: BlockHeader, initialBlockHash: BlockHash, l2TipsCache: L2TipsCache);
23
+ }, instrumentation: ArchiverInstrumentation, initialHeader: BlockHeader, initialBlockHash: BlockHash, l2TipsCache: L2TipsCache, dateProvider?: DateProvider, configOverrides?: NoopL1ArchiverConfigOverrides);
20
24
  /** Override start to skip L1 validation checks. */
21
25
  start(_blockUntilSynced?: boolean): Promise<void>;
22
26
  /** Always reports as fully synced since there is no real L1 to sync from. */
@@ -25,5 +29,6 @@ export declare class NoopL1Archiver extends Archiver {
25
29
  /** Creates an archiver with mocked L1 connectivity for testing. */
26
30
  export declare function createNoopL1Archiver(dataStores: ArchiverDataStores, l1Constants: L1RollupConstants & {
27
31
  genesisArchiveRoot: Fr;
28
- }, telemetry: TelemetryClient | undefined, initialHeader: BlockHeader): Promise<NoopL1Archiver>;
29
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibm9vcF9sMV9hcmNoaXZlci5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3Rlc3Qvbm9vcF9sMV9hcmNoaXZlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFJQSxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0saUNBQWlDLENBQUM7QUFFN0QsT0FBTyxFQUFFLEVBQUUsRUFBRSxNQUFNLGdDQUFnQyxDQUFDO0FBSXBELE9BQU8sS0FBSyxFQUFtQixTQUFTLEVBQUUsTUFBTSxxQkFBcUIsQ0FBQztBQUN0RSxPQUFPLEtBQUssRUFBRSxpQkFBaUIsRUFBRSxNQUFNLDZCQUE2QixDQUFDO0FBQ3JFLE9BQU8sS0FBSyxFQUFFLFdBQVcsRUFBRSxNQUFNLGtCQUFrQixDQUFDO0FBQ3BELE9BQU8sRUFBRSxLQUFLLGVBQWUsRUFBbUMsTUFBTSx5QkFBeUIsQ0FBQztBQUtoRyxPQUFPLEVBQUUsUUFBUSxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFDMUMsT0FBTyxFQUFFLHVCQUF1QixFQUFFLE1BQU0sK0JBQStCLENBQUM7QUFFeEUsT0FBTyxLQUFLLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUNsRSxPQUFPLEVBQUUsV0FBVyxFQUFFLE1BQU0sMkJBQTJCLENBQUM7QUF5QnhEOzs7O0dBSUc7QUFDSCxxQkFBYSxjQUFlLFNBQVEsUUFBUTtJQUMxQyxZQUNFLFVBQVUsRUFBRSxrQkFBa0IsRUFDOUIsV0FBVyxFQUFFLGlCQUFpQixHQUFHO1FBQUUsa0JBQWtCLEVBQUUsRUFBRSxDQUFBO0tBQUUsRUFDM0QsZUFBZSxFQUFFLHVCQUF1QixFQUN4QyxhQUFhLEVBQUUsV0FBVyxFQUMxQixnQkFBZ0IsRUFBRSxTQUFTLEVBQzNCLFdBQVcsRUFBRSxXQUFXLEVBaUR6QjtJQUVELG1EQUFtRDtJQUNuQyxLQUFLLENBQUMsaUJBQWlCLENBQUMsRUFBRSxPQUFPLEdBQUcsT0FBTyxDQUFDLElBQUksQ0FBQyxDQUloRTtJQUVELDZFQUE2RTtJQUM3RCxxQkFBcUIsSUFBSSxPQUFPLENBQUMsVUFBVSxHQUFHLFNBQVMsQ0FBQyxDQUV2RTtDQUNGO0FBRUQsbUVBQW1FO0FBQ25FLHdCQUFzQixvQkFBb0IsQ0FDeEMsVUFBVSxFQUFFLGtCQUFrQixFQUM5QixXQUFXLEVBQUUsaUJBQWlCLEdBQUc7SUFBRSxrQkFBa0IsRUFBRSxFQUFFLENBQUE7Q0FBRSxFQUMzRCxTQUFTLDZCQUF3QyxFQUNqRCxhQUFhLEVBQUUsV0FBVyxHQUN6QixPQUFPLENBQUMsY0FBYyxDQUFDLENBU3pCIn0=
32
+ }, telemetry: TelemetryClient | undefined, initialHeader: BlockHeader, dateProvider?: DateProvider, configOverrides?: NoopL1ArchiverConfigOverrides): Promise<NoopL1Archiver>;
33
+ export {};
34
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibm9vcF9sMV9hcmNoaXZlci5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3Rlc3Qvbm9vcF9sMV9hcmNoaXZlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFHQSxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0saUNBQWlDLENBQUM7QUFFN0QsT0FBTyxFQUFFLEVBQUUsRUFBRSxNQUFNLGdDQUFnQyxDQUFDO0FBRXBELE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUV2RCxPQUFPLEtBQUssRUFBbUIsU0FBUyxFQUFXLE1BQU0scUJBQXFCLENBQUM7QUFDL0UsT0FBTyxLQUFLLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSw2QkFBNkIsQ0FBQztBQUNyRSxPQUFPLEtBQUssRUFBRSxXQUFXLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUNwRCxPQUFPLEVBQUUsS0FBSyxlQUFlLEVBQW1DLE1BQU0seUJBQXlCLENBQUM7QUFLaEcsT0FBTyxFQUFFLFFBQVEsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBQzFDLE9BQU8sRUFBRSx1QkFBdUIsRUFBRSxNQUFNLCtCQUErQixDQUFDO0FBRXhFLE9BQU8sS0FBSyxFQUFFLGtCQUFrQixFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFDbEUsT0FBTyxFQUFFLFdBQVcsRUFBRSxNQUFNLDJCQUEyQixDQUFDO0FBRXhELEtBQUssNkJBQTZCLEdBQUcsT0FBTyxDQUFDO0lBQzNDLDhCQUE4QixFQUFFLE9BQU8sQ0FBQztDQUN6QyxDQUFDLENBQUM7QUF5Qkg7Ozs7R0FJRztBQUNILHFCQUFhLGNBQWUsU0FBUSxRQUFRO0lBQzFDLFlBQ0UsVUFBVSxFQUFFLGtCQUFrQixFQUM5QixXQUFXLEVBQUUsaUJBQWlCLEdBQUc7UUFBRSxrQkFBa0IsRUFBRSxFQUFFLENBQUE7S0FBRSxFQUMzRCxlQUFlLEVBQUUsdUJBQXVCLEVBQ3hDLGFBQWEsRUFBRSxXQUFXLEVBQzFCLGdCQUFnQixFQUFFLFNBQVMsRUFDM0IsV0FBVyxFQUFFLFdBQVcsRUFDeEIsWUFBWSxHQUFFLFlBQWlDLEVBQy9DLGVBQWUsR0FBRSw2QkFBa0MsRUFvRHBEO0lBRUQsbURBQW1EO0lBQ25DLEtBQUssQ0FBQyxpQkFBaUIsQ0FBQyxFQUFFLE9BQU8sR0FBRyxPQUFPLENBQUMsSUFBSSxDQUFDLENBSWhFO0lBRUQsNkVBQTZFO0lBQzdELHFCQUFxQixJQUFJLE9BQU8sQ0FBQyxVQUFVLEdBQUcsU0FBUyxDQUFDLENBRXZFO0NBQ0Y7QUFFRCxtRUFBbUU7QUFDbkUsd0JBQXNCLG9CQUFvQixDQUN4QyxVQUFVLEVBQUUsa0JBQWtCLEVBQzlCLFdBQVcsRUFBRSxpQkFBaUIsR0FBRztJQUFFLGtCQUFrQixFQUFFLEVBQUUsQ0FBQTtDQUFFLEVBQzNELFNBQVMsNkJBQXdDLEVBQ2pELGFBQWEsRUFBRSxXQUFXLEVBQzFCLFlBQVksQ0FBQyxFQUFFLFlBQVksRUFDM0IsZUFBZSxDQUFDLEVBQUUsNkJBQTZCLEdBQzlDLE9BQU8sQ0FBQyxjQUFjLENBQUMsQ0FrQnpCIn0=
@@ -1 +1 @@
1
- {"version":3,"file":"noop_l1_archiver.d.ts","sourceRoot":"","sources":["../../src/test/noop_l1_archiver.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAC;AAE7D,OAAO,EAAE,EAAE,EAAE,MAAM,gCAAgC,CAAC;AAIpD,OAAO,KAAK,EAAmB,SAAS,EAAE,MAAM,qBAAqB,CAAC;AACtE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AACrE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,KAAK,eAAe,EAAmC,MAAM,yBAAyB,CAAC;AAKhG,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,uBAAuB,EAAE,MAAM,+BAA+B,CAAC;AAExE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAClE,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAyBxD;;;;GAIG;AACH,qBAAa,cAAe,SAAQ,QAAQ;IAC1C,YACE,UAAU,EAAE,kBAAkB,EAC9B,WAAW,EAAE,iBAAiB,GAAG;QAAE,kBAAkB,EAAE,EAAE,CAAA;KAAE,EAC3D,eAAe,EAAE,uBAAuB,EACxC,aAAa,EAAE,WAAW,EAC1B,gBAAgB,EAAE,SAAS,EAC3B,WAAW,EAAE,WAAW,EAiDzB;IAED,mDAAmD;IACnC,KAAK,CAAC,iBAAiB,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAIhE;IAED,6EAA6E;IAC7D,qBAAqB,IAAI,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC,CAEvE;CACF;AAED,mEAAmE;AACnE,wBAAsB,oBAAoB,CACxC,UAAU,EAAE,kBAAkB,EAC9B,WAAW,EAAE,iBAAiB,GAAG;IAAE,kBAAkB,EAAE,EAAE,CAAA;CAAE,EAC3D,SAAS,6BAAwC,EACjD,aAAa,EAAE,WAAW,GACzB,OAAO,CAAC,cAAc,CAAC,CASzB"}
1
+ {"version":3,"file":"noop_l1_archiver.d.ts","sourceRoot":"","sources":["../../src/test/noop_l1_archiver.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAC;AAE7D,OAAO,EAAE,EAAE,EAAE,MAAM,gCAAgC,CAAC;AAEpD,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAEvD,OAAO,KAAK,EAAmB,SAAS,EAAW,MAAM,qBAAqB,CAAC;AAC/E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AACrE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,KAAK,eAAe,EAAmC,MAAM,yBAAyB,CAAC;AAKhG,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,uBAAuB,EAAE,MAAM,+BAA+B,CAAC;AAExE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAClE,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAExD,KAAK,6BAA6B,GAAG,OAAO,CAAC;IAC3C,8BAA8B,EAAE,OAAO,CAAC;CACzC,CAAC,CAAC;AAyBH;;;;GAIG;AACH,qBAAa,cAAe,SAAQ,QAAQ;IAC1C,YACE,UAAU,EAAE,kBAAkB,EAC9B,WAAW,EAAE,iBAAiB,GAAG;QAAE,kBAAkB,EAAE,EAAE,CAAA;KAAE,EAC3D,eAAe,EAAE,uBAAuB,EACxC,aAAa,EAAE,WAAW,EAC1B,gBAAgB,EAAE,SAAS,EAC3B,WAAW,EAAE,WAAW,EACxB,YAAY,GAAE,YAAiC,EAC/C,eAAe,GAAE,6BAAkC,EAoDpD;IAED,mDAAmD;IACnC,KAAK,CAAC,iBAAiB,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAIhE;IAED,6EAA6E;IAC7D,qBAAqB,IAAI,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC,CAEvE;CACF;AAED,mEAAmE;AACnE,wBAAsB,oBAAoB,CACxC,UAAU,EAAE,kBAAkB,EAC9B,WAAW,EAAE,iBAAiB,GAAG;IAAE,kBAAkB,EAAE,EAAE,CAAA;CAAE,EAC3D,SAAS,6BAAwC,EACjD,aAAa,EAAE,WAAW,EAC1B,YAAY,CAAC,EAAE,YAAY,EAC3B,eAAe,CAAC,EAAE,6BAA6B,GAC9C,OAAO,CAAC,cAAc,CAAC,CAkBzB"}
@@ -24,7 +24,7 @@ import { L2TipsCache } from '../store/l2_tips_cache.js';
24
24
  return Promise.resolve();
25
25
  }
26
26
  syncFromL1(_initialSyncComplete) {
27
- return Promise.resolve();
27
+ return Promise.resolve([]);
28
28
  }
29
29
  }
30
30
  /**
@@ -32,20 +32,19 @@ import { L2TipsCache } from '../store/l2_tips_cache.js';
32
32
  * Uses mock L1 clients and a noop synchronizer, enabling tests that
33
33
  * don't require real Ethereum connectivity.
34
34
  */ export class NoopL1Archiver extends Archiver {
35
- constructor(dataStores, l1Constants, instrumentation, initialHeader, initialBlockHash, l2TipsCache){
35
+ constructor(dataStores, l1Constants, instrumentation, initialHeader, initialBlockHash, l2TipsCache, dateProvider = new DateProvider(), configOverrides = {}){
36
36
  // Create mocks for L1 clients
37
37
  const publicClient = mock();
38
38
  const debugClient = mock();
39
39
  const rollup = mock();
40
+ const outbox = mock();
40
41
  const blobClient = mock();
41
42
  // Mock methods called during start()
42
43
  blobClient.testSources.mockResolvedValue();
43
44
  publicClient.getBlockNumber.mockResolvedValue(1n);
44
45
  const events = new EventEmitter();
45
46
  const synchronizer = new NoopL1Synchronizer(instrumentation.tracer);
46
- const epochCache = mock();
47
- epochCache.pipeliningOffset.mockReturnValue(0);
48
- super(publicClient, debugClient, rollup, {
47
+ super(publicClient, debugClient, rollup, outbox, {
49
48
  rollupAddress: EthAddress.ZERO,
50
49
  registryAddress: EthAddress.ZERO,
51
50
  inboxAddress: EthAddress.ZERO,
@@ -58,11 +57,15 @@ import { L2TipsCache } from '../store/l2_tips_cache.js';
58
57
  maxAllowedEthClientDriftSeconds: 300,
59
58
  ethereumAllowNoDebugHosts: true,
60
59
  skipHistoricalLogsCheck: true,
61
- orphanProposedBlockPruneGraceSeconds: 2
60
+ checkpointProposalSyncGrace: 4,
61
+ orphanPruneNoProposalTolerance: 1,
62
+ skipOrphanProposedBlockPruning: true,
63
+ blockDuration: 2,
64
+ ...configOverrides
62
65
  }, blobClient, instrumentation, {
63
66
  ...l1Constants,
64
67
  l1StartBlockHash: Buffer32.random()
65
- }, synchronizer, events, initialHeader, initialBlockHash, l2TipsCache, epochCache, new DateProvider());
68
+ }, synchronizer, events, initialHeader, initialBlockHash, l2TipsCache, dateProvider);
66
69
  }
67
70
  /** Override start to skip L1 validation checks. */ start(_blockUntilSynced) {
68
71
  // Just start the running promise without L1 checks
@@ -73,7 +76,7 @@ import { L2TipsCache } from '../store/l2_tips_cache.js';
73
76
  return Promise.resolve(SlotNumber(Number.MAX_SAFE_INTEGER));
74
77
  }
75
78
  }
76
- /** Creates an archiver with mocked L1 connectivity for testing. */ export async function createNoopL1Archiver(dataStores, l1Constants, telemetry = getTelemetryClient(), initialHeader) {
79
+ /** Creates an archiver with mocked L1 connectivity for testing. */ export async function createNoopL1Archiver(dataStores, l1Constants, telemetry = getTelemetryClient(), initialHeader, dateProvider, configOverrides) {
77
80
  const instrumentation = await ArchiverInstrumentation.new(telemetry, ()=>dataStores.db.estimateSize());
78
81
  // Mirror the production factory: precompute the dynamic genesis block hash from the injected
79
82
  // initial header so `L2TipsCache` reports the correct tip hash at block 0. Without this, the
@@ -81,5 +84,5 @@ import { L2TipsCache } from '../store/l2_tips_cache.js';
81
84
  // with default empty genesis.
82
85
  const initialBlockHash = await initialHeader.hash();
83
86
  const l2TipsCache = new L2TipsCache(dataStores.blocks, initialBlockHash);
84
- return new NoopL1Archiver(dataStores, l1Constants, instrumentation, initialHeader, initialBlockHash, l2TipsCache);
87
+ return new NoopL1Archiver(dataStores, l1Constants, instrumentation, initialHeader, initialBlockHash, l2TipsCache, dateProvider, configOverrides);
85
88
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aztec/archiver",
3
- "version": "0.0.1-commit.993d240",
3
+ "version": "0.0.1-commit.9a89641",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": "./dest/index.js",
@@ -65,18 +65,18 @@
65
65
  ]
66
66
  },
67
67
  "dependencies": {
68
- "@aztec/blob-client": "0.0.1-commit.993d240",
69
- "@aztec/blob-lib": "0.0.1-commit.993d240",
70
- "@aztec/constants": "0.0.1-commit.993d240",
71
- "@aztec/epoch-cache": "0.0.1-commit.993d240",
72
- "@aztec/ethereum": "0.0.1-commit.993d240",
73
- "@aztec/foundation": "0.0.1-commit.993d240",
74
- "@aztec/kv-store": "0.0.1-commit.993d240",
75
- "@aztec/l1-artifacts": "0.0.1-commit.993d240",
76
- "@aztec/noir-protocol-circuits-types": "0.0.1-commit.993d240",
77
- "@aztec/protocol-contracts": "0.0.1-commit.993d240",
78
- "@aztec/stdlib": "0.0.1-commit.993d240",
79
- "@aztec/telemetry-client": "0.0.1-commit.993d240",
68
+ "@aztec/blob-client": "0.0.1-commit.9a89641",
69
+ "@aztec/blob-lib": "0.0.1-commit.9a89641",
70
+ "@aztec/constants": "0.0.1-commit.9a89641",
71
+ "@aztec/epoch-cache": "0.0.1-commit.9a89641",
72
+ "@aztec/ethereum": "0.0.1-commit.9a89641",
73
+ "@aztec/foundation": "0.0.1-commit.9a89641",
74
+ "@aztec/kv-store": "0.0.1-commit.9a89641",
75
+ "@aztec/l1-artifacts": "6.0.0-nightly.20260807",
76
+ "@aztec/protocol-contracts": "0.0.1-commit.9a89641",
77
+ "@aztec/standard-contracts": "0.0.1-commit.9a89641",
78
+ "@aztec/stdlib": "0.0.1-commit.9a89641",
79
+ "@aztec/telemetry-client": "0.0.1-commit.9a89641",
80
80
  "lodash.groupby": "^4.6.0",
81
81
  "lodash.omit": "^4.5.0",
82
82
  "tslib": "^2.5.0",
package/src/archiver.ts CHANGED
@@ -1,11 +1,11 @@
1
1
  import type { BlobClientInterface } from '@aztec/blob-client/client';
2
2
  import { EpochCache } from '@aztec/epoch-cache';
3
- import { BlockTagTooOldError, RollupContract } from '@aztec/ethereum/contracts';
3
+ import { BlockTagTooOldError, OutboxContract, RollupContract } from '@aztec/ethereum/contracts';
4
4
  import type { L1ContractAddresses } from '@aztec/ethereum/l1-contract-addresses';
5
5
  import type { ViemPublicClient, ViemPublicDebugClient } from '@aztec/ethereum/types';
6
6
  import { BlockNumber, CheckpointNumber, EpochNumber, SlotNumber } from '@aztec/foundation/branded-types';
7
7
  import { Buffer32 } from '@aztec/foundation/buffer';
8
- import { merge, pick } from '@aztec/foundation/collection';
8
+ import { merge } from '@aztec/foundation/collection';
9
9
  import { Fr } from '@aztec/foundation/curves/bn254';
10
10
  import { EthAddress } from '@aztec/foundation/eth-address';
11
11
  import { type Logger, createLogger } from '@aztec/foundation/log';
@@ -20,6 +20,7 @@ import {
20
20
  L2BlockSourceEvents,
21
21
  type L2Tips,
22
22
  type ValidateCheckpointResult,
23
+ l2TipsEqual,
23
24
  } from '@aztec/stdlib/block';
24
25
  import { type ProposedCheckpointInput, PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
25
26
  import {
@@ -30,7 +31,9 @@ import {
30
31
  getTimestampForSlot,
31
32
  getTimestampRangeForEpoch,
32
33
  } from '@aztec/stdlib/epoch-helpers';
33
- import type { BlockHeader } from '@aztec/stdlib/tx';
34
+ import type { L2ToL1MembershipWitness } from '@aztec/stdlib/messaging';
35
+ import { ConsensusTimetable } from '@aztec/stdlib/timetable';
36
+ import type { BlockHeader, TxHash } from '@aztec/stdlib/tx';
34
37
  import { type TelemetryClient, type Traceable, type Tracer, trackSpan } from '@aztec/telemetry-client';
35
38
 
36
39
  import { type ArchiverConfig, mapArchiverConfig } from './config.js';
@@ -41,6 +44,7 @@ import { ArchiverDataSourceBase } from './modules/data_source_base.js';
41
44
  import { ArchiverDataStoreUpdater } from './modules/data_store_updater.js';
42
45
  import type { ArchiverInstrumentation } from './modules/instrumentation.js';
43
46
  import type { ArchiverL1Synchronizer } from './modules/l1_synchronizer.js';
47
+ import { OutboxTreesResolver } from './modules/outbox_trees_resolver.js';
44
48
  import { type ArchiverDataStores, backupArchiverDataStores, getArchiverSynchPoint } from './store/data_stores.js';
45
49
  import { L2TipsCache } from './store/l2_tips_cache.js';
46
50
 
@@ -70,6 +74,16 @@ export type ArchiverDeps = {
70
74
  dateProvider?: DateProvider;
71
75
  };
72
76
 
77
+ /** Minimal dependency for observing whether a checkpoint proposal was received for a slot. */
78
+ export type CheckpointProposalPresence = {
79
+ /** Returns true when a checkpoint proposal for `slot` has been retained locally. */
80
+ hasCheckpointProposalForSlot(slot: SlotNumber): Promise<boolean>;
81
+ };
82
+
83
+ const noCheckpointProposalPresence: CheckpointProposalPresence = {
84
+ hasCheckpointProposalForSlot: () => Promise.resolve(false),
85
+ };
86
+
73
87
  /**
74
88
  * Pulls checkpoints in a non-blocking manner and provides interface for their retrieval.
75
89
  * Responsible for handling robust L1 polling so that other components do not need to
@@ -85,6 +99,9 @@ export class Archiver extends ArchiverDataSourceBase implements L2BlockSink, Tra
85
99
  /** L1 synchronizer that handles fetching checkpoints and messages from L1. */
86
100
  private readonly synchronizer: ArchiverL1Synchronizer;
87
101
 
102
+ /** Resolver for L2-to-L1 message membership witnesses, built over this archiver's read view. */
103
+ private readonly outboxTreesResolver: OutboxTreesResolver;
104
+
88
105
  private initialSyncComplete: boolean = false;
89
106
  private initialSyncPromise: PromiseWithResolvers<void>;
90
107
 
@@ -97,6 +114,9 @@ export class Archiver extends ArchiverDataSourceBase implements L2BlockSink, Tra
97
114
  /** In-memory cache for L2 chain tips. */
98
115
  private readonly l2TipsCache: L2TipsCache;
99
116
 
117
+ /** Consensus timing model used for proposed-checkpoint arrival expectations. */
118
+ private readonly timetable: ConsensusTimetable;
119
+
100
120
  public readonly tracer: Tracer;
101
121
 
102
122
  private readonly instrumentation: ArchiverInstrumentation;
@@ -106,6 +126,7 @@ export class Archiver extends ArchiverDataSourceBase implements L2BlockSink, Tra
106
126
  * @param publicClient - A client for interacting with the Ethereum node.
107
127
  * @param debugClient - A client for interacting with the Ethereum node for debug/trace methods.
108
128
  * @param rollup - Rollup contract instance.
129
+ * @param outbox - Outbox contract instance, used to read per-epoch L2-to-L1 message roots.
109
130
  * @param inbox - Inbox contract instance.
110
131
  * @param l1Addresses - L1 contract addresses (registry, governance proposer, slashing proposer).
111
132
  * @param dataStores - Archiver substores for storage & retrieval of blocks, encrypted logs & contract data.
@@ -118,7 +139,6 @@ export class Archiver extends ArchiverDataSourceBase implements L2BlockSink, Tra
118
139
  * @param initialHeader - Genesis block header.
119
140
  * @param initialBlockHash - Precomputed hash of the genesis block header.
120
141
  * @param l2TipsCache - In-memory cache for L2 chain tips.
121
- * @param epochCache - Cache used to compute the proposer pipelining offset.
122
142
  * @param dateProvider - Provider for current date/time, used for wall-clock orphan-block pruning.
123
143
  * @param log - A logger.
124
144
  */
@@ -126,6 +146,7 @@ export class Archiver extends ArchiverDataSourceBase implements L2BlockSink, Tra
126
146
  private readonly publicClient: ViemPublicClient,
127
147
  private readonly debugClient: ViemPublicDebugClient,
128
148
  private readonly rollup: RollupContract,
149
+ private readonly outbox: OutboxContract,
129
150
  private readonly l1Addresses: Pick<
130
151
  L1ContractAddresses,
131
152
  'rollupAddress' | 'registryAddress' | 'inboxAddress' | 'governanceProposerAddress'
@@ -140,7 +161,10 @@ export class Archiver extends ArchiverDataSourceBase implements L2BlockSink, Tra
140
161
  maxAllowedEthClientDriftSeconds: number;
141
162
  ethereumAllowNoDebugHosts?: boolean;
142
163
  skipHistoricalLogsCheck?: boolean;
143
- orphanProposedBlockPruneGraceSeconds: number;
164
+ checkpointProposalSyncGrace: number;
165
+ orphanPruneNoProposalTolerance: number;
166
+ skipOrphanProposedBlockPruning: boolean;
167
+ blockDuration: number;
144
168
  },
145
169
  private readonly blobClient: BlobClientInterface,
146
170
  instrumentation: ArchiverInstrumentation,
@@ -153,8 +177,8 @@ export class Archiver extends ArchiverDataSourceBase implements L2BlockSink, Tra
153
177
  initialHeader: BlockHeader,
154
178
  initialBlockHash: BlockHash,
155
179
  l2TipsCache: L2TipsCache,
156
- private readonly epochCache: EpochCache,
157
180
  private readonly dateProvider: DateProvider,
181
+ private checkpointProposalPresence: CheckpointProposalPresence = noCheckpointProposalPresence,
158
182
  private readonly log: Logger = createLogger('archiver'),
159
183
  ) {
160
184
  super(dataStores, l1Constants, initialHeader, initialBlockHash, l1Constants.genesisArchiveRoot);
@@ -165,10 +189,25 @@ export class Archiver extends ArchiverDataSourceBase implements L2BlockSink, Tra
165
189
  this.synchronizer = synchronizer;
166
190
  this.events = events;
167
191
  this.l2TipsCache = l2TipsCache;
192
+ this.timetable = new ConsensusTimetable({
193
+ l1Constants,
194
+ blockDuration: this.config.blockDuration,
195
+ checkpointProposalSyncGrace: this.config.checkpointProposalSyncGrace,
196
+ });
168
197
  this.updater = new ArchiverDataStoreUpdater(this.dataStores, this.l2TipsCache, {
169
198
  rollupManaLimit: l1Constants.rollupManaLimit,
170
199
  });
171
200
 
201
+ // The resolver reads its witness data from this archiver and pins its lazy Outbox-root fetches
202
+ // to the archiver's synced L1 block. No method on `this` is invoked during construction.
203
+ this.outboxTreesResolver = new OutboxTreesResolver(
204
+ this.outbox,
205
+ this,
206
+ this.dataStores.db,
207
+ () => Promise.resolve(this.getL1BlockNumber()),
208
+ l1Constants.epochDuration,
209
+ );
210
+
172
211
  // Running promise starts with a small interval inbetween runs, so all iterations needed for the initial sync
173
212
  // are done as fast as possible. This then gets updated once the initial sync completes.
174
213
  this.runningPromise = new RunningPromise(
@@ -179,6 +218,27 @@ export class Archiver extends ArchiverDataSourceBase implements L2BlockSink, Tra
179
218
  );
180
219
  }
181
220
 
221
+ /**
222
+ * Returns the L2-to-L1 membership witness for `message` emitted by tx `txHash`. The node selects
223
+ * the smallest partial-proof root on the Outbox that covers the tx's checkpoint and builds the
224
+ * witness against it.
225
+ *
226
+ * The node reads the Outbox roots lazily, pinned to its synced L1 block, so the witness reflects
227
+ * the node's synced view. Returns `undefined` if the tx isn't yet in a block/epoch or no covering
228
+ * root has landed on L1 as of the synced block.
229
+ *
230
+ * Caveat: roots are cached only for the current synced L1 block and re-fetched once the node
231
+ * advances, so a reorg is picked up on the next synced-block advance rather than re-validated per
232
+ * request.
233
+ */
234
+ public getL2ToL1MembershipWitness(
235
+ txHash: TxHash,
236
+ message: Fr,
237
+ messageIndexInTx?: number,
238
+ ): Promise<L2ToL1MembershipWitness | undefined> {
239
+ return this.outboxTreesResolver.getL2ToL1MembershipWitness(txHash, message, messageIndexInTx);
240
+ }
241
+
182
242
  /** Updates archiver config */
183
243
  public updateConfig(newConfig: Partial<ArchiverConfig>) {
184
244
  this.config = merge(this.config, mapArchiverConfig(newConfig));
@@ -235,6 +295,11 @@ export class Archiver extends ArchiverDataSourceBase implements L2BlockSink, Tra
235
295
  return this.runningPromise.trigger();
236
296
  }
237
297
 
298
+ /** Sets the proposal-presence provider used by orphan proposed-block pruning. */
299
+ public setCheckpointProposalPresence(checkpointProposalPresence: CheckpointProposalPresence): void {
300
+ this.checkpointProposalPresence = checkpointProposalPresence;
301
+ }
302
+
238
303
  public trySyncImmediate() {
239
304
  try {
240
305
  return this.syncImmediate();
@@ -276,9 +341,10 @@ export class Archiver extends ArchiverDataSourceBase implements L2BlockSink, Tra
276
341
  * Called at the beginning of each sync iteration.
277
342
  * Items are processed in the order they were queued.
278
343
  */
279
- private async processInboundQueue(): Promise<void> {
344
+ private async processInboundQueue(): Promise<L2Block[]> {
345
+ const blocksAdded: L2Block[] = [];
280
346
  if (this.inboundQueue.length === 0) {
281
- return;
347
+ return blocksAdded;
282
348
  }
283
349
 
284
350
  // Take all items from the queue
@@ -315,6 +381,7 @@ export class Archiver extends ArchiverDataSourceBase implements L2BlockSink, Tra
315
381
  if (type === 'block') {
316
382
  const [durationMs] = await elapsed(() => this.updater.addProposedBlock(item.block));
317
383
  this.instrumentation.processNewProposedBlock(durationMs, item.block);
384
+ blocksAdded.push(item.block);
318
385
  } else {
319
386
  await this.updater.addProposedCheckpoint(item.checkpoint);
320
387
  }
@@ -323,6 +390,7 @@ export class Archiver extends ArchiverDataSourceBase implements L2BlockSink, Tra
323
390
  } catch (err: any) {
324
391
  if (err instanceof BlockAlreadyCheckpointedError) {
325
392
  this.log.debug(`Proposed block ${itemNumber} matches already checkpointed block, ignoring late proposal`);
393
+ // A late proposal that matches an already-checkpointed block adds nothing new, so it is not appended.
326
394
  resolve();
327
395
  continue;
328
396
  }
@@ -333,6 +401,7 @@ export class Archiver extends ArchiverDataSourceBase implements L2BlockSink, Tra
333
401
  reject(err);
334
402
  }
335
403
  }
404
+ return blocksAdded;
336
405
  }
337
406
 
338
407
  public waitForInitialSync() {
@@ -344,12 +413,31 @@ export class Archiver extends ArchiverDataSourceBase implements L2BlockSink, Tra
344
413
  */
345
414
  @trackSpan('Archiver.sync')
346
415
  private async sync() {
416
+ // Capture the tips before any mutation so the aggregate event can report what moved this pass.
417
+ const fromTips = await this.getL2Tips();
418
+
419
+ // Accumulate the blocks added across the pass's sub-steps (queued proposals + L1 checkpoint payloads).
420
+ const blocksAdded: L2Block[] = [];
347
421
  // Process any queued blocks first, before doing L1 sync
348
- await this.processInboundQueue();
422
+ blocksAdded.push(...(await this.processInboundQueue()));
349
423
  // Now perform L1 sync
350
- await this.syncFromL1();
351
- // Prune proposed blocks with no corresponding proposed checkpoint by the end of their build slot.
424
+ blocksAdded.push(...(await this.syncFromL1()));
425
+ // Prune proposed blocks with no corresponding proposed checkpoint after the appropriate materialization deadline.
352
426
  await this.pruneOrphanProposedBlocks();
427
+
428
+ // Emit a single aggregate update event after all pass work has committed and the tips cache has refreshed,
429
+ // so `toTips` and the source state observed by listeners are consistent with the event payload. The pass
430
+ // mutated state if the tips moved (added blocks, a prune, or a thin tier movement) or blocks were added; a
431
+ // fully-synced no-op pass emits nothing.
432
+ const toTips = await this.getL2Tips();
433
+ if (!l2TipsEqual(fromTips, toTips) || blocksAdded.length > 0) {
434
+ this.events.emit(L2BlockSourceEvents.L2BlockSourceUpdated, {
435
+ type: L2BlockSourceEvents.L2BlockSourceUpdated,
436
+ fromTips,
437
+ toTips,
438
+ blocksAdded,
439
+ });
440
+ }
353
441
  }
354
442
 
355
443
  /**
@@ -357,38 +445,38 @@ export class Archiver extends ArchiverDataSourceBase implements L2BlockSink, Tra
357
445
  *
358
446
  * Under pipelining, a proposer publishes the blocks for a checkpoint (block-only proposals) before
359
447
  * assembling and publishing the enclosing proposed checkpoint at the end of the build slot. A node
360
- * that received those blocks but never the proposed checkpoint is left with an orphan tip it must
361
- * not build on. We prune it once enough wall-clock time has elapsed that the proposed checkpoint
362
- * should have arrived. This runs on wall-clock time (not L1 block advancement) so it fires during
363
- * quiet L1 periods, and is the liveness counterpart to the sequencer's checkSync guard.
448
+ * that received those blocks but never the proposed checkpoint is left with an orphan tip it must not build on.
449
+ * If no checkpoint proposal was received for the orphan slot, we prune after the receive deadline plus local
450
+ * tolerance. If a checkpoint proposal was received but has not materialized into proposed archiver state,
451
+ * we prune after the consensus materialization deadline.
364
452
  *
365
453
  * The uncheckpointed suffix is scanned in order. Blocks covered by proposed checkpoints are left in
366
454
  * place; the first block not covered by a proposed checkpoint starts the orphan suffix to prune.
367
455
  */
368
456
  private async pruneOrphanProposedBlocks(): Promise<void> {
457
+ if (this.config.skipOrphanProposedBlockPruning) {
458
+ return;
459
+ }
369
460
  const tips = await this.getL2Tips();
370
461
  const now = BigInt(this.dateProvider.nowInSeconds());
371
- const pipeliningOffset = this.epochCache.pipeliningOffset();
372
462
 
373
- // This only applies under pipelining
374
- if (pipeliningOffset === 0) {
375
- this.log.trace(`Pipelining offset is 0, skipping orphan proposed block pruning`);
376
- return;
377
- }
463
+ // Frontier block covered by a proposed (or, falling back, confirmed) checkpoint. Blocks beyond it
464
+ // have no enclosing checkpoint proposal and are the orphan-pruning candidates.
465
+ const proposedCheckpointBlockNumber = await this.stores.blocks.getProposedCheckpointL2BlockNumber();
378
466
 
379
467
  // The proposed tip is a proposed-checkpointed block, so there are no orphan proposed blocks to prune
380
- if (tips.proposedCheckpoint.block.number === tips.proposed.number) {
381
- this.log.trace(
382
- `No orphan proposed blocks to prune: proposed tip ${tips.proposed.number} is checkpointed`,
383
- pick(tips, 'proposed', 'proposedCheckpoint'),
384
- );
468
+ if (proposedCheckpointBlockNumber === tips.proposed.number) {
469
+ this.log.trace(`No orphan proposed blocks to prune: proposed tip ${tips.proposed.number} is checkpointed`, {
470
+ proposed: tips.proposed,
471
+ proposedCheckpointBlockNumber,
472
+ });
385
473
  return;
386
474
  }
387
475
 
388
476
  // Load the blocks that are candidates for pruning (ie blocks without a proposed checkpoint covering them)
389
477
  const blocksWithoutProposedCheckpoint = await this.stores.blocks.getBlocksData({
390
- from: BlockNumber(tips.proposedCheckpoint.block.number + 1),
391
- limit: tips.proposed.number - tips.proposedCheckpoint.block.number,
478
+ from: BlockNumber(proposedCheckpointBlockNumber + 1),
479
+ limit: tips.proposed.number - proposedCheckpointBlockNumber,
392
480
  });
393
481
 
394
482
  // Iterate through them in order, the first one with a slot that should have received a proposed checkpoint
@@ -403,15 +491,21 @@ export class Archiver extends ArchiverDataSourceBase implements L2BlockSink, Tra
403
491
  }
404
492
  lastSlotChecked = blockSlot;
405
493
 
406
- // The proposed checkpoint should have landed by the start of the slot after the block's build slot
407
- // (build slot = blockSlot - pipeliningOffset). Wait a grace period beyond that to tolerate propagation.
408
- const expectedCheckpointedBySlot = SlotNumber(Number(blockSlot) - pipeliningOffset + 1);
409
- const expectedCheckpointedByTime =
410
- getTimestampForSlot(expectedCheckpointedBySlot, this.l1Constants) +
411
- BigInt(this.config.orphanProposedBlockPruneGraceSeconds);
494
+ const hasCheckpointProposal = await this.checkpointProposalPresence.hasCheckpointProposalForSlot(blockSlot);
495
+ const deadlineType = hasCheckpointProposal ? 'checkpoint-proposal-synced' : 'checkpoint-proposal-received';
496
+ const selectedDeadline = BigInt(
497
+ hasCheckpointProposal
498
+ ? this.timetable.getCheckpointProposalSyncedDeadline(blockSlot)
499
+ : Math.ceil(
500
+ this.timetable.getCheckpointProposalReceiveDeadline(blockSlot) +
501
+ this.config.orphanPruneNoProposalTolerance,
502
+ ),
503
+ );
412
504
 
413
- // If it's not checkpointed by the expected time, prune it along with all blocks after it.
414
- if (now >= expectedCheckpointedByTime) {
505
+ // If it's still not checkpointed once strictly past the selected deadline, prune it along with all blocks after
506
+ // it. A proposal may still legitimately arrive or materialize at exactly its deadline, so the tip is only
507
+ // orphaned once that instant has fully elapsed.
508
+ if (now > selectedDeadline) {
415
509
  const pruneAfterBlockNumber = BlockNumber(blockNumber - 1);
416
510
  this.log.warn(
417
511
  `Pruning orphan blocks after block ${pruneAfterBlockNumber}: block at slot ${blockSlot} belongs to ` +
@@ -421,15 +515,16 @@ export class Archiver extends ArchiverDataSourceBase implements L2BlockSink, Tra
421
515
  blockCheckpointNumber: blockData.checkpointNumber,
422
516
  blockNumber,
423
517
  blockSlot,
424
- pipeliningOffset,
425
- expectedCheckpointedBySlot,
426
- expectedCheckpointedByTime,
518
+ hasCheckpointProposal,
519
+ deadlineType,
520
+ selectedDeadline,
427
521
  now,
428
522
  },
429
523
  );
430
524
 
431
525
  const prunedBlocks = await this.updater.removeBlocksWithoutProposedCheckpointAfter(pruneAfterBlockNumber);
432
526
  if (prunedBlocks.length > 0) {
527
+ this.instrumentation.recordPrune('orphan');
433
528
  this.events.emit(L2BlockSourceEvents.L2PruneUncheckpointed, {
434
529
  type: L2BlockSourceEvents.L2PruneUncheckpointed,
435
530
  slotNumber: blockSlot,
@@ -440,14 +535,14 @@ export class Archiver extends ArchiverDataSourceBase implements L2BlockSink, Tra
440
535
  }
441
536
  }
442
537
 
443
- this.log.trace('No orphan proposed blocks to prune: all uncheckpointed blocks are still within the grace period', {
538
+ this.log.trace('No orphan proposed blocks to prune: all uncheckpointed blocks are still within deadline', {
444
539
  blocksWithoutProposedCheckpoint: blocksWithoutProposedCheckpoint.map(b => b.header.toInspect()),
445
540
  });
446
541
  }
447
542
 
448
- private async syncFromL1() {
543
+ private async syncFromL1(): Promise<L2Block[]> {
449
544
  // Delegate to the L1 synchronizer
450
- await this.synchronizer.syncFromL1(this.initialSyncComplete);
545
+ const blocksAdded = await this.synchronizer.syncFromL1(this.initialSyncComplete);
451
546
 
452
547
  // Check if we've completed initial sync
453
548
  const currentL1BlockNumber = this.synchronizer.getL1BlockNumber();
@@ -464,6 +559,7 @@ export class Archiver extends ArchiverDataSourceBase implements L2BlockSink, Tra
464
559
  this.initialSyncPromise.resolve();
465
560
  }
466
561
  }
562
+ return blocksAdded;
467
563
  }
468
564
 
469
565
  /** Resumes the archiver after a stop. */
@@ -599,6 +695,15 @@ export class Archiver extends ArchiverDataSourceBase implements L2BlockSink, Tra
599
695
  return this.updater.removeCheckpointsAfter(checkpointNumber);
600
696
  }
601
697
 
698
+ /**
699
+ * Removes all uncheckpointed blocks strictly after `blockNumber`, along with the proposed checkpoints
700
+ * that referenced them. Used by the AutomineSequencer to undo a local insert whose propose tx failed
701
+ * to land on L1 (no reorg needed — nothing reached L1). Refuses to touch checkpointed blocks.
702
+ */
703
+ public removeUncheckpointedBlocksAfter(blockNumber: BlockNumber): Promise<L2Block[]> {
704
+ return this.updater.removeUncheckpointedBlocksAfter(blockNumber);
705
+ }
706
+
602
707
  /** Used by TXE to add checkpoints directly without syncing from L1. */
603
708
  public async addCheckpoints(
604
709
  checkpoints: PublishedCheckpoint[],
package/src/config.ts CHANGED
@@ -8,13 +8,9 @@ import {
8
8
  numberConfigHelper,
9
9
  optionalNumberConfigHelper,
10
10
  } from '@aztec/foundation/config';
11
- import {
12
- type ChainConfig,
13
- type PipelineConfig,
14
- chainConfigMappings,
15
- pipelineConfigMappings,
16
- } from '@aztec/stdlib/config';
11
+ import { type ChainConfig, type SequencerConfig, chainConfigMappings } from '@aztec/stdlib/config';
17
12
  import type { ArchiverSpecificConfig } from '@aztec/stdlib/interfaces/server';
13
+ import { DEFAULT_ORPHAN_PRUNE_NO_PROPOSAL_TOLERANCE } from '@aztec/stdlib/timetable';
18
14
 
19
15
  /**
20
16
  * The archiver configuration.
@@ -26,13 +22,12 @@ import type { ArchiverSpecificConfig } from '@aztec/stdlib/interfaces/server';
26
22
  export type ArchiverConfig = ArchiverSpecificConfig &
27
23
  L1ReaderConfig &
28
24
  L1ContractsConfig &
29
- PipelineConfig & // required to pass through to epoch cache
30
25
  BlobClientConfig &
31
- ChainConfig;
26
+ ChainConfig &
27
+ Pick<SequencerConfig, 'blockDurationMs' | 'checkpointProposalSyncGraceSeconds'>;
32
28
 
33
29
  export const archiverConfigMappings: ConfigMappingsType<ArchiverConfig> = {
34
30
  ...blobClientConfigMapping,
35
- ...pipelineConfigMappings,
36
31
  archiverPollingIntervalMS: {
37
32
  env: 'ARCHIVER_POLLING_INTERVAL_MS',
38
33
  description: 'The polling interval in ms for retrieving new L2 blocks and encrypted logs.',
@@ -48,6 +43,18 @@ export const archiverConfigMappings: ConfigMappingsType<ArchiverConfig> = {
48
43
  ...optionalNumberConfigHelper(),
49
44
  description: 'The maximum possible size of the archiver DB in KB. Overwrites the general dataStoreMapSizeKb.',
50
45
  },
46
+ blockDurationMs: {
47
+ env: 'SEQ_BLOCK_DURATION_MS',
48
+ description:
49
+ 'Duration per block in milliseconds when building multiple blocks per slot. Used to derive orphan proposed block pruning timing.',
50
+ ...optionalNumberConfigHelper(),
51
+ },
52
+ checkpointProposalSyncGraceSeconds: {
53
+ env: 'CHECKPOINT_PROPOSAL_SYNC_GRACE_SECONDS',
54
+ description:
55
+ 'Consensus grace in seconds for a received checkpoint proposal to materialize into local proposed state.',
56
+ ...optionalNumberConfigHelper(),
57
+ },
51
58
  skipValidateCheckpointAttestations: {
52
59
  description: 'Skip validating checkpoint attestations (for testing purposes only)',
53
60
  ...booleanConfigHelper(false),
@@ -73,13 +80,23 @@ export const archiverConfigMappings: ConfigMappingsType<ArchiverConfig> = {
73
80
  'Set to true to bypass the check when the connected RPC node is known to prune old logs.',
74
81
  ...booleanConfigHelper(false),
75
82
  },
76
- orphanProposedBlockPruneGraceSeconds: {
77
- env: 'ARCHIVER_ORPHAN_PROPOSED_BLOCK_PRUNE_GRACE_SECONDS',
83
+ orphanPruneNoProposalTolerance: {
84
+ env: 'ARCHIVER_ORPHAN_PRUNE_NO_PROPOSAL_TOLERANCE',
85
+ description: 'Local tolerance in seconds before pruning an orphan block when no checkpoint proposal was received.',
86
+ ...numberConfigHelper(DEFAULT_ORPHAN_PRUNE_NO_PROPOSAL_TOLERANCE),
87
+ },
88
+ skipOrphanProposedBlockPruning: {
89
+ env: 'ARCHIVER_SKIP_ORPHAN_PROPOSED_BLOCK_PRUNING',
90
+ description: 'Skip pruning orphan proposed blocks that have no matching proposed checkpoint.',
91
+ ...booleanConfigHelper(false),
92
+ },
93
+ testPreloadStandardContracts: {
94
+ env: 'TEST_PRELOAD_STANDARD_CONTRACTS',
78
95
  description:
79
- 'Grace period in seconds, measured from the end of a proposed block build slot, after which a ' +
80
- 'proposed block with no matching proposed checkpoint is pruned as an orphan. Defaults from the ' +
81
- 'sequencer block duration at the node wiring layer when unset.',
82
- ...optionalNumberConfigHelper(),
96
+ 'Preload the standard contracts (AuthRegistry, PublicChecks, HandshakeRegistry) into the contract store at ' +
97
+ 'block 0. For test environments only, and only safe when genesis seeds the matching registration/deployment ' +
98
+ 'nullifiers; otherwise a later on-chain publish would collide with the block-0 preload.',
99
+ ...booleanConfigHelper(false),
83
100
  },
84
101
  ...chainConfigMappings,
85
102
  ...l1ReaderConfigMappings,
@@ -110,6 +127,7 @@ export function mapArchiverConfig(config: Partial<ArchiverConfig>) {
110
127
  maxAllowedEthClientDriftSeconds: config.maxAllowedEthClientDriftSeconds,
111
128
  ethereumAllowNoDebugHosts: config.ethereumAllowNoDebugHosts,
112
129
  skipHistoricalLogsCheck: config.archiverSkipHistoricalLogsCheck,
113
- orphanProposedBlockPruneGraceSeconds: config.orphanProposedBlockPruneGraceSeconds,
130
+ orphanPruneNoProposalTolerance: config.orphanPruneNoProposalTolerance,
131
+ skipOrphanProposedBlockPruning: config.skipOrphanProposedBlockPruning,
114
132
  };
115
133
  }