@aztec/sequencer-client 0.0.1-commit.1bb068fb5 → 0.0.1-commit.217f559981
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/client/sequencer-client.d.ts +12 -7
- package/dest/client/sequencer-client.d.ts.map +1 -1
- package/dest/client/sequencer-client.js +15 -4
- package/dest/config.d.ts +3 -3
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +13 -4
- package/dest/global_variable_builder/global_builder.d.ts +2 -4
- package/dest/global_variable_builder/global_builder.d.ts.map +1 -1
- package/dest/publisher/config.d.ts +31 -17
- package/dest/publisher/config.d.ts.map +1 -1
- package/dest/publisher/config.js +101 -42
- package/dest/publisher/sequencer-publisher-factory.d.ts +11 -3
- package/dest/publisher/sequencer-publisher-factory.d.ts.map +1 -1
- package/dest/publisher/sequencer-publisher-factory.js +13 -2
- package/dest/publisher/sequencer-publisher.d.ts +9 -7
- package/dest/publisher/sequencer-publisher.d.ts.map +1 -1
- package/dest/publisher/sequencer-publisher.js +27 -16
- package/dest/sequencer/checkpoint_proposal_job.d.ts +1 -1
- package/dest/sequencer/checkpoint_proposal_job.d.ts.map +1 -1
- package/dest/sequencer/checkpoint_proposal_job.js +26 -6
- package/dest/sequencer/metrics.d.ts +14 -5
- package/dest/sequencer/metrics.d.ts.map +1 -1
- package/dest/sequencer/metrics.js +61 -15
- package/dest/sequencer/sequencer.d.ts +15 -7
- package/dest/sequencer/sequencer.d.ts.map +1 -1
- package/dest/sequencer/sequencer.js +23 -25
- package/dest/test/index.d.ts +3 -5
- package/dest/test/index.d.ts.map +1 -1
- package/package.json +28 -28
- package/src/client/sequencer-client.ts +25 -7
- package/src/config.ts +17 -8
- package/src/global_variable_builder/global_builder.ts +1 -1
- package/src/publisher/config.ts +112 -43
- package/src/publisher/sequencer-publisher-factory.ts +23 -6
- package/src/publisher/sequencer-publisher.ts +36 -23
- package/src/sequencer/checkpoint_proposal_job.ts +39 -7
- package/src/sequencer/metrics.ts +68 -18
- package/src/sequencer/sequencer.ts +31 -30
- package/src/test/index.ts +2 -4
package/src/publisher/config.ts
CHANGED
|
@@ -1,32 +1,45 @@
|
|
|
1
1
|
import { type BlobClientConfig, blobClientConfigMapping } from '@aztec/blob-client/client/config';
|
|
2
2
|
import { type L1ReaderConfig, l1ReaderConfigMappings } from '@aztec/ethereum/l1-reader';
|
|
3
3
|
import { type L1TxUtilsConfig, l1TxUtilsConfigMappings } from '@aztec/ethereum/l1-tx-utils/config';
|
|
4
|
-
import {
|
|
5
|
-
type ConfigMappingsType,
|
|
6
|
-
SecretValue,
|
|
7
|
-
booleanConfigHelper,
|
|
8
|
-
getConfigFromMappings,
|
|
9
|
-
} from '@aztec/foundation/config';
|
|
4
|
+
import { type ConfigMappingsType, SecretValue, booleanConfigHelper } from '@aztec/foundation/config';
|
|
10
5
|
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
11
6
|
|
|
12
|
-
/**
|
|
13
|
-
* The configuration of the rollup transaction publisher.
|
|
14
|
-
*/
|
|
7
|
+
/** Configuration of the transaction publisher. */
|
|
15
8
|
export type TxSenderConfig = L1ReaderConfig & {
|
|
16
|
-
/**
|
|
17
|
-
* The private key to be used by the publisher.
|
|
18
|
-
*/
|
|
9
|
+
/** The private key to be used by the publisher. */
|
|
19
10
|
publisherPrivateKeys?: SecretValue<`0x${string}`>[];
|
|
20
11
|
|
|
21
|
-
/**
|
|
22
|
-
* Publisher addresses to be used with a remote signer
|
|
23
|
-
*/
|
|
12
|
+
/** Publisher addresses to be used with a remote signer */
|
|
24
13
|
publisherAddresses?: EthAddress[];
|
|
25
14
|
};
|
|
26
15
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
16
|
+
export type ProverTxSenderConfig = L1ReaderConfig & {
|
|
17
|
+
proverPublisherPrivateKeys?: SecretValue<`0x${string}`>[];
|
|
18
|
+
proverPublisherAddresses?: EthAddress[];
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export type SequencerTxSenderConfig = L1ReaderConfig & {
|
|
22
|
+
sequencerPublisherPrivateKeys?: SecretValue<`0x${string}`>[];
|
|
23
|
+
sequencerPublisherAddresses?: EthAddress[];
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export function getTxSenderConfigFromProverConfig(config: ProverTxSenderConfig): TxSenderConfig {
|
|
27
|
+
return {
|
|
28
|
+
...config,
|
|
29
|
+
publisherPrivateKeys: config.proverPublisherPrivateKeys,
|
|
30
|
+
publisherAddresses: config.proverPublisherAddresses,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function getTxSenderConfigFromSequencerConfig(config: SequencerTxSenderConfig): TxSenderConfig {
|
|
35
|
+
return {
|
|
36
|
+
...config,
|
|
37
|
+
publisherPrivateKeys: config.sequencerPublisherPrivateKeys,
|
|
38
|
+
publisherAddresses: config.sequencerPublisherAddresses,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Configuration of the L1Publisher. */
|
|
30
43
|
export type PublisherConfig = L1TxUtilsConfig &
|
|
31
44
|
BlobClientConfig & {
|
|
32
45
|
/** True to use publishers in invalid states (timed out, cancelled, etc) if no other is available */
|
|
@@ -37,35 +50,76 @@ export type PublisherConfig = L1TxUtilsConfig &
|
|
|
37
50
|
publisherForwarderAddress?: EthAddress;
|
|
38
51
|
};
|
|
39
52
|
|
|
40
|
-
export
|
|
41
|
-
|
|
42
|
-
|
|
53
|
+
export type ProverPublisherConfig = L1TxUtilsConfig &
|
|
54
|
+
BlobClientConfig & {
|
|
55
|
+
fishermanMode?: boolean;
|
|
56
|
+
proverPublisherAllowInvalidStates?: boolean;
|
|
57
|
+
proverPublisherForwarderAddress?: EthAddress;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export type SequencerPublisherConfig = L1TxUtilsConfig &
|
|
61
|
+
BlobClientConfig & {
|
|
62
|
+
fishermanMode?: boolean;
|
|
63
|
+
sequencerPublisherAllowInvalidStates?: boolean;
|
|
64
|
+
sequencerPublisherForwarderAddress?: EthAddress;
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
export function getPublisherConfigFromProverConfig(config: ProverPublisherConfig): PublisherConfig {
|
|
68
|
+
return {
|
|
69
|
+
...config,
|
|
70
|
+
publisherAllowInvalidStates: config.proverPublisherAllowInvalidStates,
|
|
71
|
+
publisherForwarderAddress: config.proverPublisherForwarderAddress,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function getPublisherConfigFromSequencerConfig(config: SequencerPublisherConfig): PublisherConfig {
|
|
76
|
+
return {
|
|
77
|
+
...config,
|
|
78
|
+
publisherAllowInvalidStates: config.sequencerPublisherAllowInvalidStates,
|
|
79
|
+
publisherForwarderAddress: config.sequencerPublisherForwarderAddress,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export const proverTxSenderConfigMappings: ConfigMappingsType<Omit<ProverTxSenderConfig, 'l1Contracts'>> = {
|
|
43
84
|
...l1ReaderConfigMappings,
|
|
44
|
-
|
|
45
|
-
env:
|
|
46
|
-
description: 'The private keys to be used by the publisher.',
|
|
85
|
+
proverPublisherPrivateKeys: {
|
|
86
|
+
env: `PROVER_PUBLISHER_PRIVATE_KEYS`,
|
|
87
|
+
description: 'The private keys to be used by the prover publisher.',
|
|
47
88
|
parseEnv: (val: string) => val.split(',').map(key => new SecretValue(`0x${key.replace('0x', '')}`)),
|
|
48
89
|
defaultValue: [],
|
|
49
|
-
fallback: [
|
|
90
|
+
fallback: [`PROVER_PUBLISHER_PRIVATE_KEY`],
|
|
50
91
|
},
|
|
51
|
-
|
|
52
|
-
env:
|
|
92
|
+
proverPublisherAddresses: {
|
|
93
|
+
env: `PROVER_PUBLISHER_ADDRESSES`,
|
|
53
94
|
description: 'The addresses of the publishers to use with remote signers',
|
|
54
95
|
parseEnv: (val: string) => val.split(',').map(address => EthAddress.fromString(address)),
|
|
55
96
|
defaultValue: [],
|
|
56
97
|
},
|
|
57
|
-
}
|
|
98
|
+
};
|
|
58
99
|
|
|
59
|
-
export
|
|
60
|
-
|
|
61
|
-
|
|
100
|
+
export const sequencerTxSenderConfigMappings: ConfigMappingsType<Omit<SequencerTxSenderConfig, 'l1Contracts'>> = {
|
|
101
|
+
...l1ReaderConfigMappings,
|
|
102
|
+
sequencerPublisherPrivateKeys: {
|
|
103
|
+
env: `SEQ_PUBLISHER_PRIVATE_KEYS`,
|
|
104
|
+
description: 'The private keys to be used by the sequencer publisher.',
|
|
105
|
+
parseEnv: (val: string) => val.split(',').map(key => new SecretValue(`0x${key.replace('0x', '')}`)),
|
|
106
|
+
defaultValue: [],
|
|
107
|
+
fallback: [`SEQ_PUBLISHER_PRIVATE_KEY`],
|
|
108
|
+
},
|
|
109
|
+
sequencerPublisherAddresses: {
|
|
110
|
+
env: `SEQ_PUBLISHER_ADDRESSES`,
|
|
111
|
+
description: 'The addresses of the publishers to use with remote signers',
|
|
112
|
+
parseEnv: (val: string) => val.split(',').map(address => EthAddress.fromString(address)),
|
|
113
|
+
defaultValue: [],
|
|
114
|
+
},
|
|
115
|
+
};
|
|
62
116
|
|
|
63
|
-
export const
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
117
|
+
export const sequencerPublisherConfigMappings: ConfigMappingsType<SequencerPublisherConfig & L1TxUtilsConfig> = {
|
|
118
|
+
...l1TxUtilsConfigMappings,
|
|
119
|
+
...blobClientConfigMapping,
|
|
120
|
+
sequencerPublisherAllowInvalidStates: {
|
|
121
|
+
env: `SEQ_PUBLISHER_ALLOW_INVALID_STATES`,
|
|
67
122
|
description: 'True to use publishers in invalid states (timed out, cancelled, etc) if no other is available',
|
|
68
|
-
env: scope === `PROVER` ? `PROVER_PUBLISHER_ALLOW_INVALID_STATES` : `SEQ_PUBLISHER_ALLOW_INVALID_STATES`,
|
|
69
123
|
...booleanConfigHelper(true),
|
|
70
124
|
},
|
|
71
125
|
fishermanMode: {
|
|
@@ -74,15 +128,30 @@ export const getPublisherConfigMappings: (
|
|
|
74
128
|
'Whether to run in fisherman mode: builds blocks on every slot for validation without publishing to L1',
|
|
75
129
|
...booleanConfigHelper(false),
|
|
76
130
|
},
|
|
77
|
-
|
|
78
|
-
env:
|
|
131
|
+
sequencerPublisherForwarderAddress: {
|
|
132
|
+
env: `SEQ_PUBLISHER_FORWARDER_ADDRESS`,
|
|
79
133
|
description: 'Address of the forwarder contract to wrap all L1 transactions through (for testing purposes only)',
|
|
80
134
|
parseEnv: (val: string) => (val ? EthAddress.fromString(val) : undefined),
|
|
81
135
|
},
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
export const proverPublisherConfigMappings: ConfigMappingsType<ProverPublisherConfig & L1TxUtilsConfig> = {
|
|
82
139
|
...l1TxUtilsConfigMappings,
|
|
83
140
|
...blobClientConfigMapping,
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
}
|
|
141
|
+
proverPublisherAllowInvalidStates: {
|
|
142
|
+
env: `PROVER_PUBLISHER_ALLOW_INVALID_STATES`,
|
|
143
|
+
description: 'True to use publishers in invalid states (timed out, cancelled, etc) if no other is available',
|
|
144
|
+
...booleanConfigHelper(true),
|
|
145
|
+
},
|
|
146
|
+
fishermanMode: {
|
|
147
|
+
env: 'FISHERMAN_MODE',
|
|
148
|
+
description:
|
|
149
|
+
'Whether to run in fisherman mode: builds blocks on every slot for validation without publishing to L1',
|
|
150
|
+
...booleanConfigHelper(false),
|
|
151
|
+
},
|
|
152
|
+
proverPublisherForwarderAddress: {
|
|
153
|
+
env: `PROVER_PUBLISHER_FORWARDER_ADDRESS`,
|
|
154
|
+
description: 'Address of the forwarder contract to wrap all L1 transactions through (for testing purposes only)',
|
|
155
|
+
parseEnv: (val: string) => (val ? EthAddress.fromString(val) : undefined),
|
|
156
|
+
},
|
|
157
|
+
};
|
|
@@ -3,7 +3,7 @@ import { type Logger, createLogger } from '@aztec/aztec.js/log';
|
|
|
3
3
|
import type { BlobClientInterface } from '@aztec/blob-client/client';
|
|
4
4
|
import type { EpochCache } from '@aztec/epoch-cache';
|
|
5
5
|
import type { GovernanceProposerContract, RollupContract } from '@aztec/ethereum/contracts';
|
|
6
|
-
import type {
|
|
6
|
+
import type { L1TxUtils } from '@aztec/ethereum/l1-tx-utils';
|
|
7
7
|
import type { PublisherFilter, PublisherManager } from '@aztec/ethereum/publisher-manager';
|
|
8
8
|
import { SlotNumber } from '@aztec/foundation/branded-types';
|
|
9
9
|
import type { DateProvider } from '@aztec/foundation/timer';
|
|
@@ -26,13 +26,15 @@ export class SequencerPublisherFactory {
|
|
|
26
26
|
/** Stores the last slot in which every action was carried out by a publisher */
|
|
27
27
|
private lastActions: Partial<Record<Action, SlotNumber>> = {};
|
|
28
28
|
|
|
29
|
+
private nodeKeyStore: NodeKeystoreAdapter;
|
|
30
|
+
|
|
29
31
|
private logger: Logger;
|
|
30
32
|
|
|
31
33
|
constructor(
|
|
32
34
|
private sequencerConfig: SequencerClientConfig,
|
|
33
35
|
private deps: {
|
|
34
36
|
telemetry: TelemetryClient;
|
|
35
|
-
publisherManager: PublisherManager<
|
|
37
|
+
publisherManager: PublisherManager<L1TxUtils>;
|
|
36
38
|
blobClient: BlobClientInterface;
|
|
37
39
|
dateProvider: DateProvider;
|
|
38
40
|
epochCache: EpochCache;
|
|
@@ -45,7 +47,17 @@ export class SequencerPublisherFactory {
|
|
|
45
47
|
) {
|
|
46
48
|
this.publisherMetrics = new SequencerPublisherMetrics(deps.telemetry, 'SequencerPublisher');
|
|
47
49
|
this.logger = deps.logger ?? createLogger('sequencer');
|
|
50
|
+
this.nodeKeyStore = this.deps.nodeKeyStore;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Updates the node keystore adapter used for publisher lookups.
|
|
55
|
+
* Called when the keystore is reloaded at runtime to reflect new validator-publisher mappings.
|
|
56
|
+
*/
|
|
57
|
+
public updateNodeKeyStore(adapter: NodeKeystoreAdapter): void {
|
|
58
|
+
this.nodeKeyStore = adapter;
|
|
48
59
|
}
|
|
60
|
+
|
|
49
61
|
/**
|
|
50
62
|
* Creates a new SequencerPublisher instance.
|
|
51
63
|
* @param _validatorAddress - The address of the validator that will be using the publisher.
|
|
@@ -54,17 +66,17 @@ export class SequencerPublisherFactory {
|
|
|
54
66
|
public async create(validatorAddress?: EthAddress): Promise<AttestorPublisherPair> {
|
|
55
67
|
// If we have been given an attestor address we must only allow publishers permitted for that attestor
|
|
56
68
|
|
|
57
|
-
const allowedPublishers = !validatorAddress ? [] : this.
|
|
58
|
-
const filter: PublisherFilter<
|
|
69
|
+
const allowedPublishers = !validatorAddress ? [] : this.nodeKeyStore.getPublisherAddresses(validatorAddress);
|
|
70
|
+
const filter: PublisherFilter<L1TxUtils> = !validatorAddress
|
|
59
71
|
? () => true
|
|
60
|
-
: (utils:
|
|
72
|
+
: (utils: L1TxUtils) => {
|
|
61
73
|
const publisherAddress = utils.getSenderAddress();
|
|
62
74
|
return allowedPublishers.some(allowedPublisher => allowedPublisher.equals(publisherAddress));
|
|
63
75
|
};
|
|
64
76
|
|
|
65
77
|
const l1Publisher = await this.deps.publisherManager.getAvailablePublisher(filter);
|
|
66
78
|
const attestorAddress =
|
|
67
|
-
validatorAddress ?? this.
|
|
79
|
+
validatorAddress ?? this.nodeKeyStore.getAttestorForPublisher(l1Publisher.getSenderAddress());
|
|
68
80
|
|
|
69
81
|
const rollup = this.deps.rollupContract;
|
|
70
82
|
const slashingProposerContract = await rollup.getSlashingProposer();
|
|
@@ -89,4 +101,9 @@ export class SequencerPublisherFactory {
|
|
|
89
101
|
publisher,
|
|
90
102
|
};
|
|
91
103
|
}
|
|
104
|
+
|
|
105
|
+
/** Interrupts all publishers managed by this factory. Used during sequencer shutdown. */
|
|
106
|
+
public interruptAll(): void {
|
|
107
|
+
this.deps.publisherManager.interrupt();
|
|
108
|
+
}
|
|
92
109
|
}
|
|
@@ -19,11 +19,11 @@ import {
|
|
|
19
19
|
type L1BlobInputs,
|
|
20
20
|
type L1TxConfig,
|
|
21
21
|
type L1TxRequest,
|
|
22
|
+
type L1TxUtils,
|
|
22
23
|
MAX_L1_TX_LIMIT,
|
|
23
24
|
type TransactionStats,
|
|
24
25
|
WEI_CONST,
|
|
25
26
|
} from '@aztec/ethereum/l1-tx-utils';
|
|
26
|
-
import type { L1TxUtilsWithBlobs } from '@aztec/ethereum/l1-tx-utils-with-blobs';
|
|
27
27
|
import { FormattedViemError, formatViemError, mergeAbis, tryExtractEvent } from '@aztec/ethereum/utils';
|
|
28
28
|
import { sumBigint } from '@aztec/foundation/bigint';
|
|
29
29
|
import { toHex as toPaddedHex } from '@aztec/foundation/bigint-buffer';
|
|
@@ -33,6 +33,7 @@ import type { Fr } from '@aztec/foundation/curves/bn254';
|
|
|
33
33
|
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
34
34
|
import { Signature, type ViemSignature } from '@aztec/foundation/eth-signature';
|
|
35
35
|
import { type Logger, createLogger } from '@aztec/foundation/log';
|
|
36
|
+
import { makeBackoff, retry } from '@aztec/foundation/retry';
|
|
36
37
|
import { bufferToHex } from '@aztec/foundation/string';
|
|
37
38
|
import { DateProvider, Timer } from '@aztec/foundation/timer';
|
|
38
39
|
import { EmpireBaseAbi, ErrorsAbi, RollupAbi } from '@aztec/l1-artifacts';
|
|
@@ -46,7 +47,7 @@ import { type TelemetryClient, type Tracer, getTelemetryClient, trackSpan } from
|
|
|
46
47
|
|
|
47
48
|
import { type StateOverride, type TransactionReceipt, type TypedDataDefinition, encodeFunctionData, toHex } from 'viem';
|
|
48
49
|
|
|
49
|
-
import type {
|
|
50
|
+
import type { SequencerPublisherConfig } from './config.js';
|
|
50
51
|
import { SequencerPublisherMetrics } from './sequencer-publisher-metrics.js';
|
|
51
52
|
|
|
52
53
|
/** Arguments to the process method of the rollup contract */
|
|
@@ -115,6 +116,7 @@ export class SequencerPublisher {
|
|
|
115
116
|
protected lastActions: Partial<Record<Action, SlotNumber>> = {};
|
|
116
117
|
|
|
117
118
|
private isPayloadEmptyCache: Map<string, boolean> = new Map<string, boolean>();
|
|
119
|
+
private payloadProposedCache: Set<string> = new Set<string>();
|
|
118
120
|
|
|
119
121
|
protected log: Logger;
|
|
120
122
|
protected ethereumSlotDuration: bigint;
|
|
@@ -136,7 +138,7 @@ export class SequencerPublisher {
|
|
|
136
138
|
// Gas report for VotingWithSigTest shows a max gas of 100k, but we've seen it cost 700k+ in testnet
|
|
137
139
|
public static VOTE_GAS_GUESS: bigint = 800_000n;
|
|
138
140
|
|
|
139
|
-
public l1TxUtils:
|
|
141
|
+
public l1TxUtils: L1TxUtils;
|
|
140
142
|
public rollupContract: RollupContract;
|
|
141
143
|
public govProposerContract: GovernanceProposerContract;
|
|
142
144
|
public slashingProposerContract: EmpireSlashingProposerContract | TallySlashingProposerContract | undefined;
|
|
@@ -147,11 +149,12 @@ export class SequencerPublisher {
|
|
|
147
149
|
protected requests: RequestWithExpiry[] = [];
|
|
148
150
|
|
|
149
151
|
constructor(
|
|
150
|
-
private config:
|
|
152
|
+
private config: Pick<SequencerPublisherConfig, 'fishermanMode'> &
|
|
153
|
+
Pick<L1ContractsConfig, 'ethereumSlotDuration'> & { l1ChainId: number },
|
|
151
154
|
deps: {
|
|
152
155
|
telemetry?: TelemetryClient;
|
|
153
156
|
blobClient: BlobClientInterface;
|
|
154
|
-
l1TxUtils:
|
|
157
|
+
l1TxUtils: L1TxUtils;
|
|
155
158
|
rollupContract: RollupContract;
|
|
156
159
|
slashingProposerContract: EmpireSlashingProposerContract | TallySlashingProposerContract | undefined;
|
|
157
160
|
governanceProposerContract: GovernanceProposerContract;
|
|
@@ -639,24 +642,8 @@ export class SequencerPublisher {
|
|
|
639
642
|
options: { forcePendingCheckpointNumber?: CheckpointNumber },
|
|
640
643
|
): Promise<bigint> {
|
|
641
644
|
const ts = BigInt((await this.l1TxUtils.getBlock()).timestamp + this.ethereumSlotDuration);
|
|
642
|
-
|
|
643
|
-
// TODO(palla/mbps): This should not be needed, there's no flow where we propose with zero attestations. Or is there?
|
|
644
|
-
// If we have no attestations, we still need to provide the empty attestations
|
|
645
|
-
// so that the committee is recalculated correctly
|
|
646
|
-
// const ignoreSignatures = attestationsAndSigners.attestations.length === 0;
|
|
647
|
-
// if (ignoreSignatures) {
|
|
648
|
-
// const { committee } = await this.epochCache.getCommittee(block.header.globalVariables.slotNumber);
|
|
649
|
-
// if (!committee) {
|
|
650
|
-
// this.log.warn(`No committee found for slot ${block.header.globalVariables.slotNumber}`);
|
|
651
|
-
// throw new Error(`No committee found for slot ${block.header.globalVariables.slotNumber}`);
|
|
652
|
-
// }
|
|
653
|
-
// attestationsAndSigners.attestations = committee.map(committeeMember =>
|
|
654
|
-
// CommitteeAttestation.fromAddress(committeeMember),
|
|
655
|
-
// );
|
|
656
|
-
// }
|
|
657
|
-
|
|
658
645
|
const blobFields = checkpoint.toBlobFields();
|
|
659
|
-
const blobs = getBlobsPerL1Block(blobFields);
|
|
646
|
+
const blobs = await getBlobsPerL1Block(blobFields);
|
|
660
647
|
const blobInput = getPrefixedEthBlobCommitments(blobs);
|
|
661
648
|
|
|
662
649
|
const args = [
|
|
@@ -713,6 +700,32 @@ export class SequencerPublisher {
|
|
|
713
700
|
return false;
|
|
714
701
|
}
|
|
715
702
|
|
|
703
|
+
// Check if payload was already submitted to governance
|
|
704
|
+
const cacheKey = payload.toString();
|
|
705
|
+
if (!this.payloadProposedCache.has(cacheKey)) {
|
|
706
|
+
try {
|
|
707
|
+
const l1StartBlock = await this.rollupContract.getL1StartBlock();
|
|
708
|
+
const proposed = await retry(
|
|
709
|
+
() => base.hasPayloadBeenProposed(payload.toString(), l1StartBlock),
|
|
710
|
+
'Check if payload was proposed',
|
|
711
|
+
makeBackoff([0, 1, 2]),
|
|
712
|
+
this.log,
|
|
713
|
+
true,
|
|
714
|
+
);
|
|
715
|
+
if (proposed) {
|
|
716
|
+
this.payloadProposedCache.add(cacheKey);
|
|
717
|
+
}
|
|
718
|
+
} catch (err) {
|
|
719
|
+
this.log.warn(`Failed to check if payload ${payload} was proposed after retries, skipping signal`, err);
|
|
720
|
+
return false;
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
if (this.payloadProposedCache.has(cacheKey)) {
|
|
725
|
+
this.log.info(`Payload ${payload} was already proposed to governance, stopping signals`);
|
|
726
|
+
return false;
|
|
727
|
+
}
|
|
728
|
+
|
|
716
729
|
const cachedLastVote = this.lastActions[signalType];
|
|
717
730
|
this.lastActions[signalType] = slotNumber;
|
|
718
731
|
const action = signalType;
|
|
@@ -940,7 +953,7 @@ export class SequencerPublisher {
|
|
|
940
953
|
const checkpointHeader = checkpoint.header;
|
|
941
954
|
|
|
942
955
|
const blobFields = checkpoint.toBlobFields();
|
|
943
|
-
const blobs = getBlobsPerL1Block(blobFields);
|
|
956
|
+
const blobs = await getBlobsPerL1Block(blobFields);
|
|
944
957
|
|
|
945
958
|
const proposeTxArgs: L1ProcessArgs = {
|
|
946
959
|
header: checkpointHeader,
|
|
@@ -129,7 +129,7 @@ export class CheckpointProposalJob implements Traceable {
|
|
|
129
129
|
await Promise.all(votesPromises);
|
|
130
130
|
|
|
131
131
|
if (checkpoint) {
|
|
132
|
-
this.metrics.
|
|
132
|
+
this.metrics.recordCheckpointProposalSuccess();
|
|
133
133
|
}
|
|
134
134
|
|
|
135
135
|
// Do not post anything to L1 if we are fishermen, but do perform L1 fee analysis
|
|
@@ -186,16 +186,15 @@ export class CheckpointProposalJob implements Traceable {
|
|
|
186
186
|
const inHash = computeInHashFromL1ToL2Messages(l1ToL2Messages);
|
|
187
187
|
|
|
188
188
|
// Collect the out hashes of all the checkpoints before this one in the same epoch
|
|
189
|
-
const
|
|
190
|
-
c => c.
|
|
191
|
-
|
|
192
|
-
const previousCheckpointOutHashes = previousCheckpoints.map(c => c.getCheckpointOutHash());
|
|
189
|
+
const previousCheckpointOutHashes = (await this.l2BlockSource.getCheckpointsDataForEpoch(this.epoch))
|
|
190
|
+
.filter(c => c.checkpointNumber < this.checkpointNumber)
|
|
191
|
+
.map(c => c.checkpointOutHash);
|
|
193
192
|
|
|
194
193
|
// Get the fee asset price modifier from the oracle
|
|
195
194
|
const feeAssetPriceModifier = await this.publisher.getFeeAssetPriceModifier();
|
|
196
195
|
|
|
197
196
|
// Create a long-lived forked world state for the checkpoint builder
|
|
198
|
-
using fork = await this.worldState.fork(this.syncedToBlockNumber, { closeDelayMs: 12_000 });
|
|
197
|
+
await using fork = await this.worldState.fork(this.syncedToBlockNumber, { closeDelayMs: 12_000 });
|
|
199
198
|
|
|
200
199
|
// Create checkpoint builder for the entire slot
|
|
201
200
|
const checkpointBuilder = await this.checkpointsBuilder.startCheckpoint(
|
|
@@ -221,6 +220,7 @@ export class CheckpointProposalJob implements Traceable {
|
|
|
221
220
|
|
|
222
221
|
let blocksInCheckpoint: L2Block[] = [];
|
|
223
222
|
let blockPendingBroadcast: { block: L2Block; txs: Tx[] } | undefined = undefined;
|
|
223
|
+
const checkpointBuildTimer = new Timer();
|
|
224
224
|
|
|
225
225
|
try {
|
|
226
226
|
// Main loop: build blocks for the checkpoint
|
|
@@ -248,11 +248,28 @@ export class CheckpointProposalJob implements Traceable {
|
|
|
248
248
|
return undefined;
|
|
249
249
|
}
|
|
250
250
|
|
|
251
|
+
const minBlocksForCheckpoint = this.config.minBlocksForCheckpoint;
|
|
252
|
+
if (minBlocksForCheckpoint !== undefined && blocksInCheckpoint.length < minBlocksForCheckpoint) {
|
|
253
|
+
this.log.warn(
|
|
254
|
+
`Checkpoint has fewer blocks than minimum (${blocksInCheckpoint.length} < ${minBlocksForCheckpoint}), skipping proposal`,
|
|
255
|
+
{ slot: this.slot, blocksBuilt: blocksInCheckpoint.length, minBlocksForCheckpoint },
|
|
256
|
+
);
|
|
257
|
+
return undefined;
|
|
258
|
+
}
|
|
259
|
+
|
|
251
260
|
// Assemble and broadcast the checkpoint proposal, including the last block that was not
|
|
252
261
|
// broadcasted yet, and wait to collect the committee attestations.
|
|
253
262
|
this.setStateFn(SequencerState.ASSEMBLING_CHECKPOINT, this.slot);
|
|
254
263
|
const checkpoint = await checkpointBuilder.completeCheckpoint();
|
|
255
264
|
|
|
265
|
+
// Record checkpoint-level build metrics
|
|
266
|
+
this.metrics.recordCheckpointBuild(
|
|
267
|
+
checkpointBuildTimer.ms(),
|
|
268
|
+
blocksInCheckpoint.length,
|
|
269
|
+
checkpoint.getStats().txCount,
|
|
270
|
+
Number(checkpoint.header.totalManaUsed.toBigInt()),
|
|
271
|
+
);
|
|
272
|
+
|
|
256
273
|
// Do not collect attestations nor publish to L1 in fisherman mode
|
|
257
274
|
if (this.config.fishermanMode) {
|
|
258
275
|
this.log.info(
|
|
@@ -318,6 +335,21 @@ export class CheckpointProposalJob implements Traceable {
|
|
|
318
335
|
const aztecSlotDuration = this.l1Constants.slotDuration;
|
|
319
336
|
const slotStartBuildTimestamp = this.getSlotStartBuildTimestamp();
|
|
320
337
|
const txTimeoutAt = new Date((slotStartBuildTimestamp + aztecSlotDuration) * 1000);
|
|
338
|
+
|
|
339
|
+
// If we have been configured to potentially skip publishing checkpoint then roll the dice here
|
|
340
|
+
if (
|
|
341
|
+
this.config.skipPublishingCheckpointsPercent !== undefined &&
|
|
342
|
+
this.config.skipPublishingCheckpointsPercent > 0
|
|
343
|
+
) {
|
|
344
|
+
const result = Math.max(0, randomInt(100));
|
|
345
|
+
if (result < this.config.skipPublishingCheckpointsPercent) {
|
|
346
|
+
this.log.warn(
|
|
347
|
+
`Skipping publishing proposal for checkpoint ${checkpoint.number}. Configured percentage: ${this.config.skipPublishingCheckpointsPercent}, generated value: ${result}`,
|
|
348
|
+
);
|
|
349
|
+
return checkpoint;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
321
353
|
await this.publisher.enqueueProposeCheckpoint(checkpoint, attestations, attestationsSignature, {
|
|
322
354
|
txTimeoutAt,
|
|
323
355
|
forcePendingCheckpointNumber: this.invalidateCheckpoint?.forcePendingCheckpointNumber,
|
|
@@ -826,7 +858,7 @@ export class CheckpointProposalJob implements Traceable {
|
|
|
826
858
|
slot: this.slot,
|
|
827
859
|
feeAnalysisId: feeAnalysis?.id,
|
|
828
860
|
});
|
|
829
|
-
this.metrics.
|
|
861
|
+
this.metrics.recordCheckpointProposalFailed('block_build_failed');
|
|
830
862
|
}
|
|
831
863
|
|
|
832
864
|
this.publisher.clearPendingRequests();
|