@aztec/sequencer-client 0.86.0 → 0.87.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.
Files changed (35) hide show
  1. package/dest/config.d.ts.map +1 -1
  2. package/dest/config.js +5 -0
  3. package/dest/global_variable_builder/global_builder.d.ts +6 -3
  4. package/dest/global_variable_builder/global_builder.d.ts.map +1 -1
  5. package/dest/global_variable_builder/global_builder.js +15 -2
  6. package/dest/publisher/sequencer-publisher-metrics.js +2 -2
  7. package/dest/publisher/sequencer-publisher.d.ts +4 -6
  8. package/dest/publisher/sequencer-publisher.d.ts.map +1 -1
  9. package/dest/publisher/sequencer-publisher.js +15 -12
  10. package/dest/sequencer/metrics.d.ts +1 -0
  11. package/dest/sequencer/metrics.d.ts.map +1 -1
  12. package/dest/sequencer/metrics.js +3 -0
  13. package/dest/sequencer/sequencer.d.ts +23 -7
  14. package/dest/sequencer/sequencer.d.ts.map +1 -1
  15. package/dest/sequencer/sequencer.js +47 -22
  16. package/dest/sequencer/timetable.d.ts +4 -4
  17. package/dest/sequencer/timetable.d.ts.map +1 -1
  18. package/dest/sequencer/timetable.js +14 -4
  19. package/dest/slasher/factory.d.ts.map +1 -1
  20. package/dest/tx_validator/nullifier_cache.d.ts +0 -2
  21. package/dest/tx_validator/nullifier_cache.d.ts.map +1 -1
  22. package/dest/tx_validator/tx_validator_factory.d.ts.map +1 -1
  23. package/dest/tx_validator/tx_validator_factory.js +16 -3
  24. package/package.json +30 -30
  25. package/src/client/sequencer-client.ts +1 -1
  26. package/src/config.ts +5 -0
  27. package/src/global_variable_builder/global_builder.ts +17 -5
  28. package/src/publisher/sequencer-publisher-metrics.ts +2 -2
  29. package/src/publisher/sequencer-publisher.ts +19 -14
  30. package/src/sequencer/metrics.ts +4 -0
  31. package/src/sequencer/sequencer.ts +60 -35
  32. package/src/sequencer/timetable.ts +16 -4
  33. package/src/slasher/slasher_client.ts +1 -1
  34. package/src/test/index.ts +4 -4
  35. package/src/tx_validator/tx_validator_factory.ts +16 -3
@@ -1 +1 @@
1
- {"version":3,"file":"tx_validator_factory.d.ts","sourceRoot":"","sources":["../../src/tx_validator/tx_validator_factory.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AACjE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,KAAK,EACV,cAAc,EACd,6BAA6B,EAC7B,wBAAwB,EACzB,MAAM,iCAAiC,CAAC;AAEzC,OAAO,EAAE,eAAe,EAAE,KAAK,EAAE,EAAE,KAAK,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAE9E,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD,wBAAgB,8BAA8B,CAC5C,EAAE,EAAE,wBAAwB,EAC5B,kBAAkB,EAAE,kBAAkB,EACtC,QAAQ,EAAE,6BAA6B,GAAG,SAAS,EACnD,EACE,WAAW,EACX,SAAS,EACT,aAAa,EACb,cAAc,EACd,OAAO,EACP,kBAAkB,GACnB,EAAE;IACD,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,cAAc,EAAE,CAAC;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B,GACA,WAAW,CAAC,EAAE,CAAC,CAkBjB;AAED,wBAAgB,+BAA+B,CAC7C,EAAE,EAAE,wBAAwB,EAC5B,kBAAkB,EAAE,kBAAkB,EACtC,eAAe,EAAE,eAAe,EAChC,cAAc,EAAE,cAAc,EAAE,GAC/B;IACD,mBAAmB,EAAE,WAAW,CAAC,EAAE,CAAC,CAAC;IACrC,cAAc,EAAE,cAAc,CAAC;CAChC,CAgBA"}
1
+ {"version":3,"file":"tx_validator_factory.d.ts","sourceRoot":"","sources":["../../src/tx_validator/tx_validator_factory.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AACjE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,KAAK,EACV,cAAc,EACd,6BAA6B,EAC7B,wBAAwB,EACzB,MAAM,iCAAiC,CAAC;AAEzC,OAAO,EAAE,eAAe,EAAE,KAAK,EAAE,EAAE,KAAK,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAE9E,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD,wBAAgB,8BAA8B,CAC5C,EAAE,EAAE,wBAAwB,EAC5B,kBAAkB,EAAE,kBAAkB,EACtC,QAAQ,EAAE,6BAA6B,GAAG,SAAS,EACnD,EACE,WAAW,EACX,SAAS,EACT,aAAa,EACb,cAAc,EACd,OAAO,EACP,kBAAkB,GACnB,EAAE;IACD,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,cAAc,EAAE,CAAC;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B,GACA,WAAW,CAAC,EAAE,CAAC,CAwBjB;AAED,wBAAgB,+BAA+B,CAC7C,EAAE,EAAE,wBAAwB,EAC5B,kBAAkB,EAAE,kBAAkB,EACtC,eAAe,EAAE,eAAe,EAChC,cAAc,EAAE,cAAc,EAAE,GAC/B;IACD,mBAAmB,EAAE,WAAW,CAAC,EAAE,CAAC,CAAC;IACrC,cAAc,EAAE,cAAc,CAAC;CAChC,CAgBA"}
@@ -1,12 +1,19 @@
1
1
  import { Fr } from '@aztec/foundation/fields';
2
+ import { getVKTreeRoot } from '@aztec/noir-protocol-circuits-types/vk-tree';
2
3
  import { AggregateTxValidator, ArchiveCache, BlockHeaderTxValidator, DataTxValidator, DoubleSpendTxValidator, GasTxValidator, MetadataTxValidator, PhasesTxValidator, TxProofValidator } from '@aztec/p2p';
3
- import { ProtocolContractAddress } from '@aztec/protocol-contracts';
4
+ import { ProtocolContractAddress, protocolContractTreeRoot } from '@aztec/protocol-contracts';
4
5
  import { DatabasePublicStateSource } from '@aztec/stdlib/trees';
5
6
  import { NullifierCache } from './nullifier_cache.js';
6
7
  export function createValidatorForAcceptingTxs(db, contractDataSource, verifier, { blockNumber, l1ChainId, rollupVersion, setupAllowList, gasFees, skipFeeEnforcement }) {
7
8
  const validators = [
8
9
  new DataTxValidator(),
9
- new MetadataTxValidator(new Fr(l1ChainId), new Fr(rollupVersion), new Fr(blockNumber)),
10
+ new MetadataTxValidator({
11
+ l1ChainId: new Fr(l1ChainId),
12
+ rollupVersion: new Fr(rollupVersion),
13
+ blockNumber: new Fr(blockNumber),
14
+ protocolContractTreeRoot,
15
+ vkTreeRoot: getVKTreeRoot()
16
+ }),
10
17
  new DoubleSpendTxValidator(new NullifierCache(db)),
11
18
  new PhasesTxValidator(contractDataSource, setupAllowList, blockNumber),
12
19
  new BlockHeaderTxValidator(new ArchiveCache(db))
@@ -30,5 +37,11 @@ export function createValidatorForBlockBuilding(db, contractDataSource, globalVa
30
37
  }
31
38
  function preprocessValidator(nullifierCache, archiveCache, publicStateSource, contractDataSource, globalVariables, setupAllowList) {
32
39
  // We don't include the TxProofValidator nor the DataTxValidator here because they are already checked by the time we get to block building.
33
- return new AggregateTxValidator(new MetadataTxValidator(globalVariables.chainId, globalVariables.version, globalVariables.blockNumber), new DoubleSpendTxValidator(nullifierCache), new PhasesTxValidator(contractDataSource, setupAllowList, globalVariables.blockNumber.toNumber()), new GasTxValidator(publicStateSource, ProtocolContractAddress.FeeJuice, globalVariables.gasFees), new BlockHeaderTxValidator(archiveCache));
40
+ return new AggregateTxValidator(new MetadataTxValidator({
41
+ l1ChainId: globalVariables.chainId,
42
+ rollupVersion: globalVariables.version,
43
+ blockNumber: globalVariables.blockNumber,
44
+ protocolContractTreeRoot,
45
+ vkTreeRoot: getVKTreeRoot()
46
+ }), new DoubleSpendTxValidator(nullifierCache), new PhasesTxValidator(contractDataSource, setupAllowList, globalVariables.blockNumber.toNumber()), new GasTxValidator(publicStateSource, ProtocolContractAddress.FeeJuice, globalVariables.gasFees), new BlockHeaderTxValidator(archiveCache));
34
47
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aztec/sequencer-client",
3
- "version": "0.86.0",
3
+ "version": "0.87.0",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": "./dest/index.js",
@@ -26,49 +26,49 @@
26
26
  "test:integration:run": "NODE_NO_WARNINGS=1 node --experimental-vm-modules $(yarn bin jest) --no-cache --config jest.integration.config.json"
27
27
  },
28
28
  "dependencies": {
29
- "@aztec/aztec.js": "0.86.0",
30
- "@aztec/bb-prover": "0.86.0",
31
- "@aztec/blob-lib": "0.86.0",
32
- "@aztec/blob-sink": "0.86.0",
33
- "@aztec/constants": "0.86.0",
34
- "@aztec/epoch-cache": "0.86.0",
35
- "@aztec/ethereum": "0.86.0",
36
- "@aztec/foundation": "0.86.0",
37
- "@aztec/l1-artifacts": "0.86.0",
38
- "@aztec/merkle-tree": "0.86.0",
39
- "@aztec/noir-acvm_js": "0.86.0",
40
- "@aztec/noir-contracts.js": "0.86.0",
41
- "@aztec/noir-protocol-circuits-types": "0.86.0",
42
- "@aztec/noir-types": "0.86.0",
43
- "@aztec/p2p": "0.86.0",
44
- "@aztec/protocol-contracts": "0.86.0",
45
- "@aztec/prover-client": "0.86.0",
46
- "@aztec/simulator": "0.86.0",
47
- "@aztec/stdlib": "0.86.0",
48
- "@aztec/telemetry-client": "0.86.0",
49
- "@aztec/validator-client": "0.86.0",
50
- "@aztec/world-state": "0.86.0",
29
+ "@aztec/aztec.js": "0.87.0",
30
+ "@aztec/bb-prover": "0.87.0",
31
+ "@aztec/blob-lib": "0.87.0",
32
+ "@aztec/blob-sink": "0.87.0",
33
+ "@aztec/constants": "0.87.0",
34
+ "@aztec/epoch-cache": "0.87.0",
35
+ "@aztec/ethereum": "0.87.0",
36
+ "@aztec/foundation": "0.87.0",
37
+ "@aztec/l1-artifacts": "0.87.0",
38
+ "@aztec/merkle-tree": "0.87.0",
39
+ "@aztec/noir-acvm_js": "0.87.0",
40
+ "@aztec/noir-contracts.js": "0.87.0",
41
+ "@aztec/noir-protocol-circuits-types": "0.87.0",
42
+ "@aztec/noir-types": "0.87.0",
43
+ "@aztec/p2p": "0.87.0",
44
+ "@aztec/protocol-contracts": "0.87.0",
45
+ "@aztec/prover-client": "0.87.0",
46
+ "@aztec/simulator": "0.87.0",
47
+ "@aztec/stdlib": "0.87.0",
48
+ "@aztec/telemetry-client": "0.87.0",
49
+ "@aztec/validator-client": "0.87.0",
50
+ "@aztec/world-state": "0.87.0",
51
51
  "lodash.chunk": "^4.2.0",
52
52
  "lodash.pick": "^4.4.0",
53
53
  "tslib": "^2.4.0",
54
54
  "viem": "2.23.7"
55
55
  },
56
56
  "devDependencies": {
57
- "@aztec/archiver": "0.86.0",
58
- "@aztec/kv-store": "0.86.0",
57
+ "@aztec/archiver": "0.87.0",
58
+ "@aztec/kv-store": "0.87.0",
59
59
  "@jest/globals": "^29.5.0",
60
60
  "@types/jest": "^29.5.0",
61
61
  "@types/lodash.chunk": "^4.2.7",
62
62
  "@types/lodash.pick": "^4.4.7",
63
- "@types/node": "^18.7.23",
63
+ "@types/node": "^22.15.17",
64
64
  "concurrently": "^7.6.0",
65
- "eslint": "^8.37.0",
65
+ "eslint": "^9.26.0",
66
66
  "express": "^4.21.1",
67
67
  "jest": "^29.5.0",
68
68
  "jest-mock-extended": "^3.0.3",
69
- "prettier": "^2.8.7",
69
+ "prettier": "^3.5.3",
70
70
  "ts-node": "^10.9.1",
71
- "typescript": "^5.0.4"
71
+ "typescript": "^5.3.3"
72
72
  },
73
73
  "files": [
74
74
  "dest",
@@ -77,7 +77,7 @@
77
77
  ],
78
78
  "types": "./dest/index.d.ts",
79
79
  "engines": {
80
- "node": ">=18"
80
+ "node": ">=20.10"
81
81
  },
82
82
  "jest": {
83
83
  "extensionsToTreatAsEsm": [
@@ -157,7 +157,7 @@ export class SequencerClient {
157
157
  // make it with a propagation time into slot equal to 4s. However, we prefer being conservative.
158
158
  // See https://www.blocknative.com/blog/anatomy-of-a-slot#7 for more info.
159
159
  const maxL1TxInclusionTimeIntoSlot =
160
- config.maxL1TxInclusionTimeIntoSlot ?? isAnvilTestChain(config.l1ChainId) ? ethereumSlotDuration : 0;
160
+ (config.maxL1TxInclusionTimeIntoSlot ?? isAnvilTestChain(config.l1ChainId)) ? ethereumSlotDuration : 0;
161
161
 
162
162
  const l1Constants = {
163
163
  l1GenesisTime,
package/src/config.ts CHANGED
@@ -58,6 +58,11 @@ export const sequencerConfigMappings: ConfigMappingsType<SequencerConfig> = {
58
58
  description: 'The minimum number of txs to include in a block.',
59
59
  ...numberConfigHelper(1),
60
60
  },
61
+ publishTxsWithProposals: {
62
+ env: 'SEQ_PUBLISH_TXS_WITH_PROPOSALS',
63
+ description: 'Whether to publish txs with proposals.',
64
+ ...booleanConfigHelper(false),
65
+ },
61
66
  maxL2BlockGas: {
62
67
  env: 'SEQ_MAX_L2_BLOCK_GAS',
63
68
  description: 'The maximum L2 block gas.',
@@ -21,9 +21,12 @@ import { createPublicClient, fallback, http } from 'viem';
21
21
  export class GlobalVariableBuilder implements GlobalVariableBuilderInterface {
22
22
  private log = createLogger('sequencer:global_variable_builder');
23
23
 
24
- private rollupContract: RollupContract;
25
- private publicClient: ViemPublicClient;
26
- private ethereumSlotDuration: number;
24
+ private readonly rollupContract: RollupContract;
25
+ private readonly publicClient: ViemPublicClient;
26
+ private readonly ethereumSlotDuration: number;
27
+
28
+ private chainId?: Fr;
29
+ private version?: Fr;
27
30
 
28
31
  constructor(config: L1ReaderConfig & Pick<L1ContractsConfig, 'ethereumSlotDuration'>) {
29
32
  const { l1RpcUrls, l1ChainId: chainId, l1Contracts } = config;
@@ -58,6 +61,16 @@ export class GlobalVariableBuilder implements GlobalVariableBuilderInterface {
58
61
  return new GasFees(Fr.ZERO, new Fr(await this.rollupContract.getManaBaseFeeAt(timestamp, true)));
59
62
  }
60
63
 
64
+ public async getGlobalConstantVariables(): Promise<Pick<GlobalVariables, 'chainId' | 'version'>> {
65
+ if (!this.chainId) {
66
+ this.chainId = new Fr(this.publicClient.chain.id);
67
+ }
68
+ if (!this.version) {
69
+ this.version = new Fr(await this.rollupContract.getVersion());
70
+ }
71
+ return { chainId: this.chainId, version: this.version };
72
+ }
73
+
61
74
  /**
62
75
  * Simple builder of global variables that use the minimum time possible.
63
76
  * @param blockNumber - The block number to build global variables for.
@@ -72,8 +85,7 @@ export class GlobalVariableBuilder implements GlobalVariableBuilderInterface {
72
85
  feeRecipient: AztecAddress,
73
86
  slotNumber?: bigint,
74
87
  ): Promise<GlobalVariables> {
75
- const version = new Fr(await this.rollupContract.getVersion());
76
- const chainId = new Fr(this.publicClient.chain.id);
88
+ const { chainId, version } = await this.getGlobalConstantVariables();
77
89
 
78
90
  if (slotNumber === undefined) {
79
91
  const ts = BigInt((await this.publicClient.getBlock()).timestamp + BigInt(this.ethereumSlotDuration));
@@ -181,7 +181,7 @@ export class SequencerPublisherMetrics {
181
181
 
182
182
  try {
183
183
  this.gasPrice.record(parseInt(formatEther(stats.gasPrice, 'gwei'), 10));
184
- } catch (e) {
184
+ } catch {
185
185
  // ignore
186
186
  }
187
187
 
@@ -191,7 +191,7 @@ export class SequencerPublisherMetrics {
191
191
 
192
192
  try {
193
193
  this.txTotalFee.record(parseFloat(formatEther(totalFee)));
194
- } catch (e) {
194
+ } catch {
195
195
  // ignore
196
196
  }
197
197
  }
@@ -18,7 +18,7 @@ import {
18
18
  formatViemError,
19
19
  } from '@aztec/ethereum';
20
20
  import type { L1TxUtilsWithBlobs } from '@aztec/ethereum/l1-tx-utils-with-blobs';
21
- import { toHex } from '@aztec/foundation/bigint-buffer';
21
+ import { toHex as toPaddedHex } from '@aztec/foundation/bigint-buffer';
22
22
  import { EthAddress } from '@aztec/foundation/eth-address';
23
23
  import type { Signature } from '@aztec/foundation/eth-signature';
24
24
  import { createLogger } from '@aztec/foundation/log';
@@ -26,11 +26,11 @@ import { Timer } from '@aztec/foundation/timer';
26
26
  import { ForwarderAbi, RollupAbi } from '@aztec/l1-artifacts';
27
27
  import { ConsensusPayload, SignatureDomainSeparator, getHashedSignaturePayload } from '@aztec/stdlib/p2p';
28
28
  import type { L1PublishBlockStats } from '@aztec/stdlib/stats';
29
- import { type BlockHeader, TxHash } from '@aztec/stdlib/tx';
29
+ import { type ProposedBlockHeader, TxHash } from '@aztec/stdlib/tx';
30
30
  import { type TelemetryClient, getTelemetryClient } from '@aztec/telemetry-client';
31
31
 
32
32
  import pick from 'lodash.pick';
33
- import { type TransactionReceipt, encodeFunctionData } from 'viem';
33
+ import { type TransactionReceipt, encodeFunctionData, toHex } from 'viem';
34
34
 
35
35
  import type { PublisherConfig, TxSenderConfig } from './config.js';
36
36
  import { SequencerPublisherMetrics } from './sequencer-publisher-metrics.js';
@@ -41,6 +41,8 @@ type L1ProcessArgs = {
41
41
  header: Buffer;
42
42
  /** A root of the archive tree after the L2 block is applied. */
43
43
  archive: Buffer;
44
+ /** State reference after the L2 block is applied. */
45
+ stateReference: Buffer;
44
46
  /** L2 block blobs containing all tx effects. */
45
47
  blobs: Blob[];
46
48
  /** L2 block tx hashes */
@@ -285,7 +287,7 @@ export class SequencerPublisher {
285
287
  *
286
288
  */
287
289
  public async validateBlockForSubmission(
288
- header: BlockHeader,
290
+ header: ProposedBlockHeader,
289
291
  attestationData: { digest: Buffer; signatures: Signature[] } = {
290
292
  digest: Buffer.alloc(32),
291
293
  signatures: [],
@@ -297,11 +299,11 @@ export class SequencerPublisher {
297
299
  const flags = { ignoreDA: true, ignoreSignatures: formattedSignatures.length == 0 };
298
300
 
299
301
  const args = [
300
- `0x${header.toBuffer().toString('hex')}`,
302
+ toHex(header.toBuffer()),
301
303
  formattedSignatures,
302
- `0x${attestationData.digest.toString('hex')}`,
304
+ toHex(attestationData.digest),
303
305
  ts,
304
- `0x${header.contentCommitment.blobsHash.toString('hex')}`,
306
+ toHex(header.contentCommitment.blobsHash),
305
307
  flags,
306
308
  ] as const;
307
309
 
@@ -407,14 +409,16 @@ export class SequencerPublisher {
407
409
  txHashes?: TxHash[],
408
410
  opts: { txTimeoutAt?: Date } = {},
409
411
  ): Promise<boolean> {
410
- const consensusPayload = new ConsensusPayload(block.header, block.archive.root, txHashes ?? []);
412
+ const proposedBlockHeader = block.header.toPropose();
411
413
 
414
+ const consensusPayload = ConsensusPayload.fromBlock(block);
412
415
  const digest = getHashedSignaturePayload(consensusPayload, SignatureDomainSeparator.blockAttestation);
413
416
 
414
417
  const blobs = await Blob.getBlobs(block.body.toBlobFields());
415
418
  const proposeTxArgs = {
416
- header: block.header.toBuffer(),
419
+ header: proposedBlockHeader.toBuffer(),
417
420
  archive: block.archive.root.toBuffer(),
421
+ stateReference: block.header.state.toBuffer(),
418
422
  body: block.body.toBuffer(),
419
423
  blobs,
420
424
  attestations,
@@ -425,7 +429,7 @@ export class SequencerPublisher {
425
429
  // This means that we can avoid the simulation issues in later checks.
426
430
  // By simulation issue, I mean the fact that the block.timestamp is equal to the last block, not the next, which
427
431
  // make time consistency checks break.
428
- const ts = await this.validateBlockForSubmission(block.header, {
432
+ const ts = await this.validateBlockForSubmission(proposedBlockHeader, {
429
433
  digest: digest.toBuffer(),
430
434
  signatures: attestations ?? [],
431
435
  });
@@ -488,8 +492,9 @@ export class SequencerPublisher {
488
492
  const txHashes = encodedData.txHashes ? encodedData.txHashes.map(txHash => txHash.toString()) : [];
489
493
  const args = [
490
494
  {
491
- header: `0x${encodedData.header.toString('hex')}`,
492
- archive: `0x${encodedData.archive.toString('hex')}`,
495
+ header: toHex(encodedData.header),
496
+ archive: toHex(encodedData.archive),
497
+ stateReference: toHex(encodedData.stateReference),
493
498
  oracleInput: {
494
499
  // We are currently not modifying these. See #9963
495
500
  feeAssetPriceModifier: 0n,
@@ -531,8 +536,8 @@ export class SequencerPublisher {
531
536
  // @note we override checkBlob to false since blobs are not part simulate()
532
537
  stateDiff: [
533
538
  {
534
- slot: toHex(RollupContract.checkBlobStorageSlot, true),
535
- value: toHex(0n, true),
539
+ slot: toPaddedHex(RollupContract.checkBlobStorageSlot, true),
540
+ value: toPaddedHex(0n, true),
536
541
  },
537
542
  ],
538
543
  },
@@ -136,6 +136,10 @@ export class SequencerMetrics {
136
136
  });
137
137
  }
138
138
 
139
+ public setCoinbase(coinbase: EthAddress) {
140
+ this.coinbase = coinbase;
141
+ }
142
+
139
143
  public start() {
140
144
  this.meter.addBatchObservableCallback(this.observe, [this.rewards]);
141
145
  }
@@ -23,17 +23,11 @@ import {
23
23
  type WorldStateSynchronizer,
24
24
  } from '@aztec/stdlib/interfaces/server';
25
25
  import type { L1ToL2MessageSource } from '@aztec/stdlib/messaging';
26
+ import type { BlockProposalOptions } from '@aztec/stdlib/p2p';
26
27
  import { pickFromSchema } from '@aztec/stdlib/schemas';
27
28
  import type { L2BlockBuiltStats } from '@aztec/stdlib/stats';
28
- import { AppendOnlyTreeSnapshot, MerkleTreeId } from '@aztec/stdlib/trees';
29
- import {
30
- BlockHeader,
31
- ContentCommitment,
32
- type GlobalVariables,
33
- StateReference,
34
- Tx,
35
- type TxHash,
36
- } from '@aztec/stdlib/tx';
29
+ import { MerkleTreeId } from '@aztec/stdlib/trees';
30
+ import { ContentCommitment, GlobalVariables, ProposedBlockHeader, Tx, type TxHash } from '@aztec/stdlib/tx';
37
31
  import {
38
32
  Attributes,
39
33
  L1Metrics,
@@ -109,7 +103,7 @@ export class Sequencer {
109
103
  this.metrics = new SequencerMetrics(
110
104
  telemetry,
111
105
  () => this.state,
112
- this._coinbase,
106
+ this.config.coinbase ?? this.publisher.getSenderAddress(),
113
107
  this.publisher.getRollupContract(),
114
108
  'Sequencer',
115
109
  );
@@ -120,7 +114,7 @@ export class Sequencer {
120
114
  );
121
115
 
122
116
  // Register the block builder with the validator client for re-execution
123
- this.validatorClient?.registerBlockBuilder(this.buildBlock.bind(this));
117
+ this.validatorClient?.registerBlockBuilder(this.buildBlockFromProposal.bind(this));
124
118
 
125
119
  // Register the slasher on the publisher to fetch slashing payloads
126
120
  this.publisher.registerSlashPayloadGetter(this.slasherClient.getSlashPayload.bind(this.slasherClient));
@@ -161,6 +155,7 @@ export class Sequencer {
161
155
  }
162
156
  if (config.coinbase) {
163
157
  this._coinbase = config.coinbase;
158
+ this.metrics.setCoinbase(this._coinbase);
164
159
  }
165
160
  if (config.feeRecipient) {
166
161
  this._feeRecipient = config.feeRecipient;
@@ -311,14 +306,13 @@ export class Sequencer {
311
306
  });
312
307
 
313
308
  // If I created a "partial" header here that should make our job much easier.
314
- const proposalHeader = new BlockHeader(
315
- new AppendOnlyTreeSnapshot(chainTipArchive, 1),
316
- ContentCommitment.empty(),
317
- StateReference.empty(),
318
- newGlobalVariables,
319
- Fr.ZERO,
320
- Fr.ZERO,
321
- );
309
+ const proposalHeader = ProposedBlockHeader.from({
310
+ ...newGlobalVariables,
311
+ timestamp: newGlobalVariables.timestamp.toBigInt(),
312
+ lastArchiveRoot: chainTipArchive,
313
+ contentCommitment: ContentCommitment.empty(),
314
+ totalManaUsed: Fr.ZERO,
315
+ });
322
316
 
323
317
  let finishedFlushing = false;
324
318
  const pendingTxCount = await this.p2pClient.getPendingTxCount();
@@ -327,7 +321,7 @@ export class Sequencer {
327
321
  // and also we may need to fetch more if we don't have enough valid txs.
328
322
  const pendingTxs = this.p2pClient.iteratePendingTxs();
329
323
 
330
- await this.buildBlockAndEnqueuePublish(pendingTxs, proposalHeader).catch(err => {
324
+ await this.buildBlockAndEnqueuePublish(pendingTxs, proposalHeader, newGlobalVariables).catch(err => {
331
325
  this.log.error(`Error building/enqueuing block`, err, { blockNumber: newBlockNumber, slot });
332
326
  });
333
327
  finishedFlushing = true;
@@ -426,11 +420,8 @@ export class Sequencer {
426
420
  /**
427
421
  * Build a block
428
422
  *
429
- * Shared between the sequencer and the validator for re-execution
430
- *
431
423
  * @param pendingTxs - The pending transactions to construct the block from
432
424
  * @param newGlobalVariables - The global variables for the new block
433
- * @param historicalHeader - The historical header of the parent
434
425
  * @param opts - Whether to just validate the block as a validator, as opposed to building it as a proposal
435
426
  */
436
427
  protected async buildBlock(
@@ -509,7 +500,7 @@ export class Sequencer {
509
500
  maxBlockGas: this.maxBlockGas,
510
501
  };
511
502
  const limits = opts.validateOnly ? { deadline } : { deadline, ...proposerLimits };
512
- const [publicProcessorDuration, [processedTxs, failedTxs]] = await elapsed(() =>
503
+ const [publicProcessorDuration, [processedTxs, failedTxs, usedTxs]] = await elapsed(() =>
513
504
  processor.process(pendingTxs, limits, validator),
514
505
  );
515
506
 
@@ -553,6 +544,7 @@ export class Sequencer {
553
544
  numTxs: processedTxs.length,
554
545
  numFailedTxs: failedTxs.length,
555
546
  blockBuildingTimer,
547
+ usedTxs,
556
548
  };
557
549
  } finally {
558
550
  // We create a fresh processor each time to reset any cached state (eg storage writes)
@@ -571,6 +563,31 @@ export class Sequencer {
571
563
  }
572
564
  }
573
565
 
566
+ /**
567
+ * Build a block from a proposal. Used by the validator to re-execute transactions.
568
+ *
569
+ * @param blockNumber - The block number of the proposal.
570
+ * @param header - The header of the proposal.
571
+ * @param pendingTxs - The pending transactions to construct the block from.
572
+ * @param opts - Whether to just validate the block as a validator, as opposed to building it as a proposal.
573
+ */
574
+ async buildBlockFromProposal(
575
+ blockNumber: Fr,
576
+ header: ProposedBlockHeader,
577
+ pendingTxs: Iterable<Tx> | AsyncIterable<Tx>,
578
+ opts: { validateOnly?: boolean } = {},
579
+ ) {
580
+ const { chainId, version } = await this.globalsBuilder.getGlobalConstantVariables();
581
+ const globalVariables = GlobalVariables.from({
582
+ ...header,
583
+ blockNumber,
584
+ timestamp: new Fr(header.timestamp),
585
+ chainId,
586
+ version,
587
+ });
588
+ return await this.buildBlock(pendingTxs, globalVariables, opts);
589
+ }
590
+
574
591
  /**
575
592
  * @notice Build and propose a block to the chain
576
593
  *
@@ -580,18 +597,18 @@ export class Sequencer {
580
597
  * @param pendingTxs - Iterable of pending transactions to construct the block from
581
598
  * @param proposalHeader - The partial header constructed for the proposal
582
599
  */
583
- @trackSpan('Sequencer.buildBlockAndEnqueuePublish', (_validTxs, proposalHeader) => ({
584
- [Attributes.BLOCK_NUMBER]: proposalHeader.globalVariables.blockNumber.toNumber(),
600
+ @trackSpan('Sequencer.buildBlockAndEnqueuePublish', (_validTxs, _proposalHeader, newGlobalVariables) => ({
601
+ [Attributes.BLOCK_NUMBER]: newGlobalVariables.blockNumber.toNumber(),
585
602
  }))
586
603
  private async buildBlockAndEnqueuePublish(
587
604
  pendingTxs: Iterable<Tx> | AsyncIterable<Tx>,
588
- proposalHeader: BlockHeader,
605
+ proposalHeader: ProposedBlockHeader,
606
+ newGlobalVariables: GlobalVariables,
589
607
  ): Promise<void> {
590
608
  await this.publisher.validateBlockForSubmission(proposalHeader);
591
609
 
592
- const newGlobalVariables = proposalHeader.globalVariables;
593
610
  const blockNumber = newGlobalVariables.blockNumber.toNumber();
594
- const slot = newGlobalVariables.slotNumber.toBigInt();
611
+ const slot = proposalHeader.slotNumber.toBigInt();
595
612
 
596
613
  // this.metrics.recordNewBlock(blockNumber, validTxs.length);
597
614
  const workTimer = new Timer();
@@ -599,12 +616,12 @@ export class Sequencer {
599
616
 
600
617
  try {
601
618
  const buildBlockRes = await this.buildBlock(pendingTxs, newGlobalVariables);
602
- const { publicGas, block, publicProcessorDuration, numTxs, numMsgs, blockBuildingTimer } = buildBlockRes;
619
+ const { publicGas, block, publicProcessorDuration, numTxs, numMsgs, blockBuildingTimer, usedTxs } = buildBlockRes;
603
620
  this.metrics.recordBuiltBlock(workTimer.ms(), publicGas.l2Gas);
604
621
 
605
622
  // TODO(@PhilWindle) We should probably periodically check for things like another
606
623
  // block being published before ours instead of just waiting on our block
607
- await this.publisher.validateBlockForSubmission(block.header);
624
+ await this.publisher.validateBlockForSubmission(block.header.toPropose());
608
625
 
609
626
  const blockStats: L2BlockBuiltStats = {
610
627
  eventName: 'l2-block-built',
@@ -631,7 +648,7 @@ export class Sequencer {
631
648
 
632
649
  this.log.debug('Collecting attestations');
633
650
  const stopCollectingAttestationsTimer = this.metrics.startCollectingAttestationsTimer();
634
- const attestations = await this.collectAttestations(block, txHashes);
651
+ const attestations = await this.collectAttestations(block, usedTxs);
635
652
  if (attestations !== undefined) {
636
653
  this.log.verbose(`Collected ${attestations.length} attestations`, { blockHash, blockNumber });
637
654
  }
@@ -649,7 +666,7 @@ export class Sequencer {
649
666
  [Attributes.BLOCK_ARCHIVE]: block.archive.toString(),
650
667
  [Attributes.BLOCK_TXS_COUNT]: txHashes.length,
651
668
  }))
652
- protected async collectAttestations(block: L2Block, txHashes: TxHash[]): Promise<Signature[] | undefined> {
669
+ protected async collectAttestations(block: L2Block, txs: Tx[]): Promise<Signature[] | undefined> {
653
670
  // TODO(https://github.com/AztecProtocol/aztec-packages/issues/7962): inefficient to have a round trip in here - this should be cached
654
671
  const committee = await this.publisher.getCurrentEpochCommittee();
655
672
 
@@ -671,14 +688,22 @@ export class Sequencer {
671
688
  this.setState(SequencerState.COLLECTING_ATTESTATIONS, slotNumber);
672
689
 
673
690
  this.log.debug('Creating block proposal for validators');
674
- const proposal = await this.validatorClient.createBlockProposal(block.header, block.archive.root, txHashes);
691
+ const blockProposalOptions: BlockProposalOptions = { publishFullTxs: !!this.config.publishTxsWithProposals };
692
+ const proposal = await this.validatorClient.createBlockProposal(
693
+ block.header.globalVariables.blockNumber,
694
+ block.header.toPropose(),
695
+ block.archive.root,
696
+ block.header.state,
697
+ txs,
698
+ blockProposalOptions,
699
+ );
675
700
  if (!proposal) {
676
701
  const msg = `Failed to create block proposal`;
677
702
  throw new Error(msg);
678
703
  }
679
704
 
680
705
  this.log.debug('Broadcasting block proposal to validators');
681
- this.validatorClient.broadcastBlockProposal(proposal);
706
+ await this.validatorClient.broadcastBlockProposal(proposal);
682
707
 
683
708
  const attestationTimeAllowed = this.enforceTimeTable
684
709
  ? this.timetable.getMaxAllowedTime(SequencerState.PUBLISHING_BLOCK)!
@@ -3,6 +3,11 @@ import { createLogger } from '@aztec/aztec.js';
3
3
  import type { SequencerMetrics } from './metrics.js';
4
4
  import { SequencerState } from './utils.js';
5
5
 
6
+ const MIN_EXECUTION_TIME = 1;
7
+ const BLOCK_PREPARE_TIME = 1;
8
+ const BLOCK_VALIDATION_TIME = 1;
9
+ const ATTESTATION_PROPAGATION_TIME = 2;
10
+
6
11
  export class SequencerTimetable {
7
12
  /**
8
13
  * How late into the slot can we be to start working. Computed as the total time needed for assembling and publishing a block,
@@ -19,16 +24,16 @@ export class SequencerTimetable {
19
24
  public readonly l1PublishingTime;
20
25
 
21
26
  /** What's the minimum time we want to leave available for execution and reexecution (used to derive init deadline) */
22
- public readonly minExecutionTime = 1;
27
+ public readonly minExecutionTime: number = MIN_EXECUTION_TIME;
23
28
 
24
29
  /** How long it takes to get ready to start building */
25
- public readonly blockPrepareTime = 1;
30
+ public readonly blockPrepareTime: number = BLOCK_PREPARE_TIME;
26
31
 
27
32
  /** How long it takes to for proposals and attestations to travel across the p2p layer (one-way) */
28
- public readonly attestationPropagationTime = 2;
33
+ public readonly attestationPropagationTime: number = ATTESTATION_PROPAGATION_TIME;
29
34
 
30
35
  /** How much time we spend validating and processing a block after building it, and assembling the proposal to send to attestors */
31
- public readonly blockValidationTime = 1;
36
+ public readonly blockValidationTime: number = BLOCK_VALIDATION_TIME;
32
37
 
33
38
  constructor(
34
39
  private readonly ethereumSlotDuration: number,
@@ -40,6 +45,13 @@ export class SequencerTimetable {
40
45
  ) {
41
46
  this.l1PublishingTime = this.ethereumSlotDuration - this.maxL1TxInclusionTimeIntoSlot;
42
47
 
48
+ // Assume zero-cost propagation time and faster runs in test environments where L1 slot duration is shortened
49
+ if (this.ethereumSlotDuration < 8) {
50
+ this.attestationPropagationTime = 0;
51
+ this.blockValidationTime = 0.5;
52
+ this.blockPrepareTime = 0.5;
53
+ }
54
+
43
55
  const allWorkToDo =
44
56
  this.blockPrepareTime +
45
57
  this.minExecutionTime * 2 +
@@ -154,7 +154,7 @@ export class SlasherClient extends WithTracer {
154
154
 
155
155
  public handleBlockStreamEvent(event: L2BlockSourceEvent): Promise<void> {
156
156
  this.log.debug(`Handling block stream event ${event.type}`);
157
- switch (event.type) {
157
+ switch (event.type as L2BlockSourceEvents) {
158
158
  case L2BlockSourceEvents.L2PruneDetected:
159
159
  this.handlePruneL2Blocks(event);
160
160
  break;
package/src/test/index.ts CHANGED
@@ -6,15 +6,15 @@ import { Sequencer } from '../sequencer/sequencer.js';
6
6
  import type { SequencerTimetable } from '../sequencer/timetable.js';
7
7
 
8
8
  class TestSequencer_ extends Sequencer {
9
- public declare publicProcessorFactory: PublicProcessorFactory;
10
- public declare timetable: SequencerTimetable;
11
- public declare publisher: SequencerPublisher;
9
+ declare public publicProcessorFactory: PublicProcessorFactory;
10
+ declare public timetable: SequencerTimetable;
11
+ declare public publisher: SequencerPublisher;
12
12
  }
13
13
 
14
14
  export type TestSequencer = TestSequencer_;
15
15
 
16
16
  class TestSequencerClient_ extends SequencerClient {
17
- public declare sequencer: TestSequencer;
17
+ declare public sequencer: TestSequencer;
18
18
  }
19
19
 
20
20
  export type TestSequencerClient = TestSequencerClient_;