From aab9c08caefb9f72460261ec0c38df88ae0f02ce Mon Sep 17 00:00:00 2001 From: Danish Arora <35004822+danisharora099@users.noreply.github.com> Date: Thu, 17 Aug 2023 19:50:35 +0530 Subject: [PATCH] feat(dns-discovery): set default for `NodeRequirements` (#1466) * add: test for multi enrtree + static multiaddr * wrap up test * rm: only * move test to optional file * dns-disc: setup default for node requirements & move to constants * chore: restructure DNS Discovery for better readability * fix: build * fix: type import * fix: test expect * rm: only * update packagelock * use new libp2p interface * fix linting errors --- package-lock.json | 340 +++++++++++++++++++ packages/dns-discovery/src/constants.ts | 16 + packages/dns-discovery/src/dns.spec.ts | 3 +- packages/dns-discovery/src/dns.ts | 24 +- packages/dns-discovery/src/dns_discovery.ts | 136 ++++++++ packages/dns-discovery/src/dns_over_https.ts | 3 +- packages/dns-discovery/src/fetch_nodes.ts | 4 +- packages/dns-discovery/src/index.ts | 173 +--------- packages/interfaces/src/dns_discovery.ts | 47 +++ packages/interfaces/src/index.ts | 1 + packages/tests/package.json | 2 + 11 files changed, 555 insertions(+), 194 deletions(-) create mode 100644 packages/dns-discovery/src/constants.ts create mode 100644 packages/dns-discovery/src/dns_discovery.ts create mode 100644 packages/interfaces/src/dns_discovery.ts diff --git a/package-lock.json b/package-lock.json index ae0597fe82..3df0cca8bf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -30899,6 +30899,8 @@ "@types/mocha": "^10.0.1", "@types/sinon": "^10.0.16", "@types/tail": "^2.2.1", + "@typescript-eslint/eslint-plugin": "^5.57.0", + "@typescript-eslint/parser": "^5.62.0", "@waku/dns-discovery": "*", "@waku/message-encryption": "*", "@waku/peer-exchange": "*", @@ -31037,6 +31039,203 @@ "uint8arrays": "^4.0.4" } }, + "packages/tests/node_modules/@typescript-eslint/eslint-plugin": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz", + "integrity": "sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.4.0", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/type-utils": "5.62.0", + "@typescript-eslint/utils": "5.62.0", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "natural-compare-lite": "^1.4.0", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^5.0.0", + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "packages/tests/node_modules/@typescript-eslint/parser": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz", + "integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "packages/tests/node_modules/@typescript-eslint/scope-manager": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz", + "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "packages/tests/node_modules/@typescript-eslint/type-utils": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz", + "integrity": "sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "5.62.0", + "@typescript-eslint/utils": "5.62.0", + "debug": "^4.3.4", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "packages/tests/node_modules/@typescript-eslint/types": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz", + "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "packages/tests/node_modules/@typescript-eslint/typescript-estree": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", + "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "packages/tests/node_modules/@typescript-eslint/utils": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz", + "integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@types/json-schema": "^7.0.9", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", + "eslint-scope": "^5.1.1", + "semver": "^7.3.7" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "packages/tests/node_modules/@typescript-eslint/visitor-keys": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", + "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.62.0", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "packages/tests/node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "packages/tests/node_modules/delay": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/delay/-/delay-6.0.0.tgz", @@ -31048,6 +31247,26 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "packages/tests/node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "packages/tests/node_modules/it-stream-types": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/it-stream-types/-/it-stream-types-2.0.1.tgz", @@ -35796,6 +36015,8 @@ "@types/mocha": "^10.0.1", "@types/sinon": "^10.0.16", "@types/tail": "^2.2.1", + "@typescript-eslint/eslint-plugin": "^5.57.0", + "@typescript-eslint/parser": "^5.62.0", "@waku/core": "*", "@waku/dns-discovery": "*", "@waku/enr": "*", @@ -35947,11 +36168,130 @@ } } }, + "@typescript-eslint/eslint-plugin": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz", + "integrity": "sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==", + "dev": true, + "requires": { + "@eslint-community/regexpp": "^4.4.0", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/type-utils": "5.62.0", + "@typescript-eslint/utils": "5.62.0", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "natural-compare-lite": "^1.4.0", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/parser": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz", + "integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==", + "dev": true, + "requires": { + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", + "debug": "^4.3.4" + } + }, + "@typescript-eslint/scope-manager": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz", + "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0" + } + }, + "@typescript-eslint/type-utils": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz", + "integrity": "sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==", + "dev": true, + "requires": { + "@typescript-eslint/typescript-estree": "5.62.0", + "@typescript-eslint/utils": "5.62.0", + "debug": "^4.3.4", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/types": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz", + "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", + "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/utils": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz", + "integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==", + "dev": true, + "requires": { + "@eslint-community/eslint-utils": "^4.2.0", + "@types/json-schema": "^7.0.9", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", + "eslint-scope": "^5.1.1", + "semver": "^7.3.7" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", + "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.62.0", + "eslint-visitor-keys": "^3.3.0" + } + }, + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true + }, "delay": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/delay/-/delay-6.0.0.tgz", "integrity": "sha512-2NJozoOHQ4NuZuVIr5CWd0iiLVIRSDepakaovIN+9eIDHEhdCAEvSy2cuf1DCrPPQLvHmbqTHODlhHg8UCy4zw==" }, + "globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + } + }, "it-stream-types": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/it-stream-types/-/it-stream-types-2.0.1.tgz", diff --git a/packages/dns-discovery/src/constants.ts b/packages/dns-discovery/src/constants.ts new file mode 100644 index 0000000000..ec1378e962 --- /dev/null +++ b/packages/dns-discovery/src/constants.ts @@ -0,0 +1,16 @@ +import type { NodeCapabilityCount } from "@waku/interfaces"; + +export const enrTree = { + TEST: "enrtree://AOGECG2SPND25EEFMAJ5WF3KSGJNSGV356DSTL2YVLLZWIV6SAYBM@test.waku.nodes.status.im", + PROD: "enrtree://AOGECG2SPND25EEFMAJ5WF3KSGJNSGV356DSTL2YVLLZWIV6SAYBM@prod.waku.nodes.status.im" +}; + +export const DEFAULT_BOOTSTRAP_TAG_NAME = "bootstrap"; +export const DEFAULT_BOOTSTRAP_TAG_VALUE = 50; +export const DEFAULT_BOOTSTRAP_TAG_TTL = 100_000_000; + +export const DEFAULT_NODE_REQUIREMENTS: Partial = { + store: 2, + filter: 1, + lightPush: 1 +}; diff --git a/packages/dns-discovery/src/dns.spec.ts b/packages/dns-discovery/src/dns.spec.ts index f31a048845..6a5626d64d 100644 --- a/packages/dns-discovery/src/dns.spec.ts +++ b/packages/dns-discovery/src/dns.spec.ts @@ -1,6 +1,7 @@ +import type { DnsClient } from "@waku/interfaces"; import { expect } from "chai"; -import { DnsClient, DnsNodeDiscovery } from "./dns.js"; +import { DnsNodeDiscovery } from "./dns.js"; import testData from "./testdata.json" assert { type: "json" }; import { enrTree } from "./index.js"; diff --git a/packages/dns-discovery/src/dns.ts b/packages/dns-discovery/src/dns.ts index dfc916618d..ee19d4fe35 100644 --- a/packages/dns-discovery/src/dns.ts +++ b/packages/dns-discovery/src/dns.ts @@ -1,5 +1,10 @@ import { ENR, EnrDecoder } from "@waku/enr"; -import type { IEnr } from "@waku/interfaces"; +import type { + DnsClient, + IEnr, + NodeCapabilityCount, + SearchContext +} from "@waku/interfaces"; import debug from "debug"; import { DnsOverHttps } from "./dns_over_https.js"; @@ -11,23 +16,6 @@ import { const log = debug("waku:discovery:dns"); -export type SearchContext = { - domain: string; - publicKey: string; - visits: { [key: string]: boolean }; -}; - -export interface DnsClient { - resolveTXT: (domain: string) => Promise; -} - -export interface NodeCapabilityCount { - relay: number; - store: number; - filter: number; - lightPush: number; -} - export class DnsNodeDiscovery { private readonly dns: DnsClient; private readonly _DNSTreeCache: { [key: string]: string }; diff --git a/packages/dns-discovery/src/dns_discovery.ts b/packages/dns-discovery/src/dns_discovery.ts new file mode 100644 index 0000000000..f4ac9e9db6 --- /dev/null +++ b/packages/dns-discovery/src/dns_discovery.ts @@ -0,0 +1,136 @@ +import { CustomEvent, EventEmitter } from "@libp2p/interface/events"; +import type { + PeerDiscovery, + PeerDiscoveryEvents +} from "@libp2p/interface/peer-discovery"; +import { peerDiscovery as symbol } from "@libp2p/interface/peer-discovery"; +import type { PeerInfo } from "@libp2p/interface/peer-info"; +import type { + DnsDiscOptions, + DnsDiscoveryComponents, + IEnr, + NodeCapabilityCount +} from "@waku/interfaces"; +import debug from "debug"; + +import { + DEFAULT_BOOTSTRAP_TAG_NAME, + DEFAULT_BOOTSTRAP_TAG_TTL, + DEFAULT_BOOTSTRAP_TAG_VALUE, + DEFAULT_NODE_REQUIREMENTS +} from "./constants.js"; +import { DnsNodeDiscovery } from "./dns.js"; + +const log = debug("waku:peer-discovery-dns"); + +/** + * Parse options and expose function to return bootstrap peer addresses. + */ +export class PeerDiscoveryDns + extends EventEmitter + implements PeerDiscovery +{ + private nextPeer: (() => AsyncGenerator) | undefined; + private _started: boolean; + private _components: DnsDiscoveryComponents; + private _options: DnsDiscOptions; + + constructor(components: DnsDiscoveryComponents, options: DnsDiscOptions) { + super(); + this._started = false; + this._components = components; + this._options = options; + + const { enrUrls } = options; + log("Use following EIP-1459 ENR Tree URLs: ", enrUrls); + } + + /** + * Start discovery process + */ + async start(): Promise { + log("Starting peer discovery via dns"); + + this._started = true; + + if (this.nextPeer === undefined) { + let { enrUrls } = this._options; + if (!Array.isArray(enrUrls)) enrUrls = [enrUrls]; + + const { wantedNodeCapabilityCount } = this._options; + const dns = await DnsNodeDiscovery.dnsOverHttp(); + + this.nextPeer = dns.getNextPeer.bind( + dns, + enrUrls, + wantedNodeCapabilityCount + ); + } + + for await (const peerEnr of this.nextPeer()) { + if (!this._started) { + return; + } + + const peerInfo = peerEnr.peerInfo; + + if (!peerInfo) { + continue; + } + + const tagsToUpdate = { + tags: { + [DEFAULT_BOOTSTRAP_TAG_NAME]: { + value: this._options.tagValue ?? DEFAULT_BOOTSTRAP_TAG_VALUE, + ttl: this._options.tagTTL ?? DEFAULT_BOOTSTRAP_TAG_TTL + } + } + }; + + let isPeerChanged = false; + const isPeerExists = await this._components.peerStore.has(peerInfo.id); + + if (isPeerExists) { + const peer = await this._components.peerStore.get(peerInfo.id); + const hasBootstrapTag = peer.tags.has(DEFAULT_BOOTSTRAP_TAG_NAME); + + if (!hasBootstrapTag) { + isPeerChanged = true; + await this._components.peerStore.merge(peerInfo.id, tagsToUpdate); + } + } else { + isPeerChanged = true; + await this._components.peerStore.save(peerInfo.id, tagsToUpdate); + } + + if (isPeerChanged) { + this.dispatchEvent( + new CustomEvent("peer", { detail: peerInfo }) + ); + } + } + } + + /** + * Stop emitting events + */ + stop(): void { + this._started = false; + } + + get [symbol](): true { + return true; + } + + get [Symbol.toStringTag](): string { + return "@waku/bootstrap"; + } +} + +export function wakuDnsDiscovery( + enrUrls: string[], + wantedNodeCapabilityCount: Partial = DEFAULT_NODE_REQUIREMENTS +): (components: DnsDiscoveryComponents) => PeerDiscoveryDns { + return (components: DnsDiscoveryComponents) => + new PeerDiscoveryDns(components, { enrUrls, wantedNodeCapabilityCount }); +} diff --git a/packages/dns-discovery/src/dns_over_https.ts b/packages/dns-discovery/src/dns_over_https.ts index 9f86e8591f..4c924dc384 100644 --- a/packages/dns-discovery/src/dns_over_https.ts +++ b/packages/dns-discovery/src/dns_over_https.ts @@ -1,9 +1,8 @@ +import type { DnsClient } from "@waku/interfaces"; import { bytesToUtf8 } from "@waku/utils/bytes"; import debug from "debug"; import { Endpoint, query, wellknown } from "dns-query"; -import { DnsClient } from "./dns.js"; - const log = debug("waku:dns-over-https"); export class DnsOverHttps implements DnsClient { diff --git a/packages/dns-discovery/src/fetch_nodes.ts b/packages/dns-discovery/src/fetch_nodes.ts index 77d69107c4..e2d3380500 100644 --- a/packages/dns-discovery/src/fetch_nodes.ts +++ b/packages/dns-discovery/src/fetch_nodes.ts @@ -1,8 +1,6 @@ -import type { IEnr, Waku2 } from "@waku/interfaces"; +import type { IEnr, NodeCapabilityCount, Waku2 } from "@waku/interfaces"; import debug from "debug"; -import { NodeCapabilityCount } from "./dns.js"; - const log = debug("waku:discovery:fetch_nodes"); /** diff --git a/packages/dns-discovery/src/index.ts b/packages/dns-discovery/src/index.ts index fbf38eca13..f6713bfaea 100644 --- a/packages/dns-discovery/src/index.ts +++ b/packages/dns-discovery/src/index.ts @@ -1,170 +1,3 @@ -import { CustomEvent, EventEmitter } from "@libp2p/interface/events"; -import type { - PeerDiscovery, - PeerDiscoveryEvents -} from "@libp2p/interface/peer-discovery"; -import { peerDiscovery as symbol } from "@libp2p/interface/peer-discovery"; -import type { PeerInfo } from "@libp2p/interface/peer-info"; -import type { PeerStore } from "@libp2p/interface/peer-store"; -import type { IEnr } from "@waku/interfaces"; -import debug from "debug"; - -import { DnsNodeDiscovery, NodeCapabilityCount } from "./dns.js"; - -export { NodeCapabilityCount }; - -const log = debug("waku:peer-discovery-dns"); - -const enrTree = { - TEST: "enrtree://AOGECG2SPND25EEFMAJ5WF3KSGJNSGV356DSTL2YVLLZWIV6SAYBM@test.waku.nodes.status.im", - PROD: "enrtree://AOGECG2SPND25EEFMAJ5WF3KSGJNSGV356DSTL2YVLLZWIV6SAYBM@prod.waku.nodes.status.im" -}; - -const DEFAULT_BOOTSTRAP_TAG_NAME = "bootstrap"; -const DEFAULT_BOOTSTRAP_TAG_VALUE = 50; -const DEFAULT_BOOTSTRAP_TAG_TTL = 100_000_000; - -export interface DnsDiscoveryComponents { - peerStore: PeerStore; -} - -export interface Options { - /** - * ENR URL to use for DNS discovery - */ - enrUrls: string | string[]; - /** - * Specifies what type of nodes are wanted from the discovery process - */ - wantedNodeCapabilityCount: Partial; - /** - * Tag a bootstrap peer with this name before "discovering" it (default: 'bootstrap') - */ - tagName?: string; - - /** - * The bootstrap peer tag will have this value (default: 50) - */ - tagValue?: number; - - /** - * Cause the bootstrap peer tag to be removed after this number of ms (default: 2 minutes) - */ - tagTTL?: number; -} - -/** - * Parse options and expose function to return bootstrap peer addresses. - */ -export class PeerDiscoveryDns - extends EventEmitter - implements PeerDiscovery -{ - private nextPeer: (() => AsyncGenerator) | undefined; - private _started: boolean; - private _components: DnsDiscoveryComponents; - private _options: Options; - - constructor(components: DnsDiscoveryComponents, options: Options) { - super(); - this._started = false; - this._components = components; - this._options = options; - - const { enrUrls } = options; - log("Use following EIP-1459 ENR Tree URLs: ", enrUrls); - } - - /** - * Start discovery process - */ - async start(): Promise { - log("Starting peer discovery via dns"); - - this._started = true; - - if (this.nextPeer === undefined) { - let { enrUrls } = this._options; - if (!Array.isArray(enrUrls)) enrUrls = [enrUrls]; - - const { wantedNodeCapabilityCount } = this._options; - const dns = await DnsNodeDiscovery.dnsOverHttp(); - - this.nextPeer = dns.getNextPeer.bind( - dns, - enrUrls, - wantedNodeCapabilityCount - ); - } - - for await (const peerEnr of this.nextPeer()) { - if (!this._started) { - return; - } - - const peerInfo = peerEnr.peerInfo; - - if (!peerInfo) { - continue; - } - - const tagsToUpdate = { - tags: { - [DEFAULT_BOOTSTRAP_TAG_NAME]: { - value: this._options.tagValue ?? DEFAULT_BOOTSTRAP_TAG_VALUE, - ttl: this._options.tagTTL ?? DEFAULT_BOOTSTRAP_TAG_TTL - } - } - }; - - let isPeerChanged = false; - const isPeerExists = await this._components.peerStore.has(peerInfo.id); - - if (isPeerExists) { - const peer = await this._components.peerStore.get(peerInfo.id); - const hasBootstrapTag = peer.tags.has(DEFAULT_BOOTSTRAP_TAG_NAME); - - if (!hasBootstrapTag) { - isPeerChanged = true; - await this._components.peerStore.merge(peerInfo.id, tagsToUpdate); - } - } else { - isPeerChanged = true; - await this._components.peerStore.save(peerInfo.id, tagsToUpdate); - } - - if (isPeerChanged) { - this.dispatchEvent( - new CustomEvent("peer", { detail: peerInfo }) - ); - } - } - } - - /** - * Stop emitting events - */ - stop(): void { - this._started = false; - } - - get [symbol](): true { - return true; - } - - get [Symbol.toStringTag](): string { - return "@waku/bootstrap"; - } -} - -export function wakuDnsDiscovery( - enrUrls: string[], - wantedNodeCapabilityCount: Partial -): (components: DnsDiscoveryComponents) => PeerDiscoveryDns { - return (components: DnsDiscoveryComponents) => - new PeerDiscoveryDns(components, { enrUrls, wantedNodeCapabilityCount }); -} - -export { DnsNodeDiscovery, SearchContext, DnsClient } from "./dns.js"; - -export { enrTree }; +export { PeerDiscoveryDns, wakuDnsDiscovery } from "./dns_discovery.js"; +export { enrTree } from "./constants.js"; +export { DnsNodeDiscovery } from "./dns.js"; diff --git a/packages/interfaces/src/dns_discovery.ts b/packages/interfaces/src/dns_discovery.ts new file mode 100644 index 0000000000..5253069a59 --- /dev/null +++ b/packages/interfaces/src/dns_discovery.ts @@ -0,0 +1,47 @@ +import { PeerStore } from "@libp2p/interface/peer-store"; + +export type SearchContext = { + domain: string; + publicKey: string; + visits: { [key: string]: boolean }; +}; + +export interface DnsClient { + resolveTXT: (domain: string) => Promise; +} + +export interface NodeCapabilityCount { + relay: number; + store: number; + filter: number; + lightPush: number; +} + +export interface DnsDiscoveryComponents { + peerStore: PeerStore; +} + +export interface DnsDiscOptions { + /** + * ENR URL to use for DNS discovery + */ + enrUrls: string | string[]; + /** + * Specifies what type of nodes are wanted from the discovery process + */ + wantedNodeCapabilityCount: Partial; + /** + * Tag a bootstrap peer with this name before "discovering" it (default: 'bootstrap') + */ + tagName?: string; + + /** + * The bootstrap peer tag will have this value (default: 50) + */ + tagValue?: number; + + /** + * Cause the bootstrap peer tag to be removed after this number of ms (default: 2 minutes) + */ + tagTTL?: number; +} diff --git a/packages/interfaces/src/index.ts b/packages/interfaces/src/index.ts index 7987e56ba7..80b653ddbf 100644 --- a/packages/interfaces/src/index.ts +++ b/packages/interfaces/src/index.ts @@ -13,3 +13,4 @@ export * from "./receiver.js"; export * from "./misc.js"; export * from "./libp2p.js"; export * from "./keep_alive_manager.js"; +export * from "./dns_discovery.js"; diff --git a/packages/tests/package.json b/packages/tests/package.json index 2cd4137991..99a5d8dcdd 100644 --- a/packages/tests/package.json +++ b/packages/tests/package.json @@ -72,6 +72,8 @@ "@types/dockerode": "^3.3.17", "@types/mocha": "^10.0.1", "@types/tail": "^2.2.1", + "@typescript-eslint/eslint-plugin": "^5.57.0", + "@typescript-eslint/parser": "^5.62.0", "@waku/sdk": "*", "@waku/dns-discovery": "*", "@waku/message-encryption": "*",