• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

lightningnetwork / lnd / 10190851021

01 Aug 2024 02:21AM UTC coverage: 58.627% (-0.01%) from 58.641%
10190851021

push

github

web-flow
Merge pull request #8764 from ellemouton/rb-send-via-multi-path

[3/4] Route Blinding: send MPP over multiple blinded paths

197 of 248 new or added lines in 7 files covered. (79.44%)

249 existing lines in 19 files now uncovered.

125259 of 213655 relevant lines covered (58.63%)

28116.45 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

77.73
/lnwallet/test/test_interface.go
1
package lnwallettest
2

3
import (
4
        "bytes"
5
        "crypto/sha256"
6
        "encoding/hex"
7
        "fmt"
8
        "net"
9
        "path/filepath"
10
        "reflect"
11
        "runtime"
12
        "strings"
13
        "testing"
14
        "time"
15

16
        "github.com/btcsuite/btcd/blockchain"
17
        "github.com/btcsuite/btcd/btcec/v2"
18
        "github.com/btcsuite/btcd/btcjson"
19
        "github.com/btcsuite/btcd/btcutil"
20
        "github.com/btcsuite/btcd/chaincfg"
21
        "github.com/btcsuite/btcd/chaincfg/chainhash"
22
        "github.com/btcsuite/btcd/integration/rpctest"
23
        "github.com/btcsuite/btcd/mempool"
24
        "github.com/btcsuite/btcd/rpcclient"
25
        "github.com/btcsuite/btcd/txscript"
26
        "github.com/btcsuite/btcd/wire"
27
        "github.com/btcsuite/btcwallet/chain"
28
        "github.com/btcsuite/btcwallet/wallet"
29
        "github.com/btcsuite/btcwallet/walletdb"
30
        _ "github.com/btcsuite/btcwallet/walletdb/bdb"
31
        "github.com/davecgh/go-spew/spew"
32
        "github.com/lightninglabs/neutrino"
33
        "github.com/lightningnetwork/lnd/blockcache"
34
        "github.com/lightningnetwork/lnd/chainntnfs"
35
        "github.com/lightningnetwork/lnd/chainntnfs/btcdnotify"
36
        "github.com/lightningnetwork/lnd/channeldb"
37
        "github.com/lightningnetwork/lnd/input"
38
        "github.com/lightningnetwork/lnd/keychain"
39
        "github.com/lightningnetwork/lnd/kvdb"
40
        "github.com/lightningnetwork/lnd/labels"
41
        "github.com/lightningnetwork/lnd/lntest/unittest"
42
        "github.com/lightningnetwork/lnd/lnwallet"
43
        "github.com/lightningnetwork/lnd/lnwallet/btcwallet"
44
        "github.com/lightningnetwork/lnd/lnwallet/chainfee"
45
        "github.com/lightningnetwork/lnd/lnwallet/chanfunding"
46
        "github.com/lightningnetwork/lnd/lnwire"
47
        "github.com/stretchr/testify/require"
48
)
49

50
var (
51
        bobsPrivKey = []byte{
52
                0x81, 0xb6, 0x37, 0xd8, 0xfc, 0xd2, 0xc6, 0xda,
53
                0x63, 0x59, 0xe6, 0x96, 0x31, 0x13, 0xa1, 0x17,
54
                0xd, 0xe7, 0x95, 0xe4, 0xb7, 0x25, 0xb8, 0x4d,
55
                0x1e, 0xb, 0x4c, 0xfd, 0x9e, 0xc5, 0x8c, 0xe9,
56
        }
57

58
        // Use a hard-coded HD seed.
59
        testHdSeed = chainhash.Hash{
60
                0xb7, 0x94, 0x38, 0x5f, 0x2d, 0x1e, 0xf7, 0xab,
61
                0x4d, 0x92, 0x73, 0xd1, 0x90, 0x63, 0x81, 0xb4,
62
                0x4f, 0x2f, 0x6f, 0x25, 0x88, 0xa3, 0xef, 0xb9,
63
                0x6a, 0x49, 0x18, 0x83, 0x31, 0x98, 0x47, 0x53,
64
        }
65

66
        aliceHDSeed = chainhash.Hash{
67
                0xb7, 0x94, 0x38, 0x5f, 0x2d, 0x1e, 0xf7, 0xab,
68
                0x4d, 0x92, 0x73, 0xd1, 0x90, 0x63, 0x81, 0xb4,
69
                0x4f, 0x2f, 0x6f, 0x25, 0x18, 0xa3, 0xef, 0xb9,
70
                0x64, 0x49, 0x18, 0x83, 0x31, 0x98, 0x47, 0x53,
71
        }
72
        bobHDSeed = chainhash.Hash{
73
                0xb7, 0x94, 0x38, 0x5f, 0x2d, 0x1e, 0xf7, 0xab,
74
                0x4d, 0x92, 0x73, 0xd1, 0x90, 0x63, 0x81, 0xb4,
75
                0x4f, 0x2f, 0x6f, 0x25, 0x98, 0xa3, 0xef, 0xb9,
76
                0x69, 0x49, 0x18, 0x83, 0x31, 0x98, 0x47, 0x53,
77
        }
78

79
        netParams = &chaincfg.RegressionNetParams
80
        chainHash = netParams.GenesisHash
81

82
        _, alicePub = btcec.PrivKeyFromBytes(testHdSeed[:])
83
        _, bobPub   = btcec.PrivKeyFromBytes(bobsPrivKey)
84

85
        // The number of confirmations required to consider any created channel
86
        // open.
87
        numReqConfs uint16 = 1
88

89
        csvDelay uint16 = 4
90

91
        bobAddr, _   = net.ResolveTCPAddr("tcp", "10.0.0.2:9000")
92
        aliceAddr, _ = net.ResolveTCPAddr("tcp", "10.0.0.3:9000")
93

94
        defaultMaxLocalCsvDelay uint16 = 10000
95
)
96

97
// assertProperBalance asserts than the total value of the unspent outputs
98
// within the wallet are *exactly* amount. If unable to retrieve the current
99
// balance, or the assertion fails, the test will halt with a fatal error.
100
func assertProperBalance(t *testing.T, lw *lnwallet.LightningWallet,
101
        numConfirms int32, amount float64) {
8✔
102

8✔
103
        balance, err := lw.ConfirmedBalance(numConfirms, lnwallet.DefaultAccountName)
8✔
104
        require.NoError(t, err, "unable to query for balance")
8✔
105
        if balance.ToBTC() != amount {
8✔
106
                t.Fatalf("wallet credits not properly loaded, should have 40BTC, "+
×
107
                        "instead have %v", balance)
×
108
        }
×
109
}
110

111
func assertReservationDeleted(res *lnwallet.ChannelReservation, t *testing.T) {
32✔
112
        if err := res.Cancel(); err == nil {
32✔
113
                t.Fatalf("reservation wasn't deleted from wallet")
×
114
        }
×
115
}
116

117
// mineAndAssertTxInBlock asserts that a transaction is included within the next
118
// block mined.
119
func mineAndAssertTxInBlock(t *testing.T, miner *rpctest.Harness,
120
        txid chainhash.Hash) {
34✔
121

34✔
122
        t.Helper()
34✔
123

34✔
124
        // First, we'll wait for the transaction to arrive in the mempool.
34✔
125
        if err := waitForMempoolTx(miner, &txid); err != nil {
34✔
126
                t.Fatalf("unable to find %v in the mempool: %v", txid, err)
×
127
        }
×
128

129
        // We'll mined a block to confirm it.
130
        blockHashes, err := miner.Client.Generate(1)
34✔
131
        require.NoError(t, err, "unable to generate new block")
34✔
132

34✔
133
        // Finally, we'll check it was actually mined in this block.
34✔
134
        block, err := miner.Client.GetBlock(blockHashes[0])
34✔
135
        if err != nil {
34✔
136
                t.Fatalf("unable to get block %v: %v", blockHashes[0], err)
×
137
        }
×
138
        if len(block.Transactions) != 2 {
34✔
139
                t.Fatalf("expected 2 transactions in block, found %d",
×
140
                        len(block.Transactions))
×
141
        }
×
142
        txHash := block.Transactions[1].TxHash()
34✔
143
        if txHash != txid {
34✔
144
                t.Fatalf("expected transaction %v to be mined, found %v", txid,
×
145
                        txHash)
×
146
        }
×
147
}
148

149
// newPkScript generates a new public key script of the given address type.
150
func newPkScript(t *testing.T, w *lnwallet.LightningWallet,
151
        addrType lnwallet.AddressType) []byte {
18✔
152

18✔
153
        t.Helper()
18✔
154

18✔
155
        addr, err := w.NewAddress(addrType, false, lnwallet.DefaultAccountName)
18✔
156
        require.NoError(t, err, "unable to create new address")
18✔
157
        pkScript, err := txscript.PayToAddrScript(addr)
18✔
158
        require.NoError(t, err, "unable to create output script")
18✔
159

18✔
160
        return pkScript
18✔
161
}
18✔
162

163
// sendCoins is a helper function that encompasses all the things needed for two
164
// parties to send on-chain funds to each other.
165
func sendCoins(t *testing.T, miner *rpctest.Harness,
166
        sender, receiver *lnwallet.LightningWallet, output *wire.TxOut,
167
        feeRate chainfee.SatPerKWeight, mineBlock bool, minConf int32) *wire.MsgTx { //nolint:unparam
40✔
168

40✔
169
        t.Helper()
40✔
170

40✔
171
        tx, err := sender.SendOutputs(
40✔
172
                []*wire.TxOut{output}, feeRate, minConf, labels.External,
40✔
173
                sender.Cfg.CoinSelectionStrategy,
40✔
174
        )
40✔
175
        require.NoError(t, err, "unable to send transaction")
40✔
176

40✔
177
        if mineBlock {
74✔
178
                mineAndAssertTxInBlock(t, miner, tx.TxHash())
34✔
179
        }
34✔
180

181
        if err := waitForWalletSync(miner, sender); err != nil {
40✔
182
                t.Fatalf("unable to sync alice: %v", err)
×
183
        }
×
184
        if err := waitForWalletSync(miner, receiver); err != nil {
40✔
185
                t.Fatalf("unable to sync bob: %v", err)
×
186
        }
×
187

188
        return tx
40✔
189
}
190

191
// assertTxInWallet asserts that a transaction exists in the wallet with the
192
// expected confirmation status.
193
func assertTxInWallet(t *testing.T, w *lnwallet.LightningWallet,
194
        txHash chainhash.Hash, confirmed bool) {
64✔
195

64✔
196
        t.Helper()
64✔
197

64✔
198
        // We'll fetch all of our transaction and go through each one until
64✔
199
        // finding the expected transaction with its expected confirmation
64✔
200
        // status.
64✔
201
        txs, err := w.ListTransactionDetails(0, btcwallet.UnconfirmedHeight, "")
64✔
202
        require.NoError(t, err, "unable to retrieve transactions")
64✔
203
        for _, tx := range txs {
2,016✔
204
                if tx.Hash != txHash {
3,840✔
205
                        continue
1,888✔
206
                }
207
                if tx.NumConfirmations <= 0 && confirmed {
64✔
208
                        t.Fatalf("expected transaction %v to be confirmed",
×
209
                                txHash)
×
210
                }
×
211
                if tx.NumConfirmations > 0 && !confirmed {
64✔
212
                        t.Fatalf("expected transaction %v to be unconfirmed",
×
213
                                txHash)
×
214
                }
×
215

216
                // We've found the transaction and it matches the desired
217
                // confirmation status, so we can exit.
218
                return
64✔
219
        }
220

221
        t.Fatalf("transaction %v not found", txHash)
×
222
}
223

224
func loadTestCredits(miner *rpctest.Harness, w *lnwallet.LightningWallet,
225
        numOutputs int, btcPerOutput float64) error {
20✔
226

20✔
227
        // For initial neutrino connection, wait a second.
20✔
228
        // TODO(aakselrod): Eliminate the need for this.
20✔
229
        switch w.BackEnd() {
20✔
230
        case "neutrino":
5✔
231
                time.Sleep(time.Second)
5✔
232
        }
233
        // Using the mining node, spend from a coinbase output numOutputs to
234
        // give us btcPerOutput with each output.
235
        satoshiPerOutput, err := btcutil.NewAmount(btcPerOutput)
20✔
236
        if err != nil {
20✔
237
                return fmt.Errorf("unable to create amt: %w", err)
×
238
        }
×
239
        expectedBalance, err := w.ConfirmedBalance(1, lnwallet.DefaultAccountName)
20✔
240
        if err != nil {
20✔
241
                return err
×
242
        }
×
243
        expectedBalance += btcutil.Amount(int64(satoshiPerOutput) * int64(numOutputs))
20✔
244
        addrs := make([]btcutil.Address, 0, numOutputs)
20✔
245
        for i := 0; i < numOutputs; i++ {
420✔
246
                // Grab a fresh address from the wallet to house this output.
400✔
247
                walletAddr, err := w.NewAddress(
400✔
248
                        lnwallet.WitnessPubKey, false,
400✔
249
                        lnwallet.DefaultAccountName,
400✔
250
                )
400✔
251
                if err != nil {
400✔
252
                        return err
×
253
                }
×
254

255
                script, err := txscript.PayToAddrScript(walletAddr)
400✔
256
                if err != nil {
400✔
257
                        return err
×
258
                }
×
259

260
                addrs = append(addrs, walletAddr)
400✔
261

400✔
262
                output := &wire.TxOut{
400✔
263
                        Value:    int64(satoshiPerOutput),
400✔
264
                        PkScript: script,
400✔
265
                }
400✔
266
                if _, err := miner.SendOutputs([]*wire.TxOut{output}, 2500); err != nil {
400✔
267
                        return err
×
268
                }
×
269
        }
270

271
        // TODO(roasbeef): shouldn't hardcode 10, use config param that dictates
272
        // how many confs we wait before opening a channel.
273
        // Generate 10 blocks with the mining node, this should mine all
274
        // numOutputs transactions created above. We generate 10 blocks here
275
        // in order to give all the outputs a "sufficient" number of confirmations.
276
        if _, err := miner.Client.Generate(10); err != nil {
20✔
277
                return err
×
278
        }
×
279

280
        // Wait until the wallet has finished syncing up to the main chain.
281
        ticker := time.NewTicker(100 * time.Millisecond)
20✔
282
        timeout := time.After(30 * time.Second)
20✔
283

20✔
284
        for range ticker.C {
75✔
285
                balance, err := w.ConfirmedBalance(1, lnwallet.DefaultAccountName)
55✔
286
                if err != nil {
55✔
287
                        return err
×
288
                }
×
289
                if balance == expectedBalance {
75✔
290
                        break
20✔
291
                }
292
                select {
35✔
293
                case <-timeout:
×
294
                        synced, _, err := w.IsSynced()
×
295
                        if err != nil {
×
296
                                return err
×
297
                        }
×
298
                        return fmt.Errorf("timed out after 30 seconds "+
×
299
                                "waiting for balance %v, current balance %v, "+
×
300
                                "synced: %t", expectedBalance, balance, synced)
×
301
                default:
35✔
302
                }
303
        }
304
        ticker.Stop()
20✔
305

20✔
306
        return nil
20✔
307
}
308

309
// createTestWallet creates a test LightningWallet will a total of 20BTC
310
// available for funding channels.
311
func createTestWallet(tempTestDir string, miningNode *rpctest.Harness,
312
        netParams *chaincfg.Params, notifier chainntnfs.ChainNotifier,
313
        wc lnwallet.WalletController, keyRing keychain.SecretKeyRing,
314
        signer input.Signer, bio lnwallet.BlockChainIO) (*lnwallet.LightningWallet, error) {
8✔
315

8✔
316
        dbDir := filepath.Join(tempTestDir, "cdb")
8✔
317
        fullDB, err := channeldb.Open(dbDir)
8✔
318
        if err != nil {
8✔
319
                return nil, err
×
320
        }
×
321

322
        cfg := lnwallet.Config{
8✔
323
                Database:              fullDB.ChannelStateDB(),
8✔
324
                Notifier:              notifier,
8✔
325
                SecretKeyRing:         keyRing,
8✔
326
                WalletController:      wc,
8✔
327
                Signer:                signer,
8✔
328
                ChainIO:               bio,
8✔
329
                FeeEstimator:          chainfee.NewStaticEstimator(2500, 0),
8✔
330
                NetParams:             *netParams,
8✔
331
                CoinSelectionStrategy: wallet.CoinSelectionLargest,
8✔
332
        }
8✔
333

8✔
334
        wallet, err := lnwallet.NewLightningWallet(cfg)
8✔
335
        if err != nil {
8✔
336
                return nil, err
×
337
        }
×
338

339
        if err := wallet.Startup(); err != nil {
8✔
340
                return nil, err
×
341
        }
×
342

343
        // Load our test wallet with 20 outputs each holding 4BTC.
344
        if err := loadTestCredits(miningNode, wallet, 20, 4); err != nil {
8✔
345
                return nil, err
×
346
        }
×
347

348
        return wallet, nil
8✔
349
}
350

351
func testGetRecoveryInfo(miner *rpctest.Harness,
352
        alice, bob *lnwallet.LightningWallet, t *testing.T) {
4✔
353

4✔
354
        // alice's wallet is in recovery mode
4✔
355
        expectedRecoveryMode := true
4✔
356
        expectedProgress := float64(1)
4✔
357

4✔
358
        isRecoveryMode, progress, err := alice.GetRecoveryInfo()
4✔
359
        require.NoError(t, err, "unable to get alice's recovery info")
4✔
360

4✔
361
        require.Equal(t,
4✔
362
                expectedRecoveryMode, isRecoveryMode, "recovery mode incorrect",
4✔
363
        )
4✔
364
        require.Equal(t, expectedProgress, progress, "progress incorrect")
4✔
365

4✔
366
        // Generate 5 blocks and check the recovery process again.
4✔
367
        const numBlocksMined = 5
4✔
368
        _, err = miner.Client.Generate(numBlocksMined)
4✔
369
        require.NoError(t, err, "unable to mine blocks")
4✔
370

4✔
371
        // Check the recovery process. Once synced, the progress should be 1.
4✔
372
        err = waitForWalletSync(miner, alice)
4✔
373
        require.NoError(t, err, "Couldn't sync Alice's wallet")
4✔
374

4✔
375
        isRecoveryMode, progress, err = alice.GetRecoveryInfo()
4✔
376
        require.NoError(t, err, "unable to get alice's recovery info")
4✔
377

4✔
378
        require.Equal(t,
4✔
379
                expectedRecoveryMode, isRecoveryMode, "recovery mode incorrect",
4✔
380
        )
4✔
381
        require.Equal(t, expectedProgress, progress, "progress incorrect")
4✔
382

4✔
383
        // bob's wallet is not in recovery mode
4✔
384
        expectedRecoveryMode = false
4✔
385
        expectedProgress = float64(0)
4✔
386

4✔
387
        isRecoveryMode, progress, err = bob.GetRecoveryInfo()
4✔
388
        require.NoError(t, err, "unable to get bob's recovery info")
4✔
389

4✔
390
        require.Equal(t,
4✔
391
                expectedRecoveryMode, isRecoveryMode, "recovery mode incorrect",
4✔
392
        )
4✔
393
        require.Equal(t, expectedProgress, progress, "progress incorrect")
4✔
394
}
4✔
395

396
func testDualFundingReservationWorkflow(miner *rpctest.Harness,
397
        alice, bob *lnwallet.LightningWallet, t *testing.T) {
3✔
398

3✔
399
        t.Skipf("dual funding isn't exposed on the p2p layer")
3✔
400

3✔
401
        fundingAmount, err := btcutil.NewAmount(5)
3✔
402
        require.NoError(t, err, "unable to create amt")
3✔
403

3✔
404
        // In this scenario, we'll test a dual funder reservation, with each
3✔
405
        // side putting in 10 BTC.
3✔
406

3✔
407
        // Alice initiates a channel funded with 5 BTC for each side, so 10 BTC
3✔
408
        // total. She also generates 2 BTC in change.
3✔
409
        feePerKw, err := alice.Cfg.FeeEstimator.EstimateFeePerKW(1)
3✔
410
        require.NoError(t, err, "unable to query fee estimator")
3✔
411
        aliceReq := &lnwallet.InitFundingReserveMsg{
3✔
412
                ChainHash:        chainHash,
3✔
413
                NodeID:           bobPub,
3✔
414
                NodeAddr:         bobAddr,
3✔
415
                LocalFundingAmt:  fundingAmount,
3✔
416
                RemoteFundingAmt: fundingAmount,
3✔
417
                CommitFeePerKw:   feePerKw,
3✔
418
                FundingFeePerKw:  feePerKw,
3✔
419
                PushMSat:         0,
3✔
420
                Flags:            lnwire.FFAnnounceChannel,
3✔
421
        }
3✔
422
        aliceChanReservation, err := alice.InitChannelReservation(aliceReq)
3✔
423
        require.NoError(t, err, "unable to initialize funding reservation")
3✔
424
        aliceChanReservation.SetNumConfsRequired(numReqConfs)
3✔
425
        channelConstraints := &channeldb.ChannelConstraints{
3✔
426
                DustLimit:        lnwallet.DustLimitUnknownWitness(),
3✔
427
                ChanReserve:      fundingAmount / 100,
3✔
428
                MaxPendingAmount: lnwire.NewMSatFromSatoshis(fundingAmount),
3✔
429
                MinHTLC:          1,
3✔
430
                MaxAcceptedHtlcs: input.MaxHTLCNumber / 2,
3✔
431
                CsvDelay:         csvDelay,
3✔
432
        }
3✔
433
        err = aliceChanReservation.CommitConstraints(
3✔
434
                channelConstraints, defaultMaxLocalCsvDelay, false,
3✔
435
        )
3✔
436
        require.NoError(t, err, "unable to verify constraints")
3✔
437

3✔
438
        // The channel reservation should now be populated with a multi-sig key
3✔
439
        // from our HD chain, a change output with 3 BTC, and 2 outputs
3✔
440
        // selected of 4 BTC each. Additionally, the rest of the items needed
3✔
441
        // to fulfill a funding contribution should also have been filled in.
3✔
442
        aliceContribution := aliceChanReservation.OurContribution()
3✔
443
        if len(aliceContribution.Inputs) != 2 {
3✔
444
                t.Fatalf("outputs for funding tx not properly selected, have %v "+
×
445
                        "outputs should have 2", len(aliceContribution.Inputs))
×
446
        }
×
447
        assertContributionInitPopulated(t, aliceContribution)
×
448

×
449
        // Bob does the same, generating his own contribution. He then also
×
450
        // receives' Alice's contribution, and consumes that so we can continue
×
451
        // the funding process.
×
452
        bobReq := &lnwallet.InitFundingReserveMsg{
×
453
                ChainHash:        chainHash,
×
454
                NodeID:           alicePub,
×
455
                NodeAddr:         aliceAddr,
×
456
                LocalFundingAmt:  fundingAmount,
×
457
                RemoteFundingAmt: fundingAmount,
×
458
                CommitFeePerKw:   feePerKw,
×
459
                FundingFeePerKw:  feePerKw,
×
460
                PushMSat:         0,
×
461
                Flags:            lnwire.FFAnnounceChannel,
×
462
        }
×
463
        bobChanReservation, err := bob.InitChannelReservation(bobReq)
×
464
        require.NoError(t, err, "bob unable to init channel reservation")
×
465
        err = bobChanReservation.CommitConstraints(
×
466
                channelConstraints, defaultMaxLocalCsvDelay, true,
×
467
        )
×
468
        require.NoError(t, err, "unable to verify constraints")
×
469
        bobChanReservation.SetNumConfsRequired(numReqConfs)
×
470

×
471
        assertContributionInitPopulated(t, bobChanReservation.OurContribution())
×
472

×
473
        err = bobChanReservation.ProcessContribution(aliceContribution)
×
474
        require.NoError(t, err, "bob unable to process alice's contribution")
×
475
        assertContributionInitPopulated(t, bobChanReservation.TheirContribution())
×
476

×
477
        bobContribution := bobChanReservation.OurContribution()
×
478

×
479
        // Bob then sends over his contribution, which will be consumed by
×
480
        // Alice. After this phase, Alice should have all the necessary
×
481
        // material required to craft the funding transaction and commitment
×
482
        // transactions.
×
483
        err = aliceChanReservation.ProcessContribution(bobContribution)
×
484
        require.NoError(t, err, "alice unable to process bob's contribution")
×
485
        assertContributionInitPopulated(t, aliceChanReservation.TheirContribution())
×
486

×
487
        // At this point, all Alice's signatures should be fully populated.
×
488
        aliceFundingSigs, aliceCommitSig := aliceChanReservation.OurSignatures()
×
489
        if aliceFundingSigs == nil {
×
490
                t.Fatalf("alice's funding signatures not populated")
×
491
        }
×
492
        if aliceCommitSig == nil {
×
493
                t.Fatalf("alice's commit signatures not populated")
×
494
        }
×
495

496
        // Additionally, Bob's signatures should also be fully populated.
497
        bobFundingSigs, bobCommitSig := bobChanReservation.OurSignatures()
×
498
        if bobFundingSigs == nil {
×
499
                t.Fatalf("bob's funding signatures not populated")
×
500
        }
×
501
        if bobCommitSig == nil {
×
502
                t.Fatalf("bob's commit signatures not populated")
×
503
        }
×
504

505
        // To conclude, we'll consume first Alice's signatures with Bob, and
506
        // then the other way around.
507
        _, err = aliceChanReservation.CompleteReservation(
×
508
                bobFundingSigs, bobCommitSig,
×
509
        )
×
510
        if err != nil {
×
511
                for _, in := range aliceChanReservation.FinalFundingTx().TxIn {
×
512
                        fmt.Println(in.PreviousOutPoint.String())
×
513
                }
×
514
                t.Fatalf("unable to consume alice's sigs: %v", err)
×
515
        }
516
        _, err = bobChanReservation.CompleteReservation(
×
517
                aliceFundingSigs, aliceCommitSig,
×
518
        )
×
519
        require.NoError(t, err, "unable to consume bob's sigs")
×
520

×
521
        // At this point, the funding tx should have been populated.
×
522
        fundingTx := aliceChanReservation.FinalFundingTx()
×
523
        if fundingTx == nil {
×
524
                t.Fatalf("funding transaction never created!")
×
525
        }
×
526

527
        // The resulting active channel state should have been persisted to the
528
        // DB.
529
        fundingSha := fundingTx.TxHash()
×
530
        aliceChannels, err := alice.Cfg.Database.FetchOpenChannels(bobPub)
×
531
        require.NoError(t, err, "unable to retrieve channel from DB")
×
532
        if !bytes.Equal(aliceChannels[0].FundingOutpoint.Hash[:], fundingSha[:]) {
×
533
                t.Fatalf("channel state not properly saved")
×
534
        }
×
535
        if !aliceChannels[0].ChanType.IsDualFunder() {
×
536
                t.Fatalf("channel not detected as dual funder")
×
537
        }
×
538
        bobChannels, err := bob.Cfg.Database.FetchOpenChannels(alicePub)
×
539
        require.NoError(t, err, "unable to retrieve channel from DB")
×
540
        if !bytes.Equal(bobChannels[0].FundingOutpoint.Hash[:], fundingSha[:]) {
×
541
                t.Fatalf("channel state not properly saved")
×
542
        }
×
543
        if !bobChannels[0].ChanType.IsDualFunder() {
×
544
                t.Fatalf("channel not detected as dual funder")
×
545
        }
×
546

547
        // Let Alice publish the funding transaction.
548
        err = alice.PublishTransaction(fundingTx, "")
×
549
        require.NoError(t, err, "unable to publish funding tx")
×
550

×
551
        // Mine a single block, the funding transaction should be included
×
552
        // within this block.
×
553
        err = waitForMempoolTx(miner, &fundingSha)
×
554
        require.NoError(t, err, "tx not relayed to miner")
×
555
        blockHashes, err := miner.Client.Generate(1)
×
556
        require.NoError(t, err, "unable to generate block")
×
557
        block, err := miner.Client.GetBlock(blockHashes[0])
×
558
        require.NoError(t, err, "unable to find block")
×
559
        if len(block.Transactions) != 2 {
×
560
                t.Fatalf("funding transaction wasn't mined: %v", err)
×
561
        }
×
562
        blockTx := block.Transactions[1]
×
563
        if blockTx.TxHash() != fundingSha {
×
564
                t.Fatalf("incorrect transaction was mined")
×
565
        }
×
566

567
        assertReservationDeleted(aliceChanReservation, t)
×
568
        assertReservationDeleted(bobChanReservation, t)
×
569

×
570
        // Wait for wallets to catch up to prevent issues in subsequent tests.
×
571
        err = waitForWalletSync(miner, alice)
×
572
        require.NoError(t, err, "unable to sync alice")
×
573
        err = waitForWalletSync(miner, bob)
×
574
        require.NoError(t, err, "unable to sync bob")
×
575
}
576

577
func testFundingTransactionLockedOutputs(miner *rpctest.Harness,
578
        alice, _ *lnwallet.LightningWallet, t *testing.T) {
4✔
579

4✔
580
        // Create a single channel asking for 16 BTC total.
4✔
581
        fundingAmount, err := btcutil.NewAmount(8)
4✔
582
        require.NoError(t, err, "unable to create amt")
4✔
583
        feePerKw, err := alice.Cfg.FeeEstimator.EstimateFeePerKW(1)
4✔
584
        require.NoError(t, err, "unable to query fee estimator")
4✔
585
        req := &lnwallet.InitFundingReserveMsg{
4✔
586
                ChainHash:        chainHash,
4✔
587
                NodeID:           bobPub,
4✔
588
                NodeAddr:         bobAddr,
4✔
589
                LocalFundingAmt:  fundingAmount,
4✔
590
                RemoteFundingAmt: 0,
4✔
591
                CommitFeePerKw:   feePerKw,
4✔
592
                FundingFeePerKw:  feePerKw,
4✔
593
                PushMSat:         0,
4✔
594
                Flags:            lnwire.FFAnnounceChannel,
4✔
595
                PendingChanID:    [32]byte{0, 1, 2, 3},
4✔
596
        }
4✔
597
        if _, err := alice.InitChannelReservation(req); err != nil {
4✔
598
                t.Fatalf("unable to initialize funding reservation 1: %v", err)
×
599
        }
×
600

601
        // Now attempt to reserve funds for another channel, this time
602
        // requesting 900 BTC. We only have around 64BTC worth of outpoints
603
        // that aren't locked, so this should fail.
604
        amt, err := btcutil.NewAmount(900)
4✔
605
        require.NoError(t, err, "unable to create amt")
4✔
606
        failedReq := &lnwallet.InitFundingReserveMsg{
4✔
607
                ChainHash:        chainHash,
4✔
608
                NodeID:           bobPub,
4✔
609
                NodeAddr:         bobAddr,
4✔
610
                LocalFundingAmt:  amt,
4✔
611
                RemoteFundingAmt: 0,
4✔
612
                CommitFeePerKw:   feePerKw,
4✔
613
                FundingFeePerKw:  feePerKw,
4✔
614
                PushMSat:         0,
4✔
615
                Flags:            lnwire.FFAnnounceChannel,
4✔
616
                PendingChanID:    [32]byte{1, 2, 3, 4},
4✔
617
        }
4✔
618
        failedReservation, err := alice.InitChannelReservation(failedReq)
4✔
619
        if err == nil {
4✔
620
                t.Fatalf("not error returned, should fail on coin selection")
×
621
        }
×
622
        if _, ok := err.(*chanfunding.ErrInsufficientFunds); !ok {
4✔
623
                t.Fatalf("error not coinselect error: %v", err)
×
624
        }
×
625
        if failedReservation != nil {
4✔
626
                t.Fatalf("reservation should be nil")
×
627
        }
×
628
}
629

630
func testFundingCancellationNotEnoughFunds(miner *rpctest.Harness,
631
        alice, _ *lnwallet.LightningWallet, t *testing.T) {
4✔
632

4✔
633
        feePerKw, err := alice.Cfg.FeeEstimator.EstimateFeePerKW(1)
4✔
634
        require.NoError(t, err, "unable to query fee estimator")
4✔
635

4✔
636
        // Create a reservation for 44 BTC.
4✔
637
        fundingAmount, err := btcutil.NewAmount(44)
4✔
638
        require.NoError(t, err, "unable to create amt")
4✔
639
        req := &lnwallet.InitFundingReserveMsg{
4✔
640
                ChainHash:        chainHash,
4✔
641
                NodeID:           bobPub,
4✔
642
                NodeAddr:         bobAddr,
4✔
643
                LocalFundingAmt:  fundingAmount,
4✔
644
                RemoteFundingAmt: 0,
4✔
645
                CommitFeePerKw:   feePerKw,
4✔
646
                FundingFeePerKw:  feePerKw,
4✔
647
                PushMSat:         0,
4✔
648
                Flags:            lnwire.FFAnnounceChannel,
4✔
649
                PendingChanID:    [32]byte{2, 3, 4, 5},
4✔
650
        }
4✔
651
        chanReservation, err := alice.InitChannelReservation(req)
4✔
652
        require.NoError(t, err, "unable to initialize funding reservation")
4✔
653

4✔
654
        // Attempt to create another channel with 44 BTC, this should fail.
4✔
655
        req.PendingChanID = [32]byte{3, 4, 5, 6}
4✔
656
        _, err = alice.InitChannelReservation(req)
4✔
657
        if _, ok := err.(*chanfunding.ErrInsufficientFunds); !ok {
4✔
658
                t.Fatalf("coin selection succeeded should have insufficient funds: %v",
×
659
                        err)
×
660
        }
×
661

662
        // Now cancel that old reservation.
663
        if err := chanReservation.Cancel(); err != nil {
4✔
664
                t.Fatalf("unable to cancel reservation: %v", err)
×
665
        }
×
666

667
        // Those outpoints should no longer be locked.
668
        lockedOutPoints := alice.LockedOutpoints()
4✔
669
        if len(lockedOutPoints) != 0 {
4✔
670
                t.Fatalf("outpoints still locked")
×
671
        }
×
672

673
        // Reservation ID should no longer be tracked.
674
        numReservations := alice.ActiveReservations()
4✔
675
        if len(alice.ActiveReservations()) != 0 {
4✔
676
                t.Fatalf("should have 0 reservations, instead have %v",
×
677
                        numReservations)
×
678
        }
×
679

680
        // TODO(roasbeef): create method like Balance that ignores locked
681
        // outpoints, will let us fail early/fast instead of querying and
682
        // attempting coin selection.
683

684
        // Request to fund a new channel should now succeed.
685
        req.PendingChanID = [32]byte{4, 5, 6, 7, 8}
4✔
686
        if _, err := alice.InitChannelReservation(req); err != nil {
4✔
687
                t.Fatalf("unable to initialize funding reservation: %v", err)
×
688
        }
×
689
}
690

691
func testCancelNonExistentReservation(miner *rpctest.Harness,
692
        alice, _ *lnwallet.LightningWallet, t *testing.T) {
4✔
693

4✔
694
        feePerKw, err := alice.Cfg.FeeEstimator.EstimateFeePerKW(1)
4✔
695
        require.NoError(t, err, "unable to query fee estimator")
4✔
696

4✔
697
        req := &lnwallet.InitFundingReserveMsg{
4✔
698
                CommitFeePerKw: feePerKw,
4✔
699
                PushMSat:       10,
4✔
700
                Flags:          lnwire.FFAnnounceChannel,
4✔
701
                CommitType:     lnwallet.CommitmentTypeTweakless,
4✔
702
                PendingChanID:  [32]byte{},
4✔
703
        }
4✔
704

4✔
705
        // Create our own reservation, give it some ID.
4✔
706
        res, err := lnwallet.NewChannelReservation(
4✔
707
                10000, 10000, alice, 22, &testHdSeed, 0, req,
4✔
708
        )
4✔
709
        require.NoError(t, err, "unable to create res")
4✔
710

4✔
711
        // Attempt to cancel this reservation. This should fail, we know
4✔
712
        // nothing of it.
4✔
713
        if err := res.Cancel(); err == nil {
4✔
714
                t.Fatalf("canceled non-existent reservation")
×
715
        }
×
716
}
717

718
func testReservationInitiatorBalanceBelowDustCancel(miner *rpctest.Harness,
719
        alice, _ *lnwallet.LightningWallet, t *testing.T) {
4✔
720

4✔
721
        // We'll attempt to create a new reservation with an extremely high
4✔
722
        // commitment fee rate. This should push our balance into the negative
4✔
723
        // and result in a failure to create the reservation.
4✔
724
        const numBTC = 4
4✔
725
        fundingAmount, err := btcutil.NewAmount(numBTC)
4✔
726
        require.NoError(t, err, "unable to create amt")
4✔
727

4✔
728
        feePerKw := chainfee.SatPerKWeight(
4✔
729
                numBTC * numBTC * btcutil.SatoshiPerBitcoin,
4✔
730
        )
4✔
731
        req := &lnwallet.InitFundingReserveMsg{
4✔
732
                ChainHash:        chainHash,
4✔
733
                NodeID:           bobPub,
4✔
734
                NodeAddr:         bobAddr,
4✔
735
                LocalFundingAmt:  fundingAmount,
4✔
736
                RemoteFundingAmt: 0,
4✔
737
                CommitFeePerKw:   feePerKw,
4✔
738
                FundingFeePerKw:  1000,
4✔
739
                PushMSat:         0,
4✔
740
                Flags:            lnwire.FFAnnounceChannel,
4✔
741
                CommitType:       lnwallet.CommitmentTypeTweakless,
4✔
742
                PendingChanID:    [32]byte{1},
4✔
743
        }
4✔
744
        _, err = alice.InitChannelReservation(req)
4✔
745
        switch {
4✔
746
        case err == nil:
×
747
                t.Fatalf("initialization should have failed due to " +
×
748
                        "insufficient local amount")
×
749

750
        case !strings.Contains(err.Error(), "funder balance too small"):
×
751
                t.Fatalf("incorrect error: %v", err)
×
752
        }
753
}
754

755
func assertContributionInitPopulated(t *testing.T, c *lnwallet.ChannelContribution) {
64✔
756
        _, _, line, _ := runtime.Caller(1)
64✔
757

64✔
758
        if c.FirstCommitmentPoint == nil {
64✔
759
                t.Fatalf("line #%v: commitment point not fond", line)
×
760
        }
×
761

762
        if c.CsvDelay == 0 {
64✔
763
                t.Fatalf("line #%v: csv delay not set", line)
×
764
        }
×
765

766
        if c.MultiSigKey.PubKey == nil {
64✔
767
                t.Fatalf("line #%v: multi-sig key not set", line)
×
768
        }
×
769
        if c.RevocationBasePoint.PubKey == nil {
64✔
770
                t.Fatalf("line #%v: revocation key not set", line)
×
771
        }
×
772
        if c.PaymentBasePoint.PubKey == nil {
64✔
773
                t.Fatalf("line #%v: payment key not set", line)
×
774
        }
×
775
        if c.DelayBasePoint.PubKey == nil {
64✔
776
                t.Fatalf("line #%v: delay key not set", line)
×
777
        }
×
778

779
        if c.DustLimit == 0 {
64✔
780
                t.Fatalf("line #%v: dust limit not set", line)
×
781
        }
×
782
        if c.MaxPendingAmount == 0 {
64✔
783
                t.Fatalf("line #%v: max pending amt not set", line)
×
784
        }
×
785
        if c.ChanReserve == 0 {
64✔
786
                t.Fatalf("line #%v: chan reserve not set", line)
×
787
        }
×
788
        if c.MinHTLC == 0 {
64✔
789
                t.Fatalf("line #%v: min htlc not set", line)
×
790
        }
×
791
        if c.MaxAcceptedHtlcs == 0 {
64✔
792
                t.Fatalf("line #%v: max accepted htlc's not set", line)
×
793
        }
×
794
}
795

796
func testSingleFunderReservationWorkflow(miner *rpctest.Harness,
797
        alice, bob *lnwallet.LightningWallet, t *testing.T,
798
        commitType lnwallet.CommitmentType,
799
        aliceChanFunder chanfunding.Assembler, fetchFundingTx func() *wire.MsgTx,
800
        pendingChanID [32]byte, thawHeight uint32) {
16✔
801

16✔
802
        // For this scenario, Alice will be the channel initiator while bob
16✔
803
        // will act as the responder to the workflow.
16✔
804

16✔
805
        // First, Alice will Initialize a reservation for a channel with 4 BTC
16✔
806
        // funded solely by us. We'll also initially push 1 BTC of the channel
16✔
807
        // towards Bob's side.
16✔
808
        fundingAmt, err := btcutil.NewAmount(4)
16✔
809
        require.NoError(t, err, "unable to create amt")
16✔
810
        pushAmt := lnwire.NewMSatFromSatoshis(btcutil.SatoshiPerBitcoin)
16✔
811
        feePerKw, err := alice.Cfg.FeeEstimator.EstimateFeePerKW(1)
16✔
812
        require.NoError(t, err, "unable to query fee estimator")
16✔
813
        aliceReq := &lnwallet.InitFundingReserveMsg{
16✔
814
                ChainHash:        chainHash,
16✔
815
                PendingChanID:    pendingChanID,
16✔
816
                NodeID:           bobPub,
16✔
817
                NodeAddr:         bobAddr,
16✔
818
                LocalFundingAmt:  fundingAmt,
16✔
819
                RemoteFundingAmt: 0,
16✔
820
                CommitFeePerKw:   feePerKw,
16✔
821
                FundingFeePerKw:  feePerKw,
16✔
822
                PushMSat:         pushAmt,
16✔
823
                Flags:            lnwire.FFAnnounceChannel,
16✔
824
                CommitType:       commitType,
16✔
825
                ChanFunder:       aliceChanFunder,
16✔
826
        }
16✔
827
        aliceChanReservation, err := alice.InitChannelReservation(aliceReq)
16✔
828
        require.NoError(t, err, "unable to init channel reservation")
16✔
829
        aliceChanReservation.SetNumConfsRequired(numReqConfs)
16✔
830
        channelConstraints := &channeldb.ChannelConstraints{
16✔
831
                DustLimit:        lnwallet.DustLimitUnknownWitness(),
16✔
832
                ChanReserve:      fundingAmt / 100,
16✔
833
                MaxPendingAmount: lnwire.NewMSatFromSatoshis(fundingAmt),
16✔
834
                MinHTLC:          1,
16✔
835
                MaxAcceptedHtlcs: input.MaxHTLCNumber / 2,
16✔
836
                CsvDelay:         csvDelay,
16✔
837
        }
16✔
838
        err = aliceChanReservation.CommitConstraints(
16✔
839
                channelConstraints, defaultMaxLocalCsvDelay, false,
16✔
840
        )
16✔
841
        require.NoError(t, err, "unable to verify constraints")
16✔
842

16✔
843
        // Verify all contribution fields have been set properly, but only if
16✔
844
        // Alice is the funder herself.
16✔
845
        aliceContribution := aliceChanReservation.OurContribution()
16✔
846
        if fetchFundingTx == nil {
28✔
847
                if len(aliceContribution.Inputs) < 1 {
12✔
848
                        t.Fatalf("outputs for funding tx not properly "+
×
849
                                "selected, have %v outputs should at least 1",
×
850
                                len(aliceContribution.Inputs))
×
851
                }
×
852
                if len(aliceContribution.ChangeOutputs) != 1 {
12✔
853
                        t.Fatalf("coin selection failed, should have one "+
×
854
                                "change outputs, instead have: %v",
×
855
                                len(aliceContribution.ChangeOutputs))
×
856
                }
×
857
        }
858
        assertContributionInitPopulated(t, aliceContribution)
16✔
859

16✔
860
        // Next, Bob receives the initial request, generates a corresponding
16✔
861
        // reservation initiation, then consume Alice's contribution.
16✔
862
        bobReq := &lnwallet.InitFundingReserveMsg{
16✔
863
                ChainHash:        chainHash,
16✔
864
                PendingChanID:    pendingChanID,
16✔
865
                NodeID:           alicePub,
16✔
866
                NodeAddr:         aliceAddr,
16✔
867
                LocalFundingAmt:  0,
16✔
868
                RemoteFundingAmt: fundingAmt,
16✔
869
                CommitFeePerKw:   feePerKw,
16✔
870
                FundingFeePerKw:  feePerKw,
16✔
871
                PushMSat:         pushAmt,
16✔
872
                Flags:            lnwire.FFAnnounceChannel,
16✔
873
                CommitType:       commitType,
16✔
874
        }
16✔
875
        bobChanReservation, err := bob.InitChannelReservation(bobReq)
16✔
876
        require.NoError(t, err, "unable to create bob reservation")
16✔
877
        err = bobChanReservation.CommitConstraints(
16✔
878
                channelConstraints, defaultMaxLocalCsvDelay, true,
16✔
879
        )
16✔
880
        require.NoError(t, err, "unable to verify constraints")
16✔
881
        bobChanReservation.SetNumConfsRequired(numReqConfs)
16✔
882

16✔
883
        // We'll ensure that Bob's contribution also gets generated properly.
16✔
884
        bobContribution := bobChanReservation.OurContribution()
16✔
885
        assertContributionInitPopulated(t, bobContribution)
16✔
886

16✔
887
        // With his contribution generated, he can now process Alice's
16✔
888
        // contribution.
16✔
889
        err = bobChanReservation.ProcessSingleContribution(aliceContribution)
16✔
890
        require.NoError(t, err, "bob unable to process alice's contribution")
16✔
891
        assertContributionInitPopulated(t, bobChanReservation.TheirContribution())
16✔
892

16✔
893
        // Bob will next send over his contribution to Alice, we simulate this
16✔
894
        // by having Alice immediately process his contribution.
16✔
895
        err = aliceChanReservation.ProcessContribution(bobContribution)
16✔
896
        if err != nil {
16✔
897
                t.Fatalf("alice unable to process bob's contribution: %v", err)
×
898
        }
×
899
        assertContributionInitPopulated(t, bobChanReservation.TheirContribution())
16✔
900

16✔
901
        // At this point, Alice should have generated all the signatures
16✔
902
        // required for the funding transaction, as well as Alice's commitment
16✔
903
        // signature to bob, but only if the funding transaction was
16✔
904
        // constructed internally.
16✔
905
        aliceRemoteContribution := aliceChanReservation.TheirContribution()
16✔
906
        aliceFundingSigs, aliceCommitSig := aliceChanReservation.OurSignatures()
16✔
907
        if fetchFundingTx == nil && aliceFundingSigs == nil {
16✔
908
                t.Fatalf("funding sigs not found")
×
909
        }
×
910
        if aliceCommitSig == nil {
16✔
911
                t.Fatalf("commitment sig not found")
×
912
        }
×
913

914
        // Additionally, the funding tx and the funding outpoint should have
915
        // been populated.
916
        if aliceChanReservation.FinalFundingTx() == nil && fetchFundingTx == nil {
16✔
917
                t.Fatalf("funding transaction never created!")
×
918
        }
×
919
        if aliceChanReservation.FundingOutpoint() == nil {
16✔
920
                t.Fatalf("funding outpoint never created!")
×
921
        }
×
922

923
        // Their funds should also be filled in.
924
        if len(aliceRemoteContribution.Inputs) != 0 {
16✔
925
                t.Fatalf("bob shouldn't have any inputs, instead has %v",
×
926
                        len(aliceRemoteContribution.Inputs))
×
927
        }
×
928
        if len(aliceRemoteContribution.ChangeOutputs) != 0 {
16✔
929
                t.Fatalf("bob shouldn't have any change outputs, instead "+
×
930
                        "has %v",
×
931
                        aliceRemoteContribution.ChangeOutputs[0].Value)
×
932
        }
×
933

934
        // Next, Alice will send over her signature for Bob's commitment
935
        // transaction, as well as the funding outpoint.
936
        fundingPoint := aliceChanReservation.FundingOutpoint()
16✔
937
        _, err = bobChanReservation.CompleteReservationSingle(
16✔
938
                fundingPoint, aliceCommitSig,
16✔
939
        )
16✔
940
        require.NoError(t, err, "bob unable to consume single reservation")
16✔
941

16✔
942
        // Finally, we'll conclude the reservation process by sending over
16✔
943
        // Bob's commitment signature, which is the final thing Alice needs to
16✔
944
        // be able to safely broadcast the funding transaction.
16✔
945
        _, bobCommitSig := bobChanReservation.OurSignatures()
16✔
946
        if bobCommitSig == nil {
16✔
947
                t.Fatalf("bob failed to generate commitment signature: %v", err)
×
948
        }
×
949
        _, err = aliceChanReservation.CompleteReservation(
16✔
950
                nil, bobCommitSig,
16✔
951
        )
16✔
952
        require.NoError(t, err, "alice unable to complete reservation")
16✔
953

16✔
954
        // If the caller provided an alternative way to obtain the funding tx,
16✔
955
        // then we'll use that. Otherwise, we'll obtain it directly from Alice.
16✔
956
        var fundingTx *wire.MsgTx
16✔
957
        if fetchFundingTx != nil {
20✔
958
                fundingTx = fetchFundingTx()
4✔
959
        } else {
16✔
960
                fundingTx = aliceChanReservation.FinalFundingTx()
12✔
961
        }
12✔
962

963
        // The resulting active channel state should have been persisted to the
964
        // DB for both Alice and Bob.
965
        fundingSha := fundingTx.TxHash()
16✔
966
        aliceChannels, err := alice.Cfg.Database.FetchOpenChannels(bobPub)
16✔
967
        require.NoError(t, err, "unable to retrieve channel from DB")
16✔
968
        if len(aliceChannels) != 1 {
16✔
969
                t.Fatalf("alice didn't save channel state: %v", err)
×
970
        }
×
971
        if !bytes.Equal(aliceChannels[0].FundingOutpoint.Hash[:], fundingSha[:]) {
16✔
972
                t.Fatalf("channel state not properly saved: %v vs %v",
×
973
                        hex.EncodeToString(aliceChannels[0].FundingOutpoint.Hash[:]),
×
974
                        hex.EncodeToString(fundingSha[:]))
×
975
        }
×
976
        if !aliceChannels[0].IsInitiator {
16✔
977
                t.Fatalf("alice not detected as channel initiator")
×
978
        }
×
979
        if !aliceChannels[0].ChanType.IsSingleFunder() {
16✔
980
                t.Fatalf("channel type is incorrect, expected %v instead got %v",
×
981
                        channeldb.SingleFunderBit, aliceChannels[0].ChanType)
×
982
        }
×
983

984
        bobChannels, err := bob.Cfg.Database.FetchOpenChannels(alicePub)
16✔
985
        require.NoError(t, err, "unable to retrieve channel from DB")
16✔
986
        if len(bobChannels) != 1 {
16✔
987
                t.Fatalf("bob didn't save channel state: %v", err)
×
988
        }
×
989
        if !bytes.Equal(bobChannels[0].FundingOutpoint.Hash[:], fundingSha[:]) {
16✔
990
                t.Fatalf("channel state not properly saved: %v vs %v",
×
991
                        hex.EncodeToString(bobChannels[0].FundingOutpoint.Hash[:]),
×
992
                        hex.EncodeToString(fundingSha[:]))
×
993
        }
×
994
        if bobChannels[0].IsInitiator {
16✔
995
                t.Fatalf("bob not detected as channel responder")
×
996
        }
×
997
        if !bobChannels[0].ChanType.IsSingleFunder() {
16✔
998
                t.Fatalf("channel type is incorrect, expected %v instead got %v",
×
999
                        channeldb.SingleFunderBit, bobChannels[0].ChanType)
×
1000
        }
×
1001

1002
        // Let Alice publish the funding transaction.
1003
        err = alice.PublishTransaction(fundingTx, "")
16✔
1004
        require.NoError(t, err, "unable to publish funding tx")
16✔
1005

16✔
1006
        // Mine a single block, the funding transaction should be included
16✔
1007
        // within this block.
16✔
1008
        err = waitForMempoolTx(miner, &fundingSha)
16✔
1009
        require.NoError(t, err, "tx not relayed to miner")
16✔
1010
        blockHashes, err := miner.Client.Generate(1)
16✔
1011
        require.NoError(t, err, "unable to generate block")
16✔
1012
        block, err := miner.Client.GetBlock(blockHashes[0])
16✔
1013
        require.NoError(t, err, "unable to find block")
16✔
1014
        if len(block.Transactions) != 2 {
16✔
1015
                t.Fatalf("funding transaction wasn't mined: %d",
×
1016
                        len(block.Transactions))
×
1017
        }
×
1018
        blockTx := block.Transactions[1]
16✔
1019
        if blockTx.TxHash() != fundingSha {
16✔
1020
                t.Fatalf("incorrect transaction was mined")
×
1021
        }
×
1022

1023
        // If a frozen channel was requested, then we expect that both channel
1024
        // types show as being a frozen channel type.
1025
        aliceChanFrozen := aliceChannels[0].ChanType.IsFrozen()
16✔
1026
        bobChanFrozen := bobChannels[0].ChanType.IsFrozen()
16✔
1027
        if thawHeight != 0 && (!aliceChanFrozen || !bobChanFrozen) {
16✔
1028
                t.Fatalf("expected both alice and bob to have frozen chans: "+
×
1029
                        "alice_frozen=%v, bob_frozen=%v", aliceChanFrozen,
×
1030
                        bobChanFrozen)
×
1031
        }
×
1032
        if thawHeight != bobChannels[0].ThawHeight {
16✔
1033
                t.Fatalf("wrong thaw height: expected %v got %v", thawHeight,
×
1034
                        bobChannels[0].ThawHeight)
×
1035
        }
×
1036
        if thawHeight != aliceChannels[0].ThawHeight {
16✔
1037
                t.Fatalf("wrong thaw height: expected %v got %v", thawHeight,
×
1038
                        aliceChannels[0].ThawHeight)
×
1039
        }
×
1040

1041
        assertReservationDeleted(aliceChanReservation, t)
16✔
1042
        assertReservationDeleted(bobChanReservation, t)
16✔
1043
}
1044

1045
func testListTransactionDetails(miner *rpctest.Harness,
1046
        alice, _ *lnwallet.LightningWallet, t *testing.T) {
4✔
1047

4✔
1048
        // Create 5 new outputs spendable by the wallet.
4✔
1049
        const numTxns = 5
4✔
1050
        const outputAmt = btcutil.SatoshiPerBitcoin
4✔
1051
        isOurAddress := make(map[string]bool)
4✔
1052
        txids := make(map[chainhash.Hash]struct{})
4✔
1053
        for i := 0; i < numTxns; i++ {
24✔
1054
                addr, err := alice.NewAddress(
20✔
1055
                        lnwallet.WitnessPubKey, false,
20✔
1056
                        lnwallet.DefaultAccountName,
20✔
1057
                )
20✔
1058
                if err != nil {
20✔
1059
                        t.Fatalf("unable to create new address: %v", err)
×
1060
                }
×
1061
                isOurAddress[addr.EncodeAddress()] = true
20✔
1062
                script, err := txscript.PayToAddrScript(addr)
20✔
1063
                if err != nil {
20✔
1064
                        t.Fatalf("unable to create output script: %v", err)
×
1065
                }
×
1066

1067
                output := &wire.TxOut{
20✔
1068
                        Value:    outputAmt,
20✔
1069
                        PkScript: script,
20✔
1070
                }
20✔
1071
                txid, err := miner.SendOutputs([]*wire.TxOut{output}, 2500)
20✔
1072
                if err != nil {
20✔
1073
                        t.Fatalf("unable to send coinbase: %v", err)
×
1074
                }
×
1075
                txids[*txid] = struct{}{}
20✔
1076
        }
1077

1078
        // Get the miner's current best block height before we mine blocks.
1079
        _, startHeight, err := miner.Client.GetBestBlock()
4✔
1080
        require.NoError(t, err, "cannot get best block")
4✔
1081

4✔
1082
        // Generate 10 blocks to mine all the transactions created above.
4✔
1083
        const numBlocksMined = 10
4✔
1084
        blocks, err := miner.Client.Generate(numBlocksMined)
4✔
1085
        require.NoError(t, err, "unable to mine blocks")
4✔
1086

4✔
1087
        // Our new best block height should be our start height + the number of
4✔
1088
        // blocks we just mined.
4✔
1089
        chainTip := startHeight + numBlocksMined
4✔
1090

4✔
1091
        // Next, fetch all the current transaction details. We should find all
4✔
1092
        // of our transactions between our start height before we generated
4✔
1093
        // blocks, and our end height, which is the chain tip. This query does
4✔
1094
        // not include unconfirmed transactions, since all of our transactions
4✔
1095
        // should be confirmed.
4✔
1096
        err = waitForWalletSync(miner, alice)
4✔
1097
        require.NoError(t, err, "Couldn't sync Alice's wallet")
4✔
1098
        txDetails, err := alice.ListTransactionDetails(
4✔
1099
                startHeight, chainTip, "",
4✔
1100
        )
4✔
1101
        require.NoError(t, err, "unable to fetch tx details")
4✔
1102

4✔
1103
        // This is a mapping from:
4✔
1104
        // blockHash -> transactionHash -> transactionOutputs
4✔
1105
        blockTxOuts := make(map[chainhash.Hash]map[chainhash.Hash][]*wire.TxOut)
4✔
1106

4✔
1107
        // Each of the transactions created above should be found with the
4✔
1108
        // proper details populated.
4✔
1109
        for _, txDetail := range txDetails {
40✔
1110
                if _, ok := txids[txDetail.Hash]; !ok {
52✔
1111
                        continue
16✔
1112
                }
1113

1114
                if txDetail.NumConfirmations != numBlocksMined {
20✔
1115
                        t.Fatalf("num confs incorrect, got %v expected %v",
×
1116
                                txDetail.NumConfirmations, numBlocksMined)
×
1117
                }
×
1118
                if txDetail.Value != outputAmt {
20✔
1119
                        t.Fatalf("tx value incorrect, got %v expected %v",
×
1120
                                txDetail.Value, outputAmt)
×
1121
                }
×
1122

1123
                if !bytes.Equal(txDetail.BlockHash[:], blocks[0][:]) {
20✔
1124
                        t.Fatalf("block hash mismatch, got %v expected %v",
×
1125
                                txDetail.BlockHash, blocks[0])
×
1126
                }
×
1127

1128
                // This fetches the transactions in a block so that we can compare the
1129
                // txouts stored in the mined transaction against the ones in the transaction
1130
                // details
1131
                if _, ok := blockTxOuts[*txDetail.BlockHash]; !ok {
24✔
1132
                        fetchedBlock, err := alice.Cfg.ChainIO.GetBlock(txDetail.BlockHash)
4✔
1133
                        if err != nil {
4✔
1134
                                t.Fatalf("err fetching block: %s", err)
×
1135
                        }
×
1136

1137
                        transactions := make(
4✔
1138
                                map[chainhash.Hash][]*wire.TxOut,
4✔
1139
                                len(fetchedBlock.Transactions),
4✔
1140
                        )
4✔
1141
                        for _, tx := range fetchedBlock.Transactions {
32✔
1142
                                transactions[tx.TxHash()] = tx.Copy().TxOut
28✔
1143
                        }
28✔
1144

1145
                        blockTxOuts[fetchedBlock.BlockHash()] = transactions
4✔
1146
                }
1147

1148
                if txOuts, ok := blockTxOuts[*txDetail.BlockHash][txDetail.Hash]; !ok {
20✔
1149
                        t.Fatalf("tx (%v) not found in block (%v)",
×
1150
                                txDetail.Hash, txDetail.BlockHash)
×
1151
                } else {
20✔
1152
                        var destinationOutputs []lnwallet.OutputDetail
20✔
1153

20✔
1154
                        for i, txOut := range txOuts {
60✔
1155
                                sc, addrs, _, err :=
40✔
1156
                                        txscript.ExtractPkScriptAddrs(txOut.PkScript, &alice.Cfg.NetParams)
40✔
1157
                                if err != nil {
40✔
1158
                                        t.Fatalf("err extract script addresses: %s", err)
×
1159
                                }
×
1160
                                destinationOutputs = append(destinationOutputs, lnwallet.OutputDetail{
40✔
1161
                                        OutputType:   sc,
40✔
1162
                                        Addresses:    addrs,
40✔
1163
                                        PkScript:     txOut.PkScript,
40✔
1164
                                        OutputIndex:  i,
40✔
1165
                                        Value:        btcutil.Amount(txOut.Value),
40✔
1166
                                        IsOurAddress: isOurAddress[addrs[0].EncodeAddress()],
40✔
1167
                                })
40✔
1168
                        }
1169

1170
                        if !reflect.DeepEqual(txDetail.OutputDetails, destinationOutputs) {
20✔
1171
                                t.Fatalf("destination outputs mismatch, got %v expected %v",
×
1172
                                        txDetail.OutputDetails, destinationOutputs)
×
1173
                        }
×
1174
                }
1175

1176
                delete(txids, txDetail.Hash)
20✔
1177
        }
1178
        if len(txids) != 0 {
4✔
1179
                t.Fatalf("all transactions not found in details: left=%v, "+
×
1180
                        "returned_set=%v", spew.Sdump(txids),
×
1181
                        spew.Sdump(txDetails))
×
1182
        }
×
1183

1184
        // Next create a transaction paying to an output which isn't under the
1185
        // wallet's control.
1186
        minerAddr, err := miner.NewAddress()
4✔
1187
        require.NoError(t, err, "unable to generate address")
4✔
1188
        outputScript, err := txscript.PayToAddrScript(minerAddr)
4✔
1189
        require.NoError(t, err, "unable to make output script")
4✔
1190
        burnOutput := wire.NewTxOut(outputAmt, outputScript)
4✔
1191
        burnTX, err := alice.SendOutputs(
4✔
1192
                []*wire.TxOut{burnOutput}, 2500, 1, labels.External,
4✔
1193
                alice.Cfg.CoinSelectionStrategy,
4✔
1194
        )
4✔
1195
        require.NoError(t, err, "unable to create burn tx")
4✔
1196
        burnTXID := burnTX.TxHash()
4✔
1197
        err = waitForMempoolTx(miner, &burnTXID)
4✔
1198
        require.NoError(t, err, "tx not relayed to miner")
4✔
1199

4✔
1200
        // Before we mine the next block, we'll ensure that the above
4✔
1201
        // transaction shows up in the set of unconfirmed transactions returned
4✔
1202
        // by ListTransactionDetails.
4✔
1203
        err = waitForWalletSync(miner, alice)
4✔
1204
        require.NoError(t, err, "Couldn't sync Alice's wallet")
4✔
1205

4✔
1206
        // Query our wallet for transactions from the chain tip, including
4✔
1207
        // unconfirmed transactions. The transaction above should be included
4✔
1208
        // with a confirmation height of 0, indicating that it has not been
4✔
1209
        // mined yet.
4✔
1210
        txDetails, err = alice.ListTransactionDetails(
4✔
1211
                chainTip, btcwallet.UnconfirmedHeight, "",
4✔
1212
        )
4✔
1213
        require.NoError(t, err, "unable to fetch tx details")
4✔
1214
        var mempoolTxFound bool
4✔
1215
        for _, txDetail := range txDetails {
8✔
1216
                if !bytes.Equal(txDetail.Hash[:], burnTXID[:]) {
4✔
1217
                        continue
×
1218
                }
1219

1220
                // Now that we've found the transaction, ensure that it has a
1221
                // negative number of confirmations to indicate that it's
1222
                // unconfirmed.
1223
                mempoolTxFound = true
4✔
1224
                if txDetail.NumConfirmations != 0 {
4✔
1225
                        t.Fatalf("num confs incorrect, got %v expected %v",
×
1226
                                txDetail.NumConfirmations, 0)
×
1227
                }
×
1228

1229
                // We test that each txDetail has destination addresses. This ensures
1230
                // that even when we have 0 confirmation transactions, the destination
1231
                // addresses are returned.
1232
                var match bool
4✔
1233
                for _, o := range txDetail.OutputDetails {
12✔
1234
                        for _, addr := range o.Addresses {
16✔
1235
                                if addr.String() == minerAddr.String() {
12✔
1236
                                        match = true
4✔
1237
                                        break
4✔
1238
                                }
1239
                        }
1240
                }
1241
                if !match {
4✔
1242
                        t.Fatalf("minerAddr: %v should have been a dest addr", minerAddr)
×
1243
                }
×
1244
        }
1245
        if !mempoolTxFound {
4✔
1246
                t.Fatalf("unable to find mempool tx in tx details!")
×
1247
        }
×
1248

1249
        // Generate one block for our transaction to confirm in.
1250
        var numBlocks int32 = 1
4✔
1251
        burnBlock, err := miner.Client.Generate(uint32(numBlocks))
4✔
1252
        require.NoError(t, err, "unable to mine block")
4✔
1253

4✔
1254
        // Progress our chain tip by the number of blocks we have just mined.
4✔
1255
        chainTip += numBlocks
4✔
1256

4✔
1257
        // Fetch the transaction details again, the new transaction should be
4✔
1258
        // shown as debiting from the wallet's balance. Start and end height
4✔
1259
        // are inclusive, so we use chainTip for both parameters to get only
4✔
1260
        // transactions from the last block.
4✔
1261
        err = waitForWalletSync(miner, alice)
4✔
1262
        require.NoError(t, err, "Couldn't sync Alice's wallet")
4✔
1263
        txDetails, err = alice.ListTransactionDetails(chainTip, chainTip, "")
4✔
1264
        require.NoError(t, err, "unable to fetch tx details")
4✔
1265
        var burnTxFound bool
4✔
1266
        for _, txDetail := range txDetails {
8✔
1267
                if !bytes.Equal(txDetail.Hash[:], burnTXID[:]) {
4✔
1268
                        continue
×
1269
                }
1270

1271
                burnTxFound = true
4✔
1272
                if txDetail.NumConfirmations != 1 {
4✔
1273
                        t.Fatalf("num confs incorrect, got %v expected %v",
×
1274
                                txDetail.NumConfirmations, 1)
×
1275
                }
×
1276

1277
                // We assert that the value is greater than the amount we
1278
                // attempted to send, as the wallet should have paid some amount
1279
                // of network fees.
1280
                if txDetail.Value >= -outputAmt {
4✔
1281
                        fmt.Println(spew.Sdump(txDetail))
×
1282
                        t.Fatalf("tx value incorrect, got %v expected %v",
×
1283
                                int64(txDetail.Value), -int64(outputAmt))
×
1284
                }
×
1285
                if !bytes.Equal(txDetail.BlockHash[:], burnBlock[0][:]) {
4✔
1286
                        t.Fatalf("block hash mismatch, got %v expected %v",
×
1287
                                txDetail.BlockHash, burnBlock[0])
×
1288
                }
×
1289
        }
1290
        if !burnTxFound {
4✔
1291
                t.Fatal("tx burning btc not found")
×
1292
        }
×
1293

1294
        // Generate a block which has no wallet transactions in it.
1295
        chainTip += numBlocks
4✔
1296
        _, err = miner.Client.Generate(uint32(numBlocks))
4✔
1297
        require.NoError(t, err, "unable to mine block")
4✔
1298

4✔
1299
        err = waitForWalletSync(miner, alice)
4✔
1300
        require.NoError(t, err, "Couldn't sync Alice's wallet")
4✔
1301

4✔
1302
        // Query for transactions only in the latest block. We do not expect
4✔
1303
        // any transactions to be returned.
4✔
1304
        txDetails, err = alice.ListTransactionDetails(chainTip, chainTip, "")
4✔
1305
        require.NoError(t, err, "unexpected error")
4✔
1306
        if len(txDetails) != 0 {
4✔
1307
                t.Fatalf("expected 0 transactions, got: %v", len(txDetails))
×
1308
        }
×
1309
}
1310

1311
func testTransactionSubscriptions(miner *rpctest.Harness,
1312
        alice, _ *lnwallet.LightningWallet, t *testing.T) {
4✔
1313

4✔
1314
        // First, check to see if this wallet meets the TransactionNotifier
4✔
1315
        // interface, if not then we'll skip this test for this particular
4✔
1316
        // implementation of the WalletController.
4✔
1317
        txClient, err := alice.SubscribeTransactions()
4✔
1318
        if err != nil {
4✔
1319
                t.Skipf("unable to generate tx subscription: %v", err)
×
1320
        }
×
1321
        defer txClient.Cancel()
4✔
1322

4✔
1323
        const (
4✔
1324
                outputAmt = btcutil.SatoshiPerBitcoin
4✔
1325
                numTxns   = 3
4✔
1326
        )
4✔
1327
        errCh1 := make(chan error, 1)
4✔
1328
        switch alice.BackEnd() {
4✔
1329
        case "neutrino":
1✔
1330
                // Neutrino doesn't listen for unconfirmed transactions.
1331
        default:
3✔
1332
                go func() {
6✔
1333
                        for i := 0; i < numTxns; i++ {
12✔
1334
                                txDetail := <-txClient.UnconfirmedTransactions()
9✔
1335
                                if txDetail.NumConfirmations != 0 {
9✔
1336
                                        errCh1 <- fmt.Errorf("incorrect number of confs, "+
×
1337
                                                "expected %v got %v", 0,
×
1338
                                                txDetail.NumConfirmations)
×
1339
                                        return
×
1340
                                }
×
1341
                                if txDetail.Value != outputAmt {
9✔
1342
                                        errCh1 <- fmt.Errorf("incorrect output amt, "+
×
1343
                                                "expected %v got %v", outputAmt,
×
1344
                                                txDetail.Value)
×
1345
                                        return
×
1346
                                }
×
1347
                                if txDetail.BlockHash != nil {
9✔
1348
                                        errCh1 <- fmt.Errorf("block hash should be nil, "+
×
1349
                                                "is instead %v",
×
1350
                                                txDetail.BlockHash)
×
1351
                                        return
×
1352
                                }
×
1353
                        }
1354
                        errCh1 <- nil
3✔
1355
                }()
1356
        }
1357

1358
        // Next, fetch a fresh address from the wallet, create 3 new outputs
1359
        // with the pkScript.
1360
        for i := 0; i < numTxns; i++ {
16✔
1361
                addr, err := alice.NewAddress(
12✔
1362
                        lnwallet.WitnessPubKey, false,
12✔
1363
                        lnwallet.DefaultAccountName,
12✔
1364
                )
12✔
1365
                if err != nil {
12✔
1366
                        t.Fatalf("unable to create new address: %v", err)
×
1367
                }
×
1368
                script, err := txscript.PayToAddrScript(addr)
12✔
1369
                if err != nil {
12✔
1370
                        t.Fatalf("unable to create output script: %v", err)
×
1371
                }
×
1372

1373
                output := &wire.TxOut{
12✔
1374
                        Value:    outputAmt,
12✔
1375
                        PkScript: script,
12✔
1376
                }
12✔
1377
                txid, err := miner.SendOutputs([]*wire.TxOut{output}, 2500)
12✔
1378
                if err != nil {
12✔
1379
                        t.Fatalf("unable to send coinbase: %v", err)
×
1380
                }
×
1381
                err = waitForMempoolTx(miner, txid)
12✔
1382
                if err != nil {
12✔
1383
                        t.Fatalf("tx not relayed to miner: %v", err)
×
1384
                }
×
1385
        }
1386

1387
        switch alice.BackEnd() {
4✔
1388
        case "neutrino":
1✔
1389
                // Neutrino doesn't listen for on unconfirmed transactions.
1390
        default:
3✔
1391
                // We should receive a notification for all three transactions
3✔
1392
                // generated above.
3✔
1393
                select {
3✔
1394
                case <-time.After(time.Second * 10):
×
1395
                        t.Fatalf("transactions not received after 10 seconds")
×
1396
                case err := <-errCh1:
3✔
1397
                        if err != nil {
3✔
1398
                                t.Fatal(err)
×
1399
                        }
×
1400
                }
1401
        }
1402

1403
        errCh2 := make(chan error, 1)
4✔
1404
        go func() {
8✔
1405
                for i := 0; i < numTxns; i++ {
16✔
1406
                        txDetail := <-txClient.ConfirmedTransactions()
12✔
1407
                        if txDetail.NumConfirmations != 1 {
12✔
1408
                                errCh2 <- fmt.Errorf("incorrect number of confs for %s, expected %v got %v",
×
1409
                                        txDetail.Hash, 1, txDetail.NumConfirmations)
×
1410
                                return
×
1411
                        }
×
1412
                        if txDetail.Value != outputAmt {
12✔
1413
                                errCh2 <- fmt.Errorf("incorrect output amt, expected %v got %v in txid %s",
×
1414
                                        outputAmt, txDetail.Value, txDetail.Hash)
×
1415
                                return
×
1416
                        }
×
1417
                }
1418
                errCh2 <- nil
4✔
1419
        }()
1420

1421
        // Next mine a single block, all the transactions generated above
1422
        // should be included.
1423
        if _, err := miner.Client.Generate(1); err != nil {
4✔
1424
                t.Fatalf("unable to generate block: %v", err)
×
1425
        }
×
1426

1427
        // We should receive a notification for all three transactions
1428
        // since they should be mined in the next block.
1429
        select {
4✔
1430
        case <-time.After(time.Second * 5):
×
1431
                t.Fatalf("transactions not received after 5 seconds")
×
1432
        case err := <-errCh2:
4✔
1433
                if err != nil {
4✔
1434
                        t.Fatal(err)
×
1435
                }
×
1436
        }
1437

1438
        // We'll also ensure that the client is able to send our new
1439
        // notifications when we _create_ transactions ourselves that spend our
1440
        // own outputs.
1441
        addr, err := alice.NewAddress(
4✔
1442
                lnwallet.WitnessPubKey, false,
4✔
1443
                lnwallet.DefaultAccountName,
4✔
1444
        )
4✔
1445
        require.NoError(t, err)
4✔
1446

4✔
1447
        outputScript, err := txscript.PayToAddrScript(addr)
4✔
1448
        require.NoError(t, err)
4✔
1449

4✔
1450
        burnOutput := wire.NewTxOut(outputAmt, outputScript)
4✔
1451
        tx, err := alice.SendOutputs(
4✔
1452
                []*wire.TxOut{burnOutput}, 2500, 1, labels.External,
4✔
1453
                alice.Cfg.CoinSelectionStrategy,
4✔
1454
        )
4✔
1455
        require.NoError(t, err, "unable to create tx")
4✔
1456
        txid := tx.TxHash()
4✔
1457
        err = waitForMempoolTx(miner, &txid)
4✔
1458
        require.NoError(t, err, "tx not relayed to miner")
4✔
1459

4✔
1460
        // Before we mine the next block, we'll ensure that the above
4✔
1461
        // transaction shows up in the set of unconfirmed transactions returned
4✔
1462
        // by ListTransactionDetails.
4✔
1463
        err = waitForWalletSync(miner, alice)
4✔
1464
        require.NoError(t, err, "Couldn't sync Alice's wallet")
4✔
1465

4✔
1466
        // As we just sent the transaction and it was landed in the mempool, we
4✔
1467
        // should get a notification for a new unconfirmed transactions
4✔
1468
        select {
4✔
1469
        case <-time.After(time.Second * 10):
×
1470
                t.Fatalf("transactions not received after 10 seconds")
×
1471
        case unConfTx := <-txClient.UnconfirmedTransactions():
4✔
1472
                if unConfTx.Hash != txid {
4✔
1473
                        t.Fatalf("wrong txn notified: expected %v got %v",
×
1474
                                txid, unConfTx.Hash)
×
1475
                }
×
1476
        }
1477
}
1478

1479
// scriptFromKey creates a P2WKH script from the given pubkey.
1480
func scriptFromKey(pubkey *btcec.PublicKey) ([]byte, error) {
102✔
1481
        pubkeyHash := btcutil.Hash160(pubkey.SerializeCompressed())
102✔
1482
        keyAddr, err := btcutil.NewAddressWitnessPubKeyHash(
102✔
1483
                pubkeyHash, &chaincfg.RegressionNetParams,
102✔
1484
        )
102✔
1485
        if err != nil {
102✔
1486
                return nil, fmt.Errorf("unable to create addr: %w", err)
×
1487
        }
×
1488
        keyScript, err := txscript.PayToAddrScript(keyAddr)
102✔
1489
        if err != nil {
102✔
1490
                return nil, fmt.Errorf("unable to generate script: %w", err)
×
1491
        }
×
1492

1493
        return keyScript, nil
102✔
1494
}
1495

1496
// mineAndAssert mines a block and ensures the passed TX is part of that block.
1497
func mineAndAssert(r *rpctest.Harness, tx *wire.MsgTx) error {
36✔
1498
        txid := tx.TxHash()
36✔
1499
        err := waitForMempoolTx(r, &txid)
36✔
1500
        if err != nil {
36✔
1501
                return fmt.Errorf("tx not relayed to miner: %w", err)
×
1502
        }
×
1503

1504
        blockHashes, err := r.Client.Generate(1)
36✔
1505
        if err != nil {
36✔
1506
                return fmt.Errorf("unable to generate block: %w", err)
×
1507
        }
×
1508

1509
        block, err := r.Client.GetBlock(blockHashes[0])
36✔
1510
        if err != nil {
36✔
1511
                return fmt.Errorf("unable to find block: %w", err)
×
1512
        }
×
1513

1514
        if len(block.Transactions) != 2 {
36✔
1515
                return fmt.Errorf("expected 2 txs in block, got %d",
×
1516
                        len(block.Transactions))
×
1517
        }
×
1518

1519
        blockTx := block.Transactions[1]
36✔
1520
        if blockTx.TxHash() != tx.TxHash() {
36✔
1521
                return fmt.Errorf("incorrect transaction was mined")
×
1522
        }
×
1523

1524
        // Sleep for a second before returning, to make sure the block has
1525
        // propagated.
1526
        time.Sleep(1 * time.Second)
36✔
1527
        return nil
36✔
1528
}
1529

1530
// txFromOutput takes a tx paying to fromPubKey, and creates a new tx that
1531
// spends the output from this tx, to an address derived from payToPubKey.
1532
func txFromOutput(tx *wire.MsgTx, signer input.Signer, fromPubKey,
1533
        payToPubKey *btcec.PublicKey, txFee btcutil.Amount,
1534
        rbf bool) (*wire.MsgTx, error) {
43✔
1535

43✔
1536
        // Generate the script we want to spend from.
43✔
1537
        keyScript, err := scriptFromKey(fromPubKey)
43✔
1538
        if err != nil {
43✔
1539
                return nil, fmt.Errorf("unable to generate script: %w", err)
×
1540
        }
×
1541

1542
        // We assume the output was paid to the keyScript made earlier.
1543
        var outputIndex uint32
43✔
1544
        if len(tx.TxOut) == 1 || bytes.Equal(tx.TxOut[0].PkScript, keyScript) {
77✔
1545
                outputIndex = 0
34✔
1546
        } else {
43✔
1547
                outputIndex = 1
9✔
1548
        }
9✔
1549
        outputValue := tx.TxOut[outputIndex].Value
43✔
1550

43✔
1551
        // With the index located, we can create a transaction spending the
43✔
1552
        // referenced output.
43✔
1553
        tx1 := wire.NewMsgTx(2)
43✔
1554

43✔
1555
        // If we want to create a tx that signals replacement, set its
43✔
1556
        // sequence number to the max one that signals replacement.
43✔
1557
        // Otherwise we just use the standard max sequence.
43✔
1558
        sequence := wire.MaxTxInSequenceNum
43✔
1559
        if rbf {
55✔
1560
                sequence = mempool.MaxRBFSequence
12✔
1561
        }
12✔
1562

1563
        tx1.AddTxIn(&wire.TxIn{
43✔
1564
                PreviousOutPoint: wire.OutPoint{
43✔
1565
                        Hash:  tx.TxHash(),
43✔
1566
                        Index: outputIndex,
43✔
1567
                },
43✔
1568
                Sequence: sequence,
43✔
1569
        })
43✔
1570

43✔
1571
        // Create a script to pay to.
43✔
1572
        payToScript, err := scriptFromKey(payToPubKey)
43✔
1573
        if err != nil {
43✔
1574
                return nil, fmt.Errorf("unable to generate script: %w", err)
×
1575
        }
×
1576
        tx1.AddTxOut(&wire.TxOut{
43✔
1577
                Value:    outputValue - int64(txFee),
43✔
1578
                PkScript: payToScript,
43✔
1579
        })
43✔
1580

43✔
1581
        // Now we can populate the sign descriptor which we'll use to generate
43✔
1582
        // the signature.
43✔
1583
        signDesc := &input.SignDescriptor{
43✔
1584
                KeyDesc: keychain.KeyDescriptor{
43✔
1585
                        PubKey: fromPubKey,
43✔
1586
                },
43✔
1587
                WitnessScript: keyScript,
43✔
1588
                Output:        tx.TxOut[outputIndex],
43✔
1589
                HashType:      txscript.SigHashAll,
43✔
1590
                SigHashes:     input.NewTxSigHashesV0Only(tx1),
43✔
1591
                InputIndex:    0, // Has only one input.
43✔
1592
        }
43✔
1593

43✔
1594
        // With the descriptor created, we use it to generate a signature, then
43✔
1595
        // manually create a valid witness stack we'll use for signing.
43✔
1596
        spendSig, err := signer.SignOutputRaw(tx1, signDesc)
43✔
1597
        if err != nil {
43✔
1598
                return nil, fmt.Errorf("unable to generate signature: %w", err)
×
1599
        }
×
1600
        witness := make([][]byte, 2)
43✔
1601
        witness[0] = append(spendSig.Serialize(), byte(txscript.SigHashAll))
43✔
1602
        witness[1] = fromPubKey.SerializeCompressed()
43✔
1603
        tx1.TxIn[0].Witness = witness
43✔
1604

43✔
1605
        // Finally, attempt to validate the completed transaction. This should
43✔
1606
        // succeed if the wallet was able to properly generate the proper
43✔
1607
        // private key.
43✔
1608
        vm, err := txscript.NewEngine(
43✔
1609
                keyScript, tx1, 0, txscript.StandardVerifyFlags, nil,
43✔
1610
                nil, outputValue, txscript.NewCannedPrevOutputFetcher(
43✔
1611
                        keyScript, outputValue,
43✔
1612
                ),
43✔
1613
        )
43✔
1614
        if err != nil {
43✔
1615
                return nil, fmt.Errorf("unable to create engine: %w", err)
×
1616
        }
×
1617
        if err := vm.Execute(); err != nil {
43✔
1618
                return nil, fmt.Errorf("spend is invalid: %w", err)
×
1619
        }
×
1620

1621
        return tx1, nil
43✔
1622
}
1623

1624
// newTx sends coins from Alice's wallet, mines this transaction, and creates a
1625
// new, unconfirmed tx that spends this output to pubKey.
1626
func newTx(t *testing.T, r *rpctest.Harness, pubKey *btcec.PublicKey,
1627
        alice *lnwallet.LightningWallet, rbf bool) *wire.MsgTx {
16✔
1628

16✔
1629
        t.Helper()
16✔
1630

16✔
1631
        keyScript, err := scriptFromKey(pubKey)
16✔
1632
        require.NoError(t, err, "unable to generate script")
16✔
1633

16✔
1634
        // Instruct the wallet to fund the output with a newly created
16✔
1635
        // transaction.
16✔
1636
        newOutput := &wire.TxOut{
16✔
1637
                Value:    btcutil.SatoshiPerBitcoin,
16✔
1638
                PkScript: keyScript,
16✔
1639
        }
16✔
1640
        tx, err := alice.SendOutputs(
16✔
1641
                []*wire.TxOut{newOutput}, 2500, 1, labels.External,
16✔
1642
                alice.Cfg.CoinSelectionStrategy,
16✔
1643
        )
16✔
1644
        require.NoError(t, err, "unable to create output")
16✔
1645

16✔
1646
        // Query for the transaction generated above so we can located the
16✔
1647
        // index of our output.
16✔
1648
        if err := mineAndAssert(r, tx); err != nil {
16✔
1649
                t.Fatalf("unable to mine tx: %v", err)
×
1650
        }
×
1651

1652
        // Create a new unconfirmed tx that spends this output.
1653
        txFee := btcutil.Amount(0.001 * btcutil.SatoshiPerBitcoin)
16✔
1654
        tx1, err := txFromOutput(
16✔
1655
                tx, alice.Cfg.Signer, pubKey, pubKey, txFee, rbf,
16✔
1656
        )
16✔
1657
        if err != nil {
16✔
1658
                t.Fatal(err)
×
1659
        }
×
1660

1661
        return tx1
16✔
1662
}
1663

1664
// testGetTransactionDetails checks that GetTransactionDetails returns the
1665
// correct amount after a transaction has been sent from alice to bob.
1666
func testGetTransactionDetails(r *rpctest.Harness,
1667
        alice, bob *lnwallet.LightningWallet, t *testing.T) {
4✔
1668

4✔
1669
        const txFee = int64(14500)
4✔
1670

4✔
1671
        bobPkScript := newPkScript(t, bob, lnwallet.WitnessPubKey)
4✔
1672
        txFeeRate := chainfee.SatPerKWeight(2500)
4✔
1673
        amountSats := btcutil.Amount(btcutil.SatoshiPerBitcoin - txFee)
4✔
1674
        output := &wire.TxOut{
4✔
1675
                Value:    int64(amountSats),
4✔
1676
                PkScript: bobPkScript,
4✔
1677
        }
4✔
1678
        tx := sendCoins(t, r, alice, bob, output, txFeeRate, true, 1)
4✔
1679
        txHash := tx.TxHash()
4✔
1680
        assertTxInWallet(t, alice, txHash, true)
4✔
1681
        assertTxInWallet(t, bob, txHash, true)
4✔
1682

4✔
1683
        txDetails, err := bob.GetTransactionDetails(&txHash)
4✔
1684
        require.NoError(t, err, "unable to receive transaction details")
4✔
1685
        require.Equal(t, txDetails.Value, amountSats, "tx value")
4✔
1686
}
4✔
1687

1688
// testPublishTransaction checks that PublishTransaction returns the expected
1689
// error types in case the transaction being published conflicts with the
1690
// current mempool or chain.
1691
func testPublishTransaction(r *rpctest.Harness,
1692
        alice, _ *lnwallet.LightningWallet, t *testing.T) {
4✔
1693

4✔
1694
        // Generate a pubkey, and pay-to-addr script.
4✔
1695
        keyDesc, err := alice.DeriveNextKey(keychain.KeyFamilyMultiSig)
4✔
1696
        require.NoError(t, err, "unable to obtain public key")
4✔
1697

4✔
1698
        t.Run("no_error_on_duplicate_tx", func(t *testing.T) {
8✔
1699
                // We will first check that publishing a transaction already in
4✔
1700
                // the mempool does NOT return an error. Create the tx.
4✔
1701
                tx1 := newTx(t, r, keyDesc.PubKey, alice, false)
4✔
1702

4✔
1703
                // Publish the transaction.
4✔
1704
                err = alice.PublishTransaction(tx1, labels.External)
4✔
1705
                require.NoError(t, err)
4✔
1706

4✔
1707
                txid1 := tx1.TxHash()
4✔
1708
                err = waitForMempoolTx(r, &txid1)
4✔
1709
                require.NoError(t, err, "tx not relayed to miner")
4✔
1710

4✔
1711
                // Publish the exact same transaction again. This should not
4✔
1712
                // return an error, even though the transaction is already in
4✔
1713
                // the mempool.
4✔
1714
                err = alice.PublishTransaction(tx1, labels.External)
4✔
1715
                require.NoError(t, err)
4✔
1716

4✔
1717
                // Mine the transaction.
4✔
1718
                _, err := r.Client.Generate(1)
4✔
1719
                require.NoError(t, err)
4✔
1720
        })
4✔
1721

1722
        t.Run("no_error_on_minged_tx", func(t *testing.T) {
8✔
1723
                // We'll now test that we don't get an error if we try to
4✔
1724
                // publish a transaction that is already mined.
4✔
1725
                //
4✔
1726
                // Create a new transaction. We must do this to properly test
4✔
1727
                // the reject messages from our peers. They might only send us
4✔
1728
                // a reject message for a given tx once, so we create a new to
4✔
1729
                // make sure it is not just immediately rejected.
4✔
1730
                tx2 := newTx(t, r, keyDesc.PubKey, alice, false)
4✔
1731

4✔
1732
                // Publish this tx.
4✔
1733
                err = alice.PublishTransaction(tx2, labels.External)
4✔
1734
                require.NoError(t, err)
4✔
1735

4✔
1736
                // Mine the transaction.
4✔
1737
                err := mineAndAssert(r, tx2)
4✔
1738
                require.NoError(t, err)
4✔
1739

4✔
1740
                // Publish the transaction again. It is already mined, and we
4✔
1741
                // don't expect this to return an error.
4✔
1742
                err = alice.PublishTransaction(tx2, labels.External)
4✔
1743
                require.NoError(t, err)
4✔
1744
        })
4✔
1745

1746
        // We'll do the next mempool check on both RBF and non-RBF
1747
        // enabled transactions.
1748
        var (
4✔
1749
                txFee = btcutil.Amount(
4✔
1750
                        0.005 * btcutil.SatoshiPerBitcoin,
4✔
1751
                )
4✔
1752
                tx3, tx3Spend *wire.MsgTx
4✔
1753
        )
4✔
1754
        t.Run("rbf_tests", func(t *testing.T) {
8✔
1755
                for _, rbf := range []bool{false, true} {
12✔
1756
                        // Now we'll try to double spend an output with a
8✔
1757
                        // different transaction. Create a new tx and publish
8✔
1758
                        // it. This is the output we'll try to double spend.
8✔
1759
                        tx3 = newTx(t, r, keyDesc.PubKey, alice, false)
8✔
1760
                        err := alice.PublishTransaction(tx3, labels.External)
8✔
1761
                        require.NoError(t, err)
8✔
1762

8✔
1763
                        // Mine the transaction.
8✔
1764
                        err = mineAndAssert(r, tx3)
8✔
1765
                        require.NoError(t, err)
8✔
1766

8✔
1767
                        // Now we create a transaction that spends the output
8✔
1768
                        // from the tx just mined.
8✔
1769
                        tx4, err := txFromOutput(
8✔
1770
                                tx3, alice.Cfg.Signer, keyDesc.PubKey,
8✔
1771
                                keyDesc.PubKey, txFee, rbf,
8✔
1772
                        )
8✔
1773
                        require.NoError(t, err)
8✔
1774

8✔
1775
                        // This should be accepted into the mempool.
8✔
1776
                        err = alice.PublishTransaction(tx4, labels.External)
8✔
1777
                        require.NoError(t, err)
8✔
1778

8✔
1779
                        // Keep track of the last successfully published tx to
8✔
1780
                        // spend tx3.
8✔
1781
                        tx3Spend = tx4
8✔
1782

8✔
1783
                        txid4 := tx4.TxHash()
8✔
1784
                        err = waitForMempoolTx(r, &txid4)
8✔
1785
                        require.NoError(t, err, "tx not relayed to miner")
8✔
1786

8✔
1787
                        // Create a new key we'll pay to, to ensure we create a
8✔
1788
                        // unique transaction.
8✔
1789
                        keyDesc2, err := alice.DeriveNextKey(
8✔
1790
                                keychain.KeyFamilyMultiSig,
8✔
1791
                        )
8✔
1792
                        require.NoError(t, err, "unable to obtain public key")
8✔
1793

8✔
1794
                        // Create a new transaction that spends the output from
8✔
1795
                        // tx3, and that pays to a different address.
8✔
1796
                        tx5, err := txFromOutput(
8✔
1797
                                tx3, alice.Cfg.Signer, keyDesc.PubKey,
8✔
1798
                                keyDesc2.PubKey, txFee, rbf,
8✔
1799
                        )
8✔
1800
                        require.NoError(t, err)
8✔
1801

8✔
1802
                        err = alice.PublishTransaction(tx5, labels.External)
8✔
1803

8✔
1804
                        // If RBF is not enabled, we expect this to be rejected
8✔
1805
                        // because it is a double spend.
8✔
1806
                        expectedErr := lnwallet.ErrDoubleSpend
8✔
1807

8✔
1808
                        // If RBF is enabled, we expect it to be rejected
8✔
1809
                        // because it doesn't pay enough fees.
8✔
1810
                        if rbf {
12✔
1811
                                expectedErr = chain.ErrInsufficientFee
4✔
1812
                        }
4✔
1813

1814
                        // Assert the expected error.
1815
                        require.ErrorIsf(t, err, expectedErr, "has rbf=%v", rbf)
8✔
1816

8✔
1817
                        // Create another transaction that spends the same
8✔
1818
                        // output, but has a higher fee. We expect also this tx
8✔
1819
                        // to be rejected for non-RBF enabled transactions,
8✔
1820
                        // while it should succeed otherwise.
8✔
1821
                        pubKey3, err := alice.DeriveNextKey(
8✔
1822
                                keychain.KeyFamilyMultiSig,
8✔
1823
                        )
8✔
1824
                        require.NoError(t, err, "unable to obtain public key")
8✔
1825

8✔
1826
                        tx6, err := txFromOutput(
8✔
1827
                                tx3, alice.Cfg.Signer, keyDesc.PubKey,
8✔
1828
                                pubKey3.PubKey, 2*txFee, rbf,
8✔
1829
                        )
8✔
1830
                        require.NoError(t, err)
8✔
1831

8✔
1832
                        // Expect rejection in non-RBF case.
8✔
1833
                        expErr := lnwallet.ErrDoubleSpend
8✔
1834
                        if rbf {
12✔
1835
                                // Expect success in rbf case.
4✔
1836
                                expErr = nil
4✔
1837
                                tx3Spend = tx6
4✔
1838
                        }
4✔
1839
                        err = alice.PublishTransaction(tx6, labels.External)
8✔
1840
                        require.ErrorIs(t, err, expErr)
8✔
1841

8✔
1842
                        // Mine the tx spending tx3.
8✔
1843
                        err = mineAndAssert(r, tx3Spend)
8✔
1844
                        require.NoError(t, err)
8✔
1845
                }
1846
        })
1847

1848
        t.Run("tx_double_spend", func(t *testing.T) {
8✔
1849
                // At last we try to spend an output already spent by a
4✔
1850
                // confirmed transaction.
4✔
1851
                //
4✔
1852
                // TODO(halseth): we currently skip this test for neutrino, as
4✔
1853
                // the backing btcd node will consider the tx being an orphan,
4✔
1854
                // and will accept it. Should look into if this is the behavior
4✔
1855
                // also for bitcoind, and update test accordingly.
4✔
1856
                if alice.BackEnd() != "neutrino" {
7✔
1857
                        // Create another tx spending tx3.
3✔
1858
                        pubKey4, err := alice.DeriveNextKey(
3✔
1859
                                keychain.KeyFamilyMultiSig,
3✔
1860
                        )
3✔
1861
                        require.NoError(t, err, "unable to obtain public key")
3✔
1862

3✔
1863
                        tx7, err := txFromOutput(
3✔
1864
                                tx3, alice.Cfg.Signer, keyDesc.PubKey,
3✔
1865
                                pubKey4.PubKey, txFee, false,
3✔
1866
                        )
3✔
1867
                        require.NoError(t, err)
3✔
1868

3✔
1869
                        // Expect rejection.
3✔
1870
                        err = alice.PublishTransaction(tx7, labels.External)
3✔
1871
                        require.ErrorIs(t, err, lnwallet.ErrDoubleSpend)
3✔
1872
                }
3✔
1873
        })
1874

1875
        t.Run("test_tx_size_limit", func(t *testing.T) {
8✔
1876
                // In this test, we'll try to create a massive transaction that
4✔
1877
                // can't be mined as dictacted by widely deployed transaction
4✔
1878
                // policy.
4✔
1879
                //
4✔
1880
                // To do this, we'll take out of the prior transactions, and
4✔
1881
                // add a bunch of outputs, putting it over the max weight
4✔
1882
                // limit.
4✔
1883
                testTx := tx3.Copy()
4✔
1884
                for i := 0; i < blockchain.MaxOutputsPerBlock; i++ {
444,448✔
1885
                        testTx.AddTxOut(&wire.TxOut{
444,444✔
1886
                                Value:    tx3.TxOut[0].Value,
444,444✔
1887
                                PkScript: tx3.TxOut[0].PkScript,
444,444✔
1888
                        })
444,444✔
1889
                }
444,444✔
1890

1891
                // Now broadcast the transaction, we should get an error that
1892
                // the weight is too large.
1893
                err := alice.PublishTransaction(testTx, labels.External)
4✔
1894
                require.ErrorIs(t, err, chain.ErrOversizeTx)
4✔
1895
        })
1896
}
1897

1898
func testSignOutputUsingTweaks(r *rpctest.Harness,
1899
        alice, _ *lnwallet.LightningWallet, t *testing.T) {
4✔
1900

4✔
1901
        // We'd like to test the ability of the wallet's Signer implementation
4✔
1902
        // to be able to sign with a private key derived from tweaking the
4✔
1903
        // specific public key. This scenario exercises the case when the
4✔
1904
        // wallet needs to sign for a sweep of a revoked output, or just claim
4✔
1905
        // any output that pays to a tweaked key.
4✔
1906

4✔
1907
        // First, generate a new public key under the control of the wallet,
4✔
1908
        // then generate a revocation key using it.
4✔
1909
        pubKey, err := alice.DeriveNextKey(
4✔
1910
                keychain.KeyFamilyMultiSig,
4✔
1911
        )
4✔
1912
        require.NoError(t, err, "unable to obtain public key")
4✔
1913

4✔
1914
        // As we'd like to test both single tweak, and double tweak spends,
4✔
1915
        // we'll generate a commitment pre-image, then derive a revocation key
4✔
1916
        // and single tweak from that.
4✔
1917
        commitPreimage := bytes.Repeat([]byte{2}, 32)
4✔
1918
        commitSecret, commitPoint := btcec.PrivKeyFromBytes(commitPreimage)
4✔
1919

4✔
1920
        revocationKey := input.DeriveRevocationPubkey(pubKey.PubKey, commitPoint)
4✔
1921
        commitTweak := input.SingleTweakBytes(commitPoint, pubKey.PubKey)
4✔
1922

4✔
1923
        tweakedPub := input.TweakPubKey(pubKey.PubKey, commitPoint)
4✔
1924

4✔
1925
        // As we'd like to test both single and double tweaks, we'll repeat
4✔
1926
        // the same set up twice. The first will use a regular single tweak,
4✔
1927
        // and the second will use a double tweak.
4✔
1928
        baseKey := pubKey
4✔
1929
        for i := 0; i < 2; i++ {
12✔
1930
                var tweakedKey *btcec.PublicKey
8✔
1931
                if i == 0 {
12✔
1932
                        tweakedKey = tweakedPub
4✔
1933
                } else {
8✔
1934
                        tweakedKey = revocationKey
4✔
1935
                }
4✔
1936

1937
                // Using the given key for the current iteration, we'll
1938
                // generate a regular p2wkh from that.
1939
                pubkeyHash := btcutil.Hash160(tweakedKey.SerializeCompressed())
8✔
1940
                keyAddr, err := btcutil.NewAddressWitnessPubKeyHash(pubkeyHash,
8✔
1941
                        &chaincfg.RegressionNetParams)
8✔
1942
                if err != nil {
8✔
1943
                        t.Fatalf("unable to create addr: %v", err)
×
1944
                }
×
1945
                keyScript, err := txscript.PayToAddrScript(keyAddr)
8✔
1946
                if err != nil {
8✔
1947
                        t.Fatalf("unable to generate script: %v", err)
×
1948
                }
×
1949

1950
                // With the script fully assembled, instruct the wallet to fund
1951
                // the output with a newly created transaction.
1952
                newOutput := &wire.TxOut{
8✔
1953
                        Value:    btcutil.SatoshiPerBitcoin,
8✔
1954
                        PkScript: keyScript,
8✔
1955
                }
8✔
1956
                tx, err := alice.SendOutputs(
8✔
1957
                        []*wire.TxOut{newOutput}, 2500, 1, labels.External,
8✔
1958
                        alice.Cfg.CoinSelectionStrategy,
8✔
1959
                )
8✔
1960
                if err != nil {
8✔
1961
                        t.Fatalf("unable to create output: %v", err)
×
1962
                }
×
1963
                txid := tx.TxHash()
8✔
1964
                // Query for the transaction generated above so we can located
8✔
1965
                // the index of our output.
8✔
1966
                err = waitForMempoolTx(r, &txid)
8✔
1967
                if err != nil {
8✔
1968
                        t.Fatalf("tx not relayed to miner: %v", err)
×
1969
                }
×
1970
                var outputIndex uint32
8✔
1971
                if bytes.Equal(tx.TxOut[0].PkScript, keyScript) {
13✔
1972
                        outputIndex = 0
5✔
1973
                } else {
8✔
1974
                        outputIndex = 1
3✔
1975
                }
3✔
1976

1977
                // With the index located, we can create a transaction spending
1978
                // the referenced output.
1979
                sweepTx := wire.NewMsgTx(2)
8✔
1980
                sweepTx.AddTxIn(&wire.TxIn{
8✔
1981
                        PreviousOutPoint: wire.OutPoint{
8✔
1982
                                Hash:  txid,
8✔
1983
                                Index: outputIndex,
8✔
1984
                        },
8✔
1985
                })
8✔
1986
                sweepTx.AddTxOut(&wire.TxOut{
8✔
1987
                        Value:    1000,
8✔
1988
                        PkScript: keyScript,
8✔
1989
                })
8✔
1990

8✔
1991
                // Now we can populate the sign descriptor which we'll use to
8✔
1992
                // generate the signature. Within the descriptor we set the
8✔
1993
                // private tweak value as the key in the script is derived
8✔
1994
                // based on this tweak value and the key we originally
8✔
1995
                // generated above.
8✔
1996
                signDesc := &input.SignDescriptor{
8✔
1997
                        KeyDesc: keychain.KeyDescriptor{
8✔
1998
                                PubKey: baseKey.PubKey,
8✔
1999
                        },
8✔
2000
                        WitnessScript: keyScript,
8✔
2001
                        Output:        newOutput,
8✔
2002
                        HashType:      txscript.SigHashAll,
8✔
2003
                        SigHashes:     input.NewTxSigHashesV0Only(sweepTx),
8✔
2004
                        InputIndex:    0,
8✔
2005
                }
8✔
2006

8✔
2007
                // If this is the first, loop, we'll use the generated single
8✔
2008
                // tweak, otherwise, we'll use the double tweak.
8✔
2009
                if i == 0 {
12✔
2010
                        signDesc.SingleTweak = commitTweak
4✔
2011
                } else {
8✔
2012
                        signDesc.DoubleTweak = commitSecret
4✔
2013
                }
4✔
2014

2015
                // With the descriptor created, we use it to generate a
2016
                // signature, then manually create a valid witness stack we'll
2017
                // use for signing.
2018
                spendSig, err := alice.Cfg.Signer.SignOutputRaw(sweepTx, signDesc)
8✔
2019
                if err != nil {
8✔
2020
                        t.Fatalf("unable to generate signature: %v", err)
×
2021
                }
×
2022
                witness := make([][]byte, 2)
8✔
2023
                witness[0] = append(spendSig.Serialize(), byte(txscript.SigHashAll))
8✔
2024
                witness[1] = tweakedKey.SerializeCompressed()
8✔
2025
                sweepTx.TxIn[0].Witness = witness
8✔
2026

8✔
2027
                // Finally, attempt to validate the completed transaction. This
8✔
2028
                // should succeed if the wallet was able to properly generate
8✔
2029
                // the proper private key.
8✔
2030
                vm, err := txscript.NewEngine(
8✔
2031
                        keyScript, sweepTx, 0, txscript.StandardVerifyFlags,
8✔
2032
                        nil, nil, int64(btcutil.SatoshiPerBitcoin),
8✔
2033
                        txscript.NewCannedPrevOutputFetcher(
8✔
2034
                                keyScript, int64(btcutil.SatoshiPerBitcoin),
8✔
2035
                        ),
8✔
2036
                )
8✔
2037
                if err != nil {
8✔
2038
                        t.Fatalf("unable to create engine: %v", err)
×
2039
                }
×
2040
                if err := vm.Execute(); err != nil {
8✔
2041
                        t.Fatalf("spend #%v is invalid: %v", i, err)
×
2042
                }
×
2043
        }
2044
}
2045

2046
func testReorgWalletBalance(r *rpctest.Harness, w *lnwallet.LightningWallet,
2047
        _ *lnwallet.LightningWallet, t *testing.T) {
4✔
2048

4✔
2049
        // We first mine a few blocks to ensure any transactions still in the
4✔
2050
        // mempool confirm, and then get the original balance, before a
4✔
2051
        // reorganization that doesn't invalidate any existing transactions or
4✔
2052
        // create any new non-coinbase transactions. We'll then check if it's
4✔
2053
        // the same after the empty reorg.
4✔
2054
        _, err := r.Client.Generate(5)
4✔
2055
        require.NoError(t, err, "unable to generate blocks on passed node")
4✔
2056

4✔
2057
        // Give wallet time to catch up.
4✔
2058
        err = waitForWalletSync(r, w)
4✔
2059
        require.NoError(t, err, "unable to sync wallet")
4✔
2060

4✔
2061
        // Send some money from the miner to the wallet
4✔
2062
        err = loadTestCredits(r, w, 20, 4)
4✔
2063
        require.NoError(t, err, "unable to send money to lnwallet")
4✔
2064

4✔
2065
        // Send some money from the wallet back to the miner.
4✔
2066
        // Grab a fresh address from the miner to house this output.
4✔
2067
        minerAddr, err := r.NewAddress()
4✔
2068
        require.NoError(t, err, "unable to generate address for miner")
4✔
2069
        script, err := txscript.PayToAddrScript(minerAddr)
4✔
2070
        require.NoError(t, err, "unable to create pay to addr script")
4✔
2071
        output := &wire.TxOut{
4✔
2072
                Value:    1e8,
4✔
2073
                PkScript: script,
4✔
2074
        }
4✔
2075
        tx, err := w.SendOutputs(
4✔
2076
                []*wire.TxOut{output}, 2500, 1, labels.External,
4✔
2077
                w.Cfg.CoinSelectionStrategy,
4✔
2078
        )
4✔
2079
        require.NoError(t, err, "unable to send outputs")
4✔
2080
        txid := tx.TxHash()
4✔
2081
        err = waitForMempoolTx(r, &txid)
4✔
2082
        require.NoError(t, err, "tx not relayed to miner")
4✔
2083
        _, err = r.Client.Generate(50)
4✔
2084
        require.NoError(t, err, "unable to generate blocks on passed node")
4✔
2085

4✔
2086
        // Give wallet time to catch up.
4✔
2087
        err = waitForWalletSync(r, w)
4✔
2088
        require.NoError(t, err, "unable to sync wallet")
4✔
2089

4✔
2090
        // Get the original balance.
4✔
2091
        origBalance, err := w.ConfirmedBalance(1, lnwallet.DefaultAccountName)
4✔
2092
        require.NoError(t, err, "unable to query for balance")
4✔
2093

4✔
2094
        // Now we cause a reorganization as follows.
4✔
2095
        // Step 1: create a new miner and start it.
4✔
2096
        r2 := unittest.NewMiner(
4✔
2097
                t, r.ActiveNet, []string{"--txindex"}, false, 0,
4✔
2098
        )
4✔
2099
        newBalance, err := w.ConfirmedBalance(1, lnwallet.DefaultAccountName)
4✔
2100
        require.NoError(t, err, "unable to query for balance")
4✔
2101
        if origBalance != newBalance {
4✔
2102
                t.Fatalf("wallet balance incorrect, should have %v, "+
×
2103
                        "instead have %v", origBalance, newBalance)
×
2104
        }
×
2105

2106
        // Step 2: connect the miner to the passed miner and wait for
2107
        // synchronization.
2108
        err = r2.Client.AddNode(r.P2PAddress(), rpcclient.ANAdd)
4✔
2109
        require.NoError(t, err, "unable to connect mining nodes together")
4✔
2110
        err = rpctest.JoinNodes([]*rpctest.Harness{r2, r}, rpctest.Blocks)
4✔
2111
        require.NoError(t, err, "unable to synchronize mining nodes")
4✔
2112

4✔
2113
        // Step 3: Do a set of reorgs by disconnecting the two miners, mining
4✔
2114
        // one block on the passed miner and two on the created miner,
4✔
2115
        // connecting them, and waiting for them to sync.
4✔
2116
        for i := 0; i < 5; i++ {
24✔
2117
                // Wait for disconnection
20✔
2118
                timeout := time.After(30 * time.Second)
20✔
2119
                stillConnected := true
20✔
2120
                var peers []btcjson.GetPeerInfoResult
20✔
2121
                for stillConnected {
41✔
2122
                        // Allow for timeout
21✔
2123
                        time.Sleep(100 * time.Millisecond)
21✔
2124
                        select {
21✔
2125
                        case <-timeout:
×
2126
                                t.Fatalf("timeout waiting for miner disconnect")
×
2127
                        default:
21✔
2128
                        }
2129
                        err = r2.Client.AddNode(r.P2PAddress(), rpcclient.ANRemove)
21✔
2130
                        if err != nil {
21✔
2131
                                t.Fatalf("unable to disconnect mining nodes: %v",
×
2132
                                        err)
×
2133
                        }
×
2134
                        peers, err = r2.Client.GetPeerInfo()
21✔
2135
                        if err != nil {
21✔
2136
                                t.Fatalf("unable to get peer info: %v", err)
×
2137
                        }
×
2138
                        stillConnected = false
21✔
2139
                        for _, peer := range peers {
22✔
2140
                                if peer.Addr == r.P2PAddress() {
2✔
2141
                                        stillConnected = true
1✔
2142
                                        break
1✔
2143
                                }
2144
                        }
2145
                }
2146
                _, err = r.Client.Generate(2)
20✔
2147
                if err != nil {
20✔
2148
                        t.Fatalf("unable to generate blocks on passed node: %v",
×
2149
                                err)
×
2150
                }
×
2151
                _, err = r2.Client.Generate(3)
20✔
2152
                if err != nil {
20✔
2153
                        t.Fatalf("unable to generate blocks on created node: %v",
×
2154
                                err)
×
2155
                }
×
2156

2157
                // Step 5: Reconnect the miners and wait for them to synchronize.
2158
                err = r2.Client.AddNode(r.P2PAddress(), rpcclient.ANAdd)
20✔
2159
                if err != nil {
20✔
2160
                        switch err := err.(type) {
×
2161
                        case *btcjson.RPCError:
×
2162
                                if err.Code != -8 {
×
2163
                                        t.Fatalf("unable to connect mining "+
×
2164
                                                "nodes together: %v", err)
×
2165
                                }
×
2166
                        default:
×
2167
                                t.Fatalf("unable to connect mining nodes "+
×
2168
                                        "together: %v", err)
×
2169
                        }
2170
                }
2171
                err = rpctest.JoinNodes([]*rpctest.Harness{r2, r},
20✔
2172
                        rpctest.Blocks)
20✔
2173
                if err != nil {
20✔
2174
                        t.Fatalf("unable to synchronize mining nodes: %v", err)
×
2175
                }
×
2176

2177
                // Give wallet time to catch up.
2178
                err = waitForWalletSync(r, w)
20✔
2179
                if err != nil {
20✔
2180
                        t.Fatalf("unable to sync wallet: %v", err)
×
2181
                }
×
2182
        }
2183

2184
        // Now we check that the wallet balance stays the same.
2185
        newBalance, err = w.ConfirmedBalance(1, lnwallet.DefaultAccountName)
4✔
2186
        require.NoError(t, err, "unable to query for balance")
4✔
2187
        if origBalance != newBalance {
4✔
2188
                t.Fatalf("wallet balance incorrect, should have %v, "+
×
2189
                        "instead have %v", origBalance, newBalance)
×
2190
        }
×
2191
}
2192

2193
// testChangeOutputSpendConfirmation ensures that when we attempt to spend a
2194
// change output created by the wallet, the wallet receives its confirmation
2195
// once included in the chain.
2196
func testChangeOutputSpendConfirmation(r *rpctest.Harness,
2197
        alice, bob *lnwallet.LightningWallet, t *testing.T) {
4✔
2198

4✔
2199
        // In order to test that we see the confirmation of a transaction that
4✔
2200
        // spends an output created by SendOutputs, we'll start by emptying
4✔
2201
        // Alice's wallet so that no other UTXOs can be picked. To do so, we'll
4✔
2202
        // generate an address for Bob, who will receive all the coins.
4✔
2203
        // Assuming a balance of 80 BTC and a transaction fee of 2500 sat/kw,
4✔
2204
        // we'll craft the following transaction so that Alice doesn't have any
4✔
2205
        // UTXOs left.
4✔
2206
        aliceBalance, err := alice.ConfirmedBalance(0, lnwallet.DefaultAccountName)
4✔
2207
        require.NoError(t, err, "unable to retrieve alice's balance")
4✔
2208
        bobPkScript := newPkScript(t, bob, lnwallet.WitnessPubKey)
4✔
2209

4✔
2210
        // We'll use a transaction fee of 14380 satoshis, which will allow us to
4✔
2211
        // sweep all of Alice's balance in one transaction containing 1 input
4✔
2212
        // and 1 output.
4✔
2213
        //
4✔
2214
        // TODO(wilmer): replace this once SendOutputs easily supports sending
4✔
2215
        // all funds in one transaction.
4✔
2216
        txFeeRate := chainfee.SatPerKWeight(2500)
4✔
2217
        const txFee = int64(14500)
4✔
2218
        output := &wire.TxOut{
4✔
2219
                Value:    int64(aliceBalance) - txFee,
4✔
2220
                PkScript: bobPkScript,
4✔
2221
        }
4✔
2222
        tx := sendCoins(t, r, alice, bob, output, txFeeRate, true, 1)
4✔
2223
        txHash := tx.TxHash()
4✔
2224
        assertTxInWallet(t, alice, txHash, true)
4✔
2225
        assertTxInWallet(t, bob, txHash, true)
4✔
2226

4✔
2227
        // With the transaction sent and confirmed, Alice's balance should now
4✔
2228
        // be 0.
4✔
2229
        aliceBalance, err = alice.ConfirmedBalance(0, lnwallet.DefaultAccountName)
4✔
2230
        require.NoError(t, err, "unable to retrieve alice's balance")
4✔
2231
        if aliceBalance != 0 {
4✔
2232
                t.Fatalf("expected alice's balance to be 0 BTC, found %v",
×
2233
                        aliceBalance)
×
2234
        }
×
2235

2236
        // Now, we'll send an output back to Alice from Bob of 1 BTC.
2237
        alicePkScript := newPkScript(t, alice, lnwallet.WitnessPubKey)
4✔
2238
        output = &wire.TxOut{
4✔
2239
                Value:    btcutil.SatoshiPerBitcoin,
4✔
2240
                PkScript: alicePkScript,
4✔
2241
        }
4✔
2242
        tx = sendCoins(t, r, bob, alice, output, txFeeRate, true, 1)
4✔
2243
        txHash = tx.TxHash()
4✔
2244
        assertTxInWallet(t, alice, txHash, true)
4✔
2245
        assertTxInWallet(t, bob, txHash, true)
4✔
2246

4✔
2247
        // Alice now has an available output to spend, but it was not a change
4✔
2248
        // output, which is what the test expects. Therefore, we'll generate one
4✔
2249
        // by sending Bob back some coins.
4✔
2250
        output = &wire.TxOut{
4✔
2251
                Value:    btcutil.SatoshiPerBitcent,
4✔
2252
                PkScript: bobPkScript,
4✔
2253
        }
4✔
2254
        tx = sendCoins(t, r, alice, bob, output, txFeeRate, true, 1)
4✔
2255
        txHash = tx.TxHash()
4✔
2256
        assertTxInWallet(t, alice, txHash, true)
4✔
2257
        assertTxInWallet(t, bob, txHash, true)
4✔
2258

4✔
2259
        // Then, we'll spend the change output and ensure we see its
4✔
2260
        // confirmation come in.
4✔
2261
        tx = sendCoins(t, r, alice, bob, output, txFeeRate, true, 1)
4✔
2262
        txHash = tx.TxHash()
4✔
2263
        assertTxInWallet(t, alice, txHash, true)
4✔
2264
        assertTxInWallet(t, bob, txHash, true)
4✔
2265

4✔
2266
        // Finally, we'll replenish Alice's wallet with some more coins to
4✔
2267
        // ensure she has enough for any following test cases.
4✔
2268
        if err := loadTestCredits(r, alice, 20, 4); err != nil {
4✔
2269
                t.Fatalf("unable to replenish alice's wallet: %v", err)
×
2270
        }
×
2271
}
2272

2273
// testSpendUnconfirmed ensures that when can spend unconfirmed outputs.
2274
func testSpendUnconfirmed(miner *rpctest.Harness,
2275
        alice, bob *lnwallet.LightningWallet, t *testing.T) {
3✔
2276

3✔
2277
        bobPkScript := newPkScript(t, bob, lnwallet.WitnessPubKey)
3✔
2278
        alicePkScript := newPkScript(t, alice, lnwallet.WitnessPubKey)
3✔
2279
        txFeeRate := chainfee.SatPerKWeight(2500)
3✔
2280

3✔
2281
        // First we will empty out bob's wallet, sending the entire balance
3✔
2282
        // to alice.
3✔
2283
        bobBalance, err := bob.ConfirmedBalance(0, lnwallet.DefaultAccountName)
3✔
2284
        require.NoError(t, err, "unable to retrieve bob's balance")
3✔
2285
        txFee := btcutil.Amount(28760)
3✔
2286
        output := &wire.TxOut{
3✔
2287
                Value:    int64(bobBalance - txFee),
3✔
2288
                PkScript: alicePkScript,
3✔
2289
        }
3✔
2290
        tx := sendCoins(t, miner, bob, alice, output, txFeeRate, true, 1)
3✔
2291
        txHash := tx.TxHash()
3✔
2292
        assertTxInWallet(t, alice, txHash, true)
3✔
2293
        assertTxInWallet(t, bob, txHash, true)
3✔
2294

3✔
2295
        // Verify that bob doesn't have enough balance to send coins.
3✔
2296
        output = &wire.TxOut{
3✔
2297
                Value:    btcutil.SatoshiPerBitcoin * 0.5,
3✔
2298
                PkScript: alicePkScript,
3✔
2299
        }
3✔
2300
        _, err = bob.SendOutputs(
3✔
2301
                []*wire.TxOut{output}, txFeeRate, 0, labels.External,
3✔
2302
                bob.Cfg.CoinSelectionStrategy,
3✔
2303
        )
3✔
2304
        if err == nil {
3✔
2305
                t.Fatalf("should have not been able to pay due to insufficient balance: %v", err)
×
2306
        }
×
2307

2308
        // Next we will send a transaction to bob but leave it in an
2309
        // unconfirmed state.
2310
        output = &wire.TxOut{
3✔
2311
                Value:    btcutil.SatoshiPerBitcoin,
3✔
2312
                PkScript: bobPkScript,
3✔
2313
        }
3✔
2314
        tx = sendCoins(t, miner, alice, bob, output, txFeeRate, false, 1)
3✔
2315
        txHash = tx.TxHash()
3✔
2316
        assertTxInWallet(t, alice, txHash, false)
3✔
2317
        assertTxInWallet(t, bob, txHash, false)
3✔
2318

3✔
2319
        // Now, try to spend some of the unconfirmed funds from bob's wallet.
3✔
2320
        output = &wire.TxOut{
3✔
2321
                Value:    btcutil.SatoshiPerBitcoin * 0.5,
3✔
2322
                PkScript: alicePkScript,
3✔
2323
        }
3✔
2324

3✔
2325
        // First, verify that we don't have enough balance to send the coins
3✔
2326
        // using confirmed outputs only.
3✔
2327
        _, err = bob.SendOutputs(
3✔
2328
                []*wire.TxOut{output}, txFeeRate, 1, labels.External,
3✔
2329
                bob.Cfg.CoinSelectionStrategy,
3✔
2330
        )
3✔
2331
        if err == nil {
3✔
2332
                t.Fatalf("should have not been able to pay due to insufficient balance: %v", err)
×
2333
        }
×
2334

2335
        // Now try the send again using unconfirmed outputs.
2336
        tx = sendCoins(t, miner, bob, alice, output, txFeeRate, false, 0)
3✔
2337
        txHash = tx.TxHash()
3✔
2338
        assertTxInWallet(t, alice, txHash, false)
3✔
2339
        assertTxInWallet(t, bob, txHash, false)
3✔
2340

3✔
2341
        // Mine the unconfirmed transactions.
3✔
2342
        err = waitForMempoolTx(miner, &txHash)
3✔
2343
        require.NoError(t, err, "tx not relayed to miner")
3✔
2344
        if _, err := miner.Client.Generate(1); err != nil {
3✔
2345
                t.Fatalf("unable to generate block: %v", err)
×
2346
        }
×
2347
        if err := waitForWalletSync(miner, alice); err != nil {
3✔
2348
                t.Fatalf("unable to sync alice: %v", err)
×
2349
        }
×
2350
        if err := waitForWalletSync(miner, bob); err != nil {
3✔
2351
                t.Fatalf("unable to sync bob: %v", err)
×
2352
        }
×
2353

2354
        // Finally, send the remainder of bob's wallet balance back to him so
2355
        // that these money movements dont mess up later tests.
2356
        output = &wire.TxOut{
3✔
2357
                Value:    int64(bobBalance) - (btcutil.SatoshiPerBitcoin * 0.4),
3✔
2358
                PkScript: bobPkScript,
3✔
2359
        }
3✔
2360
        tx = sendCoins(t, miner, alice, bob, output, txFeeRate, true, 1)
3✔
2361
        txHash = tx.TxHash()
3✔
2362
        assertTxInWallet(t, alice, txHash, true)
3✔
2363
        assertTxInWallet(t, bob, txHash, true)
3✔
2364
}
2365

2366
// testLastUnusedAddr tests that the LastUnusedAddress returns the address if
2367
// it isn't used, and also that once the address becomes used, then it's
2368
// properly rotated.
2369
func testLastUnusedAddr(miner *rpctest.Harness,
2370
        alice, bob *lnwallet.LightningWallet, t *testing.T) {
4✔
2371

4✔
2372
        if _, err := miner.Client.Generate(1); err != nil {
4✔
2373
                t.Fatalf("unable to generate block: %v", err)
×
2374
        }
×
2375

2376
        // We'll repeat this test for each address type to ensure they're all
2377
        // rotated properly.
2378
        addrTypes := []lnwallet.AddressType{
4✔
2379
                lnwallet.WitnessPubKey, lnwallet.NestedWitnessPubKey,
4✔
2380
        }
4✔
2381
        for _, addrType := range addrTypes {
12✔
2382
                addr1, err := alice.LastUnusedAddress(
8✔
2383
                        addrType, lnwallet.DefaultAccountName,
8✔
2384
                )
8✔
2385
                if err != nil {
8✔
2386
                        t.Fatalf("unable to get addr: %v", err)
×
2387
                }
×
2388
                addr2, err := alice.LastUnusedAddress(
8✔
2389
                        addrType, lnwallet.DefaultAccountName,
8✔
2390
                )
8✔
2391
                if err != nil {
8✔
2392
                        t.Fatalf("unable to get addr: %v", err)
×
2393
                }
×
2394

2395
                // If we generate two addresses back to back, then we should
2396
                // get the same addr, as none of them have been used yet.
2397
                if addr1.String() != addr2.String() {
8✔
2398
                        t.Fatalf("addresses changed w/o use: %v vs %v", addr1, addr2)
×
2399
                }
×
2400

2401
                // Next, we'll have Bob pay to Alice's new address. This should
2402
                // trigger address rotation at the backend wallet.
2403
                addrScript, err := txscript.PayToAddrScript(addr1)
8✔
2404
                if err != nil {
8✔
2405
                        t.Fatalf("unable to convert addr to script: %v", err)
×
2406
                }
×
2407
                feeRate := chainfee.SatPerKWeight(2500)
8✔
2408
                output := &wire.TxOut{
8✔
2409
                        Value:    1000000,
8✔
2410
                        PkScript: addrScript,
8✔
2411
                }
8✔
2412
                sendCoins(t, miner, bob, alice, output, feeRate, true, 1)
8✔
2413

8✔
2414
                // If we make a new address, then it should be brand new, as
8✔
2415
                // the prior address has been used.
8✔
2416
                addr3, err := alice.LastUnusedAddress(
8✔
2417
                        addrType, lnwallet.DefaultAccountName,
8✔
2418
                )
8✔
2419
                if err != nil {
8✔
2420
                        t.Fatalf("unable to get addr: %v", err)
×
2421
                }
×
2422
                if addr1.String() == addr3.String() {
8✔
2423
                        t.Fatalf("address should have changed but didn't")
×
2424
                }
×
2425
        }
2426
}
2427

2428
// testCreateSimpleTx checks that a call to CreateSimpleTx will return a
2429
// transaction that is equal to the one that is being created by SendOutputs in
2430
// a subsequent call.
2431
// All test cases are doubled-up: one for testing unconfirmed inputs,
2432
// one for testing only confirmed inputs.
2433
func testCreateSimpleTx(r *rpctest.Harness, w *lnwallet.LightningWallet,
2434
        _ *lnwallet.LightningWallet, t *testing.T) {
4✔
2435

4✔
2436
        // Send some money from the miner to the wallet
4✔
2437
        err := loadTestCredits(r, w, 20, 4)
4✔
2438
        require.NoError(t, err, "unable to send money to lnwallet")
4✔
2439

4✔
2440
        // The test cases we will run through for all backends.
4✔
2441
        testCases := []struct {
4✔
2442
                outVals     []int64
4✔
2443
                feeRate     chainfee.SatPerKWeight
4✔
2444
                valid       bool
4✔
2445
                unconfirmed bool
4✔
2446
        }{
4✔
2447
                {
4✔
2448
                        outVals:     []int64{},
4✔
2449
                        feeRate:     2500,
4✔
2450
                        valid:       false, // No outputs.
4✔
2451
                        unconfirmed: false,
4✔
2452
                },
4✔
2453
                {
4✔
2454
                        outVals:     []int64{},
4✔
2455
                        feeRate:     2500,
4✔
2456
                        valid:       false, // No outputs.
4✔
2457
                        unconfirmed: true,
4✔
2458
                },
4✔
2459

4✔
2460
                {
4✔
2461
                        outVals:     []int64{200},
4✔
2462
                        feeRate:     2500,
4✔
2463
                        valid:       false, // Dust output.
4✔
2464
                        unconfirmed: false,
4✔
2465
                },
4✔
2466
                {
4✔
2467
                        outVals:     []int64{200},
4✔
2468
                        feeRate:     2500,
4✔
2469
                        valid:       false, // Dust output.
4✔
2470
                        unconfirmed: true,
4✔
2471
                },
4✔
2472

4✔
2473
                {
4✔
2474
                        outVals:     []int64{1e8},
4✔
2475
                        feeRate:     2500,
4✔
2476
                        valid:       true,
4✔
2477
                        unconfirmed: false,
4✔
2478
                },
4✔
2479
                {
4✔
2480
                        outVals:     []int64{1e8},
4✔
2481
                        feeRate:     2500,
4✔
2482
                        valid:       true,
4✔
2483
                        unconfirmed: true,
4✔
2484
                },
4✔
2485

4✔
2486
                {
4✔
2487
                        outVals:     []int64{1e8, 2e8, 1e8, 2e7, 3e5},
4✔
2488
                        feeRate:     2500,
4✔
2489
                        valid:       true,
4✔
2490
                        unconfirmed: false,
4✔
2491
                },
4✔
2492
                {
4✔
2493
                        outVals:     []int64{1e8, 2e8, 1e8, 2e7, 3e5},
4✔
2494
                        feeRate:     2500,
4✔
2495
                        valid:       true,
4✔
2496
                        unconfirmed: true,
4✔
2497
                },
4✔
2498

4✔
2499
                {
4✔
2500
                        outVals:     []int64{1e8, 2e8, 1e8, 2e7, 3e5},
4✔
2501
                        feeRate:     12500,
4✔
2502
                        valid:       true,
4✔
2503
                        unconfirmed: false,
4✔
2504
                },
4✔
2505
                {
4✔
2506
                        outVals:     []int64{1e8, 2e8, 1e8, 2e7, 3e5},
4✔
2507
                        feeRate:     12500,
4✔
2508
                        valid:       true,
4✔
2509
                        unconfirmed: true,
4✔
2510
                },
4✔
2511

4✔
2512
                {
4✔
2513
                        outVals:     []int64{1e8, 2e8, 1e8, 2e7, 3e5},
4✔
2514
                        feeRate:     50000,
4✔
2515
                        valid:       true,
4✔
2516
                        unconfirmed: false,
4✔
2517
                },
4✔
2518
                {
4✔
2519
                        outVals:     []int64{1e8, 2e8, 1e8, 2e7, 3e5},
4✔
2520
                        feeRate:     50000,
4✔
2521
                        valid:       true,
4✔
2522
                        unconfirmed: true,
4✔
2523
                },
4✔
2524

4✔
2525
                {
4✔
2526
                        outVals: []int64{1e8, 2e8, 1e8, 2e7, 3e5, 1e8, 2e8,
4✔
2527
                                1e8, 2e7, 3e5},
4✔
2528
                        feeRate:     44250,
4✔
2529
                        valid:       true,
4✔
2530
                        unconfirmed: false,
4✔
2531
                },
4✔
2532
                {
4✔
2533
                        outVals: []int64{1e8, 2e8, 1e8, 2e7, 3e5, 1e8, 2e8,
4✔
2534
                                1e8, 2e7, 3e5},
4✔
2535
                        feeRate:     44250,
4✔
2536
                        valid:       true,
4✔
2537
                        unconfirmed: true,
4✔
2538
                },
4✔
2539
        }
4✔
2540

4✔
2541
        for i, test := range testCases {
60✔
2542
                var minConfs int32 = 1
56✔
2543

56✔
2544
                feeRate := test.feeRate
56✔
2545

56✔
2546
                // Grab some fresh addresses from the miner that we will send
56✔
2547
                // to.
56✔
2548
                outputs := make([]*wire.TxOut, len(test.outVals))
56✔
2549
                for i, outVal := range test.outVals {
272✔
2550
                        minerAddr, err := r.NewAddress()
216✔
2551
                        if err != nil {
216✔
2552
                                t.Fatalf("unable to generate address for "+
×
2553
                                        "miner: %v", err)
×
2554
                        }
×
2555
                        script, err := txscript.PayToAddrScript(minerAddr)
216✔
2556
                        if err != nil {
216✔
2557
                                t.Fatalf("unable to create pay to addr "+
×
2558
                                        "script: %v", err)
×
2559
                        }
×
2560
                        output := &wire.TxOut{
216✔
2561
                                Value:    outVal,
216✔
2562
                                PkScript: script,
216✔
2563
                        }
216✔
2564

216✔
2565
                        outputs[i] = output
216✔
2566
                }
2567

2568
                // Now try creating a tx spending to these outputs.
2569
                createTx, createErr := w.CreateSimpleTx(
56✔
2570
                        outputs, feeRate, minConfs,
56✔
2571
                        w.Cfg.CoinSelectionStrategy, true,
56✔
2572
                )
56✔
2573
                switch {
56✔
2574
                case test.valid && createErr != nil:
×
2575
                        fmt.Println(spew.Sdump(createTx.Tx))
×
2576
                        t.Fatalf("got unexpected error when creating tx: %v",
×
2577
                                createErr)
×
2578

2579
                case !test.valid && createErr == nil:
×
2580
                        t.Fatalf("test #%v should have failed on tx "+
×
2581
                                "creation", i)
×
2582
                }
2583

2584
                // Also send to these outputs. This should result in a tx
2585
                // _very_ similar to the one we just created being sent. The
2586
                // only difference is that the dry run tx is not signed, and
2587
                // that the change output position might be different.
2588
                tx, sendErr := w.SendOutputs(
56✔
2589
                        outputs, feeRate, minConfs, labels.External,
56✔
2590
                        w.Cfg.CoinSelectionStrategy,
56✔
2591
                )
56✔
2592
                switch {
56✔
2593
                case test.valid && sendErr != nil:
×
2594
                        t.Fatalf("got unexpected error when sending tx: %v",
×
2595
                                sendErr)
×
2596

2597
                case !test.valid && sendErr == nil:
×
2598
                        t.Fatalf("test #%v should fail for tx sending", i)
×
2599
                }
2600

2601
                // We expected either both to not fail, or both to fail with
2602
                // the same error.
2603
                if createErr != sendErr {
56✔
2604
                        t.Fatalf("error creating tx (%v) different "+
×
2605
                                "from error sending outputs (%v)",
×
2606
                                createErr, sendErr)
×
2607
                }
×
2608

2609
                // If we expected the creation to fail, then this test is over.
2610
                if !test.valid {
72✔
2611
                        continue
16✔
2612
                }
2613

2614
                txid := tx.TxHash()
40✔
2615
                err = waitForMempoolTx(r, &txid)
40✔
2616
                if err != nil {
40✔
2617
                        t.Fatalf("tx not relayed to miner: %v", err)
×
2618
                }
×
2619

2620
                // Helper method to check that the two txs are similar.
2621
                assertSimilarTx := func(a, b *wire.MsgTx) error {
80✔
2622
                        if a.Version != b.Version {
40✔
2623
                                return fmt.Errorf("different versions: "+
×
2624
                                        "%v vs %v", a.Version, b.Version)
×
2625
                        }
×
2626
                        if a.LockTime != b.LockTime {
40✔
2627
                                return fmt.Errorf("different locktimes: "+
×
2628
                                        "%v vs %v", a.LockTime, b.LockTime)
×
2629
                        }
×
2630
                        if len(a.TxIn) != len(b.TxIn) {
40✔
2631
                                return fmt.Errorf("different number of "+
×
2632
                                        "inputs: %v vs %v", len(a.TxIn),
×
2633
                                        len(b.TxIn))
×
2634
                        }
×
2635
                        if len(a.TxOut) != len(b.TxOut) {
40✔
2636
                                return fmt.Errorf("different number of "+
×
2637
                                        "outputs: %v vs %v", len(a.TxOut),
×
2638
                                        len(b.TxOut))
×
2639
                        }
×
2640

2641
                        // They should be spending the same inputs.
2642
                        for i := range a.TxIn {
120✔
2643
                                prevA := a.TxIn[i].PreviousOutPoint
80✔
2644
                                prevB := b.TxIn[i].PreviousOutPoint
80✔
2645
                                if prevA != prevB {
80✔
2646
                                        return fmt.Errorf("different inputs: "+
×
2647
                                                "%v vs %v", spew.Sdump(prevA),
×
2648
                                                spew.Sdump(prevB))
×
2649
                                }
×
2650
                        }
2651

2652
                        // They should have the same outputs. Since the change
2653
                        // output position gets randomized, they are not
2654
                        // guaranteed to be in the same order.
2655
                        for _, outA := range a.TxOut {
288✔
2656
                                found := false
248✔
2657
                                for _, outB := range b.TxOut {
1,304✔
2658
                                        if reflect.DeepEqual(outA, outB) {
1,304✔
2659
                                                found = true
248✔
2660
                                                break
248✔
2661
                                        }
2662
                                }
2663
                                if !found {
248✔
2664
                                        return fmt.Errorf("did not find "+
×
2665
                                                "output %v", spew.Sdump(outA))
×
2666
                                }
×
2667
                        }
2668
                        return nil
40✔
2669
                }
2670

2671
                // Assert that our "template tx" was similar to the one that
2672
                // ended up being sent.
2673
                if err := assertSimilarTx(createTx.Tx, tx); err != nil {
40✔
2674
                        t.Fatalf("transactions not similar: %v", err)
×
2675
                }
×
2676

2677
                // Now that we know both transactions were essentially
2678
                // identical, we'll make sure that a P2TR addr was used as the
2679
                // change output, which is the current default.
2680
                changeTxOut := createTx.Tx.TxOut[createTx.ChangeIndex]
40✔
2681
                changeScriptType, _, _, err := txscript.ExtractPkScriptAddrs(
40✔
2682
                        changeTxOut.PkScript, &w.Cfg.NetParams,
40✔
2683
                )
40✔
2684
                require.NoError(t, err)
40✔
2685
                require.Equal(t, changeScriptType, txscript.WitnessV1TaprootTy)
40✔
2686
        }
2687
}
2688

2689
// testSignOutputCreateAccount tests that we're able to properly sign for an
2690
// output if the target account hasn't yet been created on disk. In this case,
2691
// we'll create the account, then sign.
2692
func testSignOutputCreateAccount(r *rpctest.Harness, w *lnwallet.LightningWallet,
2693
        _ *lnwallet.LightningWallet, t *testing.T) {
4✔
2694

4✔
2695
        // First, we'll create a sign desc that references a non-default key
4✔
2696
        // family. Under the hood, key families are actually accounts, so this
4✔
2697
        // should force create of the account so we can sign with it.
4✔
2698
        fakeTx := wire.NewMsgTx(2)
4✔
2699
        fakeTx.AddTxIn(&wire.TxIn{
4✔
2700
                PreviousOutPoint: wire.OutPoint{
4✔
2701
                        Hash:  chainhash.Hash{},
4✔
2702
                        Index: 0,
4✔
2703
                },
4✔
2704
        })
4✔
2705
        signDesc := &input.SignDescriptor{
4✔
2706
                KeyDesc: keychain.KeyDescriptor{
4✔
2707
                        KeyLocator: keychain.KeyLocator{
4✔
2708
                                Family: 99,
4✔
2709
                                Index:  1,
4✔
2710
                        },
4✔
2711
                },
4✔
2712
                WitnessScript: []byte{},
4✔
2713
                Output: &wire.TxOut{
4✔
2714
                        Value: 1000,
4✔
2715
                },
4✔
2716
                HashType:   txscript.SigHashAll,
4✔
2717
                SigHashes:  input.NewTxSigHashesV0Only(fakeTx),
4✔
2718
                InputIndex: 0,
4✔
2719
        }
4✔
2720

4✔
2721
        // We'll now sign and expect this to succeed, as even though the
4✔
2722
        // account doesn't exist atm, it should be created in order to process
4✔
2723
        // the inbound signing request.
4✔
2724
        _, err := w.Cfg.Signer.SignOutputRaw(fakeTx, signDesc)
4✔
2725
        if err != nil {
4✔
2726
                t.Fatalf("unable to sign for output with non-existent "+
×
2727
                        "account: %v", err)
×
2728
        }
×
2729
}
2730

2731
type walletTestCase struct {
2732
        name string
2733
        test func(miner *rpctest.Harness, alice, bob *lnwallet.LightningWallet,
2734
                test *testing.T)
2735
}
2736

2737
var walletTests = []walletTestCase{
2738
        {
2739
                // TODO(wilmer): this test should remain first until the wallet
2740
                // can properly craft a transaction that spends all of its
2741
                // on-chain funds.
2742
                name: "change output spend confirmation",
2743
                test: testChangeOutputSpendConfirmation,
2744
        },
2745
        {
2746
                // TODO(guggero): this test should remain second until dual
2747
                // funding can properly exchange full UTXO information and we
2748
                // can use P2TR change outputs as the funding inputs for a dual
2749
                // funded channel.
2750
                name: "dual funder workflow",
2751
                test: testDualFundingReservationWorkflow,
2752
        },
2753
        {
2754
                name: "spend unconfirmed outputs",
2755
                test: testSpendUnconfirmed,
2756
        },
2757
        {
2758
                name: "insane fee reject",
2759
                test: testReservationInitiatorBalanceBelowDustCancel,
2760
        },
2761
        {
2762
                name: "single funding workflow",
2763
                test: func(miner *rpctest.Harness, alice,
2764
                        bob *lnwallet.LightningWallet, t *testing.T) {
4✔
2765

4✔
2766
                        testSingleFunderReservationWorkflow(
4✔
2767
                                miner, alice, bob, t,
4✔
2768
                                lnwallet.CommitmentTypeLegacy, nil,
4✔
2769
                                nil, [32]byte{1}, 0,
4✔
2770
                        )
4✔
2771
                },
4✔
2772
        },
2773
        {
2774
                name: "single funding workflow tweakless",
2775
                test: func(miner *rpctest.Harness, alice,
2776
                        bob *lnwallet.LightningWallet, t *testing.T) {
4✔
2777

4✔
2778
                        testSingleFunderReservationWorkflow(
4✔
2779
                                miner, alice, bob, t,
4✔
2780
                                lnwallet.CommitmentTypeTweakless, nil,
4✔
2781
                                nil, [32]byte{1}, 0,
4✔
2782
                        )
4✔
2783
                },
4✔
2784
        },
2785
        {
2786
                name: "single funding workflow musig2",
2787
                test: func(miner *rpctest.Harness, alice,
2788
                        bob *lnwallet.LightningWallet, t *testing.T) {
4✔
2789

4✔
2790
                        testSingleFunderReservationWorkflow(
4✔
2791
                                miner, alice, bob, t,
4✔
2792
                                lnwallet.CommitmentTypeSimpleTaproot, nil,
4✔
2793
                                nil, [32]byte{1}, 0,
4✔
2794
                        )
4✔
2795
                },
4✔
2796
        },
2797
        // TODO(roasbeef): add musig2 external funding
2798
        {
2799
                name: "single funding workflow external funding tx",
2800
                test: testSingleFunderExternalFundingTx,
2801
        },
2802
        {
2803
                name: "output locking",
2804
                test: testFundingTransactionLockedOutputs,
2805
        },
2806
        {
2807
                name: "reservation insufficient funds",
2808
                test: testFundingCancellationNotEnoughFunds,
2809
        },
2810
        {
2811
                name: "transaction subscriptions",
2812
                test: testTransactionSubscriptions,
2813
        },
2814
        {
2815
                name: "transaction details",
2816
                test: testListTransactionDetails,
2817
        },
2818
        {
2819
                name: "get transaction details",
2820
                test: testGetTransactionDetails,
2821
        },
2822
        {
2823
                name: "publish transaction",
2824
                test: testPublishTransaction,
2825
        },
2826
        {
2827
                name: "signed with tweaked pubkeys",
2828
                test: testSignOutputUsingTweaks,
2829
        },
2830
        {
2831
                name: "test cancel non-existent reservation",
2832
                test: testCancelNonExistentReservation,
2833
        },
2834
        {
2835
                name: "last unused addr",
2836
                test: testLastUnusedAddr,
2837
        },
2838
        {
2839
                name: "reorg wallet balance",
2840
                test: testReorgWalletBalance,
2841
        },
2842
        {
2843
                name: "create simple tx",
2844
                test: testCreateSimpleTx,
2845
        },
2846
        {
2847
                name: "test sign create account",
2848
                test: testSignOutputCreateAccount,
2849
        },
2850
        {
2851
                name: "test get recovery info",
2852
                test: testGetRecoveryInfo,
2853
        },
2854
}
2855

2856
func clearWalletStates(a, b *lnwallet.LightningWallet) error {
84✔
2857
        a.ResetReservations()
84✔
2858
        b.ResetReservations()
84✔
2859

84✔
2860
        if err := a.Cfg.Database.GetParentDB().Wipe(); err != nil {
84✔
2861
                return err
×
2862
        }
×
2863

2864
        return b.Cfg.Database.GetParentDB().Wipe()
84✔
2865
}
2866

2867
func waitForMempoolTx(r *rpctest.Harness, txid *chainhash.Hash) error {
173✔
2868
        var found bool
173✔
2869
        var tx *btcutil.Tx
173✔
2870
        var err error
173✔
2871
        timeout := time.After(30 * time.Second)
173✔
2872
        for !found {
1,675✔
2873
                // Do a short wait
1,502✔
2874
                select {
1,502✔
2875
                case <-timeout:
×
2876
                        return fmt.Errorf("timeout after 10s")
×
2877
                default:
1,502✔
2878
                }
2879
                time.Sleep(100 * time.Millisecond)
1,502✔
2880

1,502✔
2881
                // Check for the harness' knowledge of the txid
1,502✔
2882
                tx, err = r.Client.GetRawTransaction(txid)
1,502✔
2883
                if err != nil {
2,831✔
2884
                        switch e := err.(type) {
1,329✔
2885
                        case *btcjson.RPCError:
1,329✔
2886
                                if e.Code == btcjson.ErrRPCNoTxInfo {
2,658✔
2887
                                        continue
1,329✔
2888
                                }
2889
                        default:
×
2890
                        }
2891
                        return err
×
2892
                }
2893
                if tx != nil && tx.MsgTx().TxHash() == *txid {
346✔
2894
                        found = true
173✔
2895
                }
173✔
2896
        }
2897
        return nil
173✔
2898
}
2899

2900
func waitForWalletSync(r *rpctest.Harness, w *lnwallet.LightningWallet) error {
138✔
2901
        var (
138✔
2902
                synced                  bool
138✔
2903
                err                     error
138✔
2904
                bestHash, knownHash     *chainhash.Hash
138✔
2905
                bestHeight, knownHeight int32
138✔
2906
        )
138✔
2907
        timeout := time.After(10 * time.Second)
138✔
2908
        for !synced {
329✔
2909
                // Do a short wait
191✔
2910
                select {
191✔
2911
                case <-timeout:
×
2912
                        return fmt.Errorf("timeout after 30s")
×
2913
                case <-time.Tick(100 * time.Millisecond):
191✔
2914
                }
2915

2916
                // Check whether the chain source of the wallet is caught up to
2917
                // the harness it's supposed to be catching up to.
2918
                bestHash, bestHeight, err = r.Client.GetBestBlock()
191✔
2919
                if err != nil {
191✔
2920
                        return err
×
2921
                }
×
2922
                knownHash, knownHeight, err = w.Cfg.ChainIO.GetBestBlock()
191✔
2923
                if err != nil {
191✔
2924
                        return err
×
2925
                }
×
2926
                if knownHeight != bestHeight {
191✔
UNCOV
2927
                        continue
×
2928
                }
2929
                if *knownHash != *bestHash {
191✔
2930
                        return fmt.Errorf("hash at height %d doesn't match: "+
×
2931
                                "expected %s, got %s", bestHeight, bestHash,
×
2932
                                knownHash)
×
2933
                }
×
2934

2935
                // Check for synchronization.
2936
                synced, _, err = w.IsSynced()
191✔
2937
                if err != nil {
191✔
2938
                        return err
×
2939
                }
×
2940
        }
2941
        return nil
138✔
2942
}
2943

2944
// testSingleFunderExternalFundingTx tests that the wallet is able to properly
2945
// carry out a funding flow backed by a channel point that has been crafted
2946
// outside the wallet.
2947
func testSingleFunderExternalFundingTx(miner *rpctest.Harness,
2948
        alice, bob *lnwallet.LightningWallet, t *testing.T) {
4✔
2949

4✔
2950
        // Define a filter function without any restrictions.
4✔
2951
        allowUtxo := func(lnwallet.Utxo) bool {
76✔
2952
                return true
72✔
2953
        }
72✔
2954

2955
        // First, we'll obtain multi-sig keys from both Alice and Bob which
2956
        // simulates them exchanging keys on a higher level.
2957
        aliceFundingKey, err := alice.DeriveNextKey(keychain.KeyFamilyMultiSig)
4✔
2958
        require.NoError(t, err, "unable to obtain alice funding key")
4✔
2959
        bobFundingKey, err := bob.DeriveNextKey(keychain.KeyFamilyMultiSig)
4✔
2960
        require.NoError(t, err, "unable to obtain bob funding key")
4✔
2961

4✔
2962
        // We'll now set up for them to open a 4 BTC channel, with 1 BTC pushed
4✔
2963
        // to Bob's side.
4✔
2964
        chanAmt := 4 * btcutil.SatoshiPerBitcoin
4✔
2965

4✔
2966
        // Simulating external funding negotiation, we'll now create the
4✔
2967
        // funding transaction for both parties. Utilizing existing tools,
4✔
2968
        // we'll create a new chanfunding.Assembler hacked by Alice's wallet.
4✔
2969
        aliceChanFunder := chanfunding.NewWalletAssembler(
4✔
2970
                chanfunding.WalletConfig{
4✔
2971
                        CoinSource: lnwallet.NewCoinSource(
4✔
2972
                                alice, allowUtxo,
4✔
2973
                        ),
4✔
2974
                        CoinSelectLocker:      alice,
4✔
2975
                        CoinLeaser:            alice,
4✔
2976
                        Signer:                alice.Cfg.Signer,
4✔
2977
                        DustLimit:             600,
4✔
2978
                        CoinSelectionStrategy: wallet.CoinSelectionLargest,
4✔
2979
                },
4✔
2980
        )
4✔
2981

4✔
2982
        // With the chan funder created, we'll now provision a funding intent,
4✔
2983
        // bind the keys we obtained above, and finally obtain our funding
4✔
2984
        // transaction and outpoint.
4✔
2985
        fundingIntent, err := aliceChanFunder.ProvisionChannel(
4✔
2986
                &chanfunding.Request{
4✔
2987
                        LocalAmt: btcutil.Amount(chanAmt),
4✔
2988
                        MinConfs: 1,
4✔
2989
                        FeeRate:  253,
4✔
2990
                        ChangeAddr: func() (btcutil.Address, error) {
8✔
2991
                                return alice.NewAddress(
4✔
2992
                                        lnwallet.WitnessPubKey, true,
4✔
2993
                                        lnwallet.DefaultAccountName,
4✔
2994
                                )
4✔
2995
                        },
4✔
2996
                },
2997
        )
2998
        require.NoError(t, err, "unable to perform coin selection")
4✔
2999

4✔
3000
        // With our intent created, we'll instruct it to finalize the funding
4✔
3001
        // transaction, and also hand us the outpoint so we can simulate
4✔
3002
        // external crafting of the funding transaction.
4✔
3003
        var (
4✔
3004
                fundingTx *wire.MsgTx
4✔
3005
                chanPoint *wire.OutPoint
4✔
3006
        )
4✔
3007
        if fullIntent, ok := fundingIntent.(*chanfunding.FullIntent); ok {
8✔
3008
                fullIntent.BindKeys(&aliceFundingKey, bobFundingKey.PubKey)
4✔
3009

4✔
3010
                fundingTx, err = fullIntent.CompileFundingTx(nil, nil)
4✔
3011
                if err != nil {
4✔
3012
                        t.Fatalf("unable to compile funding tx: %v", err)
×
3013
                }
×
3014
                chanPoint, err = fullIntent.ChanPoint()
4✔
3015
                if err != nil {
4✔
3016
                        t.Fatalf("unable to obtain chan point: %v", err)
×
3017
                }
×
3018
        } else {
×
3019
                t.Fatalf("expected full intent, instead got: %T", fullIntent)
×
3020
        }
×
3021

3022
        // Now that we have the fully constructed funding transaction, we'll
3023
        // create a new shim external funder out of it for Alice, and prep a
3024
        // shim intent for Bob.
3025
        thawHeight := uint32(200)
4✔
3026
        aliceExternalFunder := chanfunding.NewCannedAssembler(
4✔
3027
                thawHeight, *chanPoint, btcutil.Amount(chanAmt), &aliceFundingKey,
4✔
3028
                bobFundingKey.PubKey, true, false,
4✔
3029
        )
4✔
3030
        bobShimIntent, err := chanfunding.NewCannedAssembler(
4✔
3031
                thawHeight, *chanPoint, btcutil.Amount(chanAmt), &bobFundingKey,
4✔
3032
                aliceFundingKey.PubKey, false, false,
4✔
3033
        ).ProvisionChannel(&chanfunding.Request{
4✔
3034
                LocalAmt: btcutil.Amount(chanAmt),
4✔
3035
                MinConfs: 1,
4✔
3036
                FeeRate:  253,
4✔
3037
                ChangeAddr: func() (btcutil.Address, error) {
4✔
3038
                        return bob.NewAddress(
×
3039
                                lnwallet.WitnessPubKey, true,
×
3040
                                lnwallet.DefaultAccountName,
×
3041
                        )
×
3042
                },
×
3043
        })
3044
        require.NoError(t, err, "unable to create shim intent for bob")
4✔
3045

4✔
3046
        // At this point, we have everything we need to carry out our test, so
4✔
3047
        // we'll being the funding flow between Alice and Bob.
4✔
3048
        //
4✔
3049
        // However, before we do so, we'll register a new shim intent for Bob,
4✔
3050
        // so he knows what keys to use when he receives the funding request
4✔
3051
        // from Alice.
4✔
3052
        pendingChanID := testHdSeed
4✔
3053
        err = bob.RegisterFundingIntent(pendingChanID, bobShimIntent)
4✔
3054
        require.NoError(t, err, "unable to register intent")
4✔
3055

4✔
3056
        // Now we can carry out the single funding flow as normal, we'll
4✔
3057
        // specify our external funder and funding transaction, as well as the
4✔
3058
        // pending channel ID generated above to allow Alice and Bob to track
4✔
3059
        // the funding flow externally.
4✔
3060
        testSingleFunderReservationWorkflow(
4✔
3061
                miner, alice, bob, t, lnwallet.CommitmentTypeTweakless,
4✔
3062
                aliceExternalFunder, func() *wire.MsgTx {
8✔
3063
                        return fundingTx
4✔
3064
                }, pendingChanID, thawHeight,
4✔
3065
        )
3066
}
3067

3068
// TestInterfaces tests all registered interfaces with a unified set of tests
3069
// which exercise each of the required methods found within the WalletController
3070
// interface.
3071
//
3072
// NOTE: In the future, when additional implementations of the WalletController
3073
// interface have been implemented, in order to ensure the new concrete
3074
// implementation is automatically tested, two steps must be undertaken. First,
3075
// one needs add a "non-captured" (_) import from the new sub-package. This
3076
// import should trigger an init() method within the package which registers
3077
// the interface. Second, an additional case in the switch within the main loop
3078
// below needs to be added which properly initializes the interface.
3079
//
3080
// TODO(roasbeef): purge bobNode in favor of dual lnwallet's
3081
func TestLightningWallet(t *testing.T, targetBackEnd string) {
4✔
3082
        t.Parallel()
4✔
3083

4✔
3084
        // Initialize the harness around a btcd node which will serve as our
4✔
3085
        // dedicated miner to generate blocks, cause re-orgs, etc. We'll set
4✔
3086
        // up this node with a chain length of 125, so we have plenty of BTC
4✔
3087
        // to play around with.
4✔
3088
        miningNode := unittest.NewMiner(
4✔
3089
                t, netParams, []string{"--txindex"}, true, 25,
4✔
3090
        )
4✔
3091

4✔
3092
        // Next mine enough blocks in order for segwit and the CSV package
4✔
3093
        // soft-fork to activate on RegNet.
4✔
3094
        numBlocks := netParams.MinerConfirmationWindow * 2
4✔
3095
        if _, err := miningNode.Client.Generate(numBlocks); err != nil {
4✔
3096
                t.Fatalf("unable to generate blocks: %v", err)
×
3097
        }
×
3098

3099
        rpcConfig := miningNode.RPCConfig()
4✔
3100

4✔
3101
        tempDir := t.TempDir()
4✔
3102
        db, err := channeldb.Open(tempDir)
4✔
3103
        require.NoError(t, err, "unable to create db")
4✔
3104
        testCfg := channeldb.CacheConfig{
4✔
3105
                QueryDisable: false,
4✔
3106
        }
4✔
3107
        hintCache, err := channeldb.NewHeightHintCache(testCfg, db.Backend)
4✔
3108
        require.NoError(t, err, "unable to create height hint cache")
4✔
3109
        blockCache := blockcache.NewBlockCache(10000)
4✔
3110
        chainNotifier, err := btcdnotify.New(
4✔
3111
                &rpcConfig, netParams, hintCache, hintCache, blockCache,
4✔
3112
        )
4✔
3113
        require.NoError(t, err, "unable to create notifier")
4✔
3114
        if err := chainNotifier.Start(); err != nil {
4✔
3115
                t.Fatalf("unable to start notifier: %v", err)
×
3116
        }
×
3117

3118
        for _, walletDriver := range lnwallet.RegisteredWallets() {
8✔
3119
                for _, backEnd := range walletDriver.BackEnds() {
20✔
3120
                        if backEnd != targetBackEnd {
28✔
3121
                                continue
12✔
3122
                        }
3123

3124
                        if !runTests(t, walletDriver, backEnd, miningNode,
4✔
3125
                                rpcConfig, chainNotifier) {
4✔
3126

×
3127
                                return
×
3128
                        }
×
3129
                }
3130
        }
3131
}
3132

3133
// runTests runs all of the tests for a single interface implementation and
3134
// chain back-end combination. This makes it easier to use `defer` as well as
3135
// factoring out the test logic from the loop which cycles through the
3136
// interface implementations.
3137
func runTests(t *testing.T, walletDriver *lnwallet.WalletDriver,
3138
        backEnd string, miningNode *rpctest.Harness,
3139
        rpcConfig rpcclient.ConnConfig,
3140
        chainNotifier chainntnfs.ChainNotifier) bool {
4✔
3141

4✔
3142
        var (
4✔
3143
                bio lnwallet.BlockChainIO
4✔
3144

4✔
3145
                aliceSigner input.Signer
4✔
3146
                bobSigner   input.Signer
4✔
3147

4✔
3148
                aliceKeyRing keychain.SecretKeyRing
4✔
3149
                bobKeyRing   keychain.SecretKeyRing
4✔
3150

4✔
3151
                aliceWalletController lnwallet.WalletController
4✔
3152
                bobWalletController   lnwallet.WalletController
4✔
3153

4✔
3154
                err error
4✔
3155
        )
4✔
3156

4✔
3157
        tempTestDirAlice := t.TempDir()
4✔
3158
        tempTestDirBob := t.TempDir()
4✔
3159

4✔
3160
        blockCache := blockcache.NewBlockCache(10000)
4✔
3161

4✔
3162
        walletType := walletDriver.WalletType
4✔
3163
        switch walletType {
4✔
3164
        case "btcwallet":
4✔
3165
                var aliceClient, bobClient chain.Interface
4✔
3166
                switch backEnd {
4✔
3167
                case "btcd":
1✔
3168
                        aliceClient, err = chain.NewRPCClient(
1✔
3169
                                netParams, rpcConfig.Host, rpcConfig.User,
1✔
3170
                                rpcConfig.Pass, rpcConfig.Certificates, false,
1✔
3171
                                20,
1✔
3172
                        )
1✔
3173
                        if err != nil {
1✔
3174
                                t.Fatalf("unable to make chain rpc: %v", err)
×
3175
                        }
×
3176
                        bobClient, err = chain.NewRPCClient(
1✔
3177
                                netParams, rpcConfig.Host, rpcConfig.User,
1✔
3178
                                rpcConfig.Pass, rpcConfig.Certificates, false,
1✔
3179
                                20,
1✔
3180
                        )
1✔
3181
                        if err != nil {
1✔
3182
                                t.Fatalf("unable to make chain rpc: %v", err)
×
3183
                        }
×
3184

3185
                case "neutrino":
1✔
3186
                        // Set some package-level variable to speed up
1✔
3187
                        // operation for tests.
1✔
3188
                        neutrino.BanDuration = time.Millisecond * 100
1✔
3189
                        neutrino.QueryTimeout = time.Millisecond * 500
1✔
3190
                        neutrino.QueryNumRetries = 1
1✔
3191

1✔
3192
                        // Start Alice - open a database, start a neutrino
1✔
3193
                        // instance, and initialize a btcwallet driver for it.
1✔
3194
                        aliceDB, err := walletdb.Create(
1✔
3195
                                "bdb", tempTestDirAlice+"/neutrino.db", true,
1✔
3196
                                kvdb.DefaultDBTimeout,
1✔
3197
                        )
1✔
3198
                        if err != nil {
1✔
3199
                                t.Fatalf("unable to create DB: %v", err)
×
3200
                        }
×
3201
                        defer aliceDB.Close()
1✔
3202
                        aliceChain, err := neutrino.NewChainService(
1✔
3203
                                neutrino.Config{
1✔
3204
                                        DataDir:     tempTestDirAlice,
1✔
3205
                                        Database:    aliceDB,
1✔
3206
                                        ChainParams: *netParams,
1✔
3207
                                        ConnectPeers: []string{
1✔
3208
                                                miningNode.P2PAddress(),
1✔
3209
                                        },
1✔
3210
                                },
1✔
3211
                        )
1✔
3212
                        if err != nil {
1✔
3213
                                t.Fatalf("unable to make neutrino: %v", err)
×
3214
                        }
×
3215
                        aliceChain.Start()
1✔
3216
                        defer aliceChain.Stop()
1✔
3217
                        aliceClient = chain.NewNeutrinoClient(
1✔
3218
                                netParams, aliceChain,
1✔
3219
                        )
1✔
3220

1✔
3221
                        // Start Bob - open a database, start a neutrino
1✔
3222
                        // instance, and initialize a btcwallet driver for it.
1✔
3223
                        bobDB, err := walletdb.Create(
1✔
3224
                                "bdb", tempTestDirBob+"/neutrino.db", true,
1✔
3225
                                kvdb.DefaultDBTimeout,
1✔
3226
                        )
1✔
3227
                        if err != nil {
1✔
3228
                                t.Fatalf("unable to create DB: %v", err)
×
3229
                        }
×
3230
                        defer bobDB.Close()
1✔
3231
                        bobChain, err := neutrino.NewChainService(
1✔
3232
                                neutrino.Config{
1✔
3233
                                        DataDir:     tempTestDirBob,
1✔
3234
                                        Database:    bobDB,
1✔
3235
                                        ChainParams: *netParams,
1✔
3236
                                        ConnectPeers: []string{
1✔
3237
                                                miningNode.P2PAddress(),
1✔
3238
                                        },
1✔
3239
                                },
1✔
3240
                        )
1✔
3241
                        if err != nil {
1✔
3242
                                t.Fatalf("unable to make neutrino: %v", err)
×
3243
                        }
×
3244
                        bobChain.Start()
1✔
3245
                        defer bobChain.Stop()
1✔
3246
                        bobClient = chain.NewNeutrinoClient(
1✔
3247
                                netParams, bobChain,
1✔
3248
                        )
1✔
3249

3250
                case "bitcoind":
1✔
3251
                        // Start a bitcoind instance.
1✔
3252
                        chainConn := unittest.NewBitcoindBackend(
1✔
3253
                                t, unittest.NetParams, miningNode.P2PAddress(),
1✔
3254
                                true, false,
1✔
3255
                        )
1✔
3256

1✔
3257
                        // Create a btcwallet bitcoind client for both Alice and
1✔
3258
                        // Bob.
1✔
3259
                        aliceClient = chainConn.NewBitcoindClient()
1✔
3260
                        bobClient = chainConn.NewBitcoindClient()
1✔
3261

3262
                case "bitcoind-rpc-polling":
1✔
3263
                        // Start a bitcoind instance.
1✔
3264
                        chainConn := unittest.NewBitcoindBackend(
1✔
3265
                                t, unittest.NetParams, miningNode.P2PAddress(),
1✔
3266
                                true, true,
1✔
3267
                        )
1✔
3268

1✔
3269
                        // Create a btcwallet bitcoind client for both Alice and
1✔
3270
                        // Bob.
1✔
3271
                        aliceClient = chainConn.NewBitcoindClient()
1✔
3272
                        bobClient = chainConn.NewBitcoindClient()
1✔
3273

3274
                default:
×
3275
                        t.Fatalf("unknown chain driver: %v", backEnd)
×
3276
                }
3277

3278
                aliceSeed := sha256.New()
4✔
3279
                aliceSeed.Write([]byte(backEnd))
4✔
3280
                aliceSeed.Write(aliceHDSeed[:])
4✔
3281
                aliceSeedBytes := aliceSeed.Sum(nil)
4✔
3282

4✔
3283
                aliceWalletConfig := &btcwallet.Config{
4✔
3284
                        PrivatePass: []byte("alice-pass"),
4✔
3285
                        HdSeed:      aliceSeedBytes,
4✔
3286
                        NetParams:   netParams,
4✔
3287
                        ChainSource: aliceClient,
4✔
3288
                        CoinType:    keychain.CoinTypeTestnet,
4✔
3289
                        // wallet starts in recovery mode
4✔
3290
                        RecoveryWindow: 2,
4✔
3291
                        LoaderOptions: []btcwallet.LoaderOption{
4✔
3292
                                btcwallet.LoaderWithLocalWalletDB(
4✔
3293
                                        tempTestDirAlice, false, time.Minute,
4✔
3294
                                ),
4✔
3295
                        },
4✔
3296
                }
4✔
3297
                aliceWalletController, err = walletDriver.New(
4✔
3298
                        aliceWalletConfig, blockCache,
4✔
3299
                )
4✔
3300
                if err != nil {
4✔
3301
                        t.Fatalf("unable to create btcwallet: %v", err)
×
3302
                }
×
3303
                aliceSigner = aliceWalletController.(*btcwallet.BtcWallet)
4✔
3304
                aliceKeyRing = keychain.NewBtcWalletKeyRing(
4✔
3305
                        aliceWalletController.(*btcwallet.BtcWallet).InternalWallet(),
4✔
3306
                        keychain.CoinTypeTestnet,
4✔
3307
                )
4✔
3308

4✔
3309
                bobSeed := sha256.New()
4✔
3310
                bobSeed.Write([]byte(backEnd))
4✔
3311
                bobSeed.Write(bobHDSeed[:])
4✔
3312
                bobSeedBytes := bobSeed.Sum(nil)
4✔
3313

4✔
3314
                bobWalletConfig := &btcwallet.Config{
4✔
3315
                        PrivatePass: []byte("bob-pass"),
4✔
3316
                        HdSeed:      bobSeedBytes,
4✔
3317
                        NetParams:   netParams,
4✔
3318
                        ChainSource: bobClient,
4✔
3319
                        CoinType:    keychain.CoinTypeTestnet,
4✔
3320
                        // wallet starts without recovery mode
4✔
3321
                        RecoveryWindow: 0,
4✔
3322
                        LoaderOptions: []btcwallet.LoaderOption{
4✔
3323
                                btcwallet.LoaderWithLocalWalletDB(
4✔
3324
                                        tempTestDirBob, false, time.Minute,
4✔
3325
                                ),
4✔
3326
                        },
4✔
3327
                }
4✔
3328
                bobWalletController, err = walletDriver.New(
4✔
3329
                        bobWalletConfig, blockCache,
4✔
3330
                )
4✔
3331
                if err != nil {
4✔
3332
                        t.Fatalf("unable to create btcwallet: %v", err)
×
3333
                }
×
3334
                bobSigner = bobWalletController.(*btcwallet.BtcWallet)
4✔
3335
                bobKeyRing = keychain.NewBtcWalletKeyRing(
4✔
3336
                        bobWalletController.(*btcwallet.BtcWallet).InternalWallet(),
4✔
3337
                        keychain.CoinTypeTestnet,
4✔
3338
                )
4✔
3339
                bio = bobWalletController.(*btcwallet.BtcWallet)
4✔
3340
        default:
×
3341
                t.Fatalf("unknown wallet driver: %v", walletType)
×
3342
        }
3343

3344
        // Funding via 20 outputs with 4BTC each.
3345
        alice, err := createTestWallet(
4✔
3346
                tempTestDirAlice, miningNode, netParams,
4✔
3347
                chainNotifier, aliceWalletController, aliceKeyRing,
4✔
3348
                aliceSigner, bio,
4✔
3349
        )
4✔
3350
        require.NoError(t, err, "unable to create test ln wallet")
4✔
3351
        defer alice.Shutdown()
4✔
3352

4✔
3353
        bob, err := createTestWallet(
4✔
3354
                tempTestDirBob, miningNode, netParams,
4✔
3355
                chainNotifier, bobWalletController, bobKeyRing, bobSigner, bio,
4✔
3356
        )
4✔
3357
        require.NoError(t, err, "unable to create test ln wallet")
4✔
3358
        defer bob.Shutdown()
4✔
3359

4✔
3360
        // Both wallets should now have 80BTC available for
4✔
3361
        // spending.
4✔
3362
        assertProperBalance(t, alice, 1, 80)
4✔
3363
        assertProperBalance(t, bob, 1, 80)
4✔
3364

4✔
3365
        // Execute every test, clearing possibly mutated
4✔
3366
        // wallet state after each step.
4✔
3367
        for _, walletTest := range walletTests {
88✔
3368

84✔
3369
                walletTest := walletTest
84✔
3370

84✔
3371
                testName := fmt.Sprintf("%v/%v:%v", walletType, backEnd,
84✔
3372
                        walletTest.name)
84✔
3373
                success := t.Run(testName, func(t *testing.T) {
168✔
3374
                        if backEnd == "neutrino" &&
84✔
3375
                                strings.Contains(walletTest.name, "dual funder") {
85✔
3376

1✔
3377
                                t.Skip("skipping dual funder tests for neutrino")
1✔
3378
                        }
1✔
3379
                        if backEnd == "neutrino" &&
83✔
3380
                                strings.Contains(walletTest.name, "spend unconfirmed") {
84✔
3381

1✔
3382
                                t.Skip("skipping spend unconfirmed tests for neutrino")
1✔
3383
                        }
1✔
3384

3385
                        walletTest.test(miningNode, alice, bob, t)
82✔
3386
                })
3387
                if !success {
84✔
3388
                        return false
×
3389
                }
×
3390

3391
                // TODO(roasbeef): possible reset mining
3392
                // node's chainstate to initial level, cleanly
3393
                // wipe buckets
3394
                if err := clearWalletStates(alice, bob); err !=
84✔
3395
                        nil && err != kvdb.ErrBucketNotFound {
84✔
3396

×
3397
                        t.Fatalf("unable to wipe wallet state: %v", err)
×
3398
                }
×
3399
        }
3400

3401
        return true
4✔
3402
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc