@lodestar/beacon-node
Version:
A Typescript implementation of the beacon chain
166 lines • 7.22 kB
JavaScript
import { bootstrap } from "@libp2p/bootstrap";
import { identify } from "@libp2p/identify";
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 { ENR } from "@chainsafe/enr";
import { noise } from "@chainsafe/libp2p-noise";
import { asCrypto, defaultCrypto } from "@chainsafe/libp2p-noise/crypto";
import { quic } from "@chainsafe/libp2p-quic";
import { defaultNetworkOptions } from "../options.js";
import { Eth2PeerDataStore } from "../peers/datastore.js";
export async function getDiscv5Multiaddrs(bootEnrs, quicEnabled) {
const bootMultiaddrs = [];
for (const enrStr of bootEnrs) {
const enr = ENR.decodeTxt(enrStr);
// Prefer QUIC over TCP when available
const quicMultiaddr = quicEnabled ? (await enr.getFullMultiaddr("quic"))?.toString() : undefined;
const tcpMultiaddr = (await enr.getFullMultiaddr("tcp"))?.toString();
const multiaddrWithPeerId = quicMultiaddr ?? tcpMultiaddr;
if (multiaddrWithPeerId) {
bootMultiaddrs.push(multiaddrWithPeerId);
}
}
return bootMultiaddrs;
}
export async function createNodeJsLibp2p(privateKey, networkOpts = {}, nodeJsLibp2pOpts = {}) {
const localMultiaddrs = networkOpts.localMultiaddrs || defaultNetworkOptions.localMultiaddrs;
const disconnectThreshold = networkOpts.disconnectThreshold ?? defaultNetworkOptions.disconnectThreshold;
const tcpEnabled = networkOpts.tcp ?? defaultNetworkOptions.tcp;
const quicEnabled = networkOpts.quic ?? defaultNetworkOptions.quic;
const { peerStoreDir, disablePeerDiscovery } = nodeJsLibp2pOpts;
let datastore = undefined;
if (peerStoreDir) {
datastore = new Eth2PeerDataStore(peerStoreDir);
await datastore.open();
}
const peerDiscovery = [];
if (!disablePeerDiscovery) {
const bootMultiaddrs = [
...(networkOpts.bootMultiaddrs ?? defaultNetworkOptions.bootMultiaddrs ?? []),
// Append discv5.bootEnrs to bootMultiaddrs if requested
...(networkOpts.connectToDiscv5Bootnodes
? await getDiscv5Multiaddrs(networkOpts.discv5?.bootEnrs ?? [], quicEnabled)
: []),
];
if ((bootMultiaddrs.length ?? 0) > 0) {
peerDiscovery.push(bootstrap({ list: bootMultiaddrs }));
}
if (networkOpts.mdns) {
peerDiscovery.push(mdns());
}
}
const transports = [];
if (tcpEnabled) {
transports.unshift(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 (quicEnabled) {
const quicMultiaddrs = localMultiaddrs.filter((ma) => ma.includes("/quic-v1"));
const hasIpv4Quic = quicMultiaddrs.some((ma) => ma.includes("/ip4/"));
const hasIpv6Quic = quicMultiaddrs.some((ma) => ma.includes("/ip6/"));
// Only add QUIC transport if at least one QUIC listen address is configured,
// otherwise the transport constructor will throw
if (hasIpv4Quic || hasIpv6Quic) {
transports.unshift(quic({
handshakeTimeout: 5_000,
maxIdleTimeout: 10_000,
keepAliveInterval: 5_000,
maxConcurrentStreamLimit: 256,
maxStreamData: 10_000_000,
maxConnectionData: 15_000_000,
ipv4: hasIpv4Quic,
ipv6: hasIpv6Quic,
}));
}
}
const noiseCrypto = {
...defaultCrypto,
};
if (globalThis.Bun) {
noiseCrypto.chaCha20Poly1305Decrypt = asCrypto.chaCha20Poly1305Decrypt;
noiseCrypto.chaCha20Poly1305Encrypt = asCrypto.chaCha20Poly1305Encrypt;
}
return createLibp2p({
privateKey,
nodeInfo: {
name: "lodestar",
version: networkOpts.version ?? "unknown",
userAgent: networkOpts.private ? "" : networkOpts.version ? `lodestar/${networkOpts.version}` : "lodestar",
},
addresses: {
listen: localMultiaddrs,
announce: [],
},
connectionEncrypters: [noise({ crypto: noiseCrypto })],
transports,
streamMuxers: [mplex({ disconnectThreshold })],
peerDiscovery,
metrics: nodeJsLibp2pOpts.metrics
? prometheusMetrics({
collectDefaultMetrics: false,
preserveExistingMetrics: true,
registry: nodeJsLibp2pOpts.metricsRegistry,
})
: undefined,
connectionManager: {
// dialer config
maxParallelDials: 100,
maxPeerAddrsToDial: 4,
dialTimeout: 30_000,
// the maximum number of pending connections libp2p will accept before it starts rejecting incoming connections.
// make it the same to backlog option above
maxIncomingPendingConnections: 5,
},
// rely on lodestar's peer manager to ping peers
connectionMonitor: {
enabled: false,
},
// for our purposes, we don't want peer store data to expire
// see https://github.com/libp2p/js-libp2p/pull/3019
peerStore: {
maxAddressAge: Infinity,
maxPeerAge: Infinity,
},
datastore,
services: {
identify: identify({
runOnConnectionOpen: false,
}),
// individual components are specified because the components object is a Proxy
// and passing it here directly causes problems downstream, not to mention is slowwww
components: (components) => ({
peerId: components.peerId,
privateKey: components.privateKey,
nodeInfo: components.nodeInfo,
logger: components.logger,
events: components.events,
addressManager: components.addressManager,
peerStore: components.peerStore,
upgrader: components.upgrader,
registrar: components.registrar,
connectionManager: components.connectionManager,
transportManager: components.transportManager,
connectionGater: components.connectionGater,
contentRouting: components.contentRouting,
peerRouting: components.peerRouting,
datastore: components.datastore,
connectionProtector: components.connectionProtector,
metrics: components.metrics,
}),
},
});
}
//# sourceMappingURL=index.js.map