@aztec/prover-node 0.0.1-commit.d3ec352c → 0.0.1-commit.d58ff9d0

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 (81) hide show
  1. package/README.md +518 -0
  2. package/dest/actions/download-epoch-proving-job.d.ts +1 -1
  3. package/dest/actions/download-epoch-proving-job.js +1 -1
  4. package/dest/actions/rerun-epoch-proving-job.d.ts +6 -4
  5. package/dest/actions/rerun-epoch-proving-job.d.ts.map +1 -1
  6. package/dest/actions/rerun-epoch-proving-job.js +191 -28
  7. package/dest/actions/upload-epoch-proof-failure.d.ts +2 -2
  8. package/dest/actions/upload-epoch-proof-failure.d.ts.map +1 -1
  9. package/dest/bin/run-failed-epoch.js +6 -5
  10. package/dest/checkpoint-store.d.ts +87 -0
  11. package/dest/checkpoint-store.d.ts.map +1 -0
  12. package/dest/checkpoint-store.js +186 -0
  13. package/dest/config.d.ts +10 -10
  14. package/dest/config.d.ts.map +1 -1
  15. package/dest/config.js +27 -22
  16. package/dest/factory.d.ts +23 -16
  17. package/dest/factory.d.ts.map +1 -1
  18. package/dest/factory.js +49 -68
  19. package/dest/index.d.ts +3 -1
  20. package/dest/index.d.ts.map +1 -1
  21. package/dest/index.js +2 -0
  22. package/dest/job/checkpoint-prover.d.ts +134 -0
  23. package/dest/job/checkpoint-prover.d.ts.map +1 -0
  24. package/dest/job/checkpoint-prover.js +353 -0
  25. package/dest/job/epoch-proving-job-data.d.ts +2 -2
  26. package/dest/job/epoch-proving-job-data.d.ts.map +1 -1
  27. package/dest/job/epoch-proving-job-data.js +1 -1
  28. package/dest/job/epoch-session.d.ts +146 -0
  29. package/dest/job/epoch-session.d.ts.map +1 -0
  30. package/dest/job/epoch-session.js +720 -0
  31. package/dest/job/top-tree-job.d.ts +82 -0
  32. package/dest/job/top-tree-job.d.ts.map +1 -0
  33. package/dest/job/top-tree-job.js +152 -0
  34. package/dest/metrics.d.ts +41 -4
  35. package/dest/metrics.d.ts.map +1 -1
  36. package/dest/metrics.js +125 -102
  37. package/dest/monitors/epoch-monitor.d.ts +1 -1
  38. package/dest/monitors/epoch-monitor.d.ts.map +1 -1
  39. package/dest/monitors/epoch-monitor.js +12 -19
  40. package/dest/proof-publishing-service.d.ts +161 -0
  41. package/dest/proof-publishing-service.d.ts.map +1 -0
  42. package/dest/proof-publishing-service.js +335 -0
  43. package/dest/prover-node-publisher.d.ts +31 -19
  44. package/dest/prover-node-publisher.d.ts.map +1 -1
  45. package/dest/prover-node-publisher.js +222 -78
  46. package/dest/prover-node.d.ts +122 -72
  47. package/dest/prover-node.d.ts.map +1 -1
  48. package/dest/prover-node.js +872 -229
  49. package/dest/prover-publisher-factory.d.ts +12 -6
  50. package/dest/prover-publisher-factory.d.ts.map +1 -1
  51. package/dest/prover-publisher-factory.js +8 -5
  52. package/dest/session-manager.d.ts +158 -0
  53. package/dest/session-manager.d.ts.map +1 -0
  54. package/dest/session-manager.js +452 -0
  55. package/dest/test/index.d.ts +7 -6
  56. package/dest/test/index.d.ts.map +1 -1
  57. package/package.json +25 -23
  58. package/src/actions/download-epoch-proving-job.ts +1 -1
  59. package/src/actions/rerun-epoch-proving-job.ts +120 -30
  60. package/src/actions/upload-epoch-proof-failure.ts +1 -1
  61. package/src/bin/run-failed-epoch.ts +5 -3
  62. package/src/checkpoint-store.ts +218 -0
  63. package/src/config.ts +39 -34
  64. package/src/factory.ts +86 -113
  65. package/src/index.ts +2 -0
  66. package/src/job/checkpoint-prover.ts +468 -0
  67. package/src/job/epoch-proving-job-data.ts +1 -1
  68. package/src/job/epoch-session.ts +436 -0
  69. package/src/job/top-tree-job.ts +227 -0
  70. package/src/metrics.ts +143 -93
  71. package/src/monitors/epoch-monitor.ts +6 -14
  72. package/src/proof-publishing-service.ts +427 -0
  73. package/src/prover-node-publisher.ts +264 -98
  74. package/src/prover-node.ts +568 -259
  75. package/src/prover-publisher-factory.ts +22 -11
  76. package/src/session-manager.ts +552 -0
  77. package/src/test/index.ts +6 -6
  78. package/dest/job/epoch-proving-job.d.ts +0 -62
  79. package/dest/job/epoch-proving-job.d.ts.map +0 -1
  80. package/dest/job/epoch-proving-job.js +0 -363
  81. package/src/job/epoch-proving-job.ts +0 -426
@@ -1,14 +1,15 @@
1
1
  import { BatchedBlob } from '@aztec/blob-lib';
2
- import { AZTEC_MAX_EPOCH_DURATION } from '@aztec/constants';
3
- import type { L1TxUtils, RollupContract, ViemCommitteeAttestation } from '@aztec/ethereum';
2
+ import type { RollupContract, ViemCommitteeAttestation } from '@aztec/ethereum/contracts';
3
+ import type { L1TxUtils } from '@aztec/ethereum/l1-tx-utils';
4
4
  import { CheckpointNumber, EpochNumber } from '@aztec/foundation/branded-types';
5
+ import { Fr } from '@aztec/foundation/curves/bn254';
5
6
  import { EthAddress } from '@aztec/foundation/eth-address';
6
- import { Fr } from '@aztec/foundation/fields';
7
- import type { Tuple } from '@aztec/foundation/serialize';
7
+ import { type Logger, type LoggerBindings } from '@aztec/foundation/log';
8
8
  import type { PublisherConfig, TxSenderConfig } from '@aztec/sequencer-client';
9
9
  import type { Proof } from '@aztec/stdlib/proofs';
10
- import type { FeeRecipient, RootRollupPublicInputs } from '@aztec/stdlib/rollup';
10
+ import type { CheckpointHeader, RootRollupPublicInputs } from '@aztec/stdlib/rollup';
11
11
  import { type TelemetryClient } from '@aztec/telemetry-client';
12
+ import { type Hex } from 'viem';
12
13
  /** Arguments to the submitEpochProof method of the rollup contract */
13
14
  export type L1SubmitEpochProofArgs = {
14
15
  epochSize: number;
@@ -17,30 +18,22 @@ export type L1SubmitEpochProofArgs = {
17
18
  endTimestamp: Fr;
18
19
  outHash: Fr;
19
20
  proverId: Fr;
20
- fees: Tuple<FeeRecipient, typeof AZTEC_MAX_EPOCH_DURATION>;
21
+ headers: CheckpointHeader[];
21
22
  proof: Proof;
22
23
  };
23
24
  export declare class ProverNodePublisher {
24
- private interrupted;
25
25
  private metrics;
26
- protected log: import("@aztec/foundation/log").Logger;
26
+ protected log: Logger;
27
27
  protected rollupContract: RollupContract;
28
+ protected proofSubmissionTarget: Hex;
28
29
  readonly l1TxUtils: L1TxUtils;
29
30
  constructor(config: TxSenderConfig & PublisherConfig, deps: {
30
31
  rollupContract: RollupContract;
31
32
  l1TxUtils: L1TxUtils;
33
+ proofSubmissionTarget?: EthAddress;
32
34
  telemetry?: TelemetryClient;
33
- });
35
+ }, bindings?: LoggerBindings);
34
36
  getRollupContract(): RollupContract;
35
- /**
36
- * Calling `interrupt` will cause any in progress call to `publishRollup` to return `false` asap.
37
- * Be warned, the call may return false even if the tx subsequently gets successfully mined.
38
- * In practice this shouldn't matter, as we'll only ever be calling `interrupt` when we know it's going to fail.
39
- * A call to `restart` is required before you can continue publishing.
40
- */
41
- interrupt(): void;
42
- /** Restarts the publisher after calling `interrupt`. */
43
- restart(): void;
44
37
  getSenderAddress(): EthAddress;
45
38
  submitEpochProof(args: {
46
39
  epochNumber: EpochNumber;
@@ -50,10 +43,29 @@ export declare class ProverNodePublisher {
50
43
  proof: Proof;
51
44
  batchedBlobInputs: BatchedBlob;
52
45
  attestations: ViemCommitteeAttestation[];
46
+ headers: CheckpointHeader[];
47
+ /** Wall-clock deadline (proof-submission window end) past which the L1 tx should stop retrying. */
48
+ deadline?: Date;
53
49
  }): Promise<boolean>;
54
50
  private validateEpochProofSubmission;
51
+ /**
52
+ * Estimates what submitting the epoch proof would have cost on L1 without actually sending it.
53
+ * Runs the same validation as `submitEpochProof`, encodes the calldata, estimates gas, and records metrics.
54
+ * Used when proof publishing is disabled (e.g. PROVER_NODE_DISABLE_PROOF_PUBLISH=true on mainnet).
55
+ */
56
+ analyzeEpochProofSubmission(args: {
57
+ epochNumber: EpochNumber;
58
+ fromCheckpoint: CheckpointNumber;
59
+ toCheckpoint: CheckpointNumber;
60
+ publicInputs: RootRollupPublicInputs;
61
+ proof: Proof;
62
+ batchedBlobInputs: BatchedBlob;
63
+ attestations: ViemCommitteeAttestation[];
64
+ headers: CheckpointHeader[];
65
+ }): Promise<void>;
66
+ private encodeSubmitEpochProofCalldata;
55
67
  private sendSubmitEpochProofTx;
56
68
  private getEpochProofPublicInputsArgs;
57
69
  private getSubmitEpochProofArgs;
58
70
  }
59
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHJvdmVyLW5vZGUtcHVibGlzaGVyLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvcHJvdmVyLW5vZGUtcHVibGlzaGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxXQUFXLEVBQThCLE1BQU0saUJBQWlCLENBQUM7QUFDMUUsT0FBTyxFQUFFLHdCQUF3QixFQUFFLE1BQU0sa0JBQWtCLENBQUM7QUFDNUQsT0FBTyxLQUFLLEVBQUUsU0FBUyxFQUFFLGNBQWMsRUFBRSx3QkFBd0IsRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBRTNGLE9BQU8sRUFBRSxnQkFBZ0IsRUFBRSxXQUFXLEVBQUUsTUFBTSxpQ0FBaUMsQ0FBQztBQUVoRixPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sK0JBQStCLENBQUM7QUFDM0QsT0FBTyxFQUFFLEVBQUUsRUFBRSxNQUFNLDBCQUEwQixDQUFDO0FBRTlDLE9BQU8sS0FBSyxFQUFFLEtBQUssRUFBRSxNQUFNLDZCQUE2QixDQUFDO0FBR3pELE9BQU8sS0FBSyxFQUFFLGVBQWUsRUFBRSxjQUFjLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUUvRSxPQUFPLEtBQUssRUFBRSxLQUFLLEVBQUUsTUFBTSxzQkFBc0IsQ0FBQztBQUNsRCxPQUFPLEtBQUssRUFBRSxZQUFZLEVBQUUsc0JBQXNCLEVBQUUsTUFBTSxzQkFBc0IsQ0FBQztBQUVqRixPQUFPLEVBQUUsS0FBSyxlQUFlLEVBQXNCLE1BQU0seUJBQXlCLENBQUM7QUFPbkYsc0VBQXNFO0FBQ3RFLE1BQU0sTUFBTSxzQkFBc0IsR0FBRztJQUNuQyxTQUFTLEVBQUUsTUFBTSxDQUFDO0lBQ2xCLGVBQWUsRUFBRSxFQUFFLENBQUM7SUFDcEIsVUFBVSxFQUFFLEVBQUUsQ0FBQztJQUNmLFlBQVksRUFBRSxFQUFFLENBQUM7SUFDakIsT0FBTyxFQUFFLEVBQUUsQ0FBQztJQUNaLFFBQVEsRUFBRSxFQUFFLENBQUM7SUFDYixJQUFJLEVBQUUsS0FBSyxDQUFDLFlBQVksRUFBRSxPQUFPLHdCQUF3QixDQUFDLENBQUM7SUFDM0QsS0FBSyxFQUFFLEtBQUssQ0FBQztDQUNkLENBQUM7QUFFRixxQkFBYSxtQkFBbUI7SUFDOUIsT0FBTyxDQUFDLFdBQVcsQ0FBUztJQUM1QixPQUFPLENBQUMsT0FBTyxDQUE2QjtJQUU1QyxTQUFTLENBQUMsR0FBRyx5Q0FBK0M7SUFFNUQsU0FBUyxDQUFDLGNBQWMsRUFBRSxjQUFjLENBQUM7SUFFekMsU0FBZ0IsU0FBUyxFQUFFLFNBQVMsQ0FBQztJQUVyQyxZQUNFLE1BQU0sRUFBRSxjQUFjLEdBQUcsZUFBZSxFQUN4QyxJQUFJLEVBQUU7UUFDSixjQUFjLEVBQUUsY0FBYyxDQUFDO1FBQy9CLFNBQVMsRUFBRSxTQUFTLENBQUM7UUFDckIsU0FBUyxDQUFDLEVBQUUsZUFBZSxDQUFDO0tBQzdCLEVBUUY7SUFFTSxpQkFBaUIsbUJBRXZCO0lBRUQ7Ozs7O09BS0c7SUFDSSxTQUFTLFNBR2Y7SUFFRCx3REFBd0Q7SUFDakQsT0FBTyxTQUdiO0lBRU0sZ0JBQWdCLGVBRXRCO0lBRVksZ0JBQWdCLENBQUMsSUFBSSxFQUFFO1FBQ2xDLFdBQVcsRUFBRSxXQUFXLENBQUM7UUFDekIsY0FBYyxFQUFFLGdCQUFnQixDQUFDO1FBQ2pDLFlBQVksRUFBRSxnQkFBZ0IsQ0FBQztRQUMvQixZQUFZLEVBQUUsc0JBQXNCLENBQUM7UUFDckMsS0FBSyxFQUFFLEtBQUssQ0FBQztRQUNiLGlCQUFpQixFQUFFLFdBQVcsQ0FBQztRQUMvQixZQUFZLEVBQUUsd0JBQXdCLEVBQUUsQ0FBQztLQUMxQyxHQUFHLE9BQU8sQ0FBQyxPQUFPLENBQUMsQ0FnRG5CO1lBRWEsNEJBQTRCO1lBK0Q1QixzQkFBc0I7SUF5Q3BDLE9BQU8sQ0FBQyw2QkFBNkI7SUF5QnJDLE9BQU8sQ0FBQyx1QkFBdUI7Q0F1QmhDIn0=
71
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHJvdmVyLW5vZGUtcHVibGlzaGVyLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvcHJvdmVyLW5vZGUtcHVibGlzaGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxXQUFXLEVBQThCLE1BQU0saUJBQWlCLENBQUM7QUFFMUUsT0FBTyxLQUFLLEVBQUUsY0FBYyxFQUFFLHdCQUF3QixFQUFFLE1BQU0sMkJBQTJCLENBQUM7QUFDMUYsT0FBTyxLQUFLLEVBQUUsU0FBUyxFQUFFLE1BQU0sNkJBQTZCLENBQUM7QUFDN0QsT0FBTyxFQUFFLGdCQUFnQixFQUFFLFdBQVcsRUFBRSxNQUFNLGlDQUFpQyxDQUFDO0FBRWhGLE9BQU8sRUFBRSxFQUFFLEVBQUUsTUFBTSxnQ0FBZ0MsQ0FBQztBQUNwRCxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sK0JBQStCLENBQUM7QUFDM0QsT0FBTyxFQUFFLEtBQUssTUFBTSxFQUFFLEtBQUssY0FBYyxFQUFnQixNQUFNLHVCQUF1QixDQUFDO0FBR3ZGLE9BQU8sS0FBSyxFQUFFLGVBQWUsRUFBRSxjQUFjLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUUvRSxPQUFPLEtBQUssRUFBRSxLQUFLLEVBQUUsTUFBTSxzQkFBc0IsQ0FBQztBQUNsRCxPQUFPLEtBQUssRUFBRSxnQkFBZ0IsRUFBRSxzQkFBc0IsRUFBRSxNQUFNLHNCQUFzQixDQUFDO0FBRXJGLE9BQU8sRUFBRSxLQUFLLGVBQWUsRUFBc0IsTUFBTSx5QkFBeUIsQ0FBQztBQUduRixPQUFPLEVBQUUsS0FBSyxHQUFHLEVBQXdFLE1BQU0sTUFBTSxDQUFDO0FBSXRHLHNFQUFzRTtBQUN0RSxNQUFNLE1BQU0sc0JBQXNCLEdBQUc7SUFDbkMsU0FBUyxFQUFFLE1BQU0sQ0FBQztJQUNsQixlQUFlLEVBQUUsRUFBRSxDQUFDO0lBQ3BCLFVBQVUsRUFBRSxFQUFFLENBQUM7SUFDZixZQUFZLEVBQUUsRUFBRSxDQUFDO0lBQ2pCLE9BQU8sRUFBRSxFQUFFLENBQUM7SUFDWixRQUFRLEVBQUUsRUFBRSxDQUFDO0lBQ2IsT0FBTyxFQUFFLGdCQUFnQixFQUFFLENBQUM7SUFDNUIsS0FBSyxFQUFFLEtBQUssQ0FBQztDQUNkLENBQUM7QUFFRixxQkFBYSxtQkFBbUI7SUFDOUIsT0FBTyxDQUFDLE9BQU8sQ0FBNkI7SUFFNUMsU0FBUyxDQUFDLEdBQUcsRUFBRSxNQUFNLENBQUM7SUFFdEIsU0FBUyxDQUFDLGNBQWMsRUFBRSxjQUFjLENBQUM7SUFFekMsU0FBUyxDQUFDLHFCQUFxQixFQUFFLEdBQUcsQ0FBQztJQUVyQyxTQUFnQixTQUFTLEVBQUUsU0FBUyxDQUFDO0lBRXJDLFlBQ0UsTUFBTSxFQUFFLGNBQWMsR0FBRyxlQUFlLEVBQ3hDLElBQUksRUFBRTtRQUNKLGNBQWMsRUFBRSxjQUFjLENBQUM7UUFDL0IsU0FBUyxFQUFFLFNBQVMsQ0FBQztRQUNyQixxQkFBcUIsQ0FBQyxFQUFFLFVBQVUsQ0FBQztRQUNuQyxTQUFTLENBQUMsRUFBRSxlQUFlLENBQUM7S0FDN0IsRUFDRCxRQUFRLENBQUMsRUFBRSxjQUFjLEVBVTFCO0lBRU0saUJBQWlCLG1CQUV2QjtJQUVNLGdCQUFnQixlQUV0QjtJQUVZLGdCQUFnQixDQUFDLElBQUksRUFBRTtRQUNsQyxXQUFXLEVBQUUsV0FBVyxDQUFDO1FBQ3pCLGNBQWMsRUFBRSxnQkFBZ0IsQ0FBQztRQUNqQyxZQUFZLEVBQUUsZ0JBQWdCLENBQUM7UUFDL0IsWUFBWSxFQUFFLHNCQUFzQixDQUFDO1FBQ3JDLEtBQUssRUFBRSxLQUFLLENBQUM7UUFDYixpQkFBaUIsRUFBRSxXQUFXLENBQUM7UUFDL0IsWUFBWSxFQUFFLHdCQUF3QixFQUFFLENBQUM7UUFDekMsT0FBTyxFQUFFLGdCQUFnQixFQUFFLENBQUM7UUFDNUIsbUdBQW1HO1FBQ25HLFFBQVEsQ0FBQyxFQUFFLElBQUksQ0FBQztLQUNqQixHQUFHLE9BQU8sQ0FBQyxPQUFPLENBQUMsQ0E2Q25CO1lBRWEsNEJBQTRCO0lBb0UxQzs7OztPQUlHO0lBQ1UsMkJBQTJCLENBQUMsSUFBSSxFQUFFO1FBQzdDLFdBQVcsRUFBRSxXQUFXLENBQUM7UUFDekIsY0FBYyxFQUFFLGdCQUFnQixDQUFDO1FBQ2pDLFlBQVksRUFBRSxnQkFBZ0IsQ0FBQztRQUMvQixZQUFZLEVBQUUsc0JBQXNCLENBQUM7UUFDckMsS0FBSyxFQUFFLEtBQUssQ0FBQztRQUNiLGlCQUFpQixFQUFFLFdBQVcsQ0FBQztRQUMvQixZQUFZLEVBQUUsd0JBQXdCLEVBQUUsQ0FBQztRQUN6QyxPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsQ0FBQztLQUM3QixHQUFHLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FzQ2hCO0lBRUQsT0FBTyxDQUFDLDhCQUE4QjtZQWdCeEIsc0JBQXNCO0lBaURwQyxPQUFPLENBQUMsNkJBQTZCO0lBdUJyQyxPQUFPLENBQUMsdUJBQXVCO0NBd0JoQyJ9
@@ -1 +1 @@
1
- {"version":3,"file":"prover-node-publisher.d.ts","sourceRoot":"","sources":["../src/prover-node-publisher.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAA8B,MAAM,iBAAiB,CAAC;AAC1E,OAAO,EAAE,wBAAwB,EAAE,MAAM,kBAAkB,CAAC;AAC5D,OAAO,KAAK,EAAE,SAAS,EAAE,cAAc,EAAE,wBAAwB,EAAE,MAAM,iBAAiB,CAAC;AAE3F,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAEhF,OAAO,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAC3D,OAAO,EAAE,EAAE,EAAE,MAAM,0BAA0B,CAAC;AAE9C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,6BAA6B,CAAC;AAGzD,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAE/E,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAClD,OAAO,KAAK,EAAE,YAAY,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAEjF,OAAO,EAAE,KAAK,eAAe,EAAsB,MAAM,yBAAyB,CAAC;AAOnF,sEAAsE;AACtE,MAAM,MAAM,sBAAsB,GAAG;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,EAAE,CAAC;IACpB,UAAU,EAAE,EAAE,CAAC;IACf,YAAY,EAAE,EAAE,CAAC;IACjB,OAAO,EAAE,EAAE,CAAC;IACZ,QAAQ,EAAE,EAAE,CAAC;IACb,IAAI,EAAE,KAAK,CAAC,YAAY,EAAE,OAAO,wBAAwB,CAAC,CAAC;IAC3D,KAAK,EAAE,KAAK,CAAC;CACd,CAAC;AAEF,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,OAAO,CAA6B;IAE5C,SAAS,CAAC,GAAG,yCAA+C;IAE5D,SAAS,CAAC,cAAc,EAAE,cAAc,CAAC;IAEzC,SAAgB,SAAS,EAAE,SAAS,CAAC;IAErC,YACE,MAAM,EAAE,cAAc,GAAG,eAAe,EACxC,IAAI,EAAE;QACJ,cAAc,EAAE,cAAc,CAAC;QAC/B,SAAS,EAAE,SAAS,CAAC;QACrB,SAAS,CAAC,EAAE,eAAe,CAAC;KAC7B,EAQF;IAEM,iBAAiB,mBAEvB;IAED;;;;;OAKG;IACI,SAAS,SAGf;IAED,wDAAwD;IACjD,OAAO,SAGb;IAEM,gBAAgB,eAEtB;IAEY,gBAAgB,CAAC,IAAI,EAAE;QAClC,WAAW,EAAE,WAAW,CAAC;QACzB,cAAc,EAAE,gBAAgB,CAAC;QACjC,YAAY,EAAE,gBAAgB,CAAC;QAC/B,YAAY,EAAE,sBAAsB,CAAC;QACrC,KAAK,EAAE,KAAK,CAAC;QACb,iBAAiB,EAAE,WAAW,CAAC;QAC/B,YAAY,EAAE,wBAAwB,EAAE,CAAC;KAC1C,GAAG,OAAO,CAAC,OAAO,CAAC,CAgDnB;YAEa,4BAA4B;YA+D5B,sBAAsB;IAyCpC,OAAO,CAAC,6BAA6B;IAyBrC,OAAO,CAAC,uBAAuB;CAuBhC"}
1
+ {"version":3,"file":"prover-node-publisher.d.ts","sourceRoot":"","sources":["../src/prover-node-publisher.ts"],"names":[],"mappings":"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"}
@@ -1,46 +1,34 @@
1
1
  import { getEthBlobEvaluationInputs } from '@aztec/blob-lib';
2
- import { AZTEC_MAX_EPOCH_DURATION } from '@aztec/constants';
3
- import { makeTuple } from '@aztec/foundation/array';
2
+ import { MAX_CHECKPOINTS_PER_EPOCH } from '@aztec/constants';
4
3
  import { CheckpointNumber } from '@aztec/foundation/branded-types';
5
4
  import { areArraysEqual } from '@aztec/foundation/collection';
5
+ import { Fr } from '@aztec/foundation/curves/bn254';
6
6
  import { EthAddress } from '@aztec/foundation/eth-address';
7
- import { Fr } from '@aztec/foundation/fields';
8
7
  import { createLogger } from '@aztec/foundation/log';
9
8
  import { Timer } from '@aztec/foundation/timer';
10
9
  import { RollupAbi } from '@aztec/l1-artifacts';
11
10
  import { CommitteeAttestation, CommitteeAttestationsAndSigners } from '@aztec/stdlib/block';
12
11
  import { getTelemetryClient } from '@aztec/telemetry-client';
13
12
  import { inspect } from 'util';
14
- import { encodeFunctionData } from 'viem';
13
+ import { encodeFunctionData, formatEther, formatGwei } from 'viem';
15
14
  import { ProverNodePublisherMetrics } from './metrics.js';
16
15
  export class ProverNodePublisher {
17
- interrupted = false;
18
16
  metrics;
19
- log = createLogger('prover-node:l1-tx-publisher');
17
+ log;
20
18
  rollupContract;
19
+ proofSubmissionTarget;
21
20
  l1TxUtils;
22
- constructor(config, deps){
21
+ constructor(config, deps, bindings){
23
22
  const telemetry = deps.telemetry ?? getTelemetryClient();
24
23
  this.metrics = new ProverNodePublisherMetrics(telemetry, 'ProverNode');
24
+ this.log = createLogger('prover-node:l1-tx-publisher', bindings);
25
25
  this.rollupContract = deps.rollupContract;
26
+ this.proofSubmissionTarget = deps.proofSubmissionTarget?.toString() ?? deps.rollupContract.address;
26
27
  this.l1TxUtils = deps.l1TxUtils;
27
28
  }
28
29
  getRollupContract() {
29
30
  return this.rollupContract;
30
31
  }
31
- /**
32
- * Calling `interrupt` will cause any in progress call to `publishRollup` to return `false` asap.
33
- * Be warned, the call may return false even if the tx subsequently gets successfully mined.
34
- * In practice this shouldn't matter, as we'll only ever be calling `interrupt` when we know it's going to fail.
35
- * A call to `restart` is required before you can continue publishing.
36
- */ interrupt() {
37
- this.interrupted = true;
38
- this.l1TxUtils.interrupt();
39
- }
40
- /** Restarts the publisher after calling `interrupt`. */ restart() {
41
- this.interrupted = false;
42
- this.l1TxUtils.restart();
43
- }
44
32
  getSenderAddress() {
45
33
  return this.l1TxUtils.getSenderAddress();
46
34
  }
@@ -51,44 +39,42 @@ export class ProverNodePublisher {
51
39
  fromCheckpoint,
52
40
  toCheckpoint
53
41
  };
54
- if (!this.interrupted) {
55
- const timer = new Timer();
56
- // Validate epoch proof range and hashes are correct before submitting
57
- await this.validateEpochProofSubmission(args);
58
- const txReceipt = await this.sendSubmitEpochProofTx(args);
59
- if (!txReceipt) {
60
- return false;
61
- }
62
- try {
63
- this.metrics.recordSenderBalance(await this.l1TxUtils.getSenderBalance(), this.l1TxUtils.getSenderAddress().toString());
64
- } catch (err) {
65
- this.log.warn(`Failed to record the ETH balance of the prover node: ${err}`);
66
- }
67
- // Tx was mined successfully
68
- if (txReceipt.status === 'success') {
69
- const tx = await this.l1TxUtils.getTransactionStats(txReceipt.transactionHash);
70
- const stats = {
71
- gasPrice: txReceipt.effectiveGasPrice,
72
- gasUsed: txReceipt.gasUsed,
73
- transactionHash: txReceipt.transactionHash,
74
- calldataGas: tx.calldataGas,
75
- calldataSize: tx.calldataSize,
76
- sender: tx.sender,
77
- blobDataGas: 0n,
78
- blobGasUsed: 0n,
79
- eventName: 'proof-published-to-l1'
80
- };
81
- this.log.info(`Published epoch proof to L1 rollup contract`, {
82
- ...stats,
83
- ...ctx
84
- });
85
- this.metrics.recordSubmitProof(timer.ms(), stats);
86
- return true;
87
- }
88
- this.metrics.recordFailedTx();
89
- this.log.error(`Rollup.submitEpochProof tx status failed ${txReceipt.transactionHash}`, undefined, ctx);
42
+ const timer = new Timer();
43
+ // Validate epoch proof range and hashes are correct before submitting
44
+ await this.validateEpochProofSubmission(args);
45
+ const txReceipt = await this.sendSubmitEpochProofTx(args);
46
+ if (!txReceipt) {
47
+ this.log.error(`Failed to mine submitEpochProof tx`, undefined, ctx);
48
+ return false;
49
+ }
50
+ try {
51
+ this.metrics.recordSenderBalance(await this.l1TxUtils.getSenderBalance(), this.l1TxUtils.getSenderAddress().toString());
52
+ } catch (err) {
53
+ this.log.warn(`Failed to record the ETH balance of the prover node: ${err}`);
90
54
  }
91
- this.log.verbose('Checkpoint data syncing interrupted', ctx);
55
+ // Tx was mined successfully
56
+ if (txReceipt.status === 'success') {
57
+ const tx = await this.l1TxUtils.getTransactionStats(txReceipt.transactionHash);
58
+ const stats = {
59
+ gasPrice: txReceipt.effectiveGasPrice,
60
+ gasUsed: txReceipt.gasUsed,
61
+ transactionHash: txReceipt.transactionHash,
62
+ calldataGas: tx.calldataGas,
63
+ calldataSize: tx.calldataSize,
64
+ sender: tx.sender,
65
+ blobDataGas: 0n,
66
+ blobGasUsed: 0n,
67
+ eventName: 'proof-published-to-l1'
68
+ };
69
+ this.log.info(`Published epoch proof to L1 rollup contract`, {
70
+ ...stats,
71
+ ...ctx
72
+ });
73
+ this.metrics.recordSubmitProof(timer.ms(), stats);
74
+ return true;
75
+ }
76
+ this.metrics.recordFailedTx();
77
+ this.log.error(`Rollup submitEpochProof tx reverted ${txReceipt.transactionHash}`, undefined, ctx);
92
78
  return false;
93
79
  }
94
80
  async validateEpochProofSubmission(args) {
@@ -101,17 +87,17 @@ export class ProverNodePublisher {
101
87
  }
102
88
  // toCheckpoint can't be greater than pending
103
89
  if (toCheckpoint > pending) {
104
- throw new Error(`Cannot submit epoch proof for ${fromCheckpoint}-${toCheckpoint} as pending checkpoint is ${pending}`);
90
+ throw new Error(`Cannot submit epoch proof for ${fromCheckpoint}-${toCheckpoint} as proposed checkpoint is ${pending}`);
105
91
  }
106
92
  // Check the archive for the immediate checkpoint before the epoch
107
93
  const checkpointLog = await this.rollupContract.getCheckpoint(CheckpointNumber(fromCheckpoint - 1));
108
- if (publicInputs.previousArchiveRoot.toString() !== checkpointLog.archive) {
109
- throw new Error(`Previous archive root mismatch: ${publicInputs.previousArchiveRoot.toString()} !== ${checkpointLog.archive}`);
94
+ if (!publicInputs.previousArchiveRoot.equals(checkpointLog.archive)) {
95
+ throw new Error(`Previous archive root mismatch: ${publicInputs.previousArchiveRoot.toString()} !== ${checkpointLog.archive.toString()}`);
110
96
  }
111
97
  // Check the archive for the last checkpoint in the epoch
112
98
  const endCheckpointLog = await this.rollupContract.getCheckpoint(toCheckpoint);
113
- if (publicInputs.endArchiveRoot.toString() !== endCheckpointLog.archive) {
114
- throw new Error(`End archive root mismatch: ${publicInputs.endArchiveRoot.toString()} !== ${endCheckpointLog.archive}`);
99
+ if (!publicInputs.endArchiveRoot.equals(endCheckpointLog.archive)) {
100
+ throw new Error(`End archive root mismatch: ${publicInputs.endArchiveRoot.toString()} !== ${endCheckpointLog.archive.toString()}`);
115
101
  }
116
102
  // Check the batched blob inputs from the root rollup against the batched blob computed in ts
117
103
  const finalBlobAccumulator = batchedBlobInputs.toFinalBlobAccumulator();
@@ -123,11 +109,66 @@ export class ProverNodePublisher {
123
109
  const argsPublicInputs = [
124
110
  ...publicInputs.toFields()
125
111
  ];
126
- if (!areArraysEqual(rollupPublicInputs.map(Fr.fromHexString), argsPublicInputs, (a, b)=>a.equals(b))) {
127
- const fmt = (inputs)=>inputs.map((x)=>x.toString()).join(', ');
128
- throw new Error(`Root rollup public inputs mismatch:\nRollup: ${fmt(rollupPublicInputs)}\nComputed:${fmt(argsPublicInputs)}`);
112
+ if (!areArraysEqual(rollupPublicInputs, argsPublicInputs, (a, b)=>a.equals(b))) {
113
+ throw await reportPublicInputsMismatch({
114
+ rollupPublicInputs,
115
+ argsPublicInputs,
116
+ fromCheckpoint,
117
+ toCheckpoint,
118
+ rollupContract: this.rollupContract,
119
+ log: this.log
120
+ });
129
121
  }
130
122
  }
123
+ /**
124
+ * Estimates what submitting the epoch proof would have cost on L1 without actually sending it.
125
+ * Runs the same validation as `submitEpochProof`, encodes the calldata, estimates gas, and records metrics.
126
+ * Used when proof publishing is disabled (e.g. PROVER_NODE_DISABLE_PROOF_PUBLISH=true on mainnet).
127
+ */ async analyzeEpochProofSubmission(args) {
128
+ const { epochNumber, fromCheckpoint, toCheckpoint } = args;
129
+ await this.validateEpochProofSubmission(args);
130
+ const data = this.encodeSubmitEpochProofCalldata(args);
131
+ const senderAddress = this.l1TxUtils.getSenderAddress();
132
+ const [gasLimit, gasPrice, latestBlock] = await Promise.all([
133
+ this.l1TxUtils.estimateGas(senderAddress.toString(), {
134
+ to: this.proofSubmissionTarget,
135
+ data
136
+ }),
137
+ this.l1TxUtils.getGasPrice(),
138
+ this.l1TxUtils.client.getBlock({
139
+ blockTag: 'latest'
140
+ })
141
+ ]);
142
+ const baseFeePerGas = latestBlock.baseFeePerGas ?? 0n;
143
+ const { maxPriorityFeePerGas } = gasPrice;
144
+ const effectiveFeePerGas = baseFeePerGas + maxPriorityFeePerGas;
145
+ const estimatedTotalFee = gasLimit * effectiveFeePerGas;
146
+ const stats = {
147
+ gasLimit,
148
+ baseFeePerGas,
149
+ maxPriorityFeePerGas,
150
+ estimatedTotalFee
151
+ };
152
+ this.log.info(`Estimated epoch proof submission cost (not submitted)`, {
153
+ epochNumber,
154
+ fromCheckpoint,
155
+ toCheckpoint,
156
+ gasLimit: gasLimit.toString(),
157
+ baseFeePerGas: formatGwei(baseFeePerGas),
158
+ maxPriorityFeePerGas: formatGwei(maxPriorityFeePerGas),
159
+ estimatedTotalFeeEth: formatEther(estimatedTotalFee)
160
+ });
161
+ this.metrics.recordEstimatedSubmitProof(stats);
162
+ }
163
+ encodeSubmitEpochProofCalldata(args) {
164
+ return encodeFunctionData({
165
+ abi: RollupAbi,
166
+ functionName: 'submitEpochRootProof',
167
+ args: [
168
+ this.getSubmitEpochProofArgs(args)
169
+ ]
170
+ });
171
+ }
131
172
  async sendSubmitEpochProofTx(args) {
132
173
  const txArgs = [
133
174
  this.getSubmitEpochProofArgs(args)
@@ -144,21 +185,26 @@ export class ProverNodePublisher {
144
185
  });
145
186
  try {
146
187
  const { receipt } = await this.l1TxUtils.sendAndMonitorTransaction({
147
- to: this.rollupContract.address,
188
+ to: this.proofSubmissionTarget,
148
189
  data
190
+ }, {
191
+ txTimeoutAt: args.deadline
149
192
  });
193
+ if (receipt.status !== 'success') {
194
+ const errorMsg = await this.l1TxUtils.tryGetErrorFromRevertedTx(data, {
195
+ args: [
196
+ ...txArgs
197
+ ],
198
+ functionName: 'submitEpochRootProof',
199
+ abi: RollupAbi,
200
+ address: this.proofSubmissionTarget
201
+ }, /*blobInputs*/ undefined, /*stateOverride*/ []);
202
+ this.log.error(`Rollup submit epoch proof tx reverted with ${errorMsg ?? 'unknown error'}`);
203
+ return undefined;
204
+ }
150
205
  return receipt;
151
206
  } catch (err) {
152
207
  this.log.error(`Rollup submit epoch proof failed`, err);
153
- const errorMsg = await this.l1TxUtils.tryGetErrorFromRevertedTx(data, {
154
- args: [
155
- ...txArgs
156
- ],
157
- functionName: 'submitEpochRootProof',
158
- abi: RollupAbi,
159
- address: this.rollupContract.address
160
- }, /*blobInputs*/ undefined, /*stateOverride*/ []);
161
- this.log.error(`Rollup submit epoch proof tx reverted. ${errorMsg}`);
162
208
  return undefined;
163
209
  }
164
210
  }
@@ -170,9 +216,10 @@ export class ProverNodePublisher {
170
216
  {
171
217
  previousArchive: args.publicInputs.previousArchiveRoot.toString(),
172
218
  endArchive: args.publicInputs.endArchiveRoot.toString(),
219
+ outHash: args.publicInputs.outHash.toString(),
173
220
  proverId: EthAddress.fromField(args.publicInputs.constants.proverId).toString()
174
221
  } /*_args*/ ,
175
- makeTuple(AZTEC_MAX_EPOCH_DURATION * 2, (i)=>i % 2 === 0 ? args.publicInputs.fees[i / 2].recipient.toField().toString() : args.publicInputs.fees[(i - 1) / 2].value.toString()),
222
+ args.headers.map((header)=>header.toViem()),
176
223
  getEthBlobEvaluationInputs(args.batchedBlobInputs)
177
224
  ];
178
225
  }
@@ -184,10 +231,107 @@ export class ProverNodePublisher {
184
231
  start: argsArray[0],
185
232
  end: argsArray[1],
186
233
  args: argsArray[2],
187
- fees: argsArray[3],
188
- attestations: new CommitteeAttestationsAndSigners(args.attestations.map((a)=>CommitteeAttestation.fromViem(a))).getPackedAttestations(),
234
+ headers: argsArray[3],
235
+ attestations: CommitteeAttestationsAndSigners.packAttestations(args.attestations.map((a)=>CommitteeAttestation.fromViem(a))),
189
236
  blobInputs: argsArray[4],
190
237
  proof: proofHex
191
238
  };
192
239
  }
193
240
  }
241
+ /**
242
+ * Decodes a `Root rollup public inputs mismatch`, fetches the on-chain CheckpointLog for any
243
+ * mismatching `checkpointHeaderHashes[i]`, emits a structured error log, and returns a thrown-ready
244
+ * Error with a human-readable summary.
245
+ *
246
+ * Layout of `RootRollupPublicInputs.toFields()`:
247
+ * [0] previousArchiveRoot
248
+ * [1] endArchiveRoot
249
+ * [2] outHash
250
+ * [3 .. 3+N-1] checkpointHeaderHashes[i] for i in 0..N-1 (N = MAX_CHECKPOINTS_PER_EPOCH)
251
+ * [3+N .. 3+3N-1] fees[i] = (recipient, value) for i in 0..N-1
252
+ * [3+3N .. 3+3N+4] EpochConstantData (chainId, version, vkTreeRoot, protocolContractsHash, proverId)
253
+ * [3+3N+5 ..] blobPublicInputs (FinalBlobAccumulator)
254
+ */ async function reportPublicInputsMismatch(input) {
255
+ const { rollupPublicInputs, argsPublicInputs, fromCheckpoint, toCheckpoint, rollupContract, log } = input;
256
+ const N = MAX_CHECKPOINTS_PER_EPOCH;
257
+ const constantsStart = 3 + 3 * N;
258
+ const blobStart = constantsStart + 5;
259
+ const constantLabels = [
260
+ 'chainId',
261
+ 'version',
262
+ 'vkTreeRoot',
263
+ 'protocolContractsHash',
264
+ 'proverId'
265
+ ];
266
+ const diffs = [];
267
+ const len = Math.max(rollupPublicInputs.length, argsPublicInputs.length);
268
+ for(let i = 0; i < len; i++){
269
+ const a = rollupPublicInputs[i] ?? Fr.ZERO;
270
+ const b = argsPublicInputs[i] ?? Fr.ZERO;
271
+ if (a.equals(b)) {
272
+ continue;
273
+ }
274
+ let label;
275
+ let checkpointIndex;
276
+ if (i === 0) {
277
+ label = 'previousArchiveRoot';
278
+ } else if (i === 1) {
279
+ label = 'endArchiveRoot';
280
+ } else if (i === 2) {
281
+ label = 'outHash';
282
+ } else if (i < 3 + N) {
283
+ checkpointIndex = i - 3;
284
+ label = `checkpointHeaderHashes[${checkpointIndex}]`;
285
+ } else if (i < 3 + 3 * N) {
286
+ const feePairIndex = i - (3 + N);
287
+ const feeIndex = Math.floor(feePairIndex / 2);
288
+ const sub = feePairIndex % 2 === 0 ? 'recipient' : 'value';
289
+ label = `fees[${feeIndex}].${sub}`;
290
+ } else if (i < blobStart) {
291
+ label = `constants.${constantLabels[i - constantsStart]}`;
292
+ } else {
293
+ label = `blobPublicInputs[${i - blobStart}]`;
294
+ }
295
+ diffs.push({
296
+ index: i,
297
+ label,
298
+ rollup: a,
299
+ computed: b,
300
+ checkpointIndex
301
+ });
302
+ }
303
+ // For each mismatching checkpointHeaderHash, fetch the L1 CheckpointLog so the operator can
304
+ // see what was published on-chain alongside the prover's recomputed hash.
305
+ const onChainCheckpoints = await Promise.all(diffs.filter((d)=>d.checkpointIndex !== undefined).map(async (d)=>{
306
+ const checkpointNumber = CheckpointNumber(fromCheckpoint + d.checkpointIndex);
307
+ try {
308
+ const cp = await rollupContract.getCheckpoint(checkpointNumber);
309
+ return {
310
+ checkpointIndex: d.checkpointIndex,
311
+ checkpointNumber,
312
+ headerHash: cp.headerHash.toString()
313
+ };
314
+ } catch (err) {
315
+ return {
316
+ checkpointIndex: d.checkpointIndex,
317
+ checkpointNumber,
318
+ error: err.message
319
+ };
320
+ }
321
+ }));
322
+ log.error(`Root rollup public inputs mismatch`, undefined, {
323
+ fromCheckpoint,
324
+ toCheckpoint,
325
+ numDiffs: diffs.length,
326
+ diffs: diffs.map((d)=>({
327
+ index: d.index,
328
+ label: d.label,
329
+ rollup: d.rollup.toString(),
330
+ computed: d.computed.toString()
331
+ })),
332
+ onChainCheckpoints
333
+ });
334
+ const fmt = (inputs)=>inputs.map((x)=>x.toString()).join(', ');
335
+ const summary = diffs.map((d)=>`[${d.index} ${d.label}] L1=${d.rollup} prover=${d.computed}`).join('\n');
336
+ return new Error(`Root rollup public inputs mismatch (${diffs.length} fields differ):\n${summary}\n` + `Rollup: ${fmt(rollupPublicInputs)}\nComputed:${fmt(argsPublicInputs)}`);
337
+ }