ln-service 53.16.0 → 53.17.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 +10 -0
- package/README.md +13 -3
- package/package.json +6 -6
- package/test/integration/test_create_chain_address.js +3 -0
- package/test/integration/test_delete_pending_channel.js +1 -1
- package/test/integration/test_get_network_info.js +14 -8
- package/test/macros/wait_for_channel.js +1 -1
- package/test/signerrpc-integration/test_sign_taproot.js +359 -0
- package/test/signerrpc-integration/test_sign_transaction.js +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Versions
|
|
2
2
|
|
|
3
|
+
## 53.17.1
|
|
4
|
+
|
|
5
|
+
- `signTransaction`: Add `root_hash` to support Taproot signatures with scripts
|
|
6
|
+
|
|
7
|
+
## 53.16.1
|
|
8
|
+
|
|
9
|
+
- `getFailedPayments`, `getPayments`, `getPendingPayments`: Remove
|
|
10
|
+
`confirmed_at` date when a payment is not confirmed, add `created_at` and
|
|
11
|
+
`failed_at` dates for attempt start and attempt failed dates.
|
|
12
|
+
|
|
3
13
|
## 53.16.0
|
|
4
14
|
|
|
5
15
|
- `beginGroupSigningSession`: Add method to start a MuSig2 signing session
|
package/README.md
CHANGED
|
@@ -1819,7 +1819,9 @@ Requires `offchain:read` permission
|
|
|
1819
1819
|
message: <Error Message String>
|
|
1820
1820
|
}
|
|
1821
1821
|
[index]: <Payment Add Index Number>
|
|
1822
|
-
[confirmed_at]: <Payment
|
|
1822
|
+
[confirmed_at]: <Payment Attempt Succeeded At ISO 8601 Date String>
|
|
1823
|
+
created_at: <Attempt Was Started At ISO 8601 Date String>
|
|
1824
|
+
[failed_at]: <Payment Attempt Failed At ISO 8601 Date String>
|
|
1823
1825
|
is_confirmed: <Payment Attempt Succeeded Bool>
|
|
1824
1826
|
is_failed: <Payment Attempt Failed Bool>
|
|
1825
1827
|
is_pending: <Payment Attempt is Waiting For Resolution Bool>
|
|
@@ -2602,6 +2604,9 @@ Requires `offchain:read` permission
|
|
|
2602
2604
|
}
|
|
2603
2605
|
message: <Error Message String>
|
|
2604
2606
|
}
|
|
2607
|
+
[confirmed_at]: <Payment Attempt Succeeded At ISO 8601 Date String>
|
|
2608
|
+
created_at: <Attempt Was Started At ISO 8601 Date String>
|
|
2609
|
+
[failed_at]: <Payment Attempt Failed At ISO 8601 Date String>
|
|
2605
2610
|
is_confirmed: <Payment Attempt Succeeded Bool>
|
|
2606
2611
|
is_failed: <Payment Attempt Failed Bool>
|
|
2607
2612
|
is_pending: <Payment Attempt is Waiting For Resolution Bool>
|
|
@@ -2817,7 +2822,9 @@ Requires `offchain:read` permission
|
|
|
2817
2822
|
message: <Error Message String>
|
|
2818
2823
|
}
|
|
2819
2824
|
[index]: <Payment Add Index Number>
|
|
2820
|
-
[confirmed_at]: <Payment
|
|
2825
|
+
[confirmed_at]: <Payment Attempt Succeeded At ISO 8601 Date String>
|
|
2826
|
+
created_at: <Attempt Was Started At ISO 8601 Date String>
|
|
2827
|
+
[failed_at]: <Payment Attempt Failed At ISO 8601 Date String>
|
|
2821
2828
|
is_confirmed: <Payment Attempt Succeeded Bool>
|
|
2822
2829
|
is_failed: <Payment Attempt Failed Bool>
|
|
2823
2830
|
is_pending: <Payment Attempt is Waiting For Resolution Bool>
|
|
@@ -3393,6 +3400,7 @@ const {app, server, wss} = grpcProxyServer({
|
|
|
3393
3400
|
// Create an authenticated LND for the gRPC REST gateway
|
|
3394
3401
|
const {lnd} = lndGateway({
|
|
3395
3402
|
request,
|
|
3403
|
+
websocket,
|
|
3396
3404
|
macaroon: base64EncodedMacaroonFileString,
|
|
3397
3405
|
url: `http://localhost:${port}${path}`,
|
|
3398
3406
|
});
|
|
@@ -4774,6 +4782,7 @@ Requires LND built with `signrpc` build tag
|
|
|
4774
4782
|
|
|
4775
4783
|
Requires `signer:generate` permission
|
|
4776
4784
|
|
|
4785
|
+
`root_hash` is not supported in LND 0.14.3 and below
|
|
4777
4786
|
`spending` is not supported in LND 0.14.3 and below
|
|
4778
4787
|
|
|
4779
4788
|
{
|
|
@@ -4782,9 +4791,10 @@ Requires `signer:generate` permission
|
|
|
4782
4791
|
key_index: <Key Index Number>
|
|
4783
4792
|
output_script: <Output Script Hex String>
|
|
4784
4793
|
output_tokens: <Output Tokens Number>
|
|
4794
|
+
[root_hash]: <Taproot Root Hash Hex String>
|
|
4785
4795
|
sighash: <Sighash Type Number>
|
|
4786
4796
|
vin: <Input Index To Sign Number>
|
|
4787
|
-
witness_script: <Witness Script Hex String>
|
|
4797
|
+
[witness_script]: <Witness Script Hex String>
|
|
4788
4798
|
}]
|
|
4789
4799
|
lnd: <Authenticated LND API Object>
|
|
4790
4800
|
[spending]: [{
|
package/package.json
CHANGED
|
@@ -11,10 +11,10 @@
|
|
|
11
11
|
"cors": "2.8.5",
|
|
12
12
|
"express": "4.18.1",
|
|
13
13
|
"invoices": "2.0.6",
|
|
14
|
-
"lightning": "5.
|
|
14
|
+
"lightning": "5.16.1",
|
|
15
15
|
"macaroon": "3.0.4",
|
|
16
16
|
"morgan": "1.10.0",
|
|
17
|
-
"ws": "8.
|
|
17
|
+
"ws": "8.7.0"
|
|
18
18
|
},
|
|
19
19
|
"description": "Interaction helper for your Lightning Network daemon",
|
|
20
20
|
"devDependencies": {
|
|
@@ -25,13 +25,13 @@
|
|
|
25
25
|
"bip32": "3.0.1",
|
|
26
26
|
"bip66": "1.1.5",
|
|
27
27
|
"bitcoinjs-lib": "6.0.1",
|
|
28
|
-
"bn.js": "5.2.
|
|
28
|
+
"bn.js": "5.2.1",
|
|
29
29
|
"bs58check": "2.1.2",
|
|
30
30
|
"ecpair": "2.0.1",
|
|
31
|
-
"ln-docker-daemons": "2.2.
|
|
31
|
+
"ln-docker-daemons": "2.2.11",
|
|
32
32
|
"p2tr": "1.3.1",
|
|
33
33
|
"portfinder": "1.0.28",
|
|
34
|
-
"psbt": "2.0
|
|
34
|
+
"psbt": "2.4.0",
|
|
35
35
|
"rimraf": "3.0.2",
|
|
36
36
|
"secp256k1": "4.0.3",
|
|
37
37
|
"tiny-secp256k1": "2.2.1",
|
|
@@ -70,5 +70,5 @@
|
|
|
70
70
|
"integration-test-0.12.0": "DOCKER_LND_VERSION=v0.12.0-beta npm run test",
|
|
71
71
|
"test": "echo $DOCKER_LND_VERSION && tap -j 2 --branches=1 --functions=1 --lines=1 --statements=1 -t 200 test/autopilotrpc-integration/*.js test/chainrpc-integration/*.js test/integration/*.js test/invoicesrpc-integration/*.js test/peersrpc-integration/*.js test/routerrpc-integration/*.js test/signerrpc-integration/*.js test/tower_clientrpc-integration/*.js test/tower_serverrpc-integration/*.js test/walletrpc-integration/*.js"
|
|
72
72
|
},
|
|
73
|
-
"version": "53.
|
|
73
|
+
"version": "53.17.1"
|
|
74
74
|
}
|
|
@@ -41,6 +41,9 @@ test(`Create address results in address creation`, async ({end, equal}) => {
|
|
|
41
41
|
equal(address.startsWith(prefixForV1), true, 'A taproot address is made');
|
|
42
42
|
} catch (err) {
|
|
43
43
|
// LND 0.14.3 and below do not support TR addresses
|
|
44
|
+
const [code] = err;
|
|
45
|
+
|
|
46
|
+
equal(code, 501, 'Taproot addresses are unsupported');
|
|
44
47
|
}
|
|
45
48
|
|
|
46
49
|
await kill({});
|
|
@@ -126,7 +126,7 @@ test(`Forfeit pending channel`, async ({end, equal, strictSame}) => {
|
|
|
126
126
|
|
|
127
127
|
const [pending] = (await getPendingChannels({lnd})).pending_channels;
|
|
128
128
|
|
|
129
|
-
const stuckTx = extractTransaction({psbt: signTarget.psbt});
|
|
129
|
+
const stuckTx = extractTransaction({ecp, psbt: signTarget.psbt});
|
|
130
130
|
|
|
131
131
|
const [stuckPending] = proposeToTarget.pending;
|
|
132
132
|
|
|
@@ -2,22 +2,28 @@ const {spawnLightningCluster} = require('ln-docker-daemons');
|
|
|
2
2
|
const {test} = require('@alexbosworth/tap');
|
|
3
3
|
|
|
4
4
|
const {getNetworkInfo} = require('./../../');
|
|
5
|
+
const {setupChannel} = require('./../macros');
|
|
6
|
+
|
|
7
|
+
const size = 2;
|
|
8
|
+
const tokens = 1e6;
|
|
5
9
|
|
|
6
10
|
// Getting the network info should return basic network statistics
|
|
7
11
|
test(`Get network info`, async ({end, equal}) => {
|
|
8
|
-
const {kill, nodes} = await spawnLightningCluster({});
|
|
12
|
+
const {kill, nodes} = await spawnLightningCluster({size});
|
|
13
|
+
|
|
14
|
+
const [{generate, lnd}, target] = nodes;
|
|
9
15
|
|
|
10
|
-
|
|
16
|
+
await setupChannel({generate, lnd, to: target});
|
|
11
17
|
|
|
12
18
|
const result = await getNetworkInfo({lnd});
|
|
13
19
|
|
|
14
|
-
equal(result.average_channel_size,
|
|
15
|
-
equal(result.channel_count,
|
|
16
|
-
equal(result.max_channel_size,
|
|
17
|
-
equal(result.median_channel_size,
|
|
18
|
-
equal(result.min_channel_size,
|
|
20
|
+
equal(result.average_channel_size, tokens, 'Average channel size');
|
|
21
|
+
equal(result.channel_count, 1, 'Channel count');
|
|
22
|
+
equal(result.max_channel_size, tokens, 'Maximum channel size');
|
|
23
|
+
equal(result.median_channel_size, tokens, 'Median channel size');
|
|
24
|
+
equal(result.min_channel_size, tokens, 'Minimum channel size');
|
|
19
25
|
equal(result.not_recently_updated_policy_count, 0, 'Not updated count');
|
|
20
|
-
equal(result.total_capacity,
|
|
26
|
+
equal(result.total_capacity, tokens, 'Total capacity');
|
|
21
27
|
|
|
22
28
|
await kill({});
|
|
23
29
|
|
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
const asyncRetry = require('async/retry');
|
|
2
|
+
const {address} = require('bitcoinjs-lib');
|
|
3
|
+
const {controlBlock} = require('p2tr');
|
|
4
|
+
const {createPsbt} = require('psbt');
|
|
5
|
+
const {hashForTree} = require('p2tr');
|
|
6
|
+
const {networks} = require('bitcoinjs-lib');
|
|
7
|
+
const {script} = require('bitcoinjs-lib');
|
|
8
|
+
const {spawnLightningCluster} = require('ln-docker-daemons');
|
|
9
|
+
const {test} = require('@alexbosworth/tap');
|
|
10
|
+
const tinysecp = require('tiny-secp256k1');
|
|
11
|
+
const {Transaction} = require('bitcoinjs-lib');
|
|
12
|
+
const {v1OutputScript} = require('p2tr');
|
|
13
|
+
|
|
14
|
+
const {beginGroupSigningSession} = require('./../../');
|
|
15
|
+
const {broadcastChainTransaction} = require('./../../');
|
|
16
|
+
const {createChainAddress} = require('./../../');
|
|
17
|
+
const {fundPsbt} = require('./../../');
|
|
18
|
+
const {getPublicKey} = require('./../../');
|
|
19
|
+
const {getUtxos} = require('./../../');
|
|
20
|
+
const {signPsbt} = require('./../../');
|
|
21
|
+
const {signTransaction} = require('./../../');
|
|
22
|
+
|
|
23
|
+
const {compile} = script;
|
|
24
|
+
const count = 100;
|
|
25
|
+
const defaultInternalKey = '0350929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0';
|
|
26
|
+
const {fromHex} = Transaction;
|
|
27
|
+
const hexAsBuffer = hex => Buffer.from(hex, 'hex');
|
|
28
|
+
const interval = retryCount => 10 * Math.pow(2, retryCount);
|
|
29
|
+
const OP_CHECKSIG = 172;
|
|
30
|
+
const smallTokens = 2e5;
|
|
31
|
+
const times = 20;
|
|
32
|
+
const {toOutputScript} = address;
|
|
33
|
+
const tokens = 1e6;
|
|
34
|
+
|
|
35
|
+
// Signing a taproot transaction should result in a valid signature
|
|
36
|
+
test(`Sign a taproot transaction`, async ({end, equal}) => {
|
|
37
|
+
const ecp = (await import('ecpair')).ECPairFactory(tinysecp);
|
|
38
|
+
const {kill, nodes} = await spawnLightningCluster({});
|
|
39
|
+
|
|
40
|
+
const [{generate, lnd}] = nodes;
|
|
41
|
+
|
|
42
|
+
try {
|
|
43
|
+
await beginGroupSigningSession({
|
|
44
|
+
lnd,
|
|
45
|
+
is_key_spend: true,
|
|
46
|
+
key_family: 0,
|
|
47
|
+
key_index: 0,
|
|
48
|
+
public_keys: [Buffer.alloc(33, 2).toString('hex')],
|
|
49
|
+
});
|
|
50
|
+
} catch (err) {
|
|
51
|
+
// On LND 0.14.3 and below, taproot signing is not supported
|
|
52
|
+
if (err.slice().shift() === 501) {
|
|
53
|
+
await kill({});
|
|
54
|
+
|
|
55
|
+
return end();
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
throw err;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
await generate({count});
|
|
62
|
+
|
|
63
|
+
const {address} = await createChainAddress({lnd});
|
|
64
|
+
const [utxo] = (await getUtxos({lnd})).utxos;
|
|
65
|
+
|
|
66
|
+
const funded = await asyncRetry({interval, times}, async () => {
|
|
67
|
+
try {
|
|
68
|
+
return await fundPsbt({
|
|
69
|
+
lnd,
|
|
70
|
+
inputs: [{
|
|
71
|
+
transaction_id: utxo.transaction_id,
|
|
72
|
+
transaction_vout: utxo.transaction_vout,
|
|
73
|
+
}],
|
|
74
|
+
outputs: [{address, tokens}],
|
|
75
|
+
});
|
|
76
|
+
} catch (err) {
|
|
77
|
+
// On LND 0.11.1 and below, funding a PSBT is not supported
|
|
78
|
+
if (err.slice().shift() === 501) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
throw err;
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
// A Taproot script output should be funded and spent with script
|
|
87
|
+
try {
|
|
88
|
+
await generate({count});
|
|
89
|
+
|
|
90
|
+
const scriptKey = await getPublicKey({lnd, family: 805});
|
|
91
|
+
|
|
92
|
+
const publicKey = hexAsBuffer(scriptKey.public_key);
|
|
93
|
+
|
|
94
|
+
const witnessScript = compile([publicKey.slice(1), OP_CHECKSIG]);
|
|
95
|
+
|
|
96
|
+
const branches = [{script: witnessScript.toString('hex')}];
|
|
97
|
+
|
|
98
|
+
const {hash} = hashForTree({branches});
|
|
99
|
+
|
|
100
|
+
const output = v1OutputScript({hash, internal_key: defaultInternalKey});
|
|
101
|
+
|
|
102
|
+
const [utxo] = (await getUtxos({lnd})).utxos.reverse();
|
|
103
|
+
|
|
104
|
+
// Make a PSBT paying to the Taproot output
|
|
105
|
+
const {psbt} = createPsbt({
|
|
106
|
+
outputs: [{tokens, script: output.script}],
|
|
107
|
+
utxos: [{id: utxo.transaction_id, vout: utxo.transaction_vout}],
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
// Sign the PSBT
|
|
111
|
+
const signed = await signPsbt({
|
|
112
|
+
lnd,
|
|
113
|
+
psbt: (await fundPsbt({lnd, psbt})).psbt,
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
// Send the tx to the chain
|
|
117
|
+
await broadcastChainTransaction({lnd, transaction: signed.transaction});
|
|
118
|
+
|
|
119
|
+
// Make a new tx that will spend the output back into the wallet
|
|
120
|
+
const tx = new Transaction();
|
|
121
|
+
|
|
122
|
+
// The new tx spends the Taproot output
|
|
123
|
+
tx.addInput(
|
|
124
|
+
fromHex(signed.transaction).getHash(),
|
|
125
|
+
fromHex(signed.transaction).outs.findIndex(n => n.value === tokens)
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
// Make an output to pay back into the wallet
|
|
129
|
+
const chainOutput = toOutputScript(
|
|
130
|
+
(await createChainAddress({lnd})).address,
|
|
131
|
+
networks.regtest
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
// Add output to the pay back transaction
|
|
135
|
+
tx.addOutput(chainOutput, smallTokens);
|
|
136
|
+
|
|
137
|
+
const {signatures} = await signTransaction({
|
|
138
|
+
lnd,
|
|
139
|
+
inputs: [{
|
|
140
|
+
key_family: 805,
|
|
141
|
+
key_index: scriptKey.index,
|
|
142
|
+
output_script: output.script,
|
|
143
|
+
output_tokens: tokens,
|
|
144
|
+
root_hash: hash,
|
|
145
|
+
sighash: Transaction.SIGHASH_DEFAULT,
|
|
146
|
+
vin: 0,
|
|
147
|
+
witness_script: witnessScript.toString('hex'),
|
|
148
|
+
}],
|
|
149
|
+
transaction: tx.toHex(),
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
const [signature] = signatures.map(hexAsBuffer);
|
|
153
|
+
|
|
154
|
+
const {block} = controlBlock({
|
|
155
|
+
external_key: output.external_key,
|
|
156
|
+
leaf_script: witnessScript.toString('hex'),
|
|
157
|
+
script_branches: branches,
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
// Add the signature to the input
|
|
161
|
+
tx.ins.forEach((input, i) => {
|
|
162
|
+
return tx.setWitness(i, [
|
|
163
|
+
signature,
|
|
164
|
+
witnessScript,
|
|
165
|
+
hexAsBuffer(block),
|
|
166
|
+
]);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
await broadcastChainTransaction({lnd, transaction: tx.toHex()});
|
|
170
|
+
|
|
171
|
+
await asyncRetry({interval, times}, async () => {
|
|
172
|
+
await generate({});
|
|
173
|
+
|
|
174
|
+
const {utxos} = await getUtxos({lnd});
|
|
175
|
+
|
|
176
|
+
const utxo = utxos.find(n => n.transaction_id === tx.getId());
|
|
177
|
+
|
|
178
|
+
if (!utxo || !utxo.confirmation_count) {
|
|
179
|
+
throw new Error('ExpectedReceivedTaprootSpend');
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
} catch (err) {
|
|
183
|
+
equal(err, null, 'Expected no error');
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// A Taproot script can be funded and spent with internal key + script hash
|
|
187
|
+
try {
|
|
188
|
+
await generate({count});
|
|
189
|
+
|
|
190
|
+
const topLevelKey = await getPublicKey({lnd, family: 805});
|
|
191
|
+
|
|
192
|
+
const unusedKey = ecp.makeRandom({network: networks.regtest});
|
|
193
|
+
|
|
194
|
+
const witnessScript = compile([unusedKey.publicKey.slice(1), OP_CHECKSIG]);
|
|
195
|
+
|
|
196
|
+
const branches = [{script: witnessScript.toString('hex')}];
|
|
197
|
+
|
|
198
|
+
const {hash} = hashForTree({branches});
|
|
199
|
+
|
|
200
|
+
const output = v1OutputScript({
|
|
201
|
+
hash,
|
|
202
|
+
internal_key: topLevelKey.public_key,
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
const [utxo] = (await getUtxos({lnd})).utxos.reverse();
|
|
206
|
+
|
|
207
|
+
// Make a PSBT paying to the Taproot output
|
|
208
|
+
const {psbt} = createPsbt({
|
|
209
|
+
outputs: [{tokens, script: output.script}],
|
|
210
|
+
utxos: [{id: utxo.transaction_id, vout: utxo.transaction_vout}],
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
// Sign the PSBT
|
|
214
|
+
const signed = await signPsbt({
|
|
215
|
+
lnd,
|
|
216
|
+
psbt: (await fundPsbt({lnd, psbt})).psbt,
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
// Send the tx to the chain
|
|
220
|
+
await broadcastChainTransaction({lnd, transaction: signed.transaction});
|
|
221
|
+
|
|
222
|
+
// Make a new tx that will spend the output back into the wallet
|
|
223
|
+
const tx = new Transaction();
|
|
224
|
+
|
|
225
|
+
// The new tx spends the Taproot output
|
|
226
|
+
tx.addInput(
|
|
227
|
+
fromHex(signed.transaction).getHash(),
|
|
228
|
+
fromHex(signed.transaction).outs.findIndex(n => n.value === tokens)
|
|
229
|
+
);
|
|
230
|
+
|
|
231
|
+
// Make an output to pay back into the wallet
|
|
232
|
+
const chainOutput = toOutputScript(
|
|
233
|
+
(await createChainAddress({lnd})).address,
|
|
234
|
+
networks.regtest
|
|
235
|
+
);
|
|
236
|
+
|
|
237
|
+
// Add output to the pay back transaction
|
|
238
|
+
tx.addOutput(chainOutput, smallTokens);
|
|
239
|
+
|
|
240
|
+
const {signatures} = await signTransaction({
|
|
241
|
+
lnd,
|
|
242
|
+
inputs: [{
|
|
243
|
+
key_family: 805,
|
|
244
|
+
key_index: topLevelKey.index,
|
|
245
|
+
output_script: output.script,
|
|
246
|
+
output_tokens: tokens,
|
|
247
|
+
root_hash: hash,
|
|
248
|
+
sighash: Transaction.SIGHASH_DEFAULT,
|
|
249
|
+
vin: 0,
|
|
250
|
+
}],
|
|
251
|
+
transaction: tx.toHex(),
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
const [signature] = signatures.map(hexAsBuffer);
|
|
255
|
+
|
|
256
|
+
// Add the signature to the input
|
|
257
|
+
tx.ins.forEach((input, i) => tx.setWitness(i, [signature]));
|
|
258
|
+
|
|
259
|
+
await broadcastChainTransaction({lnd, transaction: tx.toHex()});
|
|
260
|
+
|
|
261
|
+
await asyncRetry({interval, times}, async () => {
|
|
262
|
+
await generate({});
|
|
263
|
+
|
|
264
|
+
const {utxos} = await getUtxos({lnd});
|
|
265
|
+
|
|
266
|
+
const utxo = utxos.find(n => n.transaction_id === tx.getId());
|
|
267
|
+
|
|
268
|
+
if (!utxo || !utxo.confirmation_count) {
|
|
269
|
+
throw new Error('ExpectedReceivedTaprootSpend');
|
|
270
|
+
}
|
|
271
|
+
});
|
|
272
|
+
} catch (err) {
|
|
273
|
+
equal(err, null, 'Expected no error');
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// A Taproot script can be funded and spent with bip86 internal key
|
|
277
|
+
try {
|
|
278
|
+
await generate({count});
|
|
279
|
+
|
|
280
|
+
const topLevelKey = await getPublicKey({lnd, family: 805});
|
|
281
|
+
|
|
282
|
+
const output = v1OutputScript({
|
|
283
|
+
internal_key: topLevelKey.public_key,
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
const [utxo] = (await getUtxos({lnd})).utxos.reverse();
|
|
287
|
+
|
|
288
|
+
// Make a PSBT paying to the Taproot output
|
|
289
|
+
const {psbt} = createPsbt({
|
|
290
|
+
outputs: [{tokens, script: output.script}],
|
|
291
|
+
utxos: [{id: utxo.transaction_id, vout: utxo.transaction_vout}],
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
// Sign the PSBT
|
|
295
|
+
const signed = await signPsbt({
|
|
296
|
+
lnd,
|
|
297
|
+
psbt: (await fundPsbt({lnd, psbt})).psbt,
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
// Send the tx to the chain
|
|
301
|
+
await broadcastChainTransaction({lnd, transaction: signed.transaction});
|
|
302
|
+
|
|
303
|
+
// Make a new tx that will spend the output back into the wallet
|
|
304
|
+
const tx = new Transaction();
|
|
305
|
+
|
|
306
|
+
// The new tx spends the Taproot output
|
|
307
|
+
tx.addInput(
|
|
308
|
+
fromHex(signed.transaction).getHash(),
|
|
309
|
+
fromHex(signed.transaction).outs.findIndex(n => n.value === tokens)
|
|
310
|
+
);
|
|
311
|
+
|
|
312
|
+
// Make an output to pay back into the wallet
|
|
313
|
+
const chainOutput = toOutputScript(
|
|
314
|
+
(await createChainAddress({lnd})).address,
|
|
315
|
+
networks.regtest
|
|
316
|
+
);
|
|
317
|
+
|
|
318
|
+
// Add output to the pay back transaction
|
|
319
|
+
tx.addOutput(chainOutput, smallTokens);
|
|
320
|
+
|
|
321
|
+
const {signatures} = await signTransaction({
|
|
322
|
+
lnd,
|
|
323
|
+
inputs: [{
|
|
324
|
+
key_family: 805,
|
|
325
|
+
key_index: topLevelKey.index,
|
|
326
|
+
output_script: output.script,
|
|
327
|
+
output_tokens: tokens,
|
|
328
|
+
sighash: Transaction.SIGHASH_DEFAULT,
|
|
329
|
+
vin: 0,
|
|
330
|
+
}],
|
|
331
|
+
transaction: tx.toHex(),
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
const [signature] = signatures.map(hexAsBuffer);
|
|
335
|
+
|
|
336
|
+
// Add the signature to the input
|
|
337
|
+
tx.ins.forEach((input, i) => tx.setWitness(i, [signature]));
|
|
338
|
+
|
|
339
|
+
await broadcastChainTransaction({lnd, transaction: tx.toHex()});
|
|
340
|
+
|
|
341
|
+
await asyncRetry({interval, times}, async () => {
|
|
342
|
+
await generate({});
|
|
343
|
+
|
|
344
|
+
const {utxos} = await getUtxos({lnd});
|
|
345
|
+
|
|
346
|
+
const utxo = utxos.find(n => n.transaction_id === tx.getId());
|
|
347
|
+
|
|
348
|
+
if (!utxo || !utxo.confirmation_count) {
|
|
349
|
+
throw new Error('ExpectedReceivedTaprootSpend');
|
|
350
|
+
}
|
|
351
|
+
});
|
|
352
|
+
} catch (err) {
|
|
353
|
+
equal(err, null, 'Expected no error');
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
await kill({});
|
|
357
|
+
|
|
358
|
+
return end();
|
|
359
|
+
});
|
|
@@ -15,7 +15,7 @@ test(`Sign transaction`, async ({end, equal}) => {
|
|
|
15
15
|
inputs: [{
|
|
16
16
|
key_family: 6,
|
|
17
17
|
key_index: 1,
|
|
18
|
-
output_script: '
|
|
18
|
+
output_script: '00147ab105a90ccd7e49d96672abcac2995bdb852baa',
|
|
19
19
|
output_tokens: 1e8,
|
|
20
20
|
sighash: Transaction.SIGHASH_ALL,
|
|
21
21
|
vin: 0,
|