ln-service 59.3.7 → 59.4.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,16 @@
1
1
  # Versions
2
2
 
3
+ ## 59.4.1
4
+
5
+ - `subscribeToForwardRequests`: Invalid requests to forward more than the
6
+ inbound HTLC amount or with an outbound CLTV past the inbound CLTV are no
7
+ longer emitted and are released for LND to fail
8
+
9
+ ## 59.4.0
10
+
11
+ - `sendMessage`: Add method to send a network onion message
12
+ - `subscribeToMessages`: Add method to listen to network onion messages
13
+
3
14
  ## 59.3.7
4
15
 
5
16
  - Add support for LND 0.21.3
package/README.md CHANGED
@@ -257,6 +257,7 @@ for `unlocker` methods.
257
257
  - [restrictMacaroon](#restrictmacaroon) - Add limitations to a macaroon
258
258
  - [revokeAccess](#revokeaccess) - Revoke all access macaroons given to an id
259
259
  - [routeFromChannels](#routefromchannels) - Convert channel series to a route
260
+ - [sendMessage](#sendmessage) - Send a network onion message
260
261
  - [sendMessageToPeer](#sendmessagetopeer) - Send a custom message to a peer
261
262
  - [sendToChainAddress](#sendtochainaddress) - Send on-chain to an address
262
263
  - [sendToChainAddresses](#sendtochainaddresses) - Send on-chain to addresses
@@ -281,6 +282,7 @@ for `unlocker` methods.
281
282
  - [subscribeToGraph](#subscribetograph) - Subscribe to network graph updates
282
283
  - [subscribeToInvoice](#subscribetoinvoice) - Subscribe to invoice updates
283
284
  - [subscribeToInvoices](#subscribetoinvoices) - Subscribe to all invoices
285
+ - [subscribeToMessages](#subscribetomessages) - Subscribe to network messages
284
286
  - [subscribeToOpenRequests](#subscribetoopenrequests) - Approve open requests
285
287
  - [subscribeToPastPayment](#subscribetopastpayment) - Subscribe to a payment
286
288
  - [subscribeToPastPayments](#subscribetopastpayments) - Subscribe to all sent
@@ -5140,6 +5142,53 @@ const res = routeFromChannels({channels, destination, height, mtokens});
5140
5142
  const {route} = res;
5141
5143
  ```
5142
5144
 
5145
+ ### sendMessage
5146
+
5147
+ Send a generic message over the network
5148
+
5149
+ The receiver of the message needs to first publish an inbound path to them,
5150
+ this is supplied via `inbound`. To send, add a series of relaying node ids
5151
+ that support message passing, to a landmark node provided by the receiver.
5152
+
5153
+ This method is not supported in LND 0.20.4 and below
5154
+
5155
+ {
5156
+ inbound: [{
5157
+ encrypted_data: <Encrypted Data Hex String>
5158
+ relay_key: <Blinded Relaying Public Key Into Destination Hex String>
5159
+ }]
5160
+ key: <Message Path Key Hex String>
5161
+ lnd: <Authenticated LND API Object>
5162
+ [message]: {
5163
+ type: <Message Payload Record Type Number String>
5164
+ value: <Message Payload Hex String>
5165
+ }
5166
+ outbound: [<Relaying Node Public Key Out of Source Hex String>]
5167
+ [reply]: [<Reply Path Relaying Node Public Key Back To Sender Hex String>]
5168
+ }
5169
+
5170
+ @returns via cbk or Promise
5171
+ {
5172
+ [reply]: <Reply Identifier Hex String>
5173
+ }
5174
+
5175
+ Example:
5176
+
5177
+ ```node
5178
+ const {sendMessage} = require('ln-service');
5179
+
5180
+ const {reply} = await sendMessage({
5181
+ lnd,
5182
+ inbound: blindedPath.path,
5183
+ key: blindedPath.key,
5184
+ outbound: [peer.public_key, blindedPath.introduction_node],
5185
+ message: {type, value},
5186
+ reply: [peer.public_key, own.public_key],
5187
+ });
5188
+
5189
+ // Wait for message reply on `reply` id
5190
+ ```
5191
+
5143
5192
  ### sendMessageToPeer
5144
5193
 
5145
5194
  Send a custom message to a connected peer
@@ -5833,6 +5882,9 @@ Subscribe to requests to forward payments
5833
5882
  Note that the outbound channel is only the requested channel, another may be
5834
5883
  selected internally to complete the forward.
5835
5884
 
5885
+ Invalid requests that have invalid fees or CLTVs cannot be forwarded and are
5886
+ not emitted, they are released for LND to fail
5887
+
5836
5888
  Requires `offchain:read`, `offchain:write` permission
5837
5889
 
5838
5890
  `onion` is not supported in LND 0.11.1 and below
@@ -6165,6 +6217,67 @@ const sub = subscribeToInvoices({lnd});
6165
6217
  const [lastUpdatedInvoice] = await once(sub, 'invoice_updated');
6166
6218
  ```
6167
6219
 
6220
+ ### subscribeToMessages
6221
+
6222
+ Subscribe to received onion messages
6223
+
6224
+ Requires `offchain:read` permission
6225
+
6226
+ This method is not supported in LND 0.20.4 and below
6227
+
6228
+ {
6229
+ lnd: <Authenticated LND API Object>
6230
+ }
6231
+
6232
+ @returns
6233
+ <EventEmitter Object>
6234
+
6235
+ @event 'message_received'
6236
+ {
6237
+ encrypted: <Encrypted Data Hex String>
6238
+ key: <Path Key Hex String>
6239
+ [message]: {
6240
+ type: <Message Payload Record Type Number String>
6241
+ value: <Message Payload Hex String>
6242
+ }
6243
+ onion: <Onion Packet Hex String>
6244
+ [reply]: {
6245
+ inbound: [{
6246
+ encrypted_data: <Encrypted Data Hex String>
6247
+ relay_key: <Blinded Relay Key Hex String>
6248
+ }]
6249
+ [introduction_edge]: <Introduction Node Edge Format Channel Id String>
6250
+ [introduction_node]: <Introduction Node Public Key Hex String>
6251
+ key: <Path Key Hex String>
6252
+ }
6253
+ via: <Message Received Via Peer with Id Public Key Hex String>
6254
+ }
6255
+
6256
+ ```node
6257
+ const {decryptBlindedPath} = require('bolt04');
6258
+ const {diffieHellmanComputeSecret} = require('ln-service');
6259
+ const {subscribeToMessages} = require('ln-service');
6260
+
6261
+ const sub = subscribeToMessages({lnd});
6262
+
6263
+ sub.on('message_received', async ({custom, encrypted, key}) => {
6264
+ // The message id is encrypted
6265
+ const {secret} = await diffieHellmanComputeSecret({
6266
+ lnd,
6267
+ partner_public_key: key,
6268
+ });
6269
+
6270
+ const decrypted = decryptBlindedPath({encrypted, key, secret});
6271
+
6272
+ // The received id should be equal to the one given out
6273
+ if (decrypted.id !== blindedPath.id) {
6274
+ return;
6275
+ }
6276
+
6277
+ // Message to path received
6278
+ });
6279
+ ```
6280
+
6168
6281
  ### subscribeToOpenRequests
6169
6282
 
6170
6283
  Subscribe to inbound channel open requests
package/index.js CHANGED
@@ -112,6 +112,7 @@ const {requestChainFeeIncrease} = require('lightning');
112
112
  const {restrictMacaroon} = require('./macaroons');
113
113
  const {revokeAccess} = require('lightning');
114
114
  const {routeFromChannels} = require('bolt07');
115
+ const {sendMessage} = require('lightning');
115
116
  const {sendMessageToPeer} = require('lightning');
116
117
  const {sendToChainAddress} = require('lightning');
117
118
  const {sendToChainAddresses} = require('lightning');
@@ -134,6 +135,7 @@ const {subscribeToForwards} = require('lightning');
134
135
  const {subscribeToGraph} = require('lightning');
135
136
  const {subscribeToInvoice} = require('lightning');
136
137
  const {subscribeToInvoices} = require('lightning');
138
+ const {subscribeToMessages} = require('lightning');
137
139
  const {subscribeToOpenRequests} = require('lightning');
138
140
  const {subscribeToPastPayment} = require('lightning');
139
141
  const {subscribeToPastPayments} = require('lightning');
@@ -279,6 +281,7 @@ module.exports = {
279
281
  restrictMacaroon,
280
282
  revokeAccess,
281
283
  routeFromChannels,
284
+ sendMessage,
282
285
  sendMessageToPeer,
283
286
  sendToChainAddress,
284
287
  sendToChainAddresses,
@@ -301,6 +304,7 @@ module.exports = {
301
304
  subscribeToGraph,
302
305
  subscribeToInvoice,
303
306
  subscribeToInvoices,
307
+ subscribeToMessages,
304
308
  subscribeToOpenRequests,
305
309
  subscribeToPastPayment,
306
310
  subscribeToPastPayments,
package/package.json CHANGED
@@ -14,25 +14,23 @@
14
14
  "dependencies": {
15
15
  "bolt07": "2.0.0",
16
16
  "invoices": "6.1.4",
17
- "lightning": "12.2.11",
17
+ "lightning": "12.3.1",
18
18
  "macaroon": "3.0.4"
19
19
  },
20
20
  "description": "Interaction helper for your Lightning Network daemon",
21
21
  "devDependencies": {
22
- "@alexbosworth/blockchain": "4.7.2",
22
+ "@alexbosworth/blockchain": "4.8.0",
23
23
  "async": "3.2.6",
24
24
  "asyncjs-util": "1.2.12",
25
25
  "bip32": "5.0.1",
26
26
  "bip66": "2.0.0",
27
- "bn.js": "5.2.5",
28
- "bs58check": "4.0.0",
29
- "ecpair": "3.0.1",
30
- "ln-docker-daemons": "8.0.4",
27
+ "bolt04": "1.0.0",
28
+ "ecpair": "3.0.2",
29
+ "ln-docker-daemons": "8.0.5",
31
30
  "p2tr": "3.0.0",
32
31
  "portfinder": "1.0.38",
33
- "psbt": "6.0.3",
34
- "tiny-secp256k1": "2.2.4",
35
- "varuint-bitcoin": "2.0.0"
32
+ "psbt": "6.0.4",
33
+ "tiny-secp256k1": "2.2.4"
36
34
  },
37
35
  "engines": {
38
36
  "node": ">=22"
@@ -63,5 +61,5 @@
63
61
  "integration-test-0.20.0": "DOCKER_LND_VERSION=v0.20.0-beta npm run test",
64
62
  "test": "echo $DOCKER_LND_VERSION && node test/runner"
65
63
  },
66
- "version": "59.3.7"
64
+ "version": "59.4.1"
67
65
  }
@@ -0,0 +1,180 @@
1
+ const {equal} = require('node:assert').strict;
2
+ const test = require('node:test');
3
+
4
+ const asyncRetry = require('async/retry');
5
+ const {blindedPathFromHops} = require('bolt04');
6
+ const {decryptBlindedPath} = require('bolt04');
7
+ const {setupChannel} = require('ln-docker-daemons');
8
+ const {spawnLightningCluster} = require('ln-docker-daemons');
9
+
10
+ const {diffieHellmanComputeSecret} = require('./../../');
11
+ const {sendMessage} = require('./../../');
12
+ const {subscribeToMessages} = require('./../../');
13
+
14
+ const capacity = 1e6;
15
+ const interval = 10;
16
+ const size = 5;
17
+ const times = 1000;
18
+ const type = '805805';
19
+ const value = Buffer.from('hello world').toString('hex');
20
+
21
+ // Subscribing to messages should surface message events
22
+ test('Subscribe to messages', async t => {
23
+ const {kill, nodes} = await spawnLightningCluster({size});
24
+ const messages = {};
25
+
26
+ const [control, alice, bob, charlie, dave] = nodes;
27
+
28
+ const {generate, lnd} = control;
29
+
30
+ try {
31
+ // Dave creates a blinded path into their node, given out of band to CTRL
32
+ const blindedPath = (() => {
33
+ const hops = [bob.id, charlie.id, dave.id];
34
+
35
+ const {id, key, path} = blindedPathFromHops({hops});
36
+
37
+ return {id, key, path, introduction_node: bob.id};
38
+ })();
39
+
40
+ // Control listens for a reply to their message
41
+ const subReply = subscribeToMessages({lnd});
42
+
43
+ subReply.on('message_received', async ({encrypted, key, message})=> {
44
+ // The message id is encrypted
45
+ const {secret} = await diffieHellmanComputeSecret({
46
+ lnd,
47
+ partner_public_key: key,
48
+ });
49
+
50
+ const decrypted = decryptBlindedPath({encrypted, key, secret});
51
+
52
+ messages.reply = {received_value: message.value, reply_id: decrypted.id};
53
+
54
+ return;
55
+ });
56
+
57
+ // Dave listens for a message to be received on their blinded path
58
+ const sub = subscribeToMessages({lnd: dave.lnd});
59
+
60
+ sub.on('message_received', async ({encrypted, key, message, reply}) => {
61
+ const {secret} = await diffieHellmanComputeSecret({
62
+ lnd: dave.lnd,
63
+ partner_public_key: key,
64
+ });
65
+
66
+ const decrypted = decryptBlindedPath({key, encrypted, secret});
67
+
68
+ // The received id should be equal to the one given out
69
+ if (decrypted.id !== blindedPath.id) {
70
+ return;
71
+ }
72
+
73
+ await sendMessage({
74
+ lnd: dave.lnd,
75
+ inbound: reply.inbound.map(hop => ({
76
+ encrypted_data: hop.encrypted_data,
77
+ relay_key: hop.relay_key,
78
+ })),
79
+ key: reply.key,
80
+ message: {type, value: message.value + message.value},
81
+ outbound: [charlie.id, bob.id, reply.introduction_node],
82
+ });
83
+ });
84
+
85
+ // Make channels between all nodes so they will relay messages
86
+ {
87
+ await asyncRetry({interval, times}, async () => {
88
+ await generate({});
89
+
90
+ await setupChannel({
91
+ capacity,
92
+ generate,
93
+ lnd,
94
+ to: alice,
95
+ });
96
+ });
97
+
98
+ // Make sure that send message is supported by LND
99
+ try {
100
+ await sendMessage({
101
+ lnd,
102
+ inbound: blindedPath.path,
103
+ key: blindedPath.key,
104
+ message: {type, value},
105
+ outbound: [alice.id, blindedPath.introduction_node],
106
+ reply: [alice.id, control.id],
107
+ });
108
+ } catch (err) {
109
+ await kill({});
110
+
111
+ const [code, message] = err;
112
+
113
+ equal(code, 501, 'Method not supported');
114
+ equal(message, 'SendOnionMessageMethodUnsupported', 'Unknown method');
115
+
116
+ return;
117
+ }
118
+
119
+ await asyncRetry({interval, times}, async () => {
120
+ await alice.generate({});
121
+
122
+ await setupChannel({
123
+ capacity,
124
+ generate: alice.generate,
125
+ lnd: alice.lnd,
126
+ to: bob,
127
+ });
128
+ });
129
+
130
+ await asyncRetry({interval, times}, async () => {
131
+ await bob.generate({});
132
+
133
+ await setupChannel({
134
+ capacity,
135
+ generate: bob.generate,
136
+ lnd: bob.lnd,
137
+ to: charlie,
138
+ });
139
+ });
140
+
141
+ await asyncRetry({interval, times}, async () => {
142
+ await charlie.generate({});
143
+
144
+ await setupChannel({
145
+ capacity,
146
+ generate: charlie.generate,
147
+ lnd: charlie.lnd,
148
+ to: dave,
149
+ });
150
+ });
151
+ }
152
+
153
+ const {reply} = await sendMessage({
154
+ lnd,
155
+ inbound: blindedPath.path,
156
+ key: blindedPath.key,
157
+ message: {type, value},
158
+ outbound: [alice.id, blindedPath.introduction_node],
159
+ reply: [alice.id, control.id],
160
+ });
161
+
162
+ // Wait for the message send and response
163
+ await asyncRetry({interval, times}, async () => {
164
+ if (!messages.reply) {
165
+ throw new Error('ExpectedMessageReplyReceived');
166
+ }
167
+
168
+ return;
169
+ });
170
+
171
+ equal(messages.reply.reply_id, reply, 'Got correct message reply id');
172
+ equal(messages.reply.received_value, value + value, 'Got message reply');
173
+
174
+ await kill({});
175
+ } catch (err) {
176
+ await kill({});
177
+
178
+ throw err;
179
+ }
180
+ });
@@ -3,7 +3,7 @@ const {equal} = require('node:assert').strict;
3
3
  const test = require('node:test');
4
4
 
5
5
  const bip32 = require('bip32');
6
- const bs58check = require('bs58check').default;
6
+ const {decodeBase58} = require('@alexbosworth/blockchain');
7
7
  const ecc = require('tiny-secp256k1')
8
8
  const {spawnLightningCluster} = require('ln-docker-daemons');
9
9
 
@@ -60,10 +60,12 @@ test(`Get master public keys`, async () => {
60
60
  const masterIdentityKey = keys.find(n => n.named === identityKeyName);
61
61
 
62
62
  // Convert the key from base58 into its raw form
63
- const rawKey = bs58check.decode(masterIdentityKey.extended_public_key);
63
+ const {payload} = decodeBase58({
64
+ encoded: masterIdentityKey.extended_public_key,
65
+ });
64
66
 
65
- const chainCode = Buffer.from(chainCodeFromMasterPublicKey(rawKey));
66
- const publicKey = Buffer.from(publicKeyFromMasterPublicKey(rawKey));
67
+ const chainCode = chainCodeFromMasterPublicKey(payload);
68
+ const publicKey = publicKeyFromMasterPublicKey(payload);
67
69
 
68
70
  // Make a bip32 object to derive from
69
71
  const masterKey = fromPublicKey(publicKey, chainCode);
@@ -1,32 +0,0 @@
1
- const BN = require('bn.js');
2
-
3
- const {encode} = require('varuint-bitcoin');
4
-
5
- const decBase = 10;
6
- const {isBuffer} = Buffer;
7
-
8
- /** Convert an array of script buffer elements to a fully formed script
9
-
10
- @param
11
- [<Script Element Buffer>, <Script OP_CODE Decimal Number>]
12
-
13
- @throws
14
- <Error> when a script element length exceeds maximum
15
-
16
- @returns
17
- <Script Hex>
18
- */
19
- module.exports = scriptElements => {
20
- // Convert numbers to buffers and hex data to pushdata
21
- const fullScript = scriptElements
22
- .map(element => {
23
- if (isBuffer(element)) {
24
- return Buffer.concat([encode(element.length), element]);
25
- } else {
26
- return new BN(element, decBase).toArrayLike(Buffer);
27
- }
28
- })
29
- .reduce((element, script) => Buffer.concat([element, script]));
30
-
31
- return fullScript.toString('hex');
32
- };