@aztec/prover-node 0.56.0 → 0.57.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.
- package/dest/bond/bond-manager.d.ts +22 -0
- package/dest/bond/bond-manager.d.ts.map +1 -0
- package/dest/bond/bond-manager.js +42 -0
- package/dest/bond/config.d.ts +8 -0
- package/dest/bond/config.d.ts.map +1 -0
- package/dest/bond/config.js +17 -0
- package/dest/bond/escrow-contract.d.ts +22 -0
- package/dest/bond/escrow-contract.d.ts.map +1 -0
- package/dest/bond/escrow-contract.js +32 -0
- package/dest/bond/factory.d.ts +9 -0
- package/dest/bond/factory.d.ts.map +1 -0
- package/dest/bond/factory.js +18 -0
- package/dest/bond/index.d.ts +3 -0
- package/dest/bond/index.d.ts.map +1 -0
- package/dest/bond/index.js +3 -0
- package/dest/bond/token-contract.d.ts +29 -0
- package/dest/bond/token-contract.d.ts.map +1 -0
- package/dest/bond/token-contract.js +58 -0
- package/dest/config.d.ts +10 -4
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +30 -9
- package/dest/factory.d.ts.map +1 -1
- package/dest/factory.js +39 -9
- package/dest/job/{block-proving-job.d.ts → epoch-proving-job.d.ts} +13 -9
- package/dest/job/epoch-proving-job.d.ts.map +1 -0
- package/dest/job/epoch-proving-job.js +119 -0
- package/dest/monitors/claims-monitor.d.ts +22 -0
- package/dest/monitors/claims-monitor.d.ts.map +1 -0
- package/dest/monitors/claims-monitor.js +37 -0
- package/dest/monitors/epoch-monitor.d.ts +20 -0
- package/dest/monitors/epoch-monitor.d.ts.map +1 -0
- package/dest/monitors/epoch-monitor.js +34 -0
- package/dest/monitors/index.d.ts +3 -0
- package/dest/monitors/index.d.ts.map +1 -0
- package/dest/monitors/index.js +3 -0
- package/dest/prover-coordination/config.d.ts +7 -0
- package/dest/prover-coordination/config.d.ts.map +1 -0
- package/dest/prover-coordination/config.js +12 -0
- package/dest/prover-coordination/factory.d.ts +4 -0
- package/dest/prover-coordination/factory.d.ts.map +1 -0
- package/dest/prover-coordination/factory.js +10 -0
- package/dest/prover-coordination/index.d.ts +3 -0
- package/dest/prover-coordination/index.d.ts.map +1 -0
- package/dest/prover-coordination/index.js +3 -0
- package/dest/prover-node.d.ts +56 -32
- package/dest/prover-node.d.ts.map +1 -1
- package/dest/prover-node.js +124 -70
- package/dest/quote-provider/http.d.ts +15 -0
- package/dest/quote-provider/http.d.ts.map +1 -0
- package/dest/quote-provider/http.js +32 -0
- package/dest/quote-provider/index.d.ts +6 -0
- package/dest/quote-provider/index.d.ts.map +1 -0
- package/dest/quote-provider/index.js +2 -0
- package/dest/quote-provider/simple.d.ts +9 -0
- package/dest/quote-provider/simple.d.ts.map +1 -0
- package/dest/quote-provider/simple.js +11 -0
- package/dest/quote-provider/utils.d.ts +4 -0
- package/dest/quote-provider/utils.d.ts.map +1 -0
- package/dest/quote-provider/utils.js +8 -0
- package/dest/quote-signer.d.ts +13 -0
- package/dest/quote-signer.d.ts.map +1 -0
- package/dest/quote-signer.js +18 -0
- package/package.json +19 -13
- package/src/bond/bond-manager.ts +48 -0
- package/src/bond/config.ts +25 -0
- package/src/bond/escrow-contract.ts +63 -0
- package/src/bond/factory.ts +47 -0
- package/src/bond/index.ts +2 -0
- package/src/bond/token-contract.ts +85 -0
- package/src/config.ts +47 -12
- package/src/factory.ts +51 -10
- package/src/job/{block-proving-job.ts → epoch-proving-job.ts} +46 -56
- package/src/monitors/claims-monitor.ts +52 -0
- package/src/monitors/epoch-monitor.ts +48 -0
- package/src/monitors/index.ts +2 -0
- package/src/prover-coordination/config.ts +17 -0
- package/src/prover-coordination/factory.ts +11 -0
- package/src/{tx-provider → prover-coordination}/index.ts +1 -2
- package/src/prover-node.ts +163 -90
- package/src/quote-provider/http.ts +47 -0
- package/src/quote-provider/index.ts +8 -0
- package/src/quote-provider/simple.ts +15 -0
- package/src/quote-provider/utils.ts +10 -0
- package/src/quote-signer.ts +24 -0
- package/dest/job/block-proving-job.d.ts.map +0 -1
- package/dest/job/block-proving-job.js +0 -120
- package/dest/tx-provider/aztec-node-tx-provider.d.ts +0 -8
- package/dest/tx-provider/aztec-node-tx-provider.d.ts.map +0 -1
- package/dest/tx-provider/aztec-node-tx-provider.js +0 -10
- package/dest/tx-provider/config.d.ts +0 -7
- package/dest/tx-provider/config.d.ts.map +0 -1
- package/dest/tx-provider/config.js +0 -12
- package/dest/tx-provider/factory.d.ts +0 -4
- package/dest/tx-provider/factory.d.ts.map +0 -1
- package/dest/tx-provider/factory.js +0 -12
- package/dest/tx-provider/index.d.ts +0 -4
- package/dest/tx-provider/index.d.ts.map +0 -1
- package/dest/tx-provider/index.js +0 -4
- package/src/tx-provider/aztec-node-tx-provider.ts +0 -10
- package/src/tx-provider/config.ts +0 -17
- 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,47 @@
|
|
|
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 { proverMinimumStakeAmount: minimumStake, proverTargetStakeAmount: targetStake } = config;
|
|
36
|
+
|
|
37
|
+
const escrowContractAddress = EthAddress.fromString(await rollupContract.read.PROOF_COMMITMENT_ESCROW());
|
|
38
|
+
const escrow = new EscrowContract(client, escrowContractAddress);
|
|
39
|
+
|
|
40
|
+
const tokenContractAddress = await escrow.getTokenAddress();
|
|
41
|
+
const token = new TokenContract(client, tokenContractAddress);
|
|
42
|
+
|
|
43
|
+
// Ensure the prover has enough balance to cover escrow and try to mint otherwise if on a dev environment
|
|
44
|
+
await token.ensureBalance((targetStake ?? minimumStake) * 3n);
|
|
45
|
+
|
|
46
|
+
return new BondManager(token, escrow, minimumStake, targetStake ?? minimumStake);
|
|
47
|
+
}
|
|
@@ -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
|
-
|
|
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
|
|
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
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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, '
|
|
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
|
-
...
|
|
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 {
|
|
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 {
|
|
14
|
-
import {
|
|
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
|
-
|
|
44
|
-
|
|
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,14 +1,13 @@
|
|
|
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,
|
|
8
7
|
type ProcessedTx,
|
|
8
|
+
type ProverCoordination,
|
|
9
9
|
type Tx,
|
|
10
10
|
type TxHash,
|
|
11
|
-
type TxProvider,
|
|
12
11
|
} from '@aztec/circuit-types';
|
|
13
12
|
import { createDebugLogger } from '@aztec/foundation/log';
|
|
14
13
|
import { Timer } from '@aztec/foundation/timer';
|
|
@@ -24,20 +23,22 @@ import { type ProverNodeMetrics } from '../metrics.js';
|
|
|
24
23
|
* re-executes their public calls, generates a rollup proof, and submits it to L1. This job will update the
|
|
25
24
|
* world state as part of public call execution via the public processor.
|
|
26
25
|
*/
|
|
27
|
-
export class
|
|
28
|
-
private state:
|
|
29
|
-
private log = createDebugLogger('aztec:
|
|
26
|
+
export class EpochProvingJob {
|
|
27
|
+
private state: EpochProvingJobState = 'initialized';
|
|
28
|
+
private log = createDebugLogger('aztec:epoch-proving-job');
|
|
30
29
|
private uuid: string;
|
|
31
30
|
|
|
32
31
|
constructor(
|
|
33
|
-
private
|
|
32
|
+
private epochNumber: bigint,
|
|
33
|
+
private blocks: L2Block[],
|
|
34
|
+
private prover: EpochProver,
|
|
34
35
|
private publicProcessorFactory: PublicProcessorFactory,
|
|
35
36
|
private publisher: L1Publisher,
|
|
36
37
|
private l2BlockSource: L2BlockSource,
|
|
37
38
|
private l1ToL2MessageSource: L1ToL2MessageSource,
|
|
38
|
-
private
|
|
39
|
+
private coordination: ProverCoordination,
|
|
39
40
|
private metrics: ProverNodeMetrics,
|
|
40
|
-
private cleanUp: (job:
|
|
41
|
+
private cleanUp: (job: EpochProvingJob) => Promise<void> = () => Promise.resolve(),
|
|
41
42
|
) {
|
|
42
43
|
this.uuid = crypto.randomUUID();
|
|
43
44
|
}
|
|
@@ -46,26 +47,36 @@ export class BlockProvingJob {
|
|
|
46
47
|
return this.uuid;
|
|
47
48
|
}
|
|
48
49
|
|
|
49
|
-
public getState():
|
|
50
|
+
public getState(): EpochProvingJobState {
|
|
50
51
|
return this.state;
|
|
51
52
|
}
|
|
52
53
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
54
|
+
/**
|
|
55
|
+
* Proves the given epoch and submits the proof to L1.
|
|
56
|
+
*/
|
|
57
|
+
public async run() {
|
|
58
|
+
const epochNumber = Number(this.epochNumber);
|
|
59
|
+
const epochSize = this.blocks.length;
|
|
60
|
+
this.log.info(`Starting epoch proving job`, { epochSize, epochNumber, uuid: this.uuid });
|
|
59
61
|
this.state = 'processing';
|
|
60
62
|
const timer = new Timer();
|
|
63
|
+
|
|
61
64
|
try {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
+
this.prover.startNewEpoch(epochNumber, epochSize);
|
|
66
|
+
|
|
67
|
+
// Get the genesis header if the first block of the epoch is the first block of the chain
|
|
68
|
+
let previousHeader =
|
|
69
|
+
this.blocks[0].number === 1
|
|
70
|
+
? this.publicProcessorFactory.getInitialHeader()
|
|
71
|
+
: await this.l2BlockSource.getBlockHeader(this.blocks[0].number - 1);
|
|
72
|
+
|
|
73
|
+
for (const block of this.blocks) {
|
|
74
|
+
// Gather all data to prove this block
|
|
65
75
|
const globalVariables = block.header.globalVariables;
|
|
66
76
|
const txHashes = block.body.txEffects.map(tx => tx.txHash);
|
|
67
77
|
const txCount = block.body.numberOfTxsIncludingPadded;
|
|
68
78
|
const l1ToL2Messages = await this.getL1ToL2Messages(block);
|
|
79
|
+
const txs = await this.getTxs(txHashes);
|
|
69
80
|
|
|
70
81
|
this.log.verbose(`Starting block processing`, {
|
|
71
82
|
number: block.number,
|
|
@@ -74,54 +85,41 @@ export class BlockProvingJob {
|
|
|
74
85
|
noteHashTreeRoot: block.header.state.partial.noteHashTree.root,
|
|
75
86
|
nullifierTreeRoot: block.header.state.partial.nullifierTree.root,
|
|
76
87
|
publicDataTreeRoot: block.header.state.partial.publicDataTree.root,
|
|
77
|
-
|
|
88
|
+
previousHeader: previousHeader?.hash(),
|
|
78
89
|
uuid: this.uuid,
|
|
79
90
|
...globalVariables,
|
|
80
91
|
});
|
|
81
92
|
|
|
82
|
-
//
|
|
83
|
-
|
|
93
|
+
// Start block proving
|
|
94
|
+
await this.prover.startNewBlock(txCount, globalVariables, l1ToL2Messages);
|
|
84
95
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
const txs = await this.getTxs(txHashes);
|
|
96
|
+
// Process public fns
|
|
97
|
+
const publicProcessor = this.publicProcessorFactory.create(previousHeader, globalVariables);
|
|
88
98
|
await this.processTxs(publicProcessor, txs, txCount);
|
|
89
|
-
|
|
90
99
|
this.log.verbose(`Processed all txs for block`, {
|
|
91
100
|
blockNumber: block.number,
|
|
92
101
|
blockHash: block.hash().toString(),
|
|
93
102
|
uuid: this.uuid,
|
|
94
103
|
});
|
|
95
104
|
|
|
105
|
+
// Mark block as completed and update archive tree
|
|
96
106
|
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;
|
|
107
|
+
previousHeader = block.header;
|
|
106
108
|
}
|
|
107
109
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
+
this.state = 'awaiting-prover';
|
|
111
|
+
const { publicInputs, proof } = await this.prover.finaliseEpoch();
|
|
112
|
+
this.log.info(`Finalised proof for epoch`, { epochNumber, uuid: this.uuid });
|
|
110
113
|
|
|
111
114
|
this.state = 'publishing-proof';
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
this.prover.getProverId(),
|
|
116
|
-
aggregationObject,
|
|
117
|
-
proof,
|
|
118
|
-
);
|
|
119
|
-
this.log.info(`Submitted proof for block range`, { fromBlock, toBlock, uuid: this.uuid });
|
|
115
|
+
const [fromBlock, toBlock] = [this.blocks[0].number, this.blocks.at(-1)!.number];
|
|
116
|
+
await this.publisher.submitEpochProof({ fromBlock, toBlock, epochNumber, publicInputs, proof });
|
|
117
|
+
this.log.info(`Submitted proof for epoch`, { epochNumber, uuid: this.uuid });
|
|
120
118
|
|
|
121
119
|
this.state = 'completed';
|
|
122
120
|
this.metrics.recordProvingJob(timer);
|
|
123
121
|
} catch (err) {
|
|
124
|
-
this.log.error(`Error running
|
|
122
|
+
this.log.error(`Error running epoch prover job`, err, { uuid: this.uuid });
|
|
125
123
|
this.state = 'failed';
|
|
126
124
|
} finally {
|
|
127
125
|
await this.cleanUp(this);
|
|
@@ -132,17 +130,9 @@ export class BlockProvingJob {
|
|
|
132
130
|
this.prover.cancel();
|
|
133
131
|
}
|
|
134
132
|
|
|
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`);
|
|
139
|
-
}
|
|
140
|
-
return block;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
133
|
private async getTxs(txHashes: TxHash[]): Promise<Tx[]> {
|
|
144
134
|
const txs = await Promise.all(
|
|
145
|
-
txHashes.map(txHash => this.
|
|
135
|
+
txHashes.map(txHash => this.coordination.getTxByHash(txHash).then(tx => [txHash, tx] as const)),
|
|
146
136
|
);
|
|
147
137
|
const notFound = txs.filter(([_, tx]) => !tx);
|
|
148
138
|
if (notFound.length) {
|
|
@@ -177,7 +167,7 @@ export class BlockProvingJob {
|
|
|
177
167
|
}
|
|
178
168
|
}
|
|
179
169
|
|
|
180
|
-
export type
|
|
170
|
+
export type EpochProvingJobState =
|
|
181
171
|
| 'initialized'
|
|
182
172
|
| 'processing'
|
|
183
173
|
| '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
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { type L2BlockSource } from '@aztec/circuit-types';
|
|
2
|
+
import { createDebugLogger } from '@aztec/foundation/log';
|
|
3
|
+
import { RunningPromise } from '@aztec/foundation/running-promise';
|
|
4
|
+
|
|
5
|
+
export interface EpochMonitorHandler {
|
|
6
|
+
handleInitialEpochSync(epochNumber: bigint): Promise<void>;
|
|
7
|
+
handleEpochCompleted(epochNumber: bigint): Promise<void>;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export class EpochMonitor {
|
|
11
|
+
private runningPromise: RunningPromise;
|
|
12
|
+
private log = createDebugLogger('aztec:prover-node:epoch-monitor');
|
|
13
|
+
|
|
14
|
+
private handler: EpochMonitorHandler | undefined;
|
|
15
|
+
|
|
16
|
+
private latestEpochNumber: bigint | undefined;
|
|
17
|
+
|
|
18
|
+
constructor(private readonly l2BlockSource: L2BlockSource, private options: { pollingIntervalMs: number }) {
|
|
19
|
+
this.runningPromise = new RunningPromise(this.work.bind(this), this.options.pollingIntervalMs);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
public start(handler: EpochMonitorHandler) {
|
|
23
|
+
this.handler = handler;
|
|
24
|
+
this.runningPromise.start();
|
|
25
|
+
this.log.info('Started EpochMonitor', this.options);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
public async stop() {
|
|
29
|
+
await this.runningPromise.stop();
|
|
30
|
+
this.log.info('Stopped EpochMonitor');
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
public async work() {
|
|
34
|
+
if (!this.latestEpochNumber) {
|
|
35
|
+
const epochNumber = await this.l2BlockSource.getL2EpochNumber();
|
|
36
|
+
if (epochNumber > 0n) {
|
|
37
|
+
await this.handler?.handleInitialEpochSync(epochNumber - 1n);
|
|
38
|
+
}
|
|
39
|
+
this.latestEpochNumber = epochNumber;
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (await this.l2BlockSource.isEpochComplete(this.latestEpochNumber)) {
|
|
44
|
+
await this.handler?.handleEpochCompleted(this.latestEpochNumber);
|
|
45
|
+
this.latestEpochNumber += 1n;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|