paid-services 3.2.0 → 3.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +4 -0
- package/capacity/accept_capacity_change.js +284 -0
- package/capacity/ask_for_change_details.js +505 -0
- package/capacity/ask_for_decrease.js +185 -0
- package/capacity/assemble_replacement_tx.js +171 -0
- package/capacity/capacity_change_requests.js +76 -0
- package/capacity/change_channel_capacity.js +183 -0
- package/capacity/encode_change_request.js +55 -0
- package/capacity/encode_sign_capacity_response.js +32 -0
- package/capacity/fee_for_replacement.js +108 -0
- package/capacity/finalize_capacity_replacement.js +67 -0
- package/capacity/get_capacity_change_requests.js +98 -0
- package/capacity/get_capacity_replacement.js +371 -0
- package/capacity/get_replacement_signature.js +232 -0
- package/capacity/index.js +4 -0
- package/capacity/initiate_capacity_change.js +506 -0
- package/capacity/interim_replacement_psbt.js +57 -0
- package/capacity/is_open_witness_script.js +40 -0
- package/capacity/parse_accept_request.js +58 -0
- package/capacity/parse_accept_response.js +52 -0
- package/capacity/parse_capacity_change_request.js +101 -0
- package/capacity/parse_sign_capacity_request.js +59 -0
- package/capacity/propose_capacity_change.js +361 -0
- package/capacity/service_types.json +6 -0
- package/capacity/sign_capacity_replacement.js +119 -0
- package/capacity/witness_script_from_close_txs.js +50 -0
- package/index.js +2 -0
- package/p2p/encode_peer_response.js +0 -1
- package/p2p/make_peer_request.js +3 -3
- package/package.json +6 -4
- package/test/capacity/test_assemble_replacement_tx.js +46 -0
- package/test/capacity/test_capacity_change_requests.js +54 -0
- package/test/capacity/test_encode_change_request.js +49 -0
- package/test/capacity/test_encode_sign_capacity_response.js +47 -0
- package/test/capacity/test_fee_for_replacement.js +40 -0
- package/test/capacity/test_finalize_capacity_replacement.js +45 -0
- package/test/capacity/test_interim_replacement_psbt.js +44 -0
- package/test/capacity/test_is_open_witness_script.js +40 -0
- package/test/capacity/test_parse_accept_request.js +42 -0
- package/test/capacity/test_parse_capacity_change_request.js +51 -0
- package/test/capacity/test_parse_sign_capacity_request.js +49 -0
- package/test/capacity/test_witness_script_from_close_txs.js +53 -0
- package/test/integration/test_accept_capacity_change.js +152 -0
- package/test/integration/test_change_channel_capacity.js +196 -0
- package/test/integration/test_decrease_channel_capacity.js +206 -0
- package/test/integration/test_increase_channel_capacity.js +197 -0
- package/test/integration/test_sign_capacity_replacement.js +142 -0
- package/test/p2p/test_encode_peer_request.js +53 -0
- package/test/p2p/test_encode_peer_response.js +38 -0
- package/test/p2p/test_encode_response_code.js +48 -0
- package/trades/network_from_network_record.js +6 -0
- package/trades/network_record_from_request.js +5 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
const {test} = require('@alexbosworth/tap');
|
|
2
|
+
|
|
3
|
+
const method = require('./../../capacity/witness_script_from_close_txs');
|
|
4
|
+
|
|
5
|
+
const makeArgs = overrides => {
|
|
6
|
+
const args = {
|
|
7
|
+
closing_tx_id: '8c037c526683ac573903a8028e764b6aee1556d584c06f9a4287643774388ae9',
|
|
8
|
+
transactions: [{
|
|
9
|
+
block_id: undefined,
|
|
10
|
+
confirmation_count: undefined,
|
|
11
|
+
confirmation_height: undefined,
|
|
12
|
+
created_at: '2021-12-25T03:33:28.000Z',
|
|
13
|
+
description: '0:closechannel:shortchanid-112150186098688',
|
|
14
|
+
fee: undefined,
|
|
15
|
+
id: '8c037c526683ac573903a8028e764b6aee1556d584c06f9a4287643774388ae9',
|
|
16
|
+
is_confirmed: false,
|
|
17
|
+
is_outgoing: false,
|
|
18
|
+
output_addresses: [],
|
|
19
|
+
tokens: 0,
|
|
20
|
+
transaction: '02000000000101b2841e16bd8db0e960ca5d861336cb21e2adb999c2b7ffd8ba68a92950215752000000000093bfcb80024a0100000000000022002063db12b145998af3bc5122d454ca37d5a4690b69626584ac3d2fdb225eea1490b2340f00000000002200200bdb542d1a67fbb991413a01530f5d430f43a082e27efe11657fbf76a1734a0c0400483045022100c0ce0fab8d4c6e3f3a46035c80a3567a801de32cb0c69fff8c18e3e6a03a66c0022018881a1b79957fd1d201ee5696ad535a4a70d8f8addd1bcec6c65e21bb7c4c8f01473044022015ab404487a28dce1975b132377a112181a0a8d6564d0b01be07506808c571a302204cf66d2de2a5c499fd2f23d00c4dc8642d133b321c99dcade227ac8291671ffc0147522102404943cc648419553e1fa33e7b620c07cb1b7225a9437dfa849bb0b842e12f1121038af0a2a3cd6ca9bc1bd38e4a6fa005a7c92b3ddd9b0d4c2c816efa10cdcdadf652ae9d84ef20',
|
|
21
|
+
}],
|
|
22
|
+
transaction_id: '5257215029a968bad8ffb7c299b9ade221cb3613865dca60e9b08dbd161e84b2',
|
|
23
|
+
transaction_vout: 0,
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
Object.keys(overrides).forEach(k => args[k] = overrides[k]);
|
|
27
|
+
|
|
28
|
+
return args;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const tests = [
|
|
32
|
+
{
|
|
33
|
+
args: makeArgs({}),
|
|
34
|
+
description: 'Get the witness script from a list of close transactions',
|
|
35
|
+
expected: {
|
|
36
|
+
script: '522102404943cc648419553e1fa33e7b620c07cb1b7225a9437dfa849bb0b842e12f1121038af0a2a3cd6ca9bc1bd38e4a6fa005a7c92b3ddd9b0d4c2c816efa10cdcdadf652ae',
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
];
|
|
40
|
+
|
|
41
|
+
tests.forEach(({args, description, error, expected}) => {
|
|
42
|
+
return test(description, async ({end, strictSame, throws}) => {
|
|
43
|
+
if (!!error) {
|
|
44
|
+
throws(() => method(args), new Error(error), 'Got error');
|
|
45
|
+
} else {
|
|
46
|
+
const res = method(args);
|
|
47
|
+
|
|
48
|
+
strictSame(res, expected, 'Got expected result');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return end();
|
|
52
|
+
});
|
|
53
|
+
});
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
const {address} = require('bitcoinjs-lib');
|
|
2
|
+
const asyncAuto = require('async/auto');
|
|
3
|
+
const asyncMap = require('async/map');
|
|
4
|
+
const asyncRetry = require('async/retry');
|
|
5
|
+
const {broadcastChainTransaction} = require('ln-service');
|
|
6
|
+
const {closeChannel} = require('ln-service');
|
|
7
|
+
const {createChainAddress} = require('ln-service');
|
|
8
|
+
const {getChainTransactions} = require('ln-service');
|
|
9
|
+
const {getChannels} = require('ln-service');
|
|
10
|
+
const {getNetwork} = require('ln-sync');
|
|
11
|
+
const {networks} = require('bitcoinjs-lib');
|
|
12
|
+
const {openChannel} = require('ln-service');
|
|
13
|
+
const {spawnLightningCluster} = require('ln-docker-daemons');
|
|
14
|
+
const {test} = require('@alexbosworth/tap');
|
|
15
|
+
const {Transaction} = require('bitcoinjs-lib');
|
|
16
|
+
|
|
17
|
+
const accept = require('./../../capacity/accept_capacity_change');
|
|
18
|
+
const finalize = require('./../../capacity/finalize_capacity_replacement');
|
|
19
|
+
const method = require('./../../capacity/sign_capacity_replacement');
|
|
20
|
+
const propose = require('./../../capacity/propose_capacity_change');
|
|
21
|
+
const {servicePeerRequests} = require('./../../');
|
|
22
|
+
|
|
23
|
+
const acceptCapacityChange = accept;
|
|
24
|
+
const bufferAsHex = buffer => buffer.toString('hex');
|
|
25
|
+
const capacity = 1e6;
|
|
26
|
+
const finalizeCapacityReplacement = finalize;
|
|
27
|
+
const {fromHex} = Transaction;
|
|
28
|
+
const id = Buffer.alloc(32).toString('hex');
|
|
29
|
+
const interval = 10;
|
|
30
|
+
const logger = {error: () => {}, info: () => {}};
|
|
31
|
+
const maturity = 100;
|
|
32
|
+
const proposeCapacityChange = propose;
|
|
33
|
+
const signCapacityReplacement = method;
|
|
34
|
+
const size = 2;
|
|
35
|
+
const sumOf = arr => arr.reduce((sum, n) => sum + n, 0);
|
|
36
|
+
const times = 2000;
|
|
37
|
+
const {toOutputScript} = address;
|
|
38
|
+
const weightAsVBytes = n => Math.ceil(n / 4);
|
|
39
|
+
|
|
40
|
+
// A capacity replacement proposal should be counter signed and accepted
|
|
41
|
+
test(`Accept capacity replacement`, async ({end, equal, strictSame}) => {
|
|
42
|
+
const {kill, nodes} = await spawnLightningCluster({size});
|
|
43
|
+
|
|
44
|
+
const [control, target] = nodes;
|
|
45
|
+
|
|
46
|
+
const {generate, lnd} = control;
|
|
47
|
+
|
|
48
|
+
const network = networks[(await getNetwork({lnd})).bitcoinjs];
|
|
49
|
+
|
|
50
|
+
// Make some funds
|
|
51
|
+
await generate({count: maturity});
|
|
52
|
+
|
|
53
|
+
try {
|
|
54
|
+
// Open up a new channel
|
|
55
|
+
const channelOpen = await openChannel({
|
|
56
|
+
lnd,
|
|
57
|
+
local_tokens: capacity,
|
|
58
|
+
partner_public_key: target.id,
|
|
59
|
+
partner_socket: target.socket,
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// Wait for the channel to be active
|
|
63
|
+
const channel = await asyncRetry({interval, times}, async () => {
|
|
64
|
+
const [channel] = (await getChannels({lnd, is_active: true})).channels;
|
|
65
|
+
|
|
66
|
+
if (!!channel) {
|
|
67
|
+
return channel;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
await generate({});
|
|
71
|
+
|
|
72
|
+
throw new Error('ExpectedChannelActivation');
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
// Find the open tx
|
|
76
|
+
const tx = (await getChainTransactions({lnd})).transactions.find(tx => {
|
|
77
|
+
return tx.id === channel.transaction_id;
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// Get the open tx output
|
|
81
|
+
const output = fromHex(tx.transaction).outs[channel.transaction_vout];
|
|
82
|
+
|
|
83
|
+
const baseFee = channel.commit_transaction_fee;
|
|
84
|
+
const deltaFee = weightAsVBytes(channel.commit_transaction_weight);
|
|
85
|
+
|
|
86
|
+
const openTxHash = fromHex(tx.transaction).getHash();
|
|
87
|
+
|
|
88
|
+
// Setup service to ack the accept
|
|
89
|
+
const service = servicePeerRequests({lnd});
|
|
90
|
+
const type = '8050002';
|
|
91
|
+
|
|
92
|
+
// Listen for the acceptance of the capacity change
|
|
93
|
+
service.request({type}, (req, res) => {
|
|
94
|
+
service.stop({});
|
|
95
|
+
|
|
96
|
+
return res.success({records: [{type: '1', value: tx.transaction}]});
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
await asyncAuto({
|
|
100
|
+
// Propose the channel
|
|
101
|
+
propose: async () => {
|
|
102
|
+
await proposeCapacityChange({
|
|
103
|
+
id,
|
|
104
|
+
lnd,
|
|
105
|
+
logger,
|
|
106
|
+
bitcoinjs_network: (await getNetwork({lnd})).bitcoinjs,
|
|
107
|
+
channel: channel.id,
|
|
108
|
+
decrease: [],
|
|
109
|
+
open_transaction: tx.transaction,
|
|
110
|
+
partner_public_key: target.id,
|
|
111
|
+
transaction_id: channel.transaction_id,
|
|
112
|
+
transaction_vout: channel.transaction_vout,
|
|
113
|
+
});
|
|
114
|
+
},
|
|
115
|
+
|
|
116
|
+
// Wait for a
|
|
117
|
+
accept: async () => {
|
|
118
|
+
await acceptCapacityChange({
|
|
119
|
+
id,
|
|
120
|
+
logger,
|
|
121
|
+
channel: channel.id,
|
|
122
|
+
from: control.id,
|
|
123
|
+
lnd: target.lnd,
|
|
124
|
+
});
|
|
125
|
+
},
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
// Wait for the new channel to be active
|
|
129
|
+
const recreated = await asyncRetry({interval, times}, async () => {
|
|
130
|
+
const [channel] = (await getChannels({lnd, is_active: true})).channels;
|
|
131
|
+
|
|
132
|
+
if (!!channel) {
|
|
133
|
+
return channel;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
await generate({});
|
|
137
|
+
|
|
138
|
+
throw new Error('ExpectedChannelActivation');
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
strictSame(recreated.is_active, true, 'Recreated channel is active');
|
|
142
|
+
|
|
143
|
+
// Recreate the channel but in reverse roles
|
|
144
|
+
|
|
145
|
+
} catch (err) {
|
|
146
|
+
strictSame(err, null, 'Expected no failure');
|
|
147
|
+
} finally {
|
|
148
|
+
await kill({});
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return end();
|
|
152
|
+
});
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
const {address} = require('bitcoinjs-lib');
|
|
2
|
+
const asyncAuto = require('async/auto');
|
|
3
|
+
const asyncMap = require('async/map');
|
|
4
|
+
const asyncRetry = require('async/retry');
|
|
5
|
+
const {broadcastChainTransaction} = require('ln-service');
|
|
6
|
+
const {closeChannel} = require('ln-service');
|
|
7
|
+
const {createChainAddress} = require('ln-service');
|
|
8
|
+
const {getChainTransactions} = require('ln-service');
|
|
9
|
+
const {getChannel} = require('ln-service');
|
|
10
|
+
const {getChannels} = require('ln-service');
|
|
11
|
+
const {getNetwork} = require('ln-sync');
|
|
12
|
+
const {networks} = require('bitcoinjs-lib');
|
|
13
|
+
const {openChannel} = require('ln-service');
|
|
14
|
+
const {spawnLightningCluster} = require('ln-docker-daemons');
|
|
15
|
+
const {test} = require('@alexbosworth/tap');
|
|
16
|
+
const {Transaction} = require('bitcoinjs-lib');
|
|
17
|
+
|
|
18
|
+
const {changeChannelCapacity} = require('./../../capacity');
|
|
19
|
+
const {getCapacityChangeRequests} = require('./../../capacity');
|
|
20
|
+
|
|
21
|
+
const bufferAsHex = buffer => buffer.toString('hex');
|
|
22
|
+
const capacity = 1e6;
|
|
23
|
+
const {fromHex} = Transaction;
|
|
24
|
+
const id = Buffer.alloc(32).toString('hex');
|
|
25
|
+
const interval = 10;
|
|
26
|
+
const log = () => {};
|
|
27
|
+
const maturity = 100;
|
|
28
|
+
const size = 2;
|
|
29
|
+
const slow = 5000;
|
|
30
|
+
const sumOf = arr => arr.reduce((sum, n) => sum + n, 0);
|
|
31
|
+
const times = 2000;
|
|
32
|
+
const {toOutputScript} = address;
|
|
33
|
+
const weightAsVBytes = n => Math.ceil(n / 4);
|
|
34
|
+
|
|
35
|
+
// A capacity replacement proposal should be counter signed and accepted
|
|
36
|
+
test(`Accept capacity replacement`, async ({end, equal, strictSame}) => {
|
|
37
|
+
const {kill, nodes} = await spawnLightningCluster({size});
|
|
38
|
+
|
|
39
|
+
const [control, target] = nodes;
|
|
40
|
+
|
|
41
|
+
const {generate, lnd} = control;
|
|
42
|
+
|
|
43
|
+
const network = networks[(await getNetwork({lnd})).bitcoinjs];
|
|
44
|
+
|
|
45
|
+
// Make some funds
|
|
46
|
+
await generate({count: maturity});
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
// Open up a new channel
|
|
50
|
+
const channelOpen = await openChannel({
|
|
51
|
+
lnd,
|
|
52
|
+
local_tokens: capacity,
|
|
53
|
+
partner_public_key: target.id,
|
|
54
|
+
partner_socket: target.socket,
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
// Wait for the channel to be active
|
|
58
|
+
const channel = await asyncRetry({interval, times}, async () => {
|
|
59
|
+
const [channel] = (await getChannels({lnd, is_active: true})).channels;
|
|
60
|
+
|
|
61
|
+
if (!channel) {
|
|
62
|
+
await generate({});
|
|
63
|
+
|
|
64
|
+
throw new Error('ExpectedChannelActivation');
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const {policies} = await getChannel({lnd, id: channel.id});
|
|
68
|
+
|
|
69
|
+
const noPolicy = policies.find(n => !n.cltv_delta);
|
|
70
|
+
|
|
71
|
+
if (!!channel && !noPolicy) {
|
|
72
|
+
return channel;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
await generate({});
|
|
76
|
+
|
|
77
|
+
throw new Error('ExpectedChannelActivation');
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
await asyncAuto({
|
|
81
|
+
// Propose the channel
|
|
82
|
+
propose: async () => {
|
|
83
|
+
await changeChannelCapacity({
|
|
84
|
+
lnd,
|
|
85
|
+
ask: (args, cbk) => {
|
|
86
|
+
if (args.name === 'amount') {
|
|
87
|
+
return cbk({amount: '0'});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (args.name === 'direction') {
|
|
91
|
+
return cbk({direction: 'decrease'});
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (args.name === 'ok') {
|
|
95
|
+
return cbk({ok: true});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (args.name === 'proceed') {
|
|
99
|
+
return cbk({proceed: true});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (args.name === 'query') {
|
|
103
|
+
return cbk({query: target.id});
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
throw new Error('UnknownQueryNameForInitiator');
|
|
107
|
+
},
|
|
108
|
+
logger: {error: log, info: log},
|
|
109
|
+
});
|
|
110
|
+
},
|
|
111
|
+
|
|
112
|
+
// Wait to see the inbound proposal
|
|
113
|
+
waitForProposal: async () => {
|
|
114
|
+
await asyncRetry({interval, times}, async () => {
|
|
115
|
+
const {requests} = await getCapacityChangeRequests({
|
|
116
|
+
lnd: target.lnd,
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
if (!requests.length) {
|
|
120
|
+
throw new Error('FailedToFindChangeRequest');
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
},
|
|
124
|
+
|
|
125
|
+
// Accept the channel
|
|
126
|
+
accept: ['waitForProposal', async ({}) => {
|
|
127
|
+
await changeChannelCapacity({
|
|
128
|
+
ask: (args, cbk) => {
|
|
129
|
+
if (args.name === 'accept') {
|
|
130
|
+
return cbk({accept: true});
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (args.name === 'ok') {
|
|
134
|
+
return cbk({ok: true});
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
throw new Error('UnexpectedQueryForAcceptingPeer');
|
|
138
|
+
},
|
|
139
|
+
lnd: target.lnd,
|
|
140
|
+
logger: {error: log, info: log},
|
|
141
|
+
});
|
|
142
|
+
}],
|
|
143
|
+
|
|
144
|
+
// Generate to confirm the change
|
|
145
|
+
generate: ['waitForProposal', async ({}) => {
|
|
146
|
+
// Wait for the new channel to be active
|
|
147
|
+
const recreated = await asyncRetry({
|
|
148
|
+
times,
|
|
149
|
+
interval: slow,
|
|
150
|
+
},
|
|
151
|
+
async () => {
|
|
152
|
+
const {channels} = await getChannels({lnd, is_active: true});
|
|
153
|
+
|
|
154
|
+
const [channel] = channels.filter(n => n.capacity < capacity);
|
|
155
|
+
|
|
156
|
+
await generate({});
|
|
157
|
+
|
|
158
|
+
if (!channel) {
|
|
159
|
+
throw new Error('ExpectedChannelActivation');
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
{
|
|
163
|
+
const {policies} = await getChannel({lnd, id: channel.id});
|
|
164
|
+
|
|
165
|
+
const noPolicy = policies.find(n => !n.cltv_delta);
|
|
166
|
+
|
|
167
|
+
if (!!noPolicy) {
|
|
168
|
+
throw new Error('ExpectedChannelPolicy');
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
{
|
|
173
|
+
const {policies} = await getChannel({
|
|
174
|
+
id: channel.id,
|
|
175
|
+
lnd: target.lnd,
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
const noPolicy = policies.find(n => !n.cltv_delta);
|
|
179
|
+
|
|
180
|
+
if (!!noPolicy) {
|
|
181
|
+
throw new Error('ExpectedChannelPolicy');
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return;
|
|
186
|
+
});
|
|
187
|
+
}],
|
|
188
|
+
});
|
|
189
|
+
} catch (err) {
|
|
190
|
+
strictSame(err, null, 'Expected no failure');
|
|
191
|
+
} finally {
|
|
192
|
+
await kill({});
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return end();
|
|
196
|
+
});
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
const {address} = require('bitcoinjs-lib');
|
|
2
|
+
const asyncAuto = require('async/auto');
|
|
3
|
+
const asyncMap = require('async/map');
|
|
4
|
+
const asyncRetry = require('async/retry');
|
|
5
|
+
const {broadcastChainTransaction} = require('ln-service');
|
|
6
|
+
const {closeChannel} = require('ln-service');
|
|
7
|
+
const {createChainAddress} = require('ln-service');
|
|
8
|
+
const {getChainTransactions} = require('ln-service');
|
|
9
|
+
const {getChannel} = require('ln-service');
|
|
10
|
+
const {getChannels} = require('ln-service');
|
|
11
|
+
const {getNetwork} = require('ln-sync');
|
|
12
|
+
const {networks} = require('bitcoinjs-lib');
|
|
13
|
+
const {openChannel} = require('ln-service');
|
|
14
|
+
const {spawnLightningCluster} = require('ln-docker-daemons');
|
|
15
|
+
const {test} = require('@alexbosworth/tap');
|
|
16
|
+
const {Transaction} = require('bitcoinjs-lib');
|
|
17
|
+
|
|
18
|
+
const {changeChannelCapacity} = require('./../../capacity');
|
|
19
|
+
const {getCapacityChangeRequests} = require('./../../capacity');
|
|
20
|
+
|
|
21
|
+
const bufferAsHex = buffer => buffer.toString('hex');
|
|
22
|
+
const capacity = 1e6;
|
|
23
|
+
const {fromHex} = Transaction;
|
|
24
|
+
const id = Buffer.alloc(32).toString('hex');
|
|
25
|
+
const interval = 10;
|
|
26
|
+
const logger = {error: () => {}, info: () => {}};
|
|
27
|
+
const maturity = 100;
|
|
28
|
+
const size = 2;
|
|
29
|
+
const slow = 3000;
|
|
30
|
+
const sumOf = arr => arr.reduce((sum, n) => sum + n, 0);
|
|
31
|
+
const times = 2000;
|
|
32
|
+
const {toOutputScript} = address;
|
|
33
|
+
const weightAsVBytes = n => Math.ceil(n / 4);
|
|
34
|
+
|
|
35
|
+
// A capacity replacement proposal should be counter signed and accepted
|
|
36
|
+
test(`Decrease capacity replacement`, async ({end, equal, strictSame}) => {
|
|
37
|
+
const {kill, nodes} = await spawnLightningCluster({size});
|
|
38
|
+
|
|
39
|
+
const [control, target] = nodes;
|
|
40
|
+
|
|
41
|
+
const {generate, lnd} = control;
|
|
42
|
+
|
|
43
|
+
const network = networks[(await getNetwork({lnd})).bitcoinjs];
|
|
44
|
+
|
|
45
|
+
// Make some funds
|
|
46
|
+
await generate({count: maturity});
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
// Open up a new channel
|
|
50
|
+
const channelOpen = await openChannel({
|
|
51
|
+
lnd,
|
|
52
|
+
local_tokens: capacity,
|
|
53
|
+
partner_public_key: target.id,
|
|
54
|
+
partner_socket: target.socket,
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
// Wait for the channel to be active
|
|
58
|
+
const channel = await asyncRetry({interval, times}, async () => {
|
|
59
|
+
const [channel] = (await getChannels({lnd, is_active: true})).channels;
|
|
60
|
+
|
|
61
|
+
if (!channel) {
|
|
62
|
+
await generate({});
|
|
63
|
+
|
|
64
|
+
throw new Error('ExpectedChannelActivation');
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const {policies} = await getChannel({lnd, id: channel.id});
|
|
68
|
+
|
|
69
|
+
const noPolicy = policies.find(n => !n.cltv_delta);
|
|
70
|
+
|
|
71
|
+
if (!!channel && !noPolicy) {
|
|
72
|
+
return channel;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
await generate({});
|
|
76
|
+
|
|
77
|
+
throw new Error('ExpectedChannelActivation');
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
await asyncAuto({
|
|
81
|
+
// Propose the channel
|
|
82
|
+
propose: async () => {
|
|
83
|
+
await changeChannelCapacity({
|
|
84
|
+
lnd,
|
|
85
|
+
logger,
|
|
86
|
+
ask: (args, cbk) => {
|
|
87
|
+
if (args.name === 'amount') {
|
|
88
|
+
return cbk({amount: '200000'});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (args.name === 'direction') {
|
|
92
|
+
return cbk({direction: 'decrease'});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (args.name === 'internal') {
|
|
96
|
+
return cbk({internal: true});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (args.name === 'ok') {
|
|
100
|
+
return cbk({ok: true});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (args.name === 'proceed') {
|
|
104
|
+
return cbk({proceed: true});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (args.name === 'rate') {
|
|
108
|
+
return cbk({rate: args.default});
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (args.name === 'spend') {
|
|
112
|
+
return cbk({spend: false});
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (args.name === 'query') {
|
|
116
|
+
return cbk({query: target.id});
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
throw new Error('UnexpectedQueryNameForProposingSide');
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
},
|
|
123
|
+
|
|
124
|
+
// Wait to see the inbound proposal
|
|
125
|
+
waitForProposal: async () => {
|
|
126
|
+
await asyncRetry({interval, times}, async () => {
|
|
127
|
+
const {requests} = await getCapacityChangeRequests({lnd: target.lnd});
|
|
128
|
+
|
|
129
|
+
if (!requests.length) {
|
|
130
|
+
throw new Error('FailedToFindChangeRequest');
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
},
|
|
134
|
+
|
|
135
|
+
// Accept the channel
|
|
136
|
+
accept: ['waitForProposal', async ({}) => {
|
|
137
|
+
await changeChannelCapacity({
|
|
138
|
+
logger,
|
|
139
|
+
ask: (args, cbk) => {
|
|
140
|
+
if (args.name === 'accept') {
|
|
141
|
+
return cbk({accept: true});
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (args.name === 'ok') {
|
|
145
|
+
return cbk({ok: true});
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
throw new Error('UnexpectedQueryNameForAcceptingSide');
|
|
149
|
+
},
|
|
150
|
+
lnd: target.lnd,
|
|
151
|
+
});
|
|
152
|
+
}],
|
|
153
|
+
|
|
154
|
+
// Generate to confirm the change
|
|
155
|
+
generate: ['waitForProposal', async ({}) => {
|
|
156
|
+
// Wait for the new channel to be active
|
|
157
|
+
const recreated = await asyncRetry({
|
|
158
|
+
times,
|
|
159
|
+
interval: slow,
|
|
160
|
+
},
|
|
161
|
+
async () => {
|
|
162
|
+
const [channel] = (await getChannels({lnd, is_active: true})).channels.filter(n => n.capacity < capacity);
|
|
163
|
+
|
|
164
|
+
await generate({});
|
|
165
|
+
|
|
166
|
+
if (!channel) {
|
|
167
|
+
throw new Error('ExpectedChannelActivation');
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
{
|
|
171
|
+
const {policies} = await getChannel({lnd, id: channel.id});
|
|
172
|
+
|
|
173
|
+
const noPolicy = policies.find(n => !n.cltv_delta);
|
|
174
|
+
|
|
175
|
+
if (!!noPolicy) {
|
|
176
|
+
throw new Error('ExpectedChannelPolicy');
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
{
|
|
181
|
+
const {policies} = await getChannel({
|
|
182
|
+
id: channel.id,
|
|
183
|
+
lnd: target.lnd,
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
const noPolicy = policies.find(n => !n.cltv_delta);
|
|
187
|
+
|
|
188
|
+
if (!!noPolicy) {
|
|
189
|
+
throw new Error('ExpectedChannelPolicy');
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
return;
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
return;
|
|
197
|
+
}],
|
|
198
|
+
});
|
|
199
|
+
} catch (err) {
|
|
200
|
+
strictSame(err, null, 'Expected no failure');
|
|
201
|
+
} finally {
|
|
202
|
+
await kill({});
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return end();
|
|
206
|
+
});
|