@aztec/sequencer-client 0.0.1-commit.6b113946b → 0.0.1-commit.6bd18f1aa
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 +4 -1
- package/dest/client/sequencer-client.d.ts.map +1 -1
- package/dest/client/sequencer-client.js +8 -13
- package/dest/global_variable_builder/global_builder.d.ts +13 -7
- package/dest/global_variable_builder/global_builder.d.ts.map +1 -1
- package/dest/global_variable_builder/global_builder.js +22 -22
- package/dest/global_variable_builder/index.d.ts +2 -2
- package/dest/global_variable_builder/index.d.ts.map +1 -1
- package/dest/publisher/config.d.ts +13 -1
- package/dest/publisher/config.d.ts.map +1 -1
- package/dest/publisher/config.js +17 -2
- package/dest/publisher/sequencer-publisher-factory.d.ts +3 -3
- package/dest/publisher/sequencer-publisher-factory.d.ts.map +1 -1
- package/dest/publisher/sequencer-publisher-factory.js +2 -2
- package/dest/publisher/sequencer-publisher.d.ts +10 -5
- package/dest/publisher/sequencer-publisher.d.ts.map +1 -1
- package/dest/publisher/sequencer-publisher.js +46 -33
- package/dest/sequencer/checkpoint_proposal_job.js +1 -1
- package/dest/sequencer/checkpoint_voter.d.ts +1 -2
- package/dest/sequencer/checkpoint_voter.d.ts.map +1 -1
- package/dest/sequencer/checkpoint_voter.js +2 -5
- package/dest/sequencer/sequencer.js +3 -3
- package/package.json +27 -27
- package/src/client/sequencer-client.ts +11 -17
- package/src/global_variable_builder/global_builder.ts +22 -24
- package/src/global_variable_builder/index.ts +1 -1
- package/src/publisher/config.ts +32 -0
- package/src/publisher/sequencer-publisher-factory.ts +3 -3
- package/src/publisher/sequencer-publisher.ts +37 -39
- package/src/sequencer/checkpoint_proposal_job.ts +1 -1
- package/src/sequencer/checkpoint_voter.ts +1 -12
- package/src/sequencer/sequencer.ts +3 -3
package/src/publisher/config.ts
CHANGED
|
@@ -4,6 +4,8 @@ import { type L1TxUtilsConfig, l1TxUtilsConfigMappings } from '@aztec/ethereum/l
|
|
|
4
4
|
import { type ConfigMappingsType, SecretValue, booleanConfigHelper } from '@aztec/foundation/config';
|
|
5
5
|
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
6
6
|
|
|
7
|
+
import { parseEther } from 'viem';
|
|
8
|
+
|
|
7
9
|
/** Configuration of the transaction publisher. */
|
|
8
10
|
export type TxSenderConfig = L1ReaderConfig & {
|
|
9
11
|
/** The private key to be used by the publisher. */
|
|
@@ -50,13 +52,37 @@ export type PublisherConfig = L1TxUtilsConfig &
|
|
|
50
52
|
publisherForwarderAddress?: EthAddress;
|
|
51
53
|
/** Store for failed L1 transaction inputs (test networks only). Format: gs://bucket/path */
|
|
52
54
|
l1TxFailedStore?: string;
|
|
55
|
+
/** Min ETH balance below which a publisher gets funded. Undefined = funding disabled. */
|
|
56
|
+
publisherFundingThreshold?: bigint;
|
|
57
|
+
/** Amount of ETH to send when funding a publisher. Undefined = funding disabled. */
|
|
58
|
+
publisherFundingAmount?: bigint;
|
|
53
59
|
};
|
|
54
60
|
|
|
61
|
+
/** Shared config mappings for publisher funding, used by both sequencer and prover publisher configs. */
|
|
62
|
+
const publisherFundingConfigMappings = {
|
|
63
|
+
publisherFundingThreshold: {
|
|
64
|
+
env: 'PUBLISHER_FUNDING_THRESHOLD' as const,
|
|
65
|
+
description:
|
|
66
|
+
'Min ETH balance below which a publisher gets funded. Specified in ether (e.g. 0.1). Unset = funding disabled.',
|
|
67
|
+
parseEnv: (val: string) => parseEther(val),
|
|
68
|
+
},
|
|
69
|
+
publisherFundingAmount: {
|
|
70
|
+
env: 'PUBLISHER_FUNDING_AMOUNT' as const,
|
|
71
|
+
description:
|
|
72
|
+
'Amount of ETH to send when funding a publisher. Specified in ether (e.g. 0.5). Unset = funding disabled.',
|
|
73
|
+
parseEnv: (val: string) => parseEther(val),
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
|
|
55
77
|
export type ProverPublisherConfig = L1TxUtilsConfig &
|
|
56
78
|
BlobClientConfig & {
|
|
57
79
|
fishermanMode?: boolean;
|
|
58
80
|
proverPublisherAllowInvalidStates?: boolean;
|
|
59
81
|
proverPublisherForwarderAddress?: EthAddress;
|
|
82
|
+
/** Min ETH balance below which a publisher gets funded. Undefined = funding disabled. */
|
|
83
|
+
publisherFundingThreshold?: bigint;
|
|
84
|
+
/** Amount of ETH to send when funding a publisher. Undefined = funding disabled. */
|
|
85
|
+
publisherFundingAmount?: bigint;
|
|
60
86
|
};
|
|
61
87
|
|
|
62
88
|
export type SequencerPublisherConfig = L1TxUtilsConfig &
|
|
@@ -66,6 +92,10 @@ export type SequencerPublisherConfig = L1TxUtilsConfig &
|
|
|
66
92
|
sequencerPublisherForwarderAddress?: EthAddress;
|
|
67
93
|
/** Store for failed L1 transaction inputs (test networks only). Format: gs://bucket/path */
|
|
68
94
|
l1TxFailedStore?: string;
|
|
95
|
+
/** Min ETH balance below which a publisher gets funded. Undefined = funding disabled. */
|
|
96
|
+
publisherFundingThreshold?: bigint;
|
|
97
|
+
/** Amount of ETH to send when funding a publisher. Undefined = funding disabled. */
|
|
98
|
+
publisherFundingAmount?: bigint;
|
|
69
99
|
};
|
|
70
100
|
|
|
71
101
|
export function getPublisherConfigFromProverConfig(config: ProverPublisherConfig): PublisherConfig {
|
|
@@ -142,6 +172,7 @@ export const sequencerPublisherConfigMappings: ConfigMappingsType<SequencerPubli
|
|
|
142
172
|
env: 'L1_TX_FAILED_STORE',
|
|
143
173
|
description: 'Store for failed L1 transaction inputs (test networks only). Format: gs://bucket/path',
|
|
144
174
|
},
|
|
175
|
+
...publisherFundingConfigMappings,
|
|
145
176
|
};
|
|
146
177
|
|
|
147
178
|
export const proverPublisherConfigMappings: ConfigMappingsType<ProverPublisherConfig & L1TxUtilsConfig> = {
|
|
@@ -163,4 +194,5 @@ export const proverPublisherConfigMappings: ConfigMappingsType<ProverPublisherCo
|
|
|
163
194
|
description: 'Address of the forwarder contract to wrap all L1 transactions through (for testing purposes only)',
|
|
164
195
|
parseEnv: (val: string) => (val ? EthAddress.fromString(val) : undefined),
|
|
165
196
|
},
|
|
197
|
+
...publisherFundingConfigMappings,
|
|
166
198
|
};
|
|
@@ -117,8 +117,8 @@ export class SequencerPublisherFactory {
|
|
|
117
117
|
};
|
|
118
118
|
}
|
|
119
119
|
|
|
120
|
-
/**
|
|
121
|
-
public
|
|
122
|
-
this.deps.publisherManager.
|
|
120
|
+
/** Stops all publishers managed by this factory. Used during sequencer shutdown. */
|
|
121
|
+
public async stopAll(): Promise<void> {
|
|
122
|
+
await this.deps.publisherManager.stop();
|
|
123
123
|
}
|
|
124
124
|
}
|
|
@@ -42,6 +42,7 @@ import { EmpireBaseAbi, ErrorsAbi, RollupAbi } from '@aztec/l1-artifacts';
|
|
|
42
42
|
import { type ProposerSlashAction, encodeSlashConsensusVotes } from '@aztec/slasher';
|
|
43
43
|
import { CommitteeAttestationsAndSigners, type ValidateCheckpointResult } from '@aztec/stdlib/block';
|
|
44
44
|
import type { Checkpoint } from '@aztec/stdlib/checkpoint';
|
|
45
|
+
import { getLastL1SlotTimestampForL2Slot, getNextL1SlotTimestamp } from '@aztec/stdlib/epoch-helpers';
|
|
45
46
|
import { SlashFactoryContract } from '@aztec/stdlib/l1-contracts';
|
|
46
47
|
import type { CheckpointHeader } from '@aztec/stdlib/rollup';
|
|
47
48
|
import type { L1PublishCheckpointStats } from '@aztec/stdlib/stats';
|
|
@@ -134,6 +135,7 @@ export class SequencerPublisher {
|
|
|
134
135
|
protected log: Logger;
|
|
135
136
|
protected ethereumSlotDuration: bigint;
|
|
136
137
|
protected aztecSlotDuration: bigint;
|
|
138
|
+
private dateProvider: DateProvider;
|
|
137
139
|
|
|
138
140
|
private blobClient: BlobClientInterface;
|
|
139
141
|
|
|
@@ -187,6 +189,7 @@ export class SequencerPublisher {
|
|
|
187
189
|
this.log = deps.log ?? createLogger('sequencer:publisher');
|
|
188
190
|
this.ethereumSlotDuration = BigInt(config.ethereumSlotDuration);
|
|
189
191
|
this.aztecSlotDuration = BigInt(config.aztecSlotDuration);
|
|
192
|
+
this.dateProvider = deps.dateProvider;
|
|
190
193
|
this.epochCache = deps.epochCache;
|
|
191
194
|
this.lastActions = deps.lastActions;
|
|
192
195
|
|
|
@@ -612,9 +615,10 @@ export class SequencerPublisher {
|
|
|
612
615
|
|
|
613
616
|
const pipelined = opts.pipelined ?? this.epochCache.isProposerPipeliningEnabled();
|
|
614
617
|
const slotOffset = pipelined ? this.aztecSlotDuration : 0n;
|
|
618
|
+
const nextL1SlotTs = this.getNextL1SlotTimestamp() + slotOffset;
|
|
615
619
|
|
|
616
620
|
return this.rollupContract
|
|
617
|
-
.canProposeAt(tipArchive.toBuffer(), msgSender.toString(),
|
|
621
|
+
.canProposeAt(tipArchive.toBuffer(), msgSender.toString(), nextL1SlotTs, {
|
|
618
622
|
forcePendingCheckpointNumber: opts.forcePendingCheckpointNumber,
|
|
619
623
|
})
|
|
620
624
|
.catch(err => {
|
|
@@ -652,7 +656,7 @@ export class SequencerPublisher {
|
|
|
652
656
|
flags,
|
|
653
657
|
] as const;
|
|
654
658
|
|
|
655
|
-
const ts =
|
|
659
|
+
const ts = this.getSimulationTimestamp(header.slotNumber);
|
|
656
660
|
const stateOverrides = await this.rollupContract.makePendingCheckpointNumberOverride(
|
|
657
661
|
opts?.forcePendingCheckpointNumber,
|
|
658
662
|
);
|
|
@@ -675,7 +679,7 @@ export class SequencerPublisher {
|
|
|
675
679
|
data: encodeFunctionData({ abi: RollupAbi, functionName: 'validateHeaderWithAttestations', args }),
|
|
676
680
|
from: MULTI_CALL_3_ADDRESS,
|
|
677
681
|
},
|
|
678
|
-
{ time: ts
|
|
682
|
+
{ time: ts },
|
|
679
683
|
stateOverrides,
|
|
680
684
|
);
|
|
681
685
|
this.log.debug(`Simulated validateHeader`);
|
|
@@ -816,10 +820,7 @@ export class SequencerPublisher {
|
|
|
816
820
|
attestationsAndSigners: CommitteeAttestationsAndSigners,
|
|
817
821
|
attestationsAndSignersSignature: Signature,
|
|
818
822
|
options: { forcePendingCheckpointNumber?: CheckpointNumber },
|
|
819
|
-
): Promise<
|
|
820
|
-
// Anchor the simulation timestamp to the checkpoint's own slot start time
|
|
821
|
-
// rather than the current L1 block timestamp, which may overshoot into the next slot if the build ran late.
|
|
822
|
-
const ts = checkpoint.header.timestamp;
|
|
823
|
+
): Promise<void> {
|
|
823
824
|
const blobFields = checkpoint.toBlobFields();
|
|
824
825
|
const blobs = await getBlobsPerL1Block(blobFields);
|
|
825
826
|
const blobInput = getPrefixedEthBlobCommitments(blobs);
|
|
@@ -838,13 +839,11 @@ export class SequencerPublisher {
|
|
|
838
839
|
blobInput,
|
|
839
840
|
] as const;
|
|
840
841
|
|
|
841
|
-
await this.simulateProposeTx(args,
|
|
842
|
-
return ts;
|
|
842
|
+
await this.simulateProposeTx(args, options);
|
|
843
843
|
}
|
|
844
844
|
|
|
845
845
|
private async enqueueCastSignalHelper(
|
|
846
846
|
slotNumber: SlotNumber,
|
|
847
|
-
timestamp: bigint,
|
|
848
847
|
signalType: GovernanceSignalAction,
|
|
849
848
|
payload: EthAddress,
|
|
850
849
|
base: IEmpireBase,
|
|
@@ -923,13 +922,17 @@ export class SequencerPublisher {
|
|
|
923
922
|
});
|
|
924
923
|
|
|
925
924
|
const l1BlockNumber = await this.l1TxUtils.getBlockNumber();
|
|
925
|
+
const timestamp = this.getSimulationTimestamp(slotNumber);
|
|
926
926
|
|
|
927
927
|
try {
|
|
928
928
|
await this.l1TxUtils.simulate(request, { time: timestamp }, [], mergeAbis([request.abi ?? [], ErrorsAbi]));
|
|
929
929
|
this.log.debug(`Simulation for ${action} at slot ${slotNumber} succeeded`, { request });
|
|
930
930
|
} catch (err) {
|
|
931
931
|
const viemError = formatViemError(err);
|
|
932
|
-
this.log.error(`Failed simulation for ${action} at slot ${slotNumber} (enqueuing the action anyway)`, viemError
|
|
932
|
+
this.log.error(`Failed simulation for ${action} at slot ${slotNumber} (enqueuing the action anyway)`, viemError, {
|
|
933
|
+
simulationTimestamp: timestamp,
|
|
934
|
+
l1BlockNumber,
|
|
935
|
+
});
|
|
933
936
|
this.backupFailedTx({
|
|
934
937
|
id: keccak256(request.data!),
|
|
935
938
|
failureType: 'simulation',
|
|
@@ -992,19 +995,16 @@ export class SequencerPublisher {
|
|
|
992
995
|
/**
|
|
993
996
|
* Enqueues a governance castSignal transaction to cast a signal for a given slot number.
|
|
994
997
|
* @param slotNumber - The slot number to cast a signal for.
|
|
995
|
-
* @param timestamp - The timestamp of the slot to cast a signal for.
|
|
996
998
|
* @returns True if the signal was successfully enqueued, false otherwise.
|
|
997
999
|
*/
|
|
998
1000
|
public enqueueGovernanceCastSignal(
|
|
999
1001
|
governancePayload: EthAddress,
|
|
1000
1002
|
slotNumber: SlotNumber,
|
|
1001
|
-
timestamp: bigint,
|
|
1002
1003
|
signerAddress: EthAddress,
|
|
1003
1004
|
signer: (msg: TypedDataDefinition) => Promise<`0x${string}`>,
|
|
1004
1005
|
): Promise<boolean> {
|
|
1005
1006
|
return this.enqueueCastSignalHelper(
|
|
1006
1007
|
slotNumber,
|
|
1007
|
-
timestamp,
|
|
1008
1008
|
'governance-signal',
|
|
1009
1009
|
governancePayload,
|
|
1010
1010
|
this.govProposerContract,
|
|
@@ -1017,7 +1017,6 @@ export class SequencerPublisher {
|
|
|
1017
1017
|
public async enqueueSlashingActions(
|
|
1018
1018
|
actions: ProposerSlashAction[],
|
|
1019
1019
|
slotNumber: SlotNumber,
|
|
1020
|
-
timestamp: bigint,
|
|
1021
1020
|
signerAddress: EthAddress,
|
|
1022
1021
|
signer: (msg: TypedDataDefinition) => Promise<`0x${string}`>,
|
|
1023
1022
|
): Promise<boolean> {
|
|
@@ -1038,7 +1037,6 @@ export class SequencerPublisher {
|
|
|
1038
1037
|
});
|
|
1039
1038
|
await this.enqueueCastSignalHelper(
|
|
1040
1039
|
slotNumber,
|
|
1041
|
-
timestamp,
|
|
1042
1040
|
'empire-slashing-signal',
|
|
1043
1041
|
action.payload,
|
|
1044
1042
|
this.slashingProposerContract,
|
|
@@ -1057,7 +1055,6 @@ export class SequencerPublisher {
|
|
|
1057
1055
|
(receipt: TransactionReceipt) =>
|
|
1058
1056
|
!!this.slashFactoryContract.tryExtractSlashPayloadCreatedEvent(receipt.logs),
|
|
1059
1057
|
slotNumber,
|
|
1060
|
-
timestamp,
|
|
1061
1058
|
);
|
|
1062
1059
|
break;
|
|
1063
1060
|
}
|
|
@@ -1075,7 +1072,6 @@ export class SequencerPublisher {
|
|
|
1075
1072
|
request,
|
|
1076
1073
|
(receipt: TransactionReceipt) => !!empireSlashingProposer.tryExtractPayloadSubmittedEvent(receipt.logs),
|
|
1077
1074
|
slotNumber,
|
|
1078
|
-
timestamp,
|
|
1079
1075
|
);
|
|
1080
1076
|
break;
|
|
1081
1077
|
}
|
|
@@ -1099,7 +1095,6 @@ export class SequencerPublisher {
|
|
|
1099
1095
|
request,
|
|
1100
1096
|
(receipt: TransactionReceipt) => !!tallySlashingProposer.tryExtractVoteCastEvent(receipt.logs),
|
|
1101
1097
|
slotNumber,
|
|
1102
|
-
timestamp,
|
|
1103
1098
|
);
|
|
1104
1099
|
break;
|
|
1105
1100
|
}
|
|
@@ -1121,7 +1116,6 @@ export class SequencerPublisher {
|
|
|
1121
1116
|
request,
|
|
1122
1117
|
(receipt: TransactionReceipt) => !!tallySlashingProposer.tryExtractRoundExecutedEvent(receipt.logs),
|
|
1123
1118
|
slotNumber,
|
|
1124
|
-
timestamp,
|
|
1125
1119
|
);
|
|
1126
1120
|
break;
|
|
1127
1121
|
}
|
|
@@ -1157,15 +1151,13 @@ export class SequencerPublisher {
|
|
|
1157
1151
|
feeAssetPriceModifier: checkpoint.feeAssetPriceModifier,
|
|
1158
1152
|
};
|
|
1159
1153
|
|
|
1160
|
-
let ts: bigint;
|
|
1161
|
-
|
|
1162
1154
|
try {
|
|
1163
1155
|
// @note This will make sure that we are passing the checks for our header ASSUMING that the data is also made available
|
|
1164
1156
|
// This means that we can avoid the simulation issues in later checks.
|
|
1165
1157
|
// By simulation issue, I mean the fact that the block.timestamp is equal to the last block, not the next, which
|
|
1166
1158
|
// make time consistency checks break.
|
|
1167
1159
|
// TODO(palla): Check whether we're validating twice, once here and once within addProposeTx, since we call simulateProposeTx in both places.
|
|
1168
|
-
|
|
1160
|
+
await this.validateCheckpointForSubmission(
|
|
1169
1161
|
checkpoint,
|
|
1170
1162
|
attestationsAndSigners,
|
|
1171
1163
|
attestationsAndSignersSignature,
|
|
@@ -1181,7 +1173,7 @@ export class SequencerPublisher {
|
|
|
1181
1173
|
}
|
|
1182
1174
|
|
|
1183
1175
|
this.log.verbose(`Enqueuing checkpoint propose transaction`, { ...checkpoint.toCheckpointInfo(), ...opts });
|
|
1184
|
-
await this.addProposeTx(checkpoint, proposeTxArgs, opts
|
|
1176
|
+
await this.addProposeTx(checkpoint, proposeTxArgs, opts);
|
|
1185
1177
|
}
|
|
1186
1178
|
|
|
1187
1179
|
public enqueueInvalidateCheckpoint(
|
|
@@ -1224,8 +1216,8 @@ export class SequencerPublisher {
|
|
|
1224
1216
|
request: L1TxRequest,
|
|
1225
1217
|
checkSuccess: (receipt: TransactionReceipt) => boolean | undefined,
|
|
1226
1218
|
slotNumber: SlotNumber,
|
|
1227
|
-
timestamp: bigint,
|
|
1228
1219
|
) {
|
|
1220
|
+
const timestamp = this.getSimulationTimestamp(slotNumber);
|
|
1229
1221
|
const logData = { slotNumber, timestamp, gasLimit: undefined as bigint | undefined };
|
|
1230
1222
|
if (this.lastActions[action] && this.lastActions[action] === slotNumber) {
|
|
1231
1223
|
this.log.debug(`Skipping duplicate action ${action} for slot ${slotNumber}`);
|
|
@@ -1241,8 +1233,9 @@ export class SequencerPublisher {
|
|
|
1241
1233
|
|
|
1242
1234
|
let gasUsed: bigint;
|
|
1243
1235
|
const simulateAbi = mergeAbis([request.abi ?? [], ErrorsAbi]);
|
|
1236
|
+
|
|
1244
1237
|
try {
|
|
1245
|
-
({ gasUsed } = await this.l1TxUtils.simulate(request, { time: timestamp }, [], simulateAbi));
|
|
1238
|
+
({ gasUsed } = await this.l1TxUtils.simulate(request, { time: timestamp }, [], simulateAbi));
|
|
1246
1239
|
this.log.verbose(`Simulation for ${action} succeeded`, { ...logData, request, gasUsed });
|
|
1247
1240
|
} catch (err) {
|
|
1248
1241
|
const viemError = formatViemError(err, simulateAbi);
|
|
@@ -1311,7 +1304,6 @@ export class SequencerPublisher {
|
|
|
1311
1304
|
|
|
1312
1305
|
private async prepareProposeTx(
|
|
1313
1306
|
encodedData: L1ProcessArgs,
|
|
1314
|
-
timestamp: bigint,
|
|
1315
1307
|
options: { forcePendingCheckpointNumber?: CheckpointNumber },
|
|
1316
1308
|
) {
|
|
1317
1309
|
const kzg = Blob.getViemKzgInstance();
|
|
@@ -1384,7 +1376,7 @@ export class SequencerPublisher {
|
|
|
1384
1376
|
blobInput,
|
|
1385
1377
|
] as const;
|
|
1386
1378
|
|
|
1387
|
-
const { rollupData, simulationResult } = await this.simulateProposeTx(args,
|
|
1379
|
+
const { rollupData, simulationResult } = await this.simulateProposeTx(args, options);
|
|
1388
1380
|
|
|
1389
1381
|
return { args, blobEvaluationGas, rollupData, simulationResult };
|
|
1390
1382
|
}
|
|
@@ -1392,7 +1384,6 @@ export class SequencerPublisher {
|
|
|
1392
1384
|
/**
|
|
1393
1385
|
* Simulates the propose tx with eth_simulateV1
|
|
1394
1386
|
* @param args - The propose tx args
|
|
1395
|
-
* @param timestamp - The timestamp to simulate proposal at
|
|
1396
1387
|
* @returns The simulation result
|
|
1397
1388
|
*/
|
|
1398
1389
|
private async simulateProposeTx(
|
|
@@ -1409,7 +1400,6 @@ export class SequencerPublisher {
|
|
|
1409
1400
|
ViemSignature,
|
|
1410
1401
|
`0x${string}`,
|
|
1411
1402
|
],
|
|
1412
|
-
timestamp: bigint,
|
|
1413
1403
|
options: { forcePendingCheckpointNumber?: CheckpointNumber },
|
|
1414
1404
|
) {
|
|
1415
1405
|
const rollupData = encodeFunctionData({
|
|
@@ -1444,6 +1434,7 @@ export class SequencerPublisher {
|
|
|
1444
1434
|
}
|
|
1445
1435
|
|
|
1446
1436
|
const l1BlockNumber = await this.l1TxUtils.getBlockNumber();
|
|
1437
|
+
const simTs = this.getSimulationTimestamp(SlotNumber.fromBigInt(args[0].header.slotNumber));
|
|
1447
1438
|
|
|
1448
1439
|
const simulationResult = await this.l1TxUtils
|
|
1449
1440
|
.simulate(
|
|
@@ -1454,8 +1445,7 @@ export class SequencerPublisher {
|
|
|
1454
1445
|
...(this.proposerAddressForSimulation && { from: this.proposerAddressForSimulation.toString() }),
|
|
1455
1446
|
},
|
|
1456
1447
|
{
|
|
1457
|
-
|
|
1458
|
-
time: timestamp + 1n,
|
|
1448
|
+
time: simTs,
|
|
1459
1449
|
// @note reth should have a 30m gas limit per block but throws errors that this tx is beyond limit so we increase here
|
|
1460
1450
|
gasLimit: MAX_L1_TX_LIMIT * 2n,
|
|
1461
1451
|
},
|
|
@@ -1477,7 +1467,7 @@ export class SequencerPublisher {
|
|
|
1477
1467
|
logs: [],
|
|
1478
1468
|
};
|
|
1479
1469
|
}
|
|
1480
|
-
this.log.error(`Failed to simulate propose tx`, viemError);
|
|
1470
|
+
this.log.error(`Failed to simulate propose tx`, viemError, { simulationTimestamp: simTs });
|
|
1481
1471
|
this.backupFailedTx({
|
|
1482
1472
|
id: keccak256(rollupData),
|
|
1483
1473
|
failureType: 'simulation',
|
|
@@ -1500,16 +1490,11 @@ export class SequencerPublisher {
|
|
|
1500
1490
|
checkpoint: Checkpoint,
|
|
1501
1491
|
encodedData: L1ProcessArgs,
|
|
1502
1492
|
opts: { txTimeoutAt?: Date; forcePendingCheckpointNumber?: CheckpointNumber } = {},
|
|
1503
|
-
timestamp: bigint,
|
|
1504
1493
|
): Promise<void> {
|
|
1505
1494
|
const slot = checkpoint.header.slotNumber;
|
|
1506
1495
|
const timer = new Timer();
|
|
1507
1496
|
const kzg = Blob.getViemKzgInstance();
|
|
1508
|
-
const { rollupData, simulationResult, blobEvaluationGas } = await this.prepareProposeTx(
|
|
1509
|
-
encodedData,
|
|
1510
|
-
timestamp,
|
|
1511
|
-
opts,
|
|
1512
|
-
);
|
|
1497
|
+
const { rollupData, simulationResult, blobEvaluationGas } = await this.prepareProposeTx(encodedData, opts);
|
|
1513
1498
|
const startBlock = await this.l1TxUtils.getBlockNumber();
|
|
1514
1499
|
const gasLimit = this.l1TxUtils.bumpGasLimit(
|
|
1515
1500
|
BigInt(Math.ceil((Number(simulationResult.gasUsed) * 64) / 63)) +
|
|
@@ -1585,4 +1570,17 @@ export class SequencerPublisher {
|
|
|
1585
1570
|
},
|
|
1586
1571
|
});
|
|
1587
1572
|
}
|
|
1573
|
+
|
|
1574
|
+
/** Returns the timestamp of the last L1 slot within a given L2 slot. Used as the simulation timestamp
|
|
1575
|
+
* for eth_simulateV1 calls, since it's guaranteed to be greater than any L1 block produced during the slot. */
|
|
1576
|
+
private getSimulationTimestamp(slot: SlotNumber): bigint {
|
|
1577
|
+
const l1Constants = this.epochCache.getL1Constants();
|
|
1578
|
+
return getLastL1SlotTimestampForL2Slot(slot, l1Constants);
|
|
1579
|
+
}
|
|
1580
|
+
|
|
1581
|
+
/** Returns the timestamp of the next L1 slot boundary after now. */
|
|
1582
|
+
private getNextL1SlotTimestamp(): bigint {
|
|
1583
|
+
const l1Constants = this.epochCache.getL1Constants();
|
|
1584
|
+
return getNextL1SlotTimestamp(this.dateProvider.nowInSeconds(), l1Constants);
|
|
1585
|
+
}
|
|
1588
1586
|
}
|
|
@@ -960,7 +960,7 @@ export class CheckpointProposalJob implements Traceable {
|
|
|
960
960
|
* would never receive its own block without this explicit sync.
|
|
961
961
|
*/
|
|
962
962
|
private async syncProposedBlockToArchiver(block: L2Block): Promise<void> {
|
|
963
|
-
if (this.config.skipPushProposedBlocksToArchiver
|
|
963
|
+
if (this.config.skipPushProposedBlocksToArchiver) {
|
|
964
964
|
this.log.warn(`Skipping push of proposed block ${block.number} to archiver`, {
|
|
965
965
|
blockNumber: block.number,
|
|
966
966
|
slot: block.header.globalVariables.slotNumber,
|
|
@@ -2,7 +2,6 @@ import type { SlotNumber } from '@aztec/foundation/branded-types';
|
|
|
2
2
|
import type { EthAddress } from '@aztec/foundation/eth-address';
|
|
3
3
|
import type { Logger } from '@aztec/foundation/log';
|
|
4
4
|
import type { SlasherClientInterface } from '@aztec/slasher';
|
|
5
|
-
import { getTimestampForSlot } from '@aztec/stdlib/epoch-helpers';
|
|
6
5
|
import type { ResolvedSequencerConfig } from '@aztec/stdlib/interfaces/server';
|
|
7
6
|
import type { ValidatorClient } from '@aztec/validator-client';
|
|
8
7
|
import { DutyAlreadySignedError } from '@aztec/validator-ha-signer/errors';
|
|
@@ -18,7 +17,6 @@ import type { SequencerRollupConstants } from './types.js';
|
|
|
18
17
|
* Handles governance and slashing voting for a given slot.
|
|
19
18
|
*/
|
|
20
19
|
export class CheckpointVoter {
|
|
21
|
-
private slotTimestamp: bigint;
|
|
22
20
|
private governanceSigner: (msg: TypedDataDefinition) => Promise<`0x${string}`>;
|
|
23
21
|
private slashingSigner: (msg: TypedDataDefinition) => Promise<`0x${string}`>;
|
|
24
22
|
|
|
@@ -33,8 +31,6 @@ export class CheckpointVoter {
|
|
|
33
31
|
private readonly metrics: SequencerMetrics,
|
|
34
32
|
private readonly log: Logger,
|
|
35
33
|
) {
|
|
36
|
-
this.slotTimestamp = getTimestampForSlot(this.slot, this.l1Constants);
|
|
37
|
-
|
|
38
34
|
// Create separate signers with appropriate duty contexts for governance and slashing votes
|
|
39
35
|
// These use HA protection to ensure only one node signs per slot/duty
|
|
40
36
|
const governanceContext: SigningContext = { slot: this.slot, dutyType: DutyType.GOVERNANCE_VOTE };
|
|
@@ -77,7 +73,6 @@ export class CheckpointVoter {
|
|
|
77
73
|
return await this.publisher.enqueueGovernanceCastSignal(
|
|
78
74
|
governanceProposerPayload,
|
|
79
75
|
this.slot,
|
|
80
|
-
this.slotTimestamp,
|
|
81
76
|
this.attestorAddress,
|
|
82
77
|
this.governanceSigner,
|
|
83
78
|
);
|
|
@@ -108,13 +103,7 @@ export class CheckpointVoter {
|
|
|
108
103
|
|
|
109
104
|
this.metrics.recordSlashingAttempt(actions.length);
|
|
110
105
|
|
|
111
|
-
return await this.publisher.enqueueSlashingActions(
|
|
112
|
-
actions,
|
|
113
|
-
this.slot,
|
|
114
|
-
this.slotTimestamp,
|
|
115
|
-
this.attestorAddress,
|
|
116
|
-
this.slashingSigner,
|
|
117
|
-
);
|
|
106
|
+
return await this.publisher.enqueueSlashingActions(actions, this.slot, this.attestorAddress, this.slashingSigner);
|
|
118
107
|
} catch (err) {
|
|
119
108
|
if (err instanceof DutyAlreadySignedError) {
|
|
120
109
|
this.log.info(`Slashing vote already signed by another node`, {
|
|
@@ -147,7 +147,7 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
|
|
|
147
147
|
public async stop(): Promise<void> {
|
|
148
148
|
this.log.info(`Stopping sequencer`);
|
|
149
149
|
this.setState(SequencerState.STOPPING, undefined, { force: true });
|
|
150
|
-
this.publisherFactory.
|
|
150
|
+
await this.publisherFactory.stopAll();
|
|
151
151
|
await this.runningPromise?.stop();
|
|
152
152
|
this.setState(SequencerState.STOPPED, undefined, { force: true });
|
|
153
153
|
this.log.info('Stopped sequencer');
|
|
@@ -501,8 +501,8 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
|
|
|
501
501
|
*/
|
|
502
502
|
protected async checkSync(args: { ts: bigint; slot: SlotNumber }): Promise<SequencerSyncCheckResult | undefined> {
|
|
503
503
|
// Check that the archiver has fully synced the L2 slot before the one we want to propose in.
|
|
504
|
-
//
|
|
505
|
-
//
|
|
504
|
+
// The archiver reports sync progress via L1 block timestamps and synced checkpoint slots.
|
|
505
|
+
// See getSyncedL2SlotNumber for how missed L1 blocks are handled.
|
|
506
506
|
const syncedL2Slot = await this.l2BlockSource.getSyncedL2SlotNumber();
|
|
507
507
|
const { slot } = args;
|
|
508
508
|
if (syncedL2Slot === undefined || syncedL2Slot + 1 < slot) {
|