lightning 10.1.3 → 10.2.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 CHANGED
@@ -1,6 +1,11 @@
1
1
  # Versions
2
2
 
3
- ## 10.1.3
3
+ ## 10.2.0
4
+
5
+ - `deleteChainTransaction`: Add method to delete a chain transaction
6
+ - `getChainTransaction`: Add method to get a chain transaction
7
+
8
+ ## 10.1.4
4
9
 
5
10
  - `getBlockHeader`: Add method to get the header portion of a block
6
11
 
package/README.md CHANGED
@@ -110,6 +110,8 @@ variables set:
110
110
  - [createWallet](https://github.com/alexbosworth/ln-service#createwallet): Make a new wallet.
111
111
  - [decodePaymentRequest](https://github.com/alexbosworth/ln-service#decodepaymentrequest):
112
112
  Get parsed details for a payment request.
113
+ - [deleteChainTransaction](https://github.com/alexbosworth/ln-service#deletechaintransaction)
114
+ Delete a broadcast chain transaction
113
115
  - [deleteFailedPayAttempts](https://github.com/alexbosworth/ln-service#deletefailedpayattempts)
114
116
  Remove failed payment paths from database.
115
117
  - [deleteFailedPayments](https://github.com/alexbosworth/ln-service#deletefailedpayments)
@@ -153,6 +155,8 @@ variables set:
153
155
  Estimate a chain fee to send funds to an address.
154
156
  - [getChainFeeRate](https://github.com/alexbosworth/ln-service#getchainfeerate): Get an estimate
155
157
  for an on-chain fee rate.
158
+ - [getChainTransaction](https://github.com/alexbosworth/ln-service#getchaintransaction):
159
+ Get a wallet on-chain transaction.
156
160
  - [getChainTransactions](https://github.com/alexbosworth/ln-service#getchaintransactions): List
157
161
  past on-chain transactions.
158
162
  - [getChannel](https://github.com/alexbosworth/ln-service#getchannel): Lookup network graph
@@ -7,6 +7,24 @@ package walletrpc;
7
7
 
8
8
  option go_package = "github.com/lightningnetwork/lnd/lnrpc/walletrpc";
9
9
 
10
+ /*
11
+ * Comments in this file will be directly parsed into the API
12
+ * Documentation as descriptions of the associated method, message, or field.
13
+ * These descriptions should go right above the definition of the object, and
14
+ * can be in either block or // comment format.
15
+ *
16
+ * An RPC method can be matched to an lncli command by placing a line in the
17
+ * beginning of the description in exactly the following format:
18
+ * lncli: `methodname`
19
+ *
20
+ * Failure to specify the exact name of the command will cause documentation
21
+ * generation to fail.
22
+ *
23
+ * More information on how exactly the gRPC documentation is generated from
24
+ * this proto file can be found here:
25
+ * https://github.com/lightninglabs/lightning-api
26
+ */
27
+
10
28
  // WalletKit is a service that gives access to the core functionalities of the
11
29
  // daemon's wallet.
12
30
  service WalletKit {
@@ -18,7 +36,7 @@ service WalletKit {
18
36
  */
19
37
  rpc ListUnspent (ListUnspentRequest) returns (ListUnspentResponse);
20
38
 
21
- /*
39
+ /* lncli: `wallet leaseoutput`
22
40
  LeaseOutput locks an output to the given ID, preventing it from being
23
41
  available for any future coin selection attempts. The absolute time of the
24
42
  lock's expiration is returned. The expiration of the lock can be extended by
@@ -27,14 +45,14 @@ service WalletKit {
27
45
  */
28
46
  rpc LeaseOutput (LeaseOutputRequest) returns (LeaseOutputResponse);
29
47
 
30
- /*
48
+ /* lncli: `wallet releaseoutput`
31
49
  ReleaseOutput unlocks an output, allowing it to be available for coin
32
50
  selection if it remains unspent. The ID should match the one used to
33
51
  originally lock the output.
34
52
  */
35
53
  rpc ReleaseOutput (ReleaseOutputRequest) returns (ReleaseOutputResponse);
36
54
 
37
- /*
55
+ /* lncli: `wallet listleases`
38
56
  ListLeases lists all currently locked utxos.
39
57
  */
40
58
  rpc ListLeases (ListLeasesRequest) returns (ListLeasesResponse);
@@ -57,14 +75,19 @@ service WalletKit {
57
75
  */
58
76
  rpc NextAddr (AddrRequest) returns (AddrResponse);
59
77
 
60
- /*
78
+ /* lncli: `wallet gettx`
79
+ GetTransaction returns details for a transaction found in the wallet.
80
+ */
81
+ rpc GetTransaction (GetTransactionRequest) returns (lnrpc.Transaction);
82
+
83
+ /* lncli: `wallet accounts list`
61
84
  ListAccounts retrieves all accounts belonging to the wallet by default. A
62
85
  name and key scope filter can be provided to filter through all of the
63
86
  wallet accounts and return only those matching.
64
87
  */
65
88
  rpc ListAccounts (ListAccountsRequest) returns (ListAccountsResponse);
66
89
 
67
- /*
90
+ /* lncli: `wallet requiredreserve`
68
91
  RequiredReserve returns the minimum amount of satoshis that should be kept
69
92
  in the wallet in order to fee bump anchor channels if necessary. The value
70
93
  scales with the number of public anchor channels but is capped at a maximum.
@@ -72,14 +95,14 @@ service WalletKit {
72
95
  rpc RequiredReserve (RequiredReserveRequest)
73
96
  returns (RequiredReserveResponse);
74
97
 
75
- /*
98
+ /* lncli: `wallet addresses list`
76
99
  ListAddresses retrieves all the addresses along with their balance. An
77
100
  account name filter can be provided to filter through all of the
78
101
  wallet accounts and return the addresses of only those matching.
79
102
  */
80
103
  rpc ListAddresses (ListAddressesRequest) returns (ListAddressesResponse);
81
104
 
82
- /*
105
+ /* lncli: `wallet addresses signmessage`
83
106
  SignMessageWithAddr returns the compact signature (base64 encoded) created
84
107
  with the private key of the provided address. This requires the address
85
108
  to be solely based on a public key lock (no scripts). Obviously the internal
@@ -96,7 +119,7 @@ service WalletKit {
96
119
  rpc SignMessageWithAddr (SignMessageWithAddrRequest)
97
120
  returns (SignMessageWithAddrResponse);
98
121
 
99
- /*
122
+ /* lncli: `wallet addresses verifymessage`
100
123
  VerifyMessageWithAddr returns the validity and the recovered public key of
101
124
  the provided compact signature (base64 encoded). The verification is
102
125
  twofold. First the validity of the signature itself is checked and then
@@ -120,7 +143,7 @@ service WalletKit {
120
143
  rpc VerifyMessageWithAddr (VerifyMessageWithAddrRequest)
121
144
  returns (VerifyMessageWithAddrResponse);
122
145
 
123
- /*
146
+ /* lncli: `wallet accounts import`
124
147
  ImportAccount imports an account backed by an account extended public key.
125
148
  The master key fingerprint denotes the fingerprint of the root key
126
149
  corresponding to the account public key (also known as the key with
@@ -147,7 +170,7 @@ service WalletKit {
147
170
  */
148
171
  rpc ImportAccount (ImportAccountRequest) returns (ImportAccountResponse);
149
172
 
150
- /*
173
+ /* lncli: `wallet accounts import-pubkey`
151
174
  ImportPublicKey imports a public key as watch-only into the wallet. The
152
175
  public key is converted into a simple address of the given type and that
153
176
  address script is watched on chain. For Taproot keys, this will only watch
@@ -177,7 +200,7 @@ service WalletKit {
177
200
  rpc ImportTapscript (ImportTapscriptRequest)
178
201
  returns (ImportTapscriptResponse);
179
202
 
180
- /*
203
+ /* lncli: `wallet publishtx`
181
204
  PublishTransaction attempts to publish the passed transaction to the
182
205
  network. Once this returns without an error, the wallet will continually
183
206
  attempt to re-broadcast the transaction on start up, until it enters the
@@ -185,6 +208,13 @@ service WalletKit {
185
208
  */
186
209
  rpc PublishTransaction (Transaction) returns (PublishResponse);
187
210
 
211
+ /* lncli: `wallet removetx`
212
+ RemoveTransaction attempts to remove the provided transaction from the
213
+ internal transaction store of the wallet.
214
+ */
215
+ rpc RemoveTransaction (GetTransactionRequest)
216
+ returns (RemoveTransactionResponse);
217
+
188
218
  /*
189
219
  SendOutputs is similar to the existing sendmany call in Bitcoind, and
190
220
  allows the caller to create a transaction that sends to several outputs at
@@ -199,7 +229,7 @@ service WalletKit {
199
229
  */
200
230
  rpc EstimateFee (EstimateFeeRequest) returns (EstimateFeeResponse);
201
231
 
202
- /*
232
+ /* lncli: `pendingsweeps`
203
233
  PendingSweeps returns lists of on-chain outputs that lnd is currently
204
234
  attempting to sweep within its central batching engine. Outputs with similar
205
235
  fee rates are batched together in order to sweep them within a single
@@ -211,7 +241,7 @@ service WalletKit {
211
241
  */
212
242
  rpc PendingSweeps (PendingSweepsRequest) returns (PendingSweepsResponse);
213
243
 
214
- /*
244
+ /* lncli: `wallet bumpfee`
215
245
  BumpFee bumps the fee of an arbitrary input within a transaction. This RPC
216
246
  takes a different approach than bitcoind's bumpfee command. lnd has a
217
247
  central batching engine in which inputs with similar fee rates are batched
@@ -240,14 +270,14 @@ service WalletKit {
240
270
  */
241
271
  rpc BumpFee (BumpFeeRequest) returns (BumpFeeResponse);
242
272
 
243
- /*
273
+ /* lncli: `wallet listsweeps`
244
274
  ListSweeps returns a list of the sweep transactions our node has produced.
245
275
  Note that these sweeps may not be confirmed yet, as we record sweeps on
246
276
  broadcast, not confirmation.
247
277
  */
248
278
  rpc ListSweeps (ListSweepsRequest) returns (ListSweepsResponse);
249
279
 
250
- /*
280
+ /* lncli: `wallet labeltx`
251
281
  LabelTransaction adds a label to a transaction. If the transaction already
252
282
  has a label the call will fail unless the overwrite bool is set. This will
253
283
  overwrite the exiting transaction label. Labels must not be empty, and
@@ -256,7 +286,7 @@ service WalletKit {
256
286
  rpc LabelTransaction (LabelTransactionRequest)
257
287
  returns (LabelTransactionResponse);
258
288
 
259
- /*
289
+ /* lncli: `wallet psbt fund`
260
290
  FundPsbt creates a fully populated PSBT that contains enough inputs to fund
261
291
  the outputs specified in the template. There are two ways of specifying a
262
292
  template: Either by passing in a PSBT with at least one output declared or
@@ -293,7 +323,7 @@ service WalletKit {
293
323
  */
294
324
  rpc SignPsbt (SignPsbtRequest) returns (SignPsbtResponse);
295
325
 
296
- /*
326
+ /* lncli: `wallet psbt finalize`
297
327
  FinalizePsbt expects a partial transaction with all inputs and outputs fully
298
328
  declared and tries to sign all inputs that belong to the wallet. Lnd must be
299
329
  the last signer of the transaction. That means, if there are any unsigned
@@ -538,6 +568,11 @@ message ListAddressesResponse {
538
568
  repeated AccountWithAddresses account_with_addresses = 1;
539
569
  }
540
570
 
571
+ message GetTransactionRequest {
572
+ // The txid of the transaction.
573
+ string txid = 1;
574
+ }
575
+
541
576
  message SignMessageWithAddrRequest {
542
577
  // The message to be signed. When using REST, this field must be encoded as
543
578
  // base64.
@@ -723,6 +758,7 @@ message Transaction {
723
758
  */
724
759
  string label = 2;
725
760
  }
761
+
726
762
  message PublishResponse {
727
763
  /*
728
764
  If blank, then no error occurred and the transaction was successfully
@@ -734,6 +770,11 @@ message PublishResponse {
734
770
  string publish_error = 1;
735
771
  }
736
772
 
773
+ message RemoveTransactionResponse {
774
+ // The status of the remove transaction operation.
775
+ string status = 1;
776
+ }
777
+
737
778
  message SendOutputsRequest {
738
779
  /*
739
780
  The number of satoshis per kilo weight that should be used when crafting
package/index.js CHANGED
@@ -14,6 +14,7 @@ const {createInvoice} = require('./lnd_methods');
14
14
  const {createSeed} = require('./lnd_methods');
15
15
  const {createWallet} = require('./lnd_methods');
16
16
  const {decodePaymentRequest} = require('./lnd_methods');
17
+ const {deleteChainTransaction} = require('./lnd_methods');
17
18
  const {deleteFailedPayAttempts} = require('./lnd_methods');
18
19
  const {deleteFailedPayments} = require('./lnd_methods');
19
20
  const {deleteForwardingReputations} = require('./lnd_methods');
@@ -37,6 +38,7 @@ const {getChainAddresses} = require('./lnd_methods');
37
38
  const {getChainBalance} = require('./lnd_methods');
38
39
  const {getChainFeeEstimate} = require('./lnd_methods');
39
40
  const {getChainFeeRate} = require('./lnd_methods');
41
+ const {getChainTransaction} = require('./lnd_methods');
40
42
  const {getChainTransactions} = require('./lnd_methods');
41
43
  const {getChannel} = require('./lnd_methods');
42
44
  const {getChannelBalance} = require('./lnd_methods');
@@ -166,6 +168,7 @@ module.exports = {
166
168
  createSeed,
167
169
  createWallet,
168
170
  decodePaymentRequest,
171
+ deleteChainTransaction,
169
172
  deleteFailedPayAttempts,
170
173
  deleteFailedPayments,
171
174
  deleteForwardingReputations,
@@ -189,6 +192,7 @@ module.exports = {
189
192
  getChainBalance,
190
193
  getChainFeeEstimate,
191
194
  getChainFeeRate,
195
+ getChainTransaction,
192
196
  getChainTransactions,
193
197
  getChannel,
194
198
  getChannelBalance,
@@ -11,6 +11,7 @@ const decBase = 10;
11
11
  const defaultOdds = 950000;
12
12
  const fullConfidence = 1e6;
13
13
  const {isArray} = Array;
14
+ const notFoundIndex = -1;
14
15
  const oddsDenominator = BigInt(1e6);
15
16
  const unimplemented = 'QueryProbabilityNotImplemented';
16
17
 
@@ -43,7 +44,7 @@ module.exports = ({from, hops, lnd}, cbk) => {
43
44
  return cbk([400, 'ExpectedArrayOfHopsToCalculateRoutingOdds']);
44
45
  }
45
46
 
46
- if (!!hops.find(n => !n.channel || !n.public_key)) {
47
+ if (hops.findIndex(n => !n.public_key) !== notFoundIndex) {
47
48
  return cbk([400, 'ExpectedHopsWithEdges']);
48
49
  }
49
50
 
@@ -13,6 +13,7 @@ const {createInvoice} = require('./invoices');
13
13
  const {createSeed} = require('./unauthenticated');
14
14
  const {createWallet} = require('./unauthenticated');
15
15
  const {decodePaymentRequest} = require('./offchain');
16
+ const {deleteChainTransaction} = require('./onchain');
16
17
  const {deleteFailedPayAttempts} = require('./offchain');
17
18
  const {deleteFailedPayments} = require('./offchain');
18
19
  const {deleteForwardingReputations} = require('./offchain');
@@ -36,6 +37,7 @@ const {getChainAddresses} = require('./onchain');
36
37
  const {getChainBalance} = require('./onchain');
37
38
  const {getChainFeeEstimate} = require('./onchain');
38
39
  const {getChainFeeRate} = require('./onchain');
40
+ const {getChainTransaction} = require('./onchain');
39
41
  const {getChainTransactions} = require('./onchain');
40
42
  const {getChannel} = require('./info');
41
43
  const {getChannelBalance} = require('./offchain');
@@ -164,6 +166,7 @@ module.exports = {
164
166
  createSeed,
165
167
  createWallet,
166
168
  decodePaymentRequest,
169
+ deleteChainTransaction,
167
170
  deleteFailedPayAttempts,
168
171
  deleteFailedPayments,
169
172
  deleteForwardingReputations,
@@ -187,6 +190,7 @@ module.exports = {
187
190
  getChainBalance,
188
191
  getChainFeeEstimate,
189
192
  getChainFeeRate,
193
+ getChainTransaction,
190
194
  getChainTransactions,
191
195
  getChannel,
192
196
  getChannelBalance,
@@ -19,6 +19,7 @@
19
19
  "prefAttachType": "preferential",
20
20
  "versions": {
21
21
  "0d5b0fefa4d9082f7964836f5e58c3a6bda8e471": "0.10.2-beta",
22
+ "13aa7f99248c7ee63989d3b62e0cbfe86d7b0964": "0.17.3-beta",
22
23
  "15c1eb13972f86a7c1e8cb084aa6d52700d685ff": "0.14.5-beta",
23
24
  "1a3194d302f33bb52823297d9d7f75cd37516053": "0.10.0-beta",
24
25
  "1e511be523eb8e97c4e2d9c89a7a263963a3929f": "0.14.2-beta",
@@ -52,6 +52,10 @@
52
52
  "method": "DecodePayReq",
53
53
  "type": "default"
54
54
  },
55
+ "deleteChainTransaction": {
56
+ "method": "RemoveTransaction",
57
+ "type": "wallet"
58
+ },
55
59
  "deleteFailedPayAttempts": {
56
60
  "depends_on": ["deletePayment"],
57
61
  "method": "DeleteAllPayments",
@@ -145,6 +149,10 @@
145
149
  "method": "EstimateFee",
146
150
  "type": "wallet"
147
151
  },
152
+ "getChainTransaction": {
153
+ "method": "GetTransaction",
154
+ "type": "wallet"
155
+ },
148
156
  "getChainTransactions": {
149
157
  "method": "GetTransactions",
150
158
  "type": "default"
@@ -0,0 +1,66 @@
1
+ const asyncAuto = require('async/auto');
2
+ const {returnResult} = require('asyncjs-util');
3
+
4
+ const {isLnd} = require('./../../lnd_requests');
5
+
6
+ const isHash = n => !!n && /^[0-9A-F]{64}$/i.test(n);
7
+ const method = 'removeTransaction';
8
+ const notSupported = /unknown.*walletrpc.WalletKit/;
9
+ const statusSuccess = 'Successfully removed transaction';
10
+ const type = 'wallet';
11
+
12
+ /** Remove a chain transaction.
13
+
14
+ Requires `onchain:write` permission
15
+
16
+ This method is not supported on LND 0.17.3 and below
17
+
18
+ {
19
+ id: <Transaction Id Hex String>
20
+ lnd: <Authenticated LND API Object>
21
+ }
22
+
23
+ @returns via cbk or Promise
24
+ */
25
+ module.exports = ({id, lnd}, cbk) => {
26
+ return new Promise((resolve, reject) => {
27
+ return asyncAuto({
28
+ // Check arguments
29
+ validate: cbk => {
30
+ if (!isHash(id)) {
31
+ return cbk([400, 'ExpectedIdentifyingTxHashOfChainTxToDelete']);
32
+ }
33
+
34
+ if (!isLnd({lnd, method, type})) {
35
+ return cbk([400, 'ExpectedLndToDeleteChainTransaction']);
36
+ }
37
+
38
+ return cbk();
39
+ },
40
+
41
+ // Delete the transaction
42
+ deleteTransaction: ['validate', ({}, cbk) => {
43
+ return lnd[type][method]({txid: id}, (err, res) => {
44
+ if (!!err && notSupported.test(err.details)) {
45
+ return cbk([501, 'RemoveChainTransactionMethodNotSupported']);
46
+ }
47
+
48
+ if (!!err) {
49
+ return cbk([503, 'UnexpectedDeleteChainTransactionError', {err}]);
50
+ }
51
+
52
+ if (!res) {
53
+ return cbk([503, 'ExpectedResponseDeletingChainTransaction']);
54
+ }
55
+
56
+ if (res.status !== statusSuccess) {
57
+ return cbk([503, 'UnexpectedResponseDeletingChainTransaction']);
58
+ }
59
+
60
+ return cbk();
61
+ });
62
+ }],
63
+ },
64
+ returnResult({reject, resolve}, cbk));
65
+ });
66
+ };
@@ -0,0 +1,81 @@
1
+ const asyncAuto = require('async/auto');
2
+ const {returnResult} = require('asyncjs-util');
3
+
4
+ const {isLnd} = require('./../../lnd_requests');
5
+ const {rpcTxAsTransaction} = require('./../../lnd_responses');
6
+
7
+ const isHash = n => !!n && /^[0-9A-F]{64}$/i.test(n);
8
+ const method = 'getTransaction';
9
+ const notSupported = /unknown.*walletrpc.WalletKit/;
10
+ const type = 'wallet';
11
+
12
+ /** Get a chain transaction.
13
+
14
+ Requires `onchain:read` permission
15
+
16
+ This method is not supported on LND 0.17.3 and below
17
+
18
+ {
19
+ id: <Transaction Id Hex String>
20
+ lnd: <Authenticated LND API Object>
21
+ }
22
+
23
+ @returns via cbk or Promise
24
+ {
25
+ [block_id]: <Block Hash String>
26
+ [confirmation_count]: <Confirmation Count Number>
27
+ [confirmation_height]: <Confirmation Block Height Number>
28
+ created_at: <Created ISO 8601 Date String>
29
+ [description]: <Transaction Label String>
30
+ [fee]: <Fees Paid Tokens Number>
31
+ id: <Transaction Id String>
32
+ inputs: [{
33
+ is_local: <Spent Outpoint is Local Bool>
34
+ transaction_id: <Transaction Id Hex String>
35
+ transaction_vout: <Transaction Output Index Number>
36
+ }]
37
+ is_confirmed: <Is Confirmed Bool>
38
+ is_outgoing: <Transaction Outbound Bool>
39
+ output_addresses: [<Address String>]
40
+ tokens: <Tokens Including Fee Number>
41
+ [transaction]: <Raw Transaction Hex String>
42
+ }
43
+ */
44
+ module.exports = ({id, lnd}, cbk) => {
45
+ return new Promise((resolve, reject) => {
46
+ return asyncAuto({
47
+ // Check arguments
48
+ validate: cbk => {
49
+ if (!isHash(id)) {
50
+ return cbk([400, 'ExpectedIdentifyingTxHashOfChainTxToRetrieve']);
51
+ }
52
+
53
+ if (!isLnd({lnd, method, type})) {
54
+ return cbk([400, 'ExpectedLndToGetChainTransaction']);
55
+ }
56
+
57
+ return cbk();
58
+ },
59
+
60
+ // Get transaction
61
+ getTransaction: ['validate', ({}, cbk) => {
62
+ return lnd[type][method]({txid: id}, (err, res) => {
63
+ if (!!err && notSupported.test(err.details)) {
64
+ return cbk([501, 'GetChainTransactionMethodNotSupported']);
65
+ }
66
+
67
+ if (!!err) {
68
+ return cbk([503, 'UnexpectedGetChainTransactionError', {err}]);
69
+ }
70
+
71
+ try {
72
+ return cbk(null, rpcTxAsTransaction(res));
73
+ } catch (err) {
74
+ return cbk([503, err.message]);
75
+ }
76
+ });
77
+ }],
78
+ },
79
+ returnResult({reject, resolve, of: 'getTransaction'}, cbk));
80
+ });
81
+ };
@@ -1,6 +1,7 @@
1
1
  const broadcastChainTransaction = require('./broadcast_chain_transaction');
2
2
  const cancelPendingChannel = require('./cancel_pending_channel');
3
3
  const closeChannel = require('./close_channel');
4
+ const deleteChainTransaction = require('./delete_chain_transaction');
4
5
  const fundPendingChannels = require('./fund_pending_channels');
5
6
  const fundPsbt = require('./fund_psbt');
6
7
  const getBlock = require('./get_block');
@@ -9,6 +10,7 @@ const getChainAddresses = require('./get_chain_addresses');
9
10
  const getChainBalance = require('./get_chain_balance');
10
11
  const getChainFeeEstimate = require('./get_chain_fee_estimate');
11
12
  const getChainFeeRate = require('./get_chain_fee_rate');
13
+ const getChainTransaction = require('./get_chain_transaction');
12
14
  const getChainTransactions = require('./get_chain_transactions');
13
15
  const getLockedUtxos = require('./get_locked_utxos');
14
16
  const getMasterPublicKeys = require('./get_master_public_keys');
@@ -40,6 +42,7 @@ module.exports = {
40
42
  broadcastChainTransaction,
41
43
  cancelPendingChannel,
42
44
  closeChannel,
45
+ deleteChainTransaction,
43
46
  fundPendingChannels,
44
47
  fundPsbt,
45
48
  getBlock,
@@ -48,6 +51,7 @@ module.exports = {
48
51
  getChainBalance,
49
52
  getChainFeeEstimate,
50
53
  getChainFeeRate,
54
+ getChainTransaction,
51
55
  getChainTransactions,
52
56
  getLockedUtxos,
53
57
  getMasterPublicKeys,
package/package.json CHANGED
@@ -7,11 +7,11 @@
7
7
  "url": "https://github.com/alexbosworth/lightning/issues"
8
8
  },
9
9
  "dependencies": {
10
- "@grpc/grpc-js": "1.9.11",
10
+ "@grpc/grpc-js": "1.9.13",
11
11
  "@grpc/proto-loader": "0.7.10",
12
- "@types/node": "20.9.2",
12
+ "@types/node": "20.10.4",
13
13
  "@types/request": "2.48.12",
14
- "@types/ws": "8.5.9",
14
+ "@types/ws": "8.5.10",
15
15
  "async": "3.2.5",
16
16
  "asyncjs-util": "1.2.12",
17
17
  "bitcoinjs-lib": "6.1.5",
@@ -22,12 +22,12 @@
22
22
  "invoices": "3.0.0",
23
23
  "psbt": "3.0.0",
24
24
  "tiny-secp256k1": "2.2.3",
25
- "type-fest": "4.8.1"
25
+ "type-fest": "4.8.3"
26
26
  },
27
27
  "description": "Lightning Network client library",
28
28
  "devDependencies": {
29
29
  "tsd": "0.29.0",
30
- "typescript": "5.3.2"
30
+ "typescript": "5.3.3"
31
31
  },
32
32
  "engines": {
33
33
  "node": ">=18"
@@ -53,5 +53,5 @@
53
53
  "directory": "test/typescript"
54
54
  },
55
55
  "types": "index.d.ts",
56
- "version": "10.1.3"
56
+ "version": "10.2.0"
57
57
  }
@@ -0,0 +1,143 @@
1
+ const {deepStrictEqual} = require('node:assert').strict;
2
+ const EventEmitter = require('events');
3
+ const {rejects} = require('node:assert').strict;
4
+ const test = require('node:test');
5
+
6
+ const {getInfoResponse} = require('./../fixtures');
7
+ const {getRouteConfidence} = require('./../../../');
8
+
9
+ const makeLnd = ({err, res}) => {
10
+ return {
11
+ default: {
12
+ getInfo: ({}, cbk) => cbk(err, getInfoResponse),
13
+ },
14
+ router: {
15
+ queryMissionControl: ({}, cbk) => {
16
+ if (!!err) {
17
+ return cbk(err);
18
+ }
19
+
20
+ if (res !== undefined) {
21
+ return cbk(null, res);
22
+ }
23
+
24
+ return cbk(null, {
25
+ pairs: [{
26
+ history: {
27
+ fail_amt_msat: '1000',
28
+ fail_amt_sat: '1',
29
+ fail_time: '1',
30
+ success_amt_msat: '1000',
31
+ success_amt_sat: '1',
32
+ success_time: '1',
33
+ },
34
+ node_from: Buffer.alloc(33, 3),
35
+ node_to: Buffer.alloc(33, 2),
36
+ }],
37
+ });
38
+ },
39
+ queryProbability: ({}, cbk) => {
40
+ if (!!err) {
41
+ return cbk(err);
42
+ }
43
+
44
+ if (res !== undefined) {
45
+ return cbk(null, res);
46
+ }
47
+
48
+ return cbk(null, {history: {}, probability: 0.5});
49
+ },
50
+ },
51
+ wallet: {
52
+ deriveKey: ({}, cbk) => cbk('err'),
53
+ },
54
+ };
55
+ };
56
+
57
+ const tests = [
58
+ {
59
+ args: {},
60
+ description: 'Hops are required',
61
+ error: [400, 'ExpectedArrayOfHopsToCalculateRoutingOdds'],
62
+ },
63
+ {
64
+ args: {hops: [{}]},
65
+ description: 'Valid hops are required',
66
+ error: [400, 'ExpectedHopsWithEdges'],
67
+ },
68
+ {
69
+ args: {
70
+ from: Buffer.alloc(33, 1).toString('hex'),
71
+ hops: [{
72
+ forward_mtokens: '1',
73
+ public_key: Buffer.alloc(33, 2).toString('hex'),
74
+ }],
75
+ lnd: {
76
+ default: {
77
+ getInfo: ({}, cbk) => cbk(null, getInfoResponse),
78
+ },
79
+ router: {
80
+ queryMissionControl: ({}, cbk) => {
81
+ return cbk(null, {
82
+ pairs: [{
83
+ history: {
84
+ fail_amt_msat: '1000',
85
+ fail_amt_sat: '1',
86
+ fail_time: '1',
87
+ success_amt_msat: '1000',
88
+ success_amt_sat: '1',
89
+ success_time: '1',
90
+ },
91
+ node_from: Buffer.alloc(33, 3),
92
+ node_to: Buffer.alloc(33, 2),
93
+ }],
94
+ });
95
+ },
96
+ queryProbability: ({}, cbk) => cbk('err'),
97
+ },
98
+ wallet: {
99
+ deriveKey: ({}, cbk) => cbk('err'),
100
+ },
101
+ },
102
+ },
103
+ description: 'Get route confidence when confidence query returns err',
104
+ error: [503, 'UnexpectedErrorFromQueryProbability', {err: 'err'}],
105
+ },
106
+ {
107
+ args: {
108
+ from: Buffer.alloc(33, 1).toString('hex'),
109
+ hops: [{
110
+ forward_mtokens: '1',
111
+ public_key: Buffer.alloc(33, 2).toString('hex'),
112
+ }],
113
+ lnd: makeLnd({}),
114
+ },
115
+ description: 'Get route confidence from non self',
116
+ expected: {confidence: 950000},
117
+ },
118
+ {
119
+ args: {
120
+ hops: [{
121
+ forward_mtokens: '1',
122
+ public_key: Buffer.alloc(33, 2).toString('hex'),
123
+ }],
124
+ lnd: makeLnd({}),
125
+ },
126
+ description: 'Get route confidence',
127
+ expected: {confidence: 950000},
128
+ },
129
+ ];
130
+
131
+ tests.forEach(({args, description, error, expected}) => {
132
+ return test(description, async () => {
133
+ if (!!error) {
134
+ await rejects(getRouteConfidence(args), error, 'Got expected error');
135
+ } else {
136
+ const got = await getRouteConfidence(args);
137
+
138
+ deepStrictEqual(got, expected, 'Got expected result');
139
+ }
140
+
141
+ return;
142
+ });
143
+ });
@@ -0,0 +1,276 @@
1
+ const {deepStrictEqual} = require('node:assert').strict;
2
+ const EventEmitter = require('node:events');
3
+ const {rejects} = require('node:assert').strict;
4
+ const test = require('node:test');
5
+
6
+ const {isDestinationPayable} = require('./../../../');
7
+
8
+ const makePaymentData = overrides => {
9
+ const data = {
10
+ creation_date: '1',
11
+ creation_time_ns: '0',
12
+ failure_reason: 'FAILURE_REASON_TIMEOUT',
13
+ fee_msat: '1000',
14
+ fee_sat: '1',
15
+ htlcs: [{
16
+ attempt_time_ns: '1000000',
17
+ failure: {
18
+ channel_update: {
19
+ base_fee: '1000',
20
+ chain_hash: Buffer.alloc(32),
21
+ chan_id: '1',
22
+ channel_flags: 1,
23
+ extra_opaque_data: Buffer.alloc(1),
24
+ fee_rate: 1,
25
+ htlc_maximum_msat: '1000',
26
+ htlc_minimum_msat: '1000',
27
+ message_flags: 1,
28
+ signature: Buffer.alloc(71),
29
+ time_lock_delta: 1,
30
+ timestamp: 1,
31
+ },
32
+ code: 'UNREADABLE_FAILURE',
33
+ failure_source_index1: 1,
34
+ height: 1,
35
+ htlc_msat: '1000',
36
+ },
37
+ resolve_time_ns: '1000000',
38
+ route: {
39
+ hops: [{
40
+ amt_to_forward_msat: '1000',
41
+ chan_id: '1',
42
+ chan_capacity: 1,
43
+ expiry: 1,
44
+ fee_msat: '1000',
45
+ mpp_record: {
46
+ payment_addr: Buffer.alloc(32),
47
+ total_amt_msat: '1000',
48
+ },
49
+ pub_key: Buffer.alloc(33).toString('hex'),
50
+ tlv_payload: true,
51
+ }],
52
+ total_amt: '1',
53
+ total_amt_msat: '1000',
54
+ total_fees: '1',
55
+ total_fees_msat: '1000',
56
+ total_time_lock: 1,
57
+ },
58
+ status: 'FAILED',
59
+ }],
60
+ path: [Buffer.alloc(33).toString('hex'), Buffer.alloc(33).toString('hex')],
61
+ payment_hash: Buffer.alloc(32).toString('hex'),
62
+ payment_index: '1',
63
+ payment_preimage: Buffer.alloc(32).toString('hex'),
64
+ payment_request: 'lntb1500n1pdn4czkpp5ugdqer05qrrxuchrzkcue94th9w2xzasp9qm7d0yxcgp4uh4kn4qdpa2fjkzep6yprkcmmzv9kzqsmj09c8gmmrw4e8yetwvdujq5n9va6kcct5d9hkucqzysdlghdpua7uvjjkcfj49psxtlqzkp5pdncffdfk2cp3mp76thrl29qhqgzufm503pjj96586n5w6edgw3n66j4rxxs707y4zdjuhyt6qqe5weu4',
65
+ status: 'FAILED',
66
+ value: '1',
67
+ value_msat: '1000',
68
+ value_sat: '1',
69
+ };
70
+
71
+ Object.keys(overrides).forEach(k => data[k] = overrides[k]);
72
+
73
+ return data;
74
+ };
75
+
76
+ const makeLnd = args => {
77
+ return {
78
+ chain: {
79
+ registerBlockEpochNtfn: ({}) => {
80
+ const emitter = new EventEmitter();
81
+
82
+ emitter.cancel = () => {};
83
+
84
+ process.nextTick(() => emitter.emit('error', 'err'));
85
+
86
+ return emitter;
87
+ },
88
+ },
89
+ default: {
90
+ getInfo: ({}, cbk) => {
91
+ return cbk(null, {
92
+ alias: 'alias',
93
+ best_header_timestamp: 1,
94
+ block_hash: '00',
95
+ block_height: 1,
96
+ chains: [{chain: 'chain', network: 'network'}],
97
+ color: '#000000',
98
+ features: {'1': {is_known: true, is_required: false}},
99
+ identity_pubkey: Buffer.alloc(33).toString('hex'),
100
+ num_active_channels: 1,
101
+ num_peers: 1,
102
+ num_pending_channels: 1,
103
+ synced_to_chain: true,
104
+ uris: [],
105
+ version: 'version',
106
+ });
107
+ },
108
+ },
109
+ router: {
110
+ sendPaymentV2: ({}) => {
111
+ const data = args.data || makePaymentData({});
112
+ const emitter = new EventEmitter();
113
+
114
+ if (!!args.is_end) {
115
+ process.nextTick(() => emitter.emit('end'));
116
+ } else if (!!args.err) {
117
+ process.nextTick(() => emitter.emit('error', args.err));
118
+ } else {
119
+ process.nextTick(() => emitter.emit('data', data));
120
+ }
121
+
122
+ return emitter;
123
+ },
124
+ },
125
+ };
126
+ };
127
+
128
+ const makeArgs = overrides => {
129
+ const args = {
130
+ cltv_delta: 1,
131
+ destination: Buffer.alloc(33).toString('hex'),
132
+ features: [{bit: 1}],
133
+ id: Buffer.alloc(32).toString('hex'),
134
+ incoming_peer: Buffer.alloc(33).toString('hex'),
135
+ lnd: makeLnd({}),
136
+ max_fee: 1,
137
+ max_fee_mtokens: '1000',
138
+ messages: [{
139
+ type: '1',
140
+ value: Buffer.alloc(1).toString('hex'),
141
+ }],
142
+ mtokens: '1000',
143
+ outgoing_channel: '0x0x1',
144
+ pathfinding_timeout: 1,
145
+ routes: [[{
146
+ base_fee_mtokens: '1000',
147
+ channel: '0x0x1',
148
+ cltv_delta: 1,
149
+ fee_rate: 1,
150
+ public_key: Buffer.alloc(33).toString('hex'),
151
+ }]],
152
+ tokens: 1,
153
+ };
154
+
155
+ Object.keys(overrides).forEach(k => args[k] = overrides[k]);
156
+
157
+ return args;
158
+ };
159
+
160
+ const makeExpectedPayment = ({}) => {
161
+ return {
162
+ failed: undefined,
163
+ is_confirmed: true,
164
+ is_failed: false,
165
+ is_pending: false,
166
+ payment: {
167
+ fee: 0,
168
+ fee_mtokens: '1',
169
+ hops: [{
170
+ channel: '0x0x1',
171
+ channel_capacity: 1,
172
+ fee: 0,
173
+ fee_mtokens: '1',
174
+ forward: 0,
175
+ forward_mtokens: '1',
176
+ public_key: 'b',
177
+ timeout: 1,
178
+ }],
179
+ id: '66687aadf862bd776c8fc18b8e9f8e20089714856ee233b3902a591d0d5f2925',
180
+ paths: [{
181
+ fee: 0,
182
+ fee_mtokens: '1',
183
+ hops: [{
184
+ channel: '0x0x1',
185
+ channel_capacity: 1,
186
+ fee: 0,
187
+ fee_mtokens: '1',
188
+ forward: 0,
189
+ forward_mtokens: '1',
190
+ public_key: 'b',
191
+ timeout: 1
192
+ }],
193
+ mtokens: '1',
194
+ safe_fee: 1,
195
+ safe_tokens: 1,
196
+ timeout: 1,
197
+ tokens: 0,
198
+ }],
199
+ mtokens: '1',
200
+ safe_fee: 1,
201
+ safe_tokens: 1,
202
+ secret: Buffer.alloc(32).toString('hex'),
203
+ timeout: 1,
204
+ tokens: 0,
205
+ },
206
+ };
207
+ };
208
+
209
+ const tests = [
210
+ {
211
+ args: makeArgs({destination: undefined}),
212
+ description: 'The payment destination is required',
213
+ error: [400, 'ExpectedDestinationToCheckPayability'],
214
+ },
215
+ {
216
+ args: makeArgs({lnd: undefined}),
217
+ description: 'LND is required',
218
+ error: [400, 'ExpectedAuthenticatedLndWithRouterToTestPayable'],
219
+ },
220
+ {
221
+ args: makeArgs({
222
+ lnd: {
223
+ chain: {
224
+ registerBlockEpochNtfn: ({}) => {
225
+ const emitter = new EventEmitter();
226
+
227
+ emitter.cancel = () => {};
228
+
229
+ process.nextTick(() => emitter.emit('error', 'err'));
230
+
231
+ return emitter;
232
+ },
233
+ },
234
+ default: {
235
+ getInfo: ({}, cbk) => {
236
+ return cbk('err');
237
+ },
238
+ },
239
+ router: {
240
+ sendPaymentV2: ({}) => {
241
+ const emitter = new EventEmitter();
242
+
243
+ process.nextTick(() => emitter.emit('error', 'err'));
244
+
245
+ return emitter;
246
+ },
247
+ },
248
+ },
249
+ }),
250
+ description: 'A payment attempt times out',
251
+ error: [
252
+ 503,
253
+ 'UnexpectedErrorCheckingPayability',
254
+ {err: [503, 'UnexpectedPaymentError', {err: 'err'}]},
255
+ ],
256
+ },
257
+ {
258
+ args: makeArgs({}),
259
+ description: 'A payment attempt times out',
260
+ expected: {is_payable: false},
261
+ },
262
+ ];
263
+
264
+ tests.forEach(({args, description, error, expected}) => {
265
+ return test(description, async () => {
266
+ if (!!error) {
267
+ await rejects(() => isDestinationPayable(args), error, 'Got error');
268
+ } else {
269
+ const payment = await isDestinationPayable(args);
270
+
271
+ deepStrictEqual(payment, expected, 'Got expected payment');
272
+ }
273
+
274
+ return;
275
+ });
276
+ });
@@ -0,0 +1,85 @@
1
+ const {deepEqual} = require('node:assert').strict;
2
+ const {rejects} = require('node:assert').strict;
3
+ const test = require('node:test');
4
+
5
+ const {deleteChainTransaction} = require('./../../../lnd_methods');
6
+
7
+ const makeLnd = () => {
8
+ return {
9
+ wallet: {
10
+ removeTransaction: ({}, cbk) => {
11
+ return cbk(null, {status: 'Successfully removed transaction'});
12
+ },
13
+ },
14
+ };
15
+ };
16
+
17
+ const tests = [
18
+ {
19
+ args: {},
20
+ description: 'An id is required to delete a chain transaction',
21
+ error: [400, 'ExpectedIdentifyingTxHashOfChainTxToDelete'],
22
+ },
23
+ {
24
+ args: {id: Buffer.alloc(32).toString('hex')},
25
+ description: 'LND Object is required to delete a chain transaction',
26
+ error: [400, 'ExpectedLndToDeleteChainTransaction'],
27
+ },
28
+ {
29
+ args: {
30
+ id: Buffer.alloc(32).toString('hex'),
31
+ lnd: {wallet: {removeTransaction: ({}, cbk) => cbk('err')}},
32
+ },
33
+ description: 'Errors are passed back from remove transaction method',
34
+ error: [503, 'UnexpectedDeleteChainTransactionError', {err: 'err'}],
35
+ },
36
+ {
37
+ args: {
38
+ id: Buffer.alloc(32).toString('hex'),
39
+ lnd: {
40
+ wallet: {
41
+ removeTransaction: ({}, cbk) => cbk({
42
+ details: 'unknown walletrpc.WalletKit',
43
+ }),
44
+ },
45
+ },
46
+ },
47
+ description: 'Unsupported errors are returned',
48
+ error: [501, 'RemoveChainTransactionMethodNotSupported'],
49
+ },
50
+ {
51
+ args: {
52
+ id: Buffer.alloc(32).toString('hex'),
53
+ lnd: {wallet: {removeTransaction: ({}, cbk) => cbk()}},
54
+ },
55
+ description: 'A response is expected from remove transaction',
56
+ error: [503, 'ExpectedResponseDeletingChainTransaction'],
57
+ },
58
+ {
59
+ args: {
60
+ id: Buffer.alloc(32).toString('hex'),
61
+ lnd: {wallet: {removeTransaction: ({}, cbk) => cbk(null, {})}},
62
+ },
63
+ description: 'A success response is expected from remove transaction',
64
+ error: [503, 'UnexpectedResponseDeletingChainTransaction'],
65
+ },
66
+ {
67
+ args: {
68
+ id: Buffer.alloc(32).toString('hex'),
69
+ lnd: makeLnd({}),
70
+ },
71
+ description: 'Remove transaction returns successfully',
72
+ },
73
+ ];
74
+
75
+ tests.forEach(({args, description, error, expected}) => {
76
+ return test(description, async () => {
77
+ if (!!error) {
78
+ await rejects(() => deleteChainTransaction(args), error, 'Got err');
79
+ } else {
80
+ await deleteChainTransaction(args);
81
+ }
82
+
83
+ return;
84
+ });
85
+ });
@@ -0,0 +1,124 @@
1
+ const {deepStrictEqual} = require('node:assert').strict;
2
+ const {rejects} = require('node:assert').strict;
3
+ const test = require('node:test');
4
+
5
+ const {getChainTransaction} = require('./../../../lnd_methods');
6
+
7
+ const makeExpected = overrides => {
8
+ const transaction = {
9
+ block_id: Buffer.alloc(32).toString('hex'),
10
+ confirmation_count: 1,
11
+ confirmation_height: 1,
12
+ created_at: '1970-01-01T00:00:01.000Z',
13
+ description: undefined,
14
+ fee: 1,
15
+ id: Buffer.alloc(32).toString('hex'),
16
+ inputs: [],
17
+ is_confirmed: true,
18
+ is_outgoing: false,
19
+ output_addresses: ['address'],
20
+ tokens: 1,
21
+ transaction: undefined,
22
+ };
23
+
24
+ Object.keys(overrides).forEach(k => transaction[k] = overrides[k]);
25
+
26
+ return transaction;
27
+ };
28
+
29
+ const makeLnd = overrides => {
30
+ return {
31
+ wallet: {
32
+ getTransaction: ({}, cbk) => {
33
+ const transaction = {
34
+ amount: '1',
35
+ block_hash: Buffer.alloc(32).toString('hex'),
36
+ block_height: 1,
37
+ dest_addresses: ['address'],
38
+ num_confirmations: 1,
39
+ previous_outpoints: [],
40
+ time_stamp: '1',
41
+ total_fees: '1',
42
+ tx_hash: Buffer.alloc(32).toString('hex'),
43
+ };
44
+
45
+ Object.keys(overrides).forEach(k => transaction[k] = overrides[k]);
46
+
47
+ return cbk(null, transaction);
48
+ },
49
+ },
50
+ };
51
+ };
52
+
53
+ const tests = [
54
+ {
55
+ args: {},
56
+ description: 'An id is required to get a chain transaction',
57
+ error: [400, 'ExpectedIdentifyingTxHashOfChainTxToRetrieve'],
58
+ },
59
+ {
60
+ args: {id: Buffer.alloc(32).toString('hex')},
61
+ description: 'LND Object is required to get a chain transaction',
62
+ error: [400, 'ExpectedLndToGetChainTransaction'],
63
+ },
64
+ {
65
+ args: {
66
+ id: Buffer.alloc(32).toString('hex'),
67
+ lnd: {wallet: {getTransaction: ({}, cbk) => cbk('err')}},
68
+ },
69
+ description: 'Errors are passed back from get transaction method',
70
+ error: [503, 'UnexpectedGetChainTransactionError', {err: 'err'}],
71
+ },
72
+ {
73
+ args: {
74
+ id: Buffer.alloc(32).toString('hex'),
75
+ lnd: {
76
+ wallet: {
77
+ getTransaction: ({}, cbk) => cbk({
78
+ details: 'unknown walletrpc.WalletKit',
79
+ }),
80
+ },
81
+ },
82
+ },
83
+ description: 'Unsupported errors are returned',
84
+ error: [501, 'GetChainTransactionMethodNotSupported'],
85
+ },
86
+ {
87
+ args: {
88
+ id: Buffer.alloc(32).toString('hex'),
89
+ lnd: {wallet: {getTransaction: ({}, cbk) => cbk()}},
90
+ },
91
+ description: 'A response is expected from get transaction',
92
+ error: [503, 'ExpectedRpcTransactionToDeriveTransactionDetails'],
93
+ },
94
+ {
95
+ args: {
96
+ id: Buffer.alloc(32).toString('hex'),
97
+ lnd: makeLnd({amount: null}),
98
+ },
99
+ description: 'An amount is expected in a chain transaction',
100
+ error: [503, 'ExpectedTransactionAmountInChainTransaction'],
101
+ },
102
+ {
103
+ args: {
104
+ id: Buffer.alloc(32).toString('hex'),
105
+ lnd: makeLnd({}),
106
+ },
107
+ description: 'Get transaction returns normalized transaction',
108
+ expected: makeExpected({}),
109
+ },
110
+ ];
111
+
112
+ tests.forEach(({args, description, error, expected}) => {
113
+ return test(description, async () => {
114
+ if (!!error) {
115
+ await rejects(() => getChainTransaction(args), error, 'Got err');
116
+ } else {
117
+ const transaction = await getChainTransaction(args);
118
+
119
+ deepStrictEqual(transaction, expected, 'Got transaction');
120
+ }
121
+
122
+ return;
123
+ });
124
+ });