balanceofsatoshis 23.1.12 → 23.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 +10 -0
- package/bos +8 -1
- package/chain/closing_fees.js +4 -3
- package/chain/fund_transaction.js +6 -1
- package/display/describe_route.js +20 -9
- package/network/blinded_path_fee.js +61 -0
- package/network/execute_probe.js +45 -21
- package/network/pay.js +15 -1
- package/network/probe.js +21 -1
- package/network/probe_destination.js +101 -8
- package/network/push_payment.js +9 -2
- package/offchain/create_invoice.js +155 -7
- package/offchain/get_invoice_amount.js +10 -4
- package/offchain/relay_policy_issue.js +56 -0
- package/offchain/select_blinded_path.js +282 -0
- package/offchain/select_next_hop.js +224 -0
- package/offchain/sign_payment_request.js +46 -4
- package/package.json +33 -7
- package/.dockerignore +0 -5
- package/.gitattributes +0 -1
- package/.prettierignore +0 -2
- package/.tm_properties +0 -1
- package/.travis.yml +0 -15
- package/CONTRIBUTING.md +0 -95
- package/Dockerfile +0 -22
- package/test/arrays/test_shuffle.js +0 -43
- package/test/arrays/test_sort_by.js +0 -47
- package/test/balances/test_balance_from_tokens.js +0 -63
- package/test/balances/test_get_accounting_report.js +0 -127
- package/test/balances/test_get_balance.js +0 -66
- package/test/balances/test_get_liquidity.js +0 -61
- package/test/balances/test_is_matching_filters.js +0 -47
- package/test/balances/test_range_for_date.js +0 -57
- package/test/balances/test_table_rows_from_csv.js +0 -47
- package/test/chain/test_get_chain_fees.js +0 -84
- package/test/chain/test_get_channel_closes.js +0 -41
- package/test/chain/test_get_deposit_address.js +0 -57
- package/test/chain/test_get_mempool_size.js +0 -69
- package/test/chain/test_get_raw_transaction.js +0 -90
- package/test/chain/test_get_utxos.js +0 -137
- package/test/display/test_chart_alias_for_peer.js +0 -43
- package/test/display/test_describe_confidence.js +0 -47
- package/test/display/test_describe_route.js +0 -101
- package/test/display/test_segment_measure.js +0 -30
- package/test/encryption/fixtures.json +0 -4
- package/test/encryption/test_decrypt_ciphertext.js +0 -70
- package/test/encryption/test_der_as_pem.js +0 -32
- package/test/encryption/test_encrypt_to_public_keys.js +0 -75
- package/test/encryption/test_pem_as_der.js +0 -22
- package/test/fixtures/chan_info_response.json +0 -25
- package/test/fixtures/describe_graph_response.json +0 -36
- package/test/fixtures/get_info_response.json +0 -16
- package/test/fixtures/get_node_info_response.json +0 -37
- package/test/fixtures/index.js +0 -23
- package/test/fixtures/liquidity_channels_response.json +0 -68
- package/test/fixtures/list_channels_response.json +0 -47
- package/test/fixtures/list_peers_response.json +0 -16
- package/test/fixtures/pending_channels_response.json +0 -21
- package/test/fixtures/query_routes_response.json +0 -19
- package/test/fixtures/version_info_response.json +0 -6
- package/test/integration/test_fund_transaction.js +0 -38
- package/test/integration/test_open_balanced_channel.js +0 -283
- package/test/integration/test_open_channels.js +0 -120
- package/test/integration/test_remove_peer.js +0 -36
- package/test/integration/test_taproot_channels.js +0 -132
- package/test/integration/test_trusted_channels.js +0 -149
- package/test/integration.js +0 -18
- package/test/lnd/test_credential_restrictions.js +0 -76
- package/test/lnd/test_find_record.js +0 -142
- package/test/lnd/test_get_cert.js +0 -58
- package/test/lnd/test_get_macaroon.js +0 -53
- package/test/lnd/test_get_path.js +0 -57
- package/test/lnd/test_get_socket.js +0 -109
- package/test/lnd/test_lnd_directory.js +0 -81
- package/test/network/test_currency_for_network.js +0 -46
- package/test/network/test_get_peers.js +0 -311
- package/test/network/test_gift_callback_error.js +0 -48
- package/test/network/test_is_relevant_forwards.js +0 -80
- package/test/network/test_is_relevant_source.js +0 -80
- package/test/network/test_multi_probe.js +0 -121
- package/test/network/test_peers_with_activity.js +0 -68
- package/test/network/test_push_payment.js +0 -84
- package/test/network/test_reconnect.js +0 -110
- package/test/network/test_send_gift.js +0 -74
- package/test/network/test_set_autopilot.js +0 -37
- package/test/nodes/test_adjust_tags.js +0 -299
- package/test/nodes/test_delete_node_credentials.js +0 -55
- package/test/nodes/test_get_saved_credentials.js +0 -95
- package/test/nodes/test_get_saved_nodes.js +0 -136
- package/test/nodes/test_put_saved_credentials.js +0 -80
- package/test/nodes/test_register_node.js +0 -331
- package/test/peers/test_channels_from_arguments.js +0 -160
- package/test/peers/test_detect_open_rule_violation.js +0 -78
- package/test/peers/test_find_tag_match.js +0 -143
- package/test/peers/test_open_request_violation.js +0 -70
- package/test/responses/test_return_chart.js +0 -66
- package/test/responses/test_return_json.js +0 -44
- package/test/responses/test_return_number.js +0 -45
- package/test/responses/test_return_object.js +0 -107
- package/test/responses/test_return_output.js +0 -44
- package/test/responses/test_write_json_file.js +0 -59
- package/test/routing/test_channel_for_gift.js +0 -119
- package/test/routing/test_channels_from_hints.js +0 -76
- package/test/routing/test_fees_for_segment.js +0 -47
- package/test/routing/test_forwards_via_peer.js +0 -39
- package/test/routing/test_get_fees_chart.js +0 -111
- package/test/routing/test_get_maximum.js +0 -71
- package/test/routing/test_get_past_forwards.js +0 -61
- package/test/routing/test_gift_route.js +0 -151
- package/test/routing/test_ignore_from_avoid.js +0 -30
- package/test/routing/test_parse_fee_rate_formula.js +0 -69
- package/test/services/test_accept_balanced_channel.js +0 -186
- package/test/services/test_balanced_open_accept_details.js +0 -116
- package/test/services/test_get_balanced_opens.js +0 -174
- package/test/services/test_initiate_balanced_channel.js +0 -332
- package/test/swaps/test_append_failing_edge.js +0 -127
- package/test/swaps/test_channel_for_send.js +0 -50
- package/test/swaps/test_find_rebalance_peer.js +0 -329
- package/test/swaps/test_get_avoid_list.js +0 -62
- package/test/swaps/test_get_paid_service.js +0 -61
- package/test/swaps/test_get_swap_cost.js +0 -135
- package/test/swaps/test_swap_api_key.js +0 -63
- package/test/swaps/test_write_avoid_list.js +0 -83
- package/test/tags/test_get_tags.js +0 -99
- package/test/telegram/test_get_socks_proxy.js +0 -72
- package/test/wallets/test_channels_as_report_activity.js +0 -126
- package/test/wallets/test_report_overview.js +0 -73
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Versions
|
|
2
2
|
|
|
3
|
+
## 23.2.0
|
|
4
|
+
|
|
5
|
+
- `invoice`: Add `--encrypted-hints` to hide the node behind blinded paths in
|
|
6
|
+
the payment request
|
|
7
|
+
- `invoice`: Add `--select-hints` with `--encrypted-hints` to choose the
|
|
8
|
+
blinded path outwards from a peer to an introduction node
|
|
9
|
+
- `invoice`: Add `--virtual-fee-rate` with selected `--encrypted-hints` to
|
|
10
|
+
charge a receiver fee on the blinded path
|
|
11
|
+
- `pay`, `probe`: Add support for payment requests with blinded paths
|
|
12
|
+
|
|
3
13
|
## 23.1.12
|
|
4
14
|
|
|
5
15
|
- Add support for LND 0.21.3
|
package/bos
CHANGED
|
@@ -1147,7 +1147,11 @@ prog
|
|
|
1147
1147
|
.help('--virtual invoices cannot be used with payers who probe before pay')
|
|
1148
1148
|
.help('Only one --virtual invoice can be active at a time')
|
|
1149
1149
|
.help('--reject-on-amount-increase can only be used with --virtual invoices')
|
|
1150
|
+
.help('--encrypted-hints hides the node with blinded paths, needs an amount')
|
|
1151
|
+
.help('--encrypted-hints with --select-hints picks the path outwards by hop')
|
|
1152
|
+
.help('--virtual-fee-rate with selected --encrypted-hints is a receiver fee')
|
|
1150
1153
|
.argument('[amount]', 'Amount for invoice', STRING, '0')
|
|
1154
|
+
.option('--encrypted-hints', 'Use blinded paths instead of hop hints')
|
|
1151
1155
|
.option('--for <description>', 'What is the invoice requesting payment for')
|
|
1152
1156
|
.option('--hours <expires_in>', 'Hours invoice is valid for', INT)
|
|
1153
1157
|
.option('--include-hints', 'Include the default set of hop hint channels')
|
|
@@ -1157,7 +1161,7 @@ prog
|
|
|
1157
1161
|
.option('--reject-on-amount-increase', 'Reject if required amount increases')
|
|
1158
1162
|
.option('--select-hints', 'Select hop hints to be added to the request')
|
|
1159
1163
|
.option('--virtual', 'Request payment over a virtual channel')
|
|
1160
|
-
.option('--virtual-fee-rate <pm>', '
|
|
1164
|
+
.option('--virtual-fee-rate <pm>', 'Receiver fee rate on virtual/path', INT)
|
|
1161
1165
|
.action((args, options, logger) => {
|
|
1162
1166
|
return new Promise(async (resolve, reject) => {
|
|
1163
1167
|
try {
|
|
@@ -1167,6 +1171,7 @@ prog
|
|
|
1167
1171
|
ask: await commands.interrogate({}),
|
|
1168
1172
|
description: options.for,
|
|
1169
1173
|
expires_in: options.hours,
|
|
1174
|
+
is_encrypting_hints: options.encryptedHints || undefined,
|
|
1170
1175
|
is_hinting: options.includeHints || undefined,
|
|
1171
1176
|
is_including_qr: !!options.qr || undefined,
|
|
1172
1177
|
is_rejecting_option: !!options.rejectOnAmountIncrease || undefined,
|
|
@@ -1551,6 +1556,7 @@ prog
|
|
|
1551
1556
|
|
|
1552
1557
|
// Pay a payment request, probing first
|
|
1553
1558
|
.command('pay', 'Pay a payment request, probing first')
|
|
1559
|
+
.help('Blinded path requests are probed to the introduction node, 1 path')
|
|
1554
1560
|
.argument('<request>', 'Payment Request')
|
|
1555
1561
|
.option('--avoid <avoid>', 'Avoid forwarding via node/chan/tag', REPEATABLE)
|
|
1556
1562
|
.option('--avoid-high-fee-routes', 'Avoid evaluating too-high fee routes')
|
|
@@ -1670,6 +1676,7 @@ prog
|
|
|
1670
1676
|
// Determine if a payment request is sendable
|
|
1671
1677
|
.command('probe', 'Check if a payment request is sendable')
|
|
1672
1678
|
.help('Simulate paying a payment request without actually paying it')
|
|
1679
|
+
.help('Blinded path requests are probed to the introduction node, 1 path')
|
|
1673
1680
|
.argument('<to>', 'Payment request or node public key')
|
|
1674
1681
|
.argument('[amount]', 'Amount to probe, default: request amount')
|
|
1675
1682
|
.option('--avoid <avoid>', 'Avoid forwarding via node/chan/tag', REPEATABLE)
|
package/chain/closing_fees.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
const {
|
|
1
|
+
const {componentsOfTransaction} = require('@alexbosworth/blockchain');
|
|
2
2
|
|
|
3
|
-
const {fromHex} = Transaction;
|
|
4
3
|
const sumOf = arr => arr.reduce((sum, n) => sum + n, Number());
|
|
5
4
|
|
|
6
5
|
/** Calculate channel closing fees paid
|
|
@@ -40,7 +39,9 @@ module.exports = args => {
|
|
|
40
39
|
}
|
|
41
40
|
|
|
42
41
|
// Calculate the value of the outputs in the tx that spends the capacity
|
|
43
|
-
const
|
|
42
|
+
const {outputs} = componentsOfTransaction({transaction: closer.transaction});
|
|
43
|
+
|
|
44
|
+
const outValue = sumOf(outputs.map(n => n.tokens));
|
|
44
45
|
|
|
45
46
|
const fees = []
|
|
46
47
|
.concat(args.capacity - outValue)
|
|
@@ -246,7 +246,12 @@ module.exports = (args, cbk) => {
|
|
|
246
246
|
try {
|
|
247
247
|
const outputs = args.addresses.map((address, i) => {
|
|
248
248
|
const amount = args.amounts[i];
|
|
249
|
-
const variables = {
|
|
249
|
+
const variables = {};
|
|
250
|
+
|
|
251
|
+
// The max variable is only defined when a maximum is calculated
|
|
252
|
+
if (getMax.max_tokens !== undefined) {
|
|
253
|
+
variables.max = getMax.max_tokens;
|
|
254
|
+
}
|
|
250
255
|
|
|
251
256
|
return {address, tokens: parseAmount({amount, variables}).tokens};
|
|
252
257
|
});
|
|
@@ -10,8 +10,10 @@ const describeConfidence = require('./describe_confidence');
|
|
|
10
10
|
const formatFeeRate = require('./format_fee_rate');
|
|
11
11
|
|
|
12
12
|
const aliasColor = n => !!n ? greenBright(n) : '';
|
|
13
|
+
const blindedChannel = 'blinded';
|
|
13
14
|
const effectiveFeeRate = (n, m) => Number(BigInt(1e6) * BigInt(n) / BigInt(m));
|
|
14
15
|
const flatten = arr => [].concat(...arr);
|
|
16
|
+
const isBlindedHop = hop => !!hop.encrypted_data && !hop.path_key;
|
|
15
17
|
const isEmoji = /(?:[\u261D\u26F9\u270A-\u270D]|\uD83C[\uDF85\uDFC2-\uDFC4\uDFC7\uDFCA-\uDFCC]|\uD83D[\uDC42\uDC43\uDC46-\uDC50\uDC66-\uDC69\uDC6E\uDC70-\uDC78\uDC7C\uDC81-\uDC83\uDC85-\uDC87\uDCAA\uDD74\uDD75\uDD7A\uDD90\uDD95\uDD96\uDE45-\uDE47\uDE4B-\uDE4F\uDEA3\uDEB4-\uDEB6\uDEC0\uDECC]|\uD83E[\uDD18-\uDD1C\uDD1E\uDD1F\uDD26\uDD30-\uDD39\uDD3D\uDD3E\uDDD1-\uDDDD])(?:\uD83C[\uDFFB-\uDFFF])?|(?:[\u231A\u231B\u23E9-\u23EC\u23F0\u23F3\u25FD\u25FE\u2614\u2615\u2648-\u2653\u267F\u2693\u26A1\u26AA\u26AB\u26BD\u26BE\u26C4\u26C5\u26CE\u26D4\u26EA\u26F2\u26F3\u26F5\u26FA\u26FD\u2705\u270A\u270B\u2728\u274C\u274E\u2753-\u2755\u2757\u2795-\u2797\u27B0\u27BF\u2B1B\u2B1C\u2B50\u2B55]|\uD83C[\uDC04\uDCCF\uDD8E\uDD91-\uDD9A\uDDE6-\uDDFF\uDE01\uDE1A\uDE2F\uDE32-\uDE36\uDE38-\uDE3A\uDE50\uDE51\uDF00-\uDF20\uDF2D-\uDF35\uDF37-\uDF7C\uDF7E-\uDF93\uDFA0-\uDFCA\uDFCF-\uDFD3\uDFE0-\uDFF0\uDFF4\uDFF8-\uDFFF]|\uD83D[\uDC00-\uDC3E\uDC40\uDC42-\uDCFC\uDCFF-\uDD3D\uDD4B-\uDD4E\uDD50-\uDD67\uDD7A\uDD95\uDD96\uDDA4\uDDFB-\uDE4F\uDE80-\uDEC5\uDECC\uDED0-\uDED2\uDEEB\uDEEC\uDEF4-\uDEF8]|\uD83E[\uDD10-\uDD3A\uDD3C-\uDD3E\uDD40-\uDD45\uDD47-\uDD4C\uDD50-\uDD6B\uDD80-\uDD97\uDDC0\uDDD0-\uDDE6])|(?:[#\*0-9\xA9\xAE\u203C\u2049\u2122\u2139\u2194-\u2199\u21A9\u21AA\u231A\u231B\u2328\u23CF\u23E9-\u23F3\u23F8-\u23FA\u24C2\u25AA\u25AB\u25B6\u25C0\u25FB-\u25FE\u2600-\u2604\u260E\u2611\u2614\u2615\u2618\u261D\u2620\u2622\u2623\u2626\u262A\u262E\u262F\u2638-\u263A\u2640\u2642\u2648-\u2653\u2660\u2663\u2665\u2666\u2668\u267B\u267F\u2692-\u2697\u2699\u269B\u269C\u26A0\u26A1\u26AA\u26AB\u26B0\u26B1\u26BD\u26BE\u26C4\u26C5\u26C8\u26CE\u26CF\u26D1\u26D3\u26D4\u26E9\u26EA\u26F0-\u26F5\u26F7-\u26FA\u26FD\u2702\u2705\u2708-\u270D\u270F\u2712\u2714\u2716\u271D\u2721\u2728\u2733\u2734\u2744\u2747\u274C\u274E\u2753-\u2755\u2757\u2763\u2764\u2795-\u2797\u27A1\u27B0\u27BF\u2934\u2935\u2B05-\u2B07\u2B1B\u2B1C\u2B50\u2B55\u3030\u303D\u3297\u3299]|\uD83C[\uDC04\uDCCF\uDD70\uDD71\uDD7E\uDD7F\uDD8E\uDD91-\uDD9A\uDDE6-\uDDFF\uDE01\uDE02\uDE1A\uDE2F\uDE32-\uDE3A\uDE50\uDE51\uDF00-\uDF21\uDF24-\uDF93\uDF96\uDF97\uDF99-\uDF9B\uDF9E-\uDFF0\uDFF3-\uDFF5\uDFF7-\uDFFF]|\uD83D[\uDC00-\uDCFD\uDCFF-\uDD3D\uDD49-\uDD4E\uDD50-\uDD67\uDD6F\uDD70\uDD73-\uDD7A\uDD87\uDD8A-\uDD8D\uDD90\uDD95\uDD96\uDDA4\uDDA5\uDDA8\uDDB1\uDDB2\uDDBC\uDDC2-\uDDC4\uDDD1-\uDDD3\uDDDC-\uDDDE\uDDE1\uDDE3\uDDE8\uDDEF\uDDF3\uDDFA-\uDE4F\uDE80-\uDEC5\uDECB-\uDED2\uDEE0-\uDEE5\uDEE9\uDEEB\uDEEC\uDEF0\uDEF3-\uDEF8]|\uD83E[\uDD10-\uDD3A\uDD3C-\uDD3E\uDD40-\uDD45\uDD47-\uDD4C\uDD50-\uDD6B\uDD80-\uDD97\uDDC0\uDDD0-\uDDE6])\uFE0F/g;
|
|
16
18
|
const pairEdgeIndex = (pair, key) => `x${Number(!pair.indexOf(key))}`;
|
|
17
19
|
|
|
@@ -26,10 +28,12 @@ const pairEdgeIndex = (pair, key) => `x${Number(!pair.indexOf(key))}`;
|
|
|
26
28
|
hops: [{
|
|
27
29
|
channel: <Standard Format Channel Id String>
|
|
28
30
|
channel_capacity: <Channel Capacity Tokens Number>
|
|
31
|
+
[encrypted_data]: <Blinded Path Encrypted Data Hex String>
|
|
29
32
|
fee: <Fee Number>
|
|
30
33
|
fee_mtokens: <Fee Millitokens String>
|
|
31
34
|
forward: <Forward Tokens Number>
|
|
32
35
|
forward_mtokens: <Forward Millitokens String>
|
|
36
|
+
[path_key]: <Blinded Path Key Hex String>
|
|
33
37
|
public_key: <Public Key Hex String>
|
|
34
38
|
timeout: <Timeout Block Height Number>
|
|
35
39
|
}]
|
|
@@ -109,28 +113,35 @@ module.exports = ({lnd, route, tagged}, cbk) => {
|
|
|
109
113
|
|
|
110
114
|
const path = route.hops.map((hop, i, hops) => {
|
|
111
115
|
const {alias} = getAliases.find(n => n.id === hop.public_key);
|
|
112
|
-
const
|
|
113
|
-
const delta = !i ? null : hops[i - 1].timeout - hops[i].timeout;
|
|
116
|
+
const isBlinded = isBlindedHop(hop);
|
|
114
117
|
const isFinal = i === hops.length - 1 && hops.length !== 1;
|
|
115
118
|
const pair = [(hops[i - 1] || {}).public_key, hop.public_key].sort();
|
|
116
119
|
|
|
117
|
-
|
|
118
|
-
const
|
|
120
|
+
// Blinded hops have no channel, timeout or amount of their own
|
|
121
|
+
const channel = !isBlinded ? hop.channel : blindedChannel;
|
|
122
|
+
const edge = isBlinded ? '' : pairEdgeIndex(pair, hop.public_key);
|
|
119
123
|
const feeMtokens = isFinal ? hops[i-1].fee_mtokens : hop.fee_mtokens;
|
|
120
124
|
const forwarder = `${aliasColor(alias)} ${hop.public_key}`.trim();
|
|
125
|
+
const hasForward = !!BigInt(hop.forward_mtokens || Number());
|
|
126
|
+
const prior = hops[i - 1];
|
|
121
127
|
|
|
122
|
-
const
|
|
128
|
+
const delta = !i || isBlinded ? null : prior.timeout - hop.timeout;
|
|
129
|
+
const expiry = delta > 240 ? redBright(` Expiry +${delta}`) : '';
|
|
123
130
|
|
|
124
|
-
|
|
131
|
+
// The rate is unknown when the hop forward amount is unknown
|
|
132
|
+
const rate = !hasForward ? null : formatFeeRate({
|
|
133
|
+
rate: effectiveFeeRate(feeMtokens, hop.forward_mtokens),
|
|
134
|
+
}).display;
|
|
125
135
|
|
|
126
|
-
const forward =
|
|
136
|
+
const forward = rate === null ? forwarder :
|
|
137
|
+
`${forwarder}. Fee rate: ${rate}${expiry}`;
|
|
127
138
|
|
|
128
139
|
if (!i) {
|
|
129
140
|
return [`${gray(channel)} ${description || String()}`, forward];
|
|
130
141
|
} else if (i === hops.length - [i].length) {
|
|
131
|
-
return [`${gray(channel +
|
|
142
|
+
return [`${gray(channel + edge)}`];
|
|
132
143
|
} else {
|
|
133
|
-
return [`${gray(channel +
|
|
144
|
+
return [`${gray(channel + edge)}`, forward];
|
|
134
145
|
}
|
|
135
146
|
});
|
|
136
147
|
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
const feeRateDenominator = BigInt(1e6);
|
|
2
|
+
const {isArray} = Array;
|
|
3
|
+
const isMtokens = n => typeof n === 'string' && /^\d+$/.test(n);
|
|
4
|
+
const isNumber = n => typeof n === 'number' && !isNaN(n);
|
|
5
|
+
const noFee = '0';
|
|
6
|
+
|
|
7
|
+
/** Calculate the fee to deliver an amount through a blinded path
|
|
8
|
+
|
|
9
|
+
{
|
|
10
|
+
mtokens: <Millitokens To Deliver To The Destination String>
|
|
11
|
+
path: {
|
|
12
|
+
base_fee_mtokens: <Accumulated Base Fee Millitokens String>
|
|
13
|
+
fee_rate: <Accumulated Fee Rate Millitokens Per Million Number>
|
|
14
|
+
hops: [{
|
|
15
|
+
encrypted_data: <Encrypted Recipient Data Hex String>
|
|
16
|
+
relay_key: <Relaying Node Public Key Hex String>
|
|
17
|
+
}]
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
@throws
|
|
22
|
+
<Error>
|
|
23
|
+
|
|
24
|
+
@returns
|
|
25
|
+
{
|
|
26
|
+
fee_mtokens: <Blinded Path Fee Millitokens String>
|
|
27
|
+
}
|
|
28
|
+
*/
|
|
29
|
+
module.exports = ({mtokens, path}) => {
|
|
30
|
+
if (!isMtokens(mtokens)) {
|
|
31
|
+
throw new Error('ExpectedMillitokensToCalculateBlindedPathFee');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (!path) {
|
|
35
|
+
throw new Error('ExpectedBlindedPathToCalculateBlindedPathFee');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (!isMtokens(path.base_fee_mtokens)) {
|
|
39
|
+
throw new Error('ExpectedBaseFeeMillitokensToCalculateBlindedPathFee');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (!isNumber(path.fee_rate)) {
|
|
43
|
+
throw new Error('ExpectedFeeRateToCalculateBlindedPathFee');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (!isArray(path.hops) || !path.hops.length) {
|
|
47
|
+
throw new Error('ExpectedPathHopsToCalculateBlindedPathFee');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Exit early when the path is just the destination, there is no relaying
|
|
51
|
+
if (path.hops.length === [path].length) {
|
|
52
|
+
return {fee_mtokens: noFee};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const baseFee = BigInt(path.base_fee_mtokens);
|
|
56
|
+
const proportional = BigInt(mtokens) * BigInt(path.fee_rate);
|
|
57
|
+
|
|
58
|
+
const fee = baseFee + proportional / feeRateDenominator;
|
|
59
|
+
|
|
60
|
+
return {fee_mtokens: fee.toString()};
|
|
61
|
+
};
|
package/network/execute_probe.js
CHANGED
|
@@ -16,9 +16,11 @@ const tokensAsMillitokens = tok => (BigInt(tok) * BigInt(1e3)).toString();
|
|
|
16
16
|
|
|
17
17
|
/** Execute a probe
|
|
18
18
|
|
|
19
|
+
`destination` is not required when probing to blinded `paths`
|
|
20
|
+
|
|
19
21
|
{
|
|
20
22
|
cltv_delta: <Final Cltv Delta Number>
|
|
21
|
-
destination: <Final Destination Public Key Hex String>
|
|
23
|
+
[destination]: <Final Destination Public Key Hex String>
|
|
22
24
|
[features]: [{
|
|
23
25
|
bit: <Feature Bit Number>
|
|
24
26
|
}]
|
|
@@ -39,6 +41,19 @@ const tokensAsMillitokens = tok => (BigInt(tok) * BigInt(1e3)).toString();
|
|
|
39
41
|
value: <Message To Final Destination Raw Value Hex Encoded String>
|
|
40
42
|
}]
|
|
41
43
|
[outgoing_channel]: <Outgoing Channel Id String>
|
|
44
|
+
[paths]: [{
|
|
45
|
+
base_fee_mtokens: <Accumulated Base Fee Millitokens String>
|
|
46
|
+
cltv_delta: <Accumulated CLTV Expiry Delta Number>
|
|
47
|
+
fee_rate: <Accumulated Fee Rate Millitokens Per Million Number>
|
|
48
|
+
hops: [{
|
|
49
|
+
encrypted_data: <Encrypted Recipient Data Hex String>
|
|
50
|
+
relay_key: <Relaying Node Public Key Hex String>
|
|
51
|
+
}]
|
|
52
|
+
[introduction_node]: <Introduction Node Public Key Hex String>
|
|
53
|
+
key: <First Hop Path Key Public Key Hex String>
|
|
54
|
+
[max_htlc_mtokens]: <Maximum HTLC Millitokens String>
|
|
55
|
+
[min_htlc_mtokens]: <Minimum HTLC Millitokens String>
|
|
56
|
+
}]
|
|
42
57
|
[payment]: <Payment Identifier Hex String>
|
|
43
58
|
[routes]: [[{
|
|
44
59
|
[base_fee_mtokens]: <Base Fee Millitokens String>
|
|
@@ -67,10 +82,12 @@ const tokensAsMillitokens = tok => (BigInt(tok) * BigInt(1e3)).toString();
|
|
|
67
82
|
hops: [{
|
|
68
83
|
channel: <Standard Format Channel Id String>
|
|
69
84
|
channel_capacity: <Channel Capacity Tokens Number>
|
|
85
|
+
[encrypted_data]: <Blinded Path Encrypted Data Hex String>
|
|
70
86
|
fee: <Fee Number>
|
|
71
87
|
fee_mtokens: <Fee Millitokens String>
|
|
72
88
|
forward: <Forward Tokens Number>
|
|
73
89
|
forward_mtokens: <Forward Millitokens String>
|
|
90
|
+
[path_key]: <Blinded Path Key Hex String>
|
|
74
91
|
public_key: <Public Key Hex String>
|
|
75
92
|
timeout: <Timeout Block Height Number>
|
|
76
93
|
}]
|
|
@@ -89,7 +106,7 @@ module.exports = (args, cbk) => {
|
|
|
89
106
|
return cbk([400, 'ExpectedFinalCltvDeltaToExecuteProbe']);
|
|
90
107
|
}
|
|
91
108
|
|
|
92
|
-
if (!args.destination) {
|
|
109
|
+
if (!args.destination && !args.paths) {
|
|
93
110
|
return cbk([400, 'ExpectedDestinationToExecuteProbe']);
|
|
94
111
|
}
|
|
95
112
|
|
|
@@ -145,25 +162,32 @@ module.exports = (args, cbk) => {
|
|
|
145
162
|
|
|
146
163
|
const timeoutMinutes = minutesAsMs((args.timeout_minutes || Number()));
|
|
147
164
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
165
|
+
let sub;
|
|
166
|
+
|
|
167
|
+
try {
|
|
168
|
+
sub = subscribeToProbeForRoute({
|
|
169
|
+
mtokens,
|
|
170
|
+
cltv_delta: args.cltv_delta,
|
|
171
|
+
destination: args.destination,
|
|
172
|
+
features: args.features,
|
|
173
|
+
ignore: args.ignore,
|
|
174
|
+
incoming_peer: args.in_through,
|
|
175
|
+
lnd: args.lnd,
|
|
176
|
+
max_fee_mtokens: strictMaxFee || undefined,
|
|
177
|
+
max_timeout_height: args.max_timeout_height,
|
|
178
|
+
messages: args.messages,
|
|
179
|
+
outgoing_channel: args.outgoing_channel,
|
|
180
|
+
path_timeout_ms: pathTimeoutMs,
|
|
181
|
+
paths: args.paths,
|
|
182
|
+
payment: args.payment,
|
|
183
|
+
probe_timeout_ms: timeoutMinutes || undefined,
|
|
184
|
+
routes: args.routes,
|
|
185
|
+
tokens: args.tokens,
|
|
186
|
+
total_mtokens: !!args.payment ? mtokens : undefined,
|
|
187
|
+
});
|
|
188
|
+
} catch (err) {
|
|
189
|
+
return cbk([400, 'FailedToStartProbeForRoute', {err}]);
|
|
190
|
+
}
|
|
167
191
|
|
|
168
192
|
const finished = (err, res) => {
|
|
169
193
|
sub.removeAllListeners();
|
package/network/pay.js
CHANGED
|
@@ -21,6 +21,7 @@ const {isArray} = Array;
|
|
|
21
21
|
const mtokensAsTokens = mtokens => Number(mtokens / BigInt(1e3));
|
|
22
22
|
const pathTimeoutMs = 1000 * 60 * 5;
|
|
23
23
|
const singlePath = 1;
|
|
24
|
+
const withTotalFee = ({fee, ...res}) => ({total_fee: fee, ...res});
|
|
24
25
|
|
|
25
26
|
/** Make a payment
|
|
26
27
|
|
|
@@ -229,6 +230,11 @@ module.exports = (args, cbk) => {
|
|
|
229
230
|
const paths = [];
|
|
230
231
|
const request = parsePaymentRequest({request: args.request});
|
|
231
232
|
|
|
233
|
+
// Multi-path payments look for paths to a destination, not to paths
|
|
234
|
+
if (!!request.paths) {
|
|
235
|
+
return cbk([501, 'MultiPathPayNotSupportedWithBlindedPaths']);
|
|
236
|
+
}
|
|
237
|
+
|
|
232
238
|
const sub = subscribeToMultiPathProbe({
|
|
233
239
|
cltv_delta: request.cltv_delta,
|
|
234
240
|
destination: request.destination,
|
|
@@ -399,7 +405,15 @@ module.exports = (args, cbk) => {
|
|
|
399
405
|
'singlePathPay',
|
|
400
406
|
({multiPathPay, singlePathPay}, cbk) =>
|
|
401
407
|
{
|
|
402
|
-
|
|
408
|
+
const payment = singlePathPay || multiPathPay;
|
|
409
|
+
|
|
410
|
+
// Exit early when the payment was not through a blinded path
|
|
411
|
+
if (!payment || payment.blinded_path_fee === undefined) {
|
|
412
|
+
return cbk(null, payment);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
// The fee of a blinded path payment is a total including the path fee
|
|
416
|
+
return cbk(null, withTotalFee(payment));
|
|
403
417
|
}],
|
|
404
418
|
},
|
|
405
419
|
returnResult({reject, resolve, of: 'payment'}, cbk));
|
package/network/probe.js
CHANGED
|
@@ -30,6 +30,7 @@ const pathTimeoutMs = 1000 * 60 * 90;
|
|
|
30
30
|
const singlePath = 1;
|
|
31
31
|
const uniq = arr => Array.from(new Set(arr));
|
|
32
32
|
const unsupported = 501;
|
|
33
|
+
const withTotalFee = ({fee, ...res}) => ({total_fee: fee, ...res});
|
|
33
34
|
|
|
34
35
|
/** Probe a destination, looking for multiple non-overlapping paths
|
|
35
36
|
|
|
@@ -54,10 +55,12 @@ const unsupported = 501;
|
|
|
54
55
|
|
|
55
56
|
@returns via cbk or Promise
|
|
56
57
|
{
|
|
58
|
+
[blinded_path_fee]: <Blinded Path Fee Tokens Number>
|
|
57
59
|
[fee]: <Total Fee Tokens To Destination Number>
|
|
58
60
|
[latency_ms]: <Latency Milliseconds Number>
|
|
59
61
|
[relays]: [[<Relaying Public Key Hex String>]]
|
|
60
62
|
[routes_maximum]: <Maximum Sendable Tokens on Paths Number>
|
|
63
|
+
[total_fee]: <Total Fee Tokens Including Blinded Path Fee Number>
|
|
61
64
|
}
|
|
62
65
|
*/
|
|
63
66
|
module.exports = (args, cbk) => {
|
|
@@ -109,6 +112,7 @@ module.exports = (args, cbk) => {
|
|
|
109
112
|
cltv_delta: decoded.cltv_delta,
|
|
110
113
|
destination: decoded.destination,
|
|
111
114
|
features: decoded.features,
|
|
115
|
+
paths: decoded.paths,
|
|
112
116
|
routes: decoded.routes,
|
|
113
117
|
});
|
|
114
118
|
}],
|
|
@@ -319,6 +323,11 @@ module.exports = (args, cbk) => {
|
|
|
319
323
|
return cbk([501, 'StrictMaxFeeNotSupportedWithMultiPathProbes']);
|
|
320
324
|
}
|
|
321
325
|
|
|
326
|
+
// Multi-path probing looks for paths to a destination, not to paths
|
|
327
|
+
if (!!decodeRequest.paths) {
|
|
328
|
+
return cbk([501, 'MultiPathProbeNotSupportedWithBlindedPaths']);
|
|
329
|
+
}
|
|
330
|
+
|
|
322
331
|
// Exit with error when the backing LND is below 0.10.0
|
|
323
332
|
if (!!isLegacy) {
|
|
324
333
|
return cbk([501, 'BackingLndDoesNotSupportMultiPathPayments']);
|
|
@@ -433,7 +442,18 @@ module.exports = (args, cbk) => {
|
|
|
433
442
|
|
|
434
443
|
return cbk();
|
|
435
444
|
}],
|
|
445
|
+
|
|
446
|
+
// Final probe result
|
|
447
|
+
result: ['checkFee', 'probe', ({probe}, cbk) => {
|
|
448
|
+
// Exit early when the probe was not through a blinded path
|
|
449
|
+
if (!probe || probe.blinded_path_fee === undefined) {
|
|
450
|
+
return cbk(null, probe);
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
// The fee of a blinded path probe is a total that includes the path fee
|
|
454
|
+
return cbk(null, withTotalFee(probe));
|
|
455
|
+
}],
|
|
436
456
|
},
|
|
437
|
-
returnResult({reject, resolve, of: '
|
|
457
|
+
returnResult({reject, resolve, of: 'result'}, cbk));
|
|
438
458
|
});
|
|
439
459
|
};
|
|
@@ -2,6 +2,7 @@ const {createHash} = require('crypto');
|
|
|
2
2
|
const {randomBytes} = require('crypto');
|
|
3
3
|
|
|
4
4
|
const asyncAuto = require('async/auto');
|
|
5
|
+
const asyncMap = require('async/map');
|
|
5
6
|
const {decodePaymentRequest} = require('ln-service');
|
|
6
7
|
const {getChannels} = require('ln-service');
|
|
7
8
|
const {getIdentity} = require('ln-service');
|
|
@@ -14,10 +15,12 @@ const {returnResult} = require('asyncjs-util');
|
|
|
14
15
|
const {signBytes} = require('ln-service');
|
|
15
16
|
const {subscribeToFindMaxPayable} = require('probing');
|
|
16
17
|
|
|
18
|
+
const blindedPathFee = require('./blinded_path_fee');
|
|
17
19
|
const executeProbe = require('./execute_probe');
|
|
18
20
|
const {getIcons} = require('./../display');
|
|
19
21
|
const {sortBy} = require('./../arrays');
|
|
20
22
|
|
|
23
|
+
const blinded = 'blinded';
|
|
21
24
|
const bufFromHex = hex => Buffer.from(hex, 'hex');
|
|
22
25
|
const channelReserve = n => n.local_reserve || Math.floor(n.capacity * 0.01);
|
|
23
26
|
const cltvBuffer = 3;
|
|
@@ -33,14 +36,17 @@ const feeBuffer = fees => fees * 2;
|
|
|
33
36
|
const {floor} = Math;
|
|
34
37
|
const fromKeyType = '34349339';
|
|
35
38
|
const htlcOutputSize = 43;
|
|
39
|
+
const isBlindedHop = hop => !!hop.encrypted_data && !hop.path_key;
|
|
36
40
|
const keySendPreimageType = '5482373484';
|
|
37
41
|
const makeNonce = () => randomBytes(32).toString('hex');
|
|
38
42
|
const {max} = Math;
|
|
39
43
|
const messageType = '34349334';
|
|
40
44
|
const {min} = Math;
|
|
45
|
+
const mtokensAsTokens = mtokens => Number(BigInt(mtokens) / BigInt(1e3));
|
|
41
46
|
const nodeKeyFamily = 6;
|
|
42
47
|
const preimageByteLength = 32;
|
|
43
48
|
const {now} = Date;
|
|
49
|
+
const pathChannel = hop => isBlindedHop(hop) ? blinded : hop.channel;
|
|
44
50
|
const rate = n => n.commit_transaction_fee / (n.commit_transaction_weight / 4);
|
|
45
51
|
const signatureType = '34349337';
|
|
46
52
|
const tokAsMtok = tokens => (BigInt(tokens || 0) * BigInt(1e3)).toString();
|
|
@@ -173,15 +179,17 @@ module.exports = (args, cbk) => {
|
|
|
173
179
|
features: details.features,
|
|
174
180
|
id: details.id,
|
|
175
181
|
mtokens: tokAsMtok(args.tokens),
|
|
182
|
+
paths: details.paths,
|
|
176
183
|
payment: details.payment,
|
|
177
184
|
routes: details.routes,
|
|
178
185
|
tokens: args.tokens,
|
|
179
186
|
});
|
|
180
187
|
}
|
|
181
188
|
|
|
189
|
+
// The destination of a blinded paths request is not known
|
|
182
190
|
args.logger.info({
|
|
183
191
|
description: details.description || undefined,
|
|
184
|
-
destination: details.destination,
|
|
192
|
+
destination: !details.paths ? details.destination : blinded,
|
|
185
193
|
expires: moment(details.expires_at).fromNow(),
|
|
186
194
|
id: details.id,
|
|
187
195
|
tokens: details.tokens,
|
|
@@ -193,6 +201,7 @@ module.exports = (args, cbk) => {
|
|
|
193
201
|
features: details.features,
|
|
194
202
|
id: details.id,
|
|
195
203
|
mtokens: details.mtokens || '0',
|
|
204
|
+
paths: details.paths,
|
|
196
205
|
payment: details.payment,
|
|
197
206
|
routes: details.routes,
|
|
198
207
|
tokens: details.tokens,
|
|
@@ -209,6 +218,11 @@ module.exports = (args, cbk) => {
|
|
|
209
218
|
|
|
210
219
|
// Lookup node destination details
|
|
211
220
|
getDestinationNode: ['to', ({to}, cbk) => {
|
|
221
|
+
// Exit early when the destination is hidden behind blinded paths
|
|
222
|
+
if (!!to.paths) {
|
|
223
|
+
return cbk(null, {alias: String()});
|
|
224
|
+
}
|
|
225
|
+
|
|
212
226
|
return getNode({
|
|
213
227
|
is_omitting_channels: true,
|
|
214
228
|
lnd: args.lnd,
|
|
@@ -224,6 +238,33 @@ module.exports = (args, cbk) => {
|
|
|
224
238
|
});
|
|
225
239
|
}],
|
|
226
240
|
|
|
241
|
+
// Lookup the introduction nodes of blinded paths
|
|
242
|
+
getIntroductionNodes: ['to', ({to}, cbk) => {
|
|
243
|
+
// Exit early when there are no blinded paths
|
|
244
|
+
if (!to.paths) {
|
|
245
|
+
return cbk();
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
const ids = to.paths.map(n => n.introduction_node);
|
|
249
|
+
|
|
250
|
+
return asyncMap(ids, (id, cbk) => {
|
|
251
|
+
return getNode({
|
|
252
|
+
is_omitting_channels: true,
|
|
253
|
+
lnd: args.lnd,
|
|
254
|
+
public_key: id,
|
|
255
|
+
},
|
|
256
|
+
(err, res) => {
|
|
257
|
+
// Suppress errors when the node is not found
|
|
258
|
+
if (!!err) {
|
|
259
|
+
return cbk(null, id);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
return cbk(null, `${res.alias} ${id}`.trim());
|
|
263
|
+
});
|
|
264
|
+
},
|
|
265
|
+
cbk);
|
|
266
|
+
}],
|
|
267
|
+
|
|
227
268
|
// For destinations that are peers, get features directly
|
|
228
269
|
getPeerFeatures: [
|
|
229
270
|
'getDestinationNode',
|
|
@@ -243,7 +284,7 @@ module.exports = (args, cbk) => {
|
|
|
243
284
|
const peer = res.peers.find(n => n.public_key === to.destination);
|
|
244
285
|
|
|
245
286
|
// Exit early when there are no peer features
|
|
246
|
-
if (!peer || !
|
|
287
|
+
if (!peer || !peer.features.length) {
|
|
247
288
|
return cbk();
|
|
248
289
|
}
|
|
249
290
|
|
|
@@ -277,8 +318,24 @@ module.exports = (args, cbk) => {
|
|
|
277
318
|
return cbk(null, {features});
|
|
278
319
|
}],
|
|
279
320
|
|
|
321
|
+
// Check that messages can be attached to the payment
|
|
322
|
+
checkMessages: ['to', ({to}, cbk) => {
|
|
323
|
+
// Exit early when there are no messages to attach
|
|
324
|
+
if (!args.message && !args.messages) {
|
|
325
|
+
return cbk();
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// Blinded path hops only accept encrypted data in their payloads
|
|
329
|
+
if (!!to.paths) {
|
|
330
|
+
return cbk([501, 'MessagesNotSupportedWithBlindedPaths']);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
return cbk();
|
|
334
|
+
}],
|
|
335
|
+
|
|
280
336
|
// Determine messages to attach
|
|
281
337
|
messages: [
|
|
338
|
+
'checkMessages',
|
|
282
339
|
'getFeatures',
|
|
283
340
|
'getIdentity',
|
|
284
341
|
'to',
|
|
@@ -379,9 +436,37 @@ module.exports = (args, cbk) => {
|
|
|
379
436
|
checkPath: [
|
|
380
437
|
'getDestinationNode',
|
|
381
438
|
'getIdentity',
|
|
439
|
+
'getIntroductionNodes',
|
|
382
440
|
'to',
|
|
383
|
-
({getDestinationNode, getIdentity, to}, cbk) =>
|
|
441
|
+
({getDestinationNode, getIdentity, getIntroductionNodes, to}, cbk) =>
|
|
384
442
|
{
|
|
443
|
+
// Exit early when the path is found to a blinded path introduction node
|
|
444
|
+
if (!!to.paths) {
|
|
445
|
+
const [node] = getIntroductionNodes;
|
|
446
|
+
const isSingle = getIntroductionNodes.length === [node].length;
|
|
447
|
+
|
|
448
|
+
// The probe amount is what the paths must deliver to the destination
|
|
449
|
+
const mtokens = !BigInt(to.mtokens) ? tokAsMtok(defaultTokens) :
|
|
450
|
+
to.mtokens;
|
|
451
|
+
|
|
452
|
+
// The fee of a blinded path is what relaying through it costs
|
|
453
|
+
const fees = to.paths.map(path => {
|
|
454
|
+
return mtokensAsTokens(blindedPathFee({mtokens, path}).fee_mtokens);
|
|
455
|
+
});
|
|
456
|
+
|
|
457
|
+
const [fee] = fees;
|
|
458
|
+
|
|
459
|
+
const nodes = getIntroductionNodes;
|
|
460
|
+
|
|
461
|
+
// A request may have a single path or multiple paths to choose from
|
|
462
|
+
args.logger.info({
|
|
463
|
+
blinded_path_fee: isSingle ? fee : fees,
|
|
464
|
+
checking_for_path_to_introduction: isSingle ? node : nodes,
|
|
465
|
+
});
|
|
466
|
+
|
|
467
|
+
return cbk();
|
|
468
|
+
}
|
|
469
|
+
|
|
385
470
|
const sendingTo = `${getDestinationNode.alias} ${to.destination}`;
|
|
386
471
|
|
|
387
472
|
if (to.destination === getIdentity.public_key) {
|
|
@@ -406,7 +491,7 @@ module.exports = (args, cbk) => {
|
|
|
406
491
|
return executeProbe({
|
|
407
492
|
messages,
|
|
408
493
|
cltv_delta: (to.cltv_delta || defaultCltvDelta) + cltvBuffer,
|
|
409
|
-
destination: to.destination,
|
|
494
|
+
destination: !to.paths ? to.destination : undefined,
|
|
410
495
|
features: getFeatures.features,
|
|
411
496
|
ignore: args.ignore,
|
|
412
497
|
in_through: args.in_through,
|
|
@@ -418,6 +503,7 @@ module.exports = (args, cbk) => {
|
|
|
418
503
|
max_fee_mtokens: args.max_fee_mtokens,
|
|
419
504
|
mtokens: !BigInt(to.mtokens) ? tokAsMtok(defaultTokens) : to.mtokens,
|
|
420
505
|
outgoing_channel: !!outId ? outId.id : undefined,
|
|
506
|
+
paths: to.paths || undefined,
|
|
421
507
|
payment: to.payment,
|
|
422
508
|
routes: to.routes,
|
|
423
509
|
tagged: !!getIcons ? getIcons.nodes : undefined,
|
|
@@ -433,6 +519,11 @@ module.exports = (args, cbk) => {
|
|
|
433
519
|
return cbk(null, {});
|
|
434
520
|
}
|
|
435
521
|
|
|
522
|
+
// Finding the maximum needs to probe the hops which are hidden in paths
|
|
523
|
+
if (!!to.paths) {
|
|
524
|
+
return cbk([501, 'FindMaxNotSupportedWithBlindedPaths']);
|
|
525
|
+
}
|
|
526
|
+
|
|
436
527
|
const sub = subscribeToFindMaxPayable({
|
|
437
528
|
cltv: (to.cltv_delta || defaultCltvDelta) + cltvBuffer,
|
|
438
529
|
hops: probe.route.hops,
|
|
@@ -476,9 +567,7 @@ module.exports = (args, cbk) => {
|
|
|
476
567
|
return cbk([400, 'MaxFeeTooLow', {required_fee: probe.route.fee}]);
|
|
477
568
|
}
|
|
478
569
|
|
|
479
|
-
args.logger.info({
|
|
480
|
-
paying: probe.route.hops.map(({channel}) => channel),
|
|
481
|
-
});
|
|
570
|
+
args.logger.info({paying: probe.route.hops.map(pathChannel)});
|
|
482
571
|
|
|
483
572
|
return payViaRoutes({
|
|
484
573
|
id: to.id,
|
|
@@ -499,7 +588,11 @@ module.exports = (args, cbk) => {
|
|
|
499
588
|
|
|
500
589
|
const {route} = probe;
|
|
501
590
|
|
|
591
|
+
// The introduction node hop of a blinded path takes the path fee
|
|
592
|
+
const introduction = route.hops.find(hop => !!hop.path_key);
|
|
593
|
+
|
|
502
594
|
return cbk(null, {
|
|
595
|
+
blinded_path_fee: !introduction ? undefined : introduction.fee,
|
|
503
596
|
fee: !route ? undefined : route.fee,
|
|
504
597
|
id: !pay ? undefined : pay.id,
|
|
505
598
|
latency_ms: !route ? undefined : probe.latency_ms,
|
|
@@ -508,7 +601,7 @@ module.exports = (args, cbk) => {
|
|
|
508
601
|
preimage: !pay ? undefined : pay.secret,
|
|
509
602
|
probed: !!pay ? undefined : route.tokens - route.fee,
|
|
510
603
|
relays: !route ? undefined : route.hops.map(n => n.public_key),
|
|
511
|
-
success: !route ? undefined : route.hops.map(
|
|
604
|
+
success: !route ? undefined : route.hops.map(pathChannel),
|
|
512
605
|
});
|
|
513
606
|
}],
|
|
514
607
|
},
|