lightning 10.0.0 → 10.1.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,10 @@
1
1
  # Versions
2
2
 
3
- ## 10.0.0
3
+ ## 10.1.0
4
+
5
+ - `getBlockHeader`: Add method to get the header portion of a block
6
+
7
+ ## 10.0.1
4
8
 
5
9
  - Add support for LND 0.17.0-beta
6
10
 
package/README.md CHANGED
@@ -143,6 +143,8 @@ variables set:
143
143
  - [getBackups](https://github.com/alexbosworth/ln-service#getbackups): Get recovery details for
144
144
  all channels.
145
145
  - [getBlock](https://github.com/alexbosworth/ln-service#getblock): Get a block
146
+ - [getBlockHeader](https://github.com/alexbosworth/ln-service#getblockheader):
147
+ Get a block header
146
148
  - [getChainAddresses](https://github.com/alexbosworth/ln-service#getchainaddresses):
147
149
  Get a list of created chain addresses
148
150
  - [getChainBalance](https://github.com/alexbosworth/ln-service#getchainbalance): Get the amount
@@ -12,6 +12,11 @@ service ChainKit {
12
12
  */
13
13
  rpc GetBlock (GetBlockRequest) returns (GetBlockResponse);
14
14
 
15
+ /* lncli: `chain getblockheader`
16
+ GetBlockHeader returns a block header with a particular block hash.
17
+ */
18
+ rpc GetBlockHeader (GetBlockHeaderRequest) returns (GetBlockHeaderResponse);
19
+
15
20
  /* lncli: `chain getbestblock`
16
21
  GetBestBlock returns the block hash and current height from the valid
17
22
  most-work chain.
@@ -37,6 +42,16 @@ message GetBlockResponse {
37
42
  bytes raw_block = 1;
38
43
  }
39
44
 
45
+ message GetBlockHeaderRequest {
46
+ // The hash of the block with the requested header.
47
+ bytes block_hash = 1;
48
+ }
49
+
50
+ message GetBlockHeaderResponse {
51
+ // The header of the block with the requested hash.
52
+ bytes raw_block_header = 1;
53
+ }
54
+
40
55
  message GetBestBlockRequest {
41
56
  }
42
57
 
package/index.js CHANGED
@@ -32,6 +32,7 @@ const {getAutopilot} = require('./lnd_methods');
32
32
  const {getBackup} = require('./lnd_methods');
33
33
  const {getBackups} = require('./lnd_methods');
34
34
  const {getBlock} = require('./lnd_methods');
35
+ const {getBlockHeader} = require('./lnd_methods');
35
36
  const {getChainAddresses} = require('./lnd_methods');
36
37
  const {getChainBalance} = require('./lnd_methods');
37
38
  const {getChainFeeEstimate} = require('./lnd_methods');
@@ -183,6 +184,7 @@ module.exports = {
183
184
  getBackup,
184
185
  getBackups,
185
186
  getBlock,
187
+ getBlockHeader,
186
188
  getChainAddresses,
187
189
  getChainBalance,
188
190
  getChainFeeEstimate,
@@ -31,6 +31,7 @@ const {getAutopilot} = require('./info');
31
31
  const {getBackup} = require('./offchain');
32
32
  const {getBackups} = require('./offchain');
33
33
  const {getBlock} = require('./onchain');
34
+ const {getBlockHeader} = require('./onchain');
34
35
  const {getChainAddresses} = require('./onchain');
35
36
  const {getChainBalance} = require('./onchain');
36
37
  const {getChainFeeEstimate} = require('./onchain');
@@ -181,6 +182,7 @@ module.exports = {
181
182
  getBackup,
182
183
  getBackups,
183
184
  getBlock,
185
+ getBlockHeader,
184
186
  getChainAddresses,
185
187
  getChainBalance,
186
188
  getChainFeeEstimate,
@@ -125,6 +125,10 @@
125
125
  "methods": ["GetBestBlock", "GetBlock", "GetBlockHash"],
126
126
  "type": "blocks"
127
127
  },
128
+ "getBlockHeader": {
129
+ "methods": ["GetBestBlock", "GetBlockHash", "GetBlockHeader"],
130
+ "type": "blocks"
131
+ },
128
132
  "getChainAddresses": {
129
133
  "method": "ListAddresses",
130
134
  "type": "wallet"
@@ -25,7 +25,7 @@ export type GetBlockResult = {
25
25
  /**
26
26
  * Get a block in the chain
27
27
  *
28
- * This method requires LND built with `chainkit` build tag
28
+ * This method requires LND built with `chainrpc` build tag
29
29
  *
30
30
  * Requires `onchain:read` permission
31
31
  *
@@ -15,7 +15,7 @@ const type = 'blocks';
15
15
 
16
16
  /** Get a block in the chain
17
17
 
18
- This method requires LND built with `chainkit` build tag
18
+ This method requires LND built with `chainrpc` build tag
19
19
 
20
20
  Requires `onchain:read` permission
21
21
 
@@ -0,0 +1,37 @@
1
+ import type {MergeExclusive} from 'type-fest';
2
+ import {
3
+ AuthenticatedLightningArgs,
4
+ AuthenticatedLightningMethod,
5
+ } from '../../typescript';
6
+
7
+ export type GetBlockArgs = AuthenticatedLightningArgs<
8
+ MergeExclusive<
9
+ {
10
+ /** Block Height Number */
11
+ height: number;
12
+ },
13
+ {
14
+ /** Block Hash Hex String */
15
+ id: string;
16
+ }
17
+ >
18
+ >;
19
+
20
+ export type GetBlockHeaderResult = {
21
+ /** Raw Block Header Bytes Hex String */
22
+ header: string;
23
+ };
24
+
25
+ /**
26
+ * Get a block header in the chain
27
+ *
28
+ * This method requires LND built with `chainrpc` build tag
29
+ *
30
+ * Requires `onchain:read` permission
31
+ *
32
+ * This method is not supported on LND 0.17.0 and below
33
+ */
34
+ export const getBlockHeader: AuthenticatedLightningMethod<
35
+ GetBlockArgs,
36
+ GetBlockHeaderResult
37
+ >;
@@ -0,0 +1,149 @@
1
+ const asyncAuto = require('async/auto');
2
+ const {returnResult} = require('asyncjs-util');
3
+
4
+ const {isLnd} = require('./../../lnd_requests');
5
+
6
+ const bufferAsHex = buffer => buffer.toString('hex');
7
+ const errorNotFound = '-5: Block not found';
8
+ const errorUnknownMethod = 'unknown method GetBlockHeader for service chainrpc.ChainKit';
9
+ const hexAsReversedBuffer = hex => Buffer.from(hex, 'hex').reverse();
10
+ const {isBuffer} = Buffer;
11
+ const isNumber = n => !isNaN(n);
12
+ const isHash = n => /^[0-9A-F]{64}$/i.test(n);
13
+ const method = 'getBlockHeader';
14
+ const type = 'blocks';
15
+
16
+ /** Get a block header in the best chain
17
+
18
+ This method requires LND built with `chainrpc` build tag
19
+
20
+ Requires `onchain:read` permission
21
+
22
+ This method is not supported on LND 0.17.0 and below
23
+
24
+ {
25
+ [height]: <Block Height Number>
26
+ [id]: <Block Hash Hex String>
27
+ lnd: <Authenticated LND API Object>
28
+ }
29
+
30
+ @returns via cbk or Promise
31
+ {
32
+ header: <Raw Block Header Bytes Hex String>
33
+ }
34
+ */
35
+ module.exports = ({height, id, lnd}, cbk) => {
36
+ return new Promise((resolve, reject) => {
37
+ return asyncAuto({
38
+ // Check arguments
39
+ validate: cbk => {
40
+ if (height !== undefined && !isNumber(height)) {
41
+ return cbk([400, 'ExpectedNumericBlockHeightOfHeaderToRetrieve']);
42
+ }
43
+
44
+ if (!!id && !isHash(id)) {
45
+ return cbk([400, 'ExpectedIdentifyingBlockHashOfHeaderToRetrieve']);
46
+ }
47
+
48
+ if (height !== undefined && !!id) {
49
+ return cbk([400, 'ExpectedEitherHeightOrIdNotBothForHeaderFetch']);
50
+ }
51
+
52
+ if (!isLnd({lnd, method, type})) {
53
+ return cbk([400, 'ExpectedAuthenticatedLndToRetrieveBlockHeader']);
54
+ }
55
+
56
+ return cbk();
57
+ },
58
+
59
+ // Get the best block in the chain to find the default block hash
60
+ getTip: ['validate', ({}, cbk) => {
61
+ // Exit early when a specific block is requested
62
+ if (height !== undefined || !!id) {
63
+ return cbk();
64
+ }
65
+
66
+ return lnd[type].getBestBlock({}, (err, res) => {
67
+ if (!!err && err.details === errorUnknownMethod) {
68
+ return cbk([501, 'GetBestBlockMethodNotSupported']);
69
+ }
70
+
71
+ if (!!err) {
72
+ return cbk([503, 'UnexpectedErrorGettingBestBlock', {err}]);
73
+ }
74
+
75
+ if (!res) {
76
+ return cbk([503, 'ExpectedResponseForBestBlockRequest']);
77
+ }
78
+
79
+ if (!isBuffer(res.block_hash)) {
80
+ return cbk([503, 'ExpectedChainTipInfoInGetBestBlockResponse']);
81
+ }
82
+
83
+ return cbk(null, res.block_hash);
84
+ });
85
+ }],
86
+
87
+ // Get the hash of the block to fetch
88
+ getHash: ['getTip', ({getTip}, cbk) => {
89
+ // Exit early when the hash to get is specified
90
+ if (!!id) {
91
+ return cbk(null, hexAsReversedBuffer(id));
92
+ }
93
+
94
+ // Exit early when the hash comes from the chain tip
95
+ if (!!getTip) {
96
+ return cbk(null, getTip);
97
+ }
98
+
99
+ return lnd[type].getBlockHash({block_height: height}, (err, res) => {
100
+ if (!!err && err.details === errorUnknownMethod) {
101
+ return cbk([501, 'GetBlockHashMethodNotSupported']);
102
+ }
103
+
104
+ if (!!err) {
105
+ return cbk([503, 'UnexpectedErrorGettingBlockHash', {err}]);
106
+ }
107
+
108
+ if (!res) {
109
+ return cbk([503, 'ExpectedResponseForGetBlockHashRequest']);
110
+ }
111
+
112
+ if (!isBuffer(res.block_hash)) {
113
+ return cbk([503, 'ExpectedBlockHashInGetBlockHashResponse']);
114
+ }
115
+
116
+ return cbk(null, res.block_hash);
117
+ });
118
+ }],
119
+
120
+ // Get the block header
121
+ getBlockHeader: ['getHash', ({getHash}, cbk) => {
122
+ return lnd[type][method]({block_hash: getHash}, (err, res) => {
123
+ if (!!err && err.details === errorNotFound) {
124
+ return cbk([404, 'BlockForHeaderNotFound']);
125
+ }
126
+
127
+ if (!!err && err.details === errorUnknownMethod) {
128
+ return cbk([501, 'GetBlockHeaderMethodNotSupported']);
129
+ }
130
+
131
+ if (!!err) {
132
+ return cbk([503, 'UnexpectedErrorWhenGettingBlockHeader', {err}]);
133
+ }
134
+
135
+ if (!res) {
136
+ return cbk([503, 'ExpectedResponseForChainBlockHeaderRequest']);
137
+ }
138
+
139
+ if (!isBuffer(res.raw_block_header)) {
140
+ return cbk([503, 'ExpectedRawHeaderInChainBlockHeaderResponse']);
141
+ }
142
+
143
+ return cbk(null, {header: bufferAsHex(res.raw_block_header)});
144
+ });
145
+ }],
146
+ },
147
+ returnResult({reject, resolve, of: 'getBlockHeader'}, cbk));
148
+ });
149
+ };
@@ -4,6 +4,7 @@ export * from './close_channel';
4
4
  export * from './fund_pending_channels';
5
5
  export * from './fund_psbt';
6
6
  export * from './get_block';
7
+ export * from './get_block_header';
7
8
  export * from './get_chain_addresses';
8
9
  export * from './get_chain_balance';
9
10
  export * from './get_chain_fee_estimate';
@@ -4,6 +4,7 @@ const closeChannel = require('./close_channel');
4
4
  const fundPendingChannels = require('./fund_pending_channels');
5
5
  const fundPsbt = require('./fund_psbt');
6
6
  const getBlock = require('./get_block');
7
+ const getBlockHeader = require('./get_block_header');
7
8
  const getChainAddresses = require('./get_chain_addresses');
8
9
  const getChainBalance = require('./get_chain_balance');
9
10
  const getChainFeeEstimate = require('./get_chain_fee_estimate');
@@ -42,6 +43,7 @@ module.exports = {
42
43
  fundPendingChannels,
43
44
  fundPsbt,
44
45
  getBlock,
46
+ getBlockHeader,
45
47
  getChainAddresses,
46
48
  getChainBalance,
47
49
  getChainFeeEstimate,
package/package.json CHANGED
@@ -7,22 +7,22 @@
7
7
  "url": "https://github.com/alexbosworth/lightning/issues"
8
8
  },
9
9
  "dependencies": {
10
- "@grpc/grpc-js": "1.9.5",
10
+ "@grpc/grpc-js": "1.9.9",
11
11
  "@grpc/proto-loader": "0.7.10",
12
- "@types/node": "20.8.2",
13
- "@types/request": "2.48.9",
14
- "@types/ws": "8.5.6",
12
+ "@types/node": "20.8.10",
13
+ "@types/request": "2.48.11",
14
+ "@types/ws": "8.5.8",
15
15
  "async": "3.2.4",
16
16
  "asyncjs-util": "1.2.12",
17
17
  "bitcoinjs-lib": "6.1.5",
18
18
  "bn.js": "5.2.1",
19
19
  "bolt07": "1.8.4",
20
- "bolt09": "1.0.0",
20
+ "bolt09": "2.0.0",
21
21
  "ecpair": "2.1.0",
22
22
  "invoices": "3.0.0",
23
23
  "psbt": "3.0.0",
24
24
  "tiny-secp256k1": "2.2.3",
25
- "type-fest": "4.3.3"
25
+ "type-fest": "4.6.0"
26
26
  },
27
27
  "description": "Lightning Network client library",
28
28
  "devDependencies": {
@@ -53,5 +53,5 @@
53
53
  "directory": "test/typescript"
54
54
  },
55
55
  "types": "index.d.ts",
56
- "version": "10.0.0"
56
+ "version": "10.1.0"
57
57
  }
@@ -0,0 +1,243 @@
1
+ const {deepStrictEqual} = require('node:assert').strict;
2
+ const {rejects} = require('node:assert').strict;
3
+ const test = require('node:test');
4
+
5
+ const {getBlockHeader} = require('./../../../lnd_methods');
6
+
7
+ const tests = [
8
+ {
9
+ args: {height: 'invalid'},
10
+ description: 'A valid height is expected to get a block header',
11
+ error: [400, 'ExpectedNumericBlockHeightOfHeaderToRetrieve'],
12
+ },
13
+ {
14
+ args: {id: 'invalid'},
15
+ description: 'A valid id is expected to get a block header',
16
+ error: [400, 'ExpectedIdentifyingBlockHashOfHeaderToRetrieve'],
17
+ },
18
+ {
19
+ args: {height: 1, id: Buffer.alloc(32).toString('hex')},
20
+ description: 'Only a height or an id is required',
21
+ error: [400, 'ExpectedEitherHeightOrIdNotBothForHeaderFetch'],
22
+ },
23
+ {
24
+ args: {id: Buffer.alloc(32).toString('hex')},
25
+ description: 'An lnd object is required to get a block header',
26
+ error: [400, 'ExpectedAuthenticatedLndToRetrieveBlockHeader'],
27
+ },
28
+ {
29
+ args: {
30
+ lnd: {
31
+ blocks: {
32
+ getBestBlock: ({}, cbk) => cbk({
33
+ details: 'unknown method GetBlockHeader for service chainrpc.ChainKit',
34
+ }),
35
+ getBlockHeader: ({}, cbk) => cbk('err'),
36
+ },
37
+ },
38
+ },
39
+ description: 'An unsupported error is returned',
40
+ error: [501, 'GetBestBlockMethodNotSupported'],
41
+ },
42
+ {
43
+ args: {
44
+ lnd: {
45
+ blocks: {
46
+ getBestBlock: ({}, cbk) => cbk('err'),
47
+ getBlockHeader: ({}, cbk) => cbk('getBlockErr'),
48
+ },
49
+ },
50
+ },
51
+ description: 'An error is returned',
52
+ error: [503, 'UnexpectedErrorGettingBestBlock', {err: 'err'}],
53
+ },
54
+ {
55
+ args: {
56
+ lnd: {
57
+ blocks: {
58
+ getBestBlock: ({}, cbk) => cbk(),
59
+ getBlockHeader: ({}, cbk) => cbk(),
60
+ },
61
+ },
62
+ },
63
+ description: 'An error is returned when there is no best block result',
64
+ error: [503, 'ExpectedResponseForBestBlockRequest'],
65
+ },
66
+ {
67
+ args: {
68
+ lnd: {
69
+ blocks: {
70
+ getBestBlock: ({}, cbk) => cbk(null, {}),
71
+ getBlockHeader: ({}, cbk) => cbk(),
72
+ },
73
+ },
74
+ },
75
+ description: 'An error is returned when there is no result info',
76
+ error: [503, 'ExpectedChainTipInfoInGetBestBlockResponse'],
77
+ },
78
+ {
79
+ args: {
80
+ height: 1,
81
+ lnd: {
82
+ blocks: {
83
+ getBlockHash: ({}, cbk) => cbk({
84
+ details: 'unknown method GetBlockHeader for service chainrpc.ChainKit',
85
+ }),
86
+ getBlockHeader: ({}, cbk) => cbk(),
87
+ },
88
+ },
89
+ },
90
+ description: 'An error is returned when get hash for height unsupported',
91
+ error: [501, 'GetBlockHashMethodNotSupported'],
92
+ },
93
+ {
94
+ args: {
95
+ height: 1,
96
+ lnd: {
97
+ blocks: {
98
+ getBlockHash: ({}, cbk) => cbk('err'),
99
+ getBlockHeader: ({}, cbk) => cbk(),
100
+ },
101
+ },
102
+ },
103
+ description: 'An unexpected error for getting block hash is returned',
104
+ error: [503, 'UnexpectedErrorGettingBlockHash', {err: 'err'}],
105
+ },
106
+ {
107
+ args: {
108
+ height: 1,
109
+ lnd: {
110
+ blocks: {
111
+ getBlockHash: ({}, cbk) => cbk(),
112
+ getBlockHeader: ({}, cbk) => cbk(),
113
+ },
114
+ },
115
+ },
116
+ description: 'A result is expected for getting a block hash',
117
+ error: [503, 'ExpectedResponseForGetBlockHashRequest'],
118
+ },
119
+ {
120
+ args: {
121
+ height: 1,
122
+ lnd: {
123
+ blocks: {
124
+ getBlockHash: ({}, cbk) => cbk(null, {}),
125
+ getBlockHeader: ({}, cbk) => cbk(),
126
+ },
127
+ },
128
+ },
129
+ description: 'A result hash is expected for getting a block hash',
130
+ error: [503, 'ExpectedBlockHashInGetBlockHashResponse'],
131
+ },
132
+ {
133
+ args: {
134
+ id: Buffer.alloc(32).toString('hex'),
135
+ lnd: {
136
+ blocks: {
137
+ getBlockHeader: ({}, cbk) => cbk({details: '-5: Block not found'}),
138
+ },
139
+ },
140
+ },
141
+ description: 'An error is returned when a block is not found',
142
+ error: [404, 'BlockForHeaderNotFound'],
143
+ },
144
+ {
145
+ args: {
146
+ id: Buffer.alloc(32).toString('hex'),
147
+ lnd: {
148
+ blocks: {
149
+ getBlockHeader: ({}, cbk) => cbk({
150
+ details: 'unknown method GetBlockHeader for service chainrpc.ChainKit',
151
+ }),
152
+ },
153
+ },
154
+ },
155
+ description: 'An error is returned when the method is not supported',
156
+ error: [501, 'GetBlockHeaderMethodNotSupported'],
157
+ },
158
+ {
159
+ args: {
160
+ id: Buffer.alloc(32).toString('hex'),
161
+ lnd: {blocks: {getBlockHeader: ({}, cbk) => cbk('err')}},
162
+ },
163
+ description: 'An error is returned',
164
+ error: [503, 'UnexpectedErrorWhenGettingBlockHeader', {err: 'err'}],
165
+ },
166
+ {
167
+ args: {
168
+ id: Buffer.alloc(32).toString('hex'),
169
+ lnd: {blocks: {getBlockHeader: ({}, cbk) => cbk()}},
170
+ },
171
+ description: 'A result is expected',
172
+ error: [503, 'ExpectedResponseForChainBlockHeaderRequest'],
173
+ },
174
+ {
175
+ args: {
176
+ id: Buffer.alloc(32).toString('hex'),
177
+ lnd: {blocks: {getBlockHeader: ({}, cbk) => cbk(null, {})}},
178
+ },
179
+ description: 'A resulting block header is expected',
180
+ error: [503, 'ExpectedRawHeaderInChainBlockHeaderResponse'],
181
+ },
182
+ {
183
+ args: {
184
+ lnd: {
185
+ blocks: {
186
+ getBestBlock: ({}, cbk) => cbk(null, {
187
+ block_hash: Buffer.alloc(1),
188
+ }),
189
+ getBlockHeader: ({}, cbk) => cbk(null, {
190
+ raw_block_header: Buffer.alloc(1),
191
+ }),
192
+ },
193
+ },
194
+ },
195
+ description: 'The chain tip block header is returned',
196
+ expected: {header: '00'},
197
+ },
198
+ {
199
+ args: {
200
+ height: 1,
201
+ lnd: {
202
+ blocks: {
203
+ getBlockHash: ({}, cbk) => cbk(null, {
204
+ block_hash: Buffer.alloc(1),
205
+ }),
206
+ getBlockHeader: ({}, cbk) => cbk(null, {
207
+ raw_block_header: Buffer.alloc(1),
208
+ }),
209
+ },
210
+ },
211
+ },
212
+ description: 'A block header at a height is returned',
213
+ expected: {header: '00'},
214
+ },
215
+ {
216
+ args: {
217
+ id: Buffer.alloc(32).toString('hex'),
218
+ lnd: {
219
+ blocks: {
220
+ getBlockHeader: ({}, cbk) => cbk(null, {
221
+ raw_block_header: Buffer.alloc(1),
222
+ }),
223
+ },
224
+ },
225
+ },
226
+ description: 'A block header is returned',
227
+ expected: {header: '00'},
228
+ },
229
+ ];
230
+
231
+ tests.forEach(({args, description, error, expected}) => {
232
+ return test(description, async () => {
233
+ if (!!error) {
234
+ await rejects(() => getBlockHeader(args), error, 'Got expected error');
235
+ } else {
236
+ const res = await getBlockHeader(args);
237
+
238
+ deepStrictEqual(res, expected, 'Got expected result');
239
+ }
240
+
241
+ return;
242
+ });
243
+ });