Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 13 additions & 7 deletions yarn-project/end-to-end/src/composed/ha/e2e_ha_full.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -416,9 +416,9 @@ describe('HA Full Setup', () => {
const round = await governanceProposer.computeRound(blockSlot);
logger.info(`Block slot ${blockSlot}, governance round ${round}`);

// Poll L1 for governance votes
// Poll L1 until at least one governance vote appears
logger.info('Polling L1 for governance votes...');
const l1VoteCount = await retryUntil(
await retryUntil(
async () => {
const voteCount = Number(
await governanceProposer.getPayloadSignals(
Expand All @@ -433,11 +433,6 @@ describe('HA Full Setup', () => {
6, // timeout in seconds (30 attempts * 200ms)
0.2, // interval in seconds (200ms)
);
logger.info(`Found ${l1VoteCount} governance vote(s) on L1 for payload ${mockGovernancePayload.toString()}`);

// Verify votes were actually sent to L1
expect(l1VoteCount).toBeGreaterThan(0);
logger.info(`Verified ${l1VoteCount} governance vote(s) successfully sent to L1`);

// Get L1 round info to determine which slots have actually landed on L1.
// We anchor the comparison on L1's lastSignalSlot since:
Expand All @@ -448,6 +443,17 @@ describe('HA Full Setup', () => {
round,
);
const lastSignalSlot = Number(roundInfo.lastSignalSlot);

// Re-query L1 vote count after getting lastSignalSlot in case more votes landed between the poll and getRoundInfo:
// the retryUntil may return a stale count if more votes land between the poll and getRoundInfo
const l1VoteCount = Number(
await governanceProposer.getPayloadSignals(
deployL1ContractsValues.l1ContractAddresses.rollupAddress.toString(),
round,
mockGovernancePayload.toString(),
),
);
expect(l1VoteCount).toBeGreaterThan(0);
logger.info(
`L1 round ${round} info: lastSignalSlot=${lastSignalSlot}, l1VoteCount=${l1VoteCount}, payloadWithMostSignals=${roundInfo.payloadWithMostSignals}`,
);
Expand Down
22 changes: 22 additions & 0 deletions yarn-project/end-to-end/src/e2e_p2p/p2p_network.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import type { BootstrapNode } from '@aztec/p2p/bootstrap';
import { createBootstrapNodeFromPrivateKey, getBootstrapNodeEnr } from '@aztec/p2p/test-helpers';
import { tryStop } from '@aztec/stdlib/interfaces/server';
import { SlashFactoryContract } from '@aztec/stdlib/l1-contracts';
import { TopicType } from '@aztec/stdlib/p2p';
import type { PublicDataTreeLeaf } from '@aztec/stdlib/trees';
import { ZkPassportProofParams } from '@aztec/stdlib/zkpassport';
import { getGenesisValues } from '@aztec/world-state/testing';
Expand Down Expand Up @@ -431,6 +432,27 @@ export class P2PNetworkTest {
);

this.logger.warn('All nodes connected to P2P mesh');

// Wait for GossipSub mesh to form for the tx topic.
// 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.
this.logger.warn('Waiting for GossipSub mesh to form for tx topic...');
await Promise.all(
nodes.map(async (node, index) => {
const p2p = node.getP2P();
await retryUntil(
async () => {
const meshPeers = await p2p.getGossipMeshPeerCount(TopicType.tx);
this.logger.debug(`Node ${index} has ${meshPeers} gossip mesh peers for tx topic`);
return meshPeers >= 1 ? true : undefined;
},
`Node ${index} to have gossip mesh peers for tx topic`,
timeoutSeconds,
checkIntervalSeconds,
);
}),
);
this.logger.warn('All nodes have gossip mesh peers for tx topic');
}

async teardown() {
Expand Down
11 changes: 10 additions & 1 deletion yarn-project/p2p/src/client/interface.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import type { SlotNumber } from '@aztec/foundation/branded-types';
import type { EthAddress, L2BlockId } from '@aztec/stdlib/block';
import type { ITxProvider, P2PApiFull } from '@aztec/stdlib/interfaces/server';
import type { BlockProposal, CheckpointAttestation, CheckpointProposal, P2PClientType } from '@aztec/stdlib/p2p';
import type {
BlockProposal,
CheckpointAttestation,
CheckpointProposal,
P2PClientType,
TopicType,
} from '@aztec/stdlib/p2p';
import type { BlockHeader, Tx, TxHash } from '@aztec/stdlib/tx';

import type { PeerId } from '@libp2p/interface';
Expand Down Expand Up @@ -237,4 +243,7 @@ export type P2P<T extends P2PClientType = P2PClientType.Full> = P2PApiFull<T> &

/** If node running this P2P stack is validator, passes in validator address to P2P layer */
registerThisValidatorAddresses(address: EthAddress[]): void;

/** Returns the number of peers in the GossipSub mesh for a given topic type. */
getGossipMeshPeerCount(topicType: TopicType): Promise<number>;
};
5 changes: 5 additions & 0 deletions yarn-project/p2p/src/client/p2p_client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
CheckpointAttestation,
type CheckpointProposal,
type P2PClientType,
type TopicType,
} from '@aztec/stdlib/p2p';
import type { BlockHeader, Tx, TxHash } from '@aztec/stdlib/tx';
import { Attributes, type TelemetryClient, WithTracer, getTelemetryClient, trackSpan } from '@aztec/telemetry-client';
Expand Down Expand Up @@ -167,6 +168,10 @@ export class P2PClient<T extends P2PClientType = P2PClientType.Full>
return Promise.resolve(this.p2pService.getPeers(includePending));
}

public getGossipMeshPeerCount(topicType: TopicType): Promise<number> {
return Promise.resolve(this.p2pService.getGossipMeshPeerCount(topicType));
}

public getL2BlockHash(number: BlockNumber): Promise<string | undefined> {
return this.l2Tips.getL2BlockHash(number);
}
Expand Down
6 changes: 5 additions & 1 deletion yarn-project/p2p/src/services/dummy_service.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { EthAddress } from '@aztec/foundation/eth-address';
import type { PeerInfo } from '@aztec/stdlib/interfaces/server';
import type { Gossipable, PeerErrorSeverity } from '@aztec/stdlib/p2p';
import type { Gossipable, PeerErrorSeverity, TopicType } from '@aztec/stdlib/p2p';
import { Tx, TxHash } from '@aztec/stdlib/tx';

import type { PeerId } from '@libp2p/interface';
Expand Down Expand Up @@ -44,6 +44,10 @@ export class DummyP2PService implements P2PService {
return [];
}

getGossipMeshPeerCount(_topicType: TopicType): number {
return 0;
}

/**
* Starts the dummy implementation.
* @returns A resolved promise.
Expand Down
4 changes: 4 additions & 0 deletions yarn-project/p2p/src/services/libp2p/libp2p_service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -615,6 +615,10 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
return this.peerManager.getPeers(includePending);
}

public getGossipMeshPeerCount(topicType: TopicType): number {
return this.node.services.pubsub.getMeshPeers(this.topicStrings[topicType]).length;
}

private handleGossipSubEvent(e: CustomEvent<GossipsubMessage>) {
this.logger.trace(`Received PUBSUB message.`);

Expand Down
11 changes: 10 additions & 1 deletion yarn-project/p2p/src/services/service.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import type { SlotNumber } from '@aztec/foundation/branded-types';
import type { EthAddress } from '@aztec/foundation/eth-address';
import type { PeerInfo } from '@aztec/stdlib/interfaces/server';
import type { BlockProposal, CheckpointAttestation, CheckpointProposalCore, Gossipable } from '@aztec/stdlib/p2p';
import type {
BlockProposal,
CheckpointAttestation,
CheckpointProposalCore,
Gossipable,
TopicType,
} from '@aztec/stdlib/p2p';
import type { Tx } from '@aztec/stdlib/tx';

import type { PeerId } from '@libp2p/interface';
Expand Down Expand Up @@ -130,6 +136,9 @@ export interface P2PService {

getPeers(includePending?: boolean): PeerInfo[];

/** Returns the number of peers in the GossipSub mesh for a given topic type. */
getGossipMeshPeerCount(topicType: TopicType): number;

validate(txs: Tx[]): Promise<void>;

addReqRespSubProtocol(
Expand Down
10 changes: 10 additions & 0 deletions yarn-project/p2p/src/test-helpers/mock-pubsub.ts
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,16 @@ class MockGossipSubService extends TypedEventEmitter<GossipsubEvents> implements
{ msgId, propagationSource, acceptance },
);
}

getMeshPeers(topic?: TopicStr): PeerIdStr[] {
if (topic && !this.subscribedTopics.has(topic)) {
return [];
}
return this.network
.getPeers()
.filter(peer => !this.peerId.equals(peer))
.map(peer => peer.toString());
}
}

/**
Expand Down
8 changes: 7 additions & 1 deletion yarn-project/p2p/src/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,13 @@ export interface PubSubLibp2p extends Pick<Libp2p, 'status' | 'start' | 'stop' |
services: {
pubsub: Pick<
GossipSub,
'addEventListener' | 'removeEventListener' | 'publish' | 'subscribe' | 'reportMessageValidationResult' | 'direct'
| 'addEventListener'
| 'removeEventListener'
| 'publish'
| 'subscribe'
| 'reportMessageValidationResult'
| 'direct'
| 'getMeshPeers'
> & { score: Pick<GossipSub['score'], 'score'> };
};
}
Expand Down
6 changes: 5 additions & 1 deletion yarn-project/txe/src/state_machine/dummy_p2p_client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import type {
} from '@aztec/p2p';
import type { EthAddress, L2BlockStreamEvent, L2Tips } from '@aztec/stdlib/block';
import type { ITxProvider, PeerInfo } from '@aztec/stdlib/interfaces/server';
import type { BlockProposal, CheckpointAttestation, CheckpointProposal } from '@aztec/stdlib/p2p';
import type { BlockProposal, CheckpointAttestation, CheckpointProposal, TopicType } from '@aztec/stdlib/p2p';
import type { BlockHeader, Tx, TxHash } from '@aztec/stdlib/tx';

export class DummyP2P implements P2P {
Expand All @@ -41,6 +41,10 @@ export class DummyP2P implements P2P {
throw new Error('DummyP2P does not implement "getPeers"');
}

public getGossipMeshPeerCount(_topicType: TopicType): Promise<number> {
return Promise.resolve(0);
}

public broadcastProposal(_proposal: BlockProposal): Promise<void> {
throw new Error('DummyP2P does not implement "broadcastProposal"');
}
Expand Down
Loading