diff --git a/.github/workflows/deploy-staging-public.yml b/.github/workflows/deploy-staging-public.yml index 3979017bf698..5c521e6c0e7e 100644 --- a/.github/workflows/deploy-staging-public.yml +++ b/.github/workflows/deploy-staging-public.yml @@ -49,7 +49,7 @@ jobs: if [ -n "$TAG" ]; then echo "Found tag: $TAG" - SEMVER="${VERSION}" + SEMVER="${TAG#v}" echo "tag=$TAG" >> $GITHUB_OUTPUT echo "semver=$SEMVER" >> $GITHUB_OUTPUT exit 0 diff --git a/aztec-up/bin/0.0.1/aztec-install b/aztec-up/bin/0.0.1/aztec-install index 119ea10d1410..a217e7b134d4 100755 --- a/aztec-up/bin/0.0.1/aztec-install +++ b/aztec-up/bin/0.0.1/aztec-install @@ -51,11 +51,12 @@ function title { echo -e "Installing version: ${bold}${g}$VERSION${r}" echo echo -e "This install script will install the following and update your PATH if necessary:" - echo -e " ${bold}${g}nargo${r} - the version of the noir programming language compatible with this version of aztec." - echo -e " ${bold}${g}bb${r} - the version of the barretenberg proving backend compatible with this version of aztec." - echo -e " ${bold}${g}aztec${r} - a collection of tools to compile and test contracts, to launch subsystems and interact with the aztec network." - echo -e " ${bold}${g}aztec-up${r} - a tool to install and manage aztec toolchain versions." - echo -e " ${bold}${g}aztec-wallet${r} - our minimalistic CLI wallet" + echo -e " ${bold}${g}nargo${r} - the version of the noir programming language compatible with this version of aztec." + echo -e " ${bold}${g}noir-profiler${r} - a sampling profiler for analyzing and visualizing Noir programs." + echo -e " ${bold}${g}bb${r} - the version of the barretenberg proving backend compatible with this version of aztec." + echo -e " ${bold}${g}aztec${r} - a collection of tools to compile and test contracts, to launch subsystems and interact with the aztec network." + echo -e " ${bold}${g}aztec-up${r} - a tool to install and manage aztec toolchain versions." + echo -e " ${bold}${g}aztec-wallet${r} - our minimalistic CLI wallet" echo read -p "Do you wish to continue? (y/n) " -n 1 -r echo diff --git a/aztec-up/bin/0.0.1/install b/aztec-up/bin/0.0.1/install index 97c619ec0805..51cbcae671d7 100755 --- a/aztec-up/bin/0.0.1/install +++ b/aztec-up/bin/0.0.1/install @@ -174,6 +174,8 @@ function install_noir { # Move the nargo binary to our version bin directory mv "$temp_nargo_home/bin/nargo" "$version_bin_path/nargo" + # Also move noir-profiler, needed by `aztec profile flamegraph` + mv "$temp_nargo_home/bin/noir-profiler" "$version_bin_path/noir-profiler" # Cleanup temp directory rm -rf "$temp_nargo_home" diff --git a/barretenberg/cpp/src/barretenberg/wasi/wasi_stubs.cpp b/barretenberg/cpp/src/barretenberg/wasi/wasi_stubs.cpp index af2520e76c42..31258e97195e 100644 --- a/barretenberg/cpp/src/barretenberg/wasi/wasi_stubs.cpp +++ b/barretenberg/cpp/src/barretenberg/wasi/wasi_stubs.cpp @@ -59,17 +59,22 @@ int32_t __imported_wasi_snapshot_preview1_fd_close(int32_t) return 0; } -int32_t __imported_wasi_snapshot_preview1_environ_get(int32_t, int32_t) +int32_t __imported_wasi_snapshot_preview1_environ_get(int32_t environ_ptr, int32_t environ_buf_ptr) { - // info("environ_get not implemented."); - // abort(); + // No environment variables, so nothing to write. The pointers point to + // arrays that would hold the environ entries and the concatenated + // key=value strings respectively, but with count == 0 they are empty. + (void)environ_ptr; + (void)environ_buf_ptr; return 0; } -int32_t __imported_wasi_snapshot_preview1_environ_sizes_get(int32_t, int32_t) +int32_t __imported_wasi_snapshot_preview1_environ_sizes_get(int32_t count_ptr, int32_t buf_size_ptr) { - // info("environ_sizes_get not implemented."); - // abort(); + // WASI requires writing the number of environment variables and the total + // buffer size needed to hold them. We have none of either. + *(int32_t*)(uintptr_t)count_ptr = 0; + *(int32_t*)(uintptr_t)buf_size_ptr = 0; return 0; } diff --git a/yarn-project/archiver/src/archiver-store.test.ts b/yarn-project/archiver/src/archiver-store.test.ts index d5cd45f3a3fa..950b90008ea8 100644 --- a/yarn-project/archiver/src/archiver-store.test.ts +++ b/yarn-project/archiver/src/archiver-store.test.ts @@ -432,4 +432,116 @@ describe('Archiver Store', () => { expect(result).toEqual([]); }); }); + + describe('rollbackTo', () => { + beforeEach(() => { + publicClient.getBlock.mockImplementation( + (args: { blockNumber?: bigint } = {}) => + Promise.resolve({ number: args.blockNumber ?? 0n, hash: `0x${'0'.repeat(64)}` }) as any, + ); + }); + + it('rejects rollback to a block that is not at a checkpoint boundary', async () => { + const genesisArchive = new AppendOnlyTreeSnapshot(new Fr(GENESIS_ARCHIVE_ROOT), 1); + // Checkpoint 1: 3 blocks (1, 2, 3). Checkpoint 2: 3 blocks (4, 5, 6). + const testCheckpoints = await makeChainedCheckpoints(2, { + previousArchive: genesisArchive, + blocksPerCheckpoint: 3, + }); + await archiverStore.addCheckpoints(testCheckpoints); + + // Block 1 is not at a checkpoint boundary (checkpoint 1 ends at block 3) + await expect(archiver.rollbackTo(BlockNumber(1))).rejects.toThrow( + /not at a checkpoint boundary.*Use block 3 to roll back to this checkpoint.*or block 0 to roll back to the previous one/, + ); + + // Block 2 is also not at a checkpoint boundary + await expect(archiver.rollbackTo(BlockNumber(2))).rejects.toThrow( + /not at a checkpoint boundary.*Use block 3 to roll back to this checkpoint.*or block 0 to roll back to the previous one/, + ); + }); + + it('allows rollback to the last block of a checkpoint and updates sync points', async () => { + const genesisArchive = new AppendOnlyTreeSnapshot(new Fr(GENESIS_ARCHIVE_ROOT), 1); + // Checkpoint 1: 3 blocks (1, 2, 3), L1 block 10. Checkpoint 2: 3 blocks (4, 5, 6), L1 block 20. + const testCheckpoints = await makeChainedCheckpoints(2, { + previousArchive: genesisArchive, + blocksPerCheckpoint: 3, + }); + await archiverStore.addCheckpoints(testCheckpoints); + + // Block 3 is the last block of checkpoint 1 — should succeed + await archiver.rollbackTo(BlockNumber(3)); + + expect(await archiver.getSynchedCheckpointNumber()).toEqual(CheckpointNumber(1)); + + // Verify sync points are set to checkpoint 1's L1 block number (10) + const synchPoint = await archiverStore.getSynchPoint(); + expect(synchPoint.blocksSynchedTo).toEqual(10n); + expect(synchPoint.messagesSynchedTo?.l1BlockNumber).toEqual(10n); + }); + + it('includes correct boundary info in error for mid-checkpoint rollback', async () => { + const genesisArchive = new AppendOnlyTreeSnapshot(new Fr(GENESIS_ARCHIVE_ROOT), 1); + // Checkpoint 1: 2 blocks (1, 2). Checkpoint 2: 3 blocks (3, 4, 5). + const checkpoints1 = await makeChainedCheckpoints(1, { + previousArchive: genesisArchive, + blocksPerCheckpoint: 2, + }); + const checkpoints2 = await makeChainedCheckpoints(1, { + previousArchive: checkpoints1[0].checkpoint.blocks.at(-1)!.archive, + startCheckpointNumber: CheckpointNumber(2), + startBlockNumber: 3, + startL1BlockNumber: 20, + blocksPerCheckpoint: 3, + }); + await archiverStore.addCheckpoints([...checkpoints1, ...checkpoints2]); + + // Block 3 is the first of checkpoint 2 (spans 3-5) + // Should suggest block 5 (end of this checkpoint) or block 2 (end of previous) + await expect(archiver.rollbackTo(BlockNumber(3))).rejects.toThrow( + /Checkpoint 2 spans blocks 3 to 5.*Use block 5 to roll back to this checkpoint.*or block 2 to roll back to the previous one/, + ); + }); + + it('rolls back proven checkpoint number when target is before proven block', async () => { + const genesisArchive = new AppendOnlyTreeSnapshot(new Fr(GENESIS_ARCHIVE_ROOT), 1); + // Checkpoint 1: blocks 1-2, Checkpoint 2: blocks 3-4, Checkpoint 3: blocks 5-6 + const testCheckpoints = await makeChainedCheckpoints(3, { + previousArchive: genesisArchive, + blocksPerCheckpoint: 2, + }); + await archiverStore.addCheckpoints(testCheckpoints); + + // Mark checkpoint 2 as proven + await archiverStore.setProvenCheckpointNumber(CheckpointNumber(2)); + expect(await archiver.getProvenCheckpointNumber()).toEqual(CheckpointNumber(2)); + + // Roll back to block 2 (end of checkpoint 1), which is before proven block 4 + await archiver.rollbackTo(BlockNumber(2)); + + expect(await archiver.getSynchedCheckpointNumber()).toEqual(CheckpointNumber(1)); + expect(await archiver.getProvenCheckpointNumber()).toEqual(CheckpointNumber(1)); + }); + + it('preserves proven checkpoint number when target is after proven block', async () => { + const genesisArchive = new AppendOnlyTreeSnapshot(new Fr(GENESIS_ARCHIVE_ROOT), 1); + // Checkpoint 1: blocks 1-2, Checkpoint 2: blocks 3-4, Checkpoint 3: blocks 5-6 + const testCheckpoints = await makeChainedCheckpoints(3, { + previousArchive: genesisArchive, + blocksPerCheckpoint: 2, + }); + await archiverStore.addCheckpoints(testCheckpoints); + + // Mark checkpoint 1 as proven + await archiverStore.setProvenCheckpointNumber(CheckpointNumber(1)); + expect(await archiver.getProvenCheckpointNumber()).toEqual(CheckpointNumber(1)); + + // Roll back to block 4 (end of checkpoint 2), which is after proven block 2 + await archiver.rollbackTo(BlockNumber(4)); + + expect(await archiver.getSynchedCheckpointNumber()).toEqual(CheckpointNumber(2)); + expect(await archiver.getProvenCheckpointNumber()).toEqual(CheckpointNumber(1)); + }); + }); }); diff --git a/yarn-project/archiver/src/archiver.ts b/yarn-project/archiver/src/archiver.ts index 4aec6f3c9e69..de82a0482186 100644 --- a/yarn-project/archiver/src/archiver.ts +++ b/yarn-project/archiver/src/archiver.ts @@ -399,7 +399,6 @@ export class Archiver extends ArchiverDataSourceBase implements L2BlockSink, Tra } public async rollbackTo(targetL2BlockNumber: BlockNumber): Promise { - // TODO(pw/mbps): This still assumes 1 block per checkpoint const currentBlocks = await this.getL2Tips(); const currentL2Block = currentBlocks.proposed.number; const currentProvenBlock = currentBlocks.proven.block.number; @@ -411,8 +410,25 @@ export class Archiver extends ArchiverDataSourceBase implements L2BlockSink, Tra if (!targetL2Block) { throw new Error(`Target L2 block ${targetL2BlockNumber} not found`); } - const targetL1BlockNumber = targetL2Block.l1.blockNumber; const targetCheckpointNumber = targetL2Block.checkpointNumber; + + // Rollback operates at checkpoint granularity: the target block must be the last block of its checkpoint. + const checkpointData = await this.store.getCheckpointData(targetCheckpointNumber); + if (checkpointData) { + const lastBlockInCheckpoint = BlockNumber(checkpointData.startBlock + checkpointData.blockCount - 1); + if (targetL2BlockNumber !== lastBlockInCheckpoint) { + const previousCheckpointBoundary = + checkpointData.startBlock > 1 ? BlockNumber(checkpointData.startBlock - 1) : BlockNumber(0); + throw new Error( + `Target L2 block ${targetL2BlockNumber} is not at a checkpoint boundary. ` + + `Checkpoint ${targetCheckpointNumber} spans blocks ${checkpointData.startBlock} to ${lastBlockInCheckpoint}. ` + + `Use block ${lastBlockInCheckpoint} to roll back to this checkpoint, ` + + `or block ${previousCheckpointBoundary} to roll back to the previous one.`, + ); + } + } + + const targetL1BlockNumber = targetL2Block.l1.blockNumber; const targetL1Block = await this.publicClient.getBlock({ blockNumber: targetL1BlockNumber, includeTransactions: false, @@ -431,13 +447,14 @@ export class Archiver extends ArchiverDataSourceBase implements L2BlockSink, Tra await this.store.setCheckpointSynchedL1BlockNumber(targetL1BlockNumber); await this.store.setMessageSynchedL1Block({ l1BlockNumber: targetL1BlockNumber, l1BlockHash: targetL1BlockHash }); if (targetL2BlockNumber < currentProvenBlock) { - this.log.info(`Clearing proven L2 block number`); - await this.updater.setProvenCheckpointNumber(CheckpointNumber.ZERO); + this.log.info(`Rolling back proven L2 checkpoint to ${targetCheckpointNumber}`); + await this.updater.setProvenCheckpointNumber(targetCheckpointNumber); } // TODO(palla/reorg): Set the finalized block when we add support for it. + // const currentFinalizedBlock = currentBlocks.finalized.block.number; // if (targetL2BlockNumber < currentFinalizedBlock) { - // this.log.info(`Clearing finalized L2 block number`); - // await this.store.setFinalizedL2BlockNumber(0); + // this.log.info(`Rolling back finalized L2 checkpoint to ${targetCheckpointNumber}`); + // await this.updater.setFinalizedCheckpointNumber(targetCheckpointNumber); // } } } diff --git a/yarn-project/p2p/src/config.ts b/yarn-project/p2p/src/config.ts index f7c54eb1e191..039e433aa163 100644 --- a/yarn-project/p2p/src/config.ts +++ b/yarn-project/p2p/src/config.ts @@ -38,7 +38,7 @@ export interface P2PConfig ChainConfig, TxCollectionConfig, TxFileStoreConfig, - Pick { + Pick { /** A flag dictating whether the P2P subsystem should be enabled. */ p2pEnabled: boolean; diff --git a/yarn-project/p2p/src/msg_validators/proposal_validator/block_proposal_validator.ts b/yarn-project/p2p/src/msg_validators/proposal_validator/block_proposal_validator.ts index 5de0076aea39..a481256e9f37 100644 --- a/yarn-project/p2p/src/msg_validators/proposal_validator/block_proposal_validator.ts +++ b/yarn-project/p2p/src/msg_validators/proposal_validator/block_proposal_validator.ts @@ -4,7 +4,7 @@ import type { BlockProposal, P2PValidator } from '@aztec/stdlib/p2p'; import { ProposalValidator } from '../proposal_validator/proposal_validator.js'; export class BlockProposalValidator extends ProposalValidator implements P2PValidator { - constructor(epochCache: EpochCacheInterface, opts: { txsPermitted: boolean }) { + constructor(epochCache: EpochCacheInterface, opts: { txsPermitted: boolean; maxTxsPerBlock?: number }) { super(epochCache, opts, 'p2p:block_proposal_validator'); } } diff --git a/yarn-project/p2p/src/msg_validators/proposal_validator/checkpoint_proposal_validator.ts b/yarn-project/p2p/src/msg_validators/proposal_validator/checkpoint_proposal_validator.ts index 763912a04814..74804fe45d21 100644 --- a/yarn-project/p2p/src/msg_validators/proposal_validator/checkpoint_proposal_validator.ts +++ b/yarn-project/p2p/src/msg_validators/proposal_validator/checkpoint_proposal_validator.ts @@ -7,7 +7,7 @@ export class CheckpointProposalValidator extends ProposalValidator implements P2PValidator { - constructor(epochCache: EpochCacheInterface, opts: { txsPermitted: boolean }) { + constructor(epochCache: EpochCacheInterface, opts: { txsPermitted: boolean; maxTxsPerBlock?: number }) { super(epochCache, opts, 'p2p:checkpoint_proposal_validator'); } } diff --git a/yarn-project/p2p/src/msg_validators/proposal_validator/proposal_validator.ts b/yarn-project/p2p/src/msg_validators/proposal_validator/proposal_validator.ts index f6fb7102e537..a926d1f3c144 100644 --- a/yarn-project/p2p/src/msg_validators/proposal_validator/proposal_validator.ts +++ b/yarn-project/p2p/src/msg_validators/proposal_validator/proposal_validator.ts @@ -9,10 +9,16 @@ export abstract class ProposalValidator this.maxTxsPerBlock) { + this.logger.warn( + `Penalizing peer for proposal with ${proposal.txHashes.length} transaction(s) when max is ${this.maxTxsPerBlock}`, + ); + return { result: 'reject', severity: PeerErrorSeverity.MidToleranceError }; + } + // Embedded txs must be listed in txHashes const hashSet = new Set(proposal.txHashes.map(h => h.toString())); const missingTxHashes = diff --git a/yarn-project/p2p/src/msg_validators/proposal_validator/proposal_validator_test_suite.ts b/yarn-project/p2p/src/msg_validators/proposal_validator/proposal_validator_test_suite.ts index 6aa79230f8cd..e58a007a3de7 100644 --- a/yarn-project/p2p/src/msg_validators/proposal_validator/proposal_validator_test_suite.ts +++ b/yarn-project/p2p/src/msg_validators/proposal_validator/proposal_validator_test_suite.ts @@ -1,4 +1,5 @@ import type { EpochCacheInterface } from '@aztec/epoch-cache'; +import { NoCommitteeError } from '@aztec/ethereum/contracts'; import type { Secp256k1Signer } from '@aztec/foundation/crypto/secp256k1-signer'; import type { EthAddress } from '@aztec/foundation/eth-address'; import { @@ -9,12 +10,13 @@ import { } from '@aztec/stdlib/p2p'; import type { TxHash } from '@aztec/stdlib/tx'; +import { jest } from '@jest/globals'; import type { MockProxy } from 'jest-mock-extended'; export interface ProposalValidatorTestParams { validatorFactory: ( epochCache: EpochCacheInterface, - opts: { txsPermitted: boolean }, + opts: { txsPermitted: boolean; maxTxsPerBlock?: number }, ) => { validate: (proposal: TProposal) => Promise }; makeProposal: (options?: any) => Promise; makeHeader: (epochNumber: number | bigint, slotNumber: number | bigint, blockNumber: number | bigint) => any; @@ -105,6 +107,26 @@ export function sharedProposalValidatorTests { + const currentProposer = getSigner(); + const header = makeHeader(1, 100, 100); + const mockProposal = await makeProposal({ + blockHeader: header, + lastBlockHeader: header, + signer: currentProposer, + }); + + // Override getSender to return undefined (invalid signature) + jest.spyOn(mockProposal as any, 'getSender').mockReturnValue(undefined); + + mockGetProposer(getAddress(currentProposer), getAddress()); + const result = await validator.validate(mockProposal); + expect(result).toEqual({ result: 'reject', severity: PeerErrorSeverity.MidToleranceError }); + + // Should not try to resolve proposer if signature is invalid + expect(epochCache.getProposerAttesterAddressInSlot).not.toHaveBeenCalled(); + }); + it('returns mid tolerance error if proposer is not current proposer for current slot', async () => { const currentProposer = getSigner(); const nextProposer = getSigner(); @@ -152,6 +174,34 @@ export function sharedProposalValidatorTests { + const currentProposer = getSigner(); + const header = makeHeader(1, 100, 100); + const mockProposal = await makeProposal({ + blockHeader: header, + lastBlockHeader: header, + signer: currentProposer, + }); + + epochCache.getProposerAttesterAddressInSlot.mockResolvedValue(undefined); + const result = await validator.validate(mockProposal); + expect(result).toEqual({ result: 'accept' }); + }); + + it('returns low tolerance error when getProposerAttesterAddressInSlot throws NoCommitteeError', async () => { + const currentProposer = getSigner(); + const header = makeHeader(1, 100, 100); + const mockProposal = await makeProposal({ + blockHeader: header, + lastBlockHeader: header, + signer: currentProposer, + }); + + epochCache.getProposerAttesterAddressInSlot.mockRejectedValue(new NoCommitteeError()); + const result = await validator.validate(mockProposal); + expect(result).toEqual({ result: 'reject', severity: PeerErrorSeverity.LowToleranceError }); + }); + it('returns undefined if proposal is valid for current slot and proposer', async () => { const currentProposer = getSigner(); const nextProposer = getSigner(); @@ -226,5 +276,98 @@ export function sharedProposalValidatorTests { + it('returns mid tolerance error if embedded txs are not listed in txHashes', async () => { + const currentProposer = getSigner(); + const txHashes = getTxHashes(2); + const header = makeHeader(1, 100, 100); + const mockProposal = await makeProposal({ + blockHeader: header, + lastBlockHeader: header, + signer: currentProposer, + txHashes, + }); + + // Create a fake tx whose hash is NOT in txHashes + const fakeTxHash = getTxHashes(1)[0]; + const fakeTx = { getTxHash: () => fakeTxHash, validateTxHash: () => Promise.resolve(true) }; + Object.defineProperty(mockProposal, 'txs', { get: () => [fakeTx], configurable: true }); + + mockGetProposer(getAddress(currentProposer), getAddress()); + const result = await validator.validate(mockProposal); + expect(result).toEqual({ result: 'reject', severity: PeerErrorSeverity.MidToleranceError }); + }); + + it('returns low tolerance error if embedded tx has invalid tx hash', async () => { + const currentProposer = getSigner(); + const txHashes = getTxHashes(2); + const header = makeHeader(1, 100, 100); + const mockProposal = await makeProposal({ + blockHeader: header, + lastBlockHeader: header, + signer: currentProposer, + txHashes, + }); + + // Create a fake tx whose hash IS in txHashes but validateTxHash returns false + const fakeTx = { getTxHash: () => txHashes[0], validateTxHash: () => Promise.resolve(false) }; + Object.defineProperty(mockProposal, 'txs', { get: () => [fakeTx], configurable: true }); + + mockGetProposer(getAddress(currentProposer), getAddress()); + const result = await validator.validate(mockProposal); + expect(result).toEqual({ result: 'reject', severity: PeerErrorSeverity.LowToleranceError }); + }); + }); + + describe('maxTxsPerBlock validation', () => { + it('rejects proposal when txHashes exceed maxTxsPerBlock', async () => { + const validatorWithMaxTxs = validatorFactory(epochCache, { txsPermitted: true, maxTxsPerBlock: 2 }); + const currentProposer = getSigner(); + const header = makeHeader(1, 100, 100); + const mockProposal = await makeProposal({ + blockHeader: header, + lastBlockHeader: header, + signer: currentProposer, + txHashes: getTxHashes(3), + }); + + mockGetProposer(getAddress(currentProposer), getAddress()); + const result = await validatorWithMaxTxs.validate(mockProposal); + expect(result).toEqual({ result: 'reject', severity: PeerErrorSeverity.MidToleranceError }); + }); + + it('accepts proposal when txHashes count equals maxTxsPerBlock', async () => { + const validatorWithMaxTxs = validatorFactory(epochCache, { txsPermitted: true, maxTxsPerBlock: 2 }); + const currentProposer = getSigner(); + const header = makeHeader(1, 100, 100); + const mockProposal = await makeProposal({ + blockHeader: header, + lastBlockHeader: header, + signer: currentProposer, + txHashes: getTxHashes(2), + }); + + mockGetProposer(getAddress(currentProposer), getAddress()); + const result = await validatorWithMaxTxs.validate(mockProposal); + expect(result).toEqual({ result: 'accept' }); + }); + + it('accepts proposal when maxTxsPerBlock is not set (unlimited)', async () => { + // Default validator has no maxTxsPerBlock + const currentProposer = getSigner(); + const header = makeHeader(1, 100, 100); + const mockProposal = await makeProposal({ + blockHeader: header, + lastBlockHeader: header, + signer: currentProposer, + txHashes: getTxHashes(10), + }); + + mockGetProposer(getAddress(currentProposer), getAddress()); + const result = await validator.validate(mockProposal); + expect(result).toEqual({ result: 'accept' }); + }); + }); }); } diff --git a/yarn-project/p2p/src/services/libp2p/libp2p_service.ts b/yarn-project/p2p/src/services/libp2p/libp2p_service.ts index 7215f2bc449e..5d6edd034eb9 100644 --- a/yarn-project/p2p/src/services/libp2p/libp2p_service.ts +++ b/yarn-project/p2p/src/services/libp2p/libp2p_service.ts @@ -224,9 +224,13 @@ export class LibP2PService extends this.protocolVersion, ); - this.blockProposalValidator = new BlockProposalValidator(epochCache, { txsPermitted: !config.disableTransactions }); + this.blockProposalValidator = new BlockProposalValidator(epochCache, { + txsPermitted: !config.disableTransactions, + maxTxsPerBlock: config.maxTxsPerBlock, + }); this.checkpointProposalValidator = new CheckpointProposalValidator(epochCache, { txsPermitted: !config.disableTransactions, + maxTxsPerBlock: config.maxTxsPerBlock, }); this.checkpointAttestationValidator = config.fishermanMode ? new FishermanAttestationValidator(epochCache, mempools.attestationPool, telemetry) diff --git a/yarn-project/sequencer-client/src/config.ts b/yarn-project/sequencer-client/src/config.ts index 469651fba387..61dcb2344c17 100644 --- a/yarn-project/sequencer-client/src/config.ts +++ b/yarn-project/sequencer-client/src/config.ts @@ -13,6 +13,7 @@ import { type P2PConfig, p2pConfigMappings } from '@aztec/p2p/config'; import { AztecAddress } from '@aztec/stdlib/aztec-address'; import { type ChainConfig, + DEFAULT_MAX_TXS_PER_BLOCK, type SequencerConfig, chainConfigMappings, sharedSequencerConfigMappings, @@ -37,7 +38,7 @@ export type { SequencerConfig }; */ export const DefaultSequencerConfig: ResolvedSequencerConfig = { sequencerPollingIntervalMS: 500, - maxTxsPerBlock: 32, + maxTxsPerBlock: DEFAULT_MAX_TXS_PER_BLOCK, minTxsPerBlock: 1, buildCheckpointIfEmpty: false, publishTxsWithProposals: false, @@ -77,11 +78,6 @@ export const sequencerConfigMappings: ConfigMappingsType = { description: 'The number of ms to wait between polling for checking to build on the next slot.', ...numberConfigHelper(DefaultSequencerConfig.sequencerPollingIntervalMS), }, - maxTxsPerBlock: { - env: 'SEQ_MAX_TX_PER_BLOCK', - description: 'The maximum number of txs to include in a block.', - ...numberConfigHelper(DefaultSequencerConfig.maxTxsPerBlock), - }, minTxsPerBlock: { env: 'SEQ_MIN_TX_PER_BLOCK', description: 'The minimum number of txs to include in a block.', diff --git a/yarn-project/stdlib/src/config/sequencer-config.ts b/yarn-project/stdlib/src/config/sequencer-config.ts index 7619cdce7e68..31d0eca9458a 100644 --- a/yarn-project/stdlib/src/config/sequencer-config.ts +++ b/yarn-project/stdlib/src/config/sequencer-config.ts @@ -1,7 +1,10 @@ -import type { ConfigMappingsType } from '@aztec/foundation/config'; +import { type ConfigMappingsType, numberConfigHelper } from '@aztec/foundation/config'; import type { SequencerConfig } from '../interfaces/configs.js'; +/** Default maximum number of transactions per block. */ +export const DEFAULT_MAX_TXS_PER_BLOCK = 32; + /** * Partial sequencer config mappings for fields that need to be shared across packages. * The full sequencer config mappings remain in sequencer-client, but shared fields @@ -9,7 +12,7 @@ import type { SequencerConfig } from '../interfaces/configs.js'; * to avoid duplication. */ export const sharedSequencerConfigMappings: ConfigMappingsType< - Pick + Pick > = { blockDurationMs: { env: 'SEQ_BLOCK_DURATION_MS', @@ -26,4 +29,9 @@ export const sharedSequencerConfigMappings: ConfigMappingsType< parseEnv: (val: string) => (val ? parseInt(val, 10) : 0), defaultValue: 0, }, + maxTxsPerBlock: { + env: 'SEQ_MAX_TX_PER_BLOCK', + description: 'The maximum number of txs to include in a block.', + ...numberConfigHelper(DEFAULT_MAX_TXS_PER_BLOCK), + }, }; diff --git a/yarn-project/stdlib/src/interfaces/validator.ts b/yarn-project/stdlib/src/interfaces/validator.ts index ae6c5faf1e25..898657f268f4 100644 --- a/yarn-project/stdlib/src/interfaces/validator.ts +++ b/yarn-project/stdlib/src/interfaces/validator.ts @@ -62,7 +62,7 @@ export type ValidatorClientConfig = ValidatorHASignerConfig & { }; export type ValidatorClientFullConfig = ValidatorClientConfig & - Pick & + Pick & Pick< SlasherConfig, 'slashBroadcastedInvalidBlockPenalty' | 'slashDuplicateProposalPenalty' | 'slashDuplicateAttestationPenalty' @@ -93,6 +93,7 @@ export const ValidatorClientFullConfigSchema = zodFor WatcherEmitter) const metrics = new ValidatorMetrics(telemetry); const blockProposalValidator = new BlockProposalValidator(epochCache, { txsPermitted: !config.disableTransactions, + maxTxsPerBlock: config.maxTxsPerBlock, }); const blockProposalHandler = new BlockProposalHandler( checkpointsBuilder,