Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 | 1x 1x 1x 1x 4x 4x 4x 3x 3x 4x 3x 3x 4x 2x 3x 4x 3x 4x 4x 4x 1x 4x 4x 1x 2x 1x 4x 1x 4x 3x 3x 2x 4x 4x 3x | import { PeerHostRecord } from "../PeerHostRecord";
import { promises as dnsPromises, SrvRecord } from "dns";
import bech32 from "bech32";
import { AddressType } from "../domain/AddressType";
export interface DnsPeerQueryOptions {
/**
* The domain of the dns seed.
*/
dnsSeed: string;
/**
* The realm byte used to specify what realm the returned nodes must support.
*/
realm?: number;
/**
* The address type bit field specifies the address types that should be returned.
* It follows the format of the address descriptor type specified in BOLT #7.
*/
addressTypes?: AddressType[];
/**
* The bech32-encoded node id used to retrive the result of a single node.
*/
nodeId?: string;
/**
* The number of desired reply records.
*/
desiredReplyRecords?: number;
}
/**
* This class implements the node discovery mechanism described
* in BOLT #10.
* The purpose of this component is to assist in the initial
* node discovery process for nodes that have no known contacts,
* and to help nodes discover the current network address of previously
* known peers. A domain name server that implements BOLT #10 is
* referred to as a DNS Seed and answers incoming DNS queries of
* type A, AAAA, or SRV.
*/
export class DnsPeerQuery {
private resolver: dnsPromises.Resolver;
constructor(resolver?: dnsPromises.Resolver) {
this.resolver = resolver || new dnsPromises.Resolver();
}
/**
* The query method starts by querying a DNS seed for SRV records.
* This will result in a list of returned SRV records that each
* represent a lightning node that can be connected to. Each record
* is a subdomain of the dns seed where the first component of the
* subdomain is the bech32 encoded public key of the node (also known
* as the node_id). Each subdomain can subsequently be used to query
* the DNS seed for the A (or AAA) records of the lightning node with
* the specified node_id. The query method ends by constructing peer
* host records using the port from the SRV records and the ip
* address from the A (or AAA) records.
* @param dnsPeerQueryOptions
* @returns valid peer host records
*/
public async query(dnsPeerQueryOptions: DnsPeerQueryOptions): Promise<PeerHostRecord[]> {
const dnsSeed = this._buildUrl(dnsPeerQueryOptions);
const peerSrvRecords: SrvRecord[] = await this._getPeerSrvRecords(dnsSeed);
const peerHostRecordPromises: Promise<PeerHostRecord>[] = [];
for (const peerSrvRecord of peerSrvRecords) {
peerHostRecordPromises.push(this._createPeerHostRecord(peerSrvRecord));
}
const peerHostRecordSettlements = await Promise.allSettled(peerHostRecordPromises);
const peers = peerHostRecordSettlements
.filter(recordSettlement => recordSettlement?.status == "fulfilled")
.map((p: PromiseFulfilledResult<PeerHostRecord>) => p.value);
return peers;
}
private async _createPeerHostRecord(peerSrvRecord: SrvRecord): Promise<PeerHostRecord> {
const peerAddress = await this._resolveSrvNameToIp(peerSrvRecord.name);
return new PeerHostRecord(
this._getPublicKeyFromSrvRecord(peerSrvRecord),
peerAddress,
peerSrvRecord.port,
);
}
private _buildUrl(dnsPeerQueryOptions: DnsPeerQueryOptions): string {
let { dnsSeed } = dnsPeerQueryOptions;
const { realm, addressTypes, nodeId, desiredReplyRecords } = dnsPeerQueryOptions;
if (desiredReplyRecords != null) {
dnsSeed = `n${desiredReplyRecords}.${dnsSeed}`;
}
Iif (nodeId != null) {
dnsSeed = `l${nodeId}.${dnsSeed}`;
}
if (addressTypes != null) {
const a = addressTypes.reduce((bitField, addressType) => {
return bitField | (1 << addressType);
}, 0);
dnsSeed = `a${a}.${dnsSeed}`;
}
if (realm != null) {
dnsSeed = `r${realm}.${dnsSeed}`;
}
return dnsSeed;
}
private _getPublicKeyFromSrvRecord(srvRecord: SrvRecord): Buffer {
const domainComponents = srvRecord.name.split(".");
const { words } = bech32.decode(domainComponents[0]);
return Buffer.from(bech32.fromWords(words));
}
private _getPeerSrvRecords(dnsSeed: string): Promise<SrvRecord[]> {
return this.resolver.resolveSrv(dnsSeed);
}
private async _resolveSrvNameToIp(hostname: string): Promise<string> {
const ipAddresses: string[] = await this.resolver.resolve(hostname, "A");
return ipAddresses?.[0];
}
}
|