Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
f663476
feat: quic support
wemeetagain Sep 4, 2024
a36a355
chore: update libp2p deps
wemeetagain Sep 4, 2024
021e987
chore: fix libp2p init
wemeetagain Sep 4, 2024
3dd2cd3
chore: disable tcp
wemeetagain Sep 4, 2024
fd8b13c
chore: bump quic
wemeetagain Sep 4, 2024
87b746a
chore: bump quic
wemeetagain Sep 4, 2024
5d819be
chore: bump quic
wemeetagain Sep 5, 2024
d4837bd
chore: bump quic
wemeetagain Sep 5, 2024
f6d27a9
chore: bump quic
wemeetagain Sep 6, 2024
ce62911
chore: bump quic
wemeetagain Sep 9, 2024
0a5eb63
chore: bump quic
wemeetagain Sep 10, 2024
882f876
chore: bump quic
wemeetagain Sep 10, 2024
80dd291
chore: disable connection monitor
wemeetagain Sep 10, 2024
2e396c0
chore: add some console logs
wemeetagain Sep 10, 2024
91c7913
chore: upgrade to js-libp2p 2.0
wemeetagain Sep 11, 2024
26d3120
chore: bump libp2p versions
wemeetagain Sep 12, 2024
f3c4117
chore: fix up yarn lock
wemeetagain Sep 12, 2024
7515ff2
chore: fix some tests
wemeetagain Sep 12, 2024
316f8b2
chore: fix connection map
wemeetagain Sep 12, 2024
acb3ff6
feat: gossipsub v1.2
wemeetagain Sep 13, 2024
e0a4065
Merge branch 'cayman/bump-libp2p' into cayman/quic
wemeetagain Sep 19, 2024
a991807
fix: merge conflict bug
wemeetagain Sep 19, 2024
1c8e44f
Merge branch 'unstable' into cayman/quic
wemeetagain Oct 14, 2024
ee1f579
Merge branch 'unstable' into cayman/quic
wemeetagain Jun 20, 2025
b218ddd
chore: bump quic lib
wemeetagain Jun 20, 2025
c39d9b3
fix: quic6 bug
wemeetagain Jun 20, 2025
93d66f5
chore: update quic
wemeetagain Jun 20, 2025
50ab166
chore: update quic
wemeetagain Jun 20, 2025
0eb1a71
chore: bump libp2p
wemeetagain Jul 9, 2025
6b7be9a
chore: update quic
wemeetagain Jul 9, 2025
a50e41d
chore: more log
wemeetagain Jul 9, 2025
5c3bf27
Merge branch 'cayman/bump-libp2p' into cayman/quic
wemeetagain Jul 9, 2025
99b77a2
chore: bump quic
wemeetagain Jul 9, 2025
a6a7cb1
chore: don't cache enrs for undialable peers
wemeetagain Jul 9, 2025
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
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
"https-browserify": "^1.0.0",
"jsdom": "^23.0.1",
"lerna": "^7.3.0",
"libp2p": "2.8.2",
"libp2p": "2.9.0",
"node-gyp": "^9.4.0",
"npm-run-all": "^4.1.5",
"path-browserify": "^1.0.1",
Expand Down
5 changes: 3 additions & 2 deletions packages/beacon-node/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@
"@chainsafe/enr": "^5.0.0",
"@chainsafe/libp2p-gossipsub": "^14.1.1",
"@chainsafe/libp2p-noise": "^16.1.0",
"@chainsafe/libp2p-quic": "https://github.com/chainsafe/js-libp2p-quic/raw/lodestar-test/package.tgz",
"@chainsafe/persistent-merkle-tree": "^1.2.0",
"@chainsafe/prometheus-gc-stats": "^1.0.0",
"@chainsafe/pubkey-index-map": "^3.0.0",
Expand All @@ -111,7 +112,7 @@
"@fastify/swagger-ui": "^5.0.1",
"@libp2p/bootstrap": "^11.0.32",
"@libp2p/crypto": "^5.0.15",
"@libp2p/identify": "3.0.27",
"@libp2p/identify": "^3.0.27",
"@libp2p/interface": "^2.7.0",
"@libp2p/mdns": "^11.0.32",
"@libp2p/mplex": "^11.0.32",
Expand Down Expand Up @@ -139,7 +140,7 @@
"it-all": "^3.0.4",
"it-pipe": "^3.0.1",
"jwt-simple": "0.5.6",
"libp2p": "2.8.2",
"libp2p": "2.9.0",
"multiformats": "^11.0.1",
"prom-client": "^15.1.0",
"qs": "^6.11.1",
Expand Down
4 changes: 4 additions & 0 deletions packages/beacon-node/src/network/discv5/worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ const onDiscovered = (enr: ENR): void => {
subject.next(enr.toObject());
}
};
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore wtf?
discv5.addListener("discovered", onDiscovered);

// Discv5 will now begin accepting request/responses
Expand All @@ -96,6 +98,8 @@ const module: Discv5WorkerApi = {
return discv5.kadValues().map((enr) => enr.toObject());
},
async discoverKadValues(): Promise<void> {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore wtf?
discv5.kadValues().map(onDiscovered);
},
async findRandomNode(): Promise<ENRData[]> {
Expand Down
45 changes: 30 additions & 15 deletions packages/beacon-node/src/network/libp2p/index.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import {ENR} from "@chainsafe/enr";
import {noise} from "@chainsafe/libp2p-noise";
import {quic} from "@chainsafe/libp2p-quic";
import {bootstrap} from "@libp2p/bootstrap";
import {identify} from "@libp2p/identify";
import {PrivateKey} from "@libp2p/interface";
import {mdns} from "@libp2p/mdns";
import {mplex} from "@libp2p/mplex";
import {prometheusMetrics} from "@libp2p/prometheus-metrics";
import {tcp} from "@libp2p/tcp";
import {createLibp2p} from "libp2p";
import {Libp2pInit, createLibp2p} from "libp2p";
import {Registry} from "prom-client";
import {Libp2p, LodestarComponents} from "../interface.js";
import {NetworkOptions, defaultNetworkOptions} from "../options.js";
Expand Down Expand Up @@ -62,6 +63,33 @@ export async function createNodeJsLibp2p(
peerDiscovery.push(mdns());
}
}
const transports: Libp2pInit["transports"] = [
// TCP is always enabled
// tcp({
// // Reject connections when the server's connection count gets high
// maxConnections: networkOpts.maxPeers,
// // socket option: the maximum length of the queue of pending connections
// // https://nodejs.org/dist/latest-v18.x/docs/api/net.html#serverlisten
// // it's not safe if we increase this number
// backlog: 5,
// closeServerOnMaxConnections: {
// closeAbove: networkOpts.maxPeers ?? Infinity,
// listenBelow: networkOpts.maxPeers ?? Infinity,
// },
// }),
];
if (!networkOpts.disableQuic) {
transports.unshift(
quic({
handshakeTimeout: 5_000,
maxIdleTimeout: 10_000,
keepAliveInterval: 5_000,
maxConcurrentStreamLimit: 256,
maxStreamData: 10_000_000,
maxConnectionData: 15_000_000,
})
);
}

return createLibp2p({
privateKey,
Expand All @@ -70,20 +98,7 @@ export async function createNodeJsLibp2p(
announce: [],
},
connectionEncrypters: [noise()],
// Reject connections when the server's connection count gets high
transports: [
tcp({
maxConnections: networkOpts.maxPeers,
// socket option: the maximum length of the queue of pending connections
// https://nodejs.org/dist/latest-v18.x/docs/api/net.html#serverlisten
// it's not safe if we increase this number
backlog: 5,
closeServerOnMaxConnections: {
closeAbove: networkOpts.maxPeers ?? Infinity,
listenBelow: networkOpts.maxPeers ?? Infinity,
},
}),
],
transports,
streamMuxers: [mplex({maxInboundStreams: 256, disconnectThreshold: networkOpts.disconnectThreshold})],
peerDiscovery,
metrics: nodeJsLibp2pOpts.metrics
Expand Down
1 change: 1 addition & 0 deletions packages/beacon-node/src/network/metadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {getCurrentAndNextForkBoundary} from "./forks.js";

export enum ENRKey {
tcp = "tcp",
quic = "quic",
eth2 = "eth2",
attnets = "attnets",
syncnets = "syncnets",
Expand Down
7 changes: 5 additions & 2 deletions packages/beacon-node/src/network/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ export interface NetworkOptions
useWorker?: boolean;
maxYoungGenerationSizeMb?: number;
disableLightClientServer?: boolean;

/**
* During E2E tests observe a lot of following `missing stream`:
*
Expand All @@ -42,12 +41,14 @@ export interface NetworkOptions
* We need to increase this only for the testing purpose
*/
disconnectThreshold?: number;
disableQuic?: boolean;
}

export const defaultNetworkOptions: NetworkOptions = {
maxPeers: 110, // Allow some room above targetPeers for new inbound peers
targetPeers: 100,
localMultiaddrs: ["/ip4/0.0.0.0/tcp/9000"],
// this default is never used, in practice, it is always overridden by the cli
localMultiaddrs: ["/ip4/0.0.0.0/tcp/9000", "/ip4/0.0.0.0/udp/9001/quic-v1"],
bootMultiaddrs: [],
/** disabled by default */
discv5: null,
Expand All @@ -61,4 +62,6 @@ export const defaultNetworkOptions: NetworkOptions = {
slotsToSubscribeBeforeAggregatorDuty: 2,
// This will enable the light client server by default
disableLightClientServer: false,
// enable quic by default
disableQuic: false,
};
38 changes: 32 additions & 6 deletions packages/beacon-node/src/network/peers/discover.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ export type SubnetDiscvQueryMs = {
type CachedENR = {
peerId: PeerId;
multiaddrTCP: Multiaddr;
multiaddrQUIC?: Multiaddr;
subnets: Record<SubnetType, boolean[]>;
addedUnixMs: number;
};
Expand All @@ -105,6 +106,7 @@ export class PeerDiscovery {
attnets: new Map(),
syncnets: new Map(),
};
private transports: string[];

private discv5StartMs: number;
private discv5FirstQueryDelayMs: number;
Expand Down Expand Up @@ -158,6 +160,8 @@ export class PeerDiscovery {
}
});
}

this.transports = libp2p.services.components.transportManager.getTransports().map((t) => t[Symbol.toStringTag]);
}

static async init(modules: PeerDiscoveryModules, opts: PeerDiscoveryOpts): Promise<PeerDiscovery> {
Expand Down Expand Up @@ -314,9 +318,16 @@ export class PeerDiscovery {
return;
}

// There is at least one multiaddr
// We assume its the tcp multiaddr, we also assume that there may be an additional quic multiaddr
// In practice this will only be challenged for mdns discovered peers
// TODO: make this smarter
const multiaddrTCP = multiaddrs[0];
const multiaddrQUIC = multiaddrs[1];

const attnets = zeroAttnets;
const syncnets = zeroSyncnets;
const status = this.handleDiscoveredPeer(id, multiaddrs[0], attnets, syncnets);
const status = this.handleDiscoveredPeer(id, multiaddrTCP, multiaddrQUIC, attnets, syncnets);
this.logger.debug("Discovered peer via libp2p", {peer: prettyPrintPeerId(id), status});
this.metrics?.discovery.discoveredStatus.inc({status});
};
Expand Down Expand Up @@ -347,7 +358,10 @@ export class PeerDiscovery {
const attnets = attnetsBytes ? deserializeEnrSubnets(attnetsBytes, ATTESTATION_SUBNET_COUNT) : zeroAttnets;
const syncnets = syncnetsBytes ? deserializeEnrSubnets(syncnetsBytes, SYNC_COMMITTEE_SUBNET_COUNT) : zeroSyncnets;

const status = this.handleDiscoveredPeer(peerId, multiaddrTCP, attnets, syncnets);
// quic multiaddr is optional
const multiaddrQUIC = enr.getLocationMultiaddr(ENRKey.quic);

const status = this.handleDiscoveredPeer(peerId, multiaddrTCP, multiaddrQUIC, attnets, syncnets);
this.logger.debug("Discovered peer via discv5", {peer: prettyPrintPeerId(peerId), status});
this.metrics?.discovery.discoveredStatus.inc({status});
};
Expand All @@ -358,6 +372,7 @@ export class PeerDiscovery {
private handleDiscoveredPeer(
peerId: PeerId,
multiaddrTCP: Multiaddr,
multiaddrQUIC: Multiaddr | undefined,
attnets: boolean[],
syncnets: boolean[]
): DiscoveredPeerStatus {
Expand All @@ -372,6 +387,11 @@ export class PeerDiscovery {
return DiscoveredPeerStatus.already_connected;
}

// ignore peers if they don't have the transport we are using
if (!this.transports.includes("tcp") && !multiaddrQUIC) {
return DiscoveredPeerStatus.no_multiaddrs;
}

// Ignore dialing peers
if (
this.libp2p.services.components.connectionManager
Expand All @@ -385,6 +405,7 @@ export class PeerDiscovery {
const cachedPeer: CachedENR = {
peerId,
multiaddrTCP,
multiaddrQUIC,
subnets: {attnets, syncnets},
addedUnixMs: Date.now(),
};
Expand Down Expand Up @@ -448,19 +469,24 @@ export class PeerDiscovery {
// are not successful.
this.peersToConnect = Math.max(this.peersToConnect - 1, 0);

const {peerId, multiaddrTCP} = cachedPeer;
const {peerId, multiaddrTCP, multiaddrQUIC} = cachedPeer;

// Must add the multiaddrs array to the address book before dialing
// https://github.com/libp2p/js-libp2p/blob/aec8e3d3bb1b245051b60c2a890550d262d5b062/src/index.js#L638
const peer = await this.libp2p.peerStore.merge(peerId, {multiaddrs: [multiaddrTCP]});
const peer = await this.libp2p.peerStore.merge(peerId, {
multiaddrs: [multiaddrQUIC, multiaddrTCP].filter(Boolean) as Multiaddr[],
});
if (peer.addresses.length === 0) {
this.metrics?.discovery.notDialReason.inc({reason: NotDialReason.no_multiaddrs});
return;
}

// Note: PeerDiscovery adds the multiaddrTCP beforehand
// Note: PeerDiscovery adds the multiaddrs beforehand
const peerIdShort = prettyPrintPeerId(peerId);
this.logger.debug("Dialing discovered peer", {peer: peerIdShort});
this.logger.debug("Dialing discovered peer", {
peer: peerIdShort,
addresses: peer.addresses.map((a) => a.multiaddr.toString()).join(", "),
});

this.metrics?.discovery.dialAttempts.inc();
const timer = this.metrics?.discovery.dialTime.startTimer();
Expand Down
Loading
Loading