lightning
Version:
Lightning Network client library
554 lines (506 loc) • 14.1 kB
JavaScript
const EventEmitter = require('events');
const {deepStrictEqual} = require('node:assert').strict;
const {promisify} = require('util');
const {rejects} = require('node:assert').strict;
const test = require('node:test');
const {throws} = require('node:assert').strict;
const {subscribeToGraph} = require('./../../../');
const nextTick = promisify(process.nextTick);
const makeLnd = ({}) => {
return {
default: {
getNodeInfo: ({}, cbk) => {
return cbk(null, {
channels: [],
node: {
addresses: [{addr: 'addr', network: 'network'}],
alias: 'alias',
color: '#123456',
features: {'1': {is_known: true, is_required: false}},
last_update: 1,
pub_key: Buffer.alloc(33).toString('hex'),
},
num_channels: 1,
total_capacity: 1,
});
},
subscribeChannelGraph: ({}) => {
const emitter = new EventEmitter();
emitter.cancel = () => {};
process.nextTick(() => {
emitter.emit('data', {
channel_updates: [{
advertising_node: Buffer.alloc(33).toString('hex'),
capacity: '1',
chan_id: '1',
chan_point: {
funding_txid_bytes: Buffer.alloc(32, 1),
output_index: 0,
},
connecting_node: Buffer.alloc(33, 1).toString('hex'),
routing_policy: {
disabled: false,
fee_base_msat: '1',
fee_rate_milli_msat: '1',
max_htlc_msat: '1',
min_htlc: '1',
time_lock_delta: 1,
},
}],
closed_chans: [{
capacity: '1',
chan_id: '1',
chan_point: {
funding_txid_bytes: Buffer.alloc(32, 1),
output_index: 1,
},
closed_height: 1,
}],
node_updates: [{
addresses: ['addr'],
alias: 'alias',
color: '#123456',
features: {'1': {is_known: true, is_required: false}},
identity_key: Buffer.alloc(33).toString('hex'),
}],
});
emitter.emit('error', 'error');
return;
});
return emitter;
},
},
version: {
getVersion: ({}, cbk) => cbk(err, {
app_minor: 1,
app_patch: 1,
build_tags: ['autopilotrpc'],
commit_hash: Buffer.alloc(20).toString('hex'),
}),
},
};
};
const tests = [
{
args: {lnd: undefined},
description: 'LND object is required to subscribe to graph',
error: 'ExpectedAuthenticatedLndToSubscribeToChannelGraph',
},
{
args: {
lnd: {
default: {
subscribeChannelGraph: ({}) => {
const emitter = new EventEmitter();
emitter.cancel = () => {};
process.nextTick(() => {
return emitter.emit('data', {
channel_updates: undefined,
closed_chans: [],
node_updates: [],
});
});
return emitter;
},
},
version: {
getVersion: ({}, cbk) => cbk(err, {
app_minor: 1,
app_patch: 1,
build_tags: ['autopilotrpc'],
commit_hash: Buffer.alloc(20).toString('hex'),
}),
},
},
},
description: 'Updates are expected',
expected: {
events: [{
data: new Error('ExpectedChannelUpdates'),
event: 'error',
}],
},
},
{
args: {
lnd: {
default: {
subscribeChannelGraph: ({}) => {
const emitter = new EventEmitter();
emitter.cancel = () => {};
process.nextTick(() => {
return emitter.emit('data', {
channel_updates: [],
closed_chans: undefined,
node_updates: [],
});
});
return emitter;
},
},
version: {
getVersion: ({}, cbk) => cbk(err, {
app_minor: 1,
app_patch: 1,
build_tags: ['autopilotrpc'],
commit_hash: Buffer.alloc(20).toString('hex'),
}),
},
},
},
description: 'Closed channels are expected',
expected: {
events: [{
data: new Error('ExpectedClosedChans'),
event: 'error',
}],
},
},
{
args: {
lnd: {
default: {
subscribeChannelGraph: ({}) => {
const emitter = new EventEmitter();
emitter.cancel = () => {};
process.nextTick(() => {
return emitter.emit('data', {
channel_updates: [],
closed_chans: [],
node_updates: undefined,
});
});
return emitter;
},
},
version: {
getVersion: ({}, cbk) => cbk(err, {
app_minor: 1,
app_patch: 1,
build_tags: ['autopilotrpc'],
commit_hash: Buffer.alloc(20).toString('hex'),
}),
},
},
},
description: 'Node updates are expected',
expected: {
events: [{
data: new Error('ExpectedNodeUpdates'),
event: 'error',
}],
},
},
{
args: {
lnd: {
default: {
subscribeChannelGraph: ({}) => {
const emitter = new EventEmitter();
emitter.cancel = () => {};
process.nextTick(() => {
return emitter.emit('data', {
channel_updates: [null],
closed_chans: [],
node_updates: [],
});
});
return emitter;
},
},
},
},
description: 'Update errors are emitted',
expected: {
events: [{
data: new Error('ExpectedChannelUpdateDetails'),
event: 'error',
}],
},
},
{
args: {
lnd: {
default: {
subscribeChannelGraph: ({}) => {
const emitter = new EventEmitter();
emitter.cancel = () => {};
process.nextTick(() => {
return emitter.emit('data', {
channel_updates: [],
closed_chans: [null],
node_updates: [],
});
});
return emitter;
},
},
version: {
getVersion: ({}, cbk) => cbk(err, {
app_minor: 1,
app_patch: 1,
build_tags: ['autopilotrpc'],
commit_hash: Buffer.alloc(20).toString('hex'),
}),
},
},
},
description: 'Closed errors are emitted',
expected: {
events: [{
data: new Error('ExpectedChannelClosedUpdateDetails'),
event: 'error',
}],
},
},
{
args: {
lnd: {
default: {
subscribeChannelGraph: ({}) => {
const emitter = new EventEmitter();
emitter.cancel = () => {};
process.nextTick(() => {
return emitter.emit('data', {
channel_updates: [],
closed_chans: [],
node_updates: [null],
});
});
return emitter;
},
},
version: {
getVersion: ({}, cbk) => cbk(err, {
app_minor: 1,
app_patch: 1,
build_tags: ['autopilotrpc'],
commit_hash: Buffer.alloc(20).toString('hex'),
}),
},
},
},
description: 'Node errors are emitted',
expected: {
events: [{
data: new Error('ExpectedDetailsInNodeUpdateAnnouncement'),
event: 'error',
}],
},
},
{
args: {
lnd: {
default: {
subscribeChannelGraph: ({}) => {
const emitter = new EventEmitter();
emitter.cancel = () => {};
process.nextTick(() => {
return emitter.emit('data', {
channel_updates: [],
closed_chans: [],
node_updates: [{}],
});
});
return emitter;
},
},
version: {
getVersion: ({}, cbk) => cbk(err, {
app_minor: 1,
app_patch: 1,
build_tags: ['autopilotrpc'],
commit_hash: Buffer.alloc(20).toString('hex'),
}),
},
},
},
description: 'Node public key is expected',
expected: {
events: [{
data: new Error('ExpectedPubKeyInNodeUpdateAnnouncement'),
event: 'error',
}],
},
},
{
args: {
lnd: {
default: {
subscribeChannelGraph: ({}) => {
const emitter = new EventEmitter();
emitter.cancel = () => {};
process.nextTick(() => {
return emitter.emit('data', {
channel_updates: [],
closed_chans: [],
node_updates: [{
identity_key: Buffer.alloc(33).toString('hex'),
}],
});
});
return emitter;
},
},
version: {
getVersion: ({}, cbk) => cbk(err, {
app_minor: 1,
app_patch: 1,
build_tags: ['autopilotrpc'],
commit_hash: Buffer.alloc(20).toString('hex'),
}),
},
},
},
description: 'Get node errors are passed back',
expected: {
events: [{
data: [400, 'ExpectedLndApiObjectToGetNodeInfo'],
event: 'error',
}],
},
},
{
args: {
lnd: {
default: {
getNodeInfo: ({}, cbk) => {
return cbk(null, {
channels: [],
node: {
addresses: [{addr: 'addr', network: 'network'}],
alias: 'alias',
color: '#123456',
features: {'1': {is_known: true, is_required: false}},
last_update: 0,
pub_key: Buffer.alloc(33).toString('hex'),
},
num_channels: 1,
total_capacity: 1,
});
},
subscribeChannelGraph: ({}) => {
const emitter = new EventEmitter();
emitter.cancel = () => {};
process.nextTick(() => {
return emitter.emit('data', {
channel_updates: [],
closed_chans: [],
node_updates: [{
identity_key: Buffer.alloc(33).toString('hex'),
}],
});
});
return emitter;
},
},
version: {
getVersion: ({}, cbk) => cbk(null, {
app_minor: 1,
app_patch: 1,
build_tags: ['autopilotrpc'],
commit_hash: Buffer.alloc(20).toString('hex'),
}),
},
},
},
description: 'Get node does not require updated at',
expected: {
events: [{
data: {
alias: 'alias',
color: '#123456',
features: [{
bit: 1,
is_known: true,
is_required: false,
type: 'data_loss_protection',
}],
public_key: '000000000000000000000000000000000000000000000000000000000000000000',
sockets: ['addr'],
},
event: 'node_updated',
}],
},
},
{
args: {lnd: makeLnd({})},
description: 'Graph events are received',
expected: {
events: [
{
event: 'channel_updated',
data: {
base_fee_mtokens: '1',
capacity: 1,
cltv_delta: 1,
fee_rate: 1,
id: '0x0x1',
inbound_base_discount_mtokens: '0',
inbound_rate_discount: 0,
is_disabled: false,
max_htlc_mtokens: '1',
min_htlc_mtokens: '1',
public_keys: [
'000000000000000000000000000000000000000000000000000000000000000000',
'010101010101010101010101010101010101010101010101010101010101010101',
],
transaction_id: '0101010101010101010101010101010101010101010101010101010101010101',
transaction_vout: 0,
},
},
{
event: 'channel_closed',
data: {
capacity: 1,
close_height: 1,
id: '0x0x1',
transaction_id: '0101010101010101010101010101010101010101010101010101010101010101',
transaction_vout: 1,
},
},
{
event: 'node_updated',
data: {
alias: 'alias',
color: '#123456',
features: [{
bit: 1,
is_known: true,
is_required: false,
type: 'data_loss_protection'
}],
public_key: '000000000000000000000000000000000000000000000000000000000000000000',
sockets: ['addr'],
},
},
{
event: 'error',
data: 'error',
},
],
},
},
];
tests.forEach(({args, description, error, expected}) => {
return test(description, async () => {
if (!!error) {
throws(() => subscribeToGraph(args), new Error(error), 'Got error');
} else {
const events = [];
const sub = subscribeToGraph(args);
['channel_closed', 'channel_updated', 'error', 'node_updated']
.forEach(event => sub.on(event, data => events.push({event, data})));
await nextTick();
sub.removeAllListeners();
subscribeToGraph(args);
await nextTick();
const gotEvents = events.map(got => {
delete got.data.updated_at;
return got;
});
deepStrictEqual(gotEvents, expected.events, 'Got graph events');
}
return;
});
});