@aztec/sequencer-client 0.71.0 → 0.72.1
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/publisher/l1-publisher.d.ts +1 -1
- package/dest/publisher/l1-publisher.d.ts.map +1 -1
- package/dest/publisher/l1-publisher.js +93 -27
- package/dest/sequencer/allowed.js +3 -3
- package/dest/sequencer/sequencer.d.ts +2 -3
- package/dest/sequencer/sequencer.d.ts.map +1 -1
- package/dest/sequencer/sequencer.js +22 -23
- package/dest/tx_validator/gas_validator.d.ts +1 -1
- package/dest/tx_validator/gas_validator.d.ts.map +1 -1
- package/dest/tx_validator/gas_validator.js +8 -4
- package/package.json +21 -21
- package/src/publisher/l1-publisher.ts +104 -24
- package/src/sequencer/allowed.ts +2 -2
- package/src/sequencer/sequencer.ts +23 -33
- package/src/tx_validator/gas_validator.ts +15 -3
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
import { type FeeRecipient, type RootRollupPublicInputs } from '@aztec/circuits.js/rollup';
|
|
20
20
|
import {
|
|
21
21
|
type EthereumChain,
|
|
22
|
+
FormattedViemError,
|
|
22
23
|
type GasPrice,
|
|
23
24
|
type L1ContractsConfig,
|
|
24
25
|
L1TxUtils,
|
|
@@ -416,7 +417,7 @@ export class L1Publisher {
|
|
|
416
417
|
digest: Buffer.alloc(32),
|
|
417
418
|
signatures: [],
|
|
418
419
|
},
|
|
419
|
-
): Promise<
|
|
420
|
+
): Promise<bigint> {
|
|
420
421
|
const ts = BigInt((await this.publicClient.getBlock()).timestamp + this.ethereumSlotDuration);
|
|
421
422
|
|
|
422
423
|
const formattedSignatures = attestationData.signatures.map(attest => attest.toViemSignature());
|
|
@@ -441,6 +442,7 @@ export class L1Publisher {
|
|
|
441
442
|
}
|
|
442
443
|
throw error;
|
|
443
444
|
}
|
|
445
|
+
return ts;
|
|
444
446
|
}
|
|
445
447
|
|
|
446
448
|
public async getCurrentEpochCommittee(): Promise<EthAddress[]> {
|
|
@@ -545,8 +547,8 @@ export class L1Publisher {
|
|
|
545
547
|
account: this.account,
|
|
546
548
|
});
|
|
547
549
|
} catch (err) {
|
|
548
|
-
const
|
|
549
|
-
logger.error(`Failed to vote`,
|
|
550
|
+
const { message, metaMessages } = formatViemError(err);
|
|
551
|
+
logger.error(`Failed to vote`, message, { metaMessages });
|
|
550
552
|
this.myLastVotes[voteType] = cachedMyLastVote;
|
|
551
553
|
return false;
|
|
552
554
|
}
|
|
@@ -609,15 +611,15 @@ export class L1Publisher {
|
|
|
609
611
|
// This means that we can avoid the simulation issues in later checks.
|
|
610
612
|
// By simulation issue, I mean the fact that the block.timestamp is equal to the last block, not the next, which
|
|
611
613
|
// make time consistency checks break.
|
|
612
|
-
await this.validateBlockForSubmission(block.header, {
|
|
614
|
+
const ts = await this.validateBlockForSubmission(block.header, {
|
|
613
615
|
digest: digest.toBuffer(),
|
|
614
616
|
signatures: attestations ?? [],
|
|
615
617
|
});
|
|
616
618
|
|
|
617
619
|
this.log.debug(`Submitting propose transaction`);
|
|
618
620
|
const result = proofQuote
|
|
619
|
-
? await this.sendProposeAndClaimTx(proposeTxArgs, proofQuote, opts)
|
|
620
|
-
: await this.sendProposeTx(proposeTxArgs, opts);
|
|
621
|
+
? await this.sendProposeAndClaimTx(proposeTxArgs, proofQuote, opts, ts)
|
|
622
|
+
: await this.sendProposeTx(proposeTxArgs, opts, ts);
|
|
621
623
|
|
|
622
624
|
if (!result?.receipt) {
|
|
623
625
|
this.log.info(`Failed to publish block ${block.number} to L1`, ctx);
|
|
@@ -689,9 +691,17 @@ export class L1Publisher {
|
|
|
689
691
|
}),
|
|
690
692
|
});
|
|
691
693
|
} catch (err) {
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
694
|
+
if (err instanceof FormattedViemError) {
|
|
695
|
+
const { message, metaMessages } = err;
|
|
696
|
+
this.log.error(`Failed to claim epoch proof right`, message, {
|
|
697
|
+
metaMessages,
|
|
698
|
+
proofQuote: proofQuote.toInspect(),
|
|
699
|
+
});
|
|
700
|
+
} else {
|
|
701
|
+
this.log.error(`Failed to claim epoch proof right`, err, {
|
|
702
|
+
proofQuote: proofQuote.toInspect(),
|
|
703
|
+
});
|
|
704
|
+
}
|
|
695
705
|
return false;
|
|
696
706
|
}
|
|
697
707
|
|
|
@@ -961,6 +971,8 @@ export class L1Publisher {
|
|
|
961
971
|
|
|
962
972
|
private async prepareProposeTx(encodedData: L1ProcessArgs) {
|
|
963
973
|
const kzg = Blob.getViemKzgInstance();
|
|
974
|
+
const blobInput = Blob.getEthBlobEvaluationInputs(encodedData.blobs);
|
|
975
|
+
this.log.debug('Validating blob input', { blobInput });
|
|
964
976
|
const blobEvaluationGas = await this.l1TxUtils.estimateGas(
|
|
965
977
|
this.account,
|
|
966
978
|
{
|
|
@@ -968,7 +980,7 @@ export class L1Publisher {
|
|
|
968
980
|
data: encodeFunctionData({
|
|
969
981
|
abi: this.rollupContract.abi,
|
|
970
982
|
functionName: 'validateBlobs',
|
|
971
|
-
args: [
|
|
983
|
+
args: [blobInput],
|
|
972
984
|
}),
|
|
973
985
|
},
|
|
974
986
|
{},
|
|
@@ -978,12 +990,6 @@ export class L1Publisher {
|
|
|
978
990
|
},
|
|
979
991
|
);
|
|
980
992
|
|
|
981
|
-
// @note We perform this guesstimate instead of the usual `gasEstimate` since
|
|
982
|
-
// viem will use the current state to simulate against, which means that
|
|
983
|
-
// we will fail estimation in the case where we are simulating for the
|
|
984
|
-
// first ethereum block within our slot (as current time is not in the
|
|
985
|
-
// slot yet).
|
|
986
|
-
const gasGuesstimate = blobEvaluationGas + L1Publisher.PROPOSE_GAS_GUESS;
|
|
987
993
|
const attestations = encodedData.attestations
|
|
988
994
|
? encodedData.attestations.map(attest => attest.toViemSignature())
|
|
989
995
|
: [];
|
|
@@ -1003,10 +1009,10 @@ export class L1Publisher {
|
|
|
1003
1009
|
attestations,
|
|
1004
1010
|
// TODO(#9101): Extract blobs from beacon chain => calldata will only contain what's needed to verify blob and body input can be removed
|
|
1005
1011
|
`0x${encodedData.body.toString('hex')}`,
|
|
1006
|
-
|
|
1012
|
+
blobInput,
|
|
1007
1013
|
] as const;
|
|
1008
1014
|
|
|
1009
|
-
return { args,
|
|
1015
|
+
return { args, blobEvaluationGas };
|
|
1010
1016
|
}
|
|
1011
1017
|
|
|
1012
1018
|
private getSubmitEpochProofArgs(args: {
|
|
@@ -1042,26 +1048,58 @@ export class L1Publisher {
|
|
|
1042
1048
|
private async sendProposeTx(
|
|
1043
1049
|
encodedData: L1ProcessArgs,
|
|
1044
1050
|
opts: { txTimeoutAt?: Date } = {},
|
|
1051
|
+
timestamp: bigint,
|
|
1045
1052
|
): Promise<L1ProcessReturnType | undefined> {
|
|
1046
1053
|
if (this.interrupted) {
|
|
1047
1054
|
return undefined;
|
|
1048
1055
|
}
|
|
1049
1056
|
try {
|
|
1050
1057
|
const kzg = Blob.getViemKzgInstance();
|
|
1051
|
-
const { args,
|
|
1058
|
+
const { args, blobEvaluationGas } = await this.prepareProposeTx(encodedData);
|
|
1052
1059
|
const data = encodeFunctionData({
|
|
1053
1060
|
abi: this.rollupContract.abi,
|
|
1054
1061
|
functionName: 'propose',
|
|
1055
1062
|
args,
|
|
1056
1063
|
});
|
|
1064
|
+
|
|
1065
|
+
const simulationResult = await this.l1TxUtils.simulateGasUsed(
|
|
1066
|
+
{
|
|
1067
|
+
to: this.rollupContract.address,
|
|
1068
|
+
data,
|
|
1069
|
+
gas: L1Publisher.PROPOSE_GAS_GUESS,
|
|
1070
|
+
},
|
|
1071
|
+
{
|
|
1072
|
+
// @note we add 1n to the timestamp because geth implementation doesn't like simulation timestamp to be equal to the current block timestamp
|
|
1073
|
+
time: timestamp + 1n,
|
|
1074
|
+
// @note reth should have a 30m gas limit per block but throws errors that this tx is beyond limit
|
|
1075
|
+
gasLimit: L1Publisher.PROPOSE_GAS_GUESS * 2n,
|
|
1076
|
+
},
|
|
1077
|
+
[
|
|
1078
|
+
{
|
|
1079
|
+
address: this.rollupContract.address,
|
|
1080
|
+
// @note we override checkBlob to false since blobs are not part simulate()
|
|
1081
|
+
stateDiff: [
|
|
1082
|
+
{
|
|
1083
|
+
slot: toHex(9n, true),
|
|
1084
|
+
value: toHex(0n, true),
|
|
1085
|
+
},
|
|
1086
|
+
],
|
|
1087
|
+
},
|
|
1088
|
+
],
|
|
1089
|
+
{
|
|
1090
|
+
// @note fallback gas estimate to use if the node doesn't support simulation API
|
|
1091
|
+
fallbackGasEstimate: L1Publisher.PROPOSE_GAS_GUESS,
|
|
1092
|
+
},
|
|
1093
|
+
);
|
|
1094
|
+
|
|
1057
1095
|
const result = await this.l1TxUtils.sendAndMonitorTransaction(
|
|
1058
1096
|
{
|
|
1059
1097
|
to: this.rollupContract.address,
|
|
1060
1098
|
data,
|
|
1061
1099
|
},
|
|
1062
1100
|
{
|
|
1063
|
-
fixedGas: gas,
|
|
1064
1101
|
...opts,
|
|
1102
|
+
gasLimit: this.l1TxUtils.bumpGasLimit(simulationResult + blobEvaluationGas),
|
|
1065
1103
|
},
|
|
1066
1104
|
{
|
|
1067
1105
|
blobs: encodedData.blobs.map(b => b.dataWithZeros),
|
|
@@ -1076,7 +1114,12 @@ export class L1Publisher {
|
|
|
1076
1114
|
data,
|
|
1077
1115
|
};
|
|
1078
1116
|
} catch (err) {
|
|
1079
|
-
|
|
1117
|
+
if (err instanceof FormattedViemError) {
|
|
1118
|
+
const { message, metaMessages } = err;
|
|
1119
|
+
this.log.error(`Rollup publish failed.`, message, { metaMessages });
|
|
1120
|
+
} else {
|
|
1121
|
+
this.log.error(`Rollup publish failed.`, err);
|
|
1122
|
+
}
|
|
1080
1123
|
return undefined;
|
|
1081
1124
|
}
|
|
1082
1125
|
}
|
|
@@ -1085,26 +1128,58 @@ export class L1Publisher {
|
|
|
1085
1128
|
encodedData: L1ProcessArgs,
|
|
1086
1129
|
quote: EpochProofQuote,
|
|
1087
1130
|
opts: { txTimeoutAt?: Date } = {},
|
|
1131
|
+
timestamp: bigint,
|
|
1088
1132
|
): Promise<L1ProcessReturnType | undefined> {
|
|
1089
1133
|
if (this.interrupted) {
|
|
1090
1134
|
return undefined;
|
|
1091
1135
|
}
|
|
1136
|
+
|
|
1092
1137
|
try {
|
|
1093
1138
|
const kzg = Blob.getViemKzgInstance();
|
|
1094
|
-
const { args,
|
|
1139
|
+
const { args, blobEvaluationGas } = await this.prepareProposeTx(encodedData);
|
|
1095
1140
|
const data = encodeFunctionData({
|
|
1096
1141
|
abi: this.rollupContract.abi,
|
|
1097
1142
|
functionName: 'proposeAndClaim',
|
|
1098
1143
|
args: [...args, quote.toViemArgs()],
|
|
1099
1144
|
});
|
|
1145
|
+
|
|
1146
|
+
const simulationResult = await this.l1TxUtils.simulateGasUsed(
|
|
1147
|
+
{
|
|
1148
|
+
to: this.rollupContract.address,
|
|
1149
|
+
data,
|
|
1150
|
+
gas: L1Publisher.PROPOSE_AND_CLAIM_GAS_GUESS,
|
|
1151
|
+
},
|
|
1152
|
+
{
|
|
1153
|
+
// @note we add 1n to the timestamp because geth implementation doesn't like simulation timestamp to be equal to the current block timestamp
|
|
1154
|
+
time: timestamp + 1n,
|
|
1155
|
+
// @note reth should have a 30m gas limit per block but throws errors that this tx is beyond limit
|
|
1156
|
+
gasLimit: L1Publisher.PROPOSE_AND_CLAIM_GAS_GUESS * 2n,
|
|
1157
|
+
},
|
|
1158
|
+
[
|
|
1159
|
+
{
|
|
1160
|
+
address: this.rollupContract.address,
|
|
1161
|
+
// @note we override checkBlob to false since blobs are not part simulate()
|
|
1162
|
+
stateDiff: [
|
|
1163
|
+
{
|
|
1164
|
+
slot: toHex(9n, true),
|
|
1165
|
+
value: toHex(0n, true),
|
|
1166
|
+
},
|
|
1167
|
+
],
|
|
1168
|
+
},
|
|
1169
|
+
],
|
|
1170
|
+
{
|
|
1171
|
+
// @note fallback gas estimate to use if the node doesn't support simulation API
|
|
1172
|
+
fallbackGasEstimate: L1Publisher.PROPOSE_AND_CLAIM_GAS_GUESS,
|
|
1173
|
+
},
|
|
1174
|
+
);
|
|
1100
1175
|
const result = await this.l1TxUtils.sendAndMonitorTransaction(
|
|
1101
1176
|
{
|
|
1102
1177
|
to: this.rollupContract.address,
|
|
1103
1178
|
data,
|
|
1104
1179
|
},
|
|
1105
1180
|
{
|
|
1106
|
-
fixedGas: gas,
|
|
1107
1181
|
...opts,
|
|
1182
|
+
gasLimit: this.l1TxUtils.bumpGasLimit(simulationResult + blobEvaluationGas),
|
|
1108
1183
|
},
|
|
1109
1184
|
{
|
|
1110
1185
|
blobs: encodedData.blobs.map(b => b.dataWithZeros),
|
|
@@ -1120,7 +1195,12 @@ export class L1Publisher {
|
|
|
1120
1195
|
data,
|
|
1121
1196
|
};
|
|
1122
1197
|
} catch (err) {
|
|
1123
|
-
|
|
1198
|
+
if (err instanceof FormattedViemError) {
|
|
1199
|
+
const { message, metaMessages } = err;
|
|
1200
|
+
this.log.error(`Rollup publish failed.`, message, { metaMessages });
|
|
1201
|
+
} else {
|
|
1202
|
+
this.log.error(`Rollup publish failed.`, err);
|
|
1203
|
+
}
|
|
1124
1204
|
return undefined;
|
|
1125
1205
|
}
|
|
1126
1206
|
}
|
package/src/sequencer/allowed.ts
CHANGED
|
@@ -17,13 +17,13 @@ export function getDefaultAllowedSetupFunctions(): AllowedElement[] {
|
|
|
17
17
|
{
|
|
18
18
|
address: ProtocolContractAddress.FeeJuice,
|
|
19
19
|
// We can't restrict the selector because public functions get routed via dispatch.
|
|
20
|
-
// selector: FunctionSelector.fromSignature('_increase_public_balance((Field),Field)'),
|
|
20
|
+
// selector: FunctionSelector.fromSignature('_increase_public_balance((Field),(Field,Field))'),
|
|
21
21
|
},
|
|
22
22
|
// needed for private transfers via FPC
|
|
23
23
|
{
|
|
24
24
|
classId: getContractClassFromArtifact(TokenContractArtifact).id,
|
|
25
25
|
// We can't restrict the selector because public functions get routed via dispatch.
|
|
26
|
-
// selector: FunctionSelector.fromSignature('_increase_public_balance((Field),Field)'),
|
|
26
|
+
// selector: FunctionSelector.fromSignature('_increase_public_balance((Field),(Field,Field))'),
|
|
27
27
|
},
|
|
28
28
|
{
|
|
29
29
|
classId: getContractClassFromArtifact(FPCContract.artifact).id,
|
|
@@ -236,20 +236,15 @@ export class Sequencer {
|
|
|
236
236
|
this.setState(SequencerState.PROPOSER_CHECK, 0n);
|
|
237
237
|
|
|
238
238
|
const chainTip = await this.l2BlockSource.getBlock(-1);
|
|
239
|
-
const historicalHeader = chainTip?.header;
|
|
240
239
|
|
|
241
|
-
const newBlockNumber =
|
|
242
|
-
(historicalHeader === undefined
|
|
243
|
-
? await this.l2BlockSource.getBlockNumber()
|
|
244
|
-
: Number(historicalHeader.globalVariables.blockNumber.toBigInt())) + 1;
|
|
240
|
+
const newBlockNumber = (chainTip?.header.globalVariables.blockNumber.toNumber() ?? 0) + 1;
|
|
245
241
|
|
|
246
242
|
// If we cannot find a tip archive, assume genesis.
|
|
247
|
-
const chainTipArchive =
|
|
248
|
-
chainTip == undefined ? new Fr(GENESIS_ARCHIVE_ROOT).toBuffer() : chainTip?.archive.root.toBuffer();
|
|
243
|
+
const chainTipArchive = chainTip?.archive.root ?? new Fr(GENESIS_ARCHIVE_ROOT);
|
|
249
244
|
|
|
250
245
|
let slot: bigint;
|
|
251
246
|
try {
|
|
252
|
-
slot = await this.mayProposeBlock(chainTipArchive, BigInt(newBlockNumber));
|
|
247
|
+
slot = await this.mayProposeBlock(chainTipArchive.toBuffer(), BigInt(newBlockNumber));
|
|
253
248
|
} catch (err) {
|
|
254
249
|
this.log.debug(`Cannot propose for block ${newBlockNumber}`);
|
|
255
250
|
return;
|
|
@@ -278,7 +273,7 @@ export class Sequencer {
|
|
|
278
273
|
|
|
279
274
|
this.setState(SequencerState.INITIALIZING_PROPOSAL, slot);
|
|
280
275
|
this.log.verbose(`Preparing proposal for block ${newBlockNumber} at slot ${slot}`, {
|
|
281
|
-
chainTipArchive
|
|
276
|
+
chainTipArchive,
|
|
282
277
|
blockNumber: newBlockNumber,
|
|
283
278
|
slot,
|
|
284
279
|
});
|
|
@@ -289,7 +284,7 @@ export class Sequencer {
|
|
|
289
284
|
|
|
290
285
|
// If I created a "partial" header here that should make our job much easier.
|
|
291
286
|
const proposalHeader = new BlockHeader(
|
|
292
|
-
new AppendOnlyTreeSnapshot(
|
|
287
|
+
new AppendOnlyTreeSnapshot(chainTipArchive, 1),
|
|
293
288
|
ContentCommitment.empty(),
|
|
294
289
|
StateReference.empty(),
|
|
295
290
|
newGlobalVariables,
|
|
@@ -302,9 +297,12 @@ export class Sequencer {
|
|
|
302
297
|
// @note It is very important that the following function will FAIL and not just return early
|
|
303
298
|
// if it have made any state changes. If not, we won't rollback the state, and you will
|
|
304
299
|
// be in for a world of pain.
|
|
305
|
-
await this.buildBlockAndAttemptToPublish(pendingTxs, proposalHeader
|
|
300
|
+
await this.buildBlockAndAttemptToPublish(pendingTxs, proposalHeader);
|
|
306
301
|
} catch (err) {
|
|
307
302
|
this.log.error(`Error assembling block`, err, { blockNumber: newBlockNumber, slot });
|
|
303
|
+
|
|
304
|
+
// If the block failed to build, we might still want to claim the proving rights
|
|
305
|
+
await this.claimEpochProofRightIfAvailable(slot);
|
|
308
306
|
}
|
|
309
307
|
this.setState(SequencerState.IDLE, 0n);
|
|
310
308
|
}
|
|
@@ -378,14 +376,13 @@ export class Sequencer {
|
|
|
378
376
|
protected async buildBlock(
|
|
379
377
|
pendingTxs: Iterable<Tx>,
|
|
380
378
|
newGlobalVariables: GlobalVariables,
|
|
381
|
-
historicalHeader?: BlockHeader,
|
|
382
379
|
opts: { validateOnly?: boolean } = {},
|
|
383
380
|
) {
|
|
384
|
-
const blockNumber = newGlobalVariables.blockNumber.
|
|
381
|
+
const blockNumber = newGlobalVariables.blockNumber.toNumber();
|
|
385
382
|
const slot = newGlobalVariables.slotNumber.toBigInt();
|
|
386
383
|
|
|
387
384
|
this.log.debug(`Requesting L1 to L2 messages from contract for block ${blockNumber}`);
|
|
388
|
-
const l1ToL2Messages = await this.l1ToL2MessageSource.getL1ToL2Messages(blockNumber);
|
|
385
|
+
const l1ToL2Messages = await this.l1ToL2MessageSource.getL1ToL2Messages(BigInt(blockNumber));
|
|
389
386
|
const msgCount = l1ToL2Messages.length;
|
|
390
387
|
|
|
391
388
|
this.log.verbose(`Building block ${blockNumber} for slot ${slot}`, {
|
|
@@ -396,23 +393,21 @@ export class Sequencer {
|
|
|
396
393
|
});
|
|
397
394
|
|
|
398
395
|
// Sync to the previous block at least
|
|
399
|
-
await this.worldState.syncImmediate(
|
|
400
|
-
this.log.debug(`Synced to previous block ${
|
|
396
|
+
await this.worldState.syncImmediate(blockNumber - 1);
|
|
397
|
+
this.log.debug(`Synced to previous block ${blockNumber - 1}`);
|
|
401
398
|
|
|
402
399
|
// NB: separating the dbs because both should update the state
|
|
403
400
|
const publicProcessorFork = await this.worldState.fork();
|
|
404
401
|
const orchestratorFork = await this.worldState.fork();
|
|
405
402
|
|
|
403
|
+
const previousBlockHeader =
|
|
404
|
+
(await this.l2BlockSource.getBlock(blockNumber - 1))?.header ?? orchestratorFork.getInitialHeader();
|
|
405
|
+
|
|
406
406
|
try {
|
|
407
|
-
const processor = this.publicProcessorFactory.create(
|
|
408
|
-
publicProcessorFork,
|
|
409
|
-
historicalHeader,
|
|
410
|
-
newGlobalVariables,
|
|
411
|
-
true,
|
|
412
|
-
);
|
|
407
|
+
const processor = this.publicProcessorFactory.create(publicProcessorFork, newGlobalVariables, true);
|
|
413
408
|
const blockBuildingTimer = new Timer();
|
|
414
409
|
const blockBuilder = this.blockBuilderFactory.create(orchestratorFork);
|
|
415
|
-
await blockBuilder.startNewBlock(newGlobalVariables, l1ToL2Messages);
|
|
410
|
+
await blockBuilder.startNewBlock(newGlobalVariables, l1ToL2Messages, previousBlockHeader);
|
|
416
411
|
|
|
417
412
|
// Deadline for processing depends on whether we're proposing a block
|
|
418
413
|
const secondsIntoSlot = this.getSecondsIntoSlot(slot);
|
|
@@ -517,16 +512,11 @@ export class Sequencer {
|
|
|
517
512
|
*
|
|
518
513
|
* @param pendingTxs - Iterable of pending transactions to construct the block from
|
|
519
514
|
* @param proposalHeader - The partial header constructed for the proposal
|
|
520
|
-
* @param historicalHeader - The historical header of the parent
|
|
521
515
|
*/
|
|
522
|
-
@trackSpan('Sequencer.buildBlockAndAttemptToPublish', (_validTxs, proposalHeader
|
|
516
|
+
@trackSpan('Sequencer.buildBlockAndAttemptToPublish', (_validTxs, proposalHeader) => ({
|
|
523
517
|
[Attributes.BLOCK_NUMBER]: proposalHeader.globalVariables.blockNumber.toNumber(),
|
|
524
518
|
}))
|
|
525
|
-
private async buildBlockAndAttemptToPublish(
|
|
526
|
-
pendingTxs: Iterable<Tx>,
|
|
527
|
-
proposalHeader: BlockHeader,
|
|
528
|
-
historicalHeader: BlockHeader | undefined,
|
|
529
|
-
): Promise<void> {
|
|
519
|
+
private async buildBlockAndAttemptToPublish(pendingTxs: Iterable<Tx>, proposalHeader: BlockHeader): Promise<void> {
|
|
530
520
|
await this.publisher.validateBlockForSubmission(proposalHeader);
|
|
531
521
|
|
|
532
522
|
const newGlobalVariables = proposalHeader.globalVariables;
|
|
@@ -541,7 +531,7 @@ export class Sequencer {
|
|
|
541
531
|
const proofQuotePromise = this.createProofClaimForPreviousEpoch(slot);
|
|
542
532
|
|
|
543
533
|
try {
|
|
544
|
-
const buildBlockRes = await this.buildBlock(pendingTxs, newGlobalVariables
|
|
534
|
+
const buildBlockRes = await this.buildBlock(pendingTxs, newGlobalVariables);
|
|
545
535
|
const { publicGas, block, publicProcessorDuration, numTxs, numMsgs, blockBuildingTimer } = buildBlockRes;
|
|
546
536
|
|
|
547
537
|
// TODO(@PhilWindle) We should probably periodically check for things like another
|
|
@@ -640,8 +630,8 @@ export class Sequencer {
|
|
|
640
630
|
this.log.debug('Creating block proposal for validators');
|
|
641
631
|
const proposal = await this.validatorClient.createBlockProposal(block.header, block.archive.root, txHashes);
|
|
642
632
|
if (!proposal) {
|
|
643
|
-
|
|
644
|
-
|
|
633
|
+
const msg = `Failed to create block proposal`;
|
|
634
|
+
throw new Error(msg);
|
|
645
635
|
}
|
|
646
636
|
|
|
647
637
|
this.log.debug('Broadcasting block proposal to validators');
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type Tx, TxExecutionPhase, type TxValidationResult, type TxValidator } from '@aztec/circuit-types';
|
|
2
|
-
import { type AztecAddress,
|
|
2
|
+
import { type AztecAddress, Fr, FunctionSelector, type GasFees } from '@aztec/circuits.js';
|
|
3
|
+
import { U128 } from '@aztec/foundation/abi';
|
|
3
4
|
import { createLogger } from '@aztec/foundation/log';
|
|
4
5
|
import { computeFeePayerBalanceStorageSlot, getExecutionRequestsByPhase } from '@aztec/simulator/server';
|
|
5
6
|
|
|
@@ -34,6 +35,12 @@ export class GasTxValidator implements TxValidator<Tx> {
|
|
|
34
35
|
return this.#validateTxFee(tx);
|
|
35
36
|
}
|
|
36
37
|
|
|
38
|
+
/**
|
|
39
|
+
* Check whether the tx's max fees are valid for the current block, and skip if not.
|
|
40
|
+
* We skip instead of invalidating since the tx may become elligible later.
|
|
41
|
+
* Note that circuits check max fees even if fee payer is unset, so we
|
|
42
|
+
* keep this validation even if the tx does not pay fees.
|
|
43
|
+
*/
|
|
37
44
|
#shouldSkip(tx: Tx): boolean {
|
|
38
45
|
const gasSettings = tx.data.constants.txContext.gasSettings;
|
|
39
46
|
|
|
@@ -78,12 +85,17 @@ export class GasTxValidator implements TxValidator<Tx> {
|
|
|
78
85
|
fn.callContext.msgSender.equals(this.#feeJuiceAddress) &&
|
|
79
86
|
fn.args.length > 2 &&
|
|
80
87
|
// Public functions get routed through the dispatch function, whose first argument is the target function selector.
|
|
81
|
-
fn.args[0].equals(
|
|
88
|
+
fn.args[0].equals(
|
|
89
|
+
FunctionSelector.fromSignature('_increase_public_balance((Field),(Field,Field))').toField(),
|
|
90
|
+
) &&
|
|
82
91
|
fn.args[1].equals(feePayer.toField()) &&
|
|
83
92
|
!fn.callContext.isStaticCall,
|
|
84
93
|
);
|
|
85
94
|
|
|
86
|
-
|
|
95
|
+
// `amount` in the claim function call arguments occupies 2 fields as it is represented as U128.
|
|
96
|
+
const balance = claimFunctionCall
|
|
97
|
+
? initialBalance.add(new Fr(U128.fromFields(claimFunctionCall.args.slice(2, 4)).toInteger()))
|
|
98
|
+
: initialBalance;
|
|
87
99
|
if (balance.lt(feeLimit)) {
|
|
88
100
|
this.#log.warn(`Rejecting transaction due to not enough fee payer balance`, {
|
|
89
101
|
feePayer,
|