Danish Arora b99f828cfb
chore: minor refactoring for protocols (#1762)
* remove: unnecessary function

* remove: test

* update doc

* add: tests

* revert unintended change

* fix: ping return

* fix(tests): stub connections

* log warning instead of throwing an error
2024-01-11 17:25:47 +05:30

113 lines
3.0 KiB
TypeScript

import type { Connection } from "@libp2p/interface/connection";
import type { Peer, PeerStore } from "@libp2p/interface/peer-store";
import { bytesToUtf8 } from "../bytes/index.js";
/**
* Returns a pseudo-random peer that supports the given protocol.
* Useful for protocols such as store and light push
*/
export function selectRandomPeer(peers: Peer[]): Peer | undefined {
if (peers.length === 0) return;
const index = Math.round(Math.random() * (peers.length - 1));
return peers[index];
}
/**
* Function to sort peers by latency from lowest to highest
* @param peerStore - The Libp2p PeerStore
* @param peers - The list of peers to choose from
* @returns Sorted array of peers by latency
*/
export async function sortPeersByLatency(
peerStore: PeerStore,
peers: Peer[]
): Promise<Peer[]> {
if (peers.length === 0) return [];
const results = await Promise.all(
peers.map(async (peer) => {
try {
const pingBytes = (await peerStore.get(peer.id)).metadata.get("ping");
if (!pingBytes) return { peer, ping: Infinity };
const ping = Number(bytesToUtf8(pingBytes));
return { peer, ping };
} catch (error) {
return { peer, ping: Infinity };
}
})
);
// filter out null values
const validResults = results.filter(
(result): result is { peer: Peer; ping: number } => result !== null
);
return validResults
.sort((a, b) => a.ping - b.ping)
.map((result) => result.peer);
}
/**
* Returns the list of peers that supports the given protocol.
*/
export async function getPeersForProtocol(
peerStore: PeerStore,
protocols: string[]
): Promise<Peer[]> {
const peers: Peer[] = [];
await peerStore.forEach((peer) => {
for (let i = 0; i < protocols.length; i++) {
if (peer.protocols.includes(protocols[i])) {
peers.push(peer);
break;
}
}
});
return peers;
}
export async function getConnectedPeersForProtocol(
connections: Connection[],
peerStore: PeerStore,
protocols: string[]
): Promise<Peer[]> {
const openConnections = connections.filter(
(connection) => connection.status === "open"
);
const peerPromises = openConnections.map(async (connection) => {
const peer = await peerStore.get(connection.remotePeer);
const supportsProtocol = peer.protocols.some((protocol) =>
protocols.includes(protocol)
);
return supportsProtocol ? peer : null;
});
const peersWithNulls = await Promise.all(peerPromises);
return peersWithNulls.filter((peer): peer is Peer => peer !== null);
}
export function selectConnection(
connections: Connection[]
): Connection | undefined {
if (!connections.length) return;
if (connections.length === 1) return connections[0];
let latestConnection: Connection | undefined;
connections.forEach((connection) => {
if (connection.status === "open") {
if (!latestConnection) {
latestConnection = connection;
} else if (connection.timeline.open > latestConnection.timeline.open) {
latestConnection = connection;
}
}
});
return latestConnection;
}