paid-services
Version:
Lightning Paid Services library
244 lines (193 loc) • 6.76 kB
JavaScript
const {addPeer} = require('ln-service');
const {address} = require('bitcoinjs-lib');
const asyncAuto = require('async/auto');
const asyncMap = require('async/map');
const asyncRetry = require('async/retry');
const {broadcastChainTransaction} = require('ln-service');
const {closeChannel} = require('ln-service');
const {createChainAddress} = require('ln-service');
const {getChainTransactions} = require('ln-service');
const {getChannel} = require('ln-service');
const {getChannels} = require('ln-service');
const {getNetwork} = require('ln-sync');
const {networks} = require('bitcoinjs-lib');
const {openChannel} = require('ln-service');
const {spawnLightningCluster} = require('ln-docker-daemons');
const {test} = require('@alexbosworth/tap');
const {Transaction} = require('bitcoinjs-lib');
const {changeChannelCapacity} = require('./../../capacity');
const {getCapacityChangeRequests} = require('./../../capacity');
const bufferAsHex = buffer => buffer.toString('hex');
const capacity = 1e6;
const {fromHex} = Transaction;
const id = Buffer.alloc(32).toString('hex');
const interval = 10;
const logger = {error: () => {}, info: () => {}};
const maturity = 100;
const size = 3;
const slow = 3000;
const splitCapacity = 200000;
const sumOf = arr => arr.reduce((sum, n) => sum + n, 0);
const times = 2000;
const {toOutputScript} = address;
const weightAsVBytes = n => Math.ceil(n / 4);
// A capacity replacement proposal should be counter signed and accepted
test(`Decrease capacity replacement`, async ({end, equal, strictSame}) => {
const {kill, nodes} = await spawnLightningCluster({size});
const [control, target, remote] = nodes;
const {generate, lnd} = control;
const network = networks[(await getNetwork({lnd})).bitcoinjs];
// Make some funds
await generate({count: maturity});
// Add remote as a peer
await addPeer({lnd, public_key: remote.id, socket: remote.socket});
try {
// Open up a new channel
const channelOpen = await asyncRetry({interval, times}, async () => {
return await openChannel({
lnd,
local_tokens: capacity,
partner_public_key: target.id,
partner_socket: target.socket,
});
});
// Wait for the channel to be active
const channel = await asyncRetry({interval, times}, async () => {
const [channel] = (await getChannels({lnd, is_active: true})).channels;
if (!channel) {
await generate({});
throw new Error('ExpectedChannelActivation');
}
const {policies} = await getChannel({lnd, id: channel.id});
const noPolicy = policies.find(n => !n.cltv_delta);
if (!!channel && !noPolicy) {
return channel;
}
await generate({});
throw new Error('ExpectedChannelActivation');
});
await asyncAuto({
// Propose the channel
propose: async () => {
await changeChannelCapacity({
lnd,
logger,
ask: (args, cbk) => {
if (args.name === 'add') {
return cbk({add: false});
}
if (args.name === 'amount') {
return cbk({amount: splitCapacity});
}
if (args.name === 'decrease') {
return cbk({decrease: 'open_channel'});
}
if (args.name === 'direction') {
return cbk({direction: 'decrease'});
}
if (args.name === 'internal') {
return cbk({internal: true});
}
if (args.name === 'key') {
return cbk({key: remote.id});
}
if (args.name === 'ok') {
return cbk({ok: true});
}
if (args.name === 'proceed') {
return cbk({proceed: true});
}
if (args.name === 'rate') {
return cbk({rate: args.default});
}
if (args.name === 'spend') {
return cbk({spend: false});
}
if (args.name === 'query') {
return cbk({query: target.id});
}
if (args.name === 'type') {
return cbk({type: 'Private'});
}
throw new Error('UnexpectedQueryNameForProposingSide');
},
nodes: [],
});
},
// Wait to see the inbound proposal
waitForProposal: async () => {
await asyncRetry({interval, times}, async () => {
const {requests} = await getCapacityChangeRequests({
lnd: target.lnd,
});
if (!requests.length) {
throw new Error('FailedToFindChangeRequest');
}
});
},
// Accept the channel
accept: ['waitForProposal', async ({}) => {
await changeChannelCapacity({
logger,
ask: (args, cbk) => {
if (args.name === 'accept') {
return cbk({accept: true});
}
if (args.name === 'ok') {
return cbk({ok: true});
}
throw new Error('UnexpectedQueryNameForAcceptingSide');
},
lnd: target.lnd,
nodes: [],
});
}],
// Generate to confirm the change
generate: ['waitForProposal', async ({}) => {
// Wait for the new channel to be active
const recreated = await asyncRetry({
times,
interval: slow,
},
async () => {
const {channels} = await getChannels({lnd, is_active: true});
const [channel] = channels
.filter(n => n.is_private)
.filter(n => n.capacity < capacity);
await generate({});
if (!channel) {
throw new Error('ExpectedChannelActivation');
}
const channelWithRemote = channels.find(channel => {
return channel.partner_public_key === remote.id;
});
equal(channelWithRemote.capacity, splitCapacity, 'Remote created');
{
const {policies} = await getChannel({lnd, id: channel.id});
const noPolicy = policies.find(n => !n.cltv_delta);
if (!!noPolicy) {
throw new Error('ExpectedChannelPolicy');
}
};
{
const {policies} = await getChannel({
id: channel.id,
lnd: target.lnd,
});
const noPolicy = policies.find(n => !n.cltv_delta);
if (!!noPolicy) {
throw new Error('ExpectedChannelPolicy');
}
};
return;
});
return;
}],
});
} catch (err) {
strictSame(err, null, 'Expected no failure');
} finally {
await kill({});
}
return end();
});