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 | 1x 1x 1x 1x 1x 1x 1x 1x | import { ShortChannelId } from "@node-lightning/core";
import { ILogger } from "@node-lightning/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);
}
}
|