ln-service 57.10.2 → 57.12.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 +9 -0
- package/README.md +66 -0
- package/index.js +2 -0
- package/package.json +4 -4
- package/test/integration/test_get_chain_transactions.js +0 -7
- package/test/integration/test_open_channels.js +2 -0
- package/test/integration/test_update_routing_fees.js +8 -0
- package/test/routerrpc-integration/test_get_route_through_hops.js +16 -6
- package/test/walletrpc-integration/test_create_funded_psbt.js +141 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# Versions
|
|
2
2
|
|
|
3
|
+
## 57.12.0
|
|
4
|
+
|
|
5
|
+
- `createFundedPsbt`: Add method to create a funded PSBT given inputs/outputs
|
|
6
|
+
|
|
7
|
+
## 57.11.0
|
|
8
|
+
|
|
9
|
+
- `getChannel`, `getNetworkGraph`, `getNode`: Add
|
|
10
|
+
`inbound_base_discount_mtokens` and `inbound_rate_discount`
|
|
11
|
+
|
|
3
12
|
## 57.10.2
|
|
4
13
|
|
|
5
14
|
- `getChainFeeEstimate`: Add default chain fee conf target when none is passed
|
package/README.md
CHANGED
|
@@ -147,6 +147,8 @@ for `unlocker` methods.
|
|
|
147
147
|
- [closeChannel](#closechannel) - Terminate an open channel
|
|
148
148
|
- [connectWatchtower](#connectwatchtower) - Connect a watchtower
|
|
149
149
|
- [createChainAddress](#createchainaddress) - Get a chain address to receive at
|
|
150
|
+
- [createFundedPsbt](#createfundedpsbt): Create a funded PSBT given inputs and
|
|
151
|
+
outputs
|
|
150
152
|
- [createHodlInvoice](#createhodlinvoice) - Make a HODL HTLC invoice
|
|
151
153
|
- [createInvoice](#createinvoice) - Make a regular invoice
|
|
152
154
|
- [createSeed](#createseed) - Generate a wallet seed for a new wallet
|
|
@@ -688,6 +690,55 @@ const format = 'p2wpkh';
|
|
|
688
690
|
const {address} = await createChainAddress({format, lnd});
|
|
689
691
|
```
|
|
690
692
|
|
|
693
|
+
### createFundedPsbt
|
|
694
|
+
|
|
695
|
+
Create an unsigned funded PSBT given inputs or outputs
|
|
696
|
+
|
|
697
|
+
When specifying local inputs, they must be locked before using
|
|
698
|
+
|
|
699
|
+
`utxo_selection` methods: 'largest', 'random'
|
|
700
|
+
|
|
701
|
+
Requires `onchain:write` permission
|
|
702
|
+
|
|
703
|
+
Requires LND built with `walletrpc` tag
|
|
704
|
+
|
|
705
|
+
This method is not supported on LND 0.17.5 or below
|
|
706
|
+
|
|
707
|
+
{
|
|
708
|
+
[fee_tokens_per_vbyte]: <Chain Fee Tokens Per Virtual Byte Number>
|
|
709
|
+
[inputs]: [{
|
|
710
|
+
[sequence]: <Sequence Number>
|
|
711
|
+
transaction_id: <Unspent Transaction Id Hex String>
|
|
712
|
+
transaction_vout: <Unspent Transaction Output Index Number>
|
|
713
|
+
}]
|
|
714
|
+
lnd: <Authenticated LND API Object>
|
|
715
|
+
[min_confirmations]: <Select Inputs With Minimum Confirmations Number>
|
|
716
|
+
[outputs]: [{
|
|
717
|
+
[is_change]: <Use This Output For Change Bool>
|
|
718
|
+
script: <Output Script Hex String>
|
|
719
|
+
tokens: <Send Tokens Tokens Number>
|
|
720
|
+
}]
|
|
721
|
+
[target_confirmations]: <Blocks To Wait for Confirmation Number>
|
|
722
|
+
[timelock]: <Spendable Lock Time on Transaction Number>
|
|
723
|
+
[utxo_selection]: <Select Inputs Using Selection Methodology Type String>
|
|
724
|
+
[version]: <Transaction Version Number>
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
@returns via cbk or Promise
|
|
728
|
+
{
|
|
729
|
+
psbt: <Unsigned PSBT Hex String>
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
```node
|
|
733
|
+
const {createFundedPsbt} = require('ln-service');
|
|
734
|
+
|
|
735
|
+
const script = '00';
|
|
736
|
+
const tokens = 1e6;
|
|
737
|
+
|
|
738
|
+
// Create an unsigned PSBT that sends 1mm to an output script
|
|
739
|
+
const {psbt} = await createFundedPsbt({lnd, outputs: [{script, tokens}]});
|
|
740
|
+
```
|
|
741
|
+
|
|
691
742
|
### createHodlInvoice
|
|
692
743
|
|
|
693
744
|
Create HODL invoice. This invoice will not settle automatically when an
|
|
@@ -1833,6 +1884,9 @@ Get graph information about a channel on the network
|
|
|
1833
1884
|
|
|
1834
1885
|
Requires `info:read` permission
|
|
1835
1886
|
|
|
1887
|
+
`inbound_base_discount_mtokens` is not supported on LND 0.17.5 and below
|
|
1888
|
+
`inbound_rate_discount` is not supported on LND 0.17.5 and below
|
|
1889
|
+
|
|
1836
1890
|
{
|
|
1837
1891
|
id: <Standard Format Channel Id String>
|
|
1838
1892
|
lnd: <Authenticated LND API Object>
|
|
@@ -1846,6 +1900,8 @@ Requires `info:read` permission
|
|
|
1846
1900
|
[base_fee_mtokens]: <Base Fee Millitokens String>
|
|
1847
1901
|
[cltv_delta]: <Locktime Delta Number>
|
|
1848
1902
|
[fee_rate]: <Fees Charged Per Million Millitokens Number>
|
|
1903
|
+
[inbound_base_discount_mtokens]: <Source Based Base Fee Reduction String>
|
|
1904
|
+
[inbound_rate_discount]: <Source Based Per Million Rate Reduction Number>
|
|
1849
1905
|
[is_disabled]: <Channel Is Disabled Bool>
|
|
1850
1906
|
[max_htlc_mtokens]: <Maximum HTLC Millitokens Value String>
|
|
1851
1907
|
[min_htlc_mtokens]: <Minimum HTLC Millitokens Value String>
|
|
@@ -2693,6 +2749,9 @@ Get the network graph
|
|
|
2693
2749
|
|
|
2694
2750
|
Requires `info:read` permission
|
|
2695
2751
|
|
|
2752
|
+
`inbound_base_discount_mtokens` is not supported on LND 0.17.5 and below
|
|
2753
|
+
`inbound_rate_discount` is not supported on LND 0.17.5 and below
|
|
2754
|
+
|
|
2696
2755
|
{
|
|
2697
2756
|
lnd: <Authenticated LND API Object>
|
|
2698
2757
|
}
|
|
@@ -2706,6 +2765,8 @@ Requires `info:read` permission
|
|
|
2706
2765
|
[base_fee_mtokens]: <Bae Fee Millitokens String>
|
|
2707
2766
|
[cltv_delta]: <CLTV Height Delta Number>
|
|
2708
2767
|
[fee_rate]: <Fee Rate In Millitokens Per Million Number>
|
|
2768
|
+
[inbound_base_discount_mtokens]: <Source Base Fee Reduction String>
|
|
2769
|
+
[inbound_rate_discount]: <Source Per Million Rate Reduction Number>
|
|
2709
2770
|
[is_disabled]: <Edge is Disabled Bool>
|
|
2710
2771
|
[max_htlc_mtokens]: <Maximum HTLC Millitokens String>
|
|
2711
2772
|
[min_htlc_mtokens]: <Minimum HTLC Millitokens String>
|
|
@@ -2773,6 +2834,9 @@ Get information about a node
|
|
|
2773
2834
|
|
|
2774
2835
|
Requires `info:read` permission
|
|
2775
2836
|
|
|
2837
|
+
`inbound_base_discount_mtokens` is not supported on LND 0.17.5 and below
|
|
2838
|
+
`inbound_rate_discount` is not supported on LND 0.17.5 and below
|
|
2839
|
+
|
|
2776
2840
|
{
|
|
2777
2841
|
[is_omitting_channels]: <Omit Channels from Node Bool>
|
|
2778
2842
|
lnd: <Authenticated LND API Object>
|
|
@@ -2791,6 +2855,8 @@ Requires `info:read` permission
|
|
|
2791
2855
|
[base_fee_mtokens]: <Base Fee Millitokens String>
|
|
2792
2856
|
[cltv_delta]: <Locktime Delta Number>
|
|
2793
2857
|
[fee_rate]: <Fees Charged Per Million Millitokens Number>
|
|
2858
|
+
[inbound_base_discount_mtokens]: <Source Base Fee Reduction String>
|
|
2859
|
+
[inbound_rate_discount]: <Source Per Million Rate Reduction Number>
|
|
2794
2860
|
[is_disabled]: <Channel Is Disabled Bool>
|
|
2795
2861
|
[max_htlc_mtokens]: <Maximum HTLC Millitokens Value String>
|
|
2796
2862
|
[min_htlc_mtokens]: <Minimum HTLC Millitokens Value String>
|
package/index.js
CHANGED
|
@@ -10,6 +10,7 @@ const {changePassword} = require('lightning');
|
|
|
10
10
|
const {closeChannel} = require('lightning');
|
|
11
11
|
const {connectWatchtower} = require('lightning');
|
|
12
12
|
const {createChainAddress} = require('lightning');
|
|
13
|
+
const {createFundedPsbt} = require('lightning');
|
|
13
14
|
const {createHodlInvoice} = require('lightning');
|
|
14
15
|
const {createInvoice} = require('lightning');
|
|
15
16
|
const {createSeed} = require('lightning');
|
|
@@ -174,6 +175,7 @@ module.exports = {
|
|
|
174
175
|
closeChannel,
|
|
175
176
|
connectWatchtower,
|
|
176
177
|
createChainAddress,
|
|
178
|
+
createFundedPsbt,
|
|
177
179
|
createHodlInvoice,
|
|
178
180
|
createInvoice,
|
|
179
181
|
createSeed,
|
package/package.json
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"dependencies": {
|
|
10
10
|
"bolt07": "1.8.4",
|
|
11
11
|
"invoices": "3.0.0",
|
|
12
|
-
"lightning": "10.
|
|
12
|
+
"lightning": "10.12.0",
|
|
13
13
|
"macaroon": "3.0.4"
|
|
14
14
|
},
|
|
15
15
|
"description": "Interaction helper for your Lightning Network daemon",
|
|
@@ -24,11 +24,11 @@
|
|
|
24
24
|
"bn.js": "5.2.1",
|
|
25
25
|
"bs58check": "3.0.1",
|
|
26
26
|
"ecpair": "2.1.0",
|
|
27
|
-
"ln-docker-daemons": "6.0.
|
|
27
|
+
"ln-docker-daemons": "6.0.16",
|
|
28
28
|
"p2tr": "2.0.0",
|
|
29
29
|
"portfinder": "1.0.32",
|
|
30
30
|
"psbt": "3.0.0",
|
|
31
|
-
"rimraf": "5.0.
|
|
31
|
+
"rimraf": "5.0.7",
|
|
32
32
|
"secp256k1": "5.0.0",
|
|
33
33
|
"tiny-secp256k1": "2.2.3",
|
|
34
34
|
"uuid": "9.0.1",
|
|
@@ -71,5 +71,5 @@
|
|
|
71
71
|
"integration-test-0.14.4": "DOCKER_LND_VERSION=v0.14.4-beta npm run test",
|
|
72
72
|
"test": "echo $DOCKER_LND_VERSION && node test/runner"
|
|
73
73
|
},
|
|
74
|
-
"version": "57.
|
|
74
|
+
"version": "57.12.0"
|
|
75
75
|
}
|
|
@@ -65,13 +65,6 @@ test(`Get chain transactions`, async () => {
|
|
|
65
65
|
equal(tx.tokens, 5000000000, 'Got coinbase reward tokens');
|
|
66
66
|
equal(!!tx.transaction, true, 'Got transaction hex');
|
|
67
67
|
|
|
68
|
-
const onlyAfter = await getChainTransactions({
|
|
69
|
-
lnd,
|
|
70
|
-
after: tx.confirmation_height,
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
equal(onlyAfter.transactions.length, [].length, 'No txs after');
|
|
74
|
-
|
|
75
68
|
const onlyBefore = await getChainTransactions({lnd, before: 2});
|
|
76
69
|
|
|
77
70
|
equal(onlyBefore.transactions.length < 100, true, 'Got before txs');
|
|
@@ -47,6 +47,8 @@ test(`Open channels`, async () => {
|
|
|
47
47
|
await asyncRetry({interval, times}, async () => {
|
|
48
48
|
const lnds = [lnd, target.lnd, remote.lnd];
|
|
49
49
|
|
|
50
|
+
await generate({});
|
|
51
|
+
|
|
50
52
|
const heights = await asyncMap(lnds, async lnd => {
|
|
51
53
|
return (await getHeight({lnd})).current_block_height;
|
|
52
54
|
});
|
|
@@ -83,12 +83,20 @@ test(`Update routing fees`, async () => {
|
|
|
83
83
|
base_fee_mtokens: `${BigInt(baseFeeTokens) * BigInt(n) * BigInt(1e3)}`,
|
|
84
84
|
cltv_delta: cltvDelta * n,
|
|
85
85
|
fee_rate: feeRate * n,
|
|
86
|
+
inbound_base_discount_mtokens: 1,
|
|
87
|
+
inbound_rate_discount: 1,
|
|
86
88
|
});
|
|
87
89
|
|
|
88
90
|
const policy = (await getChannel({id, lnd})).policies.find(policy => {
|
|
89
91
|
return policy.public_key === target.id;
|
|
90
92
|
});
|
|
91
93
|
|
|
94
|
+
// LND 0.17.5 and below do not support fee discounts
|
|
95
|
+
if (!!policy.inbound_rate_discount) {
|
|
96
|
+
equal(policy.inbound_base_discount_mtokens, '1', 'Got base discount');
|
|
97
|
+
equal(policy.inbound_rate_discount, 1, 'Got rate discount');
|
|
98
|
+
}
|
|
99
|
+
|
|
92
100
|
equal(policy.base_fee_mtokens, `${baseFeeTokens*mtokPerTok*n}`, 'Base');
|
|
93
101
|
equal(policy.cltv_delta, cltvDelta*n, 'Global CLTV delta updated');
|
|
94
102
|
equal(policy.fee_rate, feeRate*n, 'Global Fee rate updated');
|
|
@@ -33,14 +33,24 @@ test(`Get route through hops`, async () => {
|
|
|
33
33
|
|
|
34
34
|
const [{generate, lnd}, target, remote] = nodes;
|
|
35
35
|
|
|
36
|
-
await target.generate({count: maturity});
|
|
37
|
-
|
|
38
36
|
const controlToTargetChan = await setupChannel({generate, lnd, to: target});
|
|
39
37
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
38
|
+
await generate({});
|
|
39
|
+
|
|
40
|
+
await asyncRetry({interval, times}, async () => {
|
|
41
|
+
await generate({});
|
|
42
|
+
|
|
43
|
+
await addPeer({
|
|
44
|
+
lnd: target.lnd,
|
|
45
|
+
public_key: remote.id,
|
|
46
|
+
socket: remote.socket,
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
const targetToRemoteChan = await setupChannel({
|
|
50
|
+
generate: target.generate,
|
|
51
|
+
lnd: target.lnd,
|
|
52
|
+
to: remote,
|
|
53
|
+
});
|
|
44
54
|
});
|
|
45
55
|
|
|
46
56
|
await target.generate({count: confirmationCount});
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
const {equal} = require('node:assert').strict;
|
|
2
|
+
const test = require('node:test');
|
|
3
|
+
|
|
4
|
+
const asyncRetry = require('async/retry');
|
|
5
|
+
const {address} = require('bitcoinjs-lib');
|
|
6
|
+
const {componentsOfTransaction} = require('@alexbosworth/blockchain');
|
|
7
|
+
const {spawnLightningCluster} = require('ln-docker-daemons');
|
|
8
|
+
|
|
9
|
+
const {broadcastChainTransaction} = require('./../../');
|
|
10
|
+
const {createChainAddress} = require('./../../');
|
|
11
|
+
const {createFundedPsbt} = require('./../../');
|
|
12
|
+
const {getChainTransactions} = require('./../../');
|
|
13
|
+
const {getUtxos} = require('./../../');
|
|
14
|
+
const {lockUtxo} = require('./../../');
|
|
15
|
+
const {signPsbt} = require('./../../');
|
|
16
|
+
|
|
17
|
+
const bufferAsHex = buffer => buffer.toString('hex');
|
|
18
|
+
const {concat} = Buffer;
|
|
19
|
+
const count = 100;
|
|
20
|
+
const format = 'p2tr';
|
|
21
|
+
const {fromBech32} = address;
|
|
22
|
+
const interval = retryCount => 10 * Math.pow(2, retryCount);
|
|
23
|
+
const OP_1 = Buffer.from([81]);
|
|
24
|
+
const push32 = Buffer.from([32]);
|
|
25
|
+
const sequence = 0;
|
|
26
|
+
const timelock = 1;
|
|
27
|
+
const times = 20;
|
|
28
|
+
const version = 1;
|
|
29
|
+
|
|
30
|
+
// Creating a funded PSBT should result in a funded PSBT
|
|
31
|
+
test(`Create funded PSBT`, async () => {
|
|
32
|
+
const {kill, nodes} = await spawnLightningCluster({});
|
|
33
|
+
|
|
34
|
+
const [{generate, lnd}] = nodes;
|
|
35
|
+
|
|
36
|
+
await generate({count});
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
await createFundedPsbt({lnd});
|
|
40
|
+
} catch (err) {
|
|
41
|
+
if (!Array.isArray(err)) {
|
|
42
|
+
await kill({});
|
|
43
|
+
|
|
44
|
+
equal(err, null, 'Expected array error');
|
|
45
|
+
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const [code, message] = err;
|
|
50
|
+
|
|
51
|
+
// LND 0.17.5 and before do not support the create funded PSBT method
|
|
52
|
+
if (code === 501) {
|
|
53
|
+
await kill({});
|
|
54
|
+
|
|
55
|
+
equal(message, 'CreateFundedPsbtMethodNotSupported', 'Unsupported');
|
|
56
|
+
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
try {
|
|
62
|
+
const {address} = await createChainAddress({format, lnd});
|
|
63
|
+
const {utxos} = await getUtxos({lnd});
|
|
64
|
+
|
|
65
|
+
const outputScriptElements = [OP_1, push32, fromBech32(address).data];
|
|
66
|
+
const [utxo] = utxos;
|
|
67
|
+
|
|
68
|
+
const output = bufferAsHex(concat(outputScriptElements));
|
|
69
|
+
|
|
70
|
+
// Creating a funded PSBT requires pre-locking the inputs
|
|
71
|
+
const lock = await lockUtxo({
|
|
72
|
+
lnd,
|
|
73
|
+
transaction_id: utxo.transaction_id,
|
|
74
|
+
transaction_vout: utxo.transaction_vout,
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
const {psbt} = await createFundedPsbt({
|
|
78
|
+
fee_tokens_per_vbyte: 50,
|
|
79
|
+
lnd,
|
|
80
|
+
inputs: [{
|
|
81
|
+
sequence,
|
|
82
|
+
transaction_id: utxo.transaction_id,
|
|
83
|
+
transaction_vout: utxo.transaction_vout,
|
|
84
|
+
}],
|
|
85
|
+
outputs: [{is_change: true, script: output, tokens: 1}],
|
|
86
|
+
timelock,
|
|
87
|
+
version,
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
const signed = await signPsbt({lnd, psbt});
|
|
91
|
+
|
|
92
|
+
// Make sure this tx can be published
|
|
93
|
+
await broadcastChainTransaction({lnd, transaction: signed.transaction});
|
|
94
|
+
|
|
95
|
+
// Make sure the publishing succeeded by looking for it in chain txns
|
|
96
|
+
const {transactions} = await getChainTransactions({lnd});
|
|
97
|
+
|
|
98
|
+
const [{transaction}] = transactions;
|
|
99
|
+
|
|
100
|
+
equal(signed.transaction, transaction, 'Transaction is broadcast');
|
|
101
|
+
|
|
102
|
+
const tx = componentsOfTransaction({transaction});
|
|
103
|
+
|
|
104
|
+
// Check all the elements of the 1 in 1 out transaction
|
|
105
|
+
equal(tx.inputs.length, [utxo].length, 'Used a single input');
|
|
106
|
+
equal(tx.inputs[0].sequence, sequence, 'Used specified input sequence');
|
|
107
|
+
equal(tx.inputs[0].id, utxo.transaction_id, 'Used specified tx id');
|
|
108
|
+
equal(tx.inputs[0].vout, utxo.transaction_vout, 'Used specified tx vout');
|
|
109
|
+
equal(tx.locktime, timelock, 'Used specified timelock value');
|
|
110
|
+
equal(tx.outputs.length, [output].length, 'Used a single output');
|
|
111
|
+
equal(tx.outputs[0].script, output, 'Output used output script');
|
|
112
|
+
equal(tx.outputs[0].tokens, 4999993913, 'Output spent all tokens');
|
|
113
|
+
equal(tx.version, version, 'Used specified version value');
|
|
114
|
+
|
|
115
|
+
// Create a 1 in 2 out transaction, so one with change
|
|
116
|
+
const withChange = await asyncRetry({interval, times}, async () => {
|
|
117
|
+
await generate({});
|
|
118
|
+
|
|
119
|
+
return await createFundedPsbt({
|
|
120
|
+
lnd,
|
|
121
|
+
outputs: [{script: output, tokens: 500}],
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
const signedChange = await signPsbt({lnd, psbt: withChange.psbt});
|
|
126
|
+
|
|
127
|
+
const signedTx = componentsOfTransaction({
|
|
128
|
+
transaction: signedChange.transaction,
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
equal(signedTx.outputs.length, 2, 'Change output created');
|
|
132
|
+
} catch (err) {
|
|
133
|
+
await kill({});
|
|
134
|
+
|
|
135
|
+
equal(err, null, 'No error expected');
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
await kill({});
|
|
139
|
+
|
|
140
|
+
return;
|
|
141
|
+
});
|