paid-services 4.1.0 → 4.2.1

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,5 +1,9 @@
1
1
  # Versions
2
2
 
3
+ ## Version 4.2.1
4
+
5
+ - `manageSwap`: Add support for P2TR output addresses
6
+
3
7
  ## Version 4.1.0
4
8
 
5
9
  - `createGroupChannel`: Add `members` to restrict and order group membership
package/package.json CHANGED
@@ -7,25 +7,25 @@
7
7
  "url": "https://github.com/alexbosworth/paid-services/issues"
8
8
  },
9
9
  "dependencies": {
10
- "@alexbosworth/fiat": "1.0.3",
10
+ "@alexbosworth/fiat": "1.0.4",
11
11
  "async": "3.2.4",
12
- "asyncjs-util": "1.2.10",
12
+ "asyncjs-util": "1.2.11",
13
13
  "bech32": "2.0.0",
14
- "bolt01": "1.2.5",
15
- "bolt07": "1.8.2",
14
+ "bolt01": "1.2.6",
15
+ "bolt07": "1.8.3",
16
16
  "ecpair": "2.1.0",
17
- "goldengate": "11.4.0",
18
- "invoices": "2.2.2",
19
- "ln-service": "54.3.1",
20
- "ln-sync": "4.0.5",
21
- "psbt": "2.7.1",
22
- "p2tr": "1.3.2",
17
+ "goldengate": "12.0.1",
18
+ "invoices": "2.2.3",
19
+ "ln-service": "54.9.0",
20
+ "ln-sync": "4.1.1",
21
+ "psbt": "2.7.2",
22
+ "p2tr": "1.3.3",
23
23
  "tiny-secp256k1": "2.2.1"
24
24
  },
25
25
  "description": "Lightning Paid Services library",
26
26
  "devDependencies": {
27
- "@alexbosworth/tap": "15.0.11",
28
- "ln-docker-daemons": "3.1.5",
27
+ "@alexbosworth/tap": "15.0.12",
28
+ "ln-docker-daemons": "4.0.4",
29
29
  "mock-lnd": "1.4.4",
30
30
  "secp256k1": "4.0.3"
31
31
  },
@@ -47,5 +47,5 @@
47
47
  "integration-tests": "tap -j 2 --branches=1 --functions=1 --lines=1 --statements=1 -t 180 test/integration/*.js",
48
48
  "test": "tap --branches=1 --functions=1 --lines=1 --statements=1 -t 60 test/actions/*.js test/balanced/*.js test/capacity/*.js test/client/*.js test/config/*.js test/p2p/*.js test/records/*.js test/respond/*.js test/server/*.js test/swaps/*.js test/services/*.js test/trades/*.js"
49
49
  },
50
- "version": "4.1.0"
50
+ "version": "4.2.1"
51
51
  }
@@ -65,6 +65,7 @@ const encodeAddress = (prefix, data) => bech32m.encode(prefix, data);
65
65
  const externalKeyAsOutputScript = key => `5120${key}`;
66
66
  const family = 805;
67
67
  const {floor} = Math;
68
+ const format = 'p2tr';
68
69
  const {from} = Buffer;
69
70
  const {fromHex} = Transaction;
70
71
  const fuzzBlocks = 100;
@@ -79,7 +80,7 @@ const {min} = Math;
79
80
  const minTokens = 10000;
80
81
  const minBlockMs = 1000 * 60;
81
82
  const pollInterval = {btcregtest: 100};
82
- const ppmRate = (fee, total) => fee * 1e6 / total.tokens;
83
+ const ppmRate = (fee, total) => fee * 1e6 / total;
83
84
  const preimageByteLength = 32;
84
85
  const pubKeyAsInternalKey = key => Buffer.from(key).slice(1).toString('hex');
85
86
  const pushSecret = asyncTimeout(releaseSwapOutSecret, 1000 * 30);
@@ -155,7 +156,7 @@ module.exports = (args, cbk) => {
155
156
  return cbk(null, {address: args.sweep_address});
156
157
  }
157
158
 
158
- return createChainAddress({lnd: args.lnd}, cbk);
159
+ return createChainAddress({format, lnd: args.lnd}, cbk);
159
160
  }],
160
161
 
161
162
  // Get the current chain fee rate for the sweep fee rate calculation
@@ -42,9 +42,11 @@ const {typePayMetadata} = require('./swap_field_types');
42
42
  const bufferAsHex = buffer => buffer.toString('hex');
43
43
  const defaultMaxFundingConfirmationDelay = 12;
44
44
  const defaultMinDelta = 60;
45
+ const estimatedSize = 150;
45
46
  const family = 805;
46
47
  const flatten = arr => [].concat(...arr);
47
48
  const {floor} = Math;
49
+ const format = 'p2tr';
48
50
  const {from} = Buffer;
49
51
  const {fromHex} = Transaction;
50
52
  const fuzzBlocks = 100;
@@ -126,7 +128,7 @@ module.exports = (args, cbk) => {
126
128
 
127
129
  // Get an address to sweep out to
128
130
  getSweepAddress: ['validate', ({}, cbk) => {
129
- return createChainAddress({lnd: args.lnd}, cbk);
131
+ return createChainAddress({format, lnd: args.lnd}, cbk);
130
132
  }],
131
133
 
132
134
  // Get the encryption key to decode the recovery secrets
@@ -370,6 +372,11 @@ module.exports = (args, cbk) => {
370
372
  });
371
373
 
372
374
  const refunds = uniqBy(feeRates, 'rate').map(({rate, height}) => {
375
+ // Exit early when there is nothing to recover
376
+ if (rate * estimatedSize > recoveryDetails.tokens) {
377
+ return;
378
+ }
379
+
373
380
  return taprootRefundTransaction({
374
381
  ecp,
375
382
  block_height: height,
@@ -388,7 +395,7 @@ module.exports = (args, cbk) => {
388
395
  });
389
396
  });
390
397
 
391
- return cbk(null, refunds.map(n => n.transaction));
398
+ return cbk(null, refunds.filter(n => !!n).map(n => n.transaction));
392
399
  }],
393
400
 
394
401
  // Sign the refund transactions
@@ -13,6 +13,7 @@ const {returnResult} = require('asyncjs-util');
13
13
  const decodeOffToOnRequest = require('./decode_off_to_on_request');
14
14
  const decodeOffToOnRestart = require('./decode_off_to_on_restart');
15
15
  const {decodeTrade} = require('./../trades');
16
+ const outputScriptForAddress = require('./output_script_for_address');
16
17
  const recoverResponseToSwapOut = require('./recover_response_to_swap_out');
17
18
  const requestSwapOut = require('./request_swap_out');
18
19
  const respondToSwapOutRequest = require('./respond_to_swap_out_request');
@@ -310,7 +311,10 @@ module.exports = (args, cbk) => {
310
311
  }
311
312
 
312
313
  try {
313
- return !!toOutputScript(input, networks[getNetwork.bitcoinjs]);
314
+ return !!outputScriptForAddress({
315
+ address: input,
316
+ network: getNetwork.bitcoinjs,
317
+ });
314
318
  } catch (err) {
315
319
  return 'Unsupported on-chain address format';
316
320
  }
@@ -0,0 +1,55 @@
1
+ const {address} = require('bitcoinjs-lib');
2
+ const {networks} = require('bitcoinjs-lib');
3
+
4
+ const bufferAsHex = buffer => buffer.toString('hex');
5
+ const byteLengthForP2tr = 32;
6
+ const {fromBech32} = address;
7
+ const prefixForP2tr = '5120';
8
+ const toOutputScript = (a, n) => address.toOutputScript(a, n).toString('hex');
9
+ const versionTaproot = 1;
10
+
11
+ /** Get the output script for an address
12
+
13
+ {
14
+ address: <Address String>
15
+ network: <BitcoinJs Network Name String>
16
+ }
17
+
18
+ @throws
19
+ <Error>
20
+
21
+ @returns
22
+ {
23
+ script: <Output Script Hex String>
24
+ }
25
+ */
26
+ module.exports = ({address, network}) => {
27
+ try {
28
+ return {
29
+ script: bufferAsHex(toOutputScript(address, networks[network])),
30
+ };
31
+ } catch (err) {
32
+ // Exit early when this is not a bech32 address
33
+ try {
34
+ fromBech32(address);
35
+ } catch (_) {
36
+ throw err;
37
+ }
38
+
39
+ const {data, prefix, version} = fromBech32(address);
40
+
41
+ if (prefix !== networks[network].bech32) {
42
+ throw new Error('InvalidNetworkToConvertAddressToOutputScript');
43
+ }
44
+
45
+ if (version !== versionTaproot) {
46
+ throw new Error('UnexpectedVersionOfAddressToConverToOutputScript');
47
+ }
48
+
49
+ if (data.length !== byteLengthForP2tr) {
50
+ throw new Error('UnexpectedByteLengthForPayToTaprootAddress');
51
+ }
52
+
53
+ return {script: `${prefixForP2tr}${bufferAsHex(data)}`};
54
+ }
55
+ };
@@ -31,6 +31,7 @@ const defaultFeeRate = 5000;
31
31
  const defaultMaxAmount = 21e14;
32
32
  const defaultMaxFeeForDeposit = 1337;
33
33
  const defaultSwapAmount = 2500000;
34
+ const feeAsPpm = (fee, total) => Math.ceil(fee * 1e6 / total);
34
35
  const findRecord = (r, type) => (r.find(n => n.type === type) || {}).value;
35
36
  const {floor} = Math;
36
37
  const hexAsBuffer = hex => Buffer.from(hex, 'hex');
@@ -421,14 +422,19 @@ module.exports = (args, cbk) => {
421
422
 
422
423
  const deposit = mtokensAsTokens(response.deposit_mtokens);
423
424
 
425
+ const combinedFee = tokens - askForTokens + deposit;
424
426
  const serviceFee = tokensAsBigUnit(deposit);
425
427
  const totalFee = tokensAsBigUnit(tokens - askForTokens + deposit);
426
428
 
429
+ const ppmTotal = feeAsPpm(combinedFee, askForTokens);
430
+
431
+ const feeInfo = `total fee is ${totalFee}, est PPM ${ppmTotal}`
432
+
427
433
  return cbk(null, {
428
434
  deposit,
429
435
  fee: tokens - askForTokens,
430
436
  incoming_peer: response.incoming_peer,
431
- pricing: `Execution cost ${serviceFee}, total fee is ${totalFee}`,
437
+ pricing: `Execution cost ${serviceFee}, ${feeInfo}`,
432
438
  rate: rateForFee(askForTokens, deposit + (tokens - askForTokens)),
433
439
  timeout: response.timeout,
434
440
  });
@@ -53,7 +53,7 @@ test(`Start offchain swap`, async ({end, equal, strictSame}) => {
53
53
  });
54
54
 
55
55
  await asyncRetry({interval, times}, async () => {
56
- const {channels} = await getChannels({lnd, is_actve: true});
56
+ const {channels} = await getChannels({lnd, is_active: true});
57
57
 
58
58
  if (!!channels.length) {
59
59
  return channels;
@@ -53,7 +53,7 @@ test(`Swap with claim path`, async ({end, equal, strictSame}) => {
53
53
  });
54
54
 
55
55
  await asyncRetry({interval, times}, async () => {
56
- const {channels} = await getChannels({lnd, is_actve: true});
56
+ const {channels} = await getChannels({lnd, is_active: true});
57
57
 
58
58
  if (!!channels.length) {
59
59
  return channels;
@@ -58,7 +58,7 @@ test(`Timeout a swap`, async ({end, equal, strictSame}) => {
58
58
  });
59
59
 
60
60
  await asyncRetry({interval, times}, async () => {
61
- const {channels} = await getChannels({lnd, is_actve: true});
61
+ const {channels} = await getChannels({lnd, is_active: true});
62
62
 
63
63
  if (!!channels.length) {
64
64
  return channels;