UNPKG

lightning

Version:
409 lines (397 loc) 10.4 kB
const {deepStrictEqual} = require('node:assert').strict; const EventEmitter = require('node:events'); const {strictEqual} = require('node:assert').strict; const test = require('node:test'); const {throws} = require('node:assert').strict; const {subscribeToPayViaRoutes} = require('./../../../'); const deletePayment = ({}, cbk) => cbk(); const route = { fee: 1, fee_mtokens: '1000', hops: [{ channel: '0x0x1', fee_mtokens: '1', forward_mtokens: '1', public_key: 'public_key', }], tokens: 1, }; const tests = [ { args: {id: 'id'}, description: 'The id of a past payment must be a preimage hash', error: 'ExpectedPaymentHashToPayViaRoutes', }, { args: {}, description: 'LND is expected', error: 'ExpectedAuthenticatedLndToPayViaRoutes', }, { args: { lnd: {default: {deletePayment}, router: {sendToRouteV2: ({}) => {}}}, }, description: 'Routes are required', error: 'ExpectedArrayOfPaymentRoutesToPayViaRoutes', }, { args: { lnd: {default: {deletePayment}, router: {sendToRouteV2: ({}) => {}}}, routes: [], }, description: 'Non empty routes are required', error: 'ExpectedArrayOfPaymentRoutesToPayViaRoutes', }, { args: { lnd: {default: {deletePayment}, router: {sendToRouteV2: ({}) => {}}}, routes: [{}], }, description: 'Route must have hops', error: 'ExpectedRouteHopsToPayViaRoutes', }, { args: { lnd: {default: {deletePayment}, router: {sendToRouteV2: ({}) => {}}}, routes: [{hops: [{}]}], }, description: 'Hops must have public keys', error: 'ExpectedPublicKeyInPayViaRouteHops', }, { args: { lnd: {default: {deletePayment}, router: {sendToRouteV2: ({}) => {}}}, routes: [{hops: [{public_key: 'public_key'}]}], }, description: 'Hops must have channel ids', error: 'ExpectedValidRouteChannelIdsForPayViaRoutes', }, { args: { lnd: {default: {deletePayment}, router: {sendToRouteV2: ({}) => {}}}, routes: [{hops: [{public_key: 'public_key'}]}], }, description: 'Hops must have channel ids', error: 'ExpectedValidRouteChannelIdsForPayViaRoutes', }, { args: { lnd: { default: {deletePayment}, router: { sendToRouteV2: ({}, cbk) => cbk({details: 'unknown wire error'}), }, }, routes: [route], }, description: 'There is no result on unknown wire errors', expected: {attempts: [{route}], failures: []}, }, { args: { lnd: { default: {deletePayment}, router: { sendToRouteV2: ({}, cbk) => cbk({ details: 'payment attempt not completed before timeout', }), }, }, routes: [route], }, description: 'There is no result on a timeout error', expected: {attempts: [{route}], failures: []}, }, { args: { lnd: { default: {deletePayment}, router: {sendToRouteV2: ({}, cbk) => cbk('err')}, }, routes: [route], }, description: 'An unexpected payment error is passed back', expected: { attempts: [{route}], error: [503, 'UnexpectedErrorWhenPayingViaRoute', {err: 'err'}], failures: [], }, }, { args: { lnd: { default: {deletePayment}, router: {sendToRouteV2: ({}, cbk) => cbk()}, }, routes: [route], }, description: 'A result is expected from sendToRoute', expected: { attempts: [{route}], error: [503, 'ExpectedResponseFromLndWhenPayingViaRoute'], failures: [], }, }, { args: { id: Buffer.alloc(32).toString('hex'), lnd: { default: {deletePayment}, router: { sendToRouteV2: ({}, cbk) => cbk(null, { preimage: Buffer.alloc(32), }), }, }, routes: [route, route], }, description: 'A success is returned', expected: { attempts: [{route}], failures: [], success: { route, confirmed_at: undefined, fee: route.fee, fee_mtokens: route.fee_mtokens, hops: route.hops, id: Buffer.alloc(32).toString('hex'), mtokens: route.mtokens, safe_fee: undefined, safe_tokens: undefined, secret: Buffer.alloc(32).toString('hex'), tokens: route.tokens, }, }, }, { args: { id: Buffer.alloc(32).toString('hex'), lnd: { default: {deletePayment}, router: { sendToRouteV2: ({}, cbk) => cbk(null, { preimage: 'preimage', }), }, }, routes: [route], }, description: 'A success is returned', expected: { attempts: [{route}], error: [503, 'UnexpectedResultWhenPayingViaSendToRouteSync'], failures: [], }, }, { args: { lnd: { default: {deletePayment}, router: { sendToRouteV2: ({}, cbk) => cbk(null, { failure: { chan_id: '1', code: 'UNKNOWN_FAILURE', }, preimage: Buffer.alloc(Number()), }), }, }, routes: [route], }, description: 'A routing failure is returned', expected: { attempts: [{route}], failures: [{ route, channel: undefined, height: undefined, index: undefined, mtokens: undefined, policy: null, public_key: undefined, reason: 'UnknownFailure', timeout_height: undefined, update: undefined, }], }, }, { args: { lnd: { default: {deletePayment}, router: { sendToRouteV2: ({}, cbk) => cbk(null, { failure: { chan_id: '1', code: 'UNKNOWN_FAILURE', failure_source_index: 2, }, preimage: Buffer.alloc(Number()), }), }, }, routes: [ { fee: 1, fee_mtokens: '1000', hops: [ { channel: '0x0x1', fee_mtokens: '1', forward_mtokens: '1', public_key: 'public_key', }, { channel: '0x0x1', fee_mtokens: '1', forward_mtokens: '1', public_key: 'public_key', }, ], tokens: 1, }, route, ], }, description: 'A routing failure that reaches the destination stops routes', expected: { attempts: [{ route: { fee: 1, fee_mtokens: '1000', hops: [ { channel: '0x0x1', fee_mtokens: '1', forward_mtokens: '1', public_key: 'public_key', }, { channel: '0x0x1', fee_mtokens: '1', forward_mtokens: '1', public_key: 'public_key', }, ], tokens: 1, }, }], failures: [ { channel: undefined, height: undefined, index: 2, mtokens: undefined, policy: null, public_key: undefined, reason: 'UnknownFailure', route: { fee: 1, fee_mtokens: '1000', hops: [ { channel: '0x0x1', fee_mtokens: '1', forward_mtokens: '1', public_key: 'public_key', }, { channel: '0x0x1', fee_mtokens: '1', forward_mtokens: '1', public_key: 'public_key', }, ], tokens: 1, }, timeout_height: undefined, update: undefined, }, ], }, }, { args: { lnd: { default: {deletePayment}, router: { sendToRouteV2: ({}, cbk) => cbk(null, { failure: { chan_id: '1', code: 'UNKNOWN_FAILURE', failure_source_index: 1, }, preimage: Buffer.alloc(Number()), }), }, }, routes: [route], }, description: 'A routing failure is returned from the destination', expected: { attempts: [{route}], failures: [{ route, channel: undefined, height: undefined, index: 1, mtokens: undefined, policy: null, public_key: undefined, reason: 'UnknownFailure', timeout_height: undefined, update: undefined, }], }, }, { args: { lnd: { default: {deletePayment}, router: { sendToRouteV2: ({}, cbk) => { return setTimeout(() => cbk({details: 'unknown wire error'}), 100); }, }, }, pathfinding_timeout: 99, routes: [route, route], }, description: 'Pathfinding attempt times out the payment', expected: { attempts: [{route}], error: [503, 'PathfindingTimeoutExceeded'], failures: [], }, }, ]; tests.forEach(({args, description, error, expected}) => { return test(description, (t, end) => { if (!!error) { throws(() => subscribeToPayViaRoutes(args), new Error(error), 'Got err'); return end(); } else { const attempts = []; const failures = []; let gotError; let gotSuccess; const sub = subscribeToPayViaRoutes(args); sub.on('error', err => gotError = err); sub.on('paying', attempt => attempts.push(attempt)); sub.on('routing_failure', failure => failures.push(failure)); sub.on('success', success => gotSuccess = success); return sub.once('end', () => { deepStrictEqual(attempts, expected.attempts, 'Got pay attempts'); deepStrictEqual(failures, expected.failures, 'Got pay failures'); deepStrictEqual(gotSuccess, expected.success, 'Got pay success'); const [errCode, errMessage] = gotError || []; const [expectedErrCode, expectedErrMessage] = expected.error || []; strictEqual(errCode, expectedErrCode, 'Got error code'); strictEqual(errMessage, expectedErrMessage, 'Got error message'); return end(); }); } }); });