balanceofsatoshis 25.4.0 → 25.5.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 +4 -0
- package/README.md +1 -1
- package/bos +2 -0
- package/chain/fund_transaction.js +10 -9
- package/chain/get_block_transactions.js +6 -5
- package/chain/recover_p2pk.js +5 -4
- package/chain/split_utxos.js +5 -4
- package/commands/fetch_request.js +32 -24
- package/display/get_icons.js +33 -22
- package/encryption/cbor_item_head.js +45 -0
- package/encryption/decode_cbor.js +35 -0
- package/encryption/decode_cbor_item.js +169 -0
- package/encryption/decrypt_with_node.js +21 -18
- package/encryption/decrypt_with_secret.js +5 -4
- package/encryption/encode_as_cbor.js +5 -12
- package/encryption/encode_cbor_item.js +90 -0
- package/encryption/index.js +2 -0
- package/lnd/get_socket.js +11 -10
- package/lnurl/auth.js +6 -5
- package/network/get_peers.js +9 -4
- package/network/probe.js +2 -0
- package/network/probe_destination.js +97 -68
- package/network/push_payment.js +42 -17
- package/network/send_gift.js +10 -9
- package/network/transfer_funds.js +6 -6
- package/nodes/register_node.js +34 -30
- package/offchain/create_invoice.js +21 -16
- package/offchain/get_invoice_amount.js +5 -4
- package/offchain/sign_payment_request.js +10 -9
- package/package.json +3 -4
- package/peers/detect_open_rule_violation.js +6 -5
- package/routing/get_chain_fees_chart.js +3 -2
- package/routing/get_fees_paid.js +30 -12
- package/services/initiate_balanced_channel.js +13 -7
- package/swaps/append_failing_edge.js +6 -5
- package/swaps/decode_swap_api_key.js +29 -25
- package/swaps/get_paid_service.js +38 -17
- package/swaps/get_routes_for_funding.js +52 -11
- package/swaps/get_swap_service.js +5 -4
- package/swaps/rebalance.js +78 -63
- package/swaps/swap_in.js +36 -26
- package/tags/get_tags.js +26 -15
- package/telegram/connect_to_telegram.js +17 -12
- package/telegram/get_socks_proxy.js +12 -13
- package/telegram/get_telegram_bot.js +2 -2
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
package/bos
CHANGED
|
@@ -1657,6 +1657,7 @@ prog
|
|
|
1657
1657
|
.option('--avoid <avoid>', 'Avoid forwarding via node/chan/tag', REPEATABLE)
|
|
1658
1658
|
.option('--avoid-high-fee-routes', 'Avoid evaluating too-high fee routes')
|
|
1659
1659
|
.option('--find-max', 'Find the maximum routeable amount on success route')
|
|
1660
|
+
.option('--ignore-expiry', 'Probe even when the request has expired')
|
|
1660
1661
|
.option('--in <public_key>', 'Route through specific peer of destination')
|
|
1661
1662
|
.option('--max-fee <max_fee>', 'Max expected fee for route', INT, 1337)
|
|
1662
1663
|
.option('--max-paths <max>', 'Maximum paths to use for find-max', INT, 1)
|
|
@@ -1679,6 +1680,7 @@ prog
|
|
|
1679
1680
|
find_max: !!options.findMax ? 16777215 : undefined,
|
|
1680
1681
|
fs: {getFile: readFile},
|
|
1681
1682
|
in_through: inThrough.public_key,
|
|
1683
|
+
is_ignoring_expiry: !!options.ignoreExpiry,
|
|
1682
1684
|
is_strict_max_fee: !!options.avoidHighFeeRoutes,
|
|
1683
1685
|
max_fee: options.maxFee,
|
|
1684
1686
|
max_paths: options.maxPaths,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const asyncAuto = require('async/auto');
|
|
2
2
|
const asyncEach = require('async/each');
|
|
3
|
+
const asyncify = require('async/asyncify');
|
|
3
4
|
const {broadcastTransaction} = require('ln-sync');
|
|
4
5
|
const {componentsOfTransaction} = require('@alexbosworth/blockchain');
|
|
5
6
|
const {formatTokens} = require('ln-sync');
|
|
@@ -112,10 +113,10 @@ module.exports = (args, cbk) => {
|
|
|
112
113
|
getNetwork: ['validate', ({}, cbk) => getNetwork({lnd: args.lnd}, cbk)],
|
|
113
114
|
|
|
114
115
|
// Derive a list of outputs to guide input selection
|
|
115
|
-
outputs: ['validate', (
|
|
116
|
+
outputs: ['validate', asyncify(() => {
|
|
116
117
|
// Exit early when the amount is open ended and thus depends on inputs
|
|
117
118
|
if (hasMaxAmount(args.amounts)) {
|
|
118
|
-
return
|
|
119
|
+
return;
|
|
119
120
|
}
|
|
120
121
|
|
|
121
122
|
try {
|
|
@@ -125,11 +126,11 @@ module.exports = (args, cbk) => {
|
|
|
125
126
|
return {address, tokens};
|
|
126
127
|
});
|
|
127
128
|
|
|
128
|
-
return
|
|
129
|
+
return outputs;
|
|
129
130
|
} catch (err) {
|
|
130
|
-
|
|
131
|
+
throw [400, err.message];
|
|
131
132
|
}
|
|
132
|
-
}],
|
|
133
|
+
})],
|
|
133
134
|
|
|
134
135
|
// Get UTXOs to use for input selection and final fee rate calculation
|
|
135
136
|
getUtxos: ['validate', ({}, cbk) => getUtxos({lnd: args.lnd}, cbk)],
|
|
@@ -238,7 +239,7 @@ module.exports = (args, cbk) => {
|
|
|
238
239
|
}],
|
|
239
240
|
|
|
240
241
|
// Parse amounts and put together the final set of outputs
|
|
241
|
-
finalOutputs: ['getMax', ({getMax}
|
|
242
|
+
finalOutputs: ['getMax', asyncify(({getMax}) => {
|
|
242
243
|
try {
|
|
243
244
|
const outputs = args.addresses.map((address, i) => {
|
|
244
245
|
const amount = args.amounts[i];
|
|
@@ -252,11 +253,11 @@ module.exports = (args, cbk) => {
|
|
|
252
253
|
return {address, tokens: parseAmount({amount, variables}).tokens};
|
|
253
254
|
});
|
|
254
255
|
|
|
255
|
-
return
|
|
256
|
+
return outputs;
|
|
256
257
|
} catch (err) {
|
|
257
|
-
|
|
258
|
+
throw [400, err.message];
|
|
258
259
|
}
|
|
259
|
-
}],
|
|
260
|
+
})],
|
|
260
261
|
|
|
261
262
|
// Create a funded PSBT
|
|
262
263
|
fund: [
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const asyncAuto = require('async/auto');
|
|
2
|
+
const asyncify = require('async/asyncify');
|
|
2
3
|
const {getBlock} = require('ln-service');
|
|
3
4
|
const {queryTransactions} = require('@alexbosworth/blockchain');
|
|
4
5
|
const {returnResult} = require('asyncjs-util');
|
|
@@ -77,10 +78,10 @@ module.exports = (args, cbk) => {
|
|
|
77
78
|
}],
|
|
78
79
|
|
|
79
80
|
// Find the transactions in the block
|
|
80
|
-
transactions: ['getBlock', ({getBlock}
|
|
81
|
+
transactions: ['getBlock', asyncify(({getBlock}) => {
|
|
81
82
|
// Exit early when there is no block to look through
|
|
82
83
|
if (!getBlock.block) {
|
|
83
|
-
return
|
|
84
|
+
return {transactions: []};
|
|
84
85
|
}
|
|
85
86
|
|
|
86
87
|
try {
|
|
@@ -89,11 +90,11 @@ module.exports = (args, cbk) => {
|
|
|
89
90
|
ids: args.ids,
|
|
90
91
|
});
|
|
91
92
|
|
|
92
|
-
return
|
|
93
|
+
return {transactions};
|
|
93
94
|
} catch (err) {
|
|
94
|
-
|
|
95
|
+
throw [503, 'UnexpectedErrorParsingBlockTransactions', {err}];
|
|
95
96
|
}
|
|
96
|
-
}],
|
|
97
|
+
})],
|
|
97
98
|
},
|
|
98
99
|
returnResult({reject, resolve, of: 'transactions'}, cbk));
|
|
99
100
|
});
|
package/chain/recover_p2pk.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const {createHash} = require('crypto');
|
|
2
2
|
|
|
3
3
|
const asyncAuto = require('async/auto');
|
|
4
|
+
const asyncify = require('async/asyncify');
|
|
4
5
|
const {broadcastChainTransaction} = require('ln-service');
|
|
5
6
|
const {componentsOfTransaction} = require('@alexbosworth/blockchain');
|
|
6
7
|
const {createChainAddress} = require('ln-service');
|
|
@@ -135,7 +136,7 @@ module.exports = ({id, lnd, request, vout}, cbk) => {
|
|
|
135
136
|
recoveryScript: [
|
|
136
137
|
'createAddress',
|
|
137
138
|
'getNetwork',
|
|
138
|
-
({createAddress, getNetwork}
|
|
139
|
+
asyncify(({createAddress, getNetwork}) =>
|
|
139
140
|
{
|
|
140
141
|
try {
|
|
141
142
|
const {script} = outputScriptForAddress({
|
|
@@ -143,11 +144,11 @@ module.exports = ({id, lnd, request, vout}, cbk) => {
|
|
|
143
144
|
network: getNetwork.network,
|
|
144
145
|
});
|
|
145
146
|
|
|
146
|
-
return
|
|
147
|
+
return {script};
|
|
147
148
|
} catch (err) {
|
|
148
|
-
|
|
149
|
+
throw [503, 'FailedToParseRecoveryAddress', {err}];
|
|
149
150
|
}
|
|
150
|
-
}],
|
|
151
|
+
})],
|
|
151
152
|
|
|
152
153
|
// Derive the sweep output, less the amount needed to pay for chain fees
|
|
153
154
|
sweep: [
|
package/chain/split_utxos.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const asyncAuto = require('async/auto');
|
|
2
2
|
const asyncEach = require('async/each');
|
|
3
|
+
const asyncify = require('async/asyncify');
|
|
3
4
|
const asyncMapSeries = require('async/mapSeries');
|
|
4
5
|
const asyncReflect = require('async/reflect');
|
|
5
6
|
const {broadcastChainTransaction} = require('ln-service');
|
|
@@ -85,7 +86,7 @@ module.exports = (args, cbk) => {
|
|
|
85
86
|
getUtxos: ['validate', ({}, cbk) => getUtxos({lnd: args.lnd}, cbk)],
|
|
86
87
|
|
|
87
88
|
// Select the inputs and the count of outputs that reach the count
|
|
88
|
-
plan: ['feeRate', 'getUtxos', ({feeRate, getUtxos}
|
|
89
|
+
plan: ['feeRate', 'getUtxos', asyncify(({feeRate, getUtxos}) => {
|
|
89
90
|
try {
|
|
90
91
|
const plan = utxoSplitPlan({
|
|
91
92
|
count: args.count,
|
|
@@ -95,11 +96,11 @@ module.exports = (args, cbk) => {
|
|
|
95
96
|
utxos: getUtxos.utxos,
|
|
96
97
|
});
|
|
97
98
|
|
|
98
|
-
return
|
|
99
|
+
return plan;
|
|
99
100
|
} catch (err) {
|
|
100
|
-
|
|
101
|
+
throw [400, err.message];
|
|
101
102
|
}
|
|
102
|
-
}],
|
|
103
|
+
})],
|
|
103
104
|
|
|
104
105
|
// Generate addresses
|
|
105
106
|
getAddresses: ['plan', ({plan}, cbk) => {
|
|
@@ -10,14 +10,12 @@ const timeoutSignals = new WeakMap();
|
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
@returns
|
|
13
|
-
{
|
|
14
|
-
request: <Request Function>
|
|
15
|
-
}
|
|
13
|
+
<Request Function> (args, cbk) => {}
|
|
16
14
|
*/
|
|
17
15
|
module.exports = ({fetch}, cbk) => {
|
|
18
16
|
return (options, cbk) => {
|
|
19
17
|
(async () => {
|
|
20
|
-
|
|
18
|
+
const controller = new AbortController();
|
|
21
19
|
|
|
22
20
|
// Setup the timeout controller
|
|
23
21
|
if (!!options.timeout) {
|
|
@@ -28,30 +26,40 @@ module.exports = ({fetch}, cbk) => {
|
|
|
28
26
|
|
|
29
27
|
const url = options.url + encodeQs(options.qs);
|
|
30
28
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
29
|
+
// Only the request is guarded, so the callback is called once
|
|
30
|
+
const fetched = await (async () => {
|
|
31
|
+
try {
|
|
32
|
+
// Make the request
|
|
33
|
+
const response = await fetch(url, {
|
|
34
|
+
body: options.body || undefined,
|
|
35
|
+
method: options.method || undefined,
|
|
36
|
+
signal: controller.signal,
|
|
37
|
+
});
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
const res = {statusCode: response.status};
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
41
|
+
// Exit early when the response is JSON
|
|
42
|
+
if (!!options.json) {
|
|
43
|
+
return {res, body: await response.json()};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return {res, body: await response.text()};
|
|
47
|
+
} catch (err) {
|
|
48
|
+
return {err};
|
|
49
|
+
} finally {
|
|
50
|
+
// Reset the timeout since the request is over
|
|
51
|
+
if (!!options.timeout) {
|
|
52
|
+
clearTimeout(timeoutSignals.get(controller.signal));
|
|
53
|
+
}
|
|
53
54
|
}
|
|
55
|
+
})();
|
|
56
|
+
|
|
57
|
+
// Exit early when the request failed
|
|
58
|
+
if (!!fetched.err) {
|
|
59
|
+
return cbk(fetched.err);
|
|
54
60
|
}
|
|
61
|
+
|
|
62
|
+
return cbk(null, fetched.res, fetched.body);
|
|
55
63
|
})();
|
|
56
64
|
};
|
|
57
65
|
};
|
package/display/get_icons.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const asyncAuto = require('async/auto');
|
|
2
|
+
const asyncify = require('async/asyncify');
|
|
2
3
|
const {returnResult} = require('asyncjs-util');
|
|
3
4
|
|
|
4
5
|
const {homePath} = require('../storage');
|
|
@@ -38,38 +39,48 @@ module.exports = ({fs}, cbk) => {
|
|
|
38
39
|
return cbk();
|
|
39
40
|
},
|
|
40
41
|
|
|
41
|
-
// Get
|
|
42
|
-
|
|
42
|
+
// Get the tags file
|
|
43
|
+
getFile: ['validate', ({}, cbk) => {
|
|
43
44
|
return fs.getFile(tagFilePath(), (err, res) => {
|
|
44
45
|
// Exit early when there is no tag file
|
|
45
46
|
if (!!err || !res) {
|
|
46
|
-
return cbk(
|
|
47
|
+
return cbk();
|
|
47
48
|
}
|
|
48
49
|
|
|
49
|
-
|
|
50
|
-
|
|
50
|
+
return cbk(null, res.toString());
|
|
51
|
+
});
|
|
52
|
+
}],
|
|
53
|
+
|
|
54
|
+
// Get icons from the tags file
|
|
55
|
+
getIcons: ['getFile', asyncify(({getFile}) => {
|
|
56
|
+
// Exit early when there is no tag file
|
|
57
|
+
if (!getFile) {
|
|
58
|
+
return {nodes: []};
|
|
59
|
+
}
|
|
51
60
|
|
|
52
|
-
|
|
61
|
+
try {
|
|
62
|
+
const file = parse(getFile);
|
|
53
63
|
|
|
54
|
-
|
|
55
|
-
// Only tags this node is included in
|
|
56
|
-
const meta = file.tags.filter(tag => {
|
|
57
|
-
return isArray(tag.nodes) && tag.nodes.includes(key);
|
|
58
|
-
});
|
|
64
|
+
const keys = uniq(flatten(file.tags.map(n => n.nodes)));
|
|
59
65
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
};
|
|
66
|
+
const nodes = keys.map(key => {
|
|
67
|
+
// Only tags this node is included in
|
|
68
|
+
const meta = file.tags.filter(tag => {
|
|
69
|
+
return isArray(tag.nodes) && tag.nodes.includes(key);
|
|
65
70
|
});
|
|
66
71
|
|
|
67
|
-
return
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
72
|
+
return {
|
|
73
|
+
aliases: uniq(meta.map(n => n.alias)),
|
|
74
|
+
icons: uniq(meta.map(n => n.icon)),
|
|
75
|
+
public_key: key,
|
|
76
|
+
};
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
return {nodes};
|
|
80
|
+
} catch (err) {
|
|
81
|
+
return {nodes: []};
|
|
82
|
+
}
|
|
83
|
+
})],
|
|
73
84
|
},
|
|
74
85
|
returnResult({reject, resolve, of: 'getIcons'}, cbk));
|
|
75
86
|
});
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
const additionalInfo = {1: 24, 2: 25, 4: 26, 8: 27};
|
|
2
|
+
const bitsPerByte = 8;
|
|
3
|
+
const largestSize = 8;
|
|
4
|
+
const majorTypeMultiplier = 32;
|
|
5
|
+
const maxInitialNumber = 23;
|
|
6
|
+
const sizes = [1, 2, 4];
|
|
7
|
+
|
|
8
|
+
/** Encode the head of a CBOR data item, its major type and its number
|
|
9
|
+
|
|
10
|
+
{
|
|
11
|
+
major: <Major Type Number>
|
|
12
|
+
number: <Length or Value Number>
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
@returns
|
|
16
|
+
{
|
|
17
|
+
head: <Data Item Head Buffer Object>
|
|
18
|
+
}
|
|
19
|
+
*/
|
|
20
|
+
module.exports = ({major, number}) => {
|
|
21
|
+
const initial = major * majorTypeMultiplier;
|
|
22
|
+
|
|
23
|
+
// Exit early when the number fits into the initial byte
|
|
24
|
+
if (number <= maxInitialNumber) {
|
|
25
|
+
return {head: Buffer.from([initial + number])};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// The number follows the initial byte, in as few bytes as it fits into
|
|
29
|
+
const size = sizes.find(n => number < 2 ** (n * bitsPerByte)) || largestSize;
|
|
30
|
+
|
|
31
|
+
const head = Buffer.alloc(1 + size);
|
|
32
|
+
|
|
33
|
+
head[0] = initial + additionalInfo[size];
|
|
34
|
+
|
|
35
|
+
// Exit early when the number needs all eight bytes
|
|
36
|
+
if (size === largestSize) {
|
|
37
|
+
head.writeBigUInt64BE(BigInt(number), 1);
|
|
38
|
+
|
|
39
|
+
return {head};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
head.writeUIntBE(number, 1, size);
|
|
43
|
+
|
|
44
|
+
return {head};
|
|
45
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
const decodeCborItem = require('./decode_cbor_item');
|
|
2
|
+
|
|
3
|
+
const hexAsBuffer = hex => Buffer.from(hex, 'hex');
|
|
4
|
+
const isString = n => typeof n === 'string';
|
|
5
|
+
|
|
6
|
+
/** Decode CBOR encoded data
|
|
7
|
+
|
|
8
|
+
{
|
|
9
|
+
cbor: <CBOR Encoded Hex String>
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
@throws
|
|
13
|
+
<Error>
|
|
14
|
+
|
|
15
|
+
@returns
|
|
16
|
+
{
|
|
17
|
+
value: <Decoded Value Object>
|
|
18
|
+
}
|
|
19
|
+
*/
|
|
20
|
+
module.exports = ({cbor}) => {
|
|
21
|
+
if (!isString(cbor)) {
|
|
22
|
+
throw new Error('ExpectedHexEncodedCborToDecode');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const data = hexAsBuffer(cbor);
|
|
26
|
+
|
|
27
|
+
const {end, value} = decodeCborItem({data, start: 0});
|
|
28
|
+
|
|
29
|
+
// The data has to be a single item, with nothing left over
|
|
30
|
+
if (end !== data.length) {
|
|
31
|
+
throw new Error('UnexpectedDataAfterCborItem');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return {value};
|
|
35
|
+
};
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
const additionalInfoMask = 31;
|
|
2
|
+
const argumentSizes = {24: 1, 25: 2, 26: 4, 27: 8};
|
|
3
|
+
const arrayOfLength = length => [...Array(length)];
|
|
4
|
+
const firstSimpleValue = 20;
|
|
5
|
+
const {fromEntries} = Object;
|
|
6
|
+
const {isSafeInteger} = Number;
|
|
7
|
+
const isString = n => typeof n === 'string';
|
|
8
|
+
const majorTypeArray = 4;
|
|
9
|
+
const majorTypeBytes = 2;
|
|
10
|
+
const majorTypeMap = 5;
|
|
11
|
+
const majorTypeNegative = 1;
|
|
12
|
+
const majorTypeShift = 5;
|
|
13
|
+
const majorTypeSimple = 7;
|
|
14
|
+
const majorTypeText = 3;
|
|
15
|
+
const majorTypeUnsigned = 0;
|
|
16
|
+
const maxAdditionalInfo = 27;
|
|
17
|
+
const maxInitialNumber = 23;
|
|
18
|
+
const maxSafe = BigInt(Number.MAX_SAFE_INTEGER);
|
|
19
|
+
const simpleValues = [false, true, null, undefined];
|
|
20
|
+
const sizeOfLargestNumber = 8;
|
|
21
|
+
|
|
22
|
+
/** Decode the CBOR data item that starts at an index
|
|
23
|
+
|
|
24
|
+
{
|
|
25
|
+
data: <CBOR Encoded Data Buffer Object>
|
|
26
|
+
start: <Start Index Number>
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
@throws
|
|
30
|
+
<Error>
|
|
31
|
+
|
|
32
|
+
@returns
|
|
33
|
+
{
|
|
34
|
+
end: <Index After the End of the Data Item Number>
|
|
35
|
+
value: <Decoded Value Object>
|
|
36
|
+
}
|
|
37
|
+
*/
|
|
38
|
+
module.exports = ({data, start}) => {
|
|
39
|
+
if (start >= data.length) {
|
|
40
|
+
throw new Error('UnexpectedEndOfCborData');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const info = data[start] & additionalInfoMask;
|
|
44
|
+
const major = data[start] >> majorTypeShift;
|
|
45
|
+
|
|
46
|
+
// Exit early when the item is false, true, null or undefined
|
|
47
|
+
if (major === majorTypeSimple) {
|
|
48
|
+
const index = info - firstSimpleValue;
|
|
49
|
+
|
|
50
|
+
if (index < 0 || index >= simpleValues.length) {
|
|
51
|
+
throw new Error('UnsupportedCborSimpleValue');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return {end: start + 1, value: simpleValues[index]};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Indefinite lengths are not supported
|
|
58
|
+
if (info > maxAdditionalInfo) {
|
|
59
|
+
throw new Error('UnsupportedCborAdditionalInfo');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// A number too large for the initial byte follows after the initial byte
|
|
63
|
+
const size = info > maxInitialNumber ? argumentSizes[info] : 0;
|
|
64
|
+
|
|
65
|
+
const next = start + 1 + size;
|
|
66
|
+
|
|
67
|
+
if (next > data.length) {
|
|
68
|
+
throw new Error('UnexpectedEndOfCborData');
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const readNumber = () => {
|
|
72
|
+
// Exit early when the number is in the initial byte
|
|
73
|
+
if (!size) {
|
|
74
|
+
return info;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Exit early when the number is in one, two or four bytes
|
|
78
|
+
if (size !== sizeOfLargestNumber) {
|
|
79
|
+
return data.readUIntBE(start + 1, size);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const number = data.readBigUInt64BE(start + 1);
|
|
83
|
+
|
|
84
|
+
if (number > maxSafe) {
|
|
85
|
+
throw new Error('UnsupportedLargeCborNumber');
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return Number(number);
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
const number = readNumber();
|
|
92
|
+
|
|
93
|
+
// Exit early when the item is an integer at or above zero
|
|
94
|
+
if (major === majorTypeUnsigned) {
|
|
95
|
+
return {end: next, value: number};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Exit early when the item is a negative integer
|
|
99
|
+
if (major === majorTypeNegative) {
|
|
100
|
+
if (!isSafeInteger(-1 - number)) {
|
|
101
|
+
throw new Error('UnsupportedLargeCborNumber');
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return {end: next, value: -1 - number};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Exit early when the item is bytes or text
|
|
108
|
+
if (major === majorTypeBytes || major === majorTypeText) {
|
|
109
|
+
const end = next + number;
|
|
110
|
+
|
|
111
|
+
if (end > data.length) {
|
|
112
|
+
throw new Error('UnexpectedEndOfCborData');
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const bytes = Buffer.from(data.subarray(next, end));
|
|
116
|
+
|
|
117
|
+
return {end, value: major === majorTypeText ? bytes.toString() : bytes};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Exit early when the item is an array, decoding each item in order
|
|
121
|
+
if (major === majorTypeArray) {
|
|
122
|
+
// Every item takes up at least one byte
|
|
123
|
+
if (number > data.length - next) {
|
|
124
|
+
throw new Error('UnexpectedEndOfCborData');
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const items = [];
|
|
128
|
+
|
|
129
|
+
const end = arrayOfLength(number).reduce(at => {
|
|
130
|
+
const item = module.exports({data, start: at});
|
|
131
|
+
|
|
132
|
+
items.push(item.value);
|
|
133
|
+
|
|
134
|
+
return item.end;
|
|
135
|
+
},
|
|
136
|
+
next);
|
|
137
|
+
|
|
138
|
+
return {end, value: items};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (major !== majorTypeMap) {
|
|
142
|
+
throw new Error('UnsupportedCborTag');
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Every map entry takes up at least two bytes, a key and its value
|
|
146
|
+
if (number * 2 > data.length - next) {
|
|
147
|
+
throw new Error('UnexpectedEndOfCborData');
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const entries = [];
|
|
151
|
+
|
|
152
|
+
// Decode each key followed by its value
|
|
153
|
+
const end = arrayOfLength(number).reduce(at => {
|
|
154
|
+
const key = module.exports({data, start: at});
|
|
155
|
+
|
|
156
|
+
if (!isString(key.value)) {
|
|
157
|
+
throw new Error('ExpectedTextKeysInCborMap');
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const item = module.exports({data, start: key.end});
|
|
161
|
+
|
|
162
|
+
entries.push([key.value, item.value]);
|
|
163
|
+
|
|
164
|
+
return item.end;
|
|
165
|
+
},
|
|
166
|
+
next);
|
|
167
|
+
|
|
168
|
+
return {end, value: fromEntries(entries)};
|
|
169
|
+
};
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
const asyncAuto = require('async/auto');
|
|
2
|
-
const
|
|
2
|
+
const asyncify = require('async/asyncify');
|
|
3
3
|
const {diffieHellmanComputeSecret} = require('ln-service');
|
|
4
4
|
const {getIdentity} = require('ln-service');
|
|
5
5
|
const {getNode} = require('ln-service');
|
|
6
6
|
const {returnResult} = require('asyncjs-util');
|
|
7
7
|
|
|
8
|
+
const decodeCbor = require('./decode_cbor');
|
|
8
9
|
const decryptWithSecret = require('./decrypt_with_secret');
|
|
9
10
|
|
|
10
11
|
const hexAsBuf = hex => Buffer.from(hex, 'hex');
|
|
@@ -41,27 +42,29 @@ module.exports = ({encrypted, lnd}, cbk) => {
|
|
|
41
42
|
},
|
|
42
43
|
|
|
43
44
|
// Decode encrypted CBOR
|
|
44
|
-
decode: ['validate', (
|
|
45
|
-
|
|
46
|
-
if (!!err) {
|
|
47
|
-
return cbk([400, 'ExpectedCborEncodedEncryptedData', {err}]);
|
|
48
|
-
}
|
|
45
|
+
decode: ['validate', asyncify(() => {
|
|
46
|
+
let details;
|
|
49
47
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
48
|
+
try {
|
|
49
|
+
details = decodeCbor({cbor: encrypted}).value;
|
|
50
|
+
} catch (err) {
|
|
51
|
+
throw [400, 'ExpectedCborEncodedEncryptedData', {err}];
|
|
52
|
+
}
|
|
53
53
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
54
|
+
if (!details) {
|
|
55
|
+
throw [400, 'ExpectedDecodedDetailsToDecryptWithNode'];
|
|
56
|
+
}
|
|
57
57
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
58
|
+
if (!details.settings) {
|
|
59
|
+
throw [400, 'ExpectedEncryptionSettingsToDecryptWithNode'];
|
|
60
|
+
}
|
|
61
61
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
62
|
+
if (!isArray(details.settings.with)) {
|
|
63
|
+
throw [400, 'ExpectedWithNodesPublicKeyToDecrypt'];
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return details;
|
|
67
|
+
})],
|
|
65
68
|
|
|
66
69
|
// Get node public key
|
|
67
70
|
getPublicKey: ['decode', ({decode}, cbk) => getIdentity({lnd}, cbk)],
|