balanceofsatoshis 24.0.1 → 24.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 +13 -0
- package/README.md +15 -18
- package/bos +22 -4
- package/chain/index.js +0 -2
- package/chain/recover_p2pk.js +85 -53
- package/commands/fetch_request.js +1 -3
- package/display/constants.json +6 -0
- package/display/format_duration.js +34 -0
- package/display/index.js +2 -0
- package/network/execute_probe.js +2 -2
- package/network/get_out_peers.js +235 -0
- package/network/multi_path_probe.js +1 -1
- package/network/pay.js +17 -17
- package/network/probe.js +15 -18
- package/network/probe_destination.js +26 -7
- package/network/push_payment.js +1 -1
- package/network/remove_peer.js +8 -5
- package/network/transfer_funds.js +2 -2
- package/package.json +6 -8
- package/peers/intercept_inbound_channels.js +1 -1
- package/routing/index.js +2 -0
- package/routing/manage_fees.js +129 -0
- package/services/open_balanced_channel.js +1 -1
- package/swaps/execute_rebalance.js +281 -0
- package/swaps/get_routes_for_funding.js +3 -1
- package/swaps/manage_rebalance.js +70 -178
- package/swaps/parse_interval_formula.js +72 -0
- package/swaps/rebalance.js +1 -1
- package/swaps/swap_api_key.js +1 -1
- package/swaps/swap_out.js +1 -1
- package/chain/networks.json +0 -7
- package/chain/output_script_for_address.js +0 -88
- package/chain/transaction_records.js +0 -183
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# Versions
|
|
2
2
|
|
|
3
|
+
## 24.2.0
|
|
4
|
+
|
|
5
|
+
- Add support for LND 0.21.4 and LND 0.20.5
|
|
6
|
+
- `pay`, `probe`: Add support for tags in `--out`, `--out-filter`
|
|
7
|
+
|
|
8
|
+
## 24.1.0
|
|
9
|
+
|
|
10
|
+
- `fees`: Add `--repeat-interval-ms` to show or set the fees again each time an
|
|
11
|
+
interval elapses, such as `5*min` to keep formula fee rates up to date
|
|
12
|
+
- `rebalance`: Add `--repeat-interval-ms` to run the rebalance again each time
|
|
13
|
+
an interval elapses, such as `30*min`, `1*h * (0.5 + RAND())` for a random
|
|
14
|
+
interval, or `1*min * 2^FAILURES_COUNT` to back off on failures in a row
|
|
15
|
+
|
|
3
16
|
## 24.0.1
|
|
4
17
|
|
|
5
18
|
### Breaking Changes
|
package/README.md
CHANGED
|
@@ -7,8 +7,8 @@ Commands for working with LND balances.
|
|
|
7
7
|
|
|
8
8
|
Supported LND versions:
|
|
9
9
|
|
|
10
|
-
- v0.21.0-beta to v0.21.
|
|
11
|
-
- v0.20.0-beta to v0.20.
|
|
10
|
+
- v0.21.0-beta to v0.21.4-beta
|
|
11
|
+
- v0.20.0-beta to v0.20.5-beta
|
|
12
12
|
|
|
13
13
|
## Install
|
|
14
14
|
|
|
@@ -327,18 +327,17 @@ expr $(bos balance --node=savedNode1) + $(bos balance --node=savedNode2)
|
|
|
327
327
|
|
|
328
328
|
### Auto Adjust Fees
|
|
329
329
|
|
|
330
|
-
|
|
331
|
-
# Cron every 5 minutes adjust fees
|
|
332
|
-
*/5 * * * * /bin/timeout -s 2 30 /home/ubuntu/update-fees.sh
|
|
333
|
-
```
|
|
334
|
-
|
|
335
|
-
update-fees.sh:
|
|
330
|
+
Repeat the `fees` command on an interval to keep a fee rate formula up to date
|
|
336
331
|
|
|
332
|
+
```shell
|
|
333
|
+
# Every 5 minutes, raise the outbound fees to a public key when inbound increases
|
|
334
|
+
bos fees --to PUBLIC_KEY --set-fee-rate="IF(INBOUND>10000000,1000,500)" --repeat-interval-ms="5*min"
|
|
337
335
|
```
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
336
|
+
|
|
337
|
+
The interval is a formula in milliseconds that can use `s`, `min`, `h`, `d` and
|
|
338
|
+
`w` units, so `"5*min * (0.5 + RAND())"` repeats at a random interval between
|
|
339
|
+
2.5 and 7.5 minutes. See "Persist Long-Running Commands" below to keep the
|
|
340
|
+
command running.
|
|
342
341
|
|
|
343
342
|
### Auto Balance Liquidity Between Two Nodes
|
|
344
343
|
|
|
@@ -431,13 +430,11 @@ Docker example:
|
|
|
431
430
|
docker run -d --restart always -v $HOME/.bos:/home/node/.bos alexbosworth/balanceofsatoshis telegram --connect CONNECT_CODE
|
|
432
431
|
```
|
|
433
432
|
|
|
434
|
-
|
|
433
|
+
Commands like `fees` and `rebalance` can also repeat themselves, with a delay
|
|
435
434
|
|
|
436
|
-
```
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
sleep 2000;
|
|
440
|
-
done
|
|
435
|
+
```shell
|
|
436
|
+
# Rebalance again 30 minutes after each attempt, or 1 hour when it failed
|
|
437
|
+
bos rebalance --repeat-interval-ms="IF(FAILURES_COUNT > 0, 1*h, 30*min)"
|
|
441
438
|
```
|
|
442
439
|
|
|
443
440
|
## Docker
|
package/bos
CHANGED
|
@@ -14,7 +14,6 @@ const {writeFile} = require('fs');
|
|
|
14
14
|
const importLazy = require('import-lazy')(require);
|
|
15
15
|
|
|
16
16
|
const accounting = importLazy('ln-accounting');
|
|
17
|
-
const fetch = importLazy('@alexbosworth/node-fetch');
|
|
18
17
|
const fiat = importLazy('@alexbosworth/fiat');
|
|
19
18
|
const lnService = importLazy('ln-service');
|
|
20
19
|
const lnSync = importLazy('ln-sync');
|
|
@@ -731,7 +730,9 @@ prog
|
|
|
731
730
|
.help('You can use INBOUND_FEE_RATE to mirror an inbound fee')
|
|
732
731
|
.help('You can use FEE_RATE_OF_<PUBKEY> to reference other node rates')
|
|
733
732
|
.help('--set-inbound-discount-rate is total discount on traffic from peer')
|
|
733
|
+
.help('--repeat-interval-ms vars: s, min, h, d, w: 1*h repeats hourly')
|
|
734
734
|
.option('--node <node_name>', 'Saved node (not peer to set fees on)')
|
|
735
|
+
.option('--repeat-interval-ms <interval>', 'Repeat fees after interval')
|
|
735
736
|
.option('--set-cltv-delta <count>', 'Set the number of blocks for CLTV', INT)
|
|
736
737
|
.option('--set-fee-rate <rate>', 'Fee in parts per million or use a formula')
|
|
737
738
|
.option('--set-inbound-rate-discount <rate>', 'Source discount rate in PPM')
|
|
@@ -739,13 +740,14 @@ prog
|
|
|
739
740
|
.action((args, options, logger) => {
|
|
740
741
|
return new Promise(async (resolve, reject) => {
|
|
741
742
|
try {
|
|
742
|
-
return routing.
|
|
743
|
+
return routing.manageFees({
|
|
743
744
|
logger,
|
|
744
745
|
cltv_delta: options.setCltvDelta,
|
|
745
746
|
fee_rate: options.setFeeRate,
|
|
746
747
|
fs: {getFile: readFile},
|
|
747
748
|
inbound_rate_discount: options.setInboundRateDiscount,
|
|
748
749
|
lnd: (await lnd.authenticatedLnd({logger, node: options.node})).lnd,
|
|
750
|
+
repeat_interval_ms: options.repeatIntervalMs || undefined,
|
|
749
751
|
to: flatten([options.to].filter(n => !!n)),
|
|
750
752
|
},
|
|
751
753
|
responses.returnObject({logger, reject, resolve, table: 'rows'}));
|
|
@@ -1514,6 +1516,10 @@ prog
|
|
|
1514
1516
|
// Pay a payment request, probing first
|
|
1515
1517
|
.command('pay', 'Pay a payment request, probing first')
|
|
1516
1518
|
.help('Blinded path requests are probed to the introduction node, 1 path')
|
|
1519
|
+
.help('--out can take a tag to make the first hop through any tagged peer')
|
|
1520
|
+
.help('--out-filter vars: CAPACITY/HEIGHTS/INBOUND_LIQUIDITY')
|
|
1521
|
+
.help('--out-filter vars: OUTBOUND_LIQUIDITY/PENDING_PAYMENTS')
|
|
1522
|
+
.help('--out-filter vars: INBOUND_BASE_FEE/INBOUND_FEE_RATE')
|
|
1517
1523
|
.argument('<request>', 'Payment Request')
|
|
1518
1524
|
.option('--avoid <avoid>', 'Avoid forwarding via node/chan/tag', REPEATABLE)
|
|
1519
1525
|
.option('--avoid-high-fee-routes', 'Avoid evaluating too-high fee routes')
|
|
@@ -1523,7 +1529,8 @@ prog
|
|
|
1523
1529
|
.option('--message <message>', 'Attach text message to payment')
|
|
1524
1530
|
.option('--no-color', 'Mute all colors')
|
|
1525
1531
|
.option('--node <node_name>', 'Node to use to pay payment request')
|
|
1526
|
-
.option('--out <
|
|
1532
|
+
.option('--out <peer_or_tag>', 'Make first hop through peer/tag', REPEATABLE)
|
|
1533
|
+
.option('--out-filter <out_filter>', 'Filter outbound peer/tag', REPEATABLE)
|
|
1527
1534
|
.action((args, options, logger) => {
|
|
1528
1535
|
return new Promise(async (resolve, reject) => {
|
|
1529
1536
|
try {
|
|
@@ -1542,6 +1549,7 @@ prog
|
|
|
1542
1549
|
max_paths: options.maxPaths,
|
|
1543
1550
|
message: options.message,
|
|
1544
1551
|
out: flatten([options.out].filter(n => !!n)),
|
|
1552
|
+
out_filters: flatten([options.outFilter].filter(n => !!n)),
|
|
1545
1553
|
request: args.request,
|
|
1546
1554
|
},
|
|
1547
1555
|
responses.returnObject({logger, reject, resolve}));
|
|
@@ -1634,6 +1642,10 @@ prog
|
|
|
1634
1642
|
.command('probe', 'Check if a payment request is sendable')
|
|
1635
1643
|
.help('Simulate paying a payment request without actually paying it')
|
|
1636
1644
|
.help('Blinded path requests are probed to the introduction node, 1 path')
|
|
1645
|
+
.help('--out can take a tag to make the first hop through any tagged peer')
|
|
1646
|
+
.help('--out-filter vars: CAPACITY/HEIGHTS/INBOUND_LIQUIDITY')
|
|
1647
|
+
.help('--out-filter vars: OUTBOUND_LIQUIDITY/PENDING_PAYMENTS')
|
|
1648
|
+
.help('--out-filter vars: INBOUND_BASE_FEE/INBOUND_FEE_RATE')
|
|
1637
1649
|
.argument('<to>', 'Payment request or node public key')
|
|
1638
1650
|
.argument('[amount]', 'Amount to probe, default: request amount')
|
|
1639
1651
|
.option('--avoid <avoid>', 'Avoid forwarding via node/chan/tag', REPEATABLE)
|
|
@@ -1644,7 +1656,8 @@ prog
|
|
|
1644
1656
|
.option('--max-paths <max>', 'Maximum paths to use for find-max', INT, 1)
|
|
1645
1657
|
.option('--no-color', 'Mute all colors')
|
|
1646
1658
|
.option('--node <node_name>', 'Node to use for payment request check')
|
|
1647
|
-
.option('--out <
|
|
1659
|
+
.option('--out <peer_or_tag>', 'Make first hop through peer/tag', REPEATABLE)
|
|
1660
|
+
.option('--out-filter <out_filter>', 'Filter outbound peer/tag', REPEATABLE)
|
|
1648
1661
|
.action((args, options, logger) => {
|
|
1649
1662
|
return new Promise(async (resolve, reject) => {
|
|
1650
1663
|
try {
|
|
@@ -1664,6 +1677,7 @@ prog
|
|
|
1664
1677
|
max_fee: options.maxFee,
|
|
1665
1678
|
max_paths: options.maxPaths,
|
|
1666
1679
|
out: flatten([options.out].filter(n => !!n)),
|
|
1680
|
+
out_filters: flatten([options.outFilter].filter(n => !!n)),
|
|
1667
1681
|
request: args.to.length !== 66 ? args.to : undefined,
|
|
1668
1682
|
tokens: args.amount || undefined,
|
|
1669
1683
|
},
|
|
@@ -1693,6 +1707,8 @@ prog
|
|
|
1693
1707
|
.help('--in-filter/--out-filter vars: OUTBOUND_LIQUIDITY/PENDING_PAYMENTS')
|
|
1694
1708
|
.help('--in-filter/--out-filter vars: INBOUND_BASE_FEE/INBOUND_FEE_RATE')
|
|
1695
1709
|
.help('--out increases the inbound liquidity with a specific peer/tag')
|
|
1710
|
+
.help('--repeat-interval-ms vars: s, min, h, d, w: 1*h repeats hourly')
|
|
1711
|
+
.help('--repeat-interval-ms var FAILURES_COUNT: count of failures in a row')
|
|
1696
1712
|
.option('--amount <amount>', 'Maximum amount to rebalance')
|
|
1697
1713
|
.option('--avoid <pubkey_or_chanid>', 'Avoid forwarding through', REPEATABLE)
|
|
1698
1714
|
.option('--avoid-append', 'Formula for appending failing edges to list')
|
|
@@ -1709,6 +1725,7 @@ prog
|
|
|
1709
1725
|
.option('--out <pubkey_or_alias>', 'Route out through a specific peer')
|
|
1710
1726
|
.option('--out-filter <out_filter>', 'Filter outbound peer/tag', REPEATABLE)
|
|
1711
1727
|
.option('--out-target-inbound <amount>', 'Balance up to inbound amount')
|
|
1728
|
+
.option('--repeat-interval-ms <interval>', 'Repeat rebalance after interval')
|
|
1712
1729
|
.action((args, options, logger) => {
|
|
1713
1730
|
return new Promise(async (resolve, reject) => {
|
|
1714
1731
|
try {
|
|
@@ -1729,6 +1746,7 @@ prog
|
|
|
1729
1746
|
out_filters: flatten([options.outFilter].filter(n => !!n)),
|
|
1730
1747
|
out_inbound: options.outTargetInbound,
|
|
1731
1748
|
out_through: options.out || undefined,
|
|
1749
|
+
repeat_interval_ms: options.repeatIntervalMs || undefined,
|
|
1732
1750
|
timeout_minutes: options.minutes || undefined,
|
|
1733
1751
|
},
|
|
1734
1752
|
responses.returnObject({exit, logger, reject, resolve}));
|
package/chain/index.js
CHANGED
|
@@ -6,7 +6,6 @@ const getDepositAddress = require('./get_deposit_address');
|
|
|
6
6
|
const getMempoolSize = require('./get_mempool_size');
|
|
7
7
|
const getRawTransaction = require('./get_raw_transaction');
|
|
8
8
|
const getUtxos = require('./get_utxos');
|
|
9
|
-
const outputScriptForAddress = require('./output_script_for_address');
|
|
10
9
|
const recoverP2pk = require('./recover_p2pk');
|
|
11
10
|
const splitUtxos = require('./split_utxos');
|
|
12
11
|
|
|
@@ -19,7 +18,6 @@ module.exports = {
|
|
|
19
18
|
getMempoolSize,
|
|
20
19
|
getRawTransaction,
|
|
21
20
|
getUtxos,
|
|
22
|
-
outputScriptForAddress,
|
|
23
21
|
recoverP2pk,
|
|
24
22
|
splitUtxos,
|
|
25
23
|
};
|
package/chain/recover_p2pk.js
CHANGED
|
@@ -1,40 +1,39 @@
|
|
|
1
1
|
const {createHash} = require('crypto');
|
|
2
2
|
|
|
3
3
|
const asyncAuto = require('async/auto');
|
|
4
|
-
const bitcoinjsLib = require('bitcoinjs-lib');
|
|
5
4
|
const {broadcastChainTransaction} = require('ln-service');
|
|
5
|
+
const {componentsOfTransaction} = require('@alexbosworth/blockchain');
|
|
6
6
|
const {createChainAddress} = require('ln-service');
|
|
7
|
-
const {decode} = require('bip66');
|
|
8
7
|
const {getChainFeeRate} = require('ln-service');
|
|
9
8
|
const {getIdentity} = require('ln-service');
|
|
10
9
|
const {getNetwork} = require('ln-sync');
|
|
10
|
+
const {idForTransaction} = require('@alexbosworth/blockchain');
|
|
11
|
+
const {outputScriptForAddress} = require('@alexbosworth/blockchain');
|
|
11
12
|
const {returnResult} = require('asyncjs-util');
|
|
13
|
+
const {scriptElementsAsScript} = require('@alexbosworth/blockchain');
|
|
12
14
|
const {signBytes} = require('ln-service');
|
|
15
|
+
const {sizeOfTransaction} = require('@alexbosworth/blockchain');
|
|
16
|
+
const {transactionFromComponents} = require('@alexbosworth/blockchain');
|
|
13
17
|
|
|
14
18
|
const getRawTransaction = require('./get_raw_transaction');
|
|
15
19
|
|
|
16
20
|
const {ceil} = Math;
|
|
17
|
-
const {compile} = bitcoinjsLib.script;
|
|
18
21
|
const {concat} = Buffer;
|
|
19
22
|
const description = 'bos recover p2pk node identity key funds';
|
|
20
|
-
const {encode} = bitcoinjsLib.script.signature;
|
|
21
23
|
const estimatedSignatureSize = 73;
|
|
22
24
|
const format = 'p2wpkh';
|
|
23
|
-
const {fromHex} = bitcoinjsLib.Transaction;
|
|
24
25
|
const hashFlag = Buffer.from('01000000', 'hex');
|
|
25
26
|
const hexAsBuffer = hex => Buffer.from(hex, 'hex');
|
|
26
|
-
const inputIndex = 0;
|
|
27
27
|
const inputSequence = 0;
|
|
28
28
|
const isHash = n => !!n && /^[0-9A-F]{64}$/i.test(n);
|
|
29
|
-
const
|
|
30
|
-
const {networks} = bitcoinjsLib;
|
|
29
|
+
const locktime = 0;
|
|
31
30
|
const nodeIdentityKeyFamily = 6;
|
|
32
31
|
const nodeIdentityKeyIndex = 0;
|
|
33
32
|
const OP_CHECKSIG = 172;
|
|
34
33
|
const sha256 = n => createHash('sha256').update(n).digest().toString('hex');
|
|
35
|
-
const
|
|
36
|
-
const
|
|
37
|
-
const
|
|
34
|
+
const sigHashType = Buffer.from('01', 'hex');
|
|
35
|
+
const unsignedScript = '';
|
|
36
|
+
const version = 1;
|
|
38
37
|
|
|
39
38
|
/** Recover funds sent to a P2PK using the node identity key
|
|
40
39
|
|
|
@@ -97,14 +96,16 @@ module.exports = ({id, lnd, request, vout}, cbk) => {
|
|
|
97
96
|
|
|
98
97
|
// Derive the transaction details
|
|
99
98
|
output: ['getIdentity', 'getTx', ({getIdentity, getTx}, cbk) => {
|
|
100
|
-
const
|
|
99
|
+
const {transaction} = getTx;
|
|
101
100
|
|
|
102
101
|
// Make sure the tx data matches the input id
|
|
103
|
-
if (
|
|
102
|
+
if (idForTransaction({transaction}).id !== id) {
|
|
104
103
|
return cbk([503, 'ExpectedTransactionIdToMatchTxDataHashToRecover']);
|
|
105
104
|
}
|
|
106
105
|
|
|
107
|
-
const
|
|
106
|
+
const {outputs} = componentsOfTransaction({transaction});
|
|
107
|
+
|
|
108
|
+
const output = outputs[vout];
|
|
108
109
|
|
|
109
110
|
// Make sure the output exists
|
|
110
111
|
if (!output) {
|
|
@@ -113,14 +114,16 @@ module.exports = ({id, lnd, request, vout}, cbk) => {
|
|
|
113
114
|
|
|
114
115
|
const identityKey = hexAsBuffer(getIdentity.public_key);
|
|
115
116
|
|
|
116
|
-
const
|
|
117
|
+
const expected = scriptElementsAsScript({
|
|
118
|
+
elements: [identityKey, OP_CHECKSIG],
|
|
119
|
+
});
|
|
117
120
|
|
|
118
121
|
// Make sure that the output pays to the node identity key
|
|
119
|
-
if (
|
|
122
|
+
if (output.script !== expected.script) {
|
|
120
123
|
return cbk([503, 'ExpectedOutputPayingToNodeIdentityPublicKey']);
|
|
121
124
|
}
|
|
122
125
|
|
|
123
|
-
return cbk(null, {script: output.script, tokens: output.
|
|
126
|
+
return cbk(null, {script: output.script, tokens: output.tokens});
|
|
124
127
|
}],
|
|
125
128
|
|
|
126
129
|
// Create a recovery address
|
|
@@ -128,45 +131,73 @@ module.exports = ({id, lnd, request, vout}, cbk) => {
|
|
|
128
131
|
return createChainAddress({format, lnd}, cbk);
|
|
129
132
|
}],
|
|
130
133
|
|
|
131
|
-
//
|
|
132
|
-
|
|
134
|
+
// Derive the output script of the recovery address
|
|
135
|
+
recoveryScript: [
|
|
133
136
|
'createAddress',
|
|
134
|
-
'getFee',
|
|
135
137
|
'getNetwork',
|
|
136
|
-
|
|
137
|
-
({createAddress, getFee, getNetwork, output}, cbk) =>
|
|
138
|
+
({createAddress, getNetwork}, cbk) =>
|
|
138
139
|
{
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
140
|
+
try {
|
|
141
|
+
const {script} = outputScriptForAddress({
|
|
142
|
+
address: createAddress.address,
|
|
143
|
+
network: getNetwork.network,
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
return cbk(null, {script});
|
|
147
|
+
} catch (err) {
|
|
148
|
+
return cbk([503, 'FailedToParseRecoveryAddress', {err}]);
|
|
149
|
+
}
|
|
150
|
+
}],
|
|
143
151
|
|
|
144
|
-
|
|
145
|
-
|
|
152
|
+
// Derive the sweep output, less the amount needed to pay for chain fees
|
|
153
|
+
sweep: [
|
|
154
|
+
'getFee',
|
|
155
|
+
'output',
|
|
156
|
+
'recoveryScript',
|
|
157
|
+
({getFee, output, recoveryScript}, cbk) =>
|
|
158
|
+
{
|
|
159
|
+
const {script} = recoveryScript;
|
|
160
|
+
|
|
161
|
+
const unsigned = transactionFromComponents({
|
|
162
|
+
locktime,
|
|
163
|
+
version,
|
|
164
|
+
inputs: [{
|
|
165
|
+
id,
|
|
166
|
+
vout,
|
|
167
|
+
script: unsignedScript,
|
|
168
|
+
sequence: inputSequence,
|
|
169
|
+
}],
|
|
170
|
+
outputs: [{script, tokens: output.tokens}],
|
|
171
|
+
});
|
|
146
172
|
|
|
147
|
-
|
|
148
|
-
const [out] = tx.outs;
|
|
173
|
+
const {vsize} = sizeOfTransaction(unsigned);
|
|
149
174
|
|
|
150
175
|
// Include the prospective signature in the tx total weight
|
|
151
|
-
const vbytes =
|
|
176
|
+
const vbytes = vsize + estimatedSignatureSize;
|
|
152
177
|
|
|
153
178
|
// Reduce the sweep value by the amount needed to pay for chain fees
|
|
154
|
-
|
|
179
|
+
const tokens = output.tokens - ceil(vbytes * getFee.tokens_per_vbyte);
|
|
155
180
|
|
|
156
|
-
return cbk(null,
|
|
181
|
+
return cbk(null, {script, tokens});
|
|
157
182
|
}],
|
|
158
183
|
|
|
159
184
|
// Derive the preimage to use for signing
|
|
160
|
-
preimage: ['output', '
|
|
161
|
-
const cloneTx = txToSign.clone();
|
|
162
|
-
|
|
163
|
-
const [input] = cloneTx.ins;
|
|
164
|
-
|
|
185
|
+
preimage: ['output', 'sweep', ({output, sweep}, cbk) => {
|
|
165
186
|
// When signing, the input to sign is set to the previous output script
|
|
166
|
-
|
|
187
|
+
const {transaction} = transactionFromComponents({
|
|
188
|
+
locktime,
|
|
189
|
+
version,
|
|
190
|
+
inputs: [{
|
|
191
|
+
id,
|
|
192
|
+
vout,
|
|
193
|
+
script: output.script,
|
|
194
|
+
sequence: inputSequence,
|
|
195
|
+
}],
|
|
196
|
+
outputs: [sweep],
|
|
197
|
+
});
|
|
167
198
|
|
|
168
199
|
// The bytes to sign are the tx itself plus the signature hash flag
|
|
169
|
-
return cbk(null, concat([
|
|
200
|
+
return cbk(null, concat([hexAsBuffer(transaction), hashFlag]));
|
|
170
201
|
}],
|
|
171
202
|
|
|
172
203
|
// Give the preimage to signer, hashed once - signBytes does 2nd SHA hash
|
|
@@ -181,17 +212,18 @@ module.exports = ({id, lnd, request, vout}, cbk) => {
|
|
|
181
212
|
}],
|
|
182
213
|
|
|
183
214
|
// Put together the signature with the transaction
|
|
184
|
-
signedTx: ['getSig', '
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
215
|
+
signedTx: ['getSig', 'sweep', ({getSig, sweep}, cbk) => {
|
|
216
|
+
// A chain signature is the DER signature followed by the sighash type
|
|
217
|
+
const signature = concat([hexAsBuffer(getSig.signature), sigHashType]);
|
|
218
|
+
|
|
219
|
+
const {script} = scriptElementsAsScript({elements: [signature]});
|
|
220
|
+
|
|
221
|
+
return cbk(null, transactionFromComponents({
|
|
222
|
+
locktime,
|
|
223
|
+
version,
|
|
224
|
+
inputs: [{id, script, vout, sequence: inputSequence}],
|
|
225
|
+
outputs: [sweep],
|
|
226
|
+
}));
|
|
195
227
|
}],
|
|
196
228
|
|
|
197
229
|
// Broadcast the signed transaction
|
|
@@ -199,7 +231,7 @@ module.exports = ({id, lnd, request, vout}, cbk) => {
|
|
|
199
231
|
return broadcastChainTransaction({
|
|
200
232
|
description,
|
|
201
233
|
lnd,
|
|
202
|
-
transaction: signedTx.
|
|
234
|
+
transaction: signedTx.transaction,
|
|
203
235
|
},
|
|
204
236
|
cbk);
|
|
205
237
|
}],
|
|
@@ -214,7 +246,7 @@ module.exports = ({id, lnd, request, vout}, cbk) => {
|
|
|
214
246
|
return cbk(null, {
|
|
215
247
|
recovering: output.tokens,
|
|
216
248
|
recovering_to: createAddress.address,
|
|
217
|
-
transaction_id: signedTx.
|
|
249
|
+
transaction_id: idForTransaction(signedTx).id,
|
|
218
250
|
});
|
|
219
251
|
}],
|
|
220
252
|
},
|
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
const {stringify} = require('querystring');
|
|
2
2
|
|
|
3
|
-
const {AbortController} = require('abort-controller');
|
|
4
|
-
|
|
5
3
|
const encodeQs = qs => !qs ? '' : '?' + stringify(qs);
|
|
6
4
|
const timeoutSignals = new WeakMap();
|
|
7
5
|
|
|
8
6
|
/** Derive a request function that uses fetch to simulate request behavior
|
|
9
7
|
|
|
10
8
|
{
|
|
11
|
-
fetch: <
|
|
9
|
+
fetch: <Fetch Function>
|
|
12
10
|
}
|
|
13
11
|
|
|
14
12
|
@returns
|
package/display/constants.json
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
{
|
|
2
|
+
"durationUnits": [
|
|
3
|
+
{"ms": 86400000, "unit": "day"},
|
|
4
|
+
{"ms": 3600000, "unit": "hour"},
|
|
5
|
+
{"ms": 60000, "unit": "minute"},
|
|
6
|
+
{"ms": 1000, "unit": "second"}
|
|
7
|
+
],
|
|
2
8
|
"parseErrors": {
|
|
3
9
|
"#DIV/0!": "CannotDivideByZeroInFormula",
|
|
4
10
|
"#ERROR!": "FailedToParseFormula",
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
const {durationUnits} = require('./constants');
|
|
2
|
+
|
|
3
|
+
const {floor} = Math;
|
|
4
|
+
const maxUnits = 2;
|
|
5
|
+
const plural = (count, unit) => `${count} ${unit}${count === 1 ? '' : 's'}`;
|
|
6
|
+
|
|
7
|
+
/** Format a duration for display, using the two largest units of time
|
|
8
|
+
|
|
9
|
+
{
|
|
10
|
+
ms: <Duration Milliseconds Number>
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
@returns
|
|
14
|
+
{
|
|
15
|
+
display: <Display Formatted Duration String>
|
|
16
|
+
}
|
|
17
|
+
*/
|
|
18
|
+
module.exports = ({ms}) => {
|
|
19
|
+
// Take whole counts of each unit out of the duration, largest unit first
|
|
20
|
+
const {parts, remaining} = durationUnits.reduce((sum, unit) => {
|
|
21
|
+
const count = floor(sum.remaining / unit.ms);
|
|
22
|
+
|
|
23
|
+
return {
|
|
24
|
+
parts: !count ? sum.parts : sum.parts.concat(plural(count, unit.unit)),
|
|
25
|
+
remaining: sum.remaining - count * unit.ms,
|
|
26
|
+
};
|
|
27
|
+
},
|
|
28
|
+
{parts: [], remaining: ms});
|
|
29
|
+
|
|
30
|
+
// Leftover milliseconds are shown when present or when there is nothing else
|
|
31
|
+
const leftover = !remaining && !!parts.length ? [] : [`${remaining} ms`];
|
|
32
|
+
|
|
33
|
+
return {display: parts.concat(leftover).slice(0, maxUnits).join(' ')};
|
|
34
|
+
};
|
package/display/index.js
CHANGED
|
@@ -2,6 +2,7 @@ const chartAliasForPeer = require('./chart_alias_for_peer');
|
|
|
2
2
|
const describeConfidence = require('./describe_confidence');
|
|
3
3
|
const describeRoute = require('./describe_route');
|
|
4
4
|
const describeRoutingFailure = require('./describe_routing_failure');
|
|
5
|
+
const formatDuration = require('./format_duration');
|
|
5
6
|
const formatFeeRate = require('./format_fee_rate');
|
|
6
7
|
const getIcons = require('./get_icons');
|
|
7
8
|
const isMatchingFilters = require('./is_matching_filters');
|
|
@@ -13,6 +14,7 @@ module.exports = {
|
|
|
13
14
|
describeConfidence,
|
|
14
15
|
describeRoute,
|
|
15
16
|
describeRoutingFailure,
|
|
17
|
+
formatDuration,
|
|
16
18
|
formatFeeRate,
|
|
17
19
|
getIcons,
|
|
18
20
|
isMatchingFilters,
|
package/network/execute_probe.js
CHANGED
|
@@ -40,7 +40,7 @@ const tokensAsMillitokens = tok => (BigInt(tok) * BigInt(1e3)).toString();
|
|
|
40
40
|
type: <Message To Final Destination Type Number String>
|
|
41
41
|
value: <Message To Final Destination Raw Value Hex Encoded String>
|
|
42
42
|
}]
|
|
43
|
-
[
|
|
43
|
+
[outgoing_channels]: [<Outgoing Channel Id String>]
|
|
44
44
|
[paths]: [{
|
|
45
45
|
base_fee_mtokens: <Accumulated Base Fee Millitokens String>
|
|
46
46
|
cltv_delta: <Accumulated CLTV Expiry Delta Number>
|
|
@@ -176,7 +176,7 @@ module.exports = (args, cbk) => {
|
|
|
176
176
|
max_fee_mtokens: strictMaxFee || undefined,
|
|
177
177
|
max_timeout_height: args.max_timeout_height,
|
|
178
178
|
messages: args.messages,
|
|
179
|
-
|
|
179
|
+
outgoing_channels: args.outgoing_channels,
|
|
180
180
|
path_timeout_ms: pathTimeoutMs,
|
|
181
181
|
paths: args.paths,
|
|
182
182
|
payment: args.payment,
|