paid-services 4.3.5 → 5.0.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # Versions
2
2
 
3
+ ## Version 5.0.1
4
+
5
+ - `createGroupChannel`, `joinGroupChannel`, `manageGroupJoin`: Double check
6
+ open channel acceptance when peering
7
+
8
+ ## Version 5.0.0
9
+
10
+ - 'createGroupChannel': Repeatedly broadcast chain transaction
11
+
12
+ ### Breaking Changes
13
+
14
+ - node.js 14 is no longer supported, node.js 16+ required
15
+
3
16
  ## Version 4.3.5
4
17
 
5
18
  - `createGroupChannel`: Show members that are present
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2021 Alex Bosworth
3
+ Copyright (c) 2021-2023 Alex Bosworth
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -47,7 +47,6 @@ const times = 2 * 60 * 10;
47
47
  // Open is publishing
48
48
  @event 'broadcasting'
49
49
  {
50
- id: <Transaction Id Hex String>
51
50
  transaction: <Transaction Hex String>
52
51
  }
53
52
 
@@ -105,16 +104,20 @@ module.exports = ({capacity, count, ecp, identity, lnd, members, rate}) => {
105
104
  coordinator.events.once('joined', async ({ids}) => {
106
105
  emitter.emit('filled', {ids});
107
106
 
108
- // Exit early when this is a pair group
109
- if (count === minGroupCount) {
110
- return coordinator.connected();
111
- }
107
+ try {
108
+ // Exit early when this is a pair group
109
+ if (count === minGroupCount) {
110
+ const [outbound] = ids.filter(n => n !== identity);
112
111
 
113
- const {inbound, outbound} = coordinator.partners(identity);
112
+ await peerWithPartners({capacity, lnd, outbound});
114
113
 
115
- // Connect to the inbound and outbound partners
116
- try {
117
- await peerWithPartners({inbound, lnd, outbound});
114
+ return coordinator.connected();
115
+ }
116
+
117
+ const {inbound, outbound} = coordinator.partners(identity);
118
+
119
+ // Connect to the inbound and outbound partners
120
+ await peerWithPartners({capacity, inbound, lnd, outbound});
118
121
 
119
122
  // Register as connected
120
123
  return coordinator.connected();
@@ -1,5 +1,6 @@
1
1
  const asyncAuto = require('async/auto');
2
2
  const asyncMap = require('async/map');
3
+ const {broadcastTransaction} = require('ln-sync');
3
4
  const {getIdentity} = require('ln-service');
4
5
  const {getNodeAlias} = require('ln-sync');
5
6
  const {returnResult} = require('asyncjs-util');
@@ -8,6 +9,7 @@ const tinysecp = require('tiny-secp256k1');
8
9
  const askForGroupDetails = require('./ask_for_group_details');
9
10
  const assembleChannelGroup = require('./assemble_channel_group');
10
11
 
12
+ const descriptionForGroup = 'group channel open';
11
13
  const join = arr => arr.join(', ');
12
14
  const niceName = ({alias, id}) => `${alias} ${id}`.trim();
13
15
 
@@ -20,10 +22,6 @@ const niceName = ({alias, id}) => `${alias} ${id}`.trim();
20
22
  }
21
23
 
22
24
  @returns via cbk or Promise
23
- {
24
- id: <Transaction Id Hex String>
25
- transaction: <Raw Transaction Hex String>
26
- }
27
25
  */
28
26
  module.exports = ({ask, lnd, logger}, cbk) => {
29
27
  return new Promise((resolve, reject) => {
@@ -107,7 +105,15 @@ module.exports = ({ask, lnd, logger}, cbk) => {
107
105
  coordinate.events.once('broadcast', broadcast => {
108
106
  coordinate.events.removeAllListeners();
109
107
 
110
- return cbk(null, {transaction_id: broadcast.id});
108
+ logger.info({transaction_id: broadcast.id});
109
+
110
+ return broadcastTransaction({
111
+ lnd,
112
+ logger,
113
+ description: descriptionForGroup,
114
+ transaction: broadcast.transaction,
115
+ },
116
+ cbk);
111
117
  });
112
118
 
113
119
  coordinate.events.once('error', err => {
@@ -1,5 +1,6 @@
1
1
  const asyncAuto = require('async/auto');
2
2
  const asyncMap = require('async/map');
3
+ const {broadcastTransaction} = require('ln-sync');
3
4
  const {getChainBalance} = require('ln-service');
4
5
  const {getIdentity} = require('ln-service');
5
6
  const {getMethods} = require('ln-service');
@@ -9,6 +10,7 @@ const tinysecp = require('tiny-secp256k1');
9
10
 
10
11
  const assembleChannelGroup = require('./assemble_channel_group');
11
12
 
13
+ const descriptionForGroup = 'group channel open';
12
14
  const halfOf = n => n / 2;
13
15
  const {isArray} = Array;
14
16
  const isPublicKey = n => !!n && /^0[2-3][0-9A-F]{64}$/i.test(n);
@@ -214,7 +216,21 @@ module.exports = (args, cbk) => {
214
216
  coordinate.events.once('broadcast', broadcast => {
215
217
  coordinate.events.removeAllListeners();
216
218
 
217
- return cbk(null, {transaction_id: broadcast.id});
219
+ args.logger.info({transaction_id: broadcast.id});
220
+
221
+ return broadcastTransaction({
222
+ description: descriptionForGroup,
223
+ lnd: args.lnd,
224
+ logger: args.logger,
225
+ transaction: broadcast.transaction,
226
+ },
227
+ err => {
228
+ if (!!err) {
229
+ return cbk(err);
230
+ }
231
+
232
+ return cbk(null, {transaction_id: broadcast.id});
233
+ });
218
234
  });
219
235
 
220
236
  coordinate.events.once('error', err => {
@@ -107,6 +107,7 @@ module.exports = ({capacity, coordinator, count, id, lnd, rate}, cbk) => {
107
107
  });
108
108
 
109
109
  return peerWithPartners({
110
+ capacity,
110
111
  lnd,
111
112
  inbound: partners.inbound,
112
113
  outbound: partners.outbound,
@@ -80,7 +80,11 @@ module.exports = (args, cbk) => {
80
80
  ({getJoinDetails}, cbk) =>
81
81
  {
82
82
  if (getJoinDetails.rate > args.max_rate) {
83
- return cbk([400, 'ExpectedHigherMaxFeeRateToJoinGroup']);
83
+ return cbk([
84
+ 400,
85
+ 'ExpectedHigherMaxFeeRateToJoinGroup',
86
+ {needed_max_fee_rate: getJoinDetails.rate},
87
+ ]);
84
88
  }
85
89
 
86
90
  args.logger.info({waiting_for_other_members: true});
@@ -21,13 +21,14 @@ const typeGroupChannelId = '1';
21
21
 
22
22
  {
23
23
  coordinator: <Group Coordinator Identity Public Key Hex String>
24
+ count: <Group Member Count Number>
24
25
  id: <Group Identifier Hex String>
25
26
  lnd: <Authenticated LND API Object>
26
27
  }
27
28
 
28
29
  @returns via cbk or Promise
29
30
  {
30
- inbound: <Inbound Peer Public Key Identity Hex String>
31
+ [inbound]: <Inbound Peer Public Key Identity Hex String>
31
32
  outbound: <Outbound Peer Public Key Identity Hex String>
32
33
  }
33
34
  */
@@ -1,3 +1,4 @@
1
+ const {acceptsChannelOpen} = require('ln-sync');
1
2
  const asyncAuto = require('async/auto');
2
3
  const asyncRetry = require('async/retry');
3
4
  const {connectPeer} = require('ln-sync');
@@ -9,20 +10,21 @@ const times = 2 * 60 * 30;
9
10
  /** Peer up with group channel partners
10
11
 
11
12
  {
12
- inbound: <Inbound Identity Public Key Hex String>
13
+ capacity: <Channel Capacity Tokens Number>
14
+ [inbound]: <Inbound Identity Public Key Hex String>
13
15
  lnd: <Authenticated LND API Object>
14
16
  outbound: <Outbound Identity Public Key Hex String>
15
17
  }
16
18
 
17
19
  @returns via cbk or Promise
18
20
  */
19
- module.exports = ({inbound, lnd, outbound}, cbk) => {
21
+ module.exports = ({capacity, inbound, lnd, outbound}, cbk) => {
20
22
  return new Promise((resolve, reject) => {
21
23
  return asyncAuto({
22
24
  // Check arguments
23
25
  validate: cbk => {
24
- if (!inbound) {
25
- return cbk([400, 'ExpectedInboundPeerIdentityToConnectTo']);
26
+ if (!capacity) {
27
+ return cbk([400, 'ExpectedChannelCapacityToPeerWithPartners']);
26
28
  }
27
29
 
28
30
  if (!lnd) {
@@ -38,6 +40,11 @@ module.exports = ({inbound, lnd, outbound}, cbk) => {
38
40
 
39
41
  // Attempt connecting to the inbound peer
40
42
  connectInbound: ['validate', ({}, cbk) => {
43
+ // Exit early when there is no inbound peer
44
+ if (!inbound) {
45
+ return cbk();
46
+ }
47
+
41
48
  return asyncRetry({interval, times}, cbk => {
42
49
  return connectPeer({lnd, id: inbound}, cbk);
43
50
  },
@@ -51,6 +58,25 @@ module.exports = ({inbound, lnd, outbound}, cbk) => {
51
58
  },
52
59
  cbk);
53
60
  }],
61
+
62
+ // Test if outbound peer would accept a channel
63
+ getAcceptance: ['connectOutbound', ({}, cbk) => {
64
+ return acceptsChannelOpen({
65
+ capacity,
66
+ lnd,
67
+ partner_public_key: outbound,
68
+ },
69
+ cbk);
70
+ }],
71
+
72
+ // Check that the proposal would be accepted
73
+ checkAcceptance: ['getAcceptance', ({getAcceptance}, cbk) => {
74
+ if (!getAcceptance.is_accepted) {
75
+ return cbk([503, 'PeerRejectedChanOpenRequest', {peer: outbound}]);
76
+ }
77
+
78
+ return cbk();
79
+ }],
54
80
  },
55
81
  returnResult({reject, resolve}, cbk));
56
82
  });
package/package.json CHANGED
@@ -16,7 +16,7 @@
16
16
  "ecpair": "2.1.0",
17
17
  "goldengate": "12.0.5",
18
18
  "invoices": "2.2.3",
19
- "ln-service": "54.10.5",
19
+ "ln-service": "56.1.0",
20
20
  "ln-sync": "4.3.2",
21
21
  "psbt": "2.7.2",
22
22
  "p2tr": "1.3.3",
@@ -25,12 +25,12 @@
25
25
  "description": "Lightning Paid Services library",
26
26
  "devDependencies": {
27
27
  "@alexbosworth/tap": "15.0.12",
28
- "ln-docker-daemons": "4.1.2",
28
+ "ln-docker-daemons": "5.0.1",
29
29
  "mock-lnd": "1.4.4",
30
30
  "secp256k1": "5.0.0"
31
31
  },
32
32
  "engines": {
33
- "node": ">=14"
33
+ "node": ">=16"
34
34
  },
35
35
  "keywords": [
36
36
  "lightning",
@@ -47,5 +47,5 @@
47
47
  "integration-tests": "tap -j 2 --branches=1 --functions=1 --lines=1 --statements=1 -t 180 test/integration/*.js",
48
48
  "test": "tap --branches=1 --functions=1 --lines=1 --statements=1 -t 60 test/actions/*.js test/balanced/*.js test/capacity/*.js test/client/*.js test/config/*.js test/p2p/*.js test/records/*.js test/respond/*.js test/server/*.js test/swaps/*.js test/services/*.js test/trades/*.js"
49
49
  },
50
- "version": "4.3.5"
50
+ "version": "5.0.1"
51
51
  }
@@ -584,6 +584,7 @@ module.exports = (args, cbk) => {
584
584
  const vout = tx.outs.findIndex(n => n.script.equals(script));
585
585
 
586
586
  args.emitter.emit('update', {
587
+ funding_transaction: signFunding.transaction,
587
588
  funding_transaction_id: id,
588
589
  funding_transaction_vout: vout,
589
590
  });
@@ -81,12 +81,25 @@ test(`Setup joint channel group`, async ({end, equal, strictSame}) => {
81
81
  capacity,
82
82
  count: nodes.length,
83
83
  lnd: control.lnd,
84
- logger: {info: line => createLog.push(line)},
84
+ logger: {info: line => {createLog.push(line)}},
85
85
  members: [],
86
86
  rate: feeRate,
87
87
  });
88
88
  },
89
89
 
90
+ // Make blocks to activate the channel
91
+ generate: async () => {
92
+ await asyncRetry({interval, times}, async () => {
93
+ await generate({});
94
+
95
+ const {channels} = await getChannels({lnd, is_active: true});
96
+
97
+ if (!channels.length) {
98
+ throw new Error('ExpectedChannelActivation');
99
+ }
100
+ });
101
+ },
102
+
90
103
  // Join the group
91
104
  join: async () => {
92
105
  return await asyncRetry({interval, times}, async () => {
@@ -108,21 +121,6 @@ test(`Setup joint channel group`, async ({end, equal, strictSame}) => {
108
121
 
109
122
  const ids = [group.join.transaction_id, group.create.transaction_id];
110
123
 
111
- // Finished, wait for the channels to activate
112
- await generate({count});
113
-
114
- const {getPendingChannels} = require('ln-service');
115
-
116
- await asyncRetry({interval, times}, async () => {
117
- await generate({});
118
-
119
- const {channels} = await getChannels({lnd, is_active: true});
120
-
121
- if (!channels.length) {
122
- throw new Error('ExpectedChannelActivation');
123
- }
124
- });
125
-
126
124
  strictSame(ids.length, nodes.length, 'Got tx ids');
127
125
  } catch (err) {
128
126
  strictSame(err, null, 'Expected no failure');
@@ -1,4 +1,5 @@
1
1
  const asyncRetry = require('async/retry');
2
+ const {broadcastChainTransaction} = require('ln-service');
2
3
  const {createChainAddress} = require('ln-service');
3
4
  const {createInvoice} = require('ln-service');
4
5
  const {getChannelBalance} = require('ln-service');
@@ -79,6 +80,16 @@ test(`Start offchain swap`, async ({end, equal, strictSame}) => {
79
80
  await target.generate({count: maturity});
80
81
  }
81
82
 
83
+ // Make sure control can pay off chain to target
84
+ {
85
+ await asyncRetry({interval, times}, async () => {
86
+ return await pay({
87
+ lnd,
88
+ request: (await createInvoice({tokens, lnd: target.lnd})).request,
89
+ });
90
+ });
91
+ }
92
+
82
93
  // Collect request messages
83
94
  const messages = [];
84
95
 
@@ -116,6 +127,14 @@ test(`Start offchain swap`, async ({end, equal, strictSame}) => {
116
127
  lnd: target.lnd,
117
128
  logger: {
118
129
  info: async message => {
130
+ // Make sure that target gets the funding transaction
131
+ if (message.funding_transaction) {
132
+ await broadcastChainTransaction({
133
+ lnd: target.lnd,
134
+ transaction: message.funding_transaction,
135
+ });
136
+ }
137
+
119
138
  if (!!message.response) {
120
139
  return cbk({response: message.response});
121
140
  }
@@ -71,8 +71,9 @@ test(`Sign capacity replacement`, async ({end, equal, strictSame}) => {
71
71
  // Close the channel
72
72
  const channelClose = await closeChannel({
73
73
  lnd,
74
- id: channel.id,
75
74
  is_force_close: true,
75
+ transaction_id: channel.transaction_id,
76
+ transaction_vout: channel.transaction_vout,
76
77
  });
77
78
 
78
79
  // Find the open tx
@@ -124,6 +125,7 @@ test(`Sign capacity replacement`, async ({end, equal, strictSame}) => {
124
125
  });
125
126
 
126
127
  await broadcastChainTransaction({transaction, lnd});
128
+ await broadcastChainTransaction({transaction, lnd: target.lnd});
127
129
 
128
130
  await generate({count: maturity});
129
131
 
@@ -1,4 +1,5 @@
1
1
  const asyncRetry = require('async/retry');
2
+ const {broadcastChainTransaction} = require('ln-service');
2
3
  const {createChainAddress} = require('ln-service');
3
4
  const {createInvoice} = require('ln-service');
4
5
  const {getChannelBalance} = require('ln-service');
@@ -52,6 +53,18 @@ test(`Swap with claim path`, async ({end, equal, strictSame}) => {
52
53
  });
53
54
  });
54
55
 
56
+ // Make sure control can pay off chain to target
57
+ {
58
+ await asyncRetry({interval, times}, async () => {
59
+ await generate({});
60
+
61
+ return await pay({
62
+ lnd,
63
+ request: (await createInvoice({tokens, lnd: target.lnd})).request,
64
+ });
65
+ });
66
+ }
67
+
55
68
  await asyncRetry({interval, times}, async () => {
56
69
  const {channels} = await getChannels({lnd, is_active: true});
57
70
 
@@ -116,6 +129,14 @@ test(`Swap with claim path`, async ({end, equal, strictSame}) => {
116
129
  lnd: target.lnd,
117
130
  logger: {
118
131
  info: async message => {
132
+ // Make sure that target gets the funding transaction
133
+ if (message.funding_transaction) {
134
+ await broadcastChainTransaction({
135
+ lnd: target.lnd,
136
+ transaction: message.funding_transaction,
137
+ });
138
+ }
139
+
119
140
  if (!!message.response) {
120
141
  return cbk({response: message.response});
121
142
  }
@@ -84,6 +84,18 @@ test(`Timeout a swap`, async ({end, equal, strictSame}) => {
84
84
  await target.generate({count: maturity});
85
85
  }
86
86
 
87
+ // Make sure control can pay off chain to target
88
+ {
89
+ await asyncRetry({interval, times}, async () => {
90
+ await generate({});
91
+
92
+ return await pay({
93
+ lnd,
94
+ request: (await createInvoice({tokens, lnd: target.lnd})).request,
95
+ });
96
+ });
97
+ }
98
+
87
99
  // Collect request messages
88
100
  const messages = [];
89
101