UNPKG

ln-service

Version:

Interaction helper for your Lightning Network daemon

143 lines (105 loc) 4.11 kB
const {deepEqual} = require('node:assert').strict; const {equal} = require('node:assert').strict; const test = require('node:test'); const asyncRetry = require('async/retry'); const {setupChannel} = require('ln-docker-daemons'); const {spawnLightningCluster} = require('ln-docker-daemons'); const {addPeer} = require('./../../'); const {createInvoice} = require('./../../'); const {decodePaymentRequest} = require('./../../'); const {getInvoice} = require('./../../'); const {getNetworkGraph} = require('./../../'); const {getRouteThroughHops} = require('./../../'); const {getRouteToDestination} = require('./../../'); const {getWalletInfo} = require('./../../'); const {payViaRoutes} = require('./../../'); const {waitForRoute} = require('./../macros'); const confirmationCount = 6; const flatten = arr => [].concat(...arr); const interval = 10; const maturity = 100; const messages = [{type: '1000000', value: '01'}]; const size = 3; const times = 1000; const tlvOnionBit = 14; const tokens = 100; // Getting a route through hops should result in a route through specified hops test(`Get route through hops`, async () => { const {kill, nodes} = await spawnLightningCluster({size}); const [{generate, lnd}, target, remote] = nodes; const controlToTargetChan = await setupChannel({generate, lnd, to: target}); await generate({}); await asyncRetry({interval, times}, async () => { await generate({}); await addPeer({ lnd: target.lnd, public_key: remote.id, socket: remote.socket, }); const targetToRemoteChan = await setupChannel({ generate: target.generate, lnd: target.lnd, to: remote, }); }); await target.generate({count: confirmationCount}); await asyncRetry({interval, times}, async () => { const wallet = await getWalletInfo({lnd: remote.lnd}); await addPeer({lnd, public_key: remote.id, socket: remote.socket}); if (!wallet.is_synced_to_chain) { throw new Error('ExpectedWalletSyncedToChain'); } }); await asyncRetry({interval, times}, async () => { const {channels, nodes} = await getNetworkGraph({lnd}); const limitedFeatures = nodes.find(node => { return !node.features.find(n => n.bit === tlvOnionBit); }); const policies = flatten(channels.map(n => n.policies)); const cltvDeltas = policies.map(n => n.cltv_delta); if (!!cltvDeltas.filter(n => !n).length) { throw new Error('ExpectedAllChannelPolicies'); } if (!!limitedFeatures) { throw new Error('NetworkGraphSyncIncomplete'); } }); const invoice = await createInvoice({tokens, lnd: remote.lnd}); const {id} = invoice; const {request} = invoice; const decodedRequest = await decodePaymentRequest({lnd, request}); await waitForRoute({lnd, destination: remote.id, tokens: invoice.tokens}); const {route} = await asyncRetry({interval, times}, async () => { return await getRouteToDestination({ lnd, cltv_delta: decodedRequest.cltv_delta, destination: decodedRequest.destination, tokens: invoice.tokens, }); }); const res = await asyncRetry({interval, times}, async () => { return await getRouteThroughHops({ lnd, messages, cltv_delta: decodedRequest.cltv_delta + 6, mtokens: (BigInt(invoice.tokens) * BigInt(1e3)).toString(), payment: decodedRequest.payment, public_keys: route.hops.map(n => n.public_key), total_mtokens: invoice.mtokens, }); }); await payViaRoutes({lnd, id: invoice.id, routes: [res.route]}); const got = await getInvoice({lnd: remote.lnd, id: invoice.id}); delete res.route.confidence; delete route.confidence; route.messages = messages; route.payment = decodedRequest.payment; route.total_mtokens = decodedRequest.mtokens; deepEqual(res.route, route, 'Constructed route to destination'); const {payments} = got; const [payment] = payments; equal(payment.total_mtokens, invoice.mtokens, 'Got MPP total mtokens'); deepEqual(payment.messages, route.messages, 'Remote got TLV messages'); await kill({}); return; });