feat: retrieve peers from all passed enrtree URLs

Order retrieval is random, but it attemps to retrieve from all ENR trees.
This commit is contained in:
fryorcraken 2025-03-24 17:04:35 +11:00
parent 0dfe35281c
commit 25f884e05b
No known key found for this signature in database
GPG Key ID: A82ED75A8DFC50A4
3 changed files with 46 additions and 14 deletions

View File

@ -1,6 +1,6 @@
import { ENR, EnrDecoder } from "@waku/enr";
import type { DnsClient, IEnr, SearchContext } from "@waku/interfaces";
import { Logger } from "@waku/utils";
import { Logger, shuffle } from "@waku/utils";
import { DnsOverHttps } from "./dns_over_https.js";
import { ENRTree } from "./enrtree.js";
@ -27,19 +27,23 @@ export class DnsNodeDiscovery {
}
/**
* {@inheritDoc getPeers}
* Retrieve the next peers from the passed [[enrTreeUrls]],
*/
public async *getNextPeer(enrTreeUrls: string[]): AsyncGenerator<IEnr> {
const networkIndex = Math.floor(Math.random() * enrTreeUrls.length);
const { publicKey, domain } = ENRTree.parseTree(enrTreeUrls[networkIndex]);
const context: SearchContext = {
domain,
publicKey,
visits: {}
};
// Shuffle the ENR Trees so that not all clients connect to same nodes first.
for (const enrTreeUrl of shuffle(enrTreeUrls)) {
const { publicKey, domain } = ENRTree.parseTree(enrTreeUrl);
const context: SearchContext = {
domain,
publicKey,
visits: {}
};
for await (const peer of fetchNodes(() => this._search(domain, context))) {
yield peer;
for await (const peer of fetchNodes(() =>
this._search(domain, context)
)) {
yield peer;
}
}
}
@ -113,7 +117,7 @@ export class DnsNodeDiscovery {
throw new Error("Received empty result array while fetching TXT record");
if (!response[0].length) throw new Error("Received empty TXT record");
// Branch entries can be an array of strings of comma delimited subdomains, with
// Branch entries can be an array of strings of comma-delimited subdomains, with
// some subdomain strings split across the array elements
const result = response.join("");

View File

@ -77,7 +77,7 @@ describe("DNS Node Discovery [live data]", function () {
expect(dnsPeers).to.gte(minQuantityExpected);
});
it(`should retrieve ${maxQuantity} multiaddrs for test.waku.nodes.status.im`, async function () {
it(`should retrieve ${maxQuantity} multiaddrs for sandbox.waku.nodes.status.im`, async function () {
if (process.env.CI) this.skip();
this.timeout(10000);
@ -102,6 +102,34 @@ describe("DNS Node Discovery [live data]", function () {
seen.push(ma!.toString());
}
});
it(`should retrieve all multiaddrs when several ENR Tree URLs are passed`, async function () {
if (process.env.CI) this.skip();
this.timeout(10000);
// Google's dns server address. Needs to be set explicitly to run in CI
const dnsNodeDiscovery = await DnsNodeDiscovery.dnsOverHttp();
const peers = [];
for await (const peer of dnsNodeDiscovery.getNextPeer([
enrTree["SANDBOX"],
enrTree["TEST"]
])) {
peers.push(peer);
}
expect(peers.length).to.eq(6);
const multiaddrs = peers.map((peer) => peer.multiaddrs).flat();
const seen: string[] = [];
for (const ma of multiaddrs) {
expect(ma).to.not.be.undefined;
expect(seen).to.not.include(ma!.toString());
seen.push(ma!.toString());
}
});
it("passes more than one ENR URLs and attempts connection", async function () {
if (process.env.CI) this.skip();
this.timeout(30_000);

View File

@ -12,7 +12,7 @@ export function getPseudoRandomSubset<T>(
return shuffle(values).slice(0, wantedNumber);
}
function shuffle<T>(arr: T[]): T[] {
export function shuffle<T>(arr: T[]): T[] {
if (arr.length <= 1) {
return arr;
}