diff --git a/README.md b/README.md index 76f5cfb0..05592756 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ [![codecov](https://img.shields.io/codecov/c/github/multiformats/js-multiaddr.svg?style=flat-square)](https://codecov.io/gh/multiformats/js-multiaddr) [![CI](https://img.shields.io/github/actions/workflow/status/multiformats/js-multiaddr/js-test-and-release.yml?branch=main\&style=flat-square)](https://github.com/multiformats/js-multiaddr/actions/workflows/js-test-and-release.yml?query=branch%3Amain) -> multiaddr implementation (binary + string representation of network addresses) +> The JavaScript implementation of the Multiaddr spec # About @@ -26,7 +26,6 @@ repo and examine the changes made. A standard way to represent addresses that - support any standard network protocol -- are self-describing - have a binary packed format - have a nice string representation - encapsulate well @@ -45,75 +44,16 @@ addr.bytes addr.toString() // '/ip4/127.0.0.1/udp/1234' -addr.protos() +addr.getComponents() // [ -// {code: 4, name: 'ip4', size: 32}, -// {code: 273, name: 'udp', size: 16} +// { code: 4, name: 'ip4', value: '127.0.0.1' }, +// { code: 273, name: 'udp', value: '1234' } // ] -// gives you an object that is friendly with what Node.js core modules expect for addresses -addr.nodeAddress() -// { -// family: 4, -// port: 1234, -// address: "127.0.0.1" -// } - addr.encapsulate('/sctp/5678') // Multiaddr(/ip4/127.0.0.1/udp/1234/sctp/5678) ``` -## Resolving DNSADDR addresses - -[DNSADDR](https://github.com/multiformats/multiaddr/blob/master/protocols/DNSADDR.md) is a spec that allows storing a TXT DNS record that contains a Multiaddr. - -To resolve DNSADDR addresses, call the `.resolve()` function the multiaddr, optionally passing a `DNS` resolver. - -DNSADDR addresses can resolve to multiple multiaddrs, since there is no limit to the number of TXT records that can be stored. - -## Example - Resolving DNSADDR Multiaddrs - -```TypeScript -import { multiaddr, resolvers } from '@multiformats/multiaddr' -import { dnsaddrResolver } from '@multiformats/multiaddr/resolvers' - -resolvers.set('dnsaddr', dnsaddrResolver) - -const ma = multiaddr('/dnsaddr/bootstrap.libp2p.io') - -// resolve with a 5s timeout -const resolved = await ma.resolve({ - signal: AbortSignal.timeout(5000) -}) - -console.info(resolved) -// [Multiaddr('/ip4/147.75...'), Multiaddr('/ip4/147.75...'), Multiaddr('/ip4/147.75...')...] -``` - -## Example - Using a custom DNS resolver to resolve DNSADDR Multiaddrs - -See the docs for [@multiformats/dns](https://www.npmjs.com/package/@multiformats/dns) for a full breakdown of how to specify multiple resolvers or resolvers that can be used for specific TLDs. - -```TypeScript -import { multiaddr } from '@multiformats/multiaddr' -import { dns } from '@multiformats/dns' -import { dnsJsonOverHttps } from '@multiformats/dns/resolvers' - -const resolver = dns({ - resolvers: { - '.': dnsJsonOverHttps('https://cloudflare-dns.com/dns-query') - } -}) - -const ma = multiaddr('/dnsaddr/bootstrap.libp2p.io') -const resolved = await ma.resolve({ - dns: resolver -}) - -console.info(resolved) -// [Multiaddr('/ip4/147.75...'), Multiaddr('/ip4/147.75...'), Multiaddr('/ip4/147.75...')...] -``` - ## Example - Adding custom protocols To add application-specific or experimental protocols, add a protocol codec @@ -145,20 +85,6 @@ multiaddr(maWithCustomTuple) registry.removeProtocol(protocol.code) ``` -# Install - -```console -$ npm i @multiformats/multiaddr -``` - -## Browser ` -``` - # API Docs - diff --git a/benchmarks/operations/package.json b/benchmarks/operations/package.json index b97da0ba..9f4c797e 100644 --- a/benchmarks/operations/package.json +++ b/benchmarks/operations/package.json @@ -15,7 +15,7 @@ "devDependencies": { "@multiformats/multiaddr-12.4.0": "npm:@multiformats/multiaddr@12.4.0", "@multiformats/multiaddr": "../../", - "aegir": "^47.0.7", + "aegir": "^47.0.16", "tinybench": "^4.0.1" } } diff --git a/package.json b/package.json index da393dad..d90d16ba 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@multiformats/multiaddr", "version": "12.5.1", - "description": "multiaddr implementation (binary + string representation of network addresses)", + "description": "The JavaScript implementation of the Multiaddr spec", "license": "Apache-2.0 OR MIT", "homepage": "https://github.com/multiformats/js-multiaddr#readme", "repository": { @@ -22,22 +22,6 @@ ], "type": "module", "types": "./dist/src/index.d.ts", - "typesVersions": { - "*": { - "*": [ - "*", - "dist/*", - "dist/src/*", - "dist/src/*/index" - ], - "src/*": [ - "*", - "dist/*", - "dist/src/*", - "dist/src/*/index" - ] - } - }, "files": [ "src", "dist", @@ -48,14 +32,6 @@ ".": { "types": "./dist/src/index.d.ts", "import": "./dist/src/index.js" - }, - "./convert": { - "types": "./dist/src/convert.d.ts", - "import": "./dist/src/convert.js" - }, - "./resolvers": { - "types": "./dist/src/resolvers/index.d.ts", - "import": "./dist/src/resolvers/index.js" } }, "release": { @@ -169,18 +145,12 @@ }, "dependencies": { "@chainsafe/is-ip": "^2.0.1", - "@chainsafe/netmask": "^2.0.0", - "@multiformats/dns": "^1.0.3", - "abort-error": "^1.0.1", "multiformats": "^13.0.0", "uint8-varint": "^2.0.1", "uint8arrays": "^5.0.0" }, "devDependencies": { - "@types/sinon": "^17.0.2", - "aegir": "^47.0.16", - "sinon": "^21.0.0", - "sinon-ts": "^2.0.0" + "aegir": "^47.0.16" }, "browser": { "./dist/src/resolvers/dns.js": "./dist/src/resolvers/dns.browser.js" diff --git a/src/convert.ts b/src/convert.ts deleted file mode 100644 index 451ee411..00000000 --- a/src/convert.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { IpNet } from '@chainsafe/netmask' -import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string' -import { toString as uint8ArrayToString } from 'uint8arrays/to-string' -import { registry } from './registry.js' -import type { Multiaddr } from './index.ts' - -export function convertToIpNet (multiaddr: Multiaddr): IpNet { - let mask: string | undefined - let addr: string | undefined - - multiaddr.getComponents().forEach(component => { - if (component.name === 'ip4' || component.name === 'ip6') { - addr = component.value - } - if (component.name === 'ipcidr') { - mask = component.value - } - }) - - if (mask == null || addr == null) { - throw new Error('Invalid multiaddr') - } - - return new IpNet(addr, mask) -} - -/** - * converts (serializes) addresses - * - * @deprecated Will be removed in a future release - */ -export function convert (proto: string, a: string): Uint8Array -export function convert (proto: string, a: Uint8Array): string -export function convert (proto: string, a: string | Uint8Array): Uint8Array | string { - if (a instanceof Uint8Array) { - return convertToString(proto, a) - } else { - return convertToBytes(proto, a) - } -} - -/** - * Convert [code, Uint8Array] to string - * - * @deprecated Will be removed in a future release - */ -export function convertToString (proto: number | string, buf: Uint8Array): string { - const protocol = registry.getProtocol(proto) - - return protocol.bytesToValue?.(buf) ?? uint8ArrayToString(buf, 'base16') // no clue. convert to hex -} - -/** - * Convert [code, string] to Uint8Array - * - * @deprecated Will be removed in a future release - */ -export function convertToBytes (proto: string | number, str: string): Uint8Array { - const protocol = registry.getProtocol(proto) - - return protocol.valueToBytes?.(str) ?? uint8ArrayFromString(str, 'base16') // no clue. convert from hex -} diff --git a/src/filter/multiaddr-filter.ts b/src/filter/multiaddr-filter.ts deleted file mode 100644 index 66977a75..00000000 --- a/src/filter/multiaddr-filter.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { convertToIpNet } from '../convert.js' -import { multiaddr } from '../index.js' -import type { Multiaddr, MultiaddrInput } from '../index.js' -import type { IpNet } from '@chainsafe/netmask' - -/** - * A utility class to determine if a Multiaddr contains another - * multiaddr. - * - * This can be used with ipcidr ranges to determine if a given - * multiaddr is in a ipcidr range. - * - * @example - * - * ```js - * import { multiaddr, MultiaddrFilter } from '@multiformats/multiaddr' - * - * const range = multiaddr('/ip4/192.168.10.10/ipcidr/24') - * const filter = new MultiaddrFilter(range) - * - * const input = multiaddr('/ip4/192.168.10.2/udp/60') - * console.info(filter.contains(input)) // true - * ``` - */ -export class MultiaddrFilter { - private readonly multiaddr: Multiaddr - private readonly netmask: IpNet - - public constructor (input: MultiaddrInput) { - this.multiaddr = multiaddr(input) - this.netmask = convertToIpNet(this.multiaddr) - } - - public contains (input: MultiaddrInput): boolean { - if (input == null) { return false } - const m = multiaddr(input) - let ip - for (const [code, value] of m.stringTuples()) { - if (code === 4 || code === 41) { - ip = value - break - } - } - if (ip === undefined) { return false } - return this.netmask.contains(ip) - } -} diff --git a/src/index.ts b/src/index.ts index fa120e92..eed4ab3c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -4,7 +4,6 @@ * A standard way to represent addresses that * * - support any standard network protocol - * - are self-describing * - have a binary packed format * - have a nice string representation * - encapsulate well @@ -23,75 +22,16 @@ * addr.toString() * // '/ip4/127.0.0.1/udp/1234' * - * addr.protos() + * addr.getComponents() * // [ - * // {code: 4, name: 'ip4', size: 32}, - * // {code: 273, name: 'udp', size: 16} + * // { code: 4, name: 'ip4', value: '127.0.0.1' }, + * // { code: 273, name: 'udp', value: '1234' } * // ] * - * // gives you an object that is friendly with what Node.js core modules expect for addresses - * addr.nodeAddress() - * // { - * // family: 4, - * // port: 1234, - * // address: "127.0.0.1" - * // } - * * addr.encapsulate('/sctp/5678') * // Multiaddr(/ip4/127.0.0.1/udp/1234/sctp/5678) * ``` * - * ## Resolving DNSADDR addresses - * - * [DNSADDR](https://github.com/multiformats/multiaddr/blob/master/protocols/DNSADDR.md) is a spec that allows storing a TXT DNS record that contains a Multiaddr. - * - * To resolve DNSADDR addresses, call the `.resolve()` function the multiaddr, optionally passing a `DNS` resolver. - * - * DNSADDR addresses can resolve to multiple multiaddrs, since there is no limit to the number of TXT records that can be stored. - * - * @example Resolving DNSADDR Multiaddrs - * - * ```TypeScript - * import { multiaddr, resolvers } from '@multiformats/multiaddr' - * import { dnsaddrResolver } from '@multiformats/multiaddr/resolvers' - * - * resolvers.set('dnsaddr', dnsaddrResolver) - * - * const ma = multiaddr('/dnsaddr/bootstrap.libp2p.io') - * - * // resolve with a 5s timeout - * const resolved = await ma.resolve({ - * signal: AbortSignal.timeout(5000) - * }) - * - * console.info(resolved) - * // [Multiaddr('/ip4/147.75...'), Multiaddr('/ip4/147.75...'), Multiaddr('/ip4/147.75...')...] - * ``` - * - * @example Using a custom DNS resolver to resolve DNSADDR Multiaddrs - * - * See the docs for [@multiformats/dns](https://www.npmjs.com/package/@multiformats/dns) for a full breakdown of how to specify multiple resolvers or resolvers that can be used for specific TLDs. - * - * ```TypeScript - * import { multiaddr } from '@multiformats/multiaddr' - * import { dns } from '@multiformats/dns' - * import { dnsJsonOverHttps } from '@multiformats/dns/resolvers' - * - * const resolver = dns({ - * resolvers: { - * '.': dnsJsonOverHttps('https://cloudflare-dns.com/dns-query') - * } - * }) - * - * const ma = multiaddr('/dnsaddr/bootstrap.libp2p.io') - * const resolved = await ma.resolve({ - * dns: resolver - * }) - * - * console.info(resolved) - * // [Multiaddr('/ip4/147.75...'), Multiaddr('/ip4/147.75...'), Multiaddr('/ip4/147.75...')...] - * ``` - * * @example Adding custom protocols * * To add application-specific or experimental protocols, add a protocol codec @@ -124,37 +64,9 @@ * ``` */ -import { toString as uint8ArrayToString } from 'uint8arrays/to-string' -import { InvalidParametersError } from './errors.ts' import { Multiaddr as MultiaddrClass, symbol } from './multiaddr.js' import { registry, V } from './registry.ts' import type { ProtocolCodec } from './registry.ts' -import type { Resolver } from './resolvers/index.js' -import type { DNS } from '@multiformats/dns' -import type { AbortOptions } from 'abort-error' - -/** - * Protocols are present in the protocol table - * - * @deprecated - */ -export interface Protocol { - code: number - size: number - name: string - resolvable?: boolean | undefined - path?: boolean | undefined -} - -/** - * A plain JavaScript object representation of a {@link Multiaddr} - */ -export interface MultiaddrObject { - family: 4 | 6 - host: string - transport: 'tcp' | 'udp' - port: number -} /** * The protocol registry stores protocol codecs that allow transformation of @@ -178,70 +90,11 @@ export interface Registry { removeProtocol (code: number): void } -/** - * A NodeAddress is an IPv4/IPv6 address/TCP port combination - */ -export interface NodeAddress { - family: 4 | 6 - address: string - port: number -} - /** * These types can be parsed into a {@link Multiaddr} object */ export type MultiaddrInput = string | Multiaddr | Uint8Array | null | Component[] -/** - * A code/value pair - * - * @deprecated Use Component instead - */ -export type Tuple = [number, Uint8Array?] - -/** - * A code/value pair with the value as a string - * - * @deprecated Use Component instead - */ -export type StringTuple = [number, string?] - -/** - * Allows aborting long-lived operations - * - * @deprecated Import from `abort-error` instead - */ -export type { AbortOptions } - -/** - * All configured {@link Resolver}s - * - * @deprecated DNS resolving will be removed in a future release - */ -export const resolvers = new Map() - -export type { Resolver } - -export { MultiaddrFilter } from './filter/multiaddr-filter.js' - -/** - * @deprecated DNS resolving will be removed in a future release - */ -export interface ResolveOptions extends AbortOptions { - /** - * An optional DNS resolver - */ - dns?: DNS - - /** - * When resolving DNSADDR Multiaddrs that resolve to other DNSADDR Multiaddrs, - * limit how many times we will recursively resolve them. - * - * @default 32 - */ - maxRecursiveDepth?: number -} - /** * A Component is a section of a multiaddr with a name/code, possibly with a * value. @@ -315,103 +168,6 @@ export interface Multiaddr { */ getComponents(): Component[] - /** - * Returns Multiaddr as a convenient options object to be used with - * `createConnection` from `node:net` - * - * @example - * ```js - * import { multiaddr } from '@multiformats/multiaddr' - * - * multiaddr('/ip4/127.0.0.1/tcp/4001').toOptions() - * // { family: 4, host: '127.0.0.1', transport: 'tcp', port: 4001 } - * ``` - */ - toOptions(): MultiaddrObject - - /** - * Returns the protocols the Multiaddr is defined with, as an array of - * objects, in left-to-right order. Each object contains the protocol code, - * protocol name, and the size of its address space in bits. - * [See list of protocols](https://github.com/multiformats/multiaddr/blob/master/protocols.csv) - * - * @example - * ```js - * import { multiaddr } from '@multiformats/multiaddr' - * - * multiaddr('/ip4/127.0.0.1/tcp/4001').protos() - * // [ { code: 4, size: 32, name: 'ip4' }, - * // { code: 6, size: 16, name: 'tcp' } ] - * ``` - * - * @deprecated Use `getComponents()` instead - */ - protos(): Protocol[] - - /** - * Returns the codes of the protocols in left-to-right order. - * [See list of protocols](https://github.com/multiformats/multiaddr/blob/master/protocols.csv) - * - * @example - * ```js - * import { multiaddr } from '@multiformats/multiaddr' - * - * multiaddr('/ip4/127.0.0.1/tcp/4001').protoCodes() - * // [ 4, 6 ] - * ``` - * - * @deprecated Use `getComponents()` instead - */ - protoCodes(): number[] - - /** - * Returns the names of the protocols in left-to-right order. - * [See list of protocols](https://github.com/multiformats/multiaddr/blob/master/protocols.csv) - * - * @example - * ```js - * import { multiaddr } from '@multiformats/multiaddr' - * - * multiaddr('/ip4/127.0.0.1/tcp/4001').protoNames() - * // [ 'ip4', 'tcp' ] - * ``` - * - * @deprecated Use `getComponents()` instead - */ - protoNames(): string[] - - /** - * Returns a tuple of parts - * - * @example - * ```js - * import { multiaddr } from '@multiformats/multiaddr' - * - * multiaddr('/ip4/127.0.0.1/tcp/4001').tuples() - * // [ [ 4, ], [ 6, ] ] - * ``` - * - * @deprecated Use `getComponents()` instead - */ - tuples(): Tuple[] - - /** - * Returns a tuple of string/number parts - * - tuples[][0] = code of protocol - * - tuples[][1] = contents of address - * - * @example - * ```js - * import { multiaddr } from '@multiformats/multiaddr' - * - * multiaddr('/ip4/127.0.0.1/tcp/4001').stringTuples() - * // [ [ 4, '127.0.0.1' ], [ 6, '4001' ] ] - * ``` - * - * @deprecated Use `getComponents()` instead - */ - stringTuples(): StringTuple[] - /** * Encapsulates a Multiaddr in another Multiaddr * @@ -482,42 +238,6 @@ export interface Multiaddr { */ decapsulateCode(code: number): Multiaddr - /** - * Extract the peerId if the multiaddr contains one - * - * @example - * ```js - * import { multiaddr } from '@multiformats/multiaddr' - * - * const mh1 = multiaddr('/ip4/8.8.8.8/tcp/1080/ipfs/QmValidBase58string') - * // Multiaddr(/ip4/8.8.8.8/tcp/1080/ipfs/QmValidBase58string) - * - * // should return QmValidBase58string or null if the id is missing or invalid - * const peerId = mh1.getPeerId() - * ``` - * - * @deprecated A multiaddr can contain multiple PeerIds, use stringTuples() to get a specific one - */ - getPeerId(): string | null - - /** - * Extract the path if the multiaddr contains one - * - * @example - * ```js - * import { multiaddr } from '@multiformats/multiaddr' - * - * const mh1 = multiaddr('/ip4/8.8.8.8/tcp/1080/unix/tmp/p2p.sock') - * // Multiaddr(/ip4/8.8.8.8/tcp/1080/unix/tmp/p2p.sock) - * - * // should return utf8 string or null if the id is missing or invalid - * const path = mh1.getPath() - * ``` - * - * @deprecated A multiaddr can contain multiple tuples that could be interpreted as paths, use stringTuples() to get a specific one - */ - getPath(): string | null - /** * Checks if two Multiaddrs are the same * @@ -539,218 +259,6 @@ export interface Multiaddr { * ``` */ equals(addr: { bytes: Uint8Array }): boolean - - /** - * Resolve multiaddr if containing resolvable hostname. - * - * @example - * ```js - * import { multiaddr, resolvers } from '@multiformats/multiaddr' - * - * resolvers.set('dnsaddr', resolverFunction) - * const mh1 = multiaddr('/dnsaddr/bootstrap.libp2p.io/p2p/QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb') - * const resolvedMultiaddrs = await mh1.resolve() - * // [ - * // Multiaddr(/ip4/147.75.83.83/tcp/4001/p2p/QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb), - * // Multiaddr(/ip4/147.75.83.83/tcp/443/wss/p2p/QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb), - * // Multiaddr(/ip4/147.75.83.83/udp/4001/quic/p2p/QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb) - * // ] - * ``` - * - * @deprecated If you need to resolve `dnsaddr` addresses, use `getComponents()` to extract them and perform the resolution yourself - */ - resolve(options?: ResolveOptions): Promise - - /** - * Gets a Multiaddrs node-friendly address object. Note that protocol - * information is left out: in Node (and most network systems) the protocol is - * unknowable given only the address. - * - * Has to be a ThinWaist Address, otherwise throws error - * - * @example - * ```js - * import { multiaddr } from '@multiformats/multiaddr' - * - * multiaddr('/ip4/127.0.0.1/tcp/4001').nodeAddress() - * // {family: 4, address: '127.0.0.1', port: 4001} - * ``` - */ - nodeAddress(): NodeAddress - - /** - * Returns if a Multiaddr is a Thin Waist address or not. - * - * Thin Waist is if a Multiaddr adheres to the standard combination of: - * - * `{IPv4, IPv6}/{TCP, UDP}` - * - * @example - * ```js - * import { multiaddr } from '@multiformats/multiaddr' - * - * const mh1 = multiaddr('/ip4/127.0.0.1/tcp/4001') - * // Multiaddr(/ip4/127.0.0.1/tcp/4001) - * const mh2 = multiaddr('/ip4/192.168.2.1/tcp/5001') - * // Multiaddr(/ip4/192.168.2.1/tcp/5001) - * const mh3 = mh1.encapsulate(mh2) - * // Multiaddr(/ip4/127.0.0.1/tcp/4001/ip4/192.168.2.1/tcp/5001) - * const mh4 = multiaddr('/ip4/127.0.0.1/tcp/2000/wss/p2p-webrtc-star/p2p/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSooo2a') - * // Multiaddr(/ip4/127.0.0.1/tcp/2000/wss/p2p-webrtc-star/p2p/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSooo2a) - * mh1.isThinWaistAddress() - * // true - * mh2.isThinWaistAddress() - * // true - * mh3.isThinWaistAddress() - * // false - * mh4.isThinWaistAddress() - * // false - * ``` - */ - isThinWaistAddress(addr?: Multiaddr): boolean -} - -/** - * Creates a Multiaddr from a node-friendly address object - * - * @example - * ```js - * import { fromNodeAddress } from '@multiformats/multiaddr' - * - * fromNodeAddress({address: '127.0.0.1', port: '4001'}, 'tcp') - * // Multiaddr(/ip4/127.0.0.1/tcp/4001) - * ``` - */ -export function fromNodeAddress (addr: NodeAddress, transport: string): Multiaddr { - if (addr == null) { - throw new InvalidParametersError('requires node address object') - } - if (transport == null) { - throw new InvalidParametersError('requires transport protocol') - } - let ip: string | undefined - let host = addr.address - switch (addr.family) { - case 4: - ip = 'ip4' - break - case 6: - ip = 'ip6' - - if (host.includes('%')) { - const parts = host.split('%') - - if (parts.length !== 2) { - throw Error('Multiple ip6 zones in multiaddr') - } - - host = parts[0] - const zone = parts[1] - ip = `ip6zone/${zone}/ip6` - } - break - default: - throw Error('Invalid addr family, should be 4 or 6.') - } - - return new MultiaddrClass('/' + [ip, host, transport, addr.port].join('/')) -} - -/** - * Create a {@link Multiaddr} from an array of {@link Tuple}s - * - * @example - * - * ```ts - * import { fromTuples, multiaddr } from '@multiformats/multiaddr' - * - * const ma = multiaddr('/ip4/127.0.0.1') - * const tuples = ma.tuples() - * - * const ma2 = fromTuples(tuples) - * - * console.info(ma2) - * // '/ip4/127.0.0.1' - * ``` - * - * @deprecated Will be removed in a future release - */ -export function fromTuples (tuples: Tuple[]): Multiaddr { - return multiaddr(tuples.map(([code, value]) => { - const codec = registry.getProtocol(code) - - const component: Component = { - code, - name: codec.name - } - - if (value != null) { - component.value = codec.bytesToValue?.(value) ?? uint8ArrayToString(value) - } - - return component - })) -} - -/** - * Create a {@link Multiaddr} from an array of {@link StringTuple}s - * - * @example - * - * ```ts - * import { fromStringTuples, multiaddr } from '@multiformats/multiaddr' - * - * const ma = multiaddr('/ip4/127.0.0.1') - * const tuples = ma.stringTuples() - * - * const ma2 = fromStringTuples(tuples) - * - * console.info(ma2) - * // '/ip4/127.0.0.1' - * ``` - * - * @deprecated Will be removed in a future release - */ -export function fromStringTuples (tuples: StringTuple[]): Multiaddr { - return multiaddr(tuples.map(([code, value]) => { - const codec = registry.getProtocol(code) - - const component: Component = { - code, - name: codec.name - } - - if (value != null) { - component.value = value - } - - return component - })) -} - -/** - * Returns if something is a {@link Multiaddr} that is a resolvable name - * - * @example - * - * ```js - * import { isName, multiaddr } from '@multiformats/multiaddr' - * - * isName(multiaddr('/ip4/127.0.0.1')) - * // false - * isName(multiaddr('/dns/ipfs.io')) - * // true - * ``` - * - * @deprecated DNS resolving will be removed in a future release - */ -export function isName (addr: Multiaddr): boolean { - if (!isMultiaddr(addr)) { - return false - } - - // if a part of the multiaddr is resolvable, then return true - return addr.protos().some((proto) => proto.resolvable) } /** @@ -788,32 +296,6 @@ export function multiaddr (addr?: MultiaddrInput): Multiaddr { return new MultiaddrClass(addr) } -/** - * For the passed proto string or number, return a {@link Protocol} - * - * @example - * - * ```js - * import { protocol } from '@multiformats/multiaddr' - * - * console.info(protocol(4)) - * // { code: 4, size: 32, name: 'ip4', resolvable: false, path: false } - * ``` - * - * @deprecated This will be removed in a future version - */ -export function protocols (proto: number | string): Protocol { - const codec = registry.getProtocol(proto) - - return { - code: codec.code, - size: codec.size ?? 0, - name: codec.name, - resolvable: Boolean(codec.resolvable), - path: Boolean(codec.path) - } -} - /** * Export all table.csv codes. These are all named exports so can be tree-shaken * out by bundlers. diff --git a/src/multiaddr.ts b/src/multiaddr.ts index 1964dc12..919a194b 100644 --- a/src/multiaddr.ts +++ b/src/multiaddr.ts @@ -1,32 +1,13 @@ -import { base58btc } from 'multiformats/bases/base58' -import { CID } from 'multiformats/cid' import { equals as uint8ArrayEquals } from 'uint8arrays/equals' -import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string' -import { toString as uint8ArrayToString } from 'uint8arrays/to-string' import { bytesToComponents, componentsToBytes, componentsToString, stringToComponents } from './components.js' -import { CODE_DNS, CODE_DNS4, CODE_DNS6, CODE_DNSADDR, CODE_IP4, CODE_IP6, CODE_IP6ZONE, CODE_P2P, CODE_P2P_CIRCUIT, CODE_TCP, CODE_UDP } from './constants.ts' import { InvalidMultiaddrError, InvalidParametersError } from './errors.ts' import { registry } from './registry.ts' -import { isMultiaddr, multiaddr, resolvers } from './index.js' -import type { MultiaddrInput, Multiaddr as MultiaddrInterface, MultiaddrObject, Protocol, Tuple, NodeAddress, ResolveOptions, Component } from './index.js' +import { isMultiaddr } from './index.js' +import type { MultiaddrInput, Multiaddr as MultiaddrInterface, Component } from './index.js' const inspect = Symbol.for('nodejs.util.inspect.custom') export const symbol = Symbol.for('@multiformats/multiaddr') -const DNS_CODES = [ - CODE_DNS, - CODE_DNS4, - CODE_DNS6, - CODE_DNSADDR -] - -class NoAvailableResolverError extends Error { - constructor (message = 'No available resolver') { - super(message) - this.name = 'NoAvailableResolverError' - } -} - function toComponents (addr: MultiaddrInput): Component[] { if (addr == null) { addr = '/' @@ -103,107 +84,12 @@ export class Multiaddr implements MultiaddrInterface { return this.toString() } - toOptions (): MultiaddrObject { - let family: 4 | 6 | undefined - let transport: 'tcp' | 'udp' | undefined - let host: string | undefined - let port: number | undefined - let zone = '' - - for (const { code, name, value } of this.#components) { - if (code === CODE_IP6ZONE) { - zone = `%${value ?? ''}` - } - - // default to https when protocol & port are omitted from DNS addrs - if (DNS_CODES.includes(code)) { - transport = 'tcp' - port = 443 - host = `${value ?? ''}${zone}` - family = code === CODE_DNS6 ? 6 : 4 - } - - if (code === CODE_TCP || code === CODE_UDP) { - transport = name === 'tcp' ? 'tcp' : 'udp' - port = parseInt(value ?? '') - } - - if (code === CODE_IP4 || code === CODE_IP6) { - transport = 'tcp' - host = `${value ?? ''}${zone}` - family = code === CODE_IP6 ? 6 : 4 - } - } - - if (family == null || transport == null || host == null || port == null) { - throw new Error('multiaddr must have a valid format: "/{ip4, ip6, dns4, dns6, dnsaddr}/{address}/{tcp, udp}/{port}".') - } - - const opts: MultiaddrObject = { - family, - host, - transport, - port - } - - return opts - } - getComponents (): Component[] { return [ ...this.#components ] } - protos (): Protocol[] { - return this.#components.map(({ code, value }) => { - const codec = registry.getProtocol(code) - - return { - code, - size: codec.size ?? 0, - name: codec.name, - resolvable: Boolean(codec.resolvable), - path: Boolean(codec.path) - } - }) - } - - protoCodes (): number[] { - return this.#components.map(({ code }) => code) - } - - protoNames (): string[] { - return this.#components.map(({ name }) => name) - } - - tuples (): Tuple[] { - return this.#components.map(({ code, value }) => { - if (value == null) { - return [code] - } - - const codec = registry.getProtocol(code) - const output: Tuple = [code] - - if (value != null) { - output.push(codec.valueToBytes?.(value) ?? uint8ArrayFromString(value)) - } - - return output - }) - } - - stringTuples (): Array<[number, string?]> { - return this.#components.map(({ code, value }) => { - if (value == null) { - return [code] - } - - return [code, value] - }) - } - encapsulate (addr: MultiaddrInput): MultiaddrInterface { const ma = new Multiaddr(addr) @@ -221,7 +107,7 @@ export class Multiaddr implements MultiaddrInterface { const i = s.lastIndexOf(addrString) if (i < 0) { - throw new InvalidParametersError(`Address ${this.toString()} does not contain subaddress: ${addr.toString()}`) + throw new InvalidParametersError(`Address ${this.toString()} does not contain subaddress: ${addrString}`) } return new Multiaddr(s.slice(0, i), { @@ -244,109 +130,10 @@ export class Multiaddr implements MultiaddrInterface { }) } - getPeerId (): string | null { - try { - let tuples: Array<[number, string | undefined]> = [] - - this.#components.forEach(({ code, value }) => { - if (code === CODE_P2P) { - tuples.push([code, value]) - } - - // if this is a p2p-circuit address, return the target peer id if present - // not the peer id of the relay - if (code === CODE_P2P_CIRCUIT) { - tuples = [] - } - }) - - // Get the last ipfs tuple ['p2p', 'peerid string'] - const tuple = tuples.pop() - if (tuple?.[1] != null) { - const peerIdStr = tuple[1] - - // peer id is base58btc encoded string but not multibase encoded so add the `z` - // prefix so we can validate that it is correctly encoded - if (peerIdStr[0] === 'Q' || peerIdStr[0] === '1') { - return uint8ArrayToString(base58btc.decode(`z${peerIdStr}`), 'base58btc') - } - - // try to parse peer id as CID - return uint8ArrayToString(CID.parse(peerIdStr).multihash.bytes, 'base58btc') - } - - return null - } catch (e) { - return null - } - } - - getPath (): string | null { - for (const component of this.#components) { - const codec = registry.getProtocol(component.code) - - if (!codec.path) { - continue - } - - return component.value ?? null - } - - return null - } - equals (addr: { bytes: Uint8Array }): boolean { return uint8ArrayEquals(this.bytes, addr.bytes) } - async resolve (options?: ResolveOptions): Promise { - const resolvableProto = this.protos().find((p) => p.resolvable) - - // Multiaddr is not resolvable? - if (resolvableProto == null) { - return [this] - } - - const resolver = resolvers.get(resolvableProto.name) - if (resolver == null) { - throw new NoAvailableResolverError(`no available resolver for ${resolvableProto.name}`) - } - - const result = await resolver(this, options) - - return result.map(str => multiaddr(str)) - } - - nodeAddress (): NodeAddress { - const options = this.toOptions() - - if (options.transport !== 'tcp' && options.transport !== 'udp') { - throw new Error(`multiaddr must have a valid format - no protocol with name: "${options.transport}". Must have a valid transport protocol: "{tcp, udp}"`) - } - - return { - family: options.family, - address: options.host, - port: options.port - } - } - - isThinWaistAddress (): boolean { - if (this.#components.length !== 2) { - return false - } - - if (this.#components[0].code !== CODE_IP4 && this.#components[0].code !== CODE_IP6) { - return false - } - - if (this.#components[1].code !== CODE_TCP && this.#components[1].code !== CODE_UDP) { - return false - } - - return true - } - /** * Returns Multiaddr as a human-readable string * https://nodejs.org/api/util.html#utilinspectcustom diff --git a/src/protocols-table.ts b/src/protocols-table.ts deleted file mode 100644 index 82105da5..00000000 --- a/src/protocols-table.ts +++ /dev/null @@ -1,100 +0,0 @@ -import type { Protocol } from './index.js' - -const V = -1 -export const names: Record = {} -export const codes: Record = {} - -export const table: Array<[number, number, string, boolean?, boolean?]> = [ - [4, 32, 'ip4'], - [6, 16, 'tcp'], - [33, 16, 'dccp'], - [41, 128, 'ip6'], - [42, V, 'ip6zone'], - [43, 8, 'ipcidr'], - [53, V, 'dns', true], - [54, V, 'dns4', true], - [55, V, 'dns6', true], - [56, V, 'dnsaddr', true], - [132, 16, 'sctp'], - [273, 16, 'udp'], - [275, 0, 'p2p-webrtc-star'], - [276, 0, 'p2p-webrtc-direct'], - [277, 0, 'p2p-stardust'], - [280, 0, 'webrtc-direct'], - [281, 0, 'webrtc'], - [290, 0, 'p2p-circuit'], - [301, 0, 'udt'], - [302, 0, 'utp'], - [400, V, 'unix', false, true], - // `ipfs` is added before `p2p` for legacy support. - // All text representations will default to `p2p`, but `ipfs` will - // still be supported - [421, V, 'ipfs'], - // `p2p` is the preferred name for 421, and is now the default - [421, V, 'p2p'], - [443, 0, 'https'], - [444, 96, 'onion'], - [445, 296, 'onion3'], - [446, V, 'garlic64'], - [448, 0, 'tls'], - [449, V, 'sni'], - [460, 0, 'quic'], - [461, 0, 'quic-v1'], - [465, 0, 'webtransport'], - [466, V, 'certhash'], - [477, 0, 'ws'], - [478, 0, 'wss'], - [479, 0, 'p2p-websocket-star'], - [480, 0, 'http'], - [481, V, 'http-path'], - [777, V, 'memory'] -] - -// populate tables -table.forEach(row => { - const proto = createProtocol(...row) - codes[proto.code] = proto - names[proto.name] = proto -}) - -export function createProtocol (code: number, size: number, name: string, resolvable?: any, path?: any): Protocol { - return { - code, - size, - name, - resolvable: Boolean(resolvable), - path: Boolean(path) - } -} - -/** - * For the passed proto string or number, return a {@link Protocol} - * - * @example - * - * ```js - * import { protocol } from '@multiformats/multiaddr' - * - * console.info(protocol(4)) - * // { code: 4, size: 32, name: 'ip4', resolvable: false, path: false } - * ``` - * - * @deprecated This will be removed in a future version - */ -export function getProtocol (proto: number | string): Protocol { - if (typeof proto === 'number') { - if (codes[proto] != null) { - return codes[proto] - } - - throw new Error(`no protocol with code: ${proto}`) - } else if (typeof proto === 'string') { - if (names[proto] != null) { - return names[proto] - } - - throw new Error(`no protocol with name: ${proto}`) - } - - throw new Error(`invalid protocol id type: ${typeof proto}`) -} diff --git a/src/registry.ts b/src/registry.ts index bd8662d9..1c345467 100644 --- a/src/registry.ts +++ b/src/registry.ts @@ -28,20 +28,6 @@ export interface ProtocolCodec { */ size?: number - /** - * If this protocol is a path protocol. - * - * @deprecated This will be removed in a future release - */ - path?: boolean - - /** - * If this protocol can be resolved using configured resolvers. - * - * @deprecated This will be removed in a future release - */ - resolvable?: boolean - /** * If specified this protocol codec will also be used to decode tuples with * these names from string multiaddrs. @@ -179,23 +165,19 @@ const codecs: ProtocolCodec[] = [{ }, { code: CODE_DNS, name: 'dns', - size: V, - resolvable: true + size: V }, { code: CODE_DNS4, name: 'dns4', - size: V, - resolvable: true + size: V }, { code: CODE_DNS6, name: 'dns6', - size: V, - resolvable: true + size: V }, { code: CODE_DNSADDR, name: 'dnsaddr', - size: V, - resolvable: true + size: V }, { code: CODE_SCTP, name: 'sctp', @@ -213,7 +195,6 @@ const codecs: ProtocolCodec[] = [{ code: CODE_UNIX, name: 'unix', size: V, - path: true, stringToValue: (str) => decodeURIComponent(str), valueToString: (val) => encodeURIComponent(val) }, { diff --git a/src/resolvers/dnsaddr.ts b/src/resolvers/dnsaddr.ts deleted file mode 100644 index e5db8581..00000000 --- a/src/resolvers/dnsaddr.ts +++ /dev/null @@ -1,88 +0,0 @@ -import { dns, RecordType } from '@multiformats/dns' -import { multiaddr } from '../index.js' -import { getProtocol } from '../protocols-table.js' -import type { Resolver } from './index.js' -import type { AbortOptions, Multiaddr } from '../index.js' -import type { DNS } from '@multiformats/dns' - -const MAX_RECURSIVE_DEPTH = 32 -const { code: dnsaddrCode } = getProtocol('dnsaddr') - -class RecursionLimitError extends Error { - constructor (message = 'Max recursive depth reached') { - super(message) - this.name = 'RecursionLimitError' - } -} - -/** - * @deprecated DNS resolving will be removed in a future release - */ -export interface DNSADDROptions extends AbortOptions { - /** - * An optional DNS resolver - */ - dns?: DNS - - /** - * When resolving DNSADDR Multiaddrs that resolve to other DNSADDR Multiaddrs, - * limit how many times we will recursively resolve them. - * - * @default 32 - */ - maxRecursiveDepth?: number -} - -/** - * @deprecated DNS resolving will be removed in a future release - */ -export const dnsaddrResolver: Resolver = async function dnsaddrResolver (ma: Multiaddr, options: DNSADDROptions = {}): Promise { - const recursionLimit = options.maxRecursiveDepth ?? MAX_RECURSIVE_DEPTH - - if (recursionLimit === 0) { - throw new RecursionLimitError('Max recursive depth reached') - } - - const [, hostname] = ma.stringTuples().find(([proto]) => proto === dnsaddrCode) ?? [] - - const resolver = options?.dns ?? dns() - const result = await resolver.query(`_dnsaddr.${hostname}`, { - signal: options?.signal, - types: [ - RecordType.TXT - ] - }) - - const peerId = ma.getPeerId() - const output: string[] = [] - - for (const answer of result.Answer) { - const addr = answer.data - .replace(/["']/g, '') - .trim() - .split('=')[1] - - if (addr == null) { - continue - } - - if (peerId != null && !addr.includes(peerId)) { - continue - } - - const ma = multiaddr(addr) - - if (addr.startsWith('/dnsaddr')) { - const resolved = await ma.resolve({ - ...options, - maxRecursiveDepth: recursionLimit - 1 - }) - - output.push(...resolved.map(ma => ma.toString())) - } else { - output.push(ma.toString()) - } - } - - return output -} diff --git a/src/resolvers/index.ts b/src/resolvers/index.ts deleted file mode 100644 index 66d8320b..00000000 --- a/src/resolvers/index.ts +++ /dev/null @@ -1,11 +0,0 @@ -import type { AbortOptions, Multiaddr } from '../index.js' - -/** - * @deprecated DNS resolving will be removed in a future release - */ -export interface Resolver { - (ma: Multiaddr, options?: ResolveOptions): Promise -} - -export { dnsaddrResolver } from './dnsaddr.js' -export type { DNSADDROptions } from './dnsaddr.js' diff --git a/test/convert.spec.ts b/test/convert.spec.ts deleted file mode 100644 index 642776df..00000000 --- a/test/convert.spec.ts +++ /dev/null @@ -1,24 +0,0 @@ -/* eslint-env mocha */ -import { expect } from 'aegir/chai' -import { convertToIpNet } from '../src/convert.ts' -import { multiaddr } from '../src/index.js' - -describe('convert', () => { - it('convertToIpNet ip4', function () { - const ipnet = convertToIpNet(multiaddr('/ip4/192.0.2.0/ipcidr/24')) - expect(ipnet.toString()).equal('192.0.2.0/24') - }) - - it('convertToIpNet ip6', function () { - const ipnet = convertToIpNet(multiaddr('/ip6/2001:0db8:85a3:0000:0000:8a2e:0370:7334/ipcidr/64')) - expect(ipnet.toString()).equal('2001:0db8:85a3:0000:0000:0000:0000:0000/64') - }) - - it('convertToIpNet not ipcidr', function () { - expect(() => convertToIpNet(multiaddr('/ip6/2001:0db8:85a3:0000:0000:8a2e:0370:7334/tcp/64'))).to.throw() - }) - - it('convertToIpNet not ipv6', function () { - expect(() => convertToIpNet(multiaddr('/dns6/foo.com/ipcidr/64'))).to.throw() - }) -}) diff --git a/test/filter/multiaddr-filter.spec.ts b/test/filter/multiaddr-filter.spec.ts deleted file mode 100644 index dbf7f55e..00000000 --- a/test/filter/multiaddr-filter.spec.ts +++ /dev/null @@ -1,28 +0,0 @@ -/* eslint-env mocha */ -import { expect } from 'aegir/chai' -import { MultiaddrFilter, multiaddr } from '../../src/index.js' -import type { MultiaddrInput } from '../../src/index.js' - -describe('MultiaddrFilter', () => { - const cases: Array<[MultiaddrInput, MultiaddrInput, boolean]> = [ - ['/ip4/192.168.10.10/ipcidr/24', '/ip4/192.168.10.2/tcp/60', true], - [multiaddr('/ip4/192.168.10.10/ipcidr/24'), '/ip4/192.168.10.2/tcp/60', true], - [multiaddr('/ip4/192.168.10.10/ipcidr/24').bytes, '/ip4/192.168.10.2/tcp/60', true], - ['/ip4/192.168.10.10/ipcidr/24', '/ip4/192.168.10.2/udp/60', true], - ['/ip4/192.168.10.10/ipcidr/24', multiaddr('/ip4/192.168.11.2/tcp/60'), false], - ['/ip4/192.168.10.10/ipcidr/24', null, false], - ['/ip4/192.168.10.10/ipcidr/24', multiaddr('/ip4/192.168.11.2/udp/60').bytes, false], - ['/ip4/192.168.10.10/ipcidr/24', '/ip4/192.168.11.2/udp/60', false], - ['/ip4/192.168.10.10/ipcidr/24', '/ip6/2001:db8:3333:4444:5555:6666:7777:8888/tcp/60', false], - ['/ip6/2001:db8:3333:4444:5555:6666:7777:8888/ipcidr/60', '/ip6/2001:0db8:3333:4440:0000:0000:0000:0000/tcp/60', true], - ['/ip6/2001:db8:3333:4444:5555:6666:7777:8888/ipcidr/60', '/ip6/2001:0db8:3333:4450:0000:0000:0000:0000/tcp/60', false], - ['/ip6/2001:db8:3333:4444:5555:6666:7777:8888/ipcidr/128', '/ip6/2001:db8:3333:4444:5555:6666:7777:8888/tcp/60', true], - ['/ip6/2001:db8:3333:4444:5555:6666:7777:8888/ipcidr/128', '/ip6/2001:db8:3333:4444:5555:6666:7777:8880/tcp/60', false] - ] - - cases.forEach(([cidr, ip, result]) => { - it(`multiaddr filter cidr=${cidr} ip=${ip} result=${String(result)}`, function () { - expect(new MultiaddrFilter(cidr).contains(ip)).to.be.equal(result) - }) - }) -}) diff --git a/test/index.spec.ts b/test/index.spec.ts index d37b983f..f73f274a 100644 --- a/test/index.spec.ts +++ b/test/index.spec.ts @@ -2,8 +2,7 @@ /* eslint-env mocha */ import { expect } from 'aegir/chai' import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string' -import { multiaddr, isMultiaddr, fromNodeAddress, isName, fromTuples, fromStringTuples } from '../src/index.js' -import { codes } from '../src/protocols-table.js' +import { multiaddr, isMultiaddr, CODE_IP4 } from '../src/index.js' import type { Multiaddr } from '../src/index.js' function roundTrip (str: string): void { @@ -140,10 +139,17 @@ describe('manipulation', () => { expect(udpAddr.toString()).to.equal(udpAddrStr) expect(udpAddr.bytes).to.equalBytes(udpAddrBuf) - expect(udpAddr.protoCodes()).to.deep.equal([4, 273]) - expect(udpAddr.protoNames()).to.deep.equal(['ip4', 'udp']) - expect(udpAddr.protos()).to.deep.equal([codes[4], codes[273]]) - expect(udpAddr.protos()[0] === codes[4]).to.equal(false) + expect(udpAddr.getComponents()).to.deep.equal([{ + bytes: Uint8Array.from([4, 127, 0, 0, 1]), + code: 4, + name: 'ip4', + value: '127.0.0.1' + }, { + bytes: Uint8Array.from([145, 2, 4, 210]), + code: 273, + name: 'udp', + value: '1234' + }]) const udpAddrbytes2 = udpAddr.encapsulate('/udp/5678') expect(udpAddrbytes2.toString()).to.equal('/ip4/127.0.0.1/udp/1234/udp/5678') @@ -331,560 +337,144 @@ describe('normalize', () => { } }) -describe('helpers', () => { - describe('.toOptions', () => { - it('returns a well formed options object', () => { - expect(multiaddr('/ip4/0.0.0.0/tcp/1234').toOptions()) - .to.eql({ - family: 4, - host: '0.0.0.0', - transport: 'tcp', - port: 1234 - }) - }) - - it('returns an options object from a DNS addr', () => { - expect(multiaddr('/dns4/google.net/tcp/8000').toOptions()) - .to.eql({ - family: 4, - host: 'google.net', - transport: 'tcp', - port: 8000 - }) - }) - - it('returns an options object from a DNS6 addr', () => { - expect(multiaddr('/dns6/google.net/tcp/8000').toOptions()) - .to.eql({ - family: 6, - host: 'google.net', - transport: 'tcp', - port: 8000 - }) - }) - - it('returns an options object from a DNS addr defaulting to https', () => { - expect(multiaddr('/dnsaddr/google.net').toOptions()) - .to.eql({ - family: 4, - host: 'google.net', - transport: 'tcp', - port: 443 - }) - }) - - it('returns an options object from a DNS addr with a PeerID defaulting to https', () => { - expect(multiaddr('/dnsaddr/google.net/p2p/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC').toOptions()) - .to.eql({ - family: 4, - host: 'google.net', - transport: 'tcp', - port: 443 - }) - }) - - it('returns an options object from an address with an ip6 zone', () => { - expect( - multiaddr('/ip6zone/x/ip6/fe80::1/tcp/1234').toOptions() - ).to.be.eql({ - family: 6, - host: 'fe80::1%x', - transport: 'tcp', - port: 1234 - }) - }) - }) - - describe('.protos', () => { - it('returns a list of all protocols in the address', () => { - expect(multiaddr('/ip4/0.0.0.0/utp').protos()) - .to.eql([{ - code: 4, - name: 'ip4', - path: false, - size: 32, - resolvable: false - }, { - code: 302, - name: 'utp', - path: false, - size: 0, - resolvable: false - }]) - }) - - it('works with ipfs', () => { - expect( - multiaddr('/ip4/0.0.0.0/utp/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC').protos() - ).to.be.eql([{ - code: 4, - name: 'ip4', - path: false, - size: 32, - resolvable: false - }, { - code: 302, - name: 'utp', - path: false, - size: 0, - resolvable: false - }, { - code: 421, - name: 'p2p', - path: false, - size: -1, - resolvable: false - }]) - }) - - it('works with p2p', () => { - expect( - multiaddr('/ip4/0.0.0.0/utp/p2p/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC').protos() - ).to.be.eql([{ +describe('.getComponents', () => { + it('returns a list of all protocols in the address', () => { + expect(multiaddr('/ip4/0.0.0.0/utp').getComponents()) + .to.eql([{ code: 4, name: 'ip4', - path: false, - size: 32, - resolvable: false + value: '0.0.0.0' }, { code: 302, - name: 'utp', - path: false, - size: 0, - resolvable: false - }, { - code: 421, - name: 'p2p', - path: false, - size: -1, - resolvable: false - }]) - }) - - it('works with unix', () => { - expect( - multiaddr('/ip4/0.0.0.0/tcp/8000/unix/tmp%2Fp2p.sock').protos() - ).to.be.eql([{ - code: 4, - name: 'ip4', - path: false, - size: 32, - resolvable: false - }, { - code: 6, - name: 'tcp', - path: false, - size: 16, - resolvable: false - }, { - code: 400, - name: 'unix', - path: true, - size: -1, - resolvable: false - }]) - }) - - it('works with memory', () => { - expect( - multiaddr('/memory/test/p2p/QmZR5a9AAXGqQF2ADqoDdGS8zvqv8n3Pag6TDDnTNMcFW6').protos() - ).to.be.eql([{ - code: 777, - name: 'memory', - path: false, - size: -1, - resolvable: false - }, { - code: 421, - name: 'p2p', - path: false, - size: -1, - resolvable: false + name: 'utp' }]) - }) - }) - - describe('.tuples', () => { - it('returns the tuples', () => { - expect(multiaddr('/ip4/0.0.0.0/utp').tuples()) - .to.eql([ - [4, Uint8Array.from([0, 0, 0, 0])], - [302] - ]) - }) - - it('does not allow modifying parts', () => { - const ma = multiaddr('/ip4/0.0.0.0/tcp/1234') - const tuples = ma.tuples() - tuples[0][0] = 41 - - expect(ma.toOptions()).to.have.property('family', 4) - }) - }) - - describe('.stringTuples', () => { - it('returns the string partss', () => { - expect(multiaddr('/ip4/0.0.0.0/utp').stringTuples()) - .to.eql([ - [4, '0.0.0.0'], - [302] - ]) - }) - - it('does not allow modifying string parts', () => { - const ma = multiaddr('/ip4/0.0.0.0/tcp/1234') - const tuples = ma.stringTuples() - tuples[0][0] = 41 - - expect(ma.toOptions()).to.have.property('family', 4) - }) }) - describe('.decapsulate', () => { - it('throws on address with no matching subaddress', () => { - expect( - () => multiaddr('/ip4/127.0.0.1').decapsulate('/ip4/198.168.0.0') - ).to.throw( - /does not contain subaddress/ - ) - }) - }) - - describe('.decapsulateCode', () => { - it('removes the last occurrence of the code from the multiaddr', () => { - const relayTCP = multiaddr('/ip4/0.0.0.0/tcp/8080') - const relay = relayTCP.encapsulate('/p2p/QmZR5a9AAXGqQF2ADqoDdGS8zvqv8n3Pag6TDDnTNMcFW6/p2p-circuit') - const target = multiaddr('/p2p/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC') - const original = relay.encapsulate(target) - expect(original.decapsulateCode(421).toJSON()).to.equal(relay.toJSON()) - expect(relay.decapsulateCode(421).toJSON()).to.equal(relayTCP.toJSON()) - }) - - it('ignores missing codes', () => { - const tcp = multiaddr('/ip4/0.0.0.0/tcp/8080') - expect(tcp.decapsulateCode(421).toJSON()).to.equal(tcp.toJSON()) - }) - }) - - describe('.equals', () => { - it('returns true for equal addresses', () => { - const addr1 = multiaddr('/ip4/192.168.0.1') - const addr2 = multiaddr('/ip4/192.168.0.1') - - expect(addr1.equals(addr2)).to.equal(true) - }) - - it('returns false for non equal addresses', () => { - const addr1 = multiaddr('/ip4/192.168.1.1') - const addr2 = multiaddr('/ip4/192.168.0.1') - - expect(addr1.equals(addr2)).to.equal(false) - }) - }) - - describe('.nodeAddress', () => { - it('throws on an invalid node address', () => { - expect( - () => multiaddr('/ip4/192.168.0.1/utp').nodeAddress() - ).to.throw( - /multiaddr must have a valid format/ - ) - }) - - it('returns a node friendly address', () => { - expect( - multiaddr('/ip4/192.168.0.1/tcp/1234').nodeAddress() - ).to.be.eql({ - address: '192.168.0.1', - family: 4, - port: 1234 - }) - }) - - it('returns a node friendly address with dns', () => { - expect( - multiaddr('/dns/wss0.bootstrap.libp2p.io/tcp/443').nodeAddress() - ).to.be.eql({ - address: 'wss0.bootstrap.libp2p.io', - family: 4, - port: 443 - }) - }) - - it('returns a node friendly address with dns4', () => { - expect( - multiaddr('/dns4/wss0.bootstrap.libp2p.io/tcp/443').nodeAddress() - ).to.be.eql({ - address: 'wss0.bootstrap.libp2p.io', - family: 4, - port: 443 - }) - }) - - it('returns a node friendly address with dns6', () => { - expect( - multiaddr('/dns6/wss0.bootstrap.libp2p.io/tcp/443').nodeAddress() - ).to.be.eql({ - address: 'wss0.bootstrap.libp2p.io', - family: 6, - port: 443 - }) - }) - - it('returns a node friendly address with dnsaddr', () => { - expect( - multiaddr('/dnsaddr/wss0.bootstrap.libp2p.io/tcp/443').nodeAddress() - ).to.be.eql({ - address: 'wss0.bootstrap.libp2p.io', - family: 4, - port: 443 - }) - }) - - it('should transform a p2p dnsaddr without a tcp port into a node address', () => { - expect( - multiaddr('/dnsaddr/bootstrap.libp2p.io/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN').nodeAddress() - ).to.be.eql({ - address: 'bootstrap.libp2p.io', - family: 4, - port: 443 - }) - }) - - it('transforms an address with an ip6 zone', () => { - expect( - multiaddr('/ip6zone/x/ip6/fe80::1/tcp/1234').nodeAddress() - ).to.be.eql({ - address: 'fe80::1%x', - family: 6, - port: 1234 - }) - }) - - it('throws on an invalid format address when the addr is not prefixed with a /', () => { - expect( - () => multiaddr('ip4/192.168.0.1/udp').nodeAddress() - ).to.throw() - .with.property('name', 'InvalidMultiaddrError') - }) - - it('throws on an invalid protocol name when the addr has an invalid one', () => { - expect( - () => multiaddr('/ip5/127.0.0.1/udp/5000') - ).to.throw() - .with.property('name', 'UnknownProtocolError') - }) - - it('throws on an invalid protocol name when the transport protocol is not valid', () => { - expect( - () => multiaddr('/ip4/127.0.0.1/utp/5000') - ).to.throw() - .with.property('name', 'UnknownProtocolError') - }) + it('works with ipfs', () => { + expect( + multiaddr('/ip4/0.0.0.0/utp/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC').getComponents() + ).to.deep.equal([{ + code: 4, + name: 'ip4', + value: '0.0.0.0' + }, { + code: 302, + name: 'utp' + }, { + code: 421, + name: 'p2p', + value: 'QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC' + }]) + }) + + it('works with p2p', () => { + expect( + multiaddr('/ip4/0.0.0.0/utp/p2p/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC').getComponents() + ).to.deep.equal([{ + code: 4, + name: 'ip4', + value: '0.0.0.0' + }, { + code: 302, + name: 'utp' + }, { + code: 421, + name: 'p2p', + value: 'QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC' + }]) + }) + + it('works with unix', () => { + expect( + multiaddr('/ip4/0.0.0.0/tcp/8000/unix/tmp%2Fp2p.sock').getComponents() + ).to.deep.equal([{ + code: 4, + name: 'ip4', + value: '0.0.0.0' + }, { + code: 6, + name: 'tcp', + value: '8000' + }, { + code: 400, + name: 'unix', + value: 'tmp/p2p.sock' + }]) + }) + + it('works with memory', () => { + expect( + multiaddr('/memory/test/p2p/QmZR5a9AAXGqQF2ADqoDdGS8zvqv8n3Pag6TDDnTNMcFW6').getComponents() + ).to.deep.equal([{ + code: 777, + name: 'memory', + value: 'test' + }, { + code: 421, + name: 'p2p', + value: 'QmZR5a9AAXGqQF2ADqoDdGS8zvqv8n3Pag6TDDnTNMcFW6' + }]) }) - describe('.fromNodeAddress', () => { - it('throws on missing address object', () => { - expect( - // @ts-expect-error incorrect parameters - () => fromNodeAddress() - ).to.throw() - .with.property('name', 'InvalidParametersError') - }) - - it('throws on missing transport', () => { - expect( - // @ts-expect-error incorrect parameters - () => fromNodeAddress({ address: '0.0.0.0' }) - ).to.throw() - .with.property('name', 'InvalidParametersError') - }) - - it('parses a node address', () => { - expect( - fromNodeAddress({ - address: '192.168.0.1', - family: 4, - port: 1234 - }, 'tcp').toString() - ).to.equal('/ip4/192.168.0.1/tcp/1234') - }) + it('does not allow modifying parts', () => { + const ma = multiaddr('/ip4/0.0.0.0/tcp/1234') + const components = ma.getComponents() + components.shift() - it('parses a node address with an ip6zone', () => { - expect( - fromNodeAddress({ - address: 'fe80::1%x', - family: 6, - port: 1234 - }, 'tcp').toString() - ).to.equal('/ip6zone/x/ip6/fe80::1/tcp/1234') - }) + expect(ma.getComponents()).to.have.nested.property('[0].code', CODE_IP4) }) +}) - describe('.fromTuples', () => { - it('should create a multiaddr from a list of tuples', () => { - const ma = multiaddr('/ip4/0.0.0.0') - const tuples = ma.tuples() - tuples.push([0x06, Uint8Array.from([0, 100])]) - - const ma2 = fromTuples(tuples) - expect(ma2.toString()).to.equal('/ip4/0.0.0.0/tcp/100') - }) +describe('.decapsulate', () => { + it('throws on address with no matching subaddress', () => { + expect( + () => multiaddr('/ip4/127.0.0.1').decapsulate('/ip4/198.168.0.0') + ).to.throw( + /does not contain subaddress/ + ) }) +}) - describe('.fromStringTuples', () => { - it('should create a multiaddr from a list of string tuples', () => { - const ma = multiaddr('/ip4/0.0.0.0') - const tuples = ma.stringTuples() - tuples.push([0x06, '100']) - - const ma2 = fromStringTuples(tuples) - expect(ma2.toString()).to.equal('/ip4/0.0.0.0/tcp/100') - }) +describe('.decapsulateCode', () => { + it('removes the last occurrence of the code from the multiaddr', () => { + const relayTCP = multiaddr('/ip4/0.0.0.0/tcp/8080') + const relay = relayTCP.encapsulate('/p2p/QmZR5a9AAXGqQF2ADqoDdGS8zvqv8n3Pag6TDDnTNMcFW6/p2p-circuit') + const target = multiaddr('/p2p/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC') + const original = relay.encapsulate(target) + expect(original.decapsulateCode(421).toJSON()).to.equal(relay.toJSON()) + expect(relay.decapsulateCode(421).toJSON()).to.equal(relayTCP.toJSON()) }) - describe('.isThinWaistAddress', () => { - const families = ['ip4', 'ip6'] - const transports = ['tcp', 'udp'] - const addresses: Record = { - ip4: '192.168.0.1', - ip6: '2001:8a0:7ac5:4201:3ac9:86ff:fe31:7095' - } - families.forEach((family) => { - transports.forEach((transport) => { - it(`returns true for /${family}-${transport}`, () => { - expect( - multiaddr(`/${family}/${addresses[family]}/${transport}/1234`).isThinWaistAddress() - ).to.equal(true) - }) - }) - }) - - it('returns false for two protocols not using {IPv4, IPv6}/{TCP, UDP}', () => { - expect( - multiaddr('/ip4/192.168.0.1/utp').isThinWaistAddress() - ).to.be.false() - - expect( - multiaddr('/ip4/192.168.0.1/sctp/1234').isThinWaistAddress() - ).to.be.false() - - expect( - multiaddr('/http/utp').isThinWaistAddress() - ).to.be.false() - }) - - it('returns false for more than two protocols', () => { - expect( - multiaddr('/ip4/0.0.0.0/tcp/1234/utp').isThinWaistAddress() - ).to.be.false() - }) + it('ignores missing codes', () => { + const tcp = multiaddr('/ip4/0.0.0.0/tcp/8080') + expect(tcp.decapsulateCode(421).toJSON()).to.equal(tcp.toJSON()) }) +}) - describe('.getPeerId should parse id from multiaddr', () => { - it('extracts the peer Id from a multiaddr, p2p', () => { - expect( - multiaddr('/p2p-circuit/p2p/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC').getPeerId() - ).to.equal('QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC') - }) - it('extracts the correct peer Id from a circuit multiaddr', () => { - expect( - multiaddr('/ip4/0.0.0.0/tcp/8080/p2p/QmZR5a9AAXGqQF2ADqoDdGS8zvqv8n3Pag6TDDnTNMcFW6/p2p-circuit/p2p/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC').getPeerId() - ).to.equal('QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC') - }) - it('extracts the peer Id from a multiaddr, p2p and CIDv1 Base32', () => { - expect( - multiaddr('/p2p-circuit/p2p/bafzbeigweq4zr4x4ky2dvv7nanbkw6egutvrrvzw6g3h2rftp7gidyhtt4').getPeerId() - ).to.equal('QmckZzdVd72h9QUFuJJpQqhsZqGLwjhh81qSvZ9BhB2FQi') - }) - it('extracts the peer Id from a multiaddr, p2p and CIDv1 Base32, where Id contains non b58 chars', () => { - expect( - multiaddr('/p2p-circuit/p2p/bafzbeidt255unskpefjmqb2rc27vjuyxopkxgaylxij6pw35hhys4vnyp4').getPeerId() - ).to.equal('QmW8rAgaaA6sRydK1k6vonShQME47aDxaFidbtMevWs73t') - }) - it('extracts the peer Id from a multiaddr, p2p and base58btc encoded identity multihash', () => { - expect( - multiaddr('/p2p-circuit/p2p/12D3KooWNvSZnPi3RrhrTwEY4LuuBeB6K6facKUCJcyWG1aoDd2p').getPeerId() - ).to.equal('12D3KooWNvSZnPi3RrhrTwEY4LuuBeB6K6facKUCJcyWG1aoDd2p') - }) - it('does not extract a peer Id from a circuit relay multiaddr where only the relay peer id is present', () => { - expect( - multiaddr('/ip4/127.0.0.1/tcp/123/p2p/bafzbeigweq4zr4x4ky2dvv7nanbkw6egutvrrvzw6g3h2rftp7gidyhtt4/p2p-circuit').getPeerId() - ).to.be.null() - }) - }) +describe('.equals', () => { + it('returns true for equal addresses', () => { + const addr1 = multiaddr('/ip4/192.168.0.1') + const addr2 = multiaddr('/ip4/192.168.0.1') - describe('.getPeerId should return null on missing peer id in multiaddr', () => { - it('parses extracts the peer Id from a multiaddr', () => { - expect( - multiaddr('/ip4/0.0.0.0/tcp/1234/utp').getPeerId() - ).to.be.null() - }) + expect(addr1.equals(addr2)).to.equal(true) }) - describe('.getPath', () => { - it('should return a path for unix', () => { - expect( - multiaddr('/unix/%2Ftmp%2Fp2p.sock').getPath() - ).to.eql('/tmp/p2p.sock') - }) + it('returns false for non equal addresses', () => { + const addr1 = multiaddr('/ip4/192.168.1.1') + const addr2 = multiaddr('/ip4/192.168.0.1') - it('should return a path for unix when other protos exist', () => { - expect( - multiaddr('/ip4/0.0.0.0/tcp/1234/unix/%2Ftmp%2Fp2p.sock').getPath() - ).to.eql('/tmp/p2p.sock') - }) - - it('should not return a path when no path proto exists', () => { - expect( - multiaddr('/ip4/0.0.0.0/tcp/1234/p2p-circuit/p2p/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC').getPath() - ).to.eql(null) - }) + expect(addr1.equals(addr2)).to.equal(false) }) +}) - describe('multiaddr.isMultiaddr', () => { - it('handles different inputs', () => { - expect(isMultiaddr(multiaddr('/'))).to.be.eql(true) - expect(isMultiaddr(multiaddr('/ip4/192.0.2.0/ipcidr/24'))).to.be.eql(true) - expect(isMultiaddr('/')).to.be.eql(false) - expect(isMultiaddr(123)).to.be.eql(false) +describe('multiaddr.isMultiaddr', () => { + it('handles different inputs', () => { + expect(isMultiaddr(multiaddr('/'))).to.deep.equal(true) + expect(isMultiaddr(multiaddr('/ip4/192.0.2.0/ipcidr/24'))).to.deep.equal(true) + expect(isMultiaddr('/')).to.deep.equal(false) + expect(isMultiaddr(123)).to.deep.equal(false) - expect(isMultiaddr(uint8ArrayFromString('/hello'))).to.be.eql(false) - }) - }) - - describe('resolvable multiaddrs', () => { - describe('.isName', () => { - it('valid name dns', () => { - const str = '/dns/ipfs.io' - const addr = multiaddr(str) - expect(isName(addr)).to.equal(true) - }) - - it('valid name dnsaddr', () => { - const str = '/dnsaddr/ipfs.io' - const addr = multiaddr(str) - expect(isName(addr)).to.equal(true) - }) - - it('valid name dns4', () => { - const str = '/dns4/ipfs.io' - const addr = multiaddr(str) - expect(isName(addr)).to.equal(true) - }) - - it('valid name dns6', () => { - const str = '/dns6/ipfs.io' - const addr = multiaddr(str) - expect(isName(addr)).to.equal(true) - }) - - it('invalid name', () => { - const str = '/ip4/127.0.0.1' - const addr = multiaddr(str) - expect(isName(addr)).to.equal(false) - }) - }) + expect(isMultiaddr(uint8ArrayFromString('/hello'))).to.deep.equal(false) }) }) diff --git a/test/protocols.spec.ts b/test/protocols.spec.ts deleted file mode 100644 index 12c1b519..00000000 --- a/test/protocols.spec.ts +++ /dev/null @@ -1,29 +0,0 @@ -/* eslint-env mocha */ -import { expect } from 'aegir/chai' -import { protocols } from '../src/index.js' - -describe('protocols', () => { - describe('throws on non existent protocol', () => { - it('number', () => { - expect( - () => protocols(1234) - ).to.throw() - .with.property('name', 'UnknownProtocolError') - }) - - it('string', () => { - expect( - () => protocols('hello') - ).to.throw() - .with.property('name', 'UnknownProtocolError') - }) - - it('else', () => { - expect( - // @ts-expect-error incorrect parameters - () => protocols({ hi: 34 }) - ).to.throw() - .with.property('name', 'UnknownProtocolError') - }) - }) -}) diff --git a/test/resolvers.spec.ts b/test/resolvers.spec.ts deleted file mode 100644 index 719b6d10..00000000 --- a/test/resolvers.spec.ts +++ /dev/null @@ -1,211 +0,0 @@ -import { RecordType } from '@multiformats/dns' -import { expect } from 'aegir/chai' -import sinon from 'sinon' -import { stubInterface } from 'sinon-ts' -import { multiaddr, resolvers } from '../src/index.js' -import { dnsaddrResolver } from '../src/resolvers/index.js' -import type { DNS } from '@multiformats/dns' -import type { StubbedInstance } from 'sinon-ts' - -const stubs: Record = { - '_dnsaddr.bootstrap.libp2p.io': [ - 'dnsaddr=/dnsaddr/ams-1.bootstrap.libp2p.io/p2p/QmSoLer265NRgSp2LA3dPaeykiS1J6DifTC88f5uVQKNAd', - 'dnsaddr=/dnsaddr/ams-2.bootstrap.libp2p.io/p2p/QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb' - ], - '_dnsaddr.ams-1.bootstrap.libp2p.io': [ - 'dnsaddr=/ip4/147.75.83.83/tcp/4001/p2p/QmSoLer265NRgSp2LA3dPaeykiS1J6DifTC88f5uVQKNAd', - 'dnsaddr=/ip4/147.75.83.83/tcp/443/wss/p2p/QmSoLer265NRgSp2LA3dPaeykiS1J6DifTC88f5uVQKNAd', - 'dnsaddr=/ip4/147.75.83.83/udp/4001/quic/p2p/QmSoLer265NRgSp2LA3dPaeykiS1J6DifTC88f5uVQKNAd', - 'dnsaddr=/ip6/2604:1380:2000:7a00::1/tcp/4001/p2p/QmSoLer265NRgSp2LA3dPaeykiS1J6DifTC88f5uVQKNAd', - 'dnsaddr=/ip6/2604:1380:2000:7a00::1/tcp/443/wss/p2p/QmSoLer265NRgSp2LA3dPaeykiS1J6DifTC88f5uVQKNAd', - 'dnsaddr=/ip6/2604:1380:2000:7a00::1/udp/4001/quic/p2p/QmSoLer265NRgSp2LA3dPaeykiS1J6DifTC88f5uVQKNAd' - ], - '_dnsaddr.ams-2.bootstrap.libp2p.io': [ - 'dnsaddr=/ip4/147.75.83.83/tcp/4001/p2p/QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb', - 'dnsaddr=/ip4/147.75.83.83/tcp/443/wss/p2p/QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb', - 'dnsaddr=/ip4/147.75.83.83/udp/4001/quic/p2p/QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb', - 'dnsaddr=/ip6/2604:1380:2000:7a00::1/tcp/4001/p2p/QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb', - 'dnsaddr=/ip6/2604:1380:2000:7a00::1/tcp/443/wss/p2p/QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb', - 'dnsaddr=/ip6/2604:1380:2000:7a00::1/udp/4001/quic/p2p/QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb' - ], - '_dnsaddr.bad-addrs.libp2p.io': [ - 'dnsaddr=/dnsaddr/sv15.bootstrap.libp2p.io/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN', - 'dnsaddr=/dnsaddr/ny5.bootstrap.libp2p.io/p2p/QmQCU2EcMqAqQPR2i9bChDtGNJchTbq5TbXJJ16u19uLTa', - 'dnsaddr_record_value', - 'dnsaddr=/dnsaddr/am6.bootstrap.libp2p.io/p2p/QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb', - 'dnsaddr=/dnsaddr/sg1.bootstrap.libp2p.io/p2p/QmcZf59bWwK5XFi76CZX8cbJ4BhTzzA3gU1ZjYZcYW3dwt' - ], - '_dnsaddr.am6.bootstrap.libp2p.io': [ - 'dnsaddr=/ip4/147.75.83.83/tcp/4001/p2p/QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb' - ], - '_dnsaddr.self-referential.io': [ - 'dnsaddr=/dnsaddr/self-referential.io' - ], - '_dnsaddr.double-quoted-answer.io': [ - '"dnsaddr=/ip4/147.75.83.83/tcp/4001/p2p/QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb"' - ], - '_dnsaddr.single-quoted-answer.io': [ - "'dnsaddr=/ip4/147.75.83.83/tcp/4001/p2p/QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb'" - ], - '_dnsaddr.mixed-quoted-answer.io': [ - '"\'""" dnsaddr=/ip4/147.75.83.83/tcp/4001/p2p/QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb" "' - ] -} - -describe('multiaddr resolve', () => { - let dns: StubbedInstance - - beforeEach(() => { - dns = stubInterface({ - query: sinon.stub().callsFake((domain) => { - if (stubs[domain] != null) { - return { - Answer: stubs[domain].map(data => ({ - name: '_dnsaddr.bootstrap.libp2p.io', - type: RecordType.TXT, - ttl: 100, - data - })) - } - } - - throw new Error(`No result stubbed for ${domain}`) - }) - }) - - resolvers.set('dnsaddr', dnsaddrResolver) - }) - - it('should throw if no resolver is available', async () => { - const ma = multiaddr('/dnsaddr/bootstrap.libp2p.io') - - resolvers.clear() - - // Resolve - await expect(ma.resolve()).to.eventually.be.rejected() - .and.to.have.property('name', 'NoAvailableResolverError') - }) - - describe('dnsaddr', () => { - afterEach(() => { - sinon.restore() - }) - - it('can resolve dnsaddr without no peerId', async () => { - const ma = multiaddr('/dnsaddr/bootstrap.libp2p.io') - - // Resolve - const resolvedMas = await ma.resolve({ - dns - }) - - expect(resolvedMas).to.deep.equal([ - ...stubs['_dnsaddr.ams-1.bootstrap.libp2p.io'].map(addr => multiaddr(addr.split('=').pop())), - ...stubs['_dnsaddr.ams-2.bootstrap.libp2p.io'].map(addr => multiaddr(addr.split('=').pop())) - ]) - }) - - it('can resolve dnsaddr with peerId', async () => { - const ma = multiaddr('/dnsaddr/bootstrap.libp2p.io/p2p/QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb') - - // Resolve - const resolvedMas = await ma.resolve({ - dns - }) - - expect(resolvedMas).to.deep.equal([ - ...stubs['_dnsaddr.ams-2.bootstrap.libp2p.io'].map(addr => multiaddr(addr.split('=').pop())) - ]) - }) - - it('can resolve dnsaddr with bad record', async () => { - const ma = multiaddr('/dnsaddr/bad-addrs.libp2p.io/p2p/QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb') - - // Resolve - const resolvedMas = await ma.resolve({ - dns - }) - - // Should only have one address with the same peer id and should ignore the bad record - expect(resolvedMas).to.have.lengthOf(1) - expect(resolvedMas[0].toString()).to.equal(stubs['_dnsaddr.am6.bootstrap.libp2p.io'][0].split('=').pop()) - }) - - it('can cancel resolving', async () => { - const ma = multiaddr('/dnsaddr/bootstrap.libp2p.ii/p2p/QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nc') - const controller = new AbortController() - - // Resolve - const resolvePromise = ma.resolve({ - signal: controller.signal - }) - - controller.abort() - - await expect(resolvePromise).to.eventually.be.rejected() - }) - - it('should abort resolving deeply nested records', async () => { - const ma = multiaddr('/dnsaddr/bootstrap.libp2p.io') - - // Resolve - const resolvePromise = ma.resolve({ - dns, - maxRecursiveDepth: 1 - }) - - await expect(resolvePromise).to.eventually.be.rejected().with.property('name', 'RecursionLimitError') - }) - - it('should handle recursive loops', async () => { - const ma = multiaddr('/dnsaddr/self-referential.io') - - // Resolve - const resolvePromise = ma.resolve({ - dns, - maxRecursiveDepth: 1 - }) - - await expect(resolvePromise).to.eventually.be.rejected().with.property('name', 'RecursionLimitError') - }) - - it('should handle double quotes', async () => { - const ma = multiaddr('/dnsaddr/double-quoted-answer.io') - - // Resolve - const resolvedMas = await ma.resolve({ - dns - }) - - // Should ignore double quotes - expect(resolvedMas).to.have.lengthOf(1) - expect(resolvedMas[0].toString()).to.equal('/ip4/147.75.83.83/tcp/4001/p2p/QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb') - }) - - it('should handle single quotes', async () => { - const ma = multiaddr('/dnsaddr/single-quoted-answer.io') - - // Resolve - const resolvedMas = await ma.resolve({ - dns - }) - - // Should ignore double quotes - expect(resolvedMas).to.have.lengthOf(1) - expect(resolvedMas[0].toString()).to.equal('/ip4/147.75.83.83/tcp/4001/p2p/QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb') - }) - - it('should handle mixed quotes', async () => { - const ma = multiaddr('/dnsaddr/mixed-quoted-answer.io') - - // Resolve - const resolvedMas = await ma.resolve({ - dns - }) - - // Should ignore double quotes - expect(resolvedMas).to.have.lengthOf(1) - expect(resolvedMas[0].toString()).to.equal('/ip4/147.75.83.83/tcp/4001/p2p/QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb') - }) - }) -}) diff --git a/typedoc.json b/typedoc.json index 41f5560e..db0b0747 100644 --- a/typedoc.json +++ b/typedoc.json @@ -1,7 +1,6 @@ { + "readme": "none", "entryPoints": [ - "./src/index.ts", - "./src/convert.ts", - "./src/resolvers/index.ts" + "./src/index.ts" ] }