diff --git a/yarn-project/p2p/src/client/p2p_client.integration.test.ts b/yarn-project/p2p/src/client/p2p_client.integration.test.ts index f6f939b82428..643eb432e95f 100644 --- a/yarn-project/p2p/src/client/p2p_client.integration.test.ts +++ b/yarn-project/p2p/src/client/p2p_client.integration.test.ts @@ -1,20 +1,27 @@ // An integration test for the p2p client to test req resp protocols import type { EpochCache } from '@aztec/epoch-cache'; +import { Secp256k1Signer } from '@aztec/foundation/crypto'; +import { Fr } from '@aztec/foundation/fields'; +import { createLogger } from '@aztec/foundation/log'; +import { type PromiseWithResolvers, promiseWithResolvers } from '@aztec/foundation/promise'; import { sleep } from '@aztec/foundation/sleep'; import { emptyChainConfig } from '@aztec/stdlib/config'; import type { WorldStateSynchronizer } from '@aztec/stdlib/interfaces/server'; -import { PeerErrorSeverity } from '@aztec/stdlib/p2p'; -import { mockTx } from '@aztec/stdlib/testing'; -import type { Tx } from '@aztec/stdlib/tx'; +import { BlockAttestation, BlockProposal, PeerErrorSeverity } from '@aztec/stdlib/p2p'; +import { type MakeConsensusPayloadOptions, makeBlockProposal, makeHeader, mockTx } from '@aztec/stdlib/testing'; +import { Tx, TxHash } from '@aztec/stdlib/tx'; import { describe, expect, it, jest } from '@jest/globals'; +import type { Message, PeerId } from '@libp2p/interface'; import { type MockProxy, mock } from 'jest-mock-extended'; import type { P2PClient } from '../client/p2p_client.js'; import { type P2PConfig, getP2PDefaultConfig } from '../config.js'; import type { AttestationPool } from '../mem_pools/attestation_pool/attestation_pool.js'; +import { mockAttestation } from '../mem_pools/attestation_pool/mocks.js'; import type { TxPool } from '../mem_pools/tx_pool/index.js'; -import { makeTestP2PClients } from '../test-helpers/make-test-p2p-clients.js'; +import type { LibP2PService } from '../services/libp2p/libp2p_service.js'; +import { makeTestP2PClient, makeTestP2PClients } from '../test-helpers/make-test-p2p-clients.js'; const TEST_TIMEOUT = 80000; @@ -56,19 +63,72 @@ describe('p2p client integration', () => { clients = []; }; + // Replace the tx handler on a client + const replaceTxHandler = (client: P2PClient, promise: PromiseWithResolvers) => { + const p2pService = (client as any).p2pService as LibP2PService; + // @ts-expect-error - we want to spy on received tx handler + const oldTxHandler = p2pService.handleGossipedTx.bind(p2pService); + + // Mock the function to just call the old one + const handleGossipedTxSpy = jest.fn(async (msg: Message, msgId: string, source: PeerId) => { + promise.resolve(Tx.fromBuffer(Buffer.from(msg.data))); + await oldTxHandler(msg, msgId, source); + }); + // @ts-expect-error - replace with our own handler + p2pService.handleGossipedTx = handleGossipedTxSpy; + + return handleGossipedTxSpy; + }; + + // Replace the block proposal handler on a client + const replaceBlockProposalHandler = (client: P2PClient, promise: PromiseWithResolvers) => { + const p2pService = (client as any).p2pService as LibP2PService; + // @ts-expect-error - we want to spy on received proposal handler + const oldProposalHandler = p2pService.processBlockFromPeer.bind(p2pService); + + // Mock the function to just call the old one + const handleGossipedProposalSpy = jest.fn(async (msg: Message, msgId: string, source: PeerId) => { + promise.resolve(BlockProposal.fromBuffer(Buffer.from(msg.data))); + await oldProposalHandler(msg, msgId, source); + }); + // @ts-expect-error - replace with our own handler + p2pService.processBlockFromPeer = handleGossipedProposalSpy; + + return handleGossipedProposalSpy; + }; + + // Replace the block attestation handler on a client + const replaceBlockAttestationHandler = (client: P2PClient, promise: PromiseWithResolvers) => { + const p2pService = (client as any).p2pService as LibP2PService; + // @ts-expect-error - we want to spy on received attestation handler + const oldAttestationHandler = p2pService.processAttestationFromPeer.bind(p2pService); + + // Mock the function to just call the old one + const handleGossipedAttestationSpy = jest.fn(async (msg: Message, msgId: string, source: PeerId) => { + promise.resolve(BlockAttestation.fromBuffer(Buffer.from(msg.data))); + await oldAttestationHandler(msg, msgId, source); + }); + // @ts-expect-error - replace with our own handler + p2pService.processAttestationFromPeer = handleGossipedAttestationSpy; + + return handleGossipedAttestationSpy; + }; + describe('Req Resp', () => { it( 'Returns undefined if unable to find a transaction from another peer', async () => { // We want to create a set of nodes and request transaction from them // Not using a before each as a the wind down is not working as expected - clients = await makeTestP2PClients(NUMBER_OF_PEERS, { - p2pBaseConfig: { ...emptyChainConfig, ...getP2PDefaultConfig() }, - mockAttestationPool: attestationPool, - mockTxPool: txPool, - mockEpochCache: epochCache, - mockWorldState: worldState, - }); + clients = ( + await makeTestP2PClients(NUMBER_OF_PEERS, { + p2pBaseConfig: { ...emptyChainConfig, ...getP2PDefaultConfig() }, + mockAttestationPool: attestationPool, + mockTxPool: txPool, + mockEpochCache: epochCache, + mockWorldState: worldState, + }) + ).map(x => x.client); const [client1] = clients; await sleep(2000); @@ -90,13 +150,15 @@ describe('p2p client integration', () => { 'Can request a transaction from another peer', async () => { // We want to create a set of nodes and request transaction from them - clients = await makeTestP2PClients(NUMBER_OF_PEERS, { - p2pBaseConfig, - mockAttestationPool: attestationPool, - mockTxPool: txPool, - mockEpochCache: epochCache, - mockWorldState: worldState, - }); + clients = ( + await makeTestP2PClients(NUMBER_OF_PEERS, { + p2pBaseConfig, + mockAttestationPool: attestationPool, + mockTxPool: txPool, + mockEpochCache: epochCache, + mockWorldState: worldState, + }) + ).map(x => x.client); const [client1] = clients; // Give the nodes time to discover each other @@ -122,14 +184,16 @@ describe('p2p client integration', () => { 'Will penalize peers that send invalid proofs', async () => { // We want to create a set of nodes and request transaction from them - clients = await makeTestP2PClients(NUMBER_OF_PEERS, { - p2pBaseConfig, - mockAttestationPool: attestationPool, - mockTxPool: txPool, - mockEpochCache: epochCache, - mockWorldState: worldState, - alwaysTrueVerifier: false, - }); + clients = ( + await makeTestP2PClients(NUMBER_OF_PEERS, { + p2pBaseConfig, + mockAttestationPool: attestationPool, + mockTxPool: txPool, + mockEpochCache: epochCache, + mockWorldState: worldState, + alwaysTrueVerifier: false, + }) + ).map(x => x.client); const [client1, client2] = clients; const client2PeerId = await client2.getEnr()!.peerId(); @@ -161,14 +225,16 @@ describe('p2p client integration', () => { 'Will penalize peers that send the wrong transaction', async () => { // We want to create a set of nodes and request transaction from them - clients = await makeTestP2PClients(NUMBER_OF_PEERS, { - p2pBaseConfig, - mockAttestationPool: attestationPool, - mockTxPool: txPool, - mockEpochCache: epochCache, - mockWorldState: worldState, - alwaysTrueVerifier: true, - }); + clients = ( + await makeTestP2PClients(NUMBER_OF_PEERS, { + p2pBaseConfig, + mockAttestationPool: attestationPool, + mockTxPool: txPool, + mockEpochCache: epochCache, + mockWorldState: worldState, + alwaysTrueVerifier: true, + }) + ).map(x => x.client); const [client1, client2] = clients; const client2PeerId = (await client2.getEnr()?.peerId())!; @@ -196,5 +262,226 @@ describe('p2p client integration', () => { }, TEST_TIMEOUT, ); + + // Test creates 3 nodes. Node 1 sends all message types to others. + // Test confirms that nodes 2 and 3 receive all messages. + // Then nodes 2 and 3 are restarted, node 3 is restarted at a new version + // Again, node 1 sends all message types. + // Test confirms that node 2 receives all messages, node 3 receives none. + it( + 'Will propagate messages to peers at the same version', + async () => { + // Create a set of nodes, client 1 will send a messages to other peers + const numberOfNodes = 3; + // We start at rollup version 1 + const testConfig = { + p2pBaseConfig: { ...p2pBaseConfig, rollupVersion: 1 }, + mockAttestationPool: attestationPool, + mockTxPool: txPool, + mockEpochCache: epochCache, + mockWorldState: worldState, + alwaysTrueVerifier: true, + }; + + const clientsAndConfig = await makeTestP2PClients(numberOfNodes, testConfig); + const [client1, client2, client3] = clientsAndConfig; + + // Give the nodes time to discover each other + await sleep(6000); + + // Client 1 sends a tx a block proposal and an attestation and both clients 2 and 3 should receive them + { + const client2TxPromise = promiseWithResolvers(); + const client2ProposalPromise = promiseWithResolvers(); + const client2AttestationPromise = promiseWithResolvers(); + + const client3TxPromise = promiseWithResolvers(); + const client3ProposalPromise = promiseWithResolvers(); + const client3AttestationPromise = promiseWithResolvers(); + + // Replace the handlers on clients 2 and 3 so we can detect when they receive messages + const client2HandleGossipedTxSpy = replaceTxHandler(client2.client, client2TxPromise); + const client2HandleGossipedProposalSpy = replaceBlockProposalHandler(client2.client, client2ProposalPromise); + const client2HandleGossipedAttestationSpy = replaceBlockAttestationHandler( + client2.client, + client2AttestationPromise, + ); + + const client3HandleGossipedTxSpy = replaceTxHandler(client3.client, client3TxPromise); + const client3HandleGossipedProposalSpy = replaceBlockProposalHandler(client3.client, client3ProposalPromise); + const client3HandleGossipedAttestationSpy = replaceBlockAttestationHandler( + client3.client, + client3AttestationPromise, + ); + + // Client 1 sends a transaction, it should propagate to clients 2 and 3 + const tx = await mockTx(); + await client1.client.sendTx(tx); + + // Client 1 sends a block proposal + const dummyPayload: MakeConsensusPayloadOptions = { + signer: Secp256k1Signer.random(), + header: makeHeader(), + archive: Fr.random(), + txHashes: [TxHash.random()], + }; + const blockProposal = await makeBlockProposal(dummyPayload); + client1.client.broadcastProposal(blockProposal); + + // client 1 sends an attestation + const attestation = await mockAttestation( + Secp256k1Signer.random(), + Number(dummyPayload.header!.getSlot()), + dummyPayload.archive, + dummyPayload.txHashes, + ); + await (client1.client as any).p2pService.broadcastAttestation(attestation); + + // Clients 2 and 3 should receive all messages + const messagesPromise = Promise.all([ + client2TxPromise.promise, + client3TxPromise.promise, + client2ProposalPromise.promise, + client3ProposalPromise.promise, + client2AttestationPromise.promise, + client3AttestationPromise.promise, + ]); + const messages = await Promise.race([messagesPromise, sleep(2000).then(() => undefined)]); + expect(messages).toBeDefined(); + expect(client2HandleGossipedTxSpy).toHaveBeenCalled(); + expect(client2HandleGossipedProposalSpy).toHaveBeenCalled(); + expect(client2HandleGossipedAttestationSpy).toHaveBeenCalled(); + expect(client3HandleGossipedTxSpy).toHaveBeenCalled(); + expect(client3HandleGossipedProposalSpy).toHaveBeenCalled(); + expect(client3HandleGossipedAttestationSpy).toHaveBeenCalled(); + + if (messages) { + const hashes = await Promise.all([tx, messages[0], messages[1]].map(tx => tx!.getTxHash())); + expect(hashes[0].toString()).toEqual(hashes[1].toString()); + expect(hashes[0].toString()).toEqual(hashes[2].toString()); + + expect(messages[2].payload.toString()).toEqual(blockProposal.payload.toString()); + expect(messages[3].payload.toString()).toEqual(blockProposal.payload.toString()); + expect(messages[4].payload.toString()).toEqual(attestation.payload.toString()); + expect(messages[5].payload.toString()).toEqual(attestation.payload.toString()); + } + } + + // Now stop clients 2 and 3 + await Promise.all([client2.client.stop(), client3.client.stop()]); + + // We set client 3 to rollup version 2 + const newP2PConfig: P2PConfig = { + ...testConfig.p2pBaseConfig, + rollupVersion: 2, + }; + + // We re-create client 2 as before, but client 3 moves to a new rollup version + const newEnrs = [client1.enr, client2.enr, client3.enr]; + const newClient2 = await makeTestP2PClient(client2.peerPrivateKey, client2.port, newEnrs, { + ...testConfig, + logger: createLogger(`p2p:new-client-2`), + }); + const newClient3 = await makeTestP2PClient(client3.peerPrivateKey, client3.port, newEnrs, { + ...testConfig, + p2pBaseConfig: newP2PConfig, + logger: createLogger(`p2p:new-client-3`), + }); + + // Give everyone time to connect again + await sleep(5000); + + // Client 1 sends a tx a block proposal and an attestation and only client 2 should receive them, client 3 is now on a new version + { + const client2TxPromise = promiseWithResolvers(); + const client2ProposalPromise = promiseWithResolvers(); + const client2AttestationPromise = promiseWithResolvers(); + + const client3TxPromise = promiseWithResolvers(); + const client3ProposalPromise = promiseWithResolvers(); + const client3AttestationPromise = promiseWithResolvers(); + + // Replace the handlers on clients 2 and 3 so we can detect when they receive messages + const client2HandleGossipedTxSpy = replaceTxHandler(newClient2, client2TxPromise); + const client2HandleGossipedProposalSpy = replaceBlockProposalHandler(newClient2, client2ProposalPromise); + const client2HandleGossipedAttestationSpy = replaceBlockAttestationHandler( + newClient2, + client2AttestationPromise, + ); + + const client3HandleGossipedTxSpy = replaceTxHandler(newClient3, client3TxPromise); + const client3HandleGossipedProposalSpy = replaceBlockProposalHandler(newClient3, client3ProposalPromise); + const client3HandleGossipedAttestationSpy = replaceBlockAttestationHandler( + newClient3, + client3AttestationPromise, + ); + + // Client 1 sends a transaction, it should propagate to clients 2 and 3 + const tx = await mockTx(); + await client1.client.sendTx(tx); + + // Client 1 sends a block proposal + const dummyPayload: MakeConsensusPayloadOptions = { + signer: Secp256k1Signer.random(), + header: makeHeader(), + archive: Fr.random(), + txHashes: [TxHash.random()], + }; + const blockProposal = await makeBlockProposal(dummyPayload); + client1.client.broadcastProposal(blockProposal); + + // client 1 sends an attestation + const attestation = await mockAttestation( + Secp256k1Signer.random(), + Number(dummyPayload.header!.getSlot()), + dummyPayload.archive, + dummyPayload.txHashes, + ); + await (client1.client as any).p2pService.broadcastAttestation(attestation); + + // Only client 2 should receive the messages + const client2MessagesPromises = Promise.all([ + client2TxPromise.promise, + client2ProposalPromise.promise, + client2AttestationPromise.promise, + ]); + + // We use Promise.any as no messages should be received + const client3MessagesPromises = Promise.any([ + client3TxPromise.promise, + client3ProposalPromise.promise, + client3AttestationPromise.promise, + ]); + + const [client2Messages, client3Messages] = await Promise.all([ + Promise.race([client2MessagesPromises, sleep(2000).then(() => undefined)]), + Promise.race([client3MessagesPromises, sleep(2000).then(() => undefined)]), + ]); + + // We expect that all messages were received by client 2 + expect(client2Messages).toBeDefined(); + expect(client2HandleGossipedTxSpy).toHaveBeenCalled(); + expect(client2HandleGossipedProposalSpy).toHaveBeenCalled(); + expect(client2HandleGossipedAttestationSpy).toHaveBeenCalled(); + + if (client2Messages) { + const hashes = await Promise.all([tx, client2Messages[0]].map(tx => tx!.getTxHash())); + expect(hashes[0].toString()).toEqual(hashes[1].toString()); + + expect(client2Messages[1].payload.toString()).toEqual(blockProposal.payload.toString()); + expect(client2Messages[2].payload.toString()).toEqual(attestation.payload.toString()); + } + + // We expect that no messages were received by client 3 + expect(client3Messages).toBeUndefined(); + expect(client3HandleGossipedTxSpy).not.toHaveBeenCalled(); + expect(client3HandleGossipedProposalSpy).not.toHaveBeenCalled(); + expect(client3HandleGossipedAttestationSpy).not.toHaveBeenCalled(); + } + + await shutdown([client1.client, newClient2, newClient3]); + }, + TEST_TIMEOUT, + ); }); }); diff --git a/yarn-project/p2p/src/services/libp2p/libp2p_service.ts b/yarn-project/p2p/src/services/libp2p/libp2p_service.ts index 888260fa36f3..50c11144a046 100644 --- a/yarn-project/p2p/src/services/libp2p/libp2p_service.ts +++ b/yarn-project/p2p/src/services/libp2p/libp2p_service.ts @@ -15,12 +15,14 @@ import { type Gossipable, P2PClientType, PeerErrorSeverity, - TopicTypeMap, + TopicType, + createTopicString, getTopicTypeForClientType, metricsTopicStrToLabels, } from '@aztec/stdlib/p2p'; import { DatabasePublicStateSource, MerkleTreeId } from '@aztec/stdlib/trees'; import { Tx, type TxHash, type TxValidationResult } from '@aztec/stdlib/tx'; +import { compressComponentVersions } from '@aztec/stdlib/versioning'; import { Attributes, OtelMetricsAdapter, type TelemetryClient, WithTracer, trackSpan } from '@aztec/telemetry-client'; import type { ENR } from '@chainsafe/enr'; @@ -57,6 +59,7 @@ import { } from '../../msg_validators/tx_validator/index.js'; import { GossipSubEvent } from '../../types/index.js'; import { type PubSubLibp2p, convertToMultiaddr } from '../../util.js'; +import { getVersions } from '../../versioning.js'; import { AztecDatastore } from '../data_store.js'; import { SnappyTransform, fastMsgIdFn, getMsgIdFn, msgIdToStrFn } from '../encoding.js'; import { gossipScoreThresholds } from '../gossipsub/scoring.js'; @@ -95,6 +98,9 @@ export class LibP2PService extends private attestationValidator: AttestationValidator; private blockProposalValidator: BlockProposalValidator; + private protocolVersion = ''; + private topicStrings: Record = {} as Record; + // Request and response sub service public reqresp: ReqResp; @@ -127,6 +133,17 @@ export class LibP2PService extends ) { super(telemetry, 'LibP2PService'); + const versions = getVersions(config); + this.protocolVersion = compressComponentVersions(versions); + logger.info(`Started libp2p service with protocol version ${this.protocolVersion}`); + + this.topicStrings[TopicType.tx] = createTopicString(TopicType.tx, this.protocolVersion); + this.topicStrings[TopicType.block_proposal] = createTopicString(TopicType.block_proposal, this.protocolVersion); + this.topicStrings[TopicType.block_attestation] = createTopicString( + TopicType.block_attestation, + this.protocolVersion, + ); + const peerScoring = new PeerScoring(config); this.reqresp = new ReqResp(config, node, peerScoring); @@ -198,6 +215,13 @@ export class LibP2PService extends peerDiscovery.push(bootstrap({ list: bootstrapNodes })); } + const versions = getVersions(config); + const protocolVersion = compressComponentVersions(versions); + + const txTopic = createTopicString(TopicType.tx, protocolVersion); + const blockProposalTopic = createTopicString(TopicType.block_proposal, protocolVersion); + const blockAttestationTopic = createTopicString(TopicType.block_attestation, protocolVersion); + const node = await createLibp2p({ start: false, peerId, @@ -251,24 +275,24 @@ export class LibP2PService extends fastMsgIdFn: fastMsgIdFn, dataTransform: new SnappyTransform(), metricsRegister: otelMetricsAdapter, - metricsTopicStrToLabel: metricsTopicStrToLabels(), + metricsTopicStrToLabel: metricsTopicStrToLabels(protocolVersion), asyncValidation: true, scoreThresholds: gossipScoreThresholds, scoreParams: createPeerScoreParams({ // IPColocation factor can be disabled for local testing - default to -5 IPColocationFactorWeight: config.debugDisableColocationPenalty ? 0 : -5.0, topics: { - [Tx.p2pTopic]: createTopicScoreParams({ + [txTopic]: createTopicScoreParams({ topicWeight: 1, invalidMessageDeliveriesWeight: -20, invalidMessageDeliveriesDecay: 0.5, }), - [BlockAttestation.p2pTopic]: createTopicScoreParams({ + [blockAttestationTopic]: createTopicScoreParams({ topicWeight: 1, invalidMessageDeliveriesWeight: -20, invalidMessageDeliveriesDecay: 0.5, }), - [BlockProposal.p2pTopic]: createTopicScoreParams({ + [blockProposalTopic]: createTopicScoreParams({ topicWeight: 1, invalidMessageDeliveriesWeight: -20, invalidMessageDeliveriesDecay: 0.5, @@ -324,7 +348,7 @@ export class LibP2PService extends // Subscribe to standard GossipSub topics by default for (const topic of getTopicTypeForClientType(this.clientType)) { - this.subscribeToTopic(TopicTypeMap[topic].p2pTopic); + this.subscribeToTopic(this.topicStrings[topic]); } // Create request response protocol handlers @@ -483,13 +507,13 @@ export class LibP2PService extends * @param data - The message data */ protected async handleNewGossipMessage(msg: Message, msgId: string, source: PeerId) { - if (msg.topic === Tx.p2pTopic) { + if (msg.topic === this.topicStrings[TopicType.tx]) { await this.handleGossipedTx(msg, msgId, source); } - if (msg.topic === BlockAttestation.p2pTopic && this.clientType === P2PClientType.Full) { + if (msg.topic === this.topicStrings[TopicType.block_attestation] && this.clientType === P2PClientType.Full) { await this.processAttestationFromPeer(msg, msgId, source); } - if (msg.topic == BlockProposal.p2pTopic) { + if (msg.topic === this.topicStrings[TopicType.block_proposal]) { await this.processBlockFromPeer(msg, msgId, source); } @@ -901,7 +925,7 @@ export class LibP2PService extends const identifier = await message.p2pMessageIdentifier().then(i => i.toString()); this.logger.trace(`Sending message ${identifier}`, { p2pMessageIdentifier: identifier }); - const recipientsNum = await this.publishToTopic(parent.p2pTopic, message.toBuffer()); + const recipientsNum = await this.publishToTopic(this.topicStrings[parent.p2pTopic], message.toBuffer()); this.logger.debug(`Sent message ${identifier} to ${recipientsNum} peers`, { p2pMessageIdentifier: identifier, sourcePeer: this.node.peerId.toString(), diff --git a/yarn-project/p2p/src/test-helpers/make-test-p2p-clients.ts b/yarn-project/p2p/src/test-helpers/make-test-p2p-clients.ts index 1c369c49ba8e..916d2156b9da 100644 --- a/yarn-project/p2p/src/test-helpers/make-test-p2p-clients.ts +++ b/yarn-project/p2p/src/test-helpers/make-test-p2p-clients.ts @@ -1,6 +1,7 @@ import { MockL2BlockSource } from '@aztec/archiver/test'; import type { EpochCache } from '@aztec/epoch-cache'; import { type Logger, createLogger } from '@aztec/foundation/log'; +import { sleep } from '@aztec/foundation/sleep'; import type { DataStoreConfig } from '@aztec/kv-store/config'; import { openTmpStore } from '@aztec/kv-store/lmdb-v2'; import type { WorldStateSynchronizer } from '@aztec/stdlib/interfaces/server'; @@ -57,6 +58,7 @@ export async function makeTestP2PClient( p2pEnabled: true, peerIdPrivateKey, p2pIp: `127.0.0.1`, + listenAddress: `127.0.0.1`, p2pPort: port, bootstrapNodes: peers, peerCheckIntervalMS: 1000, @@ -101,7 +103,16 @@ export async function makeTestP2PClients(numberOfPeers: number, testConfig: Make const clients: P2PClient[] = []; const peerIdPrivateKeys = generatePeerIdPrivateKeys(numberOfPeers); - const ports = await getPorts(numberOfPeers); + let ports = []; + while (true) { + try { + ports = await getPorts(numberOfPeers); + break; + } catch (err) { + await sleep(1000); + } + } + const peerEnrs = await makeEnrs(peerIdPrivateKeys, ports, testConfig.p2pBaseConfig); for (let i = 0; i < numberOfPeers; i++) { @@ -113,5 +124,12 @@ export async function makeTestP2PClients(numberOfPeers: number, testConfig: Make } await Promise.all(clients.map(client => client.isReady())); - return clients; + return clients.map((client, index) => { + return { + client, + peerPrivateKey: peerIdPrivateKeys[index], + port: ports[index], + enr: peerEnrs[index], + }; + }); } diff --git a/yarn-project/stdlib/src/p2p/block_attestation.ts b/yarn-project/stdlib/src/p2p/block_attestation.ts index ae4832724ac4..acbc0c899c88 100644 --- a/yarn-project/stdlib/src/p2p/block_attestation.ts +++ b/yarn-project/stdlib/src/p2p/block_attestation.ts @@ -11,7 +11,7 @@ import type { ZodFor } from '../schemas/index.js'; import { ConsensusPayload } from './consensus_payload.js'; import { Gossipable } from './gossipable.js'; import { SignatureDomainSeparator, getHashedSignaturePayloadEthSignedMessage } from './signature_utils.js'; -import { TopicType, createTopicString } from './topic_type.js'; +import { TopicType } from './topic_type.js'; export class BlockAttestationHash extends Buffer32 { constructor(hash: Buffer) { @@ -26,7 +26,7 @@ export class BlockAttestationHash extends Buffer32 { * will produce a block attestation over the header of the block */ export class BlockAttestation extends Gossipable { - static override p2pTopic = createTopicString(TopicType.block_attestation); + static override p2pTopic = TopicType.block_attestation; private sender: EthAddress | undefined; diff --git a/yarn-project/stdlib/src/p2p/block_proposal.ts b/yarn-project/stdlib/src/p2p/block_proposal.ts index ca2c0106244d..96542ba33c85 100644 --- a/yarn-project/stdlib/src/p2p/block_proposal.ts +++ b/yarn-project/stdlib/src/p2p/block_proposal.ts @@ -12,7 +12,7 @@ import { getHashedSignaturePayload, getHashedSignaturePayloadEthSignedMessage, } from './signature_utils.js'; -import { TopicType, createTopicString } from './topic_type.js'; +import { TopicType } from './topic_type.js'; export class BlockProposalHash extends Buffer32 { constructor(hash: Buffer) { @@ -27,7 +27,7 @@ export class BlockProposalHash extends Buffer32 { * be included in the head of the chain */ export class BlockProposal extends Gossipable { - static override p2pTopic = createTopicString(TopicType.block_proposal); + static override p2pTopic = TopicType.block_proposal; private sender: EthAddress | undefined; diff --git a/yarn-project/stdlib/src/p2p/gossipable.ts b/yarn-project/stdlib/src/p2p/gossipable.ts index 7ab5b82ff05e..2eb9167349ff 100644 --- a/yarn-project/stdlib/src/p2p/gossipable.ts +++ b/yarn-project/stdlib/src/p2p/gossipable.ts @@ -1,5 +1,7 @@ import type { Buffer32 } from '@aztec/foundation/buffer'; +import type { TopicType } from './topic_type.js'; + /** * Gossipable * @@ -10,7 +12,7 @@ export abstract class Gossipable { * * - The p2p topic identifier, this determines how the message is handled */ - static p2pTopic: string; + static p2pTopic: TopicType; /** p2p Message Identifier * diff --git a/yarn-project/stdlib/src/p2p/interface.ts b/yarn-project/stdlib/src/p2p/interface.ts index 6be3f930af68..37fb79497d19 100644 --- a/yarn-project/stdlib/src/p2p/interface.ts +++ b/yarn-project/stdlib/src/p2p/interface.ts @@ -1,28 +1,4 @@ -import { Tx } from '../tx/tx.js'; -import { BlockAttestation } from './block_attestation.js'; -import { BlockProposal } from './block_proposal.js'; -import type { Gossipable } from './gossipable.js'; -import { TopicType } from './topic_type.js'; - export interface RawGossipMessage { topic: string; data: Uint8Array; } - -// Force casts as we know that each field here extends Gossipable, and we just want types from Gossipable -export const TopicTypeMap: Record = { - [TopicType.tx]: Tx as unknown as typeof Gossipable, - [TopicType.block_proposal]: BlockProposal as unknown as typeof Gossipable, - [TopicType.block_attestation]: BlockAttestation as unknown as typeof Gossipable, -}; - -/** - * Map from topic to deserialiser - * - * Used in msgIdFn libp2p to get the p2pMessageIdentifier from a message - */ -export const TopicToDeserializer = { - [Tx.p2pTopic]: Tx.fromBuffer, - [BlockProposal.p2pTopic]: BlockProposal.fromBuffer, - [BlockAttestation.p2pTopic]: BlockAttestation.fromBuffer, -}; diff --git a/yarn-project/stdlib/src/p2p/topic_type.ts b/yarn-project/stdlib/src/p2p/topic_type.ts index fca25ea31dac..cf43cbb1f074 100644 --- a/yarn-project/stdlib/src/p2p/topic_type.ts +++ b/yarn-project/stdlib/src/p2p/topic_type.ts @@ -6,8 +6,8 @@ import { P2PClientType } from './client_type.js'; * @param topicType * @returns */ -export function createTopicString(topicType: TopicType) { - return '/aztec/' + topicType + '/0.1.0'; +export function createTopicString(topicType: TopicType, protocolVersion: string) { + return `/aztec/${TopicType[topicType]}/${protocolVersion}`; } /** @@ -35,10 +35,10 @@ export function getTopicTypeForClientType(clientType: P2PClientType) { * ... * } */ -export function metricsTopicStrToLabels() { +export function metricsTopicStrToLabels(protocolVersion: string) { const topicStrToLabel = new Map(); for (const topic in TopicType) { - topicStrToLabel.set(createTopicString(TopicType[topic as keyof typeof TopicType]), topic); + topicStrToLabel.set(createTopicString(TopicType[topic as keyof typeof TopicType], protocolVersion), topic); } return topicStrToLabel; diff --git a/yarn-project/stdlib/src/tx/tx.ts b/yarn-project/stdlib/src/tx/tx.ts index d26cf633af82..3e0420f2212e 100644 --- a/yarn-project/stdlib/src/tx/tx.ts +++ b/yarn-project/stdlib/src/tx/tx.ts @@ -15,7 +15,7 @@ import type { ScopedLogHash } from '../kernel/log_hash.js'; import { PrivateKernelTailCircuitPublicInputs } from '../kernel/private_kernel_tail_circuit_public_inputs.js'; import { ContractClassLog } from '../logs/contract_class_log.js'; import { Gossipable } from '../p2p/gossipable.js'; -import { TopicType, createTopicString } from '../p2p/topic_type.js'; +import { TopicType } from '../p2p/topic_type.js'; import { ClientIvcProof } from '../proofs/client_ivc_proof.js'; import type { TxStats } from '../stats/stats.js'; import { HashedValues } from './hashed_values.js'; @@ -26,7 +26,7 @@ import { TxHash } from './tx_hash.js'; * The interface of an L2 transaction. */ export class Tx extends Gossipable { - static override p2pTopic: string; + static override p2pTopic = TopicType.tx; // For memoization private txHash: TxHash | undefined; private calldataMap: Map | undefined; @@ -53,11 +53,6 @@ export class Tx extends Gossipable { super(); } - // Gossipable method - static { - this.p2pTopic = createTopicString(TopicType.tx); - } - // Gossipable method override async p2pMessageIdentifier(): Promise { return new Buffer32((await this.getTxHash()).toBuffer());