UNPKG

@waku/discovery

Version:

Contains various discovery mechanisms: DNS Discovery (EIP-1459, Peer Exchange, Local Peer Cache Discovery.

124 lines (108 loc) 3.06 kB
import { StreamManager } from "@waku/core"; import { EnrDecoder } from "@waku/enr"; import { IPeerExchange, Libp2pComponents, PeerExchangeQueryParams, PeerExchangeQueryResult, ProtocolError } from "@waku/interfaces"; import { isDefined } from "@waku/utils"; import { Logger } from "@waku/utils"; import all from "it-all"; import * as lp from "it-length-prefixed"; import { pipe } from "it-pipe"; import { Uint8ArrayList } from "uint8arraylist"; import { PeerExchangeRPC } from "./rpc.js"; export const PeerExchangeCodec = "/vac/waku/peer-exchange/2.0.0-alpha1"; const log = new Logger("peer-exchange"); /** * Implementation of the Peer Exchange protocol (https://rfc.vac.dev/spec/34/) */ export class WakuPeerExchange implements IPeerExchange { private readonly streamManager: StreamManager; /** * @param components - libp2p components */ public constructor(private readonly components: Libp2pComponents) { this.streamManager = new StreamManager(PeerExchangeCodec, components); } /** * Make a peer exchange query to a peer */ public async query( params: PeerExchangeQueryParams ): Promise<PeerExchangeQueryResult> { const { numPeers, peerId } = params; const rpcQuery = PeerExchangeRPC.createRequest({ numPeers: BigInt(numPeers) }); const peer = await this.components.peerStore.get(peerId); if (!peer) { return { peerInfos: null, error: ProtocolError.NO_PEER_AVAILABLE }; } let stream; try { stream = await this.streamManager.getStream(peerId); } catch (err) { log.error("Failed to get stream", err); return { peerInfos: null, error: ProtocolError.NO_STREAM_AVAILABLE }; } const res = await pipe( [rpcQuery.encode()], lp.encode, stream, lp.decode, async (source) => await all(source) ); try { const bytes = new Uint8ArrayList(); res.forEach((chunk) => { bytes.append(chunk); }); const { response } = PeerExchangeRPC.decode(bytes); if (!response) { log.error( "PeerExchangeRPC message did not contains a `response` field" ); return { peerInfos: null, error: ProtocolError.EMPTY_PAYLOAD }; } const peerInfos = await Promise.all( response.peerInfos .map((peerInfo) => peerInfo.enr) .filter(isDefined) .map(async (enr) => { return { ENR: await EnrDecoder.fromRLP(enr) }; }) ); return { peerInfos, error: null }; } catch (err) { log.error("Failed to decode push reply", err); return { peerInfos: null, error: ProtocolError.DECODE_FAILED }; } } } /** * * @returns A function that creates a new peer exchange protocol */ export function wakuPeerExchange(): ( components: Libp2pComponents ) => WakuPeerExchange { return (components: Libp2pComponents) => new WakuPeerExchange(components); }