lightning 4.11.0 → 4.11.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,6 +1,6 @@
1
1
  # Versions
2
2
 
3
- ## 4.11.0
3
+ ## 4.11.1
4
4
 
5
5
  - `getFailedPayments`: Add method to get past failed payments
6
6
  - `subscribeToRpcRequests`: Add method to listen to and interact with RPC requests
@@ -557,6 +557,44 @@ service Lightning {
557
557
  */
558
558
  rpc RegisterRPCMiddleware (stream RPCMiddlewareResponse)
559
559
  returns (stream RPCMiddlewareRequest);
560
+
561
+ /* lncli: `sendcustom`
562
+ SendCustomMessage sends a custom peer message.
563
+ */
564
+ rpc SendCustomMessage (SendCustomMessageRequest)
565
+ returns (SendCustomMessageResponse);
566
+
567
+ /* lncli: `subscribecustom`
568
+ SubscribeCustomMessages subscribes to a stream of incoming custom peer
569
+ messages.
570
+ */
571
+ rpc SubscribeCustomMessages (SubscribeCustomMessagesRequest)
572
+ returns (stream CustomMessage);
573
+ }
574
+
575
+ message SubscribeCustomMessagesRequest {
576
+ }
577
+
578
+ message CustomMessage {
579
+ // Peer from which the message originates
580
+ bytes peer = 1;
581
+
582
+ // Message type. This value will be in the custom range (>= 32768).
583
+ uint32 type = 2;
584
+
585
+ // Raw message data
586
+ bytes data = 3;
587
+ }
588
+
589
+ message SendCustomMessageRequest {
590
+ // Peer to send the message to
591
+ bytes peer = 1;
592
+ // Message type. This value needs to be in the custom range (>= 32768).
593
+ uint32 type = 2;
594
+ // Raw message data.
595
+ bytes data = 3;
596
+ }
597
+ message SendCustomMessageResponse {
560
598
  }
561
599
 
562
600
  message Utxo {
@@ -84,6 +84,7 @@ const {recoverFundsFromChannels} = require('./offchain');
84
84
  const {removePeer} = require('./peers');
85
85
  const {requestChainFeeIncrease} = require('./onchain');
86
86
  const {revokeAccess} = require('./macaroon');
87
+ const {sendMessageToPeer} = require('./offchain');
87
88
  const {sendToChainAddress} = require('./onchain');
88
89
  const {sendToChainAddresses} = require('./onchain');
89
90
  const {sendToChainOutputScripts} = require('./onchain');
@@ -110,6 +111,7 @@ const {subscribeToPastPayments} = require('./offchain');
110
111
  const {subscribeToPayViaDetails} = require('./offchain');
111
112
  const {subscribeToPayViaRequest} = require('./offchain');
112
113
  const {subscribeToPayViaRoutes} = require('./offchain');
114
+ const {subscribeToPeerMessages} = require('./offchain');
113
115
  const {subscribeToPeers} = require('./peers');
114
116
  const {subscribeToProbeForRoute} = require('./offchain');
115
117
  const {subscribeToRpcRequests} = require('./macaroon');
@@ -214,6 +216,7 @@ module.exports = {
214
216
  removePeer,
215
217
  requestChainFeeIncrease,
216
218
  revokeAccess,
219
+ sendMessageToPeer,
217
220
  sendToChainAddress,
218
221
  sendToChainAddresses,
219
222
  sendToChainOutputScripts,
@@ -240,6 +243,7 @@ module.exports = {
240
243
  subscribeToPayViaDetails,
241
244
  subscribeToPayViaRequest,
242
245
  subscribeToPayViaRoutes,
246
+ subscribeToPeerMessages,
243
247
  subscribeToPeers,
244
248
  subscribeToProbeForRoute,
245
249
  subscribeToRpcRequests,
@@ -334,6 +334,10 @@
334
334
  "method": "DeleteMacaroonId",
335
335
  "type": "default"
336
336
  },
337
+ "sendMessageToPeer": {
338
+ "method": "SendCustomMessage",
339
+ "type": "default"
340
+ },
337
341
  "sendToChainAddress": {
338
342
  "method": "SendCoins",
339
343
  "type": "default"
@@ -442,6 +446,10 @@
442
446
  "method": "SendToRouteV2",
443
447
  "type": "router"
444
448
  },
449
+ "subscribeToPeerMessages": {
450
+ "method": "SubscribeCustomMessages",
451
+ "type": "default"
452
+ },
445
453
  "subscribeToPeers": {
446
454
  "method": "SubscribePeerEvents",
447
455
  "type": "default"
@@ -32,6 +32,7 @@ const payViaRoutes = require('./pay_via_routes');
32
32
  const probeForRoute = require('./probe_for_route');
33
33
  const recoverFundsFromChannel = require('./recover_funds_from_channel');
34
34
  const recoverFundsFromChannels = require('./recover_funds_from_channels');
35
+ const sendMessageToPeer = require('./send_message_to_peer');
35
36
  const subscribeToBackups = require('./subscribe_to_backups');
36
37
  const subscribeToChannels = require('./subscribe_to_channels');
37
38
  const subscribeToForwardRequests = require('./subscribe_to_forward_requests');
@@ -42,6 +43,7 @@ const subscribeToPastPayments = require('./subscribe_to_past_payments');
42
43
  const subscribeToPayViaDetails = require('./subscribe_to_pay_via_details');
43
44
  const subscribeToPayViaRequest = require('./subscribe_to_pay_via_request');
44
45
  const subscribeToPayViaRoutes = require('./subscribe_to_pay_via_routes');
46
+ const subscribeToPeerMessages = require('./subscribe_to_peer_messages');
45
47
  const subscribeToProbeForRoute = require('./subscribe_to_probe_for_route');
46
48
  const updateConnectedWatchtower = require('./update_connected_watchtower');
47
49
  const updatePathfindingSettings = require('./update_pathfinding_settings');
@@ -84,6 +86,7 @@ module.exports = {
84
86
  probeForRoute,
85
87
  recoverFundsFromChannel,
86
88
  recoverFundsFromChannels,
89
+ sendMessageToPeer,
87
90
  subscribeToBackups,
88
91
  subscribeToChannels,
89
92
  subscribeToForwardRequests,
@@ -94,6 +97,7 @@ module.exports = {
94
97
  subscribeToPayViaDetails,
95
98
  subscribeToPayViaRequest,
96
99
  subscribeToPayViaRoutes,
100
+ subscribeToPeerMessages,
97
101
  subscribeToProbeForRoute,
98
102
  updateConnectedWatchtower,
99
103
  updatePathfindingSettings,
@@ -0,0 +1,76 @@
1
+ const asyncAuto = require('async/auto');
2
+ const {returnResult} = require('asyncjs-util');
3
+
4
+ const {isLnd} = require('./../../lnd_requests');
5
+
6
+ const bufferFromHex = hex => Buffer.from(hex, 'hex');
7
+ const defaultMessageType = 32768;
8
+ const isHex = n => !!n && !(n.length % 2) && /^[0-9A-F]*$/i.test(n);
9
+ const isPublicKey = n => !!n && /^[0-9A-F]{66}$/i.test(n);
10
+ const method = 'sendCustomMessage';
11
+ const notSupported = /unknown/;
12
+ const type = 'default';
13
+
14
+ /** Send a custom message to a connected peer
15
+
16
+ If specified, message type is expected to be between 32768 and 65535
17
+
18
+ Message data should not be larger than 65533 bytes
19
+
20
+ Note: this method is not supported in LND versions 0.13.3 and below
21
+
22
+ Requires `offchain:write` permission
23
+
24
+ {
25
+ lnd: <Authenticated LND API Object>
26
+ message: <Message Hex String>
27
+ public_key: <To Peer Public Key Hex String>
28
+ [type]: <Message Type Number>
29
+ }
30
+
31
+ @returns via cbk or Promise
32
+ */
33
+ module.exports = (args, cbk) => {
34
+ return new Promise((resolve, reject) => {
35
+ return asyncAuto({
36
+ // Check arguments
37
+ validate: cbk => {
38
+ if (!isLnd({method, type, lnd: args.lnd})) {
39
+ return cbk([400, 'ExpectedAuthenticatedLndToSendMessageToPeer']);
40
+ }
41
+
42
+ if (!isHex(args.message)) {
43
+ return cbk([400, 'ExpectedCustomMessageDataToSendToPeer']);
44
+ }
45
+
46
+ if (!isPublicKey(args.public_key)) {
47
+ return cbk([400, 'ExpectedPeerPublicKeyToSendMessageTo']);
48
+ }
49
+
50
+ return cbk();
51
+ },
52
+
53
+ // Send the message to the peer
54
+ send: ['validate', ({}, cbk) => {
55
+ return args.lnd[type][method]({
56
+ data: bufferFromHex(args.message),
57
+ peer: bufferFromHex(args.public_key),
58
+ type: args.type || defaultMessageType,
59
+ },
60
+ err => {
61
+ if (!!err && notSupported.test(err.details)) {
62
+ return cbk([501, 'SendMessageToPeerMethodNotSupported']);
63
+ }
64
+
65
+ if (!!err) {
66
+ console.log("ERR", err)
67
+ return cbk([503, 'UnexpectedErrorSendingMessageToPeer', {err}]);
68
+ }
69
+
70
+ return cbk();
71
+ });
72
+ }],
73
+ },
74
+ returnResult({reject, resolve}, cbk));
75
+ });
76
+ };
@@ -0,0 +1,63 @@
1
+ const EventEmitter = require('events');
2
+
3
+ const asyncAuto = require('async/auto');
4
+ const {returnResult} = require('asyncjs-util');
5
+
6
+ const {emitSubscriptionError} = require('./../../grpc');
7
+ const {handleRemoveListener} = require('./../../grpc');
8
+ const {isLnd} = require('./../../lnd_requests');
9
+ const {rpcPeerMessageAsMessage} = require('./../../lnd_responses');
10
+
11
+ const events = ['message_received'];
12
+ const method = 'SubscribeCustomMessages';
13
+ const type = 'default';
14
+
15
+ /** Subscribe to incoming peer messages
16
+
17
+ Requires `offchain:read` permission
18
+
19
+ This method is not supported in LND 0.13.3 and below
20
+
21
+ {
22
+ lnd: <Authenticated LND API Object>
23
+ }
24
+
25
+ @returns
26
+ <EventEmitter Object>
27
+
28
+ // A message was received from a peer
29
+ @event 'message_received'
30
+ {
31
+ message: <Message Hex String>
32
+ public_key: <From Peer Public Key Hex String>
33
+ type: <Message Type Number>
34
+ }
35
+ */
36
+ module.exports = ({lnd}) => {
37
+ if (!isLnd({lnd, method, type})) {
38
+ throw new Error('ExpectedLndToSubscribeToPeerMessages');
39
+ }
40
+
41
+ const emitter = new EventEmitter();
42
+ const subscription = lnd[type][method]({});
43
+
44
+ const errored = emitSubscriptionError({emitter, subscription});
45
+
46
+ // Terminate subscription when all listeners are removed
47
+ handleRemoveListener({subscription, emitter, events});
48
+
49
+ subscription.on('end', () => emitter.emit('end'));
50
+ subscription.on('error', err => errored(err));
51
+ subscription.on('status', n => emitter.emit('status', n));
52
+
53
+ subscription.on('data', message => {
54
+ try {
55
+ // Notify listeners of the message
56
+ emitter.emit('message_received', rpcPeerMessageAsMessage(message));
57
+ } catch (err) {
58
+ return errored([503, err.message]);
59
+ }
60
+ });
61
+
62
+ return emitter;
63
+ };
@@ -31,6 +31,7 @@ const rpcNodeAsNode = require('./rpc_node_as_node');
31
31
  const rpcOutpointAsUpdate = require('./rpc_outpoint_as_update');
32
32
  const rpcPaymentAsPayment = require('./rpc_payment_as_payment');
33
33
  const rpcPeerAsPeer = require('./rpc_peer_as_peer');
34
+ const rpcPeerMessageAsMessage = require('./rpc_peer_message_as_message');
34
35
  const rpcRequestUpdateAsEvent = require('./rpc_request_update_as_event');
35
36
  const rpcResolutionAsResolution = require('./rpc_resolution_as_resolution');
36
37
  const rpcRouteAsRoute = require('./rpc_route_as_route');
@@ -72,6 +73,7 @@ module.exports = {
72
73
  rpcOutpointAsUpdate,
73
74
  rpcPaymentAsPayment,
74
75
  rpcPeerAsPeer,
76
+ rpcPeerMessageAsMessage,
75
77
  rpcRequestUpdateAsEvent,
76
78
  rpcResolutionAsResolution,
77
79
  rpcRouteAsRoute,
@@ -0,0 +1,44 @@
1
+ const bufferAsHex = buffer => buffer.toString('hex');
2
+ const {isBuffer} = Buffer;
3
+
4
+ /** Map an RPC peer message to message details
5
+
6
+ {
7
+ data: <Message Data Buffer Object>
8
+ peer: <Peer Public Key Buffer Object>
9
+ type: <Message Type Number>
10
+ }
11
+
12
+ @throws
13
+ <Error>
14
+
15
+ @returns
16
+ {
17
+ message: <Message Data Hex String>
18
+ public_key: <Peer Public Key Hex String>
19
+ type: <Message Type Number>
20
+ }
21
+ */
22
+ module.exports = args => {
23
+ if (!args) {
24
+ throw new Error('ExpectedRpcMessageToDerivePeerMessage');
25
+ }
26
+
27
+ if (!isBuffer(args.data)) {
28
+ throw new Error('ExpectedPeerMessageDataToDerivePeerMessage');
29
+ }
30
+
31
+ if (!isBuffer(args.peer)) {
32
+ throw new Error('ExpectedPeerPublicKeyBytesToDerivePeerMessage');
33
+ }
34
+
35
+ if (!args.type) {
36
+ throw new Error('ExpectedCustomMessageTypeNumberToDeriveMessage');
37
+ }
38
+
39
+ return {
40
+ message: bufferAsHex(args.data),
41
+ public_key: bufferAsHex(args.peer),
42
+ type: args.type,
43
+ };
44
+ };
package/package.json CHANGED
@@ -8,7 +8,7 @@
8
8
  },
9
9
  "dependencies": {
10
10
  "@grpc/grpc-js": "1.4.1",
11
- "@grpc/proto-loader": "0.6.5",
11
+ "@grpc/proto-loader": "0.6.6",
12
12
  "@types/express": "4.17.13",
13
13
  "@types/node": "16.11.1",
14
14
  "@types/request": "2.48.7",
@@ -56,5 +56,5 @@
56
56
  "directory": "test/typescript"
57
57
  },
58
58
  "types": "index.d.ts",
59
- "version": "4.11.0"
59
+ "version": "4.11.1"
60
60
  }
@@ -0,0 +1,68 @@
1
+ const {test} = require('@alexbosworth/tap');
2
+
3
+ const {sendMessageToPeer} = require('./../../../lnd_methods');
4
+
5
+ const makeLnd = ({err}) => {
6
+ return {default: {sendCustomMessage: ({}, cbk) => cbk(err)}};
7
+ };
8
+
9
+ const makeArgs = override => {
10
+ const args = {
11
+ lnd: makeLnd({}),
12
+ message: Buffer.from('message').toString('hex'),
13
+ public_key: Buffer.alloc(33, 3).toString('hex'),
14
+ type: 40000,
15
+ };
16
+
17
+ Object.keys(override || {}).forEach(key => args[key] = override[key]);
18
+
19
+ return args;
20
+ };
21
+
22
+ const tests = [
23
+ {
24
+ args: makeArgs({lnd: undefined}),
25
+ description: 'LND is required',
26
+ error: [400, 'ExpectedAuthenticatedLndToSendMessageToPeer'],
27
+ },
28
+ {
29
+ args: makeArgs({message: undefined}),
30
+ description: 'A message is required',
31
+ error: [400, 'ExpectedCustomMessageDataToSendToPeer'],
32
+ },
33
+ {
34
+ args: makeArgs({public_key: undefined}),
35
+ description: 'A public key is required',
36
+ error: [400, 'ExpectedPeerPublicKeyToSendMessageTo'],
37
+ },
38
+ {
39
+ args: makeArgs({lnd: makeLnd({err: {details: 'unknown service'}})}),
40
+ description: 'Unimplemented error is returned',
41
+ error: [501, 'SendMessageToPeerMethodNotSupported'],
42
+ },
43
+ {
44
+ args: makeArgs({lnd: makeLnd({err: 'err'})}),
45
+ description: 'Server error is returned',
46
+ error: [503, 'UnexpectedErrorSendingMessageToPeer', {err: 'err'}],
47
+ },
48
+ {
49
+ args: makeArgs({}),
50
+ description: 'Message is sent to peer',
51
+ },
52
+ {
53
+ args: makeArgs({type: undefined}),
54
+ description: 'Message with default type is sent to peer',
55
+ },
56
+ ];
57
+
58
+ tests.forEach(({args, description, error, expected}) => {
59
+ return test(description, async ({end, equal, rejects}) => {
60
+ if (!!error) {
61
+ await rejects(sendMessageToPeer(args), error, 'Got expected error');
62
+ } else {
63
+ await sendMessageToPeer(args);
64
+ }
65
+
66
+ return end();
67
+ });
68
+ });
@@ -0,0 +1,132 @@
1
+ const EventEmitter = require('events');
2
+
3
+ const {test} = require('@alexbosworth/tap');
4
+
5
+ const {subscribeToPeerMessages} = require('./../../../lnd_methods');
6
+
7
+ const makeLnd = overrides => {
8
+ const data = {
9
+ data: Buffer.alloc(1),
10
+ peer: Buffer.alloc(33, 3),
11
+ type: 44444,
12
+ };
13
+
14
+ Object.keys(overrides).forEach(k => data[k] = overrides[k]);
15
+
16
+ return {
17
+ default: {
18
+ SubscribeCustomMessages: ({}) => {
19
+ const emitter = new EventEmitter();
20
+
21
+ emitter.cancel = () => {};
22
+
23
+ if (overrides.data === undefined) {
24
+ process.nextTick(() => emitter.emit('data', data));
25
+ } else {
26
+ process.nextTick(() => emitter.emit('data', overrides.data));
27
+ }
28
+
29
+ return emitter;
30
+ },
31
+ },
32
+ };
33
+ };
34
+
35
+ const tests = [
36
+ {
37
+ args: {
38
+ lnd: {
39
+ default: {
40
+ SubscribeCustomMessages: ({}) => {
41
+ const emitter = new EventEmitter();
42
+
43
+ emitter.cancel = () => {};
44
+
45
+ process.nextTick(() => emitter.emit('end'));
46
+ process.nextTick(() => emitter.emit('status'));
47
+ process.nextTick(() => emitter.emit('error', 'err'));
48
+
49
+ return emitter;
50
+ },
51
+ },
52
+ },
53
+ },
54
+ description: 'Errors are returned',
55
+ error: 'err',
56
+ },
57
+ {
58
+ args: {
59
+ lnd: {
60
+ default: {
61
+ SubscribeCustomMessages: ({}) => {
62
+ const emitter = new EventEmitter();
63
+
64
+ emitter.cancel = () => {};
65
+
66
+ process.nextTick(() => emitter.emit('error', {
67
+ details: 'Cancelled on client',
68
+ }));
69
+
70
+ return emitter;
71
+ },
72
+ },
73
+ },
74
+ },
75
+ description: 'Errors are returned',
76
+ error: {details: 'Cancelled on client'},
77
+ },
78
+ {
79
+ args: {lnd: makeLnd({peer: null})},
80
+ description: 'A peer is expected',
81
+ error: [503, 'ExpectedPeerPublicKeyBytesToDerivePeerMessage'],
82
+ },
83
+ {
84
+ args: {lnd: makeLnd({})},
85
+ description: 'Peer message event emitted',
86
+ expected: {
87
+ message: '00',
88
+ public_key: Buffer.alloc(33, 3).toString('hex'),
89
+ type: 44444,
90
+ },
91
+ },
92
+ ];
93
+
94
+ tests.forEach(({args, description, error, expected}) => {
95
+ return test(description, ({end, equal, strictSame, throws}) => {
96
+ try {
97
+ subscribeToPeerMessages({});
98
+ } catch (err) {
99
+ strictSame(
100
+ err,
101
+ new Error('ExpectedLndToSubscribeToPeerMessages'), 'Needs lnd');
102
+ }
103
+
104
+ const sub = subscribeToPeerMessages(args);
105
+
106
+ if (!!error) {
107
+ sub.once('error', err => {
108
+ strictSame(err, error, 'Got expected error');
109
+
110
+ subscribeToPeerMessages(args);
111
+
112
+ process.nextTick(() => {
113
+ sub.removeAllListeners();
114
+
115
+ return end();
116
+ });
117
+ });
118
+ } else {
119
+ if (!expected) {
120
+ return end();
121
+ }
122
+
123
+ sub.once('message_received', message => {
124
+ strictSame(message, expected, 'Got message received');
125
+
126
+ return end();
127
+ });
128
+ }
129
+
130
+ return;
131
+ });
132
+ });
@@ -0,0 +1,67 @@
1
+ const {test} = require('@alexbosworth/tap');
2
+
3
+ const {rpcPeerMessageAsMessage} = require('./../../lnd_responses');
4
+
5
+ const makeArgs = overrides => {
6
+ const response = {
7
+ data: Buffer.alloc(1),
8
+ peer: Buffer.alloc(33, 3),
9
+ type: 44444,
10
+ };
11
+
12
+ Object.keys(overrides || {}).forEach(key => response[key] = overrides[key]);
13
+
14
+ return response;
15
+ };
16
+
17
+
18
+ const makeExpected = overrides => {
19
+ const expected = {
20
+ message: '00',
21
+ public_key: '030303030303030303030303030303030303030303030303030303030303030303',
22
+ type: 44444,
23
+ };
24
+
25
+ Object.keys(overrides || {}).forEach(key => expected[key] = overrides[key]);
26
+
27
+ return expected;
28
+ };
29
+
30
+ const tests = [
31
+ {
32
+ description: 'RPC peer message is expected',
33
+ error: 'ExpectedRpcMessageToDerivePeerMessage',
34
+ },
35
+ {
36
+ args: makeArgs({data: undefined}),
37
+ description: 'data is expected',
38
+ error: 'ExpectedPeerMessageDataToDerivePeerMessage',
39
+ },
40
+ {
41
+ args: makeArgs({peer: undefined}),
42
+ description: 'peer public key is expected',
43
+ error: 'ExpectedPeerPublicKeyBytesToDerivePeerMessage',
44
+ },
45
+ {
46
+ args: makeArgs({type: undefined}),
47
+ description: 'type is expected',
48
+ error: 'ExpectedCustomMessageTypeNumberToDeriveMessage',
49
+ },
50
+ {
51
+ args: makeArgs({}),
52
+ description: 'RPC peer message is mapped to message',
53
+ expected: makeExpected({}),
54
+ },
55
+ ];
56
+
57
+ tests.forEach(({args, description, error, expected}) => {
58
+ return test(description, ({end, strictSame, throws}) => {
59
+ if (!!error) {
60
+ throws(() => rpcPeerMessageAsMessage(args), new Error(error), 'Got err');
61
+ } else {
62
+ strictSame(rpcPeerMessageAsMessage(args), expected, 'Mapped to message');
63
+ }
64
+
65
+ return end();
66
+ });
67
+ });