@aztec/prover-node 0.76.3 → 0.76.4-devnet-test-rc3

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 (76) hide show
  1. package/dest/bond/bond-manager.d.ts +22 -0
  2. package/dest/bond/bond-manager.d.ts.map +1 -0
  3. package/dest/bond/bond-manager.js +42 -0
  4. package/dest/bond/config.d.ts +8 -0
  5. package/dest/bond/config.d.ts.map +1 -0
  6. package/dest/bond/config.js +17 -0
  7. package/dest/bond/escrow-contract.d.ts +19 -0
  8. package/dest/bond/escrow-contract.d.ts.map +1 -0
  9. package/dest/bond/escrow-contract.js +32 -0
  10. package/dest/bond/factory.d.ts +6 -0
  11. package/dest/bond/factory.d.ts.map +1 -0
  12. package/dest/bond/factory.js +17 -0
  13. package/dest/bond/index.d.ts +3 -0
  14. package/dest/bond/index.d.ts.map +1 -0
  15. package/dest/bond/index.js +3 -0
  16. package/dest/bond/token-contract.d.ts +26 -0
  17. package/dest/bond/token-contract.d.ts.map +1 -0
  18. package/dest/bond/token-contract.js +58 -0
  19. package/dest/config.d.ts +7 -1
  20. package/dest/config.d.ts.map +1 -1
  21. package/dest/config.js +23 -2
  22. package/dest/factory.d.ts.map +1 -1
  23. package/dest/factory.js +32 -3
  24. package/dest/job/epoch-proving-job.d.ts +0 -1
  25. package/dest/job/epoch-proving-job.d.ts.map +1 -1
  26. package/dest/job/epoch-proving-job.js +1 -4
  27. package/dest/monitors/claims-monitor.d.ts +24 -0
  28. package/dest/monitors/claims-monitor.d.ts.map +1 -0
  29. package/dest/monitors/claims-monitor.js +54 -0
  30. package/dest/monitors/epoch-monitor.d.ts +1 -0
  31. package/dest/monitors/epoch-monitor.d.ts.map +1 -1
  32. package/dest/monitors/epoch-monitor.js +2 -2
  33. package/dest/monitors/index.d.ts +1 -0
  34. package/dest/monitors/index.d.ts.map +1 -1
  35. package/dest/monitors/index.js +2 -1
  36. package/dest/prover-node-publisher.d.ts +7 -0
  37. package/dest/prover-node-publisher.d.ts.map +1 -1
  38. package/dest/prover-node-publisher.js +10 -9
  39. package/dest/prover-node.d.ts +26 -12
  40. package/dest/prover-node.d.ts.map +1 -1
  41. package/dest/prover-node.js +89 -24
  42. package/dest/quote-provider/http.d.ts +15 -0
  43. package/dest/quote-provider/http.d.ts.map +1 -0
  44. package/dest/quote-provider/http.js +33 -0
  45. package/dest/quote-provider/index.d.ts +6 -0
  46. package/dest/quote-provider/index.d.ts.map +1 -0
  47. package/dest/quote-provider/index.js +2 -0
  48. package/dest/quote-provider/simple.d.ts +9 -0
  49. package/dest/quote-provider/simple.d.ts.map +1 -0
  50. package/dest/quote-provider/simple.js +11 -0
  51. package/dest/quote-provider/utils.d.ts +4 -0
  52. package/dest/quote-provider/utils.d.ts.map +1 -0
  53. package/dest/quote-provider/utils.js +8 -0
  54. package/dest/quote-signer.d.ts +13 -0
  55. package/dest/quote-signer.d.ts.map +1 -0
  56. package/dest/quote-signer.js +18 -0
  57. package/package.json +20 -20
  58. package/src/bond/bond-manager.ts +48 -0
  59. package/src/bond/config.ts +25 -0
  60. package/src/bond/escrow-contract.ts +50 -0
  61. package/src/bond/factory.ts +28 -0
  62. package/src/bond/index.ts +2 -0
  63. package/src/bond/token-contract.ts +72 -0
  64. package/src/config.ts +37 -1
  65. package/src/factory.ts +40 -2
  66. package/src/job/epoch-proving-job.ts +0 -4
  67. package/src/monitors/claims-monitor.ts +69 -0
  68. package/src/monitors/epoch-monitor.ts +2 -1
  69. package/src/monitors/index.ts +1 -0
  70. package/src/prover-node-publisher.ts +10 -8
  71. package/src/prover-node.ts +106 -29
  72. package/src/quote-provider/http.ts +48 -0
  73. package/src/quote-provider/index.ts +8 -0
  74. package/src/quote-provider/simple.ts +15 -0
  75. package/src/quote-provider/utils.ts +10 -0
  76. package/src/quote-signer.ts +24 -0
@@ -0,0 +1,69 @@
1
+ import { type EpochProofClaim } from '@aztec/circuit-types';
2
+ import { type EthAddress } from '@aztec/circuits.js';
3
+ import { createLogger } from '@aztec/foundation/log';
4
+ import { RunningPromise } from '@aztec/foundation/running-promise';
5
+ import {
6
+ type TelemetryClient,
7
+ type Traceable,
8
+ type Tracer,
9
+ getTelemetryClient,
10
+ trackSpan,
11
+ } from '@aztec/telemetry-client';
12
+
13
+ import { type ProverNodePublisher } from '../prover-node-publisher.js';
14
+
15
+ export interface ClaimsMonitorHandler {
16
+ handleClaim(proofClaim: EpochProofClaim): Promise<void>;
17
+ }
18
+
19
+ export class ClaimsMonitor implements Traceable {
20
+ private runningPromise: RunningPromise;
21
+ private log = createLogger('prover-node:claims-monitor');
22
+
23
+ private handler: ClaimsMonitorHandler | undefined;
24
+ private lastClaimEpochNumber: bigint | undefined;
25
+
26
+ public readonly tracer: Tracer;
27
+
28
+ constructor(
29
+ private readonly l1Publisher: ProverNodePublisher,
30
+ private options: { pollingIntervalMs: number },
31
+ telemetry: TelemetryClient = getTelemetryClient(),
32
+ ) {
33
+ this.tracer = telemetry.getTracer('ClaimsMonitor');
34
+ this.runningPromise = new RunningPromise(this.work.bind(this), this.log, this.options.pollingIntervalMs);
35
+ }
36
+
37
+ public start(handler: ClaimsMonitorHandler) {
38
+ this.handler = handler;
39
+ this.runningPromise.start();
40
+ this.log.info(`Started ClaimsMonitor with prover address ${this.getProverAddress().toString()}`, this.options);
41
+ }
42
+
43
+ public async stop() {
44
+ this.log.verbose('Stopping ClaimsMonitor');
45
+ await this.runningPromise.stop();
46
+ this.log.info('Stopped ClaimsMonitor');
47
+ }
48
+
49
+ @trackSpan('ClaimsMonitor.work')
50
+ public async work() {
51
+ const proofClaim = await this.l1Publisher.getProofClaim();
52
+ if (!proofClaim) {
53
+ this.log.trace(`Found no proof claim`);
54
+ return;
55
+ }
56
+
57
+ if (this.lastClaimEpochNumber === undefined || proofClaim.epochToProve > this.lastClaimEpochNumber) {
58
+ this.log.verbose(`Found new claim for epoch ${proofClaim.epochToProve} by ${proofClaim.bondProvider.toString()}`);
59
+ if (proofClaim.bondProvider.equals(this.getProverAddress())) {
60
+ await this.handler?.handleClaim(proofClaim);
61
+ }
62
+ this.lastClaimEpochNumber = proofClaim.epochToProve;
63
+ }
64
+ }
65
+
66
+ protected getProverAddress(): EthAddress {
67
+ return this.l1Publisher.getSenderAddress();
68
+ }
69
+ }
@@ -10,6 +10,7 @@ import {
10
10
  } from '@aztec/telemetry-client';
11
11
 
12
12
  export interface EpochMonitorHandler {
13
+ handleInitialEpochSync(epochNumber: bigint): Promise<void>;
13
14
  handleEpochCompleted(epochNumber: bigint): Promise<void>;
14
15
  }
15
16
 
@@ -47,7 +48,7 @@ export class EpochMonitor implements Traceable {
47
48
  if (!this.latestEpochNumber) {
48
49
  const epochNumber = await this.l2BlockSource.getL2EpochNumber();
49
50
  if (epochNumber > 0n) {
50
- await this.handler?.handleEpochCompleted(epochNumber - 1n);
51
+ await this.handler?.handleInitialEpochSync(epochNumber - 1n);
51
52
  }
52
53
  this.latestEpochNumber = epochNumber;
53
54
  return;
@@ -1 +1,2 @@
1
+ export * from './claims-monitor.js';
1
2
  export * from './epoch-monitor.js';
@@ -85,6 +85,10 @@ export class ProverNodePublisher {
85
85
  return EthAddress.fromString(this.l1TxUtils.getSenderAddress());
86
86
  }
87
87
 
88
+ public getProofClaim() {
89
+ return this.rollupContract.getProofClaim();
90
+ }
91
+
88
92
  public async submitEpochProof(args: {
89
93
  epochNumber: number;
90
94
  fromBlock: number;
@@ -201,12 +205,11 @@ export class ProverNodePublisher {
201
205
 
202
206
  const txArgs = [
203
207
  {
204
- start: argsArray[0],
205
- end: argsArray[1],
206
- args: argsArray[2],
207
- fees: argsArray[3],
208
- blobPublicInputs: argsArray[4],
209
- aggregationObject: argsArray[5],
208
+ epochSize: argsArray[0],
209
+ args: argsArray[1],
210
+ fees: argsArray[2],
211
+ blobPublicInputs: argsArray[3],
212
+ aggregationObject: argsArray[4],
210
213
  proof: proofHex,
211
214
  },
212
215
  ] as const;
@@ -249,8 +252,7 @@ export class ProverNodePublisher {
249
252
  proof: Proof;
250
253
  }) {
251
254
  return [
252
- BigInt(args.fromBlock),
253
- BigInt(args.toBlock),
255
+ BigInt(args.toBlock - args.fromBlock + 1),
254
256
  [
255
257
  args.publicInputs.previousArchive.root.toString(),
256
258
  args.publicInputs.endArchive.root.toString(),
@@ -1,6 +1,8 @@
1
1
  import {
2
+ type EpochProofClaim,
3
+ type EpochProofQuote,
4
+ EpochProofQuotePayload,
2
5
  type EpochProverManager,
3
- EpochProvingJobTerminalState,
4
6
  type L1ToL2MessageSource,
5
7
  type L2Block,
6
8
  type L2BlockSource,
@@ -32,10 +34,14 @@ import {
32
34
  trackSpan,
33
35
  } from '@aztec/telemetry-client';
34
36
 
37
+ import { type BondManager } from './bond/bond-manager.js';
35
38
  import { EpochProvingJob, type EpochProvingJobState } from './job/epoch-proving-job.js';
36
39
  import { ProverNodeMetrics } from './metrics.js';
40
+ import { type ClaimsMonitor, type ClaimsMonitorHandler } from './monitors/claims-monitor.js';
37
41
  import { type EpochMonitor, type EpochMonitorHandler } from './monitors/epoch-monitor.js';
38
42
  import { type ProverNodePublisher } from './prover-node-publisher.js';
43
+ import { type QuoteProvider } from './quote-provider/index.js';
44
+ import { type QuoteSigner } from './quote-signer.js';
39
45
 
40
46
  export type ProverNodeOptions = {
41
47
  pollingIntervalMs: number;
@@ -52,7 +58,7 @@ export type ProverNodeOptions = {
52
58
  * from a tx source in the p2p network or an external node, re-executes their public functions, creates a rollup
53
59
  * proof for the epoch, and submits it to L1.
54
60
  */
55
- export class ProverNode implements EpochMonitorHandler, ProverNodeApi, Traceable {
61
+ export class ProverNode implements ClaimsMonitorHandler, EpochMonitorHandler, ProverNodeApi, Traceable {
56
62
  private log = createLogger('prover-node');
57
63
  private dateProvider = new DateProvider();
58
64
 
@@ -75,7 +81,11 @@ export class ProverNode implements EpochMonitorHandler, ProverNodeApi, Traceable
75
81
  protected readonly contractDataSource: ContractDataSource,
76
82
  protected readonly worldState: WorldStateSynchronizer,
77
83
  protected readonly coordination: ProverCoordination & Maybe<Service>,
84
+ protected readonly quoteProvider: QuoteProvider,
85
+ protected readonly quoteSigner: QuoteSigner,
86
+ protected readonly claimsMonitor: ClaimsMonitor,
78
87
  protected readonly epochsMonitor: EpochMonitor,
88
+ protected readonly bondManager: BondManager,
79
89
  options: Partial<ProverNodeOptions> = {},
80
90
  protected readonly telemetryClient: TelemetryClient = getTelemetryClient(),
81
91
  ) {
@@ -102,23 +112,89 @@ export class ProverNode implements EpochMonitorHandler, ProverNodeApi, Traceable
102
112
  return undefined;
103
113
  }
104
114
 
115
+ async handleClaim(proofClaim: EpochProofClaim): Promise<void> {
116
+ if (proofClaim.epochToProve === this.latestEpochWeAreProving) {
117
+ this.log.verbose(`Already proving claim for epoch ${proofClaim.epochToProve}`);
118
+ return;
119
+ }
120
+
121
+ const provenEpoch = await this.l2BlockSource.getProvenL2EpochNumber();
122
+ if (provenEpoch !== undefined && proofClaim.epochToProve <= provenEpoch) {
123
+ this.log.verbose(`Claim for epoch ${proofClaim.epochToProve} is already proven`);
124
+ return;
125
+ }
126
+
127
+ try {
128
+ await this.startProof(proofClaim.epochToProve);
129
+ this.latestEpochWeAreProving = proofClaim.epochToProve;
130
+ } catch (err) {
131
+ this.log.error(`Error handling claim for epoch ${proofClaim.epochToProve}`, err);
132
+ }
133
+
134
+ try {
135
+ // Staked amounts are lowered after a claim, so this is a good time for doing a top-up if needed
136
+ await this.bondManager.ensureBond();
137
+ } catch (err) {
138
+ this.log.error(`Error ensuring prover bond after handling claim for epoch ${proofClaim.epochToProve}`, err);
139
+ }
140
+ }
141
+
105
142
  /**
106
- * Handles an epoch being completed by starting a proof for it if there are no active jobs for it.
143
+ * Handles the epoch number to prove when the prover node starts by checking if there
144
+ * is an existing claim for it. If not, it creates and sends a quote for it.
145
+ * @param epochNumber - The epoch immediately before the current one when the prover node starts.
146
+ */
147
+ async handleInitialEpochSync(epochNumber: bigint): Promise<void> {
148
+ try {
149
+ const claim = await this.publisher.getProofClaim();
150
+ if (!claim || claim.epochToProve < epochNumber) {
151
+ this.log.verbose(`Handling epoch ${epochNumber} completed as initial sync`);
152
+ await this.handleEpochCompleted(epochNumber);
153
+ }
154
+ } catch (err) {
155
+ this.log.error(`Error handling initial epoch sync`, err);
156
+ }
157
+ }
158
+
159
+ /**
160
+ * Handles an epoch being completed by sending a quote for proving it.
107
161
  * @param epochNumber - The epoch number that was just completed.
108
162
  */
109
163
  async handleEpochCompleted(epochNumber: bigint): Promise<void> {
110
164
  try {
111
- this.log.debug('jobs', JSON.stringify(this.jobs, null, 2));
112
- const activeJobs = await this.getActiveJobsForEpoch(epochNumber);
113
- if (activeJobs.length > 0) {
114
- this.log.info(`Not starting proof for ${epochNumber} since there are active jobs`);
165
+ // Gather data for the epoch
166
+ const epochData = await this.gatherEpochData(epochNumber);
167
+ const { blocks } = epochData;
168
+ this.cachedEpochData = { epochNumber, ...epochData };
169
+
170
+ // Construct a quote for the epoch
171
+ const partialQuote = await this.quoteProvider.getQuote(Number(epochNumber), blocks);
172
+ if (!partialQuote) {
173
+ this.log.info(`No quote produced for epoch ${epochNumber}`);
115
174
  return;
116
175
  }
117
- // TODO: we probably want to skip starting a proof if we are too far into the current epoch
118
- await this.startProof(epochNumber);
176
+
177
+ // Ensure we have deposited enough funds for sending this quote
178
+ await this.bondManager.ensureBond(partialQuote.bondAmount);
179
+
180
+ // Assemble and sign full quote
181
+ const quote = EpochProofQuotePayload.from({
182
+ ...partialQuote,
183
+ epochToProve: BigInt(epochNumber),
184
+ prover: this.publisher.getSenderAddress(),
185
+ validUntilSlot: partialQuote.validUntilSlot ?? BigInt(Number.MAX_SAFE_INTEGER), // Should we constrain this?
186
+ });
187
+ const signed = await this.quoteSigner.sign(quote);
188
+
189
+ // Send it to the coordinator
190
+ this.log.info(
191
+ `Sending quote for epoch ${epochNumber} with blocks ${blocks[0].number} to ${blocks.at(-1)!.number}`,
192
+ quote.toViemArgs(),
193
+ );
194
+ await this.doSendEpochProofQuote(signed);
119
195
  } catch (err) {
120
196
  if (err instanceof EmptyEpochError) {
121
- this.log.info(`Not starting proof for ${epochNumber} since no blocks were found`);
197
+ this.log.info(`Not producing quote for ${epochNumber} since no blocks were found`);
122
198
  } else {
123
199
  this.log.error(`Error handling epoch completed`, err);
124
200
  }
@@ -126,12 +202,15 @@ export class ProverNode implements EpochMonitorHandler, ProverNodeApi, Traceable
126
202
  }
127
203
 
128
204
  /**
129
- * Starts the prover node so it periodically checks for unproven epochs in the unfinalised chain from L1 and
130
- * starts proving jobs for them.
205
+ * Starts the prover node so it periodically checks for unproven epochs in the unfinalised chain from L1 and sends
206
+ * quotes for them, as well as monitors the claims for the epochs it has sent quotes for and starts proving jobs.
207
+ * This method returns once the prover node has deposited an initial bond into the escrow contract.
131
208
  */
132
- start() {
209
+ async start() {
133
210
  this.txFetcher.start();
211
+ await this.bondManager.ensureBond();
134
212
  this.epochsMonitor.start(this);
213
+ this.claimsMonitor.start(this);
135
214
  this.log.info('Started ProverNode', this.options);
136
215
  }
137
216
 
@@ -142,6 +221,7 @@ export class ProverNode implements EpochMonitorHandler, ProverNodeApi, Traceable
142
221
  this.log.info('Stopping ProverNode');
143
222
  await this.txFetcher.stop();
144
223
  await this.epochsMonitor.stop();
224
+ await this.claimsMonitor.stop();
145
225
  await this.prover.stop();
146
226
  await tryStop(this.l2BlockSource);
147
227
  this.publisher.interrupt();
@@ -152,6 +232,16 @@ export class ProverNode implements EpochMonitorHandler, ProverNodeApi, Traceable
152
232
  this.log.info('Stopped ProverNode');
153
233
  }
154
234
 
235
+ /** Sends an epoch proof quote to the coordinator. */
236
+ public sendEpochProofQuote(quote: EpochProofQuote): Promise<void> {
237
+ this.log.info(`Sending quote for epoch`, quote.toViemArgs().quote);
238
+ return this.doSendEpochProofQuote(quote);
239
+ }
240
+
241
+ private doSendEpochProofQuote(quote: EpochProofQuote) {
242
+ return this.coordination.addEpochProofQuote(quote);
243
+ }
244
+
155
245
  /**
156
246
  * Creates a proof for a block range. Returns once the proof has been submitted to L1.
157
247
  */
@@ -178,22 +268,8 @@ export class ProverNode implements EpochMonitorHandler, ProverNodeApi, Traceable
178
268
  /**
179
269
  * Returns an array of jobs being processed.
180
270
  */
181
- public getJobs(): Promise<{ uuid: string; status: EpochProvingJobState; epochNumber: number }[]> {
182
- return Promise.resolve(
183
- Array.from(this.jobs.entries()).map(([uuid, job]) => ({
184
- uuid,
185
- status: job.getState(),
186
- epochNumber: Number(job.getEpochNumber()),
187
- })),
188
- );
189
- }
190
-
191
- protected async getActiveJobsForEpoch(
192
- epochBigInt: bigint,
193
- ): Promise<{ uuid: string; status: EpochProvingJobState }[]> {
194
- const jobs = await this.getJobs();
195
- const epochNumber = Number(epochBigInt);
196
- return jobs.filter(job => job.epochNumber === epochNumber && !EpochProvingJobTerminalState.includes(job.status));
271
+ public getJobs(): Promise<{ uuid: string; status: EpochProvingJobState }[]> {
272
+ return Promise.resolve(Array.from(this.jobs.entries()).map(([uuid, job]) => ({ uuid, status: job.getState() })));
197
273
  }
198
274
 
199
275
  private checkMaximumPendingJobs() {
@@ -322,6 +398,7 @@ export class ProverNode implements EpochMonitorHandler, ProverNodeApi, Traceable
322
398
  /** Extracted for testing purposes. */
323
399
  protected async triggerMonitors() {
324
400
  await this.epochsMonitor.work();
401
+ await this.claimsMonitor.work();
325
402
  }
326
403
  }
327
404
 
@@ -0,0 +1,48 @@
1
+ import { type L2Block } from '@aztec/circuit-types';
2
+ import { jsonStringify } from '@aztec/foundation/json-rpc';
3
+
4
+ import { type QuoteProvider, type QuoteProviderResult } from './index.js';
5
+ import { getTotalFees, getTxCount } from './utils.js';
6
+
7
+ export class HttpQuoteProvider implements QuoteProvider {
8
+ constructor(private readonly url: string) {}
9
+
10
+ public async getQuote(epochNumber: number, epoch: L2Block[]): Promise<QuoteProviderResult | undefined> {
11
+ const payload: HttpQuoteRequestPayload = {
12
+ epochNumber,
13
+ fromBlock: epoch[0].number,
14
+ toBlock: epoch.at(-1)!.number,
15
+ totalFees: getTotalFees(epoch).toString(),
16
+ txCount: getTxCount(epoch),
17
+ };
18
+
19
+ const response = await fetch(this.url, {
20
+ method: 'POST',
21
+ body: jsonStringify(payload),
22
+ headers: { 'content-type': 'application/json' },
23
+ });
24
+
25
+ if (!response.ok) {
26
+ throw new Error(`Failed to fetch quote: ${response.statusText}`);
27
+ }
28
+
29
+ const data = await response.json();
30
+ if (!data.basisPointFee || !data.bondAmount) {
31
+ throw new Error(`Missing required fields (basisPointFee | bondAmount) in response: ${jsonStringify(data)}`);
32
+ }
33
+
34
+ const basisPointFee = Number(data.basisPointFee);
35
+ const bondAmount = BigInt(data.bondAmount);
36
+ const validUntilSlot = data.validUntilSlot ? BigInt(data.validUntilSlot) : undefined;
37
+
38
+ return { basisPointFee, bondAmount, validUntilSlot };
39
+ }
40
+ }
41
+
42
+ export type HttpQuoteRequestPayload = {
43
+ epochNumber: number;
44
+ fromBlock: number;
45
+ toBlock: number;
46
+ totalFees: string;
47
+ txCount: number;
48
+ };
@@ -0,0 +1,8 @@
1
+ import { type EpochProofQuotePayload, type L2Block } from '@aztec/circuit-types';
2
+
3
+ export type QuoteProviderResult = Pick<EpochProofQuotePayload, 'basisPointFee' | 'bondAmount'> &
4
+ Partial<Pick<EpochProofQuotePayload, 'validUntilSlot'>>;
5
+
6
+ export interface QuoteProvider {
7
+ getQuote(epochNumber: number, epoch: L2Block[]): Promise<QuoteProviderResult | undefined>;
8
+ }
@@ -0,0 +1,15 @@
1
+ import { type EpochProofQuotePayload, type L2Block } from '@aztec/circuit-types';
2
+
3
+ import { type QuoteProvider } from './index.js';
4
+
5
+ export class SimpleQuoteProvider implements QuoteProvider {
6
+ constructor(public readonly basisPointFee: number, public readonly bondAmount: bigint) {}
7
+
8
+ getQuote(
9
+ _epochNumber: number,
10
+ _epoch: L2Block[],
11
+ ): Promise<Pick<EpochProofQuotePayload, 'basisPointFee' | 'bondAmount'>> {
12
+ const { basisPointFee, bondAmount } = this;
13
+ return Promise.resolve({ basisPointFee, bondAmount });
14
+ }
15
+ }
@@ -0,0 +1,10 @@
1
+ import { type L2Block } from '@aztec/circuit-types';
2
+ import { Fr } from '@aztec/circuits.js';
3
+
4
+ export function getTotalFees(epoch: L2Block[]) {
5
+ return epoch.reduce((total, block) => total.add(block.header.totalFees), Fr.ZERO).toBigInt();
6
+ }
7
+
8
+ export function getTxCount(epoch: L2Block[]) {
9
+ return epoch.reduce((total, block) => total + block.body.txEffects.length, 0);
10
+ }
@@ -0,0 +1,24 @@
1
+ import { EpochProofQuote, type EpochProofQuotePayload } from '@aztec/circuit-types';
2
+ import { Buffer32 } from '@aztec/foundation/buffer';
3
+ import { Secp256k1Signer } from '@aztec/foundation/crypto';
4
+ import { type RollupAbi } from '@aztec/l1-artifacts';
5
+
6
+ import { type GetContractReturnType, type PublicClient } from 'viem';
7
+
8
+ export class QuoteSigner {
9
+ constructor(
10
+ private readonly signer: Secp256k1Signer,
11
+ private readonly quoteToDigest: (payload: EpochProofQuotePayload) => Promise<Buffer32>,
12
+ ) {}
13
+
14
+ static new(privateKey: Buffer32, rollupContract: GetContractReturnType<typeof RollupAbi, PublicClient>): QuoteSigner {
15
+ const quoteToDigest = (payload: EpochProofQuotePayload) =>
16
+ rollupContract.read.quoteToDigest([payload.toViemArgs()]).then(Buffer32.fromString);
17
+ return new QuoteSigner(new Secp256k1Signer(privateKey), quoteToDigest);
18
+ }
19
+
20
+ public async sign(payload: EpochProofQuotePayload) {
21
+ const digest = await this.quoteToDigest(payload);
22
+ return EpochProofQuote.new(digest, payload, this.signer);
23
+ }
24
+ }