UNPKG

@node-dlc/wire

Version:
125 lines (106 loc) 3.42 kB
import { ShortChannelId } from '@node-dlc/common'; import { ILogger } from '@node-dlc/logger'; import { QueryShortChannelIdsMessage } from '../messages/QueryShortChannelIdsMessage'; import { ReplyShortChannelIdsEndMessage } from '../messages/ReplyShortChannelIdsEndMessage'; import { IMessageSender } from '../Peer'; import { GossipError, GossipErrorCode } from './GossipError'; export enum ChannelsQueryState { Idle, Active, Complete, Failed, } /** * This class manages the state machine for executing query_short_channel_ids * and will resolve as either complete or with an error. This class can accept * an arbitrarily large number of short channel ids and will chunk the requests * appropriately. */ export class ChannelsQuery { public chunkSize = 2000; private _queue: ShortChannelId[] = []; private _state: ChannelsQueryState; private _error: GossipError; private _resolve: () => void; private _reject: (error: GossipError) => void; constructor( readonly chainHash: Buffer, readonly peer: IMessageSender, readonly logger: ILogger, ) { this._state = ChannelsQueryState.Idle; } public get state(): ChannelsQueryState { return this._state; } public get error(): Error { return this._error; } public handleReplyShortChannelIdsEnd( msg: ReplyShortChannelIdsEndMessage, ): void { this.logger.debug( 'received reply_short_channel_ids_end - complete: %d', msg.complete, ); // If we receive a reply with complete=false, the remote peer // does not maintain up-to-date channel information for the // requested chain_hash if (!msg.complete) { const error = new GossipError(GossipErrorCode.ReplyChannelsNoInfo, msg); this._transitionFailed(error); return; } // This occurs when the last batch of information has been received // but there is still more short_channel_ids to request. This scenario // requires sending another QueryShortIds message if (this._queue.length > 0) { this._sendQuery(); } else { this._transitionSuccess(); return; } } /** * * @param scids */ public query(...scids: ShortChannelId[]): Promise<void> { return new Promise((resolve, reject) => { // enqueue the short ids for (const scid of scids) { this._queue.push(scid); } // Ensure we are in the active state this._state = ChannelsQueryState.Active; // send our query to the peer this._sendQuery(); // capture the promise method for use when complete this._resolve = resolve; this._reject = reject; }); } private _transitionSuccess() { if (this._state !== ChannelsQueryState.Active) return; this._state = ChannelsQueryState.Complete; this._resolve(); } private _transitionFailed(error: GossipError) { if (this._state !== ChannelsQueryState.Active) return; this._state = ChannelsQueryState.Failed; this._error = error; this._reject(error); } private _sendQuery() { // splice a chunk of work to do from the suqery const scids = this._queue.splice(0, this.chunkSize); const msg = new QueryShortChannelIdsMessage(); msg.chainHash = this.chainHash; msg.shortChannelIds = scids; this.logger.debug( 'sending query_short_channel_ids - scid_count:', scids.length, ); this.peer.sendMessage(msg); } }