github.com/decred/dcrlnd@v0.7.6/lntest/itest/lnd_wallet_import_test.go (about) 1 package itest 2 3 import ( 4 "context" 5 "fmt" 6 "math" 7 "testing" 8 9 "github.com/davecgh/go-spew/spew" 10 "github.com/decred/dcrd/txscript/v4/stdaddr" 11 "github.com/decred/dcrd/txscript/v4/stdscript" 12 "github.com/decred/dcrd/wire" 13 "github.com/decred/dcrlnd/lnrpc" 14 "github.com/decred/dcrlnd/lnrpc/walletrpc" 15 "github.com/decred/dcrlnd/lntest" 16 "github.com/decred/dcrlnd/lntest/wait" 17 "github.com/decred/dcrlnd/lnwallet" 18 "github.com/stretchr/testify/require" 19 ) 20 21 const ( 22 defaultAccount = lnwallet.DefaultAccountName 23 defaultImportedAccount = "imported" 24 ) 25 26 // walletToLNAddrType maps walletrpc.AddressType to lnrpc.AddressType. 27 func walletToLNAddrType(t *testing.T, addrType walletrpc.AddressType) lnrpc.AddressType { 28 switch addrType { 29 case walletrpc.AddressType_NESTED_WITNESS_PUBKEY_HASH, 30 walletrpc.AddressType_HYBRID_NESTED_WITNESS_PUBKEY_HASH: 31 32 return lnrpc.AddressType_NESTED_PUBKEY_HASH 33 34 case walletrpc.AddressType_WITNESS_PUBKEY_HASH: 35 return lnrpc.AddressType_WITNESS_PUBKEY_HASH 36 37 default: 38 t.Fatalf("unhandled addr type %v", addrType) 39 return 0 40 } 41 } 42 43 // newExternalAddr generates a new external address of an imported account for a 44 // pair of nodes, where one acts as the funder and the other as the signer. 45 func newExternalAddr(t *testing.T, funder, signer *lntest.HarnessNode, 46 importedAccount string, addrType walletrpc.AddressType) string { 47 48 // We'll generate a new address for Carol from Dave's node to receive 49 // and fund a new channel. 50 ctxb := context.Background() 51 ctxt, cancel := context.WithTimeout(ctxb, defaultTimeout) 52 defer cancel() 53 funderResp, err := funder.NewAddress(ctxt, &lnrpc.NewAddressRequest{ 54 Type: walletToLNAddrType(t, addrType), 55 Account: importedAccount, 56 }) 57 require.NoError(t, err) 58 59 // Carol also needs to generate the address for the sake of this test to 60 // be able to sign the channel funding input. 61 ctxt, cancel = context.WithTimeout(ctxb, defaultTimeout) 62 defer cancel() 63 signerResp, err := signer.NewAddress(ctxt, &lnrpc.NewAddressRequest{ 64 Type: walletToLNAddrType(t, addrType), 65 }) 66 require.NoError(t, err) 67 68 // Sanity check that the generated addresses match. 69 require.Equal(t, funderResp.Address, signerResp.Address) 70 assertExternalAddrType(t, funderResp.Address, addrType) 71 72 return funderResp.Address 73 } 74 75 // assertExternalAddrType asserts that an external address generated for an 76 // imported account is of the expected type. 77 func assertExternalAddrType(t *testing.T, addrStr string, 78 accountAddrType walletrpc.AddressType) { 79 80 addr, err := stdaddr.DecodeAddress(addrStr, harnessNetParams) 81 require.NoError(t, err) 82 83 switch accountAddrType { 84 85 /* 86 case walletrpc.AddressType_WITNESS_PUBKEY_HASH: 87 require.IsType(t, addr, &stdaddr.AddressWitnessPubKeyHash{}) 88 89 case walletrpc.AddressType_NESTED_WITNESS_PUBKEY_HASH, 90 walletrpc.AddressType_HYBRID_NESTED_WITNESS_PUBKEY_HASH: 91 92 require.IsType(t, addr, &stdaddr.AddressScriptHash{}) 93 */ 94 95 default: 96 _ = addr 97 t.Fatalf("unsupported account addr type %v", accountAddrType) 98 } 99 } 100 101 // assertOutputScriptType asserts that a transaction's output, indicated by the 102 // output with the given amount, has a script of the expected type. This assumes 103 // all transaction outputs have unique amounts. 104 func assertOutputScriptType(t *testing.T, expType stdscript.ScriptType, 105 tx *wire.MsgTx, outputAmt int64) { 106 107 for _, txOut := range tx.TxOut { 108 if txOut.Value != outputAmt { 109 continue 110 } 111 112 gotTyp := stdscript.DetermineScriptType(txOut.Version, txOut.PkScript) 113 require.Equal(t, gotTyp, expType) 114 return 115 } 116 117 // No output with the given amount was found. 118 t.Fatalf("output with amount %v not found in transaction %v", outputAmt, 119 spew.Sdump(tx)) 120 } 121 122 // assertAccountBalance asserts that the unconfirmed and confirmed balance for 123 // the given account is satisfied by the WalletBalance and ListUnspent RPCs. The 124 // unconfirmed balance is not checked for neutrino nodes. 125 func assertAccountBalance(t *testing.T, node *lntest.HarnessNode, account string, 126 confirmedBalance, unconfirmedBalance int64) { 127 128 err := wait.NoError(func() error { 129 balanceResp, err := node.WalletBalance( 130 context.Background(), &lnrpc.WalletBalanceRequest{}, 131 ) 132 if err != nil { 133 return err 134 } 135 require.Contains(t, balanceResp.AccountBalance, account) 136 accountBalance := balanceResp.AccountBalance[account] 137 138 // Check confirmed balance. 139 if accountBalance.ConfirmedBalance != confirmedBalance { 140 return fmt.Errorf("expected confirmed balance %v, "+ 141 "got %v", confirmedBalance, 142 accountBalance.ConfirmedBalance) 143 } 144 listUtxosReq := &lnrpc.ListUnspentRequest{ 145 MinConfs: 1, 146 MaxConfs: math.MaxInt32, 147 Account: account, 148 } 149 confirmedUtxosResp, err := node.ListUnspent( 150 context.Background(), listUtxosReq, 151 ) 152 if err != nil { 153 return err 154 } 155 var totalConfirmedVal int64 156 for _, utxo := range confirmedUtxosResp.Utxos { 157 totalConfirmedVal += utxo.AmountAtoms 158 } 159 if totalConfirmedVal != confirmedBalance { 160 return fmt.Errorf("expected total confirmed utxo "+ 161 "balance %v, got %v", confirmedBalance, 162 totalConfirmedVal) 163 } 164 165 // Skip unconfirmed balance checks for neutrino nodes. 166 if node.Cfg.BackendCfg.Name() == "spv" { 167 return nil 168 } 169 170 // Check unconfirmed balance. 171 if accountBalance.UnconfirmedBalance != unconfirmedBalance { 172 return fmt.Errorf("expected unconfirmed balance %v, "+ 173 "got %v", unconfirmedBalance, 174 accountBalance.UnconfirmedBalance) 175 } 176 listUtxosReq.MinConfs = 0 177 listUtxosReq.MaxConfs = 0 178 unconfirmedUtxosResp, err := node.ListUnspent( 179 context.Background(), listUtxosReq, 180 ) 181 require.NoError(t, err) 182 var totalUnconfirmedVal int64 183 for _, utxo := range unconfirmedUtxosResp.Utxos { 184 totalUnconfirmedVal += utxo.AmountAtoms 185 } 186 if totalUnconfirmedVal != unconfirmedBalance { 187 return fmt.Errorf("expected total unconfirmed utxo "+ 188 "balance %v, got %v", unconfirmedBalance, 189 totalUnconfirmedVal) 190 } 191 192 return nil 193 }, defaultTimeout) 194 require.NoError(t, err) 195 } 196 197 /* 198 // psbtSendFromImportedAccount attempts to fund a PSBT from the given imported 199 // account, originating from the source node to the destination. 200 func psbtSendFromImportedAccount(t *harnessTest, srcNode, destNode, 201 signer *lntest.HarnessNode, account string, 202 accountAddrType walletrpc.AddressType) { 203 204 ctxb := context.Background() 205 206 ctxt, cancel := context.WithTimeout(ctxb, defaultTimeout) 207 defer cancel() 208 balanceResp, err := srcNode.WalletBalance(ctxt, &lnrpc.WalletBalanceRequest{}) 209 require.NoError(t.t, err) 210 require.Contains(t.t, balanceResp.AccountBalance, account) 211 confBalance := balanceResp.AccountBalance[account].ConfirmedBalance 212 213 destAmt := confBalance - 10000 214 ctxt, cancel = context.WithTimeout(ctxb, defaultTimeout) 215 defer cancel() 216 destAddrResp, err := destNode.NewAddress(ctxt, &lnrpc.NewAddressRequest{ 217 Type: lnrpc.AddressType_WITNESS_PUBKEY_HASH, 218 }) 219 require.NoError(t.t, err) 220 221 ctxt, cancel = context.WithTimeout(ctxb, defaultTimeout) 222 defer cancel() 223 fundReq := &walletrpc.FundPsbtRequest{ 224 Template: &walletrpc.FundPsbtRequest_Raw{ 225 Raw: &walletrpc.TxTemplate{ 226 Outputs: map[string]uint64{ 227 destAddrResp.Address: uint64(destAmt), 228 }, 229 }, 230 }, 231 Fees: &walletrpc.FundPsbtRequest_AtomsPerByte{ 232 AtomsPerByte: 1, 233 }, 234 Account: account, 235 } 236 fundResp, err := srcNode.WalletKitClient.FundPsbt(ctxt, fundReq) 237 require.NoError(t.t, err) 238 239 // Have Carol sign the PSBT input since Dave doesn't have any private 240 // key information. 241 ctxt, cancel = context.WithTimeout(ctxb, defaultTimeout) 242 defer cancel() 243 finalizeReq := &walletrpc.FinalizePsbtRequest{ 244 FundedPsbt: fundResp.FundedPsbt, 245 } 246 finalizeResp, err := signer.WalletKitClient.FinalizePsbt(ctxt, finalizeReq) 247 require.NoError(t.t, err) 248 249 // With the PSBT signed, we can broadcast the resulting transaction. 250 ctxt, cancel = context.WithTimeout(ctxb, defaultTimeout) 251 defer cancel() 252 publishReq := &walletrpc.Transaction{ 253 TxHex: finalizeResp.RawFinalTx, 254 } 255 _, err = srcNode.WalletKitClient.PublishTransaction(ctxt, publishReq) 256 require.NoError(t.t, err) 257 258 // Carol's balance from Dave's perspective should update accordingly. 259 var ( 260 expTxFee int64 261 expChangeScriptType txscript.ScriptClass 262 ) 263 switch accountAddrType { 264 case walletrpc.AddressType_WITNESS_PUBKEY_HASH: 265 expTxFee = 141 266 expChangeScriptType = txscript.WitnessV0PubKeyHashTy 267 268 case walletrpc.AddressType_NESTED_WITNESS_PUBKEY_HASH: 269 if account != defaultImportedAccount { 270 expTxFee = 165 271 expChangeScriptType = txscript.ScriptHashTy 272 break 273 } 274 275 // Spends from the default NP2WKH imported account have the same 276 // fee rate as the hybrid address type since a NP2WKH input is 277 // spent and a P2WKH change output is created. 278 fallthrough 279 280 case walletrpc.AddressType_HYBRID_NESTED_WITNESS_PUBKEY_HASH: 281 expTxFee = 164 282 expChangeScriptType = txscript.WitnessV0PubKeyHashTy 283 284 default: 285 t.Fatalf("unsupported addr type %v", accountAddrType) 286 } 287 changeUtxoAmt := confBalance - destAmt - expTxFee 288 289 // If the transaction was created from the default imported account, 290 // then any change produced is moved to the default wallet account. 291 accountWithBalance := account 292 if account == defaultImportedAccount { 293 accountWithBalance = defaultAccount 294 } 295 assertAccountBalance(t.t, srcNode, accountWithBalance, 0, changeUtxoAmt) 296 _ = mineBlocks(t, t.lndHarness, 1, 1) 297 assertAccountBalance(t.t, srcNode, accountWithBalance, changeUtxoAmt, 0) 298 299 // Finally, assert that the transaction has the expected change address 300 // type based on the account. 301 var tx wire.MsgTx 302 err = tx.Deserialize(bytes.NewReader(finalizeResp.RawFinalTx)) 303 require.NoError(t.t, err) 304 assertOutputScriptType(t.t, expChangeScriptType, &tx, changeUtxoAmt) 305 } 306 307 // fundChanAndCloseFromImportedAccount attempts to a fund a channel from the 308 // given imported account, originating from the source node to the destination 309 // node. To ensure the channel is operational before closing it, a test payment 310 // is made. Several balance assertions are made along the way for the sake of 311 // correctness. 312 func fundChanAndCloseFromImportedAccount(t *harnessTest, srcNode, destNode, 313 signer *lntest.HarnessNode, account string, 314 accountAddrType walletrpc.AddressType, utxoAmt, chanSize int64) { 315 316 ctxb := context.Background() 317 318 // Retrieve the current confirmed balance to make some assertions later 319 // on. 320 ctxt, cancel := context.WithTimeout(ctxb, defaultTimeout) 321 defer cancel() 322 balanceResp, err := srcNode.WalletBalance(ctxt, &lnrpc.WalletBalanceRequest{}) 323 require.NoError(t.t, err) 324 require.Contains(t.t, balanceResp.AccountBalance, account) 325 accountConfBalance := balanceResp. 326 AccountBalance[account].ConfirmedBalance 327 defaultAccountConfBalance := balanceResp. 328 AccountBalance[defaultAccount].ConfirmedBalance 329 330 // Now, start the channel funding process. We'll need to connect both 331 // nodes first. 332 t.lndHarness.EnsureConnected(t.t, srcNode, destNode) 333 334 // The source node will then fund the channel through a PSBT shim. 335 var pendingChanID [32]byte 336 _, err = rand.Read(pendingChanID[:]) 337 require.NoError(t.t, err) 338 339 ctxt, cancel = context.WithTimeout(ctxb, defaultTimeout) 340 defer cancel() 341 chanUpdates, rawPsbt, err := openChannelPsbt( 342 ctxt, srcNode, destNode, lntest.OpenChannelParams{ 343 Amt: dcrutil.Amount(chanSize), 344 FundingShim: &lnrpc.FundingShim{ 345 Shim: &lnrpc.FundingShim_PsbtShim{ 346 PsbtShim: &lnrpc.PsbtShim{ 347 PendingChanId: pendingChanID[:], 348 }, 349 }, 350 }, 351 }, 352 ) 353 require.NoError(t.t, err) 354 355 ctxt, cancel = context.WithTimeout(ctxb, defaultTimeout) 356 defer cancel() 357 fundReq := &walletrpc.FundPsbtRequest{ 358 Template: &walletrpc.FundPsbtRequest_Psbt{ 359 Psbt: rawPsbt, 360 }, 361 Fees: &walletrpc.FundPsbtRequest_AtomsPerByte{ 362 AtomsPerByte: 1, 363 }, 364 Account: account, 365 } 366 fundResp, err := srcNode.WalletKitClient.FundPsbt(ctxt, fundReq) 367 require.NoError(t.t, err) 368 369 _, err = srcNode.FundingStateStep(ctxb, &lnrpc.FundingTransitionMsg{ 370 Trigger: &lnrpc.FundingTransitionMsg_PsbtVerify{ 371 PsbtVerify: &lnrpc.FundingPsbtVerify{ 372 PendingChanId: pendingChanID[:], 373 FundedPsbt: fundResp.FundedPsbt, 374 }, 375 }, 376 }) 377 require.NoError(t.t, err) 378 379 // Now that we have a PSBT to fund the channel, our signer needs to sign 380 // it. 381 ctxt, cancel = context.WithTimeout(ctxb, defaultTimeout) 382 defer cancel() 383 finalizeReq := &walletrpc.FinalizePsbtRequest{ 384 FundedPsbt: fundResp.FundedPsbt, 385 } 386 finalizeResp, err := signer.WalletKitClient.FinalizePsbt(ctxt, finalizeReq) 387 require.NoError(t.t, err) 388 389 // The source node can then submit the signed PSBT and complete the 390 // channel funding process. 391 _, err = srcNode.FundingStateStep(ctxb, &lnrpc.FundingTransitionMsg{ 392 Trigger: &lnrpc.FundingTransitionMsg_PsbtFinalize{ 393 PsbtFinalize: &lnrpc.FundingPsbtFinalize{ 394 PendingChanId: pendingChanID[:], 395 SignedPsbt: finalizeResp.SignedPsbt, 396 }, 397 }, 398 }) 399 require.NoError(t.t, err) 400 401 // We should receive a notification of the channel funding transaction 402 // being broadcast. 403 ctxt, cancel = context.WithTimeout(ctxb, defaultTimeout) 404 defer cancel() 405 updateResp, err := receiveChanUpdate(ctxt, chanUpdates) 406 require.NoError(t.t, err) 407 upd, ok := updateResp.Update.(*lnrpc.OpenStatusUpdate_ChanPending) 408 require.True(t.t, ok) 409 410 // Mine enough blocks to announce the channel to the network, making 411 // balance assertions along the way. 412 var ( 413 expChanTxFee int64 414 expChangeScriptType txscript.ScriptClass 415 ) 416 switch accountAddrType { 417 case walletrpc.AddressType_WITNESS_PUBKEY_HASH: 418 expChanTxFee = 153 419 expChangeScriptType = txscript.WitnessV0PubKeyHashTy 420 421 case walletrpc.AddressType_NESTED_WITNESS_PUBKEY_HASH: 422 if account != defaultImportedAccount { 423 expChanTxFee = 177 424 expChangeScriptType = txscript.ScriptHashTy 425 break 426 } 427 428 // Spends from the default NP2WKH imported account have the same 429 // fee rate as the hybrid address type since a NP2WKH input is 430 // spent and a P2WKH change output is created. 431 fallthrough 432 433 case walletrpc.AddressType_HYBRID_NESTED_WITNESS_PUBKEY_HASH: 434 expChanTxFee = 176 435 expChangeScriptType = txscript.WitnessV0PubKeyHashTy 436 437 default: 438 t.Fatalf("unsupported addr type %v", accountAddrType) 439 } 440 chanChangeUtxoAmt := utxoAmt - chanSize - expChanTxFee 441 txHash, err := chainhash.NewHash(upd.ChanPending.Txid) 442 require.NoError(t.t, err) 443 444 // If we're spending from the default imported account, then any change 445 // outputs produced are moved to the default wallet account, so we 446 // should expect to see balances there. 447 var confBalanceAfterChan int64 448 if account == defaultImportedAccount { 449 confBalanceAfterChan = defaultAccountConfBalance 450 assertAccountBalance(t.t, srcNode, account, 0, 0) 451 assertAccountBalance( 452 t.t, srcNode, defaultAccount, defaultAccountConfBalance, 453 chanChangeUtxoAmt, 454 ) 455 456 block := mineBlocks(t, t.lndHarness, 6, 1)[0] 457 assertTxInBlock(t, block, txHash) 458 459 confBalanceAfterChan += chanChangeUtxoAmt 460 assertAccountBalance(t.t, srcNode, account, 0, 0) 461 assertAccountBalance( 462 t.t, srcNode, defaultAccount, confBalanceAfterChan, 0, 463 ) 464 } else { 465 // Otherwise, all interactions remain within Carol's imported 466 // account. 467 confBalanceAfterChan = accountConfBalance - utxoAmt 468 assertAccountBalance( 469 t.t, srcNode, account, confBalanceAfterChan, 470 chanChangeUtxoAmt, 471 ) 472 473 block := mineBlocks(t, t.lndHarness, 6, 1)[0] 474 assertTxInBlock(t, block, txHash) 475 476 confBalanceAfterChan += chanChangeUtxoAmt 477 assertAccountBalance( 478 t.t, srcNode, account, confBalanceAfterChan, 0, 479 ) 480 } 481 482 // Assert that the transaction has the expected change address type 483 // based on the account. 484 var tx wire.MsgTx 485 err = tx.Deserialize(bytes.NewReader(finalizeResp.RawFinalTx)) 486 require.NoError(t.t, err) 487 assertOutputScriptType(t.t, expChangeScriptType, &tx, chanChangeUtxoAmt) 488 489 // Wait for the channel to be announced by both parties. 490 chanPoint := &lnrpc.ChannelPoint{ 491 FundingTxid: &lnrpc.ChannelPoint_FundingTxidBytes{ 492 FundingTxidBytes: upd.ChanPending.Txid, 493 }, 494 OutputIndex: upd.ChanPending.OutputIndex, 495 } 496 err = srcNode.WaitForNetworkChannelOpen(chanPoint) 497 require.NoError(t.t, err) 498 err = destNode.WaitForNetworkChannelOpen(chanPoint) 499 require.NoError(t.t, err) 500 501 // Send a test payment to ensure the channel is operating as normal. 502 const invoiceAmt = 100000 503 ctxt, cancel = context.WithTimeout(ctxb, defaultTimeout) 504 defer cancel() 505 resp, err := destNode.AddInvoice(ctxt, &lnrpc.Invoice{ 506 Memo: "psbt import chan", 507 Value: invoiceAmt, 508 }) 509 require.NoError(t.t, err) 510 511 err = completePaymentRequests( 512 srcNode, srcNode.RouterClient, 513 []string{resp.PaymentRequest}, true, 514 ) 515 require.NoError(t.t, err) 516 517 // Now that we've confirmed the opened channel works, we'll close it. 518 closeChannelAndAssert(t, t.lndHarness, srcNode, chanPoint, false) 519 520 // Since the channel still had funds left on the source node's side, 521 // they must've been redeemed after the close. Without a pre-negotiated 522 // close address, the funds will go into the source node's wallet 523 // instead of the imported account. 524 const chanCloseTxFee = 9050 525 balanceFromClosedChan := chanSize - invoiceAmt - chanCloseTxFee 526 527 if account == defaultImportedAccount { 528 assertAccountBalance(t.t, srcNode, account, 0, 0) 529 assertAccountBalance( 530 t.t, srcNode, defaultAccount, 531 confBalanceAfterChan+balanceFromClosedChan, 0, 532 ) 533 } else { 534 assertAccountBalance( 535 t.t, srcNode, account, confBalanceAfterChan, 0, 536 ) 537 assertAccountBalance( 538 t.t, srcNode, defaultAccount, balanceFromClosedChan, 0, 539 ) 540 } 541 } 542 */ 543 544 // testWalletImportAccount tests that an imported account can fund transactions 545 // and channels through PSBTs, by having one node (the one with the imported 546 // account) craft the transactions and another node act as the signer. 547 func testWalletImportAccount(net *lntest.NetworkHarness, t *harnessTest) { 548 t.t.Skip("Disabled due to no support for PSBT") 549 /* 550 testCases := []struct { 551 name string 552 addrType walletrpc.AddressType 553 }{ 554 { 555 name: "standard BIP-0049", 556 addrType: walletrpc.AddressType_NESTED_WITNESS_PUBKEY_HASH, 557 }, 558 { 559 name: "lnd BIP-0049 variant", 560 addrType: walletrpc.AddressType_HYBRID_NESTED_WITNESS_PUBKEY_HASH, 561 }, 562 { 563 name: "standard BIP-0084", 564 addrType: walletrpc.AddressType_WITNESS_PUBKEY_HASH, 565 }, 566 } 567 568 for _, tc := range testCases { 569 tc := tc 570 success := t.t.Run(tc.name, func(tt *testing.T) { 571 ht := newHarnessTest(tt, net) 572 ht.RunTestCase(&testCase{ 573 name: tc.name, 574 test: func(net1 *lntest.NetworkHarness, t1 *harnessTest) { 575 testWalletImportAccountScenario( 576 net, t, tc.addrType, 577 ) 578 }, 579 }) 580 }) 581 if !success { 582 // Log failure time to help relate the lnd logs to the 583 // failure. 584 t.Logf("Failure time: %v", time.Now().Format( 585 "2006-01-02 15:04:05.000", 586 )) 587 break 588 } 589 } 590 */ 591 } 592 593 /* 594 func testWalletImportAccountScenario(net *lntest.NetworkHarness, t *harnessTest, 595 addrType walletrpc.AddressType) { 596 597 // We'll start our test by having two nodes, Carol and Dave. Carol's 598 // default wallet account will be imported into Dave's node. 599 carol := net.NewNode(t.t, "carol", nil) 600 defer shutdownAndAssert(net, t, carol) 601 602 dave := net.NewNode(t.t, "dave", nil) 603 defer shutdownAndAssert(net, t, dave) 604 605 runWalletImportAccountScenario(net, t, addrType, carol, dave) 606 } 607 608 func runWalletImportAccountScenario(net *lntest.NetworkHarness, t *harnessTest, 609 addrType walletrpc.AddressType, carol, dave *lntest.HarnessNode) { 610 611 ctxb := context.Background() 612 const utxoAmt int64 = dcrutil.AtomsPerCoin 613 614 ctxt, cancel := context.WithTimeout(ctxb, defaultTimeout) 615 defer cancel() 616 listReq := &walletrpc.ListAccountsRequest{ 617 Name: "default", 618 AddressType: addrType, 619 } 620 listResp, err := carol.WalletKitClient.ListAccounts(ctxt, listReq) 621 require.NoError(t.t, err) 622 require.Equal(t.t, len(listResp.Accounts), 1) 623 carolAccount := listResp.Accounts[0] 624 625 ctxt, cancel = context.WithTimeout(ctxb, defaultTimeout) 626 defer cancel() 627 const importedAccount = "carol" 628 importReq := &walletrpc.ImportAccountRequest{ 629 Name: importedAccount, 630 ExtendedPublicKey: carolAccount.ExtendedPublicKey, 631 AddressType: addrType, 632 } 633 _, err = dave.WalletKitClient.ImportAccount(ctxt, importReq) 634 require.NoError(t.t, err) 635 636 // We'll generate an address for Carol from Dave's node to receive some 637 // funds. 638 externalAddr := newExternalAddr( 639 t.t, dave, carol, importedAccount, addrType, 640 ) 641 642 // Send coins to Carol's address and confirm them, making sure the 643 // balance updates accordingly. 644 ctxt, cancel = context.WithTimeout(ctxb, defaultTimeout) 645 defer cancel() 646 _, err = net.Alice.SendCoins(ctxt, &lnrpc.SendCoinsRequest{ 647 Addr: externalAddr, 648 Amount: utxoAmt, 649 AtomsPerByte: 1, 650 }) 651 require.NoError(t.t, err) 652 653 assertAccountBalance(t.t, dave, importedAccount, 0, utxoAmt) 654 _ = mineBlocks(t, net, 1, 1) 655 assertAccountBalance(t.t, dave, importedAccount, utxoAmt, 0) 656 657 // To ensure that Dave can use Carol's account as watch-only, we'll 658 // construct a PSBT that sends funds to Alice, which we'll then hand 659 // over to Carol to sign. 660 psbtSendFromImportedAccount( 661 t, dave, net.Alice, carol, importedAccount, addrType, 662 ) 663 664 // We'll generate a new address for Carol from Dave's node to receive 665 // and fund a new channel. 666 externalAddr = newExternalAddr( 667 t.t, dave, carol, importedAccount, addrType, 668 ) 669 670 // Retrieve the current confirmed balance of the imported account for 671 // some assertions we'll make later on. 672 ctxt, cancel = context.WithTimeout(ctxb, defaultTimeout) 673 defer cancel() 674 balanceResp, err := dave.WalletBalance(ctxt, &lnrpc.WalletBalanceRequest{}) 675 require.NoError(t.t, err) 676 require.Contains(t.t, balanceResp.AccountBalance, importedAccount) 677 confBalance := balanceResp.AccountBalance[importedAccount].ConfirmedBalance 678 679 // Send coins to Carol's address and confirm them, making sure the 680 // balance updates accordingly. 681 ctxt, cancel = context.WithTimeout(ctxb, defaultTimeout) 682 defer cancel() 683 _, err = net.Alice.SendCoins(ctxt, &lnrpc.SendCoinsRequest{ 684 Addr: externalAddr, 685 Amount: utxoAmt, 686 SatPerByte: 1, 687 }) 688 require.NoError(t.t, err) 689 690 assertAccountBalance(t.t, dave, importedAccount, confBalance, utxoAmt) 691 _ = mineBlocks(t, net, 1, 1) 692 assertAccountBalance( 693 t.t, dave, importedAccount, confBalance+utxoAmt, 0, 694 ) 695 696 // Now that we have enough funds, it's time to fund the channel, make a 697 // test payment, and close it. This contains several balance assertions 698 // along the way. 699 fundChanAndCloseFromImportedAccount( 700 t, dave, net.Alice, carol, importedAccount, addrType, utxoAmt, 701 int64(dcrlnd.MaxFundingAmount), 702 ) 703 } 704 */ 705 706 // testWalletImportPubKey tests that an imported public keys can fund 707 // transactions and channels through PSBTs, by having one node (the one with the 708 // imported account) craft the transactions and another node act as the signer. 709 func testWalletImportPubKey(net *lntest.NetworkHarness, t *harnessTest) { 710 t.t.Skip("Disabled due to no support for PSBT") 711 /* 712 testCases := []struct { 713 name string 714 addrType walletrpc.AddressType 715 }{ 716 { 717 name: "BIP-0049", 718 addrType: walletrpc.AddressType_NESTED_WITNESS_PUBKEY_HASH, 719 }, 720 { 721 name: "BIP-0084", 722 addrType: walletrpc.AddressType_WITNESS_PUBKEY_HASH, 723 }, 724 } 725 726 for _, tc := range testCases { 727 tc := tc 728 success := t.t.Run(tc.name, func(tt *testing.T) { 729 ht := newHarnessTest(tt, net) 730 ht.RunTestCase(&testCase{ 731 name: tc.name, 732 test: func(net1 *lntest.NetworkHarness, t1 *harnessTest) { 733 testWalletImportPubKeyScenario( 734 net, t, tc.addrType, 735 ) 736 }, 737 }) 738 }) 739 if !success { 740 // Log failure time to help relate the lnd logs to the 741 // failure. 742 t.Logf("Failure time: %v", time.Now().Format( 743 "2006-01-02 15:04:05.000", 744 )) 745 break 746 } 747 } 748 */ 749 } 750 751 /* 752 func testWalletImportPubKeyScenario(net *lntest.NetworkHarness, t *harnessTest, 753 addrType walletrpc.AddressType) { 754 755 ctxb := context.Background() 756 const utxoAmt int64 = dcrutil.AtomsPerCoin 757 758 // We'll start our test by having two nodes, Carol and Dave. 759 carol := net.NewNode(t.t, "carol", nil) 760 defer shutdownAndAssert(net, t, carol) 761 762 dave := net.NewNode(t.t, "dave", nil) 763 defer shutdownAndAssert(net, t, dave) 764 765 // We'll define a helper closure that we'll use throughout the test to 766 // generate a new address of the given type from Carol's perspective, 767 // import it into Dave's wallet, and fund it. 768 importPubKey := func(keyIndex uint32, prevConfBalance, prevUnconfBalance int64) { 769 // Retrieve Carol's account public key for the corresponding 770 // address type. 771 ctxt, cancel := context.WithTimeout(ctxb, defaultTimeout) 772 defer cancel() 773 listReq := &walletrpc.ListAccountsRequest{ 774 Name: "default", 775 AddressType: addrType, 776 } 777 listResp, err := carol.WalletKitClient.ListAccounts(ctxt, listReq) 778 require.NoError(t.t, err) 779 require.Equal(t.t, len(listResp.Accounts), 1) 780 p2wkhAccount := listResp.Accounts[0] 781 782 // Derive the external address at the given index. 783 accountPubKey, err := hdkeychain.NewKeyFromString( 784 p2wkhAccount.ExtendedPublicKey, 785 ) 786 require.NoError(t.t, err) 787 externalAccountExtKey, err := accountPubKey.Derive(0) 788 require.NoError(t.t, err) 789 externalAddrExtKey, err := externalAccountExtKey.Derive(keyIndex) 790 require.NoError(t.t, err) 791 externalAddrPubKey, err := externalAddrExtKey.ECPubKey() 792 require.NoError(t.t, err) 793 794 // Import the public key into Dave. 795 ctxt, cancel = context.WithTimeout(ctxb, defaultTimeout) 796 defer cancel() 797 importReq := &walletrpc.ImportPublicKeyRequest{ 798 PublicKey: externalAddrPubKey.SerializeCompressed(), 799 AddressType: addrType, 800 } 801 _, err = dave.WalletKitClient.ImportPublicKey(ctxt, importReq) 802 require.NoError(t.t, err) 803 804 // We'll also generate the same address for Carol, as it'll be 805 // required later when signing. 806 ctxt, cancel = context.WithTimeout(ctxb, defaultTimeout) 807 defer cancel() 808 carolAddrResp, err := carol.NewAddress(ctxt, &lnrpc.NewAddressRequest{ 809 Type: walletToLNAddrType(t.t, addrType), 810 }) 811 require.NoError(t.t, err) 812 813 // Send coins to Carol's address and confirm them, making sure 814 // the balance updates accordingly. 815 ctxt, cancel = context.WithTimeout(ctxb, defaultTimeout) 816 defer cancel() 817 _, err = net.Alice.SendCoins(ctxt, &lnrpc.SendCoinsRequest{ 818 Addr: carolAddrResp.Address, 819 Amount: utxoAmt, 820 SatPerByte: 1, 821 }) 822 require.NoError(t.t, err) 823 824 assertAccountBalance( 825 t.t, dave, defaultImportedAccount, prevConfBalance, 826 prevUnconfBalance+utxoAmt, 827 ) 828 _ = mineBlocks(t, net, 1, 1) 829 assertAccountBalance( 830 t.t, dave, defaultImportedAccount, 831 prevConfBalance+utxoAmt, prevUnconfBalance, 832 ) 833 } 834 835 // We'll have Carol generate a new external address, which we'll import 836 // into Dave. 837 importPubKey(0, 0, 0) 838 839 // To ensure that Dave can use Carol's public key as watch-only, we'll 840 // construct a PSBT that sends funds to Alice, which we'll then hand 841 // over to Carol to sign. 842 psbtSendFromImportedAccount( 843 t, dave, net.Alice, carol, defaultImportedAccount, addrType, 844 ) 845 846 // We'll now attempt to fund a channel. 847 // 848 // We'll have Carol generate another external address, which we'll 849 // import into Dave again. 850 ctxt, cancel := context.WithTimeout(ctxb, defaultTimeout) 851 defer cancel() 852 balanceResp, err := dave.WalletBalance(ctxt, &lnrpc.WalletBalanceRequest{}) 853 require.NoError(t.t, err) 854 require.Contains(t.t, balanceResp.AccountBalance, defaultImportedAccount) 855 confBalance := balanceResp. 856 AccountBalance[defaultImportedAccount].ConfirmedBalance 857 importPubKey(1, confBalance, 0) 858 859 // Now that we have enough funds, it's time to fund the channel, make a 860 // test payment, and close it. This contains several balance assertions 861 // along the way. 862 fundChanAndCloseFromImportedAccount( 863 t, dave, net.Alice, carol, defaultImportedAccount, addrType, 864 utxoAmt, int64(dcrlnd.MaxFundingAmount), 865 ) 866 } 867 */