diff --git a/yarn-project/end-to-end/src/e2e_p2p/gossip_network_no_cheat.test.ts b/yarn-project/end-to-end/src/e2e_p2p/gossip_network_no_cheat.test.ts index a9837a69efee..28a463778a31 100644 --- a/yarn-project/end-to-end/src/e2e_p2p/gossip_network_no_cheat.test.ts +++ b/yarn-project/end-to-end/src/e2e_p2p/gossip_network_no_cheat.test.ts @@ -13,7 +13,7 @@ import { sleep } from '@aztec/foundation/sleep'; import { MockZKPassportVerifierAbi } from '@aztec/l1-artifacts/MockZKPassportVerifierAbi'; import { RollupAbi } from '@aztec/l1-artifacts/RollupAbi'; import type { SequencerClient } from '@aztec/sequencer-client'; -import { CheckpointAttestation, ConsensusPayload } from '@aztec/stdlib/p2p'; +import { CheckpointAttestation, ConsensusPayload, TopicType } from '@aztec/stdlib/p2p'; import { ZkPassportProofParams } from '@aztec/stdlib/zkpassport'; import { jest } from '@jest/globals'; @@ -201,8 +201,21 @@ describe('e2e_p2p_network', () => { shouldCollectMetrics(), ); - // wait a bit for peers to discover each other - await sleep(8000); + // Wait for the gossipsub mesh to fully form before the committee starts producing. With + // skipInitialSequencer, the first blocks are built by this committee, and the first checkpoint + // must reach quorum (all 4 validators) to land on L1. If the proposal/checkpoint meshes are only + // partly formed, some committee members miss the first proposal, the first checkpoint stalls at + // 2/3, and every later slot rebuilds a competing un-checkpointed block 1 that peers reject as + // `block_number_already_exists` — a permanent 2/3 deadlock. Require a full mesh (N-1 peers per + // node) on the proposal/checkpoint topics so the first proposal reaches the whole committee. + await t.waitForP2PMeshConnectivity( + nodes, + NUM_VALIDATORS, + 60, + 0.5, + [TopicType.block_proposal, TopicType.checkpoint_proposal, TopicType.checkpoint_attestation], + NUM_VALIDATORS - 1, + ); // Wait for the first checkpoint to be published to L1 before submitting transactions. // With skipInitialSequencer, no blocks exist from setup, so the first blocks are built by the diff --git a/yarn-project/end-to-end/src/e2e_p2p/p2p_network.ts b/yarn-project/end-to-end/src/e2e_p2p/p2p_network.ts index 8c3691b6c026..e8d95d08901e 100644 --- a/yarn-project/end-to-end/src/e2e_p2p/p2p_network.ts +++ b/yarn-project/end-to-end/src/e2e_p2p/p2p_network.ts @@ -431,6 +431,7 @@ export class P2PNetworkTest { timeoutSeconds = 30, checkIntervalSeconds = 0.1, topics: TopicType[] = [TopicType.tx], + minMeshPeerCount = 1, ) { const nodeCount = expectedNodeCount ?? nodes.length; const minPeerCount = nodeCount - 1; @@ -457,11 +458,13 @@ export class P2PNetworkTest { this.logger.warn('All nodes connected to P2P mesh'); - // Wait for GossipSub mesh to form for all specified topics. - // We only require at least 1 mesh peer per node because GossipSub - // stops grafting once it reaches Dlo peers and won't fill the mesh to all available peers. + // Wait for the GossipSub mesh to form for all specified topics. By default we only require at + // least 1 mesh peer per node, since GossipSub stops grafting once it reaches Dlo peers and won't + // fill the mesh to every available peer. Callers that need a proposal to reach the whole + // committee within a slot (e.g. quorum-from-genesis tests) raise `minMeshPeerCount` so the mesh + // is fully formed — a single mesh peer can leave some committee members unreached at first. for (const topic of topics) { - this.logger.warn(`Waiting for GossipSub mesh to form for ${topic} topic...`); + this.logger.warn(`Waiting for GossipSub mesh (>= ${minMeshPeerCount} peers per node) for ${topic} topic...`); await Promise.all( nodes.map(async (node, index) => { const p2p = node.getP2P(); @@ -469,15 +472,15 @@ export class P2PNetworkTest { async () => { const meshPeers = await p2p.getGossipMeshPeerCount(topic); this.logger.debug(`Node ${index} has ${meshPeers} gossip mesh peers for ${topic} topic`); - return meshPeers >= 1 ? true : undefined; + return meshPeers >= minMeshPeerCount ? true : undefined; }, - `Node ${index} to have gossip mesh peers for ${topic} topic`, + `Node ${index} to have >= ${minMeshPeerCount} gossip mesh peers for ${topic} topic`, timeoutSeconds, checkIntervalSeconds, ); }), ); - this.logger.warn(`All nodes have gossip mesh peers for ${topic} topic`); + this.logger.warn(`All nodes have >= ${minMeshPeerCount} gossip mesh peers for ${topic} topic`); } }