@aztec/sequencer-client 0.70.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.
@@ -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,
@@ -36,8 +37,7 @@ import { type Tuple, serializeToBuffer } from '@aztec/foundation/serialize';
36
37
  import { InterruptibleSleep } from '@aztec/foundation/sleep';
37
38
  import { Timer } from '@aztec/foundation/timer';
38
39
  import { EmpireBaseAbi, RollupAbi, SlasherAbi } from '@aztec/l1-artifacts';
39
- import { type TelemetryClient } from '@aztec/telemetry-client';
40
- import { NoopTelemetryClient } from '@aztec/telemetry-client/noop';
40
+ import { type TelemetryClient, getTelemetryClient } from '@aztec/telemetry-client';
41
41
 
42
42
  import pick from 'lodash.pick';
43
43
  import {
@@ -209,7 +209,7 @@ export class L1Publisher {
209
209
  this.sleepTimeMs = config?.l1PublishRetryIntervalMS ?? 60_000;
210
210
  this.ethereumSlotDuration = BigInt(config.ethereumSlotDuration);
211
211
 
212
- const telemetry = deps.telemetry ?? new NoopTelemetryClient();
212
+ const telemetry = deps.telemetry ?? getTelemetryClient();
213
213
  this.blobSinkClient = deps.blobSinkClient ?? createBlobSinkClient(config.blobSinkUrl);
214
214
 
215
215
  this.metrics = new L1PublisherMetrics(telemetry, 'L1Publisher');
@@ -417,7 +417,7 @@ export class L1Publisher {
417
417
  digest: Buffer.alloc(32),
418
418
  signatures: [],
419
419
  },
420
- ): Promise<void> {
420
+ ): Promise<bigint> {
421
421
  const ts = BigInt((await this.publicClient.getBlock()).timestamp + this.ethereumSlotDuration);
422
422
 
423
423
  const formattedSignatures = attestationData.signatures.map(attest => attest.toViemSignature());
@@ -442,6 +442,7 @@ export class L1Publisher {
442
442
  }
443
443
  throw error;
444
444
  }
445
+ return ts;
445
446
  }
446
447
 
447
448
  public async getCurrentEpochCommittee(): Promise<EthAddress[]> {
@@ -546,8 +547,8 @@ export class L1Publisher {
546
547
  account: this.account,
547
548
  });
548
549
  } catch (err) {
549
- const msg = formatViemError(err);
550
- logger.error(`Failed to vote`, msg);
550
+ const { message, metaMessages } = formatViemError(err);
551
+ logger.error(`Failed to vote`, message, { metaMessages });
551
552
  this.myLastVotes[voteType] = cachedMyLastVote;
552
553
  return false;
553
554
  }
@@ -610,15 +611,15 @@ export class L1Publisher {
610
611
  // This means that we can avoid the simulation issues in later checks.
611
612
  // By simulation issue, I mean the fact that the block.timestamp is equal to the last block, not the next, which
612
613
  // make time consistency checks break.
613
- await this.validateBlockForSubmission(block.header, {
614
+ const ts = await this.validateBlockForSubmission(block.header, {
614
615
  digest: digest.toBuffer(),
615
616
  signatures: attestations ?? [],
616
617
  });
617
618
 
618
619
  this.log.debug(`Submitting propose transaction`);
619
620
  const result = proofQuote
620
- ? await this.sendProposeAndClaimTx(proposeTxArgs, proofQuote, opts)
621
- : await this.sendProposeTx(proposeTxArgs, opts);
621
+ ? await this.sendProposeAndClaimTx(proposeTxArgs, proofQuote, opts, ts)
622
+ : await this.sendProposeTx(proposeTxArgs, opts, ts);
622
623
 
623
624
  if (!result?.receipt) {
624
625
  this.log.info(`Failed to publish block ${block.number} to L1`, ctx);
@@ -690,9 +691,17 @@ export class L1Publisher {
690
691
  }),
691
692
  });
692
693
  } catch (err) {
693
- this.log.error(`Failed to claim epoch proof right`, err, {
694
- proofQuote: proofQuote.toInspect(),
695
- });
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
+ }
696
705
  return false;
697
706
  }
698
707
 
@@ -962,6 +971,8 @@ export class L1Publisher {
962
971
 
963
972
  private async prepareProposeTx(encodedData: L1ProcessArgs) {
964
973
  const kzg = Blob.getViemKzgInstance();
974
+ const blobInput = Blob.getEthBlobEvaluationInputs(encodedData.blobs);
975
+ this.log.debug('Validating blob input', { blobInput });
965
976
  const blobEvaluationGas = await this.l1TxUtils.estimateGas(
966
977
  this.account,
967
978
  {
@@ -969,7 +980,7 @@ export class L1Publisher {
969
980
  data: encodeFunctionData({
970
981
  abi: this.rollupContract.abi,
971
982
  functionName: 'validateBlobs',
972
- args: [Blob.getEthBlobEvaluationInputs(encodedData.blobs)],
983
+ args: [blobInput],
973
984
  }),
974
985
  },
975
986
  {},
@@ -979,12 +990,6 @@ export class L1Publisher {
979
990
  },
980
991
  );
981
992
 
982
- // @note We perform this guesstimate instead of the usual `gasEstimate` since
983
- // viem will use the current state to simulate against, which means that
984
- // we will fail estimation in the case where we are simulating for the
985
- // first ethereum block within our slot (as current time is not in the
986
- // slot yet).
987
- const gasGuesstimate = blobEvaluationGas + L1Publisher.PROPOSE_GAS_GUESS;
988
993
  const attestations = encodedData.attestations
989
994
  ? encodedData.attestations.map(attest => attest.toViemSignature())
990
995
  : [];
@@ -1004,10 +1009,10 @@ export class L1Publisher {
1004
1009
  attestations,
1005
1010
  // TODO(#9101): Extract blobs from beacon chain => calldata will only contain what's needed to verify blob and body input can be removed
1006
1011
  `0x${encodedData.body.toString('hex')}`,
1007
- Blob.getEthBlobEvaluationInputs(encodedData.blobs),
1012
+ blobInput,
1008
1013
  ] as const;
1009
1014
 
1010
- return { args, gas: gasGuesstimate };
1015
+ return { args, blobEvaluationGas };
1011
1016
  }
1012
1017
 
1013
1018
  private getSubmitEpochProofArgs(args: {
@@ -1043,26 +1048,58 @@ export class L1Publisher {
1043
1048
  private async sendProposeTx(
1044
1049
  encodedData: L1ProcessArgs,
1045
1050
  opts: { txTimeoutAt?: Date } = {},
1051
+ timestamp: bigint,
1046
1052
  ): Promise<L1ProcessReturnType | undefined> {
1047
1053
  if (this.interrupted) {
1048
1054
  return undefined;
1049
1055
  }
1050
1056
  try {
1051
1057
  const kzg = Blob.getViemKzgInstance();
1052
- const { args, gas } = await this.prepareProposeTx(encodedData);
1058
+ const { args, blobEvaluationGas } = await this.prepareProposeTx(encodedData);
1053
1059
  const data = encodeFunctionData({
1054
1060
  abi: this.rollupContract.abi,
1055
1061
  functionName: 'propose',
1056
1062
  args,
1057
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
+
1058
1095
  const result = await this.l1TxUtils.sendAndMonitorTransaction(
1059
1096
  {
1060
1097
  to: this.rollupContract.address,
1061
1098
  data,
1062
1099
  },
1063
1100
  {
1064
- fixedGas: gas,
1065
1101
  ...opts,
1102
+ gasLimit: this.l1TxUtils.bumpGasLimit(simulationResult + blobEvaluationGas),
1066
1103
  },
1067
1104
  {
1068
1105
  blobs: encodedData.blobs.map(b => b.dataWithZeros),
@@ -1077,7 +1114,12 @@ export class L1Publisher {
1077
1114
  data,
1078
1115
  };
1079
1116
  } catch (err) {
1080
- this.log.error(`Rollup publish failed.`, err);
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
+ }
1081
1123
  return undefined;
1082
1124
  }
1083
1125
  }
@@ -1086,26 +1128,58 @@ export class L1Publisher {
1086
1128
  encodedData: L1ProcessArgs,
1087
1129
  quote: EpochProofQuote,
1088
1130
  opts: { txTimeoutAt?: Date } = {},
1131
+ timestamp: bigint,
1089
1132
  ): Promise<L1ProcessReturnType | undefined> {
1090
1133
  if (this.interrupted) {
1091
1134
  return undefined;
1092
1135
  }
1136
+
1093
1137
  try {
1094
1138
  const kzg = Blob.getViemKzgInstance();
1095
- const { args, gas } = await this.prepareProposeTx(encodedData);
1139
+ const { args, blobEvaluationGas } = await this.prepareProposeTx(encodedData);
1096
1140
  const data = encodeFunctionData({
1097
1141
  abi: this.rollupContract.abi,
1098
1142
  functionName: 'proposeAndClaim',
1099
1143
  args: [...args, quote.toViemArgs()],
1100
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
+ );
1101
1175
  const result = await this.l1TxUtils.sendAndMonitorTransaction(
1102
1176
  {
1103
1177
  to: this.rollupContract.address,
1104
1178
  data,
1105
1179
  },
1106
1180
  {
1107
- fixedGas: gas,
1108
1181
  ...opts,
1182
+ gasLimit: this.l1TxUtils.bumpGasLimit(simulationResult + blobEvaluationGas),
1109
1183
  },
1110
1184
  {
1111
1185
  blobs: encodedData.blobs.map(b => b.dataWithZeros),
@@ -1121,7 +1195,12 @@ export class L1Publisher {
1121
1195
  data,
1122
1196
  };
1123
1197
  } catch (err) {
1124
- this.log.error(`Rollup publish failed.`, err);
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
+ }
1125
1204
  return undefined;
1126
1205
  }
1127
1206
  }
@@ -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,