github.com/decred/dcrlnd@v0.7.6/lnwallet/test/test_interface.go (about) 1 package lnwallettest 2 3 import ( 4 "bytes" 5 "context" 6 "crypto/sha256" 7 "encoding/hex" 8 "fmt" 9 "io/ioutil" 10 "math" 11 "net" 12 "os" 13 "path" 14 "path/filepath" 15 "reflect" 16 "runtime" 17 "strings" 18 "testing" 19 "time" 20 21 "github.com/davecgh/go-spew/spew" 22 "github.com/decred/dcrlnd/blockcache" 23 "github.com/decred/dcrlnd/chainntnfs/dcrdnotify" 24 "golang.org/x/exp/slices" 25 "matheusd.com/testctx" 26 27 _ "decred.org/dcrwallet/v4/wallet/drivers/bdb" 28 29 "github.com/decred/dcrd/chaincfg/chainhash" 30 "github.com/decred/dcrd/chaincfg/v3" 31 "github.com/decred/dcrd/dcrec/secp256k1/v4" 32 "github.com/decred/dcrd/dcrjson/v4" 33 "github.com/decred/dcrd/dcrutil/v4" 34 "github.com/decred/dcrd/rpcclient/v8" 35 "github.com/decred/dcrd/txscript/v4" 36 "github.com/decred/dcrd/txscript/v4/stdaddr" 37 "github.com/decred/dcrd/txscript/v4/stdscript" 38 "github.com/decred/dcrd/wire" 39 rpctest "github.com/decred/dcrtest/dcrdtest" 40 "github.com/stretchr/testify/require" 41 42 "github.com/decred/dcrlnd/chainntnfs" 43 "github.com/decred/dcrlnd/channeldb" 44 "github.com/decred/dcrlnd/input" 45 "github.com/decred/dcrlnd/internal/testutils" 46 "github.com/decred/dcrlnd/keychain" 47 "github.com/decred/dcrlnd/kvdb" 48 "github.com/decred/dcrlnd/labels" 49 "github.com/decred/dcrlnd/lnwallet" 50 "github.com/decred/dcrlnd/lnwallet/chainfee" 51 "github.com/decred/dcrlnd/lnwallet/chanfunding" 52 "github.com/decred/dcrlnd/lnwallet/dcrwallet" 53 "github.com/decred/dcrlnd/lnwallet/remotedcrwallet" 54 "github.com/decred/dcrlnd/lnwire" 55 ) 56 57 var ( 58 bobsPrivKey = []byte{ 59 0x81, 0xb6, 0x37, 0xd8, 0xfc, 0xd2, 0xc6, 0xda, 60 0x63, 0x59, 0xe6, 0x96, 0x31, 0x13, 0xa1, 0x17, 61 0xd, 0xe7, 0x95, 0xe4, 0xb7, 0x25, 0xb8, 0x4d, 62 0x1e, 0xb, 0x4c, 0xfd, 0x9e, 0xc5, 0x8c, 0xe9, 63 } 64 65 // Use a hard-coded HD seed. 66 testHdSeed = 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, 0x88, 0xa3, 0xef, 0xb9, 70 0x6a, 0x49, 0x18, 0x83, 0x31, 0x98, 0x47, 0x53, 71 } 72 73 aliceHDSeed = chainhash.Hash{ 74 0xb7, 0x94, 0x38, 0x5f, 0x2d, 0x1e, 0xf7, 0xab, 75 0x4d, 0x92, 0x73, 0xd1, 0x90, 0x63, 0x81, 0xb4, 76 0x4f, 0x2f, 0x6f, 0x25, 0x18, 0xa3, 0xef, 0xb9, 77 0x64, 0x49, 0x18, 0x83, 0x31, 0x98, 0x47, 0x53, 78 } 79 bobHDSeed = chainhash.Hash{ 80 0xb7, 0x94, 0x38, 0x5f, 0x2d, 0x1e, 0xf7, 0xab, 81 0x4d, 0x92, 0x73, 0xd1, 0x90, 0x63, 0x81, 0xb4, 82 0x4f, 0x2f, 0x6f, 0x25, 0x98, 0xa3, 0xef, 0xb9, 83 0x69, 0x49, 0x18, 0x83, 0x31, 0x98, 0x47, 0x53, 84 } 85 86 netParams = chaincfg.SimNetParams() 87 chainHash = &netParams.GenesisHash 88 89 alicePub = secp256k1.PrivKeyFromBytes(testHdSeed[:]).PubKey() 90 bobPub = secp256k1.PrivKeyFromBytes(bobsPrivKey).PubKey() 91 92 // The number of confirmations required to consider any created channel 93 // open. 94 numReqConfs uint16 = 1 95 96 csvDelay uint16 = 4 97 98 scriptVersion uint16 = 0 99 100 bobAddr, _ = net.ResolveTCPAddr("tcp", "10.0.0.2:9000") 101 aliceAddr, _ = net.ResolveTCPAddr("tcp", "10.0.0.3:9000") 102 103 defaultFeeRate = chainfee.AtomPerKByte(1e4) 104 defaultMaxLocalCsvDelay uint16 = 10000 105 ) 106 107 // assertProperBalance asserts than the total value of the unspent outputs 108 // within the wallet are *exactly* amount. If unable to retrieve the current 109 // balance, or the assertion fails, the test will halt with a fatal error. 110 func assertProperBalance(t *testing.T, lw *lnwallet.LightningWallet, 111 numConfirms int32, amount float64) { 112 113 balance, err := lw.ConfirmedBalance(numConfirms, lnwallet.DefaultAccountName) 114 if err != nil { 115 t.Fatalf("unable to query for balance: %v", err) 116 } 117 if balance.ToCoin() != amount { 118 t.Fatalf("wallet credits not properly loaded, should have %v DCR, "+ 119 "instead have %v", amount, balance) 120 } 121 } 122 123 func assertReservationDeleted(res *lnwallet.ChannelReservation, t *testing.T) { 124 if err := res.Cancel(); err == nil { 125 t.Fatalf("reservation wasn't deleted from wallet") 126 } 127 } 128 129 // mineAndAssertTxInBlock asserts that a transaction is included within the next 130 // block mined. 131 func mineAndAssertTxInBlock(t *testing.T, miner *rpctest.Harness, 132 vw *rpctest.VotingWallet, txid chainhash.Hash) { 133 134 t.Helper() 135 136 // First, we'll wait for the transaction to arrive in the mempool. 137 if err := waitForMempoolTx(miner, &txid); err != nil { 138 t.Fatalf("unable to find %v in the mempool: %v", txid, err) 139 } 140 141 // We'll mined a block to confirm it. 142 blockHashes, err := vw.GenerateBlocks(context.TODO(), 1) 143 if err != nil { 144 t.Fatalf("unable to generate new block: %v", err) 145 } 146 147 // Finally, we'll check it was actually mined in this block. 148 block, err := miner.Node.GetBlock(context.TODO(), blockHashes[0]) 149 if err != nil { 150 t.Fatalf("unable to get block %v: %v", blockHashes[0], err) 151 } 152 if len(block.Transactions) != 2 { 153 t.Fatalf("expected 2 transactions in block, found %d", 154 len(block.Transactions)) 155 } 156 txHash := block.Transactions[1].TxHash() 157 if txHash != txid { 158 t.Fatalf("expected transaction %v to be mined, found %v", txid, 159 txHash) 160 } 161 } 162 163 // newPkScript generates a new public key script of the given address type. 164 func newPkScript(t *testing.T, w *lnwallet.LightningWallet, 165 addrType lnwallet.AddressType) []byte { 166 167 t.Helper() 168 169 addr, err := w.NewAddress(addrType, false, lnwallet.DefaultAccountName) 170 if err != nil { 171 t.Fatalf("unable to create new address: %v", err) 172 } 173 pkScript, err := input.PayToAddrScript(addr) 174 if err != nil { 175 t.Fatalf("unable to create output script: %v", err) 176 } 177 178 // Wait for the wallet's address manager to close (see dcrwallet#1372). 179 time.Sleep(time.Millisecond * 50) 180 181 return pkScript 182 } 183 184 // sendCoins is a helper function that encompasses all the things needed for two 185 // parties to send on-chain funds to each other. 186 func sendCoins(t *testing.T, miner *rpctest.Harness, vw *rpctest.VotingWallet, 187 sender, receiver *lnwallet.LightningWallet, output *wire.TxOut, 188 feeRate chainfee.AtomPerKByte, mineBlock bool, minConf int32) *wire.MsgTx { //nolint:unparam 189 190 t.Helper() 191 192 tx, err := sender.SendOutputs( 193 []*wire.TxOut{output}, feeRate, minConf, labels.External, "", 194 ) 195 if err != nil { 196 t.Fatalf("unable to send transaction: %v", err) 197 } 198 199 if mineBlock { 200 mineAndAssertTxInBlock(t, miner, vw, tx.TxHash()) 201 } 202 203 if err := waitForWalletSync(miner, sender); err != nil { 204 t.Fatalf("unable to sync alice: %v", err) 205 } 206 if err := waitForWalletSync(miner, receiver); err != nil { 207 t.Fatalf("unable to sync bob: %v", err) 208 } 209 210 return tx 211 } 212 213 // assertTxInWallet asserts that a transaction exists in the wallet with the 214 // expected confirmation status. 215 func assertTxInWallet(t *testing.T, w *lnwallet.LightningWallet, 216 txHash chainhash.Hash, confirmed bool) { 217 218 t.Helper() 219 220 // We'll fetch all of our transaction and go through each one until 221 // finding the expected transaction with its expected confirmation 222 // status. 223 txs, err := w.ListTransactionDetails(0, dcrwallet.UnconfirmedHeight, "") 224 if err != nil { 225 t.Fatalf("unable to retrieve transactions: %v", err) 226 } 227 for _, tx := range txs { 228 if tx.Hash != txHash { 229 continue 230 } 231 if tx.NumConfirmations <= 0 && confirmed { 232 t.Fatalf("expected transaction %v to be confirmed", 233 txHash) 234 } 235 if tx.NumConfirmations > 0 && !confirmed { 236 t.Fatalf("expected transaction %v to be unconfirmed", 237 txHash) 238 } 239 240 // We've found the transaction and it matches the desired 241 // confirmation status, so we can exit. 242 return 243 } 244 245 t.Fatalf("transaction %v not found", txHash) 246 } 247 248 func loadTestCredits(miner *rpctest.Harness, w *lnwallet.LightningWallet, 249 blockGenerator func(context.Context, uint32) ([]*chainhash.Hash, error), 250 numOutputs int, dcrPerOutput float64) error { 251 252 // Using the mining node, spend from a coinbase output numOutputs to 253 // give us dcrPerOutput with each output. 254 atomsPerOutput, err := dcrutil.NewAmount(dcrPerOutput) 255 if err != nil { 256 return fmt.Errorf("unable to create amt: %v", err) 257 } 258 expectedBalance, err := w.ConfirmedBalance(1, lnwallet.DefaultAccountName) 259 if err != nil { 260 return err 261 } 262 expectedBalance += dcrutil.Amount(int64(atomsPerOutput) * int64(numOutputs)) 263 addrs := make([]stdaddr.Address, numOutputs) 264 for i := 0; i < numOutputs; i++ { 265 // Grab a fresh address from the wallet to house this output. 266 addrs[i], err = w.NewAddress( 267 lnwallet.PubKeyHash, false, 268 lnwallet.DefaultAccountName, 269 ) 270 if err != nil { 271 return err 272 } 273 } 274 275 // Sleep for a bit to allow the wallet to unlock the mutexes of the address 276 // manager and underlying database. This is needed to prevent a possible 277 // deadlock condition in the wallet when generating new addresses while 278 // processing a transaction (see 279 // https://github.com/dcrwallet/issues/1372). This isn't pretty, 280 // but 200ms should be more than enough to prevent triggering this bug 281 // on most dev machines. 282 time.Sleep(time.Millisecond * 200) 283 ctxb := context.Background() 284 285 for _, walletAddr := range addrs { 286 script, err := input.PayToAddrScript(walletAddr) 287 if err != nil { 288 return err 289 } 290 291 output := &wire.TxOut{ 292 Value: int64(atomsPerOutput), 293 PkScript: script, 294 Version: scriptVersion, 295 } 296 if _, err := miner.SendOutputs(ctxb, []*wire.TxOut{output}, 1e5); err != nil { 297 return err 298 } 299 } 300 301 // TODO(roasbeef): shouldn't hardcode 10, use config param that dictates 302 // how many confs we wait before opening a channel. 303 // Generate 10 blocks with the mining node, this should mine all 304 // numOutputs transactions created above. We generate 10 blocks here 305 // in order to give all the outputs a "sufficient" number of confirmations. 306 if _, err := blockGenerator(context.TODO(), 10); err != nil { 307 return err 308 } 309 310 time.Sleep(time.Millisecond * 200) 311 312 // Wait until the wallet has finished syncing up to the main chain. 313 ticker := time.NewTicker(100 * time.Millisecond) 314 timeout := time.After(30 * time.Second) 315 316 for range ticker.C { 317 balance, err := w.ConfirmedBalance(1, lnwallet.DefaultAccountName) 318 if err != nil { 319 return err 320 } 321 if balance == expectedBalance { 322 break 323 } 324 select { 325 case <-timeout: 326 synced, _, err := w.IsSynced() 327 if err != nil { 328 return err 329 } 330 return fmt.Errorf("timed out after 30 seconds "+ 331 "waiting for balance %v, current balance %v, "+ 332 "synced: %t", expectedBalance, balance, synced) 333 default: 334 } 335 } 336 ticker.Stop() 337 338 return nil 339 } 340 341 // createTestWallet creates a test LightningWallet will a total of 20DCR 342 // available for funding channels. 343 func createTestWallet(fullDb *channeldb.DB, miningNode *rpctest.Harness, 344 netParams *chaincfg.Params, notifier chainntnfs.ChainNotifier, 345 wc lnwallet.WalletController, keyRing keychain.SecretKeyRing, 346 signer input.Signer, bio lnwallet.BlockChainIO, 347 vw *rpctest.VotingWallet) (*lnwallet.LightningWallet, error) { 348 349 cfg := lnwallet.Config{ 350 Database: fullDb.ChannelStateDB(), 351 Notifier: notifier, 352 SecretKeyRing: keyRing, 353 WalletController: wc, 354 Signer: signer, 355 ChainIO: bio, 356 FeeEstimator: chainfee.NewStaticEstimator(defaultFeeRate, 0), 357 DefaultConstraints: channeldb.ChannelConstraints{ 358 DustLimit: 6030, 359 MaxPendingAmount: lnwire.NewMAtomsFromAtoms(dcrutil.AtomsPerCoin) * 100, 360 ChanReserve: 100, 361 MinHTLC: 400, 362 MaxAcceptedHtlcs: 900, 363 }, 364 NetParams: *netParams, 365 } 366 367 wallet, err := lnwallet.NewLightningWallet(cfg) 368 if err != nil { 369 return nil, err 370 } 371 372 if err := wallet.Startup(); err != nil { 373 return nil, err 374 } 375 376 return wallet, nil 377 } 378 379 func testGetRecoveryInfo(miner *rpctest.Harness, // nolint: unused 380 vw *rpctest.VotingWallet, alice, bob *lnwallet.LightningWallet, 381 t *testing.T) { 382 383 // alice's wallet is in recovery mode 384 expectedRecoveryMode := true 385 expectedProgress := float64(1) 386 387 isRecoveryMode, progress, err := alice.GetRecoveryInfo() 388 require.NoError(t, err, "unable to get alice's recovery info") 389 390 require.Equal(t, 391 expectedRecoveryMode, isRecoveryMode, "recovery mode incorrect", 392 ) 393 require.Equal(t, expectedProgress, progress, "progress incorrect") 394 395 // Generate 5 blocks and check the recovery process again. 396 const numBlocksMined = 5 397 _, err = vw.GenerateBlocks(context.TODO(), numBlocksMined) 398 require.NoError(t, err, "unable to mine blocks") 399 400 // Check the recovery process. Once synced, the progress should be 1. 401 err = waitForWalletSync(miner, alice) 402 require.NoError(t, err, "Couldn't sync Alice's wallet") 403 404 isRecoveryMode, progress, err = alice.GetRecoveryInfo() 405 require.NoError(t, err, "unable to get alice's recovery info") 406 407 require.Equal(t, 408 expectedRecoveryMode, isRecoveryMode, "recovery mode incorrect", 409 ) 410 require.Equal(t, expectedProgress, progress, "progress incorrect") 411 412 // bob's wallet is not in recovery mode 413 expectedRecoveryMode = false 414 expectedProgress = float64(0) 415 416 isRecoveryMode, progress, err = bob.GetRecoveryInfo() 417 require.NoError(t, err, "unable to get bob's recovery info") 418 419 require.Equal(t, 420 expectedRecoveryMode, isRecoveryMode, "recovery mode incorrect", 421 ) 422 require.Equal(t, expectedProgress, progress, "progress incorrect") 423 } 424 425 func testDualFundingReservationWorkflow(miner *rpctest.Harness, 426 vw *rpctest.VotingWallet, alice, bob *lnwallet.LightningWallet, 427 t *testing.T) { 428 429 fundingAmount, err := dcrutil.NewAmount(5) 430 if err != nil { 431 t.Fatalf("unable to create amt: %v", err) 432 } 433 434 // In this scenario, we'll test a dual funder reservation, with each 435 // side putting in 10 DCR. 436 437 // Alice initiates a channel funded with 5 DCR for each side, so 10 DCR 438 // total. She also generates 2 DCR in change. 439 feePerKB, err := alice.Cfg.FeeEstimator.EstimateFeePerKB(1) 440 if err != nil { 441 t.Fatalf("unable to query fee estimator: %v", err) 442 } 443 444 aliceReq := &lnwallet.InitFundingReserveMsg{ 445 ChainHash: chainHash, 446 NodeID: bobPub, 447 NodeAddr: bobAddr, 448 LocalFundingAmt: fundingAmount, 449 RemoteFundingAmt: fundingAmount, 450 CommitFeePerKB: feePerKB, 451 FundingFeePerKB: feePerKB, 452 PushMAtoms: 0, 453 Flags: lnwire.FFAnnounceChannel, 454 } 455 aliceChanReservation, err := alice.InitChannelReservation(aliceReq) 456 if err != nil { 457 t.Fatalf("unable to initialize funding reservation: %v", err) 458 } 459 aliceChanReservation.SetNumConfsRequired(numReqConfs) 460 channelConstraints := &channeldb.ChannelConstraints{ 461 DustLimit: alice.Cfg.DefaultConstraints.DustLimit, 462 ChanReserve: fundingAmount / 100, 463 MaxPendingAmount: lnwire.NewMAtomsFromAtoms(fundingAmount), 464 MinHTLC: 1, 465 MaxAcceptedHtlcs: input.MaxHTLCNumber / 2, 466 CsvDelay: csvDelay, 467 } 468 err = aliceChanReservation.CommitConstraints( 469 channelConstraints, defaultMaxLocalCsvDelay, false, 470 ) 471 if err != nil { 472 t.Fatalf("unable to verify constraints: %v", err) 473 } 474 475 // The channel reservation should now be populated with a multi-sig key 476 // from our HD chain, a change output with 3 DCR, and 2 outputs 477 // selected of 4 DCR each. Additionally, the rest of the items needed 478 // to fulfill a funding contribution should also have been filled in. 479 aliceContribution := aliceChanReservation.OurContribution() 480 if len(aliceContribution.Inputs) < 1 { 481 t.Fatalf("outputs for funding tx not properly selected, have %v "+ 482 "outputs should at least 1", len(aliceContribution.Inputs)) 483 } 484 if len(aliceContribution.ChangeOutputs) != 1 { 485 t.Fatalf("coin selection failed, should have one change outputs, "+ 486 "instead have: %v", len(aliceContribution.ChangeOutputs)) 487 } 488 assertContributionInitPopulated(t, aliceContribution) 489 490 // Bob does the same, generating his own contribution. He then also 491 // receives' Alice's contribution, and consumes that so we can continue 492 // the funding process. 493 bobReq := &lnwallet.InitFundingReserveMsg{ 494 ChainHash: chainHash, 495 NodeID: alicePub, 496 NodeAddr: aliceAddr, 497 LocalFundingAmt: fundingAmount, 498 RemoteFundingAmt: fundingAmount, 499 CommitFeePerKB: feePerKB, 500 FundingFeePerKB: feePerKB, 501 PushMAtoms: 0, 502 Flags: lnwire.FFAnnounceChannel, 503 } 504 bobChanReservation, err := bob.InitChannelReservation(bobReq) 505 if err != nil { 506 t.Fatalf("bob unable to init channel reservation: %v", err) 507 } 508 err = bobChanReservation.CommitConstraints( 509 channelConstraints, defaultMaxLocalCsvDelay, true, 510 ) 511 if err != nil { 512 t.Fatalf("unable to verify constraints: %v", err) 513 } 514 bobChanReservation.SetNumConfsRequired(numReqConfs) 515 516 assertContributionInitPopulated(t, bobChanReservation.OurContribution()) 517 518 err = bobChanReservation.ProcessContribution(aliceContribution) 519 if err != nil { 520 t.Fatalf("bob unable to process alice's contribution: %v", err) 521 } 522 assertContributionInitPopulated(t, bobChanReservation.TheirContribution()) 523 524 bobContribution := bobChanReservation.OurContribution() 525 526 // Bob then sends over his contribution, which will be consumed by 527 // Alice. After this phase, Alice should have all the necessary 528 // material required to craft the funding transaction and commitment 529 // transactions. 530 err = aliceChanReservation.ProcessContribution(bobContribution) 531 if err != nil { 532 t.Fatalf("alice unable to process bob's contribution: %v", err) 533 } 534 assertContributionInitPopulated(t, aliceChanReservation.TheirContribution()) 535 536 // At this point, all Alice's signatures should be fully populated. 537 aliceFundingSigs, aliceCommitSig := aliceChanReservation.OurSignatures() 538 if aliceFundingSigs == nil { 539 t.Fatalf("alice's funding signatures not populated") 540 } 541 if aliceCommitSig == nil { 542 t.Fatalf("alice's commit signatures not populated") 543 } 544 545 // Additionally, Bob's signatures should also be fully populated. 546 bobFundingSigs, bobCommitSig := bobChanReservation.OurSignatures() 547 if bobFundingSigs == nil { 548 t.Fatalf("bob's funding signatures not populated") 549 } 550 if bobCommitSig == nil { 551 t.Fatalf("bob's commit signatures not populated") 552 } 553 554 // To conclude, we'll consume first Alice's signatures with Bob, and 555 // then the other way around. 556 _, err = aliceChanReservation.CompleteReservation( 557 bobFundingSigs, bobCommitSig, 558 ) 559 if err != nil { 560 for _, in := range aliceChanReservation.FinalFundingTx().TxIn { 561 fmt.Println(in.PreviousOutPoint.String()) 562 } 563 t.Fatalf("unable to consume alice's sigs: %v", err) 564 } 565 _, err = bobChanReservation.CompleteReservation( 566 aliceFundingSigs, aliceCommitSig, 567 ) 568 if err != nil { 569 t.Fatalf("unable to consume bob's sigs: %v", err) 570 } 571 572 // At this point, the funding tx should have been populated. 573 fundingTx := aliceChanReservation.FinalFundingTx() 574 if fundingTx == nil { 575 t.Fatalf("funding transaction never created!") 576 } 577 578 // The resulting active channel state should have been persisted to the 579 // DB. 580 fundingSha := fundingTx.TxHash() 581 aliceChannels, err := alice.Cfg.Database.FetchOpenChannels(bobPub) 582 if err != nil { 583 t.Fatalf("unable to retrieve channel from DB: %v", err) 584 } 585 if !bytes.Equal(aliceChannels[0].FundingOutpoint.Hash[:], fundingSha[:]) { 586 t.Fatalf("channel state not properly saved") 587 } 588 if !aliceChannels[0].ChanType.IsDualFunder() { 589 t.Fatalf("channel not detected as dual funder") 590 } 591 bobChannels, err := bob.Cfg.Database.FetchOpenChannels(alicePub) 592 if err != nil { 593 t.Fatalf("unable to retrieve channel from DB: %v", err) 594 } 595 if !bytes.Equal(bobChannels[0].FundingOutpoint.Hash[:], fundingSha[:]) { 596 t.Fatalf("channel state not properly saved") 597 } 598 if !bobChannels[0].ChanType.IsDualFunder() { 599 t.Fatalf("channel not detected as dual funder") 600 } 601 602 // Let Alice publish the funding transaction. 603 err = alice.PublishTransaction(fundingTx, "") 604 if err != nil { 605 t.Fatalf("unable to publish funding tx: %v", err) 606 } 607 608 // Mine a single block, the funding transaction should be included 609 // within this block. 610 err = waitForMempoolTx(miner, &fundingSha) 611 if err != nil { 612 t.Fatalf("tx not relayed to miner: %v", err) 613 } 614 blockHashes, err := vw.GenerateBlocks(context.TODO(), 1) 615 if err != nil { 616 t.Fatalf("unable to generate block: %v", err) 617 } 618 block, err := miner.Node.GetBlock(context.TODO(), blockHashes[0]) 619 if err != nil { 620 t.Fatalf("unable to find block: %v", err) 621 } 622 if len(block.Transactions) != 2 { 623 t.Fatalf("funding transaction wasn't mined: %v", err) 624 } 625 blockTx := block.Transactions[1] 626 if blockTx.TxHash() != fundingSha { 627 t.Fatalf("incorrect transaction was mined") 628 } 629 630 assertReservationDeleted(aliceChanReservation, t) 631 assertReservationDeleted(bobChanReservation, t) 632 633 // Wait for wallets to catch up to prevent issues in subsequent tests. 634 err = waitForWalletSync(miner, alice) 635 if err != nil { 636 t.Fatalf("unable to sync alice: %v", err) 637 } 638 err = waitForWalletSync(miner, bob) 639 if err != nil { 640 t.Fatalf("unable to sync bob: %v", err) 641 } 642 } 643 644 func testFundingTransactionLockedOutputs(miner *rpctest.Harness, 645 vw *rpctest.VotingWallet, alice, _ *lnwallet.LightningWallet, 646 t *testing.T) { 647 648 // Create a single channel asking for 16 DCR total. 649 fundingAmount := dcrutil.Amount(8 * 1e8) 650 651 feePerKB, err := alice.Cfg.FeeEstimator.EstimateFeePerKB(1) 652 if err != nil { 653 t.Fatalf("unable to query fee estimator: %v", err) 654 } 655 req := &lnwallet.InitFundingReserveMsg{ 656 ChainHash: chainHash, 657 NodeID: bobPub, 658 NodeAddr: bobAddr, 659 LocalFundingAmt: fundingAmount, 660 RemoteFundingAmt: 0, 661 CommitFeePerKB: feePerKB, 662 FundingFeePerKB: feePerKB, 663 PushMAtoms: 0, 664 Flags: lnwire.FFAnnounceChannel, 665 PendingChanID: [32]byte{0, 1, 2, 3}, 666 } 667 chanReservation, err := alice.InitChannelReservation(req) 668 if err != nil { 669 t.Fatalf("unable to initialize funding reservation 1: %v", err) 670 } 671 672 // Now attempt to reserve funds for another channel, this time 673 // requesting 900 DCR. We only have around 64DCR worth of outpoints 674 // that aren't locked, so this should fail. 675 amt, err := dcrutil.NewAmount(900) 676 if err != nil { 677 t.Fatalf("unable to create amt: %v", err) 678 } 679 failedReq := &lnwallet.InitFundingReserveMsg{ 680 ChainHash: chainHash, 681 NodeID: bobPub, 682 NodeAddr: bobAddr, 683 LocalFundingAmt: amt, 684 RemoteFundingAmt: 0, 685 CommitFeePerKB: feePerKB, 686 FundingFeePerKB: feePerKB, 687 PushMAtoms: 0, 688 Flags: lnwire.FFAnnounceChannel, 689 PendingChanID: [32]byte{1, 2, 3, 4}, 690 } 691 failedReservation, err := alice.InitChannelReservation(failedReq) 692 if err == nil { 693 t.Fatalf("not error returned, should fail on coin selection") 694 } 695 if _, ok := err.(*chanfunding.ErrInsufficientFunds); !ok { 696 t.Fatalf("error not coinselect error: %v", err) 697 } 698 if failedReservation != nil { 699 t.Fatalf("reservation should be nil") 700 } 701 702 // Cancel the older reservation so it won't affect other tests. 703 if err := chanReservation.Cancel(); err != nil { 704 t.Fatalf("unable to cancel reservation: %v", err) 705 } 706 } 707 708 func testFundingCancellationNotEnoughFunds(miner *rpctest.Harness, 709 vw *rpctest.VotingWallet, alice, _ *lnwallet.LightningWallet, 710 t *testing.T) { 711 712 feePerKB, err := alice.Cfg.FeeEstimator.EstimateFeePerKB(1) 713 if err != nil { 714 t.Fatalf("unable to query fee estimator: %v", err) 715 } 716 717 totalBalance, err := alice.ConfirmedBalance(0, "") 718 if err != nil { 719 t.Fatalf("unable to fetch confirmed balance: %v", err) 720 } 721 722 // Create a reservation for almost the entire wallet amount. 723 fundingAmount := totalBalance - dcrutil.AtomsPerCoin 724 req := &lnwallet.InitFundingReserveMsg{ 725 ChainHash: chainHash, 726 NodeID: bobPub, 727 NodeAddr: bobAddr, 728 LocalFundingAmt: fundingAmount, 729 RemoteFundingAmt: 0, 730 CommitFeePerKB: feePerKB, 731 FundingFeePerKB: feePerKB, 732 PushMAtoms: 0, 733 Flags: lnwire.FFAnnounceChannel, 734 PendingChanID: [32]byte{2, 3, 4, 5}, 735 } 736 chanReservation, err := alice.InitChannelReservation(req) 737 if err != nil { 738 t.Fatalf("unable to initialize funding reservation: %v", err) 739 } 740 741 // Attempt to create another channel spending the same amount. This 742 // should fail. 743 req.PendingChanID = [32]byte{3, 4, 5, 6} 744 _, err = alice.InitChannelReservation(req) 745 if _, ok := err.(*chanfunding.ErrInsufficientFunds); !ok { 746 t.Fatalf("coin selection succeeded should have insufficient funds: %v", 747 err) 748 } 749 750 // Now cancel that old reservation. 751 if err := chanReservation.Cancel(); err != nil { 752 t.Fatalf("unable to cancel reservation: %v", err) 753 } 754 755 // Those outpoints should no longer be locked. 756 lockedOutPoints := alice.LockedOutpoints() 757 if len(lockedOutPoints) != 0 { 758 t.Fatalf("outpoints still locked") 759 } 760 761 // Reservation ID should no longer be tracked. 762 numReservations := alice.ActiveReservations() 763 if len(alice.ActiveReservations()) != 0 { 764 t.Fatalf("should have 0 reservations, instead have %v", 765 numReservations) 766 } 767 768 // TODO(roasbeef): create method like Balance that ignores locked 769 // outpoints, will let us fail early/fast instead of querying and 770 // attempting coin selection. 771 772 // Request to fund a new channel should now succeed. 773 req.PendingChanID = [32]byte{4, 5, 6, 7, 8} 774 if _, err := alice.InitChannelReservation(req); err != nil { 775 t.Fatalf("unable to initialize funding reservation: %v", err) 776 } 777 } 778 779 func testCancelNonExistentReservation(miner *rpctest.Harness, 780 vw *rpctest.VotingWallet, alice, _ *lnwallet.LightningWallet, 781 t *testing.T) { 782 783 feePerKB, err := alice.Cfg.FeeEstimator.EstimateFeePerKB(1) 784 if err != nil { 785 t.Fatalf("unable to query fee estimator: %v", err) 786 } 787 788 // Create our own reservation, give it some ID. 789 res, err := lnwallet.NewChannelReservation( 790 20000, 20000, feePerKB, alice, 22, 10, &testHdSeed, 791 lnwire.FFAnnounceChannel, lnwallet.CommitmentTypeTweakless, 792 nil, [32]byte{}, 0, 793 ) 794 if err != nil { 795 t.Fatalf("unable to create res: %v", err) 796 } 797 798 // Attempt to cancel this reservation. This should fail, we know 799 // nothing of it. 800 if err := res.Cancel(); err == nil { 801 t.Fatalf("canceled non-existent reservation") 802 } 803 } 804 805 func testReservationInitiatorBalanceBelowDustCancel(miner *rpctest.Harness, 806 vw *rpctest.VotingWallet, alice, _ *lnwallet.LightningWallet, 807 t *testing.T) { 808 809 // We'll attempt to create a new reservation with an extremely high 810 // commitment fee rate. This should push our balance into the negative 811 // and result in a failure to create the reservation. 812 const numDCR = 4 813 fundingAmount, err := dcrutil.NewAmount(numDCR) 814 if err != nil { 815 t.Fatalf("unable to create amt: %v", err) 816 } 817 818 FeePerKB := chainfee.AtomPerKByte( 819 numDCR * numDCR * dcrutil.AtomsPerCoin, 820 ) 821 req := &lnwallet.InitFundingReserveMsg{ 822 ChainHash: chainHash, 823 NodeID: bobPub, 824 NodeAddr: bobAddr, 825 LocalFundingAmt: fundingAmount, 826 RemoteFundingAmt: 0, 827 CommitFeePerKB: FeePerKB, 828 FundingFeePerKB: 1000, 829 PushMAtoms: 0, 830 Flags: lnwire.FFAnnounceChannel, 831 CommitType: lnwallet.CommitmentTypeTweakless, 832 PendingChanID: [32]byte{11, 12, 13, 14}, 833 } 834 _, err = alice.InitChannelReservation(req) 835 switch { 836 case err == nil: 837 t.Fatalf("initialization should have failed due to " + 838 "insufficient local amount") 839 840 case !strings.Contains(err.Error(), "funder balance too small"): 841 t.Fatalf("incorrect error: %v", err) 842 } 843 } 844 845 func assertContributionInitPopulated(t *testing.T, c *lnwallet.ChannelContribution) { 846 _, _, line, _ := runtime.Caller(1) 847 848 if c.FirstCommitmentPoint == nil { 849 t.Fatalf("line #%v: commitment point not fond", line) 850 } 851 852 if c.CsvDelay == 0 { 853 t.Fatalf("line #%v: csv delay not set", line) 854 } 855 856 if c.MultiSigKey.PubKey == nil { 857 t.Fatalf("line #%v: multi-sig key not set", line) 858 } 859 if c.RevocationBasePoint.PubKey == nil { 860 t.Fatalf("line #%v: revocation key not set", line) 861 } 862 if c.PaymentBasePoint.PubKey == nil { 863 t.Fatalf("line #%v: payment key not set", line) 864 } 865 if c.DelayBasePoint.PubKey == nil { 866 t.Fatalf("line #%v: delay key not set", line) 867 } 868 869 if c.DustLimit == 0 { 870 t.Fatalf("line #%v: dust limit not set", line) 871 } 872 if c.MaxPendingAmount == 0 { 873 t.Fatalf("line #%v: max pending amt not set", line) 874 } 875 if c.ChanReserve == 0 { 876 t.Fatalf("line #%v: chan reserve not set", line) 877 } 878 if c.MinHTLC == 0 { 879 t.Fatalf("line #%v: min htlc not set", line) 880 } 881 if c.MaxAcceptedHtlcs == 0 { 882 t.Fatalf("line #%v: max accepted htlc's not set", line) 883 } 884 } 885 886 func testSingleFunderReservationWorkflow(miner *rpctest.Harness, 887 vw *rpctest.VotingWallet, 888 alice, bob *lnwallet.LightningWallet, t *testing.T, 889 commitType lnwallet.CommitmentType, 890 aliceChanFunder chanfunding.Assembler, fetchFundingTx func() *wire.MsgTx, 891 pendingChanID [32]byte, thawHeight uint32) { 892 893 // For this scenario, Alice will be the channel initiator while bob 894 // will act as the responder to the workflow. 895 896 // First, Alice will Initialize a reservation for a channel with 4 DCR 897 // funded solely by us. We'll also initially push 1 DCR of the channel 898 // towards Bob's side. 899 fundingAmt, err := dcrutil.NewAmount(4) 900 if err != nil { 901 t.Fatalf("unable to create amt: %v", err) 902 } 903 pushAmt := lnwire.NewMAtomsFromAtoms(dcrutil.AtomsPerCoin) 904 feePerKB, err := alice.Cfg.FeeEstimator.EstimateFeePerKB(1) 905 if err != nil { 906 t.Fatalf("unable to query fee estimator: %v", err) 907 } 908 aliceReq := &lnwallet.InitFundingReserveMsg{ 909 ChainHash: chainHash, 910 PendingChanID: pendingChanID, 911 NodeID: bobPub, 912 NodeAddr: bobAddr, 913 LocalFundingAmt: fundingAmt, 914 RemoteFundingAmt: 0, 915 CommitFeePerKB: feePerKB, 916 FundingFeePerKB: feePerKB, 917 PushMAtoms: pushAmt, 918 Flags: lnwire.FFAnnounceChannel, 919 CommitType: commitType, 920 ChanFunder: aliceChanFunder, 921 } 922 aliceChanReservation, err := alice.InitChannelReservation(aliceReq) 923 if err != nil { 924 t.Fatalf("unable to init channel reservation: %v", err) 925 } 926 aliceChanReservation.SetNumConfsRequired(numReqConfs) 927 channelConstraints := &channeldb.ChannelConstraints{ 928 DustLimit: alice.Cfg.DefaultConstraints.DustLimit, 929 ChanReserve: fundingAmt / 100, 930 MaxPendingAmount: lnwire.NewMAtomsFromAtoms(fundingAmt), 931 MinHTLC: 1, 932 MaxAcceptedHtlcs: input.MaxHTLCNumber / 2, 933 CsvDelay: csvDelay, 934 } 935 err = aliceChanReservation.CommitConstraints( 936 channelConstraints, defaultMaxLocalCsvDelay, false, 937 ) 938 if err != nil { 939 t.Fatalf("unable to verify constraints: %v", err) 940 } 941 942 // Verify all contribution fields have been set properly, but only if 943 // Alice is the funder herself. 944 aliceContribution := aliceChanReservation.OurContribution() 945 if fetchFundingTx == nil { 946 if len(aliceContribution.Inputs) < 1 { 947 t.Fatalf("outputs for funding tx not properly "+ 948 "selected, have %v outputs should at least 1", 949 len(aliceContribution.Inputs)) 950 } 951 if len(aliceContribution.ChangeOutputs) != 1 { 952 t.Fatalf("coin selection failed, should have one "+ 953 "change outputs, instead have: %v", 954 len(aliceContribution.ChangeOutputs)) 955 } 956 } 957 assertContributionInitPopulated(t, aliceContribution) 958 959 // Next, Bob receives the initial request, generates a corresponding 960 // reservation initiation, then consume Alice's contribution. 961 bobReq := &lnwallet.InitFundingReserveMsg{ 962 ChainHash: chainHash, 963 PendingChanID: pendingChanID, 964 NodeID: alicePub, 965 NodeAddr: aliceAddr, 966 LocalFundingAmt: 0, 967 RemoteFundingAmt: fundingAmt, 968 CommitFeePerKB: feePerKB, 969 FundingFeePerKB: feePerKB, 970 PushMAtoms: pushAmt, 971 Flags: lnwire.FFAnnounceChannel, 972 CommitType: commitType, 973 } 974 bobChanReservation, err := bob.InitChannelReservation(bobReq) 975 if err != nil { 976 t.Fatalf("unable to create bob reservation: %v", err) 977 } 978 err = bobChanReservation.CommitConstraints( 979 channelConstraints, defaultMaxLocalCsvDelay, true, 980 ) 981 if err != nil { 982 t.Fatalf("unable to verify constraints: %v", err) 983 } 984 bobChanReservation.SetNumConfsRequired(numReqConfs) 985 986 // We'll ensure that Bob's contribution also gets generated properly. 987 bobContribution := bobChanReservation.OurContribution() 988 assertContributionInitPopulated(t, bobContribution) 989 990 // With his contribution generated, he can now process Alice's 991 // contribution. 992 err = bobChanReservation.ProcessSingleContribution(aliceContribution) 993 if err != nil { 994 t.Fatalf("bob unable to process alice's contribution: %v", err) 995 } 996 assertContributionInitPopulated(t, bobChanReservation.TheirContribution()) 997 998 // Bob will next send over his contribution to Alice, we simulate this 999 // by having Alice immediately process his contribution. 1000 err = aliceChanReservation.ProcessContribution(bobContribution) 1001 if err != nil { 1002 t.Fatalf("alice unable to process bob's contribution: %v", err) 1003 } 1004 assertContributionInitPopulated(t, bobChanReservation.TheirContribution()) 1005 1006 // At this point, Alice should have generated all the signatures 1007 // required for the funding transaction, as well as Alice's commitment 1008 // signature to bob, but only if the funding transaction was 1009 // constructed internally. 1010 aliceRemoteContribution := aliceChanReservation.TheirContribution() 1011 aliceFundingSigs, aliceCommitSig := aliceChanReservation.OurSignatures() 1012 if fetchFundingTx == nil && aliceFundingSigs == nil { 1013 t.Fatalf("funding sigs not found") 1014 } 1015 if aliceCommitSig == nil { 1016 t.Fatalf("commitment sig not found") 1017 } 1018 1019 // Additionally, the funding tx and the funding outpoint should have 1020 // been populated. 1021 if aliceChanReservation.FinalFundingTx() == nil && fetchFundingTx == nil { 1022 t.Fatalf("funding transaction never created!") 1023 } 1024 if aliceChanReservation.FundingOutpoint() == nil { 1025 t.Fatalf("funding outpoint never created!") 1026 } 1027 1028 // Their funds should also be filled in. 1029 if len(aliceRemoteContribution.Inputs) != 0 { 1030 t.Fatalf("bob shouldn't have any inputs, instead has %v", 1031 len(aliceRemoteContribution.Inputs)) 1032 } 1033 if len(aliceRemoteContribution.ChangeOutputs) != 0 { 1034 t.Fatalf("bob shouldn't have any change outputs, instead "+ 1035 "has %v", 1036 aliceRemoteContribution.ChangeOutputs[0].Value) 1037 } 1038 1039 // Next, Alice will send over her signature for Bob's commitment 1040 // transaction, as well as the funding outpoint. 1041 fundingPoint := aliceChanReservation.FundingOutpoint() 1042 _, err = bobChanReservation.CompleteReservationSingle( 1043 fundingPoint, aliceCommitSig, 1044 ) 1045 if err != nil { 1046 t.Fatalf("bob unable to consume single reservation: %v", err) 1047 } 1048 1049 // Finally, we'll conclude the reservation process by sending over 1050 // Bob's commitment signature, which is the final thing Alice needs to 1051 // be able to safely broadcast the funding transaction. 1052 _, bobCommitSig := bobChanReservation.OurSignatures() 1053 if bobCommitSig == nil { 1054 t.Fatalf("bob failed to generate commitment signature: %v", err) 1055 } 1056 _, err = aliceChanReservation.CompleteReservation( 1057 nil, bobCommitSig, 1058 ) 1059 if err != nil { 1060 t.Fatalf("alice unable to complete reservation: %v", err) 1061 } 1062 1063 // If the caller provided an alternative way to obtain the funding tx, 1064 // then we'll use that. Otherwise, we'll obtain it directly from Alice. 1065 var fundingTx *wire.MsgTx 1066 if fetchFundingTx != nil { 1067 fundingTx = fetchFundingTx() 1068 } else { 1069 fundingTx = aliceChanReservation.FinalFundingTx() 1070 } 1071 1072 // The resulting active channel state should have been persisted to the 1073 // DB for both Alice and Bob. 1074 fundingSha := fundingTx.TxHash() 1075 aliceChannels, err := alice.Cfg.Database.FetchOpenChannels(bobPub) 1076 if err != nil { 1077 t.Fatalf("unable to retrieve channel from DB: %v", err) 1078 } 1079 if len(aliceChannels) != 1 { 1080 t.Fatalf("alice didn't save channel state: %v", err) 1081 } 1082 if !bytes.Equal(aliceChannels[0].FundingOutpoint.Hash[:], fundingSha[:]) { 1083 t.Fatalf("channel state not properly saved: %v vs %v", 1084 hex.EncodeToString(aliceChannels[0].FundingOutpoint.Hash[:]), 1085 hex.EncodeToString(fundingSha[:])) 1086 } 1087 if !aliceChannels[0].IsInitiator { 1088 t.Fatalf("alice not detected as channel initiator") 1089 } 1090 if !aliceChannels[0].ChanType.IsSingleFunder() { 1091 t.Fatalf("channel type is incorrect, expected %v instead got %v", 1092 channeldb.SingleFunderBit, aliceChannels[0].ChanType) 1093 } 1094 1095 bobChannels, err := bob.Cfg.Database.FetchOpenChannels(alicePub) 1096 if err != nil { 1097 t.Fatalf("unable to retrieve channel from DB: %v", err) 1098 } 1099 if len(bobChannels) != 1 { 1100 t.Fatalf("bob didn't save channel state: %v", err) 1101 } 1102 if !bytes.Equal(bobChannels[0].FundingOutpoint.Hash[:], fundingSha[:]) { 1103 t.Fatalf("channel state not properly saved: %v vs %v", 1104 hex.EncodeToString(bobChannels[0].FundingOutpoint.Hash[:]), 1105 hex.EncodeToString(fundingSha[:])) 1106 } 1107 if bobChannels[0].IsInitiator { 1108 t.Fatalf("bob not detected as channel responder") 1109 } 1110 if !bobChannels[0].ChanType.IsSingleFunder() { 1111 t.Fatalf("channel type is incorrect, expected %v instead got %v", 1112 channeldb.SingleFunderBit, bobChannels[0].ChanType) 1113 } 1114 1115 // Let Alice publish the funding transaction. 1116 err = alice.PublishTransaction(fundingTx, "") 1117 if err != nil { 1118 t.Fatalf("unable to publish funding tx: %v", err) 1119 } 1120 1121 // Mine a single block, the funding transaction should be included 1122 // within this block. 1123 err = waitForMempoolTx(miner, &fundingSha) 1124 if err != nil { 1125 t.Fatalf("tx not relayed to miner: %v", err) 1126 } 1127 blockHashes, err := vw.GenerateBlocks(context.TODO(), 1) 1128 if err != nil { 1129 t.Fatalf("unable to generate block: %v", err) 1130 } 1131 block, err := miner.Node.GetBlock(context.TODO(), blockHashes[0]) 1132 if err != nil { 1133 t.Fatalf("unable to find block: %v", err) 1134 } 1135 if len(block.Transactions) != 2 { 1136 t.Fatalf("funding transaction wasn't mined: %d", 1137 len(block.Transactions)) 1138 } 1139 blockTx := block.Transactions[1] 1140 if blockTx.TxHash() != fundingSha { 1141 t.Fatalf("incorrect transaction was mined") 1142 } 1143 1144 // If a frozen channel was requested, then we expect that both channel 1145 // types show as being a frozen channel type. 1146 aliceChanFrozen := aliceChannels[0].ChanType.IsFrozen() 1147 bobChanFrozen := bobChannels[0].ChanType.IsFrozen() 1148 if thawHeight != 0 && (!aliceChanFrozen || !bobChanFrozen) { 1149 t.Fatalf("expected both alice and bob to have frozen chans: "+ 1150 "alice_frozen=%v, bob_frozen=%v", aliceChanFrozen, 1151 bobChanFrozen) 1152 } 1153 if thawHeight != bobChannels[0].ThawHeight { 1154 t.Fatalf("wrong thaw height: expected %v got %v", thawHeight, 1155 bobChannels[0].ThawHeight) 1156 } 1157 if thawHeight != aliceChannels[0].ThawHeight { 1158 t.Fatalf("wrong thaw height: expected %v got %v", thawHeight, 1159 aliceChannels[0].ThawHeight) 1160 } 1161 1162 assertReservationDeleted(aliceChanReservation, t) 1163 assertReservationDeleted(bobChanReservation, t) 1164 } 1165 1166 func testListTransactionDetails(miner *rpctest.Harness, 1167 vw *rpctest.VotingWallet, alice, _ *lnwallet.LightningWallet, 1168 t *testing.T) { 1169 1170 // Create 5 new outputs spendable by the wallet. 1171 const numTxns = 5 1172 const outputAmt = dcrutil.AtomsPerCoin 1173 var err error 1174 addrs := make([]stdaddr.Address, numTxns) 1175 for i := 0; i < numTxns; i++ { 1176 addrs[i], err = alice.NewAddress( 1177 lnwallet.PubKeyHash, false, 1178 lnwallet.DefaultAccountName, 1179 ) 1180 if err != nil { 1181 t.Fatalf("unable to create new address: %v", err) 1182 } 1183 } 1184 1185 // Let the wallet close the address manager (see issue dcrwallet#1372). 1186 time.Sleep(time.Millisecond * 50) 1187 ctxb := context.Background() 1188 1189 txids := make(map[chainhash.Hash]struct{}) 1190 for i := 0; i < numTxns; i++ { 1191 script, err := input.PayToAddrScript(addrs[i]) 1192 if err != nil { 1193 t.Fatalf("unable to create output script: %v", err) 1194 } 1195 1196 output := &wire.TxOut{ 1197 Value: outputAmt, 1198 PkScript: script, 1199 } 1200 txid, err := miner.SendOutputs(ctxb, []*wire.TxOut{output}, 1201 dcrutil.Amount(defaultFeeRate)) 1202 if err != nil { 1203 t.Fatalf("unable to send coinbase: %v", err) 1204 } 1205 txids[*txid] = struct{}{} 1206 err = waitForMempoolTx(miner, txid) 1207 if err != nil { 1208 t.Fatalf("unable to detect mempool tx: %v", err) 1209 } 1210 } 1211 1212 // Get the miner's current best block height before we mine blocks. 1213 _, startHeight, err := miner.Node.GetBestBlock(context.TODO()) 1214 if err != nil { 1215 t.Fatalf("cannot get best block: %v", err) 1216 } 1217 1218 // Generate 10 blocks to mine all the transactions created above. 1219 const numBlocksMined = 10 1220 blocks, err := vw.GenerateBlocks(context.TODO(), numBlocksMined) 1221 if err != nil { 1222 t.Fatalf("unable to mine blocks: %v", err) 1223 } 1224 1225 // Our new best block height should be our start height + the number of 1226 // blocks we just mined. 1227 chainTip := startHeight + numBlocksMined 1228 1229 // Next, fetch all the current transaction details. We should find all 1230 // of our transactions between our start height before we generated 1231 // blocks, and our end height, which is the chain tip. This query does 1232 // not include unconfirmed transactions, since all of our transactions 1233 // should be confirmed. 1234 err = waitForWalletSync(miner, alice) 1235 if err != nil { 1236 t.Fatalf("Couldn't sync Alice's wallet: %v", err) 1237 } 1238 txDetails, err := alice.ListTransactionDetails( 1239 int32(startHeight), int32(chainTip), "", 1240 ) 1241 if err != nil { 1242 t.Fatalf("unable to fetch tx details: %v", err) 1243 } 1244 1245 // This is a mapping from: 1246 // blockHash -> transactionHash -> transactionOutputs 1247 blockTxOuts := make(map[chainhash.Hash]map[chainhash.Hash][]*wire.TxOut) 1248 1249 // Each of the transactions created above should be found with the 1250 // proper details populated. 1251 for _, txDetail := range txDetails { 1252 if _, ok := txids[txDetail.Hash]; !ok { 1253 continue 1254 } 1255 1256 if txDetail.NumConfirmations != numBlocksMined { 1257 t.Fatalf("num confs incorrect, got %v expected %v", 1258 txDetail.NumConfirmations, numBlocksMined) 1259 } 1260 if txDetail.Value != outputAmt { 1261 t.Fatalf("tx value incorrect, got %v expected %v", 1262 txDetail.Value, outputAmt) 1263 } 1264 1265 if !bytes.Equal(txDetail.BlockHash[:], blocks[0][:]) { 1266 t.Fatalf("block hash mismatch, got %v expected %v", 1267 txDetail.BlockHash, blocks[0]) 1268 } 1269 1270 // This fetches the transactions in a block so that we can compare the 1271 // txouts stored in the mined transaction against the ones in the transaction 1272 // details 1273 if _, ok := blockTxOuts[*txDetail.BlockHash]; !ok { 1274 fetchedBlock, err := alice.Cfg.ChainIO.GetBlock(txDetail.BlockHash) 1275 if err != nil { 1276 t.Fatalf("err fetching block: %s", err) 1277 } 1278 1279 transactions := 1280 make(map[chainhash.Hash][]*wire.TxOut, len(fetchedBlock.Transactions)) 1281 for _, tx := range fetchedBlock.Transactions { 1282 transactions[tx.TxHash()] = tx.TxOut 1283 } 1284 1285 blockTxOuts[fetchedBlock.BlockHash()] = transactions 1286 } 1287 1288 if txOuts, ok := blockTxOuts[*txDetail.BlockHash][txDetail.Hash]; !ok { 1289 t.Fatalf("tx (%v) not found in block (%v)", 1290 txDetail.Hash, txDetail.BlockHash) 1291 } else { 1292 var destinationAddresses []stdaddr.Address 1293 1294 for _, txOut := range txOuts { 1295 _, addrs := 1296 stdscript.ExtractAddrs(txOut.Version, txOut.PkScript, 1297 &alice.Cfg.NetParams) 1298 destinationAddresses = append(destinationAddresses, addrs...) 1299 } 1300 1301 if !reflect.DeepEqual(txDetail.DestAddresses, destinationAddresses) { 1302 t.Fatalf("destination addresses mismatch, got %v expected %v", 1303 txDetail.DestAddresses, destinationAddresses) 1304 } 1305 } 1306 1307 delete(txids, txDetail.Hash) 1308 } 1309 if len(txids) != 0 { 1310 t.Fatalf("all transactions not found in details: left=%v, "+ 1311 "returned_set=%v", spew.Sdump(txids), 1312 spew.Sdump(txDetails)) 1313 } 1314 1315 // Next create a transaction paying to an output which isn't under the 1316 // wallet's control. 1317 b := txscript.NewScriptBuilder() 1318 b.AddOp(txscript.OP_0) 1319 outputScript, err := b.Script() 1320 if err != nil { 1321 t.Fatalf("unable to make output script: %v", err) 1322 } 1323 burnOutput := wire.NewTxOut(outputAmt, outputScript) 1324 burnTX, err := alice.SendOutputs( 1325 []*wire.TxOut{burnOutput}, defaultFeeRate, 1, labels.External, "", 1326 ) 1327 if err != nil { 1328 t.Fatalf("unable to create burn tx: %v", err) 1329 } 1330 burnTXID := burnTX.TxHash() 1331 err = waitForMempoolTx(miner, &burnTXID) 1332 if err != nil { 1333 t.Fatalf("tx not relayed to miner: %v", err) 1334 } 1335 1336 // Before we mine the next block, we'll ensure that the above 1337 // transaction shows up in the set of unconfirmed transactions returned 1338 // by ListTransactionDetails. 1339 err = waitForWalletSync(miner, alice) 1340 if err != nil { 1341 t.Fatalf("Couldn't sync Alice's wallet: %v", err) 1342 } 1343 1344 // Query our wallet for transactions from the chain tip, including 1345 // unconfirmed transactions. The transaction above should be included 1346 // with a confirmation height of 0, indicating that it has not been 1347 // mined yet. 1348 txDetails, err = alice.ListTransactionDetails( 1349 int32(chainTip), dcrwallet.UnconfirmedHeight, "", 1350 ) 1351 if err != nil { 1352 t.Fatalf("unable to fetch tx details: %v", err) 1353 } 1354 var mempoolTxFound bool 1355 for _, txDetail := range txDetails { 1356 if !bytes.Equal(txDetail.Hash[:], burnTXID[:]) { 1357 continue 1358 } 1359 1360 // Now that we've found the transaction, ensure that it has a 1361 // negative number of confirmations to indicate that it's 1362 // unconfirmed. 1363 mempoolTxFound = true 1364 if txDetail.NumConfirmations != 0 { 1365 t.Fatalf("num confs incorrect, got %v expected %v", 1366 txDetail.NumConfirmations, 0) 1367 } 1368 } 1369 if !mempoolTxFound { 1370 t.Fatalf("unable to find mempool tx in tx details!") 1371 } 1372 1373 // Generate one block for our transaction to confirm in. 1374 var numBlocks int32 = 1 1375 burnBlock, err := vw.GenerateBlocks(context.TODO(), uint32(numBlocks)) 1376 if err != nil { 1377 t.Fatalf("unable to mine block: %v", err) 1378 } 1379 1380 // Progress our chain tip by the number of blocks we have just mined. 1381 chainTip += int64(numBlocks) 1382 1383 // Fetch the transaction details again, the new transaction should be 1384 // shown as debiting from the wallet's balance. Start and end height 1385 // are inclusive, so we use chainTip for both parameters to get only 1386 // transactions from the last block. 1387 err = waitForWalletSync(miner, alice) 1388 if err != nil { 1389 t.Fatalf("Couldn't sync Alice's wallet: %v", err) 1390 } 1391 txDetails, err = alice.ListTransactionDetails( 1392 int32(chainTip), int32(chainTip), "", 1393 ) 1394 if err != nil { 1395 t.Fatalf("unable to fetch tx details: %v", err) 1396 } 1397 var burnTxFound bool 1398 for _, txDetail := range txDetails { 1399 if !bytes.Equal(txDetail.Hash[:], burnTXID[:]) { 1400 continue 1401 } 1402 1403 burnTxFound = true 1404 if txDetail.NumConfirmations != 1 { 1405 t.Fatalf("num confs incorrect, got %v expected %v", 1406 txDetail.NumConfirmations, 1) 1407 } 1408 1409 // We assert that the value is greater than the amount we 1410 // attempted to send, as the wallet should have paid some amount 1411 // of network fees. 1412 if txDetail.Value >= -outputAmt { 1413 fmt.Println(spew.Sdump(txDetail)) 1414 t.Fatalf("tx value incorrect, got %v expected %v", 1415 int64(txDetail.Value), -int64(outputAmt)) 1416 } 1417 if !bytes.Equal(txDetail.BlockHash[:], burnBlock[0][:]) { 1418 t.Fatalf("block hash mismatch, got %v expected %v", 1419 txDetail.BlockHash, burnBlock[0]) 1420 } 1421 } 1422 if !burnTxFound { 1423 t.Fatal("tx burning dcr not found") 1424 } 1425 1426 // Generate a block which has no wallet transactions in it. 1427 chainTip += int64(numBlocks) 1428 _, err = miner.Node.Generate(context.TODO(), uint32(numBlocks)) 1429 if err != nil { 1430 t.Fatalf("unable to mine block: %v", err) 1431 } 1432 1433 err = waitForWalletSync(miner, alice) 1434 if err != nil { 1435 t.Fatalf("Couldn't sync Alice's wallet: %v", err) 1436 } 1437 1438 // Query for transactions only in the latest block. We do not expect 1439 // any transactions to be returned. 1440 txDetails, err = alice.ListTransactionDetails( 1441 int32(chainTip), int32(chainTip), "", 1442 ) 1443 if err != nil { 1444 t.Fatalf("unexpected error: %v", err) 1445 } 1446 if len(txDetails) != 0 { 1447 t.Fatalf("expected 0 transactions, got: %v", len(txDetails)) 1448 } 1449 } 1450 1451 func testTransactionSubscriptions(miner *rpctest.Harness, 1452 vw *rpctest.VotingWallet, alice, _ *lnwallet.LightningWallet, 1453 t *testing.T) { 1454 1455 // Generate a block and wait for wallet sync to ensure any outstanding 1456 // txs from previous tests won't interfere with this test. 1457 if _, err := vw.GenerateBlocks(context.TODO(), 1); err != nil { 1458 t.Fatalf("unable to generate block: %v", err) 1459 } 1460 err := waitForWalletSync(miner, alice) 1461 if err != nil { 1462 t.Fatalf("Couldn't sync Alice's wallet: %v", err) 1463 } 1464 1465 // First, check to see if this wallet meets the TransactionNotifier 1466 // interface, if not then we'll skip this test for this particular 1467 // implementation of the WalletController. 1468 txClient, err := alice.SubscribeTransactions() 1469 if err != nil { 1470 t.Skipf("unable to generate tx subscription: %v", err) 1471 } 1472 defer txClient.Cancel() 1473 1474 const ( 1475 outputAmt = dcrutil.AtomsPerCoin 1476 numTxns = 3 1477 ) 1478 errCh1 := make(chan error, 1) 1479 switch alice.BackEnd() { 1480 case "neutrino": 1481 // Neutrino doesn't listen for unconfirmed transactions. 1482 default: 1483 go func() { 1484 for i := 0; i < numTxns; i++ { 1485 txDetail := <-txClient.UnconfirmedTransactions() 1486 if txDetail.NumConfirmations != 0 { 1487 errCh1 <- fmt.Errorf("incorrect number of confs, "+ 1488 "expected %v got %v", 0, 1489 txDetail.NumConfirmations) 1490 return 1491 } 1492 if txDetail.Value != outputAmt { 1493 errCh1 <- fmt.Errorf("incorrect output amt, "+ 1494 "expected %v got %v", outputAmt, 1495 txDetail.Value) 1496 return 1497 } 1498 if txDetail.BlockHash != nil { 1499 errCh1 <- fmt.Errorf("block hash should be nil, "+ 1500 "is instead %v", 1501 txDetail.BlockHash) 1502 return 1503 } 1504 } 1505 errCh1 <- nil 1506 }() 1507 } 1508 1509 // Next, fetch a fresh address from the wallet, create 3 new outputs 1510 // with the pkScript. 1511 addrs := make([]stdaddr.Address, numTxns) 1512 for i := 0; i < numTxns; i++ { 1513 addrs[i], err = alice.NewAddress( 1514 lnwallet.PubKeyHash, false, 1515 lnwallet.DefaultAccountName, 1516 ) 1517 if err != nil { 1518 t.Fatalf("unable to create new address: %v", err) 1519 } 1520 } 1521 1522 // Sleep to allow the wallet's address manager to close (see issue 1523 // dcrwallet#1372). 1524 time.Sleep(time.Millisecond * 50) 1525 ctxb := context.Background() 1526 1527 for i := 0; i < numTxns; i++ { 1528 script, err := input.PayToAddrScript(addrs[i]) 1529 if err != nil { 1530 t.Fatalf("unable to create output script: %v", err) 1531 } 1532 1533 output := &wire.TxOut{ 1534 Value: outputAmt, 1535 PkScript: script, 1536 } 1537 txid, err := miner.SendOutputs(ctxb, []*wire.TxOut{output}, 1538 dcrutil.Amount(defaultFeeRate)) 1539 if err != nil { 1540 t.Fatalf("unable to send coinbase: %v", err) 1541 } 1542 err = waitForMempoolTx(miner, txid) 1543 if err != nil { 1544 t.Fatalf("tx not relayed to miner: %v", err) 1545 } 1546 } 1547 1548 switch alice.BackEnd() { 1549 case "neutrino": 1550 // Neutrino doesn't listen for on unconfirmed transactions. 1551 default: 1552 // We should receive a notification for all three transactions 1553 // generated above. 1554 select { 1555 case <-time.After(time.Second * 10): 1556 t.Fatalf("transactions not received after 10 seconds") 1557 case err := <-errCh1: 1558 if err != nil { 1559 t.Fatal(err) 1560 } 1561 } 1562 } 1563 1564 errCh2 := make(chan error, 1) 1565 go func() { 1566 for i := 0; i < numTxns; i++ { 1567 txDetail := <-txClient.ConfirmedTransactions() 1568 if txDetail.NumConfirmations != 1 { 1569 errCh2 <- fmt.Errorf("incorrect number of confs for %s, expected %v got %v", 1570 txDetail.Hash, 1, txDetail.NumConfirmations) 1571 return 1572 } 1573 if txDetail.Value != outputAmt { 1574 errCh2 <- fmt.Errorf("incorrect output amt, expected %v got %v in txid %s", 1575 outputAmt, txDetail.Value, txDetail.Hash) 1576 return 1577 } 1578 } 1579 errCh2 <- nil 1580 }() 1581 1582 // Next mine a single block, all the transactions generated above 1583 // should be included. 1584 if _, err := vw.GenerateBlocks(context.TODO(), 1); err != nil { 1585 t.Fatalf("unable to generate block: %v", err) 1586 } 1587 1588 // We should receive a notification for all three transactions 1589 // since they should be mined in the next block. 1590 select { 1591 case <-time.After(time.Second * 5): 1592 t.Fatalf("transactions not received after 5 seconds") 1593 case err := <-errCh2: 1594 if err != nil { 1595 t.Fatal(err) 1596 } 1597 } 1598 1599 // We'll also ensure that the client is able to send our new 1600 // notifications when we _create_ transactions ourselves that spend our 1601 // own outputs. 1602 b := txscript.NewScriptBuilder() 1603 b.AddOp(txscript.OP_RETURN) 1604 outputScript, err := b.Script() 1605 if err != nil { 1606 t.Fatalf("unable to make output script: %v", err) 1607 } 1608 burnOutput := wire.NewTxOut(outputAmt, outputScript) 1609 tx, err := alice.SendOutputs( 1610 []*wire.TxOut{burnOutput}, defaultFeeRate, 1, labels.External, "", 1611 ) 1612 if err != nil { 1613 t.Fatalf("unable to create burn tx: %v", err) 1614 } 1615 txid := tx.TxHash() 1616 err = waitForMempoolTx(miner, &txid) 1617 if err != nil { 1618 t.Fatalf("tx not relayed to miner: %v", err) 1619 } 1620 1621 // Before we mine the next block, we'll ensure that the above 1622 // transaction shows up in the set of unconfirmed transactions returned 1623 // by ListTransactionDetails. 1624 err = waitForWalletSync(miner, alice) 1625 if err != nil { 1626 t.Fatalf("Couldn't sync Alice's wallet: %v", err) 1627 } 1628 1629 // As we just sent the transaction and it was landed in the mempool, we 1630 // should get a notification for a new unconfirmed transactions 1631 select { 1632 case <-time.After(time.Second * 10): 1633 t.Fatalf("transactions not received after 10 seconds") 1634 case unConfTx := <-txClient.UnconfirmedTransactions(): 1635 if unConfTx.Hash != txid { 1636 t.Fatalf("wrong txn notified: expected %v got %v", 1637 txid, unConfTx.Hash) 1638 } 1639 } 1640 } 1641 1642 // scriptFromKey creates a P2WKH script from the given pubkey. 1643 func scriptFromKey(pubkey *secp256k1.PublicKey) ([]byte, error) { 1644 pubkeyHash := dcrutil.Hash160(pubkey.SerializeCompressed()) 1645 keyAddr, err := stdaddr.NewAddressPubKeyHashEcdsaSecp256k1V0( 1646 pubkeyHash, chaincfg.SimNetParams(), 1647 ) 1648 if err != nil { 1649 return nil, fmt.Errorf("unable to create addr: %v", err) 1650 } 1651 keyScript, err := input.PayToAddrScript(keyAddr) 1652 if err != nil { 1653 return nil, fmt.Errorf("unable to generate script: %v", err) 1654 } 1655 1656 return keyScript, nil 1657 } 1658 1659 // mineAndAssert mines a block and ensures the passed TX is part of that block. 1660 func mineAndAssert(r *rpctest.Harness, vw *rpctest.VotingWallet, tx *wire.MsgTx) error { 1661 txid := tx.TxHash() 1662 err := waitForMempoolTx(r, &txid) 1663 if err != nil { 1664 return fmt.Errorf("tx not relayed to miner: %v", err) 1665 } 1666 1667 blockHashes, err := vw.GenerateBlocks(context.TODO(), 1) 1668 if err != nil { 1669 return fmt.Errorf("unable to generate block: %v", err) 1670 } 1671 1672 block, err := r.Node.GetBlock(context.TODO(), blockHashes[0]) 1673 if err != nil { 1674 return fmt.Errorf("unable to find block: %v", err) 1675 } 1676 1677 if len(block.Transactions) != 2 { 1678 return fmt.Errorf("expected 2 txs in block, got %d", 1679 len(block.Transactions)) 1680 } 1681 1682 blockTx := block.Transactions[1] 1683 if blockTx.TxHash() != tx.TxHash() { 1684 return fmt.Errorf("incorrect transaction was mined") 1685 } 1686 1687 // Sleep for a second before returning, to make sure the block has 1688 // propagated. 1689 time.Sleep(1 * time.Second) 1690 return nil 1691 } 1692 1693 // txFromOutput takes a tx paying to fromPubKey, and creates a new tx that 1694 // spends the output from this tx, to an address derived from payToPubKey. 1695 func txFromOutput(tx *wire.MsgTx, signer input.Signer, fromPubKey, 1696 payToPubKey *secp256k1.PublicKey, txFee dcrutil.Amount, 1697 rbf bool) (*wire.MsgTx, error) { 1698 1699 // Generate the script we want to spend from. 1700 keyScript, err := scriptFromKey(fromPubKey) 1701 if err != nil { 1702 return nil, fmt.Errorf("unable to generate script: %v", err) 1703 } 1704 1705 // We assume the output was paid to the keyScript made earlier. 1706 var outputIndex uint32 1707 if len(tx.TxOut) == 1 || bytes.Equal(tx.TxOut[0].PkScript, keyScript) { 1708 outputIndex = 0 1709 } else { 1710 outputIndex = 1 1711 } 1712 outputValue := tx.TxOut[outputIndex].Value 1713 1714 // With the index located, we can create a transaction spending the 1715 // referenced output. 1716 tx1 := wire.NewMsgTx() 1717 tx1.Version = 2 1718 1719 // If we want to create a tx that signals replacement, set its 1720 // sequence number to the max one that signals replacement. 1721 // Otherwise we just use the standard max sequence. 1722 sequence := wire.MaxTxInSequenceNum 1723 if rbf { 1724 sequence = 0xfffffffd // mempool.MaxRBFSequence 1725 } 1726 1727 tx1.AddTxIn(&wire.TxIn{ 1728 PreviousOutPoint: wire.OutPoint{ 1729 Hash: tx.TxHash(), 1730 Index: outputIndex, 1731 Tree: wire.TxTreeRegular, 1732 }, 1733 Sequence: sequence, 1734 }) 1735 1736 // Create a script to pay to. 1737 payToScript, err := scriptFromKey(payToPubKey) 1738 if err != nil { 1739 return nil, fmt.Errorf("unable to generate script: %v", err) 1740 } 1741 tx1.AddTxOut(&wire.TxOut{ 1742 Value: outputValue - int64(txFee), 1743 PkScript: payToScript, 1744 }) 1745 1746 // Now we can populate the sign descriptor which we'll use to generate 1747 // the signature. 1748 signDesc := &input.SignDescriptor{ 1749 KeyDesc: keychain.KeyDescriptor{ 1750 PubKey: fromPubKey, 1751 }, 1752 WitnessScript: keyScript, 1753 Output: tx.TxOut[outputIndex], 1754 HashType: txscript.SigHashAll, 1755 InputIndex: 0, // Has only one input. 1756 } 1757 1758 // With the descriptor created, we use it to generate a signature, then 1759 // manually create a valid witness stack we'll use for signing. 1760 spendSig, err := signer.SignOutputRaw(tx1, signDesc) 1761 if err != nil { 1762 return nil, fmt.Errorf("unable to generate signature: %v", err) 1763 } 1764 witness := make([][]byte, 2) 1765 witness[0] = append(spendSig.Serialize(), byte(txscript.SigHashAll)) 1766 witness[1] = fromPubKey.SerializeCompressed() 1767 tx1.TxIn[0].SignatureScript, err = input.WitnessStackToSigScript(witness) 1768 if err != nil { 1769 return nil, fmt.Errorf("unable to convert witness stack to sigScript: %v", err) 1770 } 1771 1772 // Finally, attempt to validate the completed transaction. This should 1773 // succeed if the wallet was able to properly generate the proper 1774 // private key. 1775 vm, err := txscript.NewEngine( 1776 keyScript, tx1, 0, input.ScriptVerifyFlags, tx.TxOut[outputIndex].Version, nil, 1777 ) 1778 if err != nil { 1779 return nil, fmt.Errorf("unable to create engine: %v", err) 1780 } 1781 if err := vm.Execute(); err != nil { 1782 return nil, fmt.Errorf("spend is invalid: %v", err) 1783 } 1784 1785 return tx1, nil 1786 } 1787 1788 // newTx sends coins from Alice's wallet, mines this transaction, and creates a 1789 // new, unconfirmed tx that spends this output to pubKey. 1790 func newTx(t *testing.T, r *rpctest.Harness, vw *rpctest.VotingWallet, pubKey *secp256k1.PublicKey, 1791 alice *lnwallet.LightningWallet, rbf bool) *wire.MsgTx { 1792 t.Helper() 1793 1794 keyScript, err := scriptFromKey(pubKey) 1795 if err != nil { 1796 t.Fatalf("unable to generate script: %v", err) 1797 } 1798 1799 // Instruct the wallet to fund the output with a newly created 1800 // transaction. 1801 newOutput := &wire.TxOut{ 1802 Value: dcrutil.AtomsPerCoin, 1803 PkScript: keyScript, 1804 } 1805 tx, err := alice.SendOutputs( 1806 []*wire.TxOut{newOutput}, defaultFeeRate, 1, labels.External, "", 1807 ) 1808 if err != nil { 1809 t.Fatalf("unable to create output: %v", err) 1810 } 1811 1812 // Query for the transaction generated above so we can located the 1813 // index of our output. 1814 if err := mineAndAssert(r, vw, tx); err != nil { 1815 t.Fatalf("unable to mine tx: %v", err) 1816 } 1817 1818 // Create a new unconfirmed tx that spends this output. 1819 txFee := dcrutil.Amount(defaultFeeRate) 1820 tx1, err := txFromOutput( 1821 tx, alice.Cfg.Signer, pubKey, pubKey, txFee, rbf, 1822 ) 1823 if err != nil { 1824 t.Fatal(err) 1825 } 1826 1827 return tx1 1828 } 1829 1830 // testPublishTransaction checks that PublishTransaction returns the expected 1831 // error types in case the transaction being published conflicts with the 1832 // current mempool or chain. 1833 func testPublishTransaction(r *rpctest.Harness, vw *rpctest.VotingWallet, 1834 alice, _ *lnwallet.LightningWallet, t *testing.T) { 1835 1836 // Generate a pubkey, and pay-to-addr script. 1837 keyDesc, err := alice.DeriveNextKey(keychain.KeyFamilyMultiSig) 1838 if err != nil { 1839 t.Fatalf("unable to obtain public key: %v", err) 1840 } 1841 1842 // We will first check that publishing a transaction already in the 1843 // mempool does NOT return an error. Create the tx. 1844 tx1 := newTx(t, r, vw, keyDesc.PubKey, alice, false) 1845 1846 // Publish the transaction. 1847 err = alice.PublishTransaction(tx1, labels.External) 1848 if err != nil { 1849 t.Fatalf("unable to publish: %v", err) 1850 } 1851 1852 txid1 := tx1.TxHash() 1853 err = waitForMempoolTx(r, &txid1) 1854 if err != nil { 1855 t.Fatalf("tx not relayed to miner: %v", err) 1856 } 1857 1858 // Publish the exact same transaction again. This should not return an 1859 // error, even though the transaction is already in the mempool. 1860 err = alice.PublishTransaction(tx1, labels.External) 1861 if err != nil { 1862 t.Fatalf("unable to publish: %v", err) 1863 } 1864 1865 // Mine the transaction. 1866 if _, err := vw.GenerateBlocks(context.TODO(), 1); err != nil { 1867 t.Fatalf("unable to generate block: %v", err) 1868 } 1869 1870 // We'll now test that we don't get an error if we try to publish a 1871 // transaction that is already mined. 1872 // 1873 // Create a new transaction. We must do this to properly test the 1874 // reject messages from our peers. They might only send us a reject 1875 // message for a given tx once, so we create a new to make sure it is 1876 // not just immediately rejected. 1877 tx2 := newTx(t, r, vw, keyDesc.PubKey, alice, false) 1878 1879 // Publish this tx. 1880 err = alice.PublishTransaction(tx2, labels.External) 1881 if err != nil { 1882 t.Fatalf("unable to publish: %v", err) 1883 } 1884 1885 // Mine the transaction. 1886 if err := mineAndAssert(r, vw, tx2); err != nil { 1887 t.Fatalf("unable to mine tx: %v", err) 1888 } 1889 1890 // Publish the transaction again. It is already mined, and we don't 1891 // expect this to return an error. 1892 err = alice.PublishTransaction(tx2, labels.External) 1893 if err != nil { 1894 t.Fatalf("unable to publish: %v", err) 1895 } 1896 1897 // We'll do the next mempool check on both RBF and non-RBF enabled 1898 // transactions. 1899 var ( 1900 txFee = dcrutil.Amount(defaultFeeRate * 5) 1901 tx3, tx3Spend *wire.MsgTx 1902 ) 1903 1904 // Note: decred does not support rbf at this time, so test only with 1905 // rbf == false. 1906 rbfTests := []bool{false} 1907 for _, rbf := range rbfTests { 1908 // Now we'll try to double spend an output with a different 1909 // transaction. Create a new tx and publish it. This is the 1910 // output we'll try to double spend. 1911 tx3 = newTx(t, r, vw, keyDesc.PubKey, alice, false) 1912 err := alice.PublishTransaction(tx3, labels.External) 1913 if err != nil { 1914 t.Fatalf("unable to publish: %v", err) 1915 } 1916 1917 // Mine the transaction. 1918 if err := mineAndAssert(r, vw, tx3); err != nil { 1919 t.Fatalf("unable to mine tx: %v", err) 1920 } 1921 1922 // Now we create a transaction that spends the output from the 1923 // tx just mined. 1924 tx4, err := txFromOutput( 1925 tx3, alice.Cfg.Signer, keyDesc.PubKey, 1926 keyDesc.PubKey, txFee, rbf, 1927 ) 1928 if err != nil { 1929 t.Fatal(err) 1930 } 1931 1932 // This should be accepted into the mempool. 1933 err = alice.PublishTransaction(tx4, labels.External) 1934 if err != nil { 1935 t.Fatalf("unable to publish: %v", err) 1936 } 1937 1938 // Keep track of the last successfully published tx to spend 1939 // tx3. 1940 tx3Spend = tx4 1941 1942 txid4 := tx4.TxHash() 1943 err = waitForMempoolTx(r, &txid4) 1944 if err != nil { 1945 t.Fatalf("tx not relayed to miner: %v", err) 1946 } 1947 1948 // Create a new key we'll pay to, to ensure we create a unique 1949 // transaction. 1950 keyDesc2, err := alice.DeriveNextKey( 1951 keychain.KeyFamilyMultiSig, 1952 ) 1953 if err != nil { 1954 t.Fatalf("unable to obtain public key: %v", err) 1955 } 1956 1957 // Create a new transaction that spends the output from tx3, 1958 // and that pays to a different address. We expect this to be 1959 // rejected because it is a double spend. 1960 tx5, err := txFromOutput( 1961 tx3, alice.Cfg.Signer, keyDesc.PubKey, 1962 keyDesc2.PubKey, txFee, rbf, 1963 ) 1964 if err != nil { 1965 t.Fatal(err) 1966 } 1967 1968 err = alice.PublishTransaction(tx5, labels.External) 1969 if err != lnwallet.ErrDoubleSpend { 1970 t.Fatalf("expected ErrDoubleSpend, got: %v", err) 1971 } 1972 1973 // Create another transaction that spends the same output, but 1974 // has a higher fee. We expect also this tx to be rejected for 1975 // non-RBF enabled transactions, while it should succeed 1976 // otherwise. 1977 pubKey3, err := alice.DeriveNextKey(keychain.KeyFamilyMultiSig) 1978 if err != nil { 1979 t.Fatalf("unable to obtain public key: %v", err) 1980 } 1981 tx6, err := txFromOutput( 1982 tx3, alice.Cfg.Signer, keyDesc.PubKey, 1983 pubKey3.PubKey, 2*txFee, rbf, 1984 ) 1985 if err != nil { 1986 t.Fatal(err) 1987 } 1988 1989 // Expect rejection in non-RBF case. 1990 expErr := lnwallet.ErrDoubleSpend 1991 if rbf { 1992 // Expect success in rbf case. 1993 expErr = nil 1994 tx3Spend = tx6 1995 } 1996 err = alice.PublishTransaction(tx6, labels.External) 1997 if err != expErr { 1998 t.Fatalf("expected ErrDoubleSpend, got: %v", err) 1999 } 2000 2001 // Mine the tx spending tx3. 2002 if err := mineAndAssert(r, vw, tx3Spend); err != nil { 2003 t.Fatalf("unable to mine tx: %v", err) 2004 } 2005 } 2006 2007 // At last we try to spend an output already spent by a confirmed 2008 // transaction. 2009 // 2010 // TODO(halseth): we currently skip this test for neutrino, as the 2011 // backing btcd node will consider the tx being an orphan, and will 2012 // accept it. Should look into if this is the behavior also for 2013 // bitcoind, and update test accordingly. 2014 // 2015 // TODO(decred) wallet/v3 also changed the semantics to simply ignore 2016 // spent output errors and return nil when publishing a double spend 2017 // that was already mined, so we ignore this test as well. 2018 if alice.BackEnd() == "disabled" { 2019 // Create another tx spending tx3. 2020 pubKey4, err := alice.DeriveNextKey( 2021 keychain.KeyFamilyMultiSig, 2022 ) 2023 if err != nil { 2024 t.Fatalf("unable to obtain public key: %v", err) 2025 } 2026 tx7, err := txFromOutput( 2027 tx3, alice.Cfg.Signer, keyDesc.PubKey, 2028 pubKey4.PubKey, txFee, false, 2029 ) 2030 2031 if err != nil { 2032 t.Fatal(err) 2033 } 2034 2035 // Expect rejection. 2036 err = alice.PublishTransaction(tx7, labels.External) 2037 if err != lnwallet.ErrDoubleSpend { 2038 t.Fatalf("expected ErrDoubleSpend, got: %v", err) 2039 } 2040 } 2041 } 2042 2043 func testSignOutputUsingTweaks(r *rpctest.Harness, 2044 avw *rpctest.VotingWallet, alice, _ *lnwallet.LightningWallet, 2045 t *testing.T) { 2046 2047 // We'd like to test the ability of the wallet's input.Signer implementation 2048 // to be able to sign with a private key derived from tweaking the 2049 // specific public key. This scenario exercises the case when the 2050 // wallet needs to sign for a sweep of a revoked output, or just claim 2051 // any output that pays to a tweaked key. 2052 2053 // First, generate a new public key under the control of the wallet, 2054 // then generate a revocation key using it. 2055 pubKey, err := alice.DeriveNextKey( 2056 keychain.KeyFamilyMultiSig, 2057 ) 2058 if err != nil { 2059 t.Fatalf("unable to obtain public key: %v", err) 2060 } 2061 2062 // As we'd like to test both single tweak, and double tweak spends, 2063 // we'll generate a commitment pre-image, then derive a revocation key 2064 // and single tweak from that. 2065 commitPreimage := bytes.Repeat([]byte{2}, 32) 2066 commitSecret := secp256k1.PrivKeyFromBytes(commitPreimage) 2067 commitPoint := commitSecret.PubKey() 2068 2069 revocationKey := input.DeriveRevocationPubkey(pubKey.PubKey, commitPoint) 2070 commitTweak := input.SingleTweakBytes(commitPoint, pubKey.PubKey) 2071 2072 tweakedPub := input.TweakPubKey(pubKey.PubKey, commitPoint) 2073 2074 // As we'd like to test both single and double tweaks, we'll repeat 2075 // the same set up twice. The first will use a regular single tweak, 2076 // and the second will use a double tweak. 2077 baseKey := pubKey 2078 for i := 0; i < 2; i++ { 2079 var tweakedKey *secp256k1.PublicKey 2080 if i == 0 { 2081 tweakedKey = tweakedPub 2082 } else { 2083 tweakedKey = revocationKey 2084 } 2085 2086 // Using the given key for the current iteration, we'll 2087 // generate a regular p2wkh from that. 2088 pubkeyHash := dcrutil.Hash160(tweakedKey.SerializeCompressed()) 2089 keyAddr, err := stdaddr.NewAddressPubKeyHashEcdsaSecp256k1V0(pubkeyHash, 2090 netParams) 2091 if err != nil { 2092 t.Fatalf("unable to create addr: %v", err) 2093 } 2094 keyScript, err := input.PayToAddrScript(keyAddr) 2095 if err != nil { 2096 t.Fatalf("unable to generate script: %v", err) 2097 } 2098 2099 // With the script fully assembled, instruct the wallet to fund 2100 // the output with a newly created transaction. 2101 newOutput := &wire.TxOut{ 2102 Value: dcrutil.AtomsPerCoin, 2103 PkScript: keyScript, 2104 Version: scriptVersion, 2105 } 2106 tx, err := alice.SendOutputs( 2107 []*wire.TxOut{newOutput}, defaultFeeRate, 1, labels.External, "", 2108 ) 2109 if err != nil { 2110 t.Fatalf("unable to create output: %v", err) 2111 } 2112 txid := tx.TxHash() 2113 // Query for the transaction generated above so we can located 2114 // the index of our output. 2115 err = waitForMempoolTx(r, &txid) 2116 if err != nil { 2117 t.Fatalf("tx not relayed to miner: %v", err) 2118 } 2119 var outputIndex uint32 2120 if bytes.Equal(tx.TxOut[0].PkScript, keyScript) { 2121 outputIndex = 0 2122 } else { 2123 outputIndex = 1 2124 } 2125 2126 // With the index located, we can create a transaction spending 2127 // the referenced output. 2128 sweepTx := wire.NewMsgTx() 2129 sweepTx.AddTxIn(&wire.TxIn{ 2130 PreviousOutPoint: wire.OutPoint{ 2131 Hash: txid, 2132 Index: outputIndex, 2133 Tree: wire.TxTreeRegular, 2134 }, 2135 }) 2136 sweepTx.AddTxOut(&wire.TxOut{ 2137 Value: 1000, 2138 PkScript: keyScript, 2139 Version: scriptVersion, 2140 }) 2141 2142 // Now we can populate the sign descriptor which we'll use to 2143 // generate the signature. Within the descriptor we set the 2144 // private tweak value as the key in the script is derived 2145 // based on this tweak value and the key we originally 2146 // generated above. 2147 signDesc := &input.SignDescriptor{ 2148 KeyDesc: keychain.KeyDescriptor{ 2149 PubKey: baseKey.PubKey, 2150 }, 2151 WitnessScript: keyScript, 2152 Output: newOutput, 2153 HashType: txscript.SigHashAll, 2154 InputIndex: 0, 2155 } 2156 2157 // If this is the first, loop, we'll use the generated single 2158 // tweak, otherwise, we'll use the double tweak. 2159 if i == 0 { 2160 signDesc.SingleTweak = commitTweak 2161 } else { 2162 signDesc.DoubleTweak = commitSecret 2163 } 2164 2165 // With the descriptor created, we use it to generate a 2166 // signature, then manually create a valid witness stack we'll 2167 // use for signing. 2168 spendSig, err := alice.Cfg.Signer.SignOutputRaw(sweepTx, signDesc) 2169 if err != nil { 2170 t.Fatalf("unable to generate signature: %v", err) 2171 } 2172 witness := make([][]byte, 2) 2173 witness[0] = append(spendSig.Serialize(), byte(txscript.SigHashAll)) 2174 witness[1] = tweakedKey.SerializeCompressed() 2175 sweepTx.TxIn[0].SignatureScript, err = input.WitnessStackToSigScript(witness) 2176 if err != nil { 2177 t.Fatalf("unable to convert witness stack to sigScript: %v", err) 2178 } 2179 2180 // Finally, attempt to validate the completed transaction. This 2181 // should succeed if the wallet was able to properly generate 2182 // the proper private key. 2183 vm, err := txscript.NewEngine(keyScript, 2184 sweepTx, 0, input.ScriptVerifyFlags, newOutput.Version, nil) 2185 if err != nil { 2186 t.Fatalf("unable to create engine: %v", err) 2187 } 2188 if err := vm.Execute(); err != nil { 2189 t.Fatalf("spend #%v is invalid: %v", i, err) 2190 } 2191 } 2192 } 2193 2194 func testReorgWalletBalance(r *rpctest.Harness, vw *rpctest.VotingWallet, 2195 w *lnwallet.LightningWallet, _ *lnwallet.LightningWallet, 2196 t *testing.T) { 2197 2198 ctxb := context.Background() 2199 2200 // Currently disabled due to 2201 // https://github.com/decred/dcrwallet/issues/1710. Re-assess after 2202 // that is fixed. 2203 if w.BackEnd() == "dcrw-spv" { 2204 t.Skipf("Skipping for SPV for the moment") 2205 } 2206 2207 // We first mine a few blocks to ensure any transactions still in the 2208 // mempool confirm, and then get the original balance, before a 2209 // reorganization that doesn't invalidate any existing transactions or 2210 // create any new non-coinbase transactions. We'll then check if it's 2211 // the same after the empty reorg. 2212 _, err := vw.GenerateBlocks(ctxb, 5) 2213 if err != nil { 2214 t.Fatalf("unable to generate blocks on passed node: %v", err) 2215 } 2216 2217 // Give wallet time to catch up. 2218 err = waitForWalletSync(r, w) 2219 if err != nil { 2220 t.Fatalf("unable to sync wallet: %v", err) 2221 } 2222 2223 // Send some money from the miner to the wallet 2224 err = loadTestCredits(r, w, vw.GenerateBlocks, 20, 4) 2225 if err != nil { 2226 t.Fatalf("unable to send money to lnwallet: %v", err) 2227 } 2228 2229 // Send some money from the wallet back to the miner. 2230 // Grab a fresh address from the miner to house this output. 2231 minerAddr, err := r.NewAddress(ctxb) 2232 if err != nil { 2233 t.Fatalf("unable to generate address for miner: %v", err) 2234 } 2235 script, err := input.PayToAddrScript(minerAddr) 2236 if err != nil { 2237 t.Fatalf("unable to create pay to addr script: %v", err) 2238 } 2239 output := &wire.TxOut{ 2240 Value: 1e8, 2241 PkScript: script, 2242 } 2243 tx, err := w.SendOutputs( 2244 []*wire.TxOut{output}, defaultFeeRate, 1, labels.External, "", 2245 ) 2246 if err != nil { 2247 t.Fatalf("unable to send outputs: %v", err) 2248 } 2249 txid := tx.TxHash() 2250 err = waitForMempoolTx(r, &txid) 2251 if err != nil { 2252 t.Fatalf("tx not relayed to miner: %v", err) 2253 } 2254 _, err = vw.GenerateBlocks(ctxb, 3) 2255 if err != nil { 2256 t.Fatalf("unable to generate blocks on passed node: %v", err) 2257 } 2258 2259 // Give wallet time to catch up. 2260 err = waitForWalletSync(r, w) 2261 if err != nil { 2262 t.Fatalf("unable to sync wallet: %v", err) 2263 } 2264 2265 // Get the original balance. 2266 origBalance, err := w.ConfirmedBalance(1, lnwallet.DefaultAccountName) 2267 if err != nil { 2268 t.Fatalf("unable to query for balance: %v", err) 2269 } 2270 2271 // Now we cause a reorganization as follows. 2272 // Step 1: create a new miner and start it. 2273 r2, err := testutils.NewSetupRPCTest(testctx.New(t), 5, r.ActiveNet, nil, []string{"--txindex"}, false, 0) 2274 if err != nil { 2275 t.Fatalf("unable to create temp miner: %v", err) 2276 } 2277 defer r2.TearDown() 2278 newBalance, err := w.ConfirmedBalance(1, lnwallet.DefaultAccountName) 2279 if err != nil { 2280 t.Fatalf("unable to query for balance: %v", err) 2281 } 2282 if origBalance != newBalance { 2283 t.Fatalf("wallet balance incorrect, should have %v, "+ 2284 "instead have %v", origBalance, newBalance) 2285 } 2286 2287 // Step 2: connect the miner to the passed miner and wait for 2288 // synchronization. 2289 err = rpctest.ConnectNode(ctxb, r2, r) 2290 if err != nil { 2291 t.Fatalf("unable to connect mining nodes together: %v", err) 2292 } 2293 err = rpctest.JoinNodes(ctxb, []*rpctest.Harness{r2, r}, rpctest.Blocks) 2294 if err != nil { 2295 t.Fatalf("unable to synchronize mining nodes: %v", err) 2296 } 2297 2298 mineFirst := func(nb uint32) ([]*chainhash.Hash, error) { 2299 return vw.GenerateBlocks(ctxb, nb) 2300 } 2301 mineSecond := func(nb uint32) ([]*chainhash.Hash, error) { 2302 return rpctest.AdjustedSimnetMiner(context.Background(), r2.Node, nb) 2303 } 2304 2305 // Step 3: Do a set of reorgs by disconnecting the two miners, mining 2306 // one block on the passed miner and two on the created miner, 2307 // connecting them, and waiting for them to sync. 2308 for i := 0; i < 5; i++ { 2309 // Wait for disconnection 2310 timeout := time.After(30 * time.Second) 2311 stillConnected := true 2312 for stillConnected { 2313 // Allow for timeout 2314 time.Sleep(100 * time.Millisecond) 2315 select { 2316 case <-timeout: 2317 t.Fatalf("timeout waiting for miner disconnect") 2318 default: 2319 } 2320 err = rpctest.RemoveNode(ctxb, r2, r) 2321 if err != nil { 2322 t.Fatalf("unable to disconnect mining nodes: %v", 2323 err) 2324 } 2325 stillConnected, err = rpctest.NodesConnected(ctxb, r2, r, true) 2326 if err != nil { 2327 t.Fatalf("error checking node connectivity: %v", 2328 err) 2329 } 2330 } 2331 _, err = mineFirst(2) 2332 if err != nil { 2333 t.Fatalf("unable to generate blocks on passed node: %v", 2334 err) 2335 } 2336 _, err = mineSecond(3) 2337 if err != nil { 2338 t.Fatalf("unable to generate blocks on created node: %v", 2339 err) 2340 } 2341 2342 // Give wallet time to catch up so we force a full reorg. 2343 err = waitForWalletSync(r, w) 2344 if err != nil { 2345 t.Fatalf("unable to sync wallet: %v", err) 2346 } 2347 2348 // Step 5: Reconnect the miners and wait for them to synchronize. 2349 err = rpctest.ConnectNode(ctxb, r2, r) 2350 if err != nil { 2351 switch err := err.(type) { 2352 case *dcrjson.RPCError: 2353 if err.Code != -8 { 2354 t.Fatalf("unable to connect mining "+ 2355 "nodes together: %v", err) 2356 } 2357 default: 2358 t.Fatalf("unable to connect mining nodes "+ 2359 "together: %v", err) 2360 } 2361 } 2362 err = rpctest.JoinNodes(ctxb, []*rpctest.Harness{r2, r}, 2363 rpctest.Blocks) 2364 if err != nil { 2365 t.Fatalf("unable to synchronize mining nodes: %v", err) 2366 } 2367 2368 // Give wallet time to catch up. 2369 err = waitForWalletSync(r, w) 2370 if err != nil { 2371 t.Fatalf("unable to sync wallet: %v", err) 2372 } 2373 } 2374 2375 // Now we check that the wallet balance stays the same. 2376 newBalance, err = w.ConfirmedBalance(1, lnwallet.DefaultAccountName) 2377 if err != nil { 2378 t.Fatalf("unable to query for balance: %v", err) 2379 } 2380 if origBalance != newBalance { 2381 t.Fatalf("wallet balance incorrect, should have %v, "+ 2382 "instead have %v", origBalance, newBalance) 2383 } 2384 } 2385 2386 // testChangeOutputSpendConfirmation ensures that when we attempt to spend a 2387 // change output created by the wallet, the wallet receives its confirmation 2388 // once included in the chain. 2389 func testChangeOutputSpendConfirmation(r *rpctest.Harness, 2390 vw *rpctest.VotingWallet, alice, bob *lnwallet.LightningWallet, 2391 t *testing.T) { 2392 2393 // In order to test that we see the confirmation of a transaction that 2394 // spends an output created by SendOutputs, we'll start by emptying 2395 // Alice's wallet so that no other UTXOs can be picked. To do so, we'll 2396 // generate an address for Bob, who will receive all the coins. 2397 // Assuming a balance of 80 DCR and a transaction fee of 2500 atom/kB, 2398 // we'll craft the following transaction so that Alice doesn't have any 2399 // UTXOs left. 2400 aliceBalance, err := alice.ConfirmedBalance(0, lnwallet.DefaultAccountName) 2401 if err != nil { 2402 t.Fatalf("unable to retrieve alice's balance: %v", err) 2403 } 2404 bobPkScript := newPkScript(t, bob, lnwallet.PubKeyHash) 2405 2406 // A transaction to sweep all 80 DCR that should be in the wallet will 2407 // be estimated to have ~3407 bytes, so calculate what a tx of that size 2408 // will pay in fees and remove from the total amount. 2409 // 2410 // TODO(wilmer): replace this once SendOutputs easily supports sending 2411 // all funds in one transaction. 2412 txFeeRate := defaultFeeRate 2413 txFee := txFeeRate.FeeForSize(3407) 2414 output := &wire.TxOut{ 2415 Value: int64(aliceBalance - txFee), 2416 PkScript: bobPkScript, 2417 } 2418 tx := sendCoins(t, r, vw, alice, bob, output, txFeeRate, true, 1) 2419 txHash := tx.TxHash() 2420 assertTxInWallet(t, alice, txHash, true) 2421 assertTxInWallet(t, bob, txHash, true) 2422 2423 // With the transaction sent and confirmed, Alice's balance should now 2424 // be 0. 2425 aliceBalance, err = alice.ConfirmedBalance(0, lnwallet.DefaultAccountName) 2426 if err != nil { 2427 t.Fatalf("unable to retrieve alice's balance: %v", err) 2428 } 2429 if aliceBalance != 0 { 2430 t.Fatalf("expected alice's balance to be 0 DCR, found %v", 2431 aliceBalance) 2432 } 2433 2434 // Now, we'll send an output back to Alice from Bob of 1 DCR. 2435 alicePkScript := newPkScript(t, alice, lnwallet.PubKeyHash) 2436 output = &wire.TxOut{ 2437 Value: dcrutil.AtomsPerCoin, 2438 PkScript: alicePkScript, 2439 Version: scriptVersion, 2440 } 2441 tx = sendCoins(t, r, vw, bob, alice, output, txFeeRate, true, 1) 2442 txHash = tx.TxHash() 2443 assertTxInWallet(t, alice, txHash, true) 2444 assertTxInWallet(t, bob, txHash, true) 2445 2446 // Alice now has an available output to spend, but it was not a change 2447 // output, which is what the test expects. Therefore, we'll generate one 2448 // by sending Bob back some coins. 2449 output = &wire.TxOut{ 2450 Value: dcrutil.AtomsPerCent, 2451 PkScript: bobPkScript, 2452 } 2453 tx = sendCoins(t, r, vw, alice, bob, output, txFeeRate, true, 1) 2454 txHash = tx.TxHash() 2455 assertTxInWallet(t, alice, txHash, true) 2456 assertTxInWallet(t, bob, txHash, true) 2457 2458 // Then, we'll spend the change output and ensure we see its 2459 // confirmation come in. 2460 tx = sendCoins(t, r, vw, alice, bob, output, txFeeRate, true, 1) 2461 txHash = tx.TxHash() 2462 assertTxInWallet(t, alice, txHash, true) 2463 assertTxInWallet(t, bob, txHash, true) 2464 2465 // Finally, we'll replenish Alice's wallet with some more coins to 2466 // ensure she has enough for any following test cases. 2467 if err := loadTestCredits(r, alice, vw.GenerateBlocks, 20, 4); err != nil { 2468 t.Fatalf("unable to replenish alice's wallet: %v", err) 2469 } 2470 } 2471 2472 // testSpendUnconfirmed ensures that when can spend unconfirmed outputs. 2473 func testSpendUnconfirmed(miner *rpctest.Harness, vw *rpctest.VotingWallet, 2474 alice, bob *lnwallet.LightningWallet, t *testing.T) { 2475 2476 bobPkScript := newPkScript(t, bob, lnwallet.PubKeyHash) 2477 alicePkScript := newPkScript(t, alice, lnwallet.PubKeyHash) 2478 txFeeRate := defaultFeeRate 2479 2480 // First we will empty out bob's wallet, sending the entire balance 2481 // to alice. We check the balance by listing the utxos to calculate 2482 // the required tx fee. 2483 var bobBalance dcrutil.Amount 2484 utxos, err := bob.ListUnspentWitness(0, math.MaxInt32, "") 2485 require.Nil(t, err) 2486 var sz input.TxSizeEstimator 2487 for _, u := range utxos { 2488 bobBalance += u.Value 2489 sz.AddP2PKHInput() 2490 } 2491 sz.AddP2PKHOutput() 2492 2493 // The wallet internally adds a possible change output when estimating 2494 // the size, therefore we add _another_ p2pkh output to account for that 2495 // and avoid an error when spending the full amount. The resulting change 2496 // will be dust, and the wallet will not actually add the change output. 2497 sz.AddP2PKHOutput() 2498 2499 txFee := txFeeRate.FeeForSize(sz.Size()) // Fee to spend bob's utxos to 1 output 2500 output := &wire.TxOut{ 2501 Value: int64(bobBalance - txFee), 2502 PkScript: alicePkScript, 2503 } 2504 2505 tx := sendCoins(t, miner, vw, bob, alice, output, txFeeRate, true, 1) 2506 txHash := tx.TxHash() 2507 assertTxInWallet(t, alice, txHash, true) 2508 assertTxInWallet(t, bob, txHash, true) 2509 2510 // Verify that bob doesn't have enough balance to send coins. 2511 output = &wire.TxOut{ 2512 Value: dcrutil.AtomsPerCoin * 0.5, 2513 PkScript: alicePkScript, 2514 } 2515 _, err = bob.SendOutputs( 2516 []*wire.TxOut{output}, txFeeRate, 0, labels.External, "", 2517 ) 2518 if err == nil { 2519 t.Fatalf("should have not been able to pay due to insufficient balance: %v", err) 2520 } 2521 2522 // Next we will send a transaction to bob but leave it in an 2523 // unconfirmed state. 2524 output = &wire.TxOut{ 2525 Value: dcrutil.AtomsPerCoin, 2526 PkScript: bobPkScript, 2527 } 2528 tx = sendCoins(t, miner, vw, alice, bob, output, txFeeRate, false, 1) 2529 txHash = tx.TxHash() 2530 assertTxInWallet(t, alice, txHash, false) 2531 assertTxInWallet(t, bob, txHash, false) 2532 2533 // Now, try to spend some of the unconfirmed funds from bob's wallet. 2534 output = &wire.TxOut{ 2535 Value: dcrutil.AtomsPerCoin * 0.5, 2536 PkScript: alicePkScript, 2537 } 2538 2539 // First, verify that we don't have enough balance to send the coins 2540 // using confirmed outputs only. 2541 _, err = bob.SendOutputs( 2542 []*wire.TxOut{output}, txFeeRate, 1, labels.External, "", 2543 ) 2544 if err == nil { 2545 t.Fatalf("should have not been able to pay due to insufficient balance: %v", err) 2546 } 2547 2548 // Now try the send again using unconfirmed outputs. 2549 tx = sendCoins(t, miner, vw, bob, alice, output, txFeeRate, false, 0) 2550 txHash = tx.TxHash() 2551 assertTxInWallet(t, alice, txHash, false) 2552 assertTxInWallet(t, bob, txHash, false) 2553 2554 // Mine the unconfirmed transactions. 2555 err = waitForMempoolTx(miner, &txHash) 2556 if err != nil { 2557 t.Fatalf("tx not relayed to miner: %v", err) 2558 } 2559 if _, err := vw.GenerateBlocks(context.TODO(), 1); err != nil { 2560 t.Fatalf("unable to generate block: %v", err) 2561 } 2562 if err := waitForWalletSync(miner, alice); err != nil { 2563 t.Fatalf("unable to sync alice: %v", err) 2564 } 2565 if err := waitForWalletSync(miner, bob); err != nil { 2566 t.Fatalf("unable to sync bob: %v", err) 2567 } 2568 2569 // Finally, send the remainder of bob's wallet balance back to him so 2570 // that these money movements dont mess up later tests. 2571 output = &wire.TxOut{ 2572 Value: int64(bobBalance) - (dcrutil.AtomsPerCoin * 0.4), 2573 PkScript: bobPkScript, 2574 } 2575 tx = sendCoins(t, miner, vw, alice, bob, output, txFeeRate, true, 1) 2576 txHash = tx.TxHash() 2577 assertTxInWallet(t, alice, txHash, true) 2578 assertTxInWallet(t, bob, txHash, true) 2579 } 2580 2581 // testLastUnusedAddr tests that the LastUnusedAddress returns the address if 2582 // it isn't used, and also that once the address becomes used, then it's 2583 // properly rotated. 2584 func testLastUnusedAddr(miner *rpctest.Harness, 2585 vw *rpctest.VotingWallet, 2586 alice, bob *lnwallet.LightningWallet, t *testing.T) { 2587 2588 // This is unimplemented on remotedcrwallet, but it's a bad 2589 // idea to use anyway and isn't currently used anywhere in the 2590 // code except the rpcserver, which we don't use anyway. 2591 if alice.BackEnd() == "remotedcrwallet" { 2592 t.Skip() 2593 } 2594 2595 if _, err := vw.GenerateBlocks(context.TODO(), 1); err != nil { 2596 t.Fatalf("unable to generate block: %v", err) 2597 } 2598 2599 // We'll repeat this test for each address type to ensure they're all 2600 // rotated properly. 2601 addrTypes := []lnwallet.AddressType{ 2602 lnwallet.PubKeyHash, 2603 } 2604 for _, addrType := range addrTypes { 2605 addr1, err := alice.LastUnusedAddress( 2606 addrType, lnwallet.DefaultAccountName, 2607 ) 2608 if err != nil { 2609 t.Fatalf("unable to get addr: %v", err) 2610 } 2611 addr2, err := alice.LastUnusedAddress( 2612 addrType, lnwallet.DefaultAccountName, 2613 ) 2614 if err != nil { 2615 t.Fatalf("unable to get addr: %v", err) 2616 } 2617 2618 // If we generate two addresses back to back, then we should 2619 // get the same addr, as none of them have been used yet. 2620 if addr1.String() != addr2.String() { 2621 t.Fatalf("addresses changed w/o use: %v vs %v", addr1, addr2) 2622 } 2623 2624 // Next, we'll have Bob pay to Alice's new address. This should 2625 // trigger address rotation at the backend wallet. 2626 addrScript, err := input.PayToAddrScript(addr1) 2627 if err != nil { 2628 t.Fatalf("unable to convert addr to script: %v", err) 2629 } 2630 feeRate := chainfee.AtomPerKByte(1e4) 2631 output := &wire.TxOut{ 2632 Value: 1000000, 2633 PkScript: addrScript, 2634 } 2635 sendCoins(t, miner, vw, bob, alice, output, feeRate, true, 1) 2636 2637 // If we make a new address, then it should be brand new, as 2638 // the prior address has been used. 2639 addr3, err := alice.LastUnusedAddress( 2640 addrType, lnwallet.DefaultAccountName, 2641 ) 2642 if err != nil { 2643 t.Fatalf("unable to get addr: %v", err) 2644 } 2645 if addr1.String() == addr3.String() { 2646 t.Fatalf("address should have changed but didn't") 2647 } 2648 } 2649 } 2650 2651 // testManyNewAddresses checks that the underlying wallet driver can generate 2652 // many new on-chain addresses without erroring. This verifies a bug that 2653 // could cause the wallet to use the wrong wrapping mode for new addresses. 2654 func testManyNewAddresses(r *rpctest.Harness, vw *rpctest.VotingWallet, 2655 w, _ *lnwallet.LightningWallet, t *testing.T) { 2656 const maxAddrs = 40 2657 for i := 0; i < maxAddrs; i++ { 2658 _, err := w.NewAddress(lnwallet.PubKeyHash, false, lnwallet.DefaultAccountName) 2659 if err != nil { 2660 t.Fatalf("unable to generate new address %d: %v", i, err) 2661 } 2662 } 2663 } 2664 2665 // testCreateSimpleTx checks that a call to CreateSimpleTx will return a 2666 // transaction that is equal to the one that is being created by SendOutputs in 2667 // a subsequent call. 2668 func testCreateSimpleTx(r *rpctest.Harness, // nolint: unused 2669 vw *rpctest.VotingWallet, 2670 w, _ *lnwallet.LightningWallet, t *testing.T) { 2671 2672 // Send some money from the miner to the wallet 2673 err := loadTestCredits(r, w, vw.GenerateBlocks, 20, 4) 2674 if err != nil { 2675 t.Fatalf("unable to send money to lnwallet: %v", err) 2676 } 2677 2678 // The test cases we will run through for all backends. 2679 testCases := []struct { 2680 outVals []int64 2681 feeRate chainfee.AtomPerKByte 2682 valid bool 2683 unconfirmed bool 2684 }{ 2685 { 2686 outVals: []int64{}, 2687 feeRate: 2500, 2688 valid: false, // No outputs. 2689 unconfirmed: false, 2690 }, 2691 { 2692 outVals: []int64{}, 2693 feeRate: 2500, 2694 valid: false, // No outputs. 2695 unconfirmed: true, 2696 }, 2697 2698 { 2699 outVals: []int64{1e3}, 2700 feeRate: 2500, 2701 valid: false, // Dust output. 2702 unconfirmed: false, 2703 }, 2704 { 2705 outVals: []int64{1e3}, 2706 feeRate: 2500, 2707 valid: false, // Dust output. 2708 unconfirmed: true, 2709 }, 2710 { 2711 outVals: []int64{1e8}, 2712 feeRate: 2500, 2713 valid: true, 2714 unconfirmed: false, 2715 }, 2716 { 2717 outVals: []int64{1e8}, 2718 feeRate: 2500, 2719 valid: true, 2720 unconfirmed: true, 2721 }, 2722 2723 { 2724 outVals: []int64{1e8, 2e8, 1e8, 2e7, 3e5}, 2725 feeRate: 2500, 2726 valid: true, 2727 unconfirmed: false, 2728 }, 2729 { 2730 outVals: []int64{1e8, 2e8, 1e8, 2e7, 3e5}, 2731 feeRate: 2500, 2732 valid: true, 2733 unconfirmed: true, 2734 }, 2735 2736 { 2737 outVals: []int64{1e8, 2e8, 1e8, 2e7, 3e5}, 2738 feeRate: 12500, 2739 valid: true, 2740 unconfirmed: false, 2741 }, 2742 { 2743 outVals: []int64{1e8, 2e8, 1e8, 2e7, 3e5}, 2744 feeRate: 12500, 2745 valid: true, 2746 unconfirmed: true, 2747 }, 2748 2749 { 2750 outVals: []int64{1e8, 2e8, 1e8, 2e7, 3e5}, 2751 feeRate: 50000, 2752 valid: true, 2753 unconfirmed: false, 2754 }, 2755 { 2756 outVals: []int64{1e8, 2e8, 1e8, 2e7, 3e5}, 2757 feeRate: 50000, 2758 valid: true, 2759 unconfirmed: true, 2760 }, 2761 2762 { 2763 outVals: []int64{1e8, 2e8, 1e8, 2e7, 3e5, 1e8, 2e8, 2764 1e8, 2e7, 3e5}, 2765 feeRate: 44250, 2766 valid: true, 2767 unconfirmed: false, 2768 }, 2769 { 2770 outVals: []int64{1e8, 2e8, 1e8, 2e7, 3e5, 1e8, 2e8, 2771 1e8, 2e7, 3e5}, 2772 feeRate: 44250, 2773 valid: true, 2774 unconfirmed: true, 2775 }, 2776 } 2777 2778 ctxb := context.Background() 2779 for _, test := range testCases { 2780 var minConfs int32 = 1 2781 feeRate := test.feeRate 2782 2783 // Grab some fresh addresses from the miner that we will send 2784 // to. 2785 outputs := make([]*wire.TxOut, len(test.outVals)) 2786 for i, outVal := range test.outVals { 2787 minerAddr, err := r.NewAddress(ctxb) 2788 if err != nil { 2789 t.Fatalf("unable to generate address for "+ 2790 "miner: %v", err) 2791 } 2792 script, err := input.PayToAddrScript(minerAddr) 2793 if err != nil { 2794 t.Fatalf("unable to create pay to addr "+ 2795 "script: %v", err) 2796 } 2797 output := &wire.TxOut{ 2798 Value: outVal, 2799 PkScript: script, 2800 } 2801 2802 outputs[i] = output 2803 } 2804 2805 // Now try creating a tx spending to these outputs. 2806 createTx, createErr := w.CreateSimpleTx( 2807 outputs, feeRate, minConfs, true, 2808 ) 2809 if test.valid == (createErr != nil) { 2810 fmt.Println(spew.Sdump(createTx.Tx)) 2811 t.Fatalf("got unexpected error when creating tx: %v", 2812 createErr) 2813 } 2814 2815 // Also send to these outputs. This should result in a tx 2816 // _very_ similar to the one we just created being sent. The 2817 // only difference is that the dry run tx is not signed, and 2818 // that the change output position might be different. 2819 tx, sendErr := w.SendOutputs(outputs, feeRate, minConfs, labels.External, "") 2820 if test.valid == (sendErr != nil) { 2821 t.Fatalf("got unexpected error when sending tx: %v", 2822 sendErr) 2823 } 2824 2825 // We expected either both to not fail, or both to fail with 2826 // the same error. 2827 if createErr != sendErr { 2828 t.Fatalf("error creating tx (%v) different "+ 2829 "from error sending outputs (%v)", 2830 createErr, sendErr) 2831 } 2832 2833 // If we expected the creation to fail, then this test is over. 2834 if !test.valid { 2835 continue 2836 } 2837 2838 txid := tx.TxHash() 2839 err = waitForMempoolTx(r, &txid) 2840 if err != nil { 2841 t.Fatalf("tx not relayed to miner: %v", err) 2842 } 2843 2844 // Helper method to check that the two txs are similar. 2845 assertSimilarTx := func(a, b *wire.MsgTx) error { 2846 if a.Version != b.Version { 2847 return fmt.Errorf("different versions: "+ 2848 "%v vs %v", a.Version, b.Version) 2849 } 2850 if a.LockTime != b.LockTime { 2851 return fmt.Errorf("different locktimes: "+ 2852 "%v vs %v", a.LockTime, b.LockTime) 2853 } 2854 if len(a.TxIn) != len(b.TxIn) { 2855 return fmt.Errorf("different number of "+ 2856 "inputs: %v vs %v", len(a.TxIn), 2857 len(b.TxIn)) 2858 } 2859 if len(a.TxOut) != len(b.TxOut) { 2860 return fmt.Errorf("different number of "+ 2861 "outputs: %v vs %v", len(a.TxOut), 2862 len(b.TxOut)) 2863 } 2864 2865 // They should be spending the same inputs. 2866 for i := range a.TxIn { 2867 prevA := a.TxIn[i].PreviousOutPoint 2868 prevB := b.TxIn[i].PreviousOutPoint 2869 if prevA != prevB { 2870 return fmt.Errorf("different inputs: "+ 2871 "%v vs %v", spew.Sdump(prevA), 2872 spew.Sdump(prevB)) 2873 } 2874 } 2875 2876 // They should have the same outputs. Since the change 2877 // output position gets randomized, they are not 2878 // guaranteed to be in the same order. 2879 for _, outA := range a.TxOut { 2880 found := false 2881 for _, outB := range b.TxOut { 2882 if reflect.DeepEqual(outA, outB) { 2883 found = true 2884 break 2885 } 2886 } 2887 if !found { 2888 return fmt.Errorf("did not find "+ 2889 "output %v", spew.Sdump(outA)) 2890 } 2891 } 2892 return nil 2893 } 2894 2895 // Assert that our "template tx" was similar to the one that 2896 // ended up being sent. 2897 if err := assertSimilarTx(createTx.Tx, tx); err != nil { 2898 t.Fatalf("transactions not similar: %v", err) 2899 } 2900 } 2901 } 2902 2903 type walletTestCase struct { 2904 name string 2905 test func(miner *rpctest.Harness, vw *rpctest.VotingWallet, 2906 alice, bob *lnwallet.LightningWallet, test *testing.T) 2907 } 2908 2909 var walletTests = []walletTestCase{ 2910 { 2911 // TODO(wilmer): this test should remain first until the wallet 2912 // can properly craft a transaction that spends all of its 2913 // on-chain funds. 2914 name: "change output spend confirmation", 2915 test: testChangeOutputSpendConfirmation, 2916 }, 2917 { 2918 // This test also needs to happen at the start of testing, to 2919 // prevent a reorg past SVH which could cause the voting wallet 2920 // to misbehave. 2921 name: "reorg wallet balance", 2922 test: testReorgWalletBalance, 2923 }, 2924 { 2925 name: "spend unconfirmed outputs", 2926 test: testSpendUnconfirmed, 2927 }, 2928 { 2929 name: "insane fee reject", 2930 test: testReservationInitiatorBalanceBelowDustCancel, 2931 }, 2932 { 2933 name: "single funding workflow", 2934 test: func(miner *rpctest.Harness, vw *rpctest.VotingWallet, 2935 alice, bob *lnwallet.LightningWallet, t *testing.T) { 2936 2937 testSingleFunderReservationWorkflow( 2938 miner, vw, alice, bob, t, 2939 lnwallet.CommitmentTypeLegacy, nil, nil, 2940 [32]byte{15, 16, 17, 18}, 0, 2941 ) 2942 }, 2943 }, 2944 { 2945 name: "single funding workflow tweakless", 2946 test: func(miner *rpctest.Harness, vw *rpctest.VotingWallet, 2947 alice, bob *lnwallet.LightningWallet, t *testing.T) { 2948 2949 testSingleFunderReservationWorkflow( 2950 miner, vw, alice, bob, t, 2951 lnwallet.CommitmentTypeTweakless, nil, nil, 2952 [32]byte{19, 20, 21, 22}, 0, 2953 ) 2954 }, 2955 }, 2956 { 2957 name: "single funding workflow external funding tx", 2958 test: testSingleFunderExternalFundingTx, 2959 }, 2960 { 2961 name: "dual funder workflow", 2962 test: testDualFundingReservationWorkflow, 2963 }, 2964 { 2965 name: "output locking", 2966 test: testFundingTransactionLockedOutputs, 2967 }, 2968 { 2969 name: "reservation insufficient funds", 2970 test: testFundingCancellationNotEnoughFunds, 2971 }, 2972 { 2973 name: "transaction subscriptions", 2974 test: testTransactionSubscriptions, 2975 }, 2976 { 2977 name: "transaction details", 2978 test: testListTransactionDetails, 2979 }, 2980 { 2981 name: "publish transaction", 2982 test: testPublishTransaction, 2983 }, 2984 { 2985 name: "signed with tweaked pubkeys", 2986 test: testSignOutputUsingTweaks, 2987 }, 2988 { 2989 name: "test cancel non-existent reservation", 2990 test: testCancelNonExistentReservation, 2991 }, 2992 { 2993 name: "last unused addr", 2994 test: testLastUnusedAddr, 2995 }, { 2996 name: "test many new addresses", 2997 test: testManyNewAddresses, 2998 }, 2999 // TODO(decred) re-enable these tests after implementing. 3000 // { name: "create simple tx", test: testCreateSimpleTx, }, 3001 // { name: "test get recovery info", test: testGetRecoveryInfo, }, 3002 } 3003 3004 func clearWalletStates(a, b *lnwallet.LightningWallet) error { 3005 a.ResetReservations() 3006 b.ResetReservations() 3007 3008 if err := a.Cfg.Database.GetParentDB().Wipe(); err != nil { 3009 return err 3010 } 3011 3012 return b.Cfg.Database.GetParentDB().Wipe() 3013 } 3014 3015 func waitForMempoolTx(r *rpctest.Harness, txid *chainhash.Hash) error { 3016 var found bool 3017 var tx *dcrutil.Tx 3018 var err error 3019 timeout := time.After(30 * time.Second) 3020 for !found { 3021 // Do a short wait 3022 select { 3023 case <-timeout: 3024 return fmt.Errorf("timeout after 10s") 3025 default: 3026 } 3027 time.Sleep(100 * time.Millisecond) 3028 3029 // Check for the harness' knowledge of the txid 3030 tx, err = r.Node.GetRawTransaction(context.TODO(), txid) 3031 if err != nil { 3032 switch e := err.(type) { 3033 case *dcrjson.RPCError: 3034 if e.Code == dcrjson.ErrRPCNoTxInfo { 3035 continue 3036 } 3037 default: 3038 } 3039 return err 3040 } 3041 if tx != nil && tx.MsgTx().TxHash() == *txid { 3042 found = true 3043 } 3044 } 3045 return nil 3046 } 3047 3048 func waitForWalletSync(r *rpctest.Harness, w *lnwallet.LightningWallet) error { 3049 var ( 3050 synced bool 3051 err error 3052 predErr error 3053 bestHash *chainhash.Hash 3054 knownHash chainhash.Hash 3055 knownHeight int64 3056 bestHeight int64 3057 ) 3058 timeout := time.After(10 * time.Second) 3059 for !synced { 3060 // Do a short wait 3061 select { 3062 case <-timeout: 3063 return fmt.Errorf("timeout after 30s. predErr=%v", predErr) 3064 case <-time.Tick(100 * time.Millisecond): 3065 } 3066 3067 // Check whether the chain source of the wallet is caught up to 3068 // the harness it's supposed to be catching up to. 3069 bestHash, bestHeight, err = r.Node.GetBestBlock(context.TODO()) 3070 if err != nil { 3071 return fmt.Errorf("error getting node best block: %v", err) 3072 } 3073 knownHeight, knownHash, _, err = w.BestBlock() 3074 if err != nil { 3075 return fmt.Errorf("error getting chainIO bestBlock: %v", err) 3076 } 3077 if knownHeight != bestHeight { 3078 predErr = fmt.Errorf("miner and wallet best heights "+ 3079 "are not the same (want=%d got=%d)", 3080 bestHeight, knownHeight) 3081 continue 3082 } 3083 if knownHash != *bestHash { 3084 return fmt.Errorf("hash at height %d doesn't match: "+ 3085 "expected %s, got %s", bestHeight, bestHash, 3086 knownHash) 3087 } 3088 3089 // Check for synchronization. 3090 synced, _, err = w.IsSynced() 3091 if err != nil { 3092 return err 3093 } 3094 if !synced { 3095 predErr = fmt.Errorf("wallet isSynced=false") 3096 } 3097 } 3098 return nil 3099 } 3100 3101 // testSingleFunderExternalFundingTx tests that the wallet is able to properly 3102 // carry out a funding flow backed by a channel point that has been crafted 3103 // outside the wallet. 3104 func testSingleFunderExternalFundingTx(miner *rpctest.Harness, 3105 vw *rpctest.VotingWallet, 3106 alice, bob *lnwallet.LightningWallet, t *testing.T) { 3107 3108 // First, we'll obtain multi-sig keys from both Alice and Bob which 3109 // simulates them exchanging keys on a higher level. 3110 aliceFundingKey, err := alice.DeriveNextKey(keychain.KeyFamilyMultiSig) 3111 if err != nil { 3112 t.Fatalf("unable to obtain alice funding key: %v", err) 3113 } 3114 bobFundingKey, err := bob.DeriveNextKey(keychain.KeyFamilyMultiSig) 3115 if err != nil { 3116 t.Fatalf("unable to obtain bob funding key: %v", err) 3117 } 3118 3119 // We'll now set up for them to open a 4 BTC channel, with 1 BTC pushed 3120 // to Bob's side. 3121 chanAmt := 4 * dcrutil.AtomsPerCoin 3122 3123 // Simulating external funding negotiation, we'll now create the 3124 // funding transaction for both parties. Utilizing existing tools, 3125 // we'll create a new chanfunding.Assembler hacked by Alice's wallet. 3126 aliceChanFunder := chanfunding.NewWalletAssembler(chanfunding.WalletConfig{ 3127 CoinSource: lnwallet.NewCoinSource(alice), 3128 CoinSelectLocker: alice, 3129 CoinLocker: alice, 3130 Signer: alice.Cfg.Signer, 3131 DustLimit: 600, 3132 }) 3133 3134 // With the chan funder created, we'll now provision a funding intent, 3135 // bind the keys we obtained above, and finally obtain our funding 3136 // transaction and outpoint. 3137 fundingIntent, err := aliceChanFunder.ProvisionChannel(&chanfunding.Request{ 3138 LocalAmt: dcrutil.Amount(chanAmt), 3139 MinConfs: 1, 3140 FeeRate: 1e4, 3141 ChangeAddr: func() (stdaddr.Address, error) { 3142 return alice.NewAddress( 3143 lnwallet.PubKeyHash, true, 3144 lnwallet.DefaultAccountName, 3145 ) 3146 }, 3147 }) 3148 if err != nil { 3149 t.Fatalf("unable to perform coin selection: %v", err) 3150 } 3151 3152 // With our intent created, we'll instruct it to finalize the funding 3153 // transaction, and also hand us the outpoint so we can simulate 3154 // external crafting of the funding transaction. 3155 var ( 3156 fundingTx *wire.MsgTx 3157 chanPoint *wire.OutPoint 3158 ) 3159 if fullIntent, ok := fundingIntent.(*chanfunding.FullIntent); ok { 3160 fullIntent.BindKeys(&aliceFundingKey, bobFundingKey.PubKey) 3161 3162 fundingTx, err = fullIntent.CompileFundingTx(nil, nil) 3163 if err != nil { 3164 t.Fatalf("unable to compile funding tx: %v", err) 3165 } 3166 chanPoint, err = fullIntent.ChanPoint() 3167 if err != nil { 3168 t.Fatalf("unable to obtain chan point: %v", err) 3169 } 3170 } else { 3171 t.Fatalf("expected full intent, instead got: %T", fullIntent) 3172 } 3173 3174 // Now that we have the fully constructed funding transaction, we'll 3175 // create a new shim external funder out of it for Alice, and prep a 3176 // shim intent for Bob. 3177 thawHeight := uint32(200) 3178 aliceExternalFunder := chanfunding.NewCannedAssembler( 3179 thawHeight, *chanPoint, dcrutil.Amount(chanAmt), &aliceFundingKey, 3180 bobFundingKey.PubKey, true, 3181 ) 3182 bobShimIntent, err := chanfunding.NewCannedAssembler( 3183 thawHeight, *chanPoint, dcrutil.Amount(chanAmt), &bobFundingKey, 3184 aliceFundingKey.PubKey, false, 3185 ).ProvisionChannel(&chanfunding.Request{ 3186 LocalAmt: dcrutil.Amount(chanAmt), 3187 MinConfs: 1, 3188 FeeRate: 1e4, 3189 ChangeAddr: func() (stdaddr.Address, error) { 3190 return bob.NewAddress( 3191 lnwallet.PubKeyHash, true, 3192 lnwallet.DefaultAccountName, 3193 ) 3194 }, 3195 }) 3196 if err != nil { 3197 t.Fatalf("unable to create shim intent for bob: %v", err) 3198 } 3199 3200 // At this point, we have everything we need to carry out our test, so 3201 // we'll being the funding flow between Alice and Bob. 3202 // 3203 // However, before we do so, we'll register a new shim intent for Bob, 3204 // so he knows what keys to use when he receives the funding request 3205 // from Alice. 3206 pendingChanID := testHdSeed 3207 err = bob.RegisterFundingIntent(pendingChanID, bobShimIntent) 3208 if err != nil { 3209 t.Fatalf("unable to register intent: %v", err) 3210 } 3211 3212 // Now we can carry out the single funding flow as normal, we'll 3213 // specify our external funder and funding transaction, as well as the 3214 // pending channel ID generated above to allow Alice and Bob to track 3215 // the funding flow externally. 3216 testSingleFunderReservationWorkflow( 3217 miner, vw, alice, bob, t, lnwallet.CommitmentTypeTweakless, 3218 aliceExternalFunder, func() *wire.MsgTx { 3219 return fundingTx 3220 }, pendingChanID, thawHeight, 3221 ) 3222 } 3223 3224 // TestInterfaces tests all registered interfaces with a unified set of tests 3225 // which exercise each of the required methods found within the WalletController 3226 // interface. 3227 // 3228 // NOTE: In the future, when additional implementations of the WalletController 3229 // interface have been implemented, in order to ensure the new concrete 3230 // implementation is automatically tested, two steps must be undertaken. First, 3231 // one needs add a "non-captured" (_) import from the new sub-package. This 3232 // import should trigger an init() method within the package which registers 3233 // the interface. Second, an additional case in the switch within the main loop 3234 // below needs to be added which properly initializes the interface. 3235 // 3236 // TODO(roasbeef): purge bobNode in favor of dual lnwallet's 3237 func TestLightningWallet(t *testing.T, driverName, backEnd string) { 3238 t.Parallel() 3239 3240 walletDriver := lnwallet.WalletDriverForName(driverName) 3241 if walletDriver == nil { 3242 t.Fatalf("wallet driver %s does not exist", driverName) 3243 } 3244 3245 if !slices.Contains(walletDriver.BackEnds(), backEnd) { 3246 t.Fatalf("wallet driver %s does not support backend %s", 3247 walletDriver.WalletType, backEnd) 3248 } 3249 3250 var miningNode *rpctest.Harness 3251 3252 // Initialize the harness around a dcrd node which will 3253 // serve as our dedicated miner to generate blocks, 3254 // cause re-orgs, etc. We'll set up this node with a 3255 // chain length of 125, so we have plenty of DCR to 3256 // play around with. 3257 minerLogDir := fmt.Sprintf(".miner-logs-%s-%s", 3258 walletDriver.WalletType, backEnd) 3259 minerArgs := []string{"--txindex", "--debuglevel=debug", 3260 "--logdir=" + minerLogDir} 3261 miningNode, err := testutils.NewSetupRPCTest( 3262 testctx.New(t), 5, netParams, nil, minerArgs, true, 0, 3263 ) 3264 require.NoError(t, err) 3265 3266 defer func() { 3267 err := miningNode.TearDown() 3268 if err != nil { 3269 t.Errorf("unable to teardown rpc test harness: %v", err) 3270 } 3271 3272 // Copy the node logs from the original log dir. 3273 oldFileName := path.Join(minerLogDir, netParams.Name, 3274 "dcrd.log") 3275 newFileName := fmt.Sprintf("output-miner-%s-%s.log", 3276 walletDriver.WalletType, backEnd) 3277 err = os.Rename(oldFileName, newFileName) 3278 if err != nil { 3279 t.Logf("could not rename %s to %s: %v\n", 3280 oldFileName, newFileName, err) 3281 } 3282 }() 3283 3284 // Generate the premine block. 3285 _, err = miningNode.Node.Generate(context.TODO(), 1) 3286 require.NoError(t, err) 3287 3288 // Generate enough blocks for the initial load of test and voting 3289 // wallet but not so many that it would trigger a reorg after SVH 3290 // during the testReorgWalletBalance test. 3291 _, err = rpctest.AdjustedSimnetMiner(context.Background(), miningNode.Node, 40) 3292 require.NoError(t, err) 3293 3294 // Setup a voting wallet for when the chain passes SVH. 3295 vwCtx, vwCancel := context.WithCancel(context.Background()) 3296 defer vwCancel() 3297 votingWallet, err := rpctest.NewVotingWallet(vwCtx, miningNode) 3298 require.NoError(t, err) 3299 votingWallet.SetErrorReporting(func(err error) { 3300 t.Logf("Voting wallet error: %v", err) 3301 }) 3302 votingWallet.SetMiner(func(ctx context.Context, nb uint32) ([]*chainhash.Hash, error) { 3303 return rpctest.AdjustedSimnetMiner(ctx, miningNode.Node, nb) 3304 }) 3305 if err = votingWallet.Start(vwCtx); err != nil { 3306 t.Fatalf("unable to start voting wallet: %v", err) 3307 } 3308 3309 rpcConfig := miningNode.RPCConfig() 3310 3311 tempDir, err := ioutil.TempDir("", "channeldb") 3312 require.NoError(t, err) 3313 db, err := channeldb.Open(tempDir) 3314 require.NoError(t, err) 3315 hintCacheCfg := chainntnfs.CacheConfig{ 3316 QueryDisable: false, 3317 } 3318 hintCache, err := chainntnfs.NewHeightHintCache(hintCacheCfg, db.Backend) 3319 require.NoError(t, err) 3320 chainNotifier, err := dcrdnotify.New( 3321 &rpcConfig, netParams, hintCache, hintCache, nil, 3322 ) 3323 require.NoError(t, err) 3324 if err := chainNotifier.Start(); err != nil { 3325 t.Fatalf("unable to start notifier: %v", err) 3326 } 3327 3328 runTests(t, walletDriver, backEnd, miningNode, rpcConfig, chainNotifier, 3329 votingWallet) 3330 } 3331 3332 // runTests runs all of the tests for a single interface implementation and 3333 // chain back-end combination. This makes it easier to use `defer` as well as 3334 // factoring out the test logic from the loop which cycles through the 3335 // interface implementations. 3336 func runTests(t *testing.T, walletDriver *lnwallet.WalletDriver, 3337 backEnd string, miningNode *rpctest.Harness, 3338 rpcConfig rpcclient.ConnConfig, 3339 chainNotifier *dcrdnotify.DcrdNotifier, 3340 votingWallet *rpctest.VotingWallet) bool { 3341 var ( 3342 aliceBio lnwallet.BlockChainIO 3343 bobBio lnwallet.BlockChainIO 3344 3345 aliceSigner input.Signer 3346 bobSigner input.Signer 3347 3348 aliceKeyRing keychain.SecretKeyRing 3349 bobKeyRing keychain.SecretKeyRing 3350 3351 aliceWalletController lnwallet.WalletController 3352 bobWalletController lnwallet.WalletController 3353 ) 3354 3355 tempTestDirAlice, err := ioutil.TempDir("", "lnwallet") 3356 if err != nil { 3357 t.Fatalf("unable to create temp directory: %v", err) 3358 } 3359 t.Logf("Alice Dir: %s", tempTestDirAlice) 3360 //defer os.RemoveAll(tempTestDirAlice) 3361 3362 tempTestDirBob, err := ioutil.TempDir("", "lnwallet") 3363 if err != nil { 3364 t.Fatalf("unable to create temp directory: %v", err) 3365 } 3366 t.Logf("Bob Dir: %s", tempTestDirBob) 3367 //defer os.RemoveAll(tempTestDirBob) 3368 3369 aliceDBDir := filepath.Join(tempTestDirAlice, "cdb") 3370 aliceCDB, err := channeldb.Open(aliceDBDir) 3371 if err != nil { 3372 t.Fatalf("unable to open alice cdb: %v", err) 3373 } 3374 defer aliceCDB.Close() 3375 3376 bobDBDir := filepath.Join(tempTestDirBob, "cdb") 3377 bobCDB, err := channeldb.Open(bobDBDir) 3378 if err != nil { 3379 t.Fatalf("unable to open bob cdb: %v", err) 3380 } 3381 defer bobCDB.Close() 3382 3383 aliceSeed := sha256.New() 3384 aliceSeed.Write([]byte(backEnd)) 3385 aliceSeed.Write([]byte(walletDriver.WalletType)) 3386 aliceSeed.Write(aliceHDSeed[:]) 3387 aliceSeedBytes := aliceSeed.Sum(nil) 3388 alicePrivatePass := []byte("alice-pass") 3389 3390 bobSeed := sha256.New() 3391 bobSeed.Write([]byte(backEnd)) 3392 bobSeed.Write([]byte(walletDriver.WalletType)) 3393 bobSeed.Write(bobHDSeed[:]) 3394 bobSeedBytes := bobSeed.Sum(nil) 3395 bobPrivatePass := []byte("bob-pass") 3396 3397 blockCache := blockcache.NewBlockCache(10000) 3398 3399 walletType := walletDriver.WalletType 3400 switch walletType { 3401 case "dcrwallet": 3402 var aliceSyncer, bobSyncer dcrwallet.WalletSyncer 3403 switch backEnd { 3404 case "dcrd": 3405 aliceSyncer, err = dcrwallet.NewRPCSyncer(rpcConfig, 3406 netParams) 3407 if err != nil { 3408 t.Fatalf("unable to make chain rpc: %v", err) 3409 } 3410 bobSyncer, err = dcrwallet.NewRPCSyncer(rpcConfig, 3411 netParams) 3412 if err != nil { 3413 t.Fatalf("unable to make chain rpc: %v", err) 3414 } 3415 3416 aliceBio, err = dcrwallet.NewRPCChainIO(rpcConfig, netParams, blockCache) 3417 if err != nil { 3418 t.Fatalf("unable to make alice chain IO: %v", err) 3419 } 3420 3421 bobBio, err = dcrwallet.NewRPCChainIO(rpcConfig, netParams, blockCache) 3422 if err != nil { 3423 t.Fatalf("unable to make bob chain IO: %v", err) 3424 } 3425 case "spv": 3426 spvCfg := &dcrwallet.SPVSyncerConfig{ 3427 Net: netParams, 3428 Peers: []string{miningNode.P2PAddress()}, 3429 AppDataDir: tempTestDirAlice, // Safe to reuse since we use a fixed addr. 3430 } 3431 3432 aliceSyncer, err = dcrwallet.NewSPVSyncer(spvCfg) 3433 if err != nil { 3434 t.Fatalf("unable to make alice spv syncer: %v", err) 3435 } 3436 bobSyncer, err = dcrwallet.NewSPVSyncer(spvCfg) 3437 if err != nil { 3438 t.Fatalf("unable to make bob spv syncer: %v", err) 3439 } 3440 default: 3441 t.Fatalf("unknown chain driver: %v", backEnd) 3442 } 3443 3444 aliceWalletConfig := &dcrwallet.Config{ 3445 PrivatePass: alicePrivatePass, 3446 HdSeed: aliceSeedBytes, 3447 NetParams: netParams, 3448 DataDir: tempTestDirAlice, 3449 Syncer: aliceSyncer, 3450 ChainIO: aliceBio, 3451 DB: aliceCDB, 3452 } 3453 aliceWalletController, err = walletDriver.New( 3454 aliceWalletConfig, blockCache, 3455 ) 3456 if err != nil { 3457 t.Fatalf("unable to create alice wallet: %v", err) 3458 } 3459 aliceSigner = aliceWalletController.(*dcrwallet.DcrWallet) 3460 aliceKeyRing = aliceWalletController.(*dcrwallet.DcrWallet) 3461 3462 bobWalletConfig := &dcrwallet.Config{ 3463 PrivatePass: bobPrivatePass, 3464 HdSeed: bobSeedBytes, 3465 NetParams: netParams, 3466 DataDir: tempTestDirBob, 3467 Syncer: bobSyncer, 3468 ChainIO: bobBio, 3469 DB: bobCDB, 3470 } 3471 bobWalletController, err = walletDriver.New( 3472 bobWalletConfig, blockCache, 3473 ) 3474 if err != nil { 3475 t.Fatalf("unable to create bob wallet: %v", err) 3476 } 3477 bobSigner = bobWalletController.(*dcrwallet.DcrWallet) 3478 bobKeyRing = bobWalletController.(*dcrwallet.DcrWallet) 3479 3480 if backEnd == "spv" { 3481 aliceBio = aliceWalletController.(*dcrwallet.DcrWallet) 3482 bobBio = bobWalletController.(*dcrwallet.DcrWallet) 3483 } 3484 case "remotedcrwallet": 3485 var rpcSyncCfg *rpcclient.ConnConfig 3486 var spvSyncCfg *testutils.SPVConfig 3487 3488 switch backEnd { 3489 case "dcrd": 3490 aliceBio, err = dcrwallet.NewRPCChainIO(rpcConfig, 3491 netParams, blockCache) 3492 if err != nil { 3493 t.Fatalf("unable to make chain rpc: %v", err) 3494 } 3495 bobBio, err = dcrwallet.NewRPCChainIO(rpcConfig, 3496 netParams, blockCache) 3497 if err != nil { 3498 t.Fatalf("unable to make chain rpc: %v", err) 3499 } 3500 3501 rpcSyncCfg = &rpcConfig 3502 3503 case "spv": 3504 spvSyncCfg = &testutils.SPVConfig{ 3505 Address: miningNode.P2PAddress(), 3506 } 3507 3508 default: 3509 t.Fatalf("unknown chain driver: %v", backEnd) 3510 } 3511 aliceConn, aliceCleanup := testutils.NewCustomTestRemoteDcrwallet( 3512 t, "Alice", tempTestDirAlice, aliceSeedBytes, 3513 alicePrivatePass, rpcSyncCfg, spvSyncCfg, 3514 ) 3515 defer aliceCleanup() 3516 if err := testutils.SetPerAccountPassphrase(aliceConn, alicePrivatePass); err != nil { 3517 t.Fatalf("unable to set alice wallet to account pwd: %v", err) 3518 } 3519 aliceWalletConfig := &remotedcrwallet.Config{ 3520 Conn: aliceConn, 3521 ChainIO: aliceBio, 3522 PrivatePass: alicePrivatePass, 3523 NetParams: netParams, 3524 DB: aliceCDB, 3525 } 3526 aliceWalletController, err = walletDriver.New(aliceWalletConfig) 3527 if err != nil { 3528 t.Fatalf("unable to create alice wallet: %v", err) 3529 } 3530 aliceSigner = aliceWalletController.(*remotedcrwallet.DcrWallet) 3531 aliceKeyRing = aliceWalletController.(*remotedcrwallet.DcrWallet) 3532 3533 bobConn, bobCleanup := testutils.NewCustomTestRemoteDcrwallet( 3534 t, "Bob", tempTestDirBob, bobSeedBytes, bobPrivatePass, 3535 rpcSyncCfg, spvSyncCfg, 3536 ) 3537 defer bobCleanup() 3538 if err := testutils.SetPerAccountPassphrase(bobConn, bobPrivatePass); err != nil { 3539 t.Fatalf("unable to set bob wallet to account pwd: %v", err) 3540 } 3541 bobWalletConfig := &remotedcrwallet.Config{ 3542 Conn: bobConn, 3543 ChainIO: bobBio, 3544 PrivatePass: bobPrivatePass, 3545 NetParams: netParams, 3546 DB: bobCDB, 3547 } 3548 bobWalletController, err = walletDriver.New(bobWalletConfig) 3549 if err != nil { 3550 t.Fatalf("unable to create bob wallet: %v", err) 3551 } 3552 bobSigner = bobWalletController.(*remotedcrwallet.DcrWallet) 3553 bobKeyRing = bobWalletController.(*remotedcrwallet.DcrWallet) 3554 3555 if backEnd == "spv" { 3556 aliceBio = aliceWalletController.(*remotedcrwallet.DcrWallet) 3557 bobBio = bobWalletController.(*remotedcrwallet.DcrWallet) 3558 } 3559 default: 3560 t.Fatalf("unknown wallet driver: %v", walletType) 3561 } 3562 3563 // Funding via 20 outputs with 4DCR each. 3564 alice, err := createTestWallet( 3565 aliceCDB, miningNode, netParams, 3566 chainNotifier, aliceWalletController, aliceKeyRing, 3567 aliceSigner, aliceBio, votingWallet, 3568 ) 3569 if err != nil { 3570 t.Fatalf("unable to create test ln wallet: %v", err) 3571 } 3572 defer alice.Shutdown() 3573 3574 bob, err := createTestWallet( 3575 bobCDB, miningNode, netParams, 3576 chainNotifier, bobWalletController, bobKeyRing, 3577 bobSigner, bobBio, votingWallet, 3578 ) 3579 if err != nil { 3580 t.Fatalf("unable to create test ln wallet: %v", err) 3581 } 3582 defer bob.Shutdown() 3583 3584 // Wait for both wallets to be synced. 3585 timeout := time.After(time.Second * 120) 3586 aliceSync := aliceWalletController.InitialSyncChannel() 3587 bobSync := bobWalletController.InitialSyncChannel() 3588 for aliceSync != nil || bobSync != nil { 3589 select { 3590 case <-timeout: 3591 t.Fatalf("timeout while waiting for wallets to sync") 3592 case <-aliceSync: 3593 t.Logf("Alice synced") 3594 aliceSync = nil 3595 case <-bobSync: 3596 t.Logf("Bob synced") 3597 bobSync = nil 3598 } 3599 } 3600 3601 // Load our test wallets with 20 outputs each holding 1DCR. 3602 if err := loadTestCredits(miningNode, alice, votingWallet.GenerateBlocks, 20, 1); err != nil { 3603 t.Fatalf("unable to send initial funds to alice: %v", err) 3604 } 3605 if err := loadTestCredits(miningNode, bob, votingWallet.GenerateBlocks, 20, 1); err != nil { 3606 t.Fatalf("unable to send initial funds to bob: %v", err) 3607 } 3608 3609 t.Logf("Loaded test credits") 3610 3611 // Both wallets should now have 20DCR available for 3612 // spending. 3613 assertProperBalance(t, alice, 1, 20) 3614 assertProperBalance(t, bob, 1, 20) 3615 3616 // Execute every test, clearing possibly mutated 3617 // wallet state after each step. 3618 for _, walletTest := range walletTests { 3619 3620 walletTest := walletTest 3621 3622 testName := fmt.Sprintf("%v/%v:%v", walletType, backEnd, 3623 walletTest.name) 3624 success := t.Run(testName, func(t *testing.T) { 3625 if backEnd == "spv" && 3626 strings.Contains(walletTest.name, "dual funder") { 3627 t.Skip("skipping dual funder tests for spv") 3628 } 3629 if backEnd == "spv" && 3630 strings.Contains(walletTest.name, "spend unconfirmed") { 3631 t.Skip("skipping spend unconfirmed tests for spv") 3632 } 3633 if backEnd == "spv" && 3634 strings.Contains(walletTest.name, "reorg ") { 3635 t.Skip("skipping reorg tests for spv") 3636 } 3637 3638 walletTest.test(miningNode, votingWallet, alice, bob, t) 3639 }) 3640 if !success { 3641 return false 3642 } 3643 3644 // TODO(roasbeef): possible reset mining 3645 // node's chainstate to initial level, cleanly 3646 // wipe buckets 3647 if err := clearWalletStates(alice, bob); err != 3648 nil && err != kvdb.ErrBucketNotFound { 3649 t.Fatalf("unable to wipe wallet state: %v", err) 3650 } 3651 } 3652 3653 return true 3654 }