Skip to content
Merged
Show file tree
Hide file tree
Changes from 14 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
3 changes: 2 additions & 1 deletion yarn-project/foundation/src/config/env_var.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,8 @@ export type EnvVar =
| 'P2P_TX_POOL_KEEP_PROVEN_FOR'
| 'P2P_ATTESTATION_POOL_KEEP_FOR'
| 'P2P_ARCHIVED_TX_LIMIT'
| 'P2P_TRUSTED_PEERS'
| 'P2P_PRIVATE_PEERS'
| 'P2P_MAX_TX_POOL_SIZE'
| 'PEER_ID_PRIVATE_KEY'
| 'PEER_ID_PRIVATE_KEY_PATH'
Expand Down Expand Up @@ -218,5 +220,4 @@ export type EnvVar =
| 'K8S_POD_UID'
| 'K8S_NAMESPACE_NAME'
| 'CUSTOM_FORWARDER_CONTRACT_ADDRESS'
| 'P2P_TRUSTED_PEERS'
| 'FEE_ASSET_HANDLER_CONTRACT_ADDRESS';
14 changes: 13 additions & 1 deletion yarn-project/p2p/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,11 @@ export interface P2PConfig extends P2PReqRespConfig, ChainConfig {
*/
trustedPeers: string[];

/**
* A list of private peers.
*/
privatePeers: string[];

/**
* The maximum possible size of the P2P DB in KB. Overwrites the general dataStoreMapSizeKB.
*/
Expand Down Expand Up @@ -357,7 +362,14 @@ export const p2pConfigMappings: ConfigMappingsType<P2PConfig> = {
trustedPeers: {
env: 'P2P_TRUSTED_PEERS',
parseEnv: (val: string) => val.split(','),
description: 'A list of trusted peers ENRs. Separated by commas.',
description: 'A list of trusted peer ENRs that will always be persisted. Separated by commas.',
defaultValue: [],
},
privatePeers: {
env: 'P2P_PRIVATE_PEERS',
parseEnv: (val: string) => val.split(','),
description:
'A list of private peer ENRs that will always be persisted and not be used for discovery. Separated by commas.',
defaultValue: [],
},
p2pStoreMapSizeKb: {
Expand Down
17 changes: 16 additions & 1 deletion yarn-project/p2p/src/services/discv5/discV5_service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export class DiscV5Service extends EventEmitter implements PeerDiscoveryService

private bootstrapNodePeerIds: PeerId[] = [];
public bootstrapNodeEnrs: ENR[] = [];
private trustedPeerEnrs: ENR[] = [];

private startTime = 0;

Expand All @@ -51,8 +52,10 @@ export class DiscV5Service extends EventEmitter implements PeerDiscoveryService
configOverrides: Partial<IDiscv5CreateOptions> = {},
) {
super();
const { p2pIp, p2pPort, bootstrapNodes } = config;
const { p2pIp, p2pPort, bootstrapNodes, trustedPeers, privatePeers } = config;
this.bootstrapNodeEnrs = bootstrapNodes.map(x => ENR.decodeTxt(x));
const privatePeerEnrs = new Set(privatePeers);
this.trustedPeerEnrs = trustedPeers.filter(x => !privatePeerEnrs.has(x)).map(x => ENR.decodeTxt(x));
// create ENR from PeerId
this.enr = SignableENR.createFromPeerId(peerId);
// Add aztec identification to ENR
Expand Down Expand Up @@ -159,6 +162,18 @@ export class DiscV5Service extends EventEmitter implements PeerDiscoveryService
}
}
}

// Add trusted peer ENRs if provided
if (this.trustedPeerEnrs?.length) {
this.logger.info(
`Adding ${this.trustedPeerEnrs.length} trusted peer ENRs: ${this.trustedPeerEnrs
.map(enr => enr.encodeTxt())
.join(', ')}`,
);
for (const enr of this.trustedPeerEnrs) {
this.discv5.addEnr(enr);
}
}
}

public async runRandomNodesQuery(): Promise<void> {
Expand Down
111 changes: 109 additions & 2 deletions yarn-project/p2p/src/services/discv5/discv5_service.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,114 @@ describe('Discv5Service', () => {
await node2.stop();
});

const createNode = async (overrides: Partial<P2PConfig & IDiscv5CreateOptions> = {}) => {
it('should use trusted peers for discovery', async () => {
const node1 = await createNode({}, false);
const trustedNode = await createNode({}, false);
const trustedEnr = trustedNode.getEnr().encodeTxt();

const node2 = await createNode(
{
trustedPeers: [trustedEnr],
privatePeers: [],
},
false,
);
const node3 = await createNode(
{
trustedPeers: [trustedEnr],
privatePeers: [],
},
false,
);

await startNodes(node1, node2, node3, trustedNode);

expect(node1.getAllPeers()).toHaveLength(0);
expect(trustedNode.getAllPeers()).toHaveLength(0);

// Verify node2 and node3 are connected to the trusted peer
expect(node2.getAllPeers().length).toBe(1);
expect(node3.getAllPeers().length).toBe(1);
expect(await getPeers(node2)).toContain(trustedNode.getPeerId().toString());
expect(await getPeers(node3)).toContain(trustedNode.getPeerId().toString());

await Promise.all([
waitForPeers(node2, 2),
waitForPeers(node3, 2),
(async () => {
await sleep(2000); // wait for peer discovery to be able to start
for (let i = 0; i < 5; i++) {
await node1.runRandomNodesQuery();
await node2.runRandomNodesQuery();
await node3.runRandomNodesQuery();
await trustedNode.runRandomNodesQuery();
await sleep(100);
}
})(),
]);

expect(node1.getAllPeers()).toHaveLength(0);

// Verify node2 and node3 discovered each other through the trusted peer
const node2Peers = await getPeers(node2);
expect(node2Peers).toHaveLength(2);
expect(node2Peers).toContain(node3.getPeerId().toString());
const node3Peers = await getPeers(node3);
expect(node3Peers).toHaveLength(2);
expect(node3Peers).toContain(node2.getPeerId().toString());
const trustedNodePeers = await getPeers(trustedNode);
expect(trustedNodePeers).toHaveLength(2);
expect(trustedNodePeers).toContain(node2.getPeerId().toString());
expect(trustedNodePeers).toContain(node3.getPeerId().toString());

await stopNodes(node1, node2, node3, trustedNode);
});

it('should not use private peers or peers marked as both trusted and private for discovery', async () => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good test!

const node1 = await createNode({}, false);
const privateNode = await createNode({}, false);
const privateEnr = privateNode.getEnr().encodeTxt();

const node2 = await createNode(
{
trustedPeers: [],
privatePeers: [privateEnr],
},
false,
);
const node3 = await createNode(
{
trustedPeers: [privateEnr],
privatePeers: [privateEnr],
},
false,
);

await startNodes(node1, node2, node3, privateNode);

expect(node1.getAllPeers()).toHaveLength(0);
expect(node2.getAllPeers()).toHaveLength(0);
expect(node3.getAllPeers()).toHaveLength(0);
expect(privateNode.getAllPeers()).toHaveLength(0);

await sleep(2000); // wait for peer discovery to be able to start
for (let i = 0; i < 3; i++) {
await node1.runRandomNodesQuery();
await node2.runRandomNodesQuery();
await node3.runRandomNodesQuery();
await privateNode.runRandomNodesQuery();
await sleep(100);
}

expect(node1.getAllPeers()).toHaveLength(0);
expect(node2.getAllPeers()).toHaveLength(0);
expect(node3.getAllPeers()).toHaveLength(0);
expect(privateNode.getAllPeers()).toHaveLength(0);

await stopNodes(node1, node2, node3, privateNode);
}, 30_000);

const createNode = async (overrides: Partial<P2PConfig & IDiscv5CreateOptions> = {}, useBootnode = true) => {
const port = ++basePort;
const bootnodeAddr = bootNode.getENR().encodeTxt();
const peerId = await createSecp256k1PeerId();
Expand All @@ -235,7 +342,7 @@ describe('Discv5Service', () => {
...baseConfig,
p2pIp: `127.0.0.1`,
p2pPort: port,
bootstrapNodes: [bootnodeAddr],
bootstrapNodes: useBootnode ? [bootnodeAddr] : [],
blockCheckIntervalMS: 50,
peerCheckIntervalMS: 50,
p2pEnabled: true,
Expand Down
2 changes: 1 addition & 1 deletion yarn-project/p2p/src/services/libp2p/libp2p_service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,7 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
// Start job queue, peer discovery service and libp2p node
this.jobQueue.start();

await this.peerManager.initializeTrustedPeers();
await this.peerManager.initializePeers();
await this.peerDiscoveryService.start();
await this.node.start();

Expand Down
Loading