ln-service 53.5.1 → 53.7.1

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.
@@ -0,0 +1,209 @@
1
+ const asyncEach = require('async/each');
2
+ const asyncMap = require('async/map');
3
+ const asyncRetry = require('async/retry');
4
+ const {combinePsbts} = require('psbt');
5
+ const {createPsbt} = require('psbt');
6
+ const {decodePsbt} = require('psbt');
7
+ const {extractTransaction} = require('psbt');
8
+ const {finalizePsbt} = require('psbt');
9
+ const {spawnLightningCluster} = require('ln-docker-daemons');
10
+ const {test} = require('@alexbosworth/tap');
11
+ const {Transaction} = require('bitcoinjs-lib');
12
+ const {updatePsbt} = require('psbt');
13
+
14
+ const {broadcastChainTransaction} = require('./../../');
15
+ const {createChainAddress} = require('./../../');
16
+ const {fundPsbt} = require('./../../');
17
+ const {getChainBalance} = require('./../../');
18
+ const {getChainTransactions} = require('./../../');
19
+ const {getUtxos} = require('./../../');
20
+ const {partiallySignPsbt} = require('./../../');
21
+ const {sendToChainAddresses} = require('./../../');
22
+
23
+ const count = 150;
24
+ const flatten = arr => [].concat(...arr);
25
+ const interval = 10;
26
+ const size = 3;
27
+ const startingFunds = 1e7;
28
+ const times = 1000;
29
+ const tokens = 1e6;
30
+
31
+ // Partially signing a PSBT should result in a partially signed PSBT
32
+ test(`Partially sign PSBT`, async ({end, equal, strictSame}) => {
33
+ const {kill, nodes} = await spawnLightningCluster({size});
34
+
35
+ const [control, target, remote] = nodes;
36
+
37
+ try {
38
+ // Generate some funds for the control
39
+ await control.generate({count});
40
+
41
+ // Send starting coins to target and remote
42
+ await sendToChainAddresses({
43
+ lnd: control.lnd,
44
+ send_to: await asyncMap([target, remote], async ({lnd}) => {
45
+ return {
46
+ address: (await createChainAddress({lnd})).address,
47
+ tokens: startingFunds,
48
+ };
49
+ }),
50
+ });
51
+
52
+ // Make sure that the nodes have funds
53
+ await asyncRetry({interval, times}, async () => {
54
+ await asyncEach([control, target, remote], async ({lnd}) => {
55
+ await control.generate({});
56
+
57
+ if (!(await getChainBalance({lnd})).chain_balance) {
58
+ throw new Error('WaitingForChainBalance');
59
+ }
60
+ });
61
+ });
62
+
63
+ // Get UTXOs to spend
64
+ const [controlUtxo] = (await getUtxos({lnd: control.lnd})).utxos.reverse();
65
+ const [targetUtxo] = (await getUtxos({lnd: target.lnd})).utxos;
66
+ const [remoteUtxo] = (await getUtxos({lnd: remote.lnd})).utxos;
67
+
68
+ // Make addresses to spend to
69
+ const addresses = await asyncMap(nodes, async ({lnd}) => {
70
+ return await createChainAddress({lnd});
71
+ });
72
+
73
+ const [controlAddress, targetAddress, remoteAddress] = addresses;
74
+
75
+ // Fund the addresses using the selected UTXOs
76
+ const controlFund = await fundPsbt({
77
+ inputs: [{
78
+ transaction_id: controlUtxo.transaction_id,
79
+ transaction_vout: controlUtxo.transaction_vout,
80
+ }],
81
+ lnd: control.lnd,
82
+ outputs: [{tokens, address: controlAddress.address}],
83
+ });
84
+
85
+ const targetFund = await fundPsbt({
86
+ inputs: [{
87
+ transaction_id: targetUtxo.transaction_id,
88
+ transaction_vout: targetUtxo.transaction_vout,
89
+ }],
90
+ lnd: target.lnd,
91
+ outputs: [{tokens, address: targetAddress.address}],
92
+ });
93
+
94
+ const remoteFund = await fundPsbt({
95
+ inputs: [{
96
+ transaction_id: remoteUtxo.transaction_id,
97
+ transaction_vout: remoteUtxo.transaction_vout,
98
+ }],
99
+ lnd: remote.lnd,
100
+ outputs: [{tokens, address: remoteAddress.address}],
101
+ });
102
+
103
+ // Collect all inputs that are spending
104
+ const inputs = []
105
+ .concat(controlFund.inputs)
106
+ .concat(targetFund.inputs)
107
+ .concat(remoteFund.inputs);
108
+
109
+ // Collect all outputs being sent to
110
+ const outputs = []
111
+ .concat(controlFund.outputs)
112
+ .concat(targetFund.outputs)
113
+ .concat(remoteFund.outputs);
114
+
115
+ // Make a new PSBT that includes all the outputs and inputs
116
+ const base = createPsbt({
117
+ outputs: outputs.map(output => ({
118
+ script: output.output_script,
119
+ tokens: output.tokens,
120
+ })),
121
+ utxos: inputs.map(input => ({
122
+ id: input.transaction_id,
123
+ vout: input.transaction_vout,
124
+ })),
125
+ });
126
+
127
+ // Decode the PSBTs to get signing key details
128
+ const controlDecoded = decodePsbt({psbt: controlFund.psbt});
129
+ const targetDecoded = decodePsbt({psbt: targetFund.psbt});
130
+ const remoteDecoded = decodePsbt({psbt: remoteFund.psbt});
131
+
132
+ // Collect all the BIP32 derivations
133
+ const controlBip32Derivations = controlDecoded.inputs
134
+ .map(n => n.bip32_derivations);
135
+
136
+ const targetBip32Derivations = targetDecoded.inputs
137
+ .map(n => n.bip32_derivations);
138
+
139
+ const remoteBip32Derivations = remoteDecoded.inputs
140
+ .map(n => n.bip32_derivations);
141
+
142
+ const controlTransactions = controlDecoded.inputs
143
+ .map(n => n.non_witness_utxo);
144
+
145
+ const targetTransactions = targetDecoded.inputs
146
+ .map(n => n.non_witness_utxo);
147
+
148
+ const remoteTransactions = remoteDecoded.inputs
149
+ .map(n => n.non_witness_utxo);
150
+
151
+ const allDerivations = []
152
+ .concat(controlBip32Derivations)
153
+ .concat(targetBip32Derivations)
154
+ .concat(remoteBip32Derivations);
155
+
156
+ const bip32Derivations = flatten(allDerivations);
157
+
158
+ // Update the PSBT so that it has the consolidated details
159
+ const updated = updatePsbt({
160
+ bip32_derivations: bip32Derivations,
161
+ psbt: base.psbt,
162
+ sighashes: inputs.map(input => ({
163
+ id: input.transaction_id,
164
+ sighash: Transaction.SIGHASH_ALL,
165
+ vout: input.transaction_vout,
166
+ })),
167
+ transactions: controlTransactions.concat(targetTransactions),
168
+ });
169
+
170
+ await partiallySignPsbt({lnd: control.lnd, psbt: updated.psbt});
171
+
172
+ // Sign the PSBT
173
+ const psbts = await asyncMap(nodes, async ({lnd}) => {
174
+ return (await partiallySignPsbt({lnd, psbt: updated.psbt})).psbt;
175
+ });
176
+
177
+ // Finalize the signatures
178
+ const finalize = finalizePsbt({psbt: combinePsbts({psbts}).psbt});
179
+
180
+ // Pull out the transaction
181
+ const {transaction} = extractTransaction({psbt: finalize.psbt});
182
+
183
+ // Publish the transaction
184
+ await broadcastChainTransaction({lnd: control.lnd, transaction});
185
+
186
+ // Make sure that the transaction confirms into a block
187
+ await asyncEach(nodes, async ({lnd}) => {
188
+ return asyncRetry({interval, times}, async () => {
189
+ await control.generate({});
190
+
191
+ const {utxos} = await getUtxos({lnd});
192
+
193
+ const [confirmed] = utxos.filter(n => n.tokens === tokens);
194
+
195
+ if (!confirmed || !confirmed.confirmation_count) {
196
+ throw new Error('ExpectedConfirmedCoin');
197
+ }
198
+
199
+ equal(confirmed.tokens, tokens, 'Got confirmed funds');
200
+ });
201
+ });
202
+ } catch (err) {
203
+ strictSame(err, [501, 'PartiallySignPsbtMethodNotSupported'], 'NoSupport');
204
+ } finally {
205
+ await kill({});
206
+ }
207
+
208
+ return end();
209
+ });