@aztec/prover-node 0.56.0 → 0.58.0

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 (102) 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 +22 -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 +9 -0
  11. package/dest/bond/factory.d.ts.map +1 -0
  12. package/dest/bond/factory.js +19 -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 +29 -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 +10 -4
  20. package/dest/config.d.ts.map +1 -1
  21. package/dest/config.js +30 -9
  22. package/dest/factory.d.ts.map +1 -1
  23. package/dest/factory.js +39 -9
  24. package/dest/job/epoch-proving-job.d.ts +39 -0
  25. package/dest/job/epoch-proving-job.d.ts.map +1 -0
  26. package/dest/job/epoch-proving-job.js +127 -0
  27. package/dest/monitors/claims-monitor.d.ts +22 -0
  28. package/dest/monitors/claims-monitor.d.ts.map +1 -0
  29. package/dest/monitors/claims-monitor.js +37 -0
  30. package/dest/monitors/epoch-monitor.d.ts +20 -0
  31. package/dest/monitors/epoch-monitor.d.ts.map +1 -0
  32. package/dest/monitors/epoch-monitor.js +34 -0
  33. package/dest/monitors/index.d.ts +3 -0
  34. package/dest/monitors/index.d.ts.map +1 -0
  35. package/dest/monitors/index.js +3 -0
  36. package/dest/prover-coordination/config.d.ts +7 -0
  37. package/dest/prover-coordination/config.d.ts.map +1 -0
  38. package/dest/prover-coordination/config.js +12 -0
  39. package/dest/prover-coordination/factory.d.ts +4 -0
  40. package/dest/prover-coordination/factory.d.ts.map +1 -0
  41. package/dest/prover-coordination/factory.js +10 -0
  42. package/dest/prover-coordination/index.d.ts +3 -0
  43. package/dest/prover-coordination/index.d.ts.map +1 -0
  44. package/dest/prover-coordination/index.js +3 -0
  45. package/dest/prover-node.d.ts +57 -33
  46. package/dest/prover-node.d.ts.map +1 -1
  47. package/dest/prover-node.js +128 -76
  48. package/dest/quote-provider/http.d.ts +15 -0
  49. package/dest/quote-provider/http.d.ts.map +1 -0
  50. package/dest/quote-provider/http.js +32 -0
  51. package/dest/quote-provider/index.d.ts +6 -0
  52. package/dest/quote-provider/index.d.ts.map +1 -0
  53. package/dest/quote-provider/index.js +2 -0
  54. package/dest/quote-provider/simple.d.ts +9 -0
  55. package/dest/quote-provider/simple.d.ts.map +1 -0
  56. package/dest/quote-provider/simple.js +11 -0
  57. package/dest/quote-provider/utils.d.ts +4 -0
  58. package/dest/quote-provider/utils.d.ts.map +1 -0
  59. package/dest/quote-provider/utils.js +8 -0
  60. package/dest/quote-signer.d.ts +13 -0
  61. package/dest/quote-signer.d.ts.map +1 -0
  62. package/dest/quote-signer.js +18 -0
  63. package/package.json +19 -13
  64. package/src/bond/bond-manager.ts +48 -0
  65. package/src/bond/config.ts +25 -0
  66. package/src/bond/escrow-contract.ts +63 -0
  67. package/src/bond/factory.ts +48 -0
  68. package/src/bond/index.ts +2 -0
  69. package/src/bond/token-contract.ts +85 -0
  70. package/src/config.ts +47 -12
  71. package/src/factory.ts +51 -10
  72. package/src/job/{block-proving-job.ts → epoch-proving-job.ts} +59 -57
  73. package/src/monitors/claims-monitor.ts +52 -0
  74. package/src/monitors/epoch-monitor.ts +48 -0
  75. package/src/monitors/index.ts +2 -0
  76. package/src/prover-coordination/config.ts +17 -0
  77. package/src/prover-coordination/factory.ts +11 -0
  78. package/src/{tx-provider → prover-coordination}/index.ts +1 -2
  79. package/src/prover-node.ts +169 -99
  80. package/src/quote-provider/http.ts +47 -0
  81. package/src/quote-provider/index.ts +8 -0
  82. package/src/quote-provider/simple.ts +15 -0
  83. package/src/quote-provider/utils.ts +10 -0
  84. package/src/quote-signer.ts +24 -0
  85. package/dest/job/block-proving-job.d.ts +0 -33
  86. package/dest/job/block-proving-job.d.ts.map +0 -1
  87. package/dest/job/block-proving-job.js +0 -120
  88. package/dest/tx-provider/aztec-node-tx-provider.d.ts +0 -8
  89. package/dest/tx-provider/aztec-node-tx-provider.d.ts.map +0 -1
  90. package/dest/tx-provider/aztec-node-tx-provider.js +0 -10
  91. package/dest/tx-provider/config.d.ts +0 -7
  92. package/dest/tx-provider/config.d.ts.map +0 -1
  93. package/dest/tx-provider/config.js +0 -12
  94. package/dest/tx-provider/factory.d.ts +0 -4
  95. package/dest/tx-provider/factory.d.ts.map +0 -1
  96. package/dest/tx-provider/factory.js +0 -12
  97. package/dest/tx-provider/index.d.ts +0 -4
  98. package/dest/tx-provider/index.d.ts.map +0 -1
  99. package/dest/tx-provider/index.js +0 -4
  100. package/src/tx-provider/aztec-node-tx-provider.ts +0 -10
  101. package/src/tx-provider/config.ts +0 -17
  102. package/src/tx-provider/factory.ts +0 -13
@@ -0,0 +1,63 @@
1
+ import { EthAddress } from '@aztec/circuits.js';
2
+ import { IProofCommitmentEscrowAbi } from '@aztec/l1-artifacts';
3
+
4
+ import {
5
+ type Chain,
6
+ type Client,
7
+ type GetContractReturnType,
8
+ type HttpTransport,
9
+ type PrivateKeyAccount,
10
+ type PublicActions,
11
+ type PublicRpcSchema,
12
+ type WalletActions,
13
+ type WalletClient,
14
+ type WalletRpcSchema,
15
+ getContract,
16
+ } from 'viem';
17
+
18
+ export class EscrowContract {
19
+ private escrow: GetContractReturnType<
20
+ typeof IProofCommitmentEscrowAbi,
21
+ WalletClient<HttpTransport, Chain, PrivateKeyAccount>
22
+ >;
23
+
24
+ constructor(
25
+ private readonly client: Client<
26
+ HttpTransport,
27
+ Chain,
28
+ PrivateKeyAccount,
29
+ [...WalletRpcSchema, ...PublicRpcSchema],
30
+ PublicActions<HttpTransport, Chain> & WalletActions<Chain, PrivateKeyAccount>
31
+ >,
32
+ address: EthAddress,
33
+ ) {
34
+ this.escrow = getContract({ address: address.toString(), abi: IProofCommitmentEscrowAbi, client });
35
+ }
36
+
37
+ /** Returns the deposit of the publisher sender address on the proof commitment escrow contract. */
38
+ public async getProverDeposit() {
39
+ return await this.escrow.read.deposits([this.getSenderAddress().toString()]);
40
+ }
41
+
42
+ /**
43
+ * Deposits the given amount of tokens into the proof commitment escrow contract. Returns once the tx is mined.
44
+ * @param amount - The amount to deposit.
45
+ */
46
+ public async depositProverBond(amount: bigint) {
47
+ const hash = await this.escrow.write.deposit([amount]);
48
+ await this.client.waitForTransactionReceipt({ hash });
49
+ }
50
+
51
+ /** Returns the sender address for the client. */
52
+ public getSenderAddress(): EthAddress {
53
+ return EthAddress.fromString(this.client.account.address);
54
+ }
55
+
56
+ public getEscrowAddress(): EthAddress {
57
+ return EthAddress.fromString(this.escrow.address);
58
+ }
59
+
60
+ public async getTokenAddress(): Promise<EthAddress> {
61
+ return EthAddress.fromString(await this.escrow.read.token());
62
+ }
63
+ }
@@ -0,0 +1,48 @@
1
+ import { EthAddress } from '@aztec/circuits.js';
2
+ import { compact } from '@aztec/foundation/collection';
3
+ import { type RollupAbi } from '@aztec/l1-artifacts';
4
+
5
+ import {
6
+ type Chain,
7
+ type Client,
8
+ type GetContractReturnType,
9
+ type HttpTransport,
10
+ type PrivateKeyAccount,
11
+ type PublicActions,
12
+ type PublicClient,
13
+ type PublicRpcSchema,
14
+ type WalletActions,
15
+ type WalletRpcSchema,
16
+ } from 'viem';
17
+
18
+ import { BondManager } from './bond-manager.js';
19
+ import { type ProverBondManagerConfig, getProverBondManagerConfigFromEnv } from './config.js';
20
+ import { EscrowContract } from './escrow-contract.js';
21
+ import { TokenContract } from './token-contract.js';
22
+
23
+ export async function createBondManager(
24
+ rollupContract: GetContractReturnType<typeof RollupAbi, PublicClient>,
25
+ client: Client<
26
+ HttpTransport,
27
+ Chain,
28
+ PrivateKeyAccount,
29
+ [...WalletRpcSchema, ...PublicRpcSchema],
30
+ PublicActions<HttpTransport, Chain> & WalletActions<Chain, PrivateKeyAccount>
31
+ >,
32
+ overrides: Partial<ProverBondManagerConfig> = {},
33
+ ) {
34
+ const config = { ...getProverBondManagerConfigFromEnv(), ...compact(overrides) };
35
+ const { proverMinimumEscrowAmount: minimumStake, proverTargetEscrowAmount: maybeTargetStake } = config;
36
+ const targetStake = maybeTargetStake ?? minimumStake * 2n;
37
+
38
+ const escrowContractAddress = EthAddress.fromString(await rollupContract.read.PROOF_COMMITMENT_ESCROW());
39
+ const escrow = new EscrowContract(client, escrowContractAddress);
40
+
41
+ const tokenContractAddress = await escrow.getTokenAddress();
42
+ const token = new TokenContract(client, tokenContractAddress);
43
+
44
+ // Ensure the prover has enough balance to cover escrow and try to mint otherwise if on a dev environment
45
+ await token.ensureBalance(targetStake * 2n);
46
+
47
+ return new BondManager(token, escrow, minimumStake, targetStake);
48
+ }
@@ -0,0 +1,2 @@
1
+ export { BondManager } from './bond-manager.js';
2
+ export * from './factory.js';
@@ -0,0 +1,85 @@
1
+ import { EthAddress } from '@aztec/circuits.js';
2
+ import { createDebugLogger } from '@aztec/foundation/log';
3
+ import { IERC20Abi, TestERC20Abi } from '@aztec/l1-artifacts';
4
+
5
+ import {
6
+ type Chain,
7
+ type Client,
8
+ type GetContractReturnType,
9
+ type HttpTransport,
10
+ type PrivateKeyAccount,
11
+ type PublicActions,
12
+ type PublicRpcSchema,
13
+ type WalletActions,
14
+ type WalletClient,
15
+ type WalletRpcSchema,
16
+ getContract,
17
+ } from 'viem';
18
+
19
+ const MAX_ALLOWANCE = (1n << 256n) - 1n;
20
+ const MIN_ALLOWANCE = 1n << 255n;
21
+
22
+ export class TokenContract {
23
+ private token: GetContractReturnType<typeof IERC20Abi, WalletClient<HttpTransport, Chain, PrivateKeyAccount>>;
24
+ private logger = createDebugLogger('aztec:prover-node:token-contract');
25
+
26
+ constructor(
27
+ private readonly client: Client<
28
+ HttpTransport,
29
+ Chain,
30
+ PrivateKeyAccount,
31
+ [...WalletRpcSchema, ...PublicRpcSchema],
32
+ PublicActions<HttpTransport, Chain> & WalletActions<Chain, PrivateKeyAccount>
33
+ >,
34
+ address: EthAddress,
35
+ ) {
36
+ this.token = getContract({ address: address.toString(), abi: IERC20Abi, client });
37
+ }
38
+
39
+ /**
40
+ * Ensures the allowed address has near-maximum allowance, or sets it otherwise.
41
+ * Returns once allowance tx is mined successfully.
42
+ * @param allowed - Who to allow.
43
+ */
44
+ public async ensureAllowance(allowed: EthAddress) {
45
+ const allowance = await this.token.read.allowance([this.getSenderAddress().toString(), allowed.toString()]);
46
+ if (allowance < MIN_ALLOWANCE) {
47
+ this.logger.verbose(`Approving max allowance for ${allowed.toString()}`);
48
+ const hash = await this.token.write.approve([allowed.toString(), MAX_ALLOWANCE]);
49
+ await this.client.waitForTransactionReceipt({ hash });
50
+ }
51
+ }
52
+
53
+ /**
54
+ * Checks the sender address has enough balance.
55
+ * If it doesn't, it tries calling a `mint` method, available on testing environments.
56
+ * If it can't, it throws an error.
57
+ * @param amount - The balance to ensure.
58
+ */
59
+ public async ensureBalance(amount: bigint) {
60
+ const balance = await this.getBalance();
61
+ if (balance < amount) {
62
+ this.logger.verbose(`Balance ${balance} is below required ${amount}. Attempting mint.`);
63
+ const testToken = getContract({ address: this.token.address, abi: TestERC20Abi, client: this.client });
64
+ try {
65
+ await testToken.simulate.mint([this.getSenderAddress().toString(), amount - balance]);
66
+ const hash = await testToken.write.mint([this.getSenderAddress().toString(), amount - balance]);
67
+ await this.client.waitForTransactionReceipt({ hash });
68
+ this.logger.verbose(`Minted ${amount - balance} test tokens`);
69
+ } catch (err) {
70
+ this.logger.warn(`Error minting test tokens: ${err}`);
71
+ throw new Error(`Insufficient balance for ${this.getSenderAddress().toString()}: ${balance} < ${amount}`);
72
+ }
73
+ }
74
+ }
75
+
76
+ /** Returns the sender address. */
77
+ public getSenderAddress(): EthAddress {
78
+ return EthAddress.fromString(this.client.account.address);
79
+ }
80
+
81
+ /** Returns the balance of the sender. */
82
+ public async getBalance() {
83
+ return await this.token.read.balanceOf([this.getSenderAddress().toString()]);
84
+ }
85
+ }
package/src/config.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { type ArchiverConfig, archiverConfigMappings, getArchiverConfigFromEnv } from '@aztec/archiver';
2
2
  import {
3
3
  type ConfigMappingsType,
4
- booleanConfigHelper,
4
+ bigintConfigHelper,
5
5
  getConfigFromMappings,
6
6
  numberConfigHelper,
7
7
  } from '@aztec/foundation/config';
@@ -16,31 +16,62 @@ import {
16
16
  } from '@aztec/sequencer-client';
17
17
  import { type WorldStateConfig, getWorldStateConfigFromEnv, worldStateConfigMappings } from '@aztec/world-state';
18
18
 
19
- import { type TxProviderConfig, getTxProviderConfigFromEnv, txProviderConfigMappings } from './tx-provider/config.js';
19
+ import { type ProverBondManagerConfig, proverBondManagerConfigMappings } from './bond/config.js';
20
+ import {
21
+ type ProverCoordinationConfig,
22
+ getTxProviderConfigFromEnv,
23
+ proverCoordinationConfigMappings,
24
+ } from './prover-coordination/config.js';
20
25
 
21
26
  export type ProverNodeConfig = ArchiverConfig &
22
27
  ProverClientConfig &
23
28
  WorldStateConfig &
24
29
  PublisherConfig &
25
30
  TxSenderConfig &
26
- TxProviderConfig & {
27
- proverNodeDisableAutomaticProving?: boolean;
28
- proverNodeMaxPendingJobs?: number;
31
+ ProverCoordinationConfig &
32
+ ProverBondManagerConfig &
33
+ QuoteProviderConfig & {
34
+ proverNodeMaxPendingJobs: number;
35
+ proverNodePollingIntervalMs: number;
29
36
  };
30
37
 
38
+ export type QuoteProviderConfig = {
39
+ quoteProviderBasisPointFee: number;
40
+ quoteProviderBondAmount: bigint;
41
+ quoteProviderUrl?: string;
42
+ };
43
+
31
44
  const specificProverNodeConfigMappings: ConfigMappingsType<
32
- Pick<ProverNodeConfig, 'proverNodeDisableAutomaticProving' | 'proverNodeMaxPendingJobs'>
45
+ Pick<ProverNodeConfig, 'proverNodePollingIntervalMs' | 'proverNodeMaxPendingJobs'>
33
46
  > = {
34
- proverNodeDisableAutomaticProving: {
35
- env: 'PROVER_NODE_DISABLE_AUTOMATIC_PROVING',
36
- description: 'Whether to disable automatic proving of pending blocks seen on L1',
37
- ...booleanConfigHelper(false),
38
- },
39
47
  proverNodeMaxPendingJobs: {
40
48
  env: 'PROVER_NODE_MAX_PENDING_JOBS',
41
49
  description: 'The maximum number of pending jobs for the prover node',
50
+ ...numberConfigHelper(10),
51
+ },
52
+ proverNodePollingIntervalMs: {
53
+ env: 'PROVER_NODE_POLLING_INTERVAL_MS',
54
+ description: 'The interval in milliseconds to poll for new jobs',
55
+ ...numberConfigHelper(1000),
56
+ },
57
+ };
58
+
59
+ const quoteProviderConfigMappings: ConfigMappingsType<QuoteProviderConfig> = {
60
+ quoteProviderBasisPointFee: {
61
+ env: 'QUOTE_PROVIDER_BASIS_POINT_FEE',
62
+ description: 'The basis point fee to charge for providing quotes',
42
63
  ...numberConfigHelper(100),
43
64
  },
65
+ quoteProviderBondAmount: {
66
+ env: 'QUOTE_PROVIDER_BOND_AMOUNT',
67
+ description: 'The bond amount to charge for providing quotes',
68
+ ...bigintConfigHelper(1000n),
69
+ },
70
+ quoteProviderUrl: {
71
+ env: 'QUOTE_PROVIDER_URL',
72
+ description:
73
+ 'The URL of the remote quote provider. Overrides QUOTE_PROVIDER_BASIS_POINT_FEE and QUOTE_PROVIDER_BOND_AMOUNT.',
74
+ },
44
75
  };
45
76
 
46
77
  export const proverNodeConfigMappings: ConfigMappingsType<ProverNodeConfig> = {
@@ -49,7 +80,9 @@ export const proverNodeConfigMappings: ConfigMappingsType<ProverNodeConfig> = {
49
80
  ...worldStateConfigMappings,
50
81
  ...getPublisherConfigMappings('PROVER'),
51
82
  ...getTxSenderConfigMappings('PROVER'),
52
- ...txProviderConfigMappings,
83
+ ...proverCoordinationConfigMappings,
84
+ ...quoteProviderConfigMappings,
85
+ ...proverBondManagerConfigMappings,
53
86
  ...specificProverNodeConfigMappings,
54
87
  };
55
88
 
@@ -61,6 +94,8 @@ export function getProverNodeConfigFromEnv(): ProverNodeConfig {
61
94
  ...getPublisherConfigFromEnv('PROVER'),
62
95
  ...getTxSenderConfigFromEnv('PROVER'),
63
96
  ...getTxProviderConfigFromEnv(),
97
+ ...getConfigFromMappings(quoteProviderConfigMappings),
64
98
  ...getConfigFromMappings(specificProverNodeConfigMappings),
99
+ ...getConfigFromMappings(proverBondManagerConfigMappings),
65
100
  };
66
101
  }
package/src/factory.ts CHANGED
@@ -1,6 +1,9 @@
1
1
  import { type Archiver, createArchiver } from '@aztec/archiver';
2
2
  import { type AztecNode } from '@aztec/circuit-types';
3
+ import { createEthereumChain } from '@aztec/ethereum';
4
+ import { Buffer32 } from '@aztec/foundation/buffer';
3
5
  import { type DebugLogger, createDebugLogger } from '@aztec/foundation/log';
6
+ import { RollupAbi } from '@aztec/l1-artifacts';
4
7
  import { createProverClient } from '@aztec/prover-client';
5
8
  import { L1Publisher } from '@aztec/sequencer-client';
6
9
  import { createSimulationProvider } from '@aztec/simulator';
@@ -8,10 +11,17 @@ import { type TelemetryClient } from '@aztec/telemetry-client';
8
11
  import { NoopTelemetryClient } from '@aztec/telemetry-client/noop';
9
12
  import { createWorldStateSynchronizer } from '@aztec/world-state';
10
13
 
11
- import { type ProverNodeConfig } from './config.js';
14
+ import { createPublicClient, getAddress, getContract, http } from 'viem';
15
+
16
+ import { createBondManager } from './bond/factory.js';
17
+ import { type ProverNodeConfig, type QuoteProviderConfig } from './config.js';
18
+ import { ClaimsMonitor } from './monitors/claims-monitor.js';
19
+ import { EpochMonitor } from './monitors/epoch-monitor.js';
20
+ import { createProverCoordination } from './prover-coordination/factory.js';
12
21
  import { ProverNode } from './prover-node.js';
13
- import { AztecNodeTxProvider } from './tx-provider/aztec-node-tx-provider.js';
14
- import { createTxProvider } from './tx-provider/factory.js';
22
+ import { HttpQuoteProvider } from './quote-provider/http.js';
23
+ import { SimpleQuoteProvider } from './quote-provider/simple.js';
24
+ import { QuoteSigner } from './quote-signer.js';
15
25
 
16
26
  /** Creates a new prover node given a config. */
17
27
  export async function createProverNode(
@@ -39,9 +49,21 @@ export async function createProverNode(
39
49
  // REFACTOR: Move publisher out of sequencer package and into an L1-related package
40
50
  const publisher = new L1Publisher(config, telemetry);
41
51
 
42
- const txProvider = deps.aztecNodeTxProvider
43
- ? new AztecNodeTxProvider(deps.aztecNodeTxProvider)
44
- : createTxProvider(config);
52
+ const txProvider = deps.aztecNodeTxProvider ? deps.aztecNodeTxProvider : createProverCoordination(config);
53
+ const quoteProvider = createQuoteProvider(config);
54
+ const quoteSigner = createQuoteSigner(config);
55
+
56
+ const proverNodeConfig = {
57
+ maxPendingJobs: config.proverNodeMaxPendingJobs,
58
+ pollingIntervalMs: config.proverNodePollingIntervalMs,
59
+ };
60
+
61
+ const claimsMonitor = new ClaimsMonitor(publisher, proverNodeConfig);
62
+ const epochMonitor = new EpochMonitor(archiver, proverNodeConfig);
63
+
64
+ const rollupContract = publisher.getRollupContract();
65
+ const walletClient = publisher.getClient();
66
+ const bondManager = await createBondManager(rollupContract, walletClient, config);
45
67
 
46
68
  return new ProverNode(
47
69
  prover!,
@@ -52,10 +74,29 @@ export async function createProverNode(
52
74
  worldStateSynchronizer,
53
75
  txProvider,
54
76
  simulationProvider,
77
+ quoteProvider,
78
+ quoteSigner,
79
+ claimsMonitor,
80
+ epochMonitor,
81
+ bondManager,
55
82
  telemetry,
56
- {
57
- disableAutomaticProving: config.proverNodeDisableAutomaticProving,
58
- maxPendingJobs: config.proverNodeMaxPendingJobs,
59
- },
83
+ proverNodeConfig,
60
84
  );
61
85
  }
86
+
87
+ function createQuoteProvider(config: QuoteProviderConfig) {
88
+ return config.quoteProviderUrl
89
+ ? new HttpQuoteProvider(config.quoteProviderUrl)
90
+ : new SimpleQuoteProvider(config.quoteProviderBasisPointFee, config.quoteProviderBondAmount);
91
+ }
92
+
93
+ function createQuoteSigner(config: ProverNodeConfig) {
94
+ // REFACTOR: We need a package that just returns an instance of a rollup contract ready to use
95
+ const { l1RpcUrl: rpcUrl, l1ChainId: chainId, l1Contracts } = config;
96
+ const chain = createEthereumChain(rpcUrl, chainId);
97
+ const client = createPublicClient({ chain: chain.chainInfo, transport: http(chain.rpcUrl) });
98
+ const address = getAddress(l1Contracts.rollupAddress.toString());
99
+ const rollupContract = getContract({ address, abi: RollupAbi, client });
100
+ const privateKey = config.publisherPrivateKey;
101
+ return QuoteSigner.new(Buffer32.fromString(privateKey), rollupContract);
102
+ }
@@ -1,16 +1,17 @@
1
1
  import {
2
- type BlockProver,
3
2
  EmptyTxValidator,
3
+ type EpochProver,
4
4
  type L1ToL2MessageSource,
5
5
  type L2Block,
6
6
  type L2BlockSource,
7
- PROVING_STATUS,
7
+ type MerkleTreeWriteOperations,
8
8
  type ProcessedTx,
9
+ type ProverCoordination,
9
10
  type Tx,
10
11
  type TxHash,
11
- type TxProvider,
12
12
  } from '@aztec/circuit-types';
13
13
  import { createDebugLogger } from '@aztec/foundation/log';
14
+ import { promiseWithResolvers } from '@aztec/foundation/promise';
14
15
  import { Timer } from '@aztec/foundation/timer';
15
16
  import { type L1Publisher } from '@aztec/sequencer-client';
16
17
  import { type PublicProcessor, type PublicProcessorFactory } from '@aztec/simulator';
@@ -24,20 +25,25 @@ import { type ProverNodeMetrics } from '../metrics.js';
24
25
  * re-executes their public calls, generates a rollup proof, and submits it to L1. This job will update the
25
26
  * world state as part of public call execution via the public processor.
26
27
  */
27
- export class BlockProvingJob {
28
- private state: BlockProvingJobState = 'initialized';
29
- private log = createDebugLogger('aztec:block-proving-job');
28
+ export class EpochProvingJob {
29
+ private state: EpochProvingJobState = 'initialized';
30
+ private log = createDebugLogger('aztec:epoch-proving-job');
30
31
  private uuid: string;
31
32
 
33
+ private runPromise: Promise<void> | undefined;
34
+
32
35
  constructor(
33
- private prover: BlockProver,
36
+ private db: MerkleTreeWriteOperations,
37
+ private epochNumber: bigint,
38
+ private blocks: L2Block[],
39
+ private prover: EpochProver,
34
40
  private publicProcessorFactory: PublicProcessorFactory,
35
41
  private publisher: L1Publisher,
36
42
  private l2BlockSource: L2BlockSource,
37
43
  private l1ToL2MessageSource: L1ToL2MessageSource,
38
- private txProvider: TxProvider,
44
+ private coordination: ProverCoordination,
39
45
  private metrics: ProverNodeMetrics,
40
- private cleanUp: (job: BlockProvingJob) => Promise<void> = () => Promise.resolve(),
46
+ private cleanUp: (job: EpochProvingJob) => Promise<void> = () => Promise.resolve(),
41
47
  ) {
42
48
  this.uuid = crypto.randomUUID();
43
49
  }
@@ -46,26 +52,39 @@ export class BlockProvingJob {
46
52
  return this.uuid;
47
53
  }
48
54
 
49
- public getState(): BlockProvingJobState {
55
+ public getState(): EpochProvingJobState {
50
56
  return this.state;
51
57
  }
52
58
 
53
- public async run(fromBlock: number, toBlock: number) {
54
- if (fromBlock !== toBlock) {
55
- throw new Error(`Block ranges are not yet supported`);
56
- }
57
-
58
- this.log.info(`Starting block proving job`, { fromBlock, toBlock, uuid: this.uuid });
59
+ /**
60
+ * Proves the given epoch and submits the proof to L1.
61
+ */
62
+ public async run() {
63
+ const epochNumber = Number(this.epochNumber);
64
+ const epochSize = this.blocks.length;
65
+ this.log.info(`Starting epoch proving job`, { epochSize, epochNumber, uuid: this.uuid });
59
66
  this.state = 'processing';
60
67
  const timer = new Timer();
68
+
69
+ const { promise, resolve } = promiseWithResolvers<void>();
70
+ this.runPromise = promise;
71
+
61
72
  try {
62
- let historicalHeader = (await this.l2BlockSource.getBlock(fromBlock - 1))?.header;
63
- for (let blockNumber = fromBlock; blockNumber <= toBlock; blockNumber++) {
64
- const block = await this.getBlock(blockNumber);
73
+ this.prover.startNewEpoch(epochNumber, epochSize);
74
+
75
+ // Get the genesis header if the first block of the epoch is the first block of the chain
76
+ let previousHeader =
77
+ this.blocks[0].number === 1
78
+ ? this.db.getInitialHeader()
79
+ : await this.l2BlockSource.getBlockHeader(this.blocks[0].number - 1);
80
+
81
+ for (const block of this.blocks) {
82
+ // Gather all data to prove this block
65
83
  const globalVariables = block.header.globalVariables;
66
84
  const txHashes = block.body.txEffects.map(tx => tx.txHash);
67
85
  const txCount = block.body.numberOfTxsIncludingPadded;
68
86
  const l1ToL2Messages = await this.getL1ToL2Messages(block);
87
+ const txs = await this.getTxs(txHashes);
69
88
 
70
89
  this.log.verbose(`Starting block processing`, {
71
90
  number: block.number,
@@ -74,75 +93,58 @@ export class BlockProvingJob {
74
93
  noteHashTreeRoot: block.header.state.partial.noteHashTree.root,
75
94
  nullifierTreeRoot: block.header.state.partial.nullifierTree.root,
76
95
  publicDataTreeRoot: block.header.state.partial.publicDataTree.root,
77
- historicalHeader: historicalHeader?.hash(),
96
+ previousHeader: previousHeader?.hash(),
78
97
  uuid: this.uuid,
79
98
  ...globalVariables,
80
99
  });
81
100
 
82
- // When we move to proving epochs, this should change into a startNewEpoch and be lifted outside the loop.
83
- const provingTicket = await this.prover.startNewBlock(txCount, globalVariables, l1ToL2Messages);
101
+ // Start block proving
102
+ await this.prover.startNewBlock(txCount, globalVariables, l1ToL2Messages);
84
103
 
85
- const publicProcessor = this.publicProcessorFactory.create(historicalHeader, globalVariables);
86
-
87
- const txs = await this.getTxs(txHashes);
104
+ // Process public fns
105
+ const publicProcessor = this.publicProcessorFactory.create(this.db, previousHeader, globalVariables);
88
106
  await this.processTxs(publicProcessor, txs, txCount);
89
-
90
107
  this.log.verbose(`Processed all txs for block`, {
91
108
  blockNumber: block.number,
92
109
  blockHash: block.hash().toString(),
93
110
  uuid: this.uuid,
94
111
  });
95
112
 
96
- await this.prover.setBlockCompleted();
97
-
98
- // This should be moved outside the loop to match the creation of the proving ticket when we move to epochs.
99
- this.state = 'awaiting-prover';
100
- const result = await provingTicket.provingPromise;
101
- if (result.status === PROVING_STATUS.FAILURE) {
102
- throw new Error(`Block proving failed: ${result.reason}`);
103
- }
104
-
105
- historicalHeader = block.header;
113
+ // Mark block as completed and update archive tree
114
+ await this.prover.setBlockCompleted(block.header);
115
+ previousHeader = block.header;
106
116
  }
107
117
 
108
- const { block, aggregationObject, proof } = await this.prover.finaliseBlock();
109
- this.log.info(`Finalised proof for block range`, { fromBlock, toBlock, uuid: this.uuid });
118
+ this.state = 'awaiting-prover';
119
+ const { publicInputs, proof } = await this.prover.finaliseEpoch();
120
+ this.log.info(`Finalised proof for epoch`, { epochNumber, uuid: this.uuid });
110
121
 
111
122
  this.state = 'publishing-proof';
112
- await this.publisher.submitProof(
113
- block.header,
114
- block.archive.root,
115
- this.prover.getProverId(),
116
- aggregationObject,
117
- proof,
118
- );
119
- this.log.info(`Submitted proof for block range`, { fromBlock, toBlock, uuid: this.uuid });
123
+ const [fromBlock, toBlock] = [this.blocks[0].number, this.blocks.at(-1)!.number];
124
+ await this.publisher.submitEpochProof({ fromBlock, toBlock, epochNumber, publicInputs, proof });
125
+ this.log.info(`Submitted proof for epoch`, { epochNumber, uuid: this.uuid });
120
126
 
121
127
  this.state = 'completed';
122
128
  this.metrics.recordProvingJob(timer);
123
129
  } catch (err) {
124
- this.log.error(`Error running block prover job`, err, { uuid: this.uuid });
130
+ this.log.error(`Error running epoch prover job`, err, { uuid: this.uuid });
125
131
  this.state = 'failed';
126
132
  } finally {
127
133
  await this.cleanUp(this);
134
+ resolve();
128
135
  }
129
136
  }
130
137
 
131
- public stop() {
138
+ public async stop() {
132
139
  this.prover.cancel();
133
- }
134
-
135
- private async getBlock(blockNumber: number): Promise<L2Block> {
136
- const block = await this.l2BlockSource.getBlock(blockNumber);
137
- if (!block) {
138
- throw new Error(`Block ${blockNumber} not found in L2 block source`);
140
+ if (this.runPromise) {
141
+ await this.runPromise;
139
142
  }
140
- return block;
141
143
  }
142
144
 
143
145
  private async getTxs(txHashes: TxHash[]): Promise<Tx[]> {
144
146
  const txs = await Promise.all(
145
- txHashes.map(txHash => this.txProvider.getTxByHash(txHash).then(tx => [txHash, tx] as const)),
147
+ txHashes.map(txHash => this.coordination.getTxByHash(txHash).then(tx => [txHash, tx] as const)),
146
148
  );
147
149
  const notFound = txs.filter(([_, tx]) => !tx);
148
150
  if (notFound.length) {
@@ -177,7 +179,7 @@ export class BlockProvingJob {
177
179
  }
178
180
  }
179
181
 
180
- export type BlockProvingJobState =
182
+ export type EpochProvingJobState =
181
183
  | 'initialized'
182
184
  | 'processing'
183
185
  | 'awaiting-prover'
@@ -0,0 +1,52 @@
1
+ import { type EpochProofClaim } from '@aztec/circuit-types';
2
+ import { type EthAddress } from '@aztec/circuits.js';
3
+ import { createDebugLogger } from '@aztec/foundation/log';
4
+ import { RunningPromise } from '@aztec/foundation/running-promise';
5
+ import { type L1Publisher } from '@aztec/sequencer-client';
6
+
7
+ export interface ClaimsMonitorHandler {
8
+ handleClaim(proofClaim: EpochProofClaim): Promise<void>;
9
+ }
10
+
11
+ export class ClaimsMonitor {
12
+ private runningPromise: RunningPromise;
13
+ private log = createDebugLogger('aztec:prover-node:claims-monitor');
14
+
15
+ private handler: ClaimsMonitorHandler | undefined;
16
+ private lastClaimEpochNumber: bigint | undefined;
17
+
18
+ constructor(private readonly l1Publisher: L1Publisher, private options: { pollingIntervalMs: number }) {
19
+ this.runningPromise = new RunningPromise(this.work.bind(this), this.options.pollingIntervalMs);
20
+ }
21
+
22
+ public start(handler: ClaimsMonitorHandler) {
23
+ this.handler = handler;
24
+ this.runningPromise.start();
25
+ this.log.info(`Started ClaimsMonitor with prover address ${this.getProverAddress().toString()}`, this.options);
26
+ }
27
+
28
+ public async stop() {
29
+ this.log.verbose('Stopping ClaimsMonitor');
30
+ await this.runningPromise.stop();
31
+ this.log.info('Stopped ClaimsMonitor');
32
+ }
33
+
34
+ public async work() {
35
+ const proofClaim = await this.l1Publisher.getProofClaim();
36
+ if (!proofClaim) {
37
+ return;
38
+ }
39
+
40
+ if (this.lastClaimEpochNumber === undefined || proofClaim.epochToProve > this.lastClaimEpochNumber) {
41
+ this.log.verbose(`Found new claim for epoch ${proofClaim.epochToProve} by ${proofClaim.bondProvider.toString()}`);
42
+ if (proofClaim.bondProvider.equals(this.getProverAddress())) {
43
+ await this.handler?.handleClaim(proofClaim);
44
+ }
45
+ this.lastClaimEpochNumber = proofClaim.epochToProve;
46
+ }
47
+ }
48
+
49
+ protected getProverAddress(): EthAddress {
50
+ return this.l1Publisher.getSenderAddress();
51
+ }
52
+ }