multi: refactor testSweepAllCoins

This commit is contained in:
yyforyongyu 2022-08-02 20:56:13 +08:00
parent 00006adeda
commit bbe69f1bd3
No known key found for this signature in database
GPG Key ID: 9BCD95C4FF296868
7 changed files with 157 additions and 114 deletions

View File

@ -971,6 +971,22 @@ func (h *HarnessTest) FundCoinsUnconfirmed(amt btcutil.Amount,
h.fundCoins(amt, hn, lnrpc.AddressType_WITNESS_PUBKEY_HASH, false)
}
// FundCoinsNP2WKH attempts to send amt satoshis from the internal mining node
// to the targeted lightning node using a NP2WKH address.
func (h *HarnessTest) FundCoinsNP2WKH(amt btcutil.Amount,
target *node.HarnessNode) {
h.fundCoins(amt, target, lnrpc.AddressType_NESTED_PUBKEY_HASH, true)
}
// FundCoinsP2TR attempts to send amt satoshis from the internal mining node to
// the targeted lightning node using a P2TR address.
func (h *HarnessTest) FundCoinsP2TR(amt btcutil.Amount,
target *node.HarnessNode) {
h.fundCoins(amt, target, lnrpc.AddressType_TAPROOT_PUBKEY, true)
}
// CompletePaymentRequests sends payments from a node to complete all payment
// requests. This function does not return until all payments successfully
// complete without errors.

View File

@ -376,3 +376,10 @@ func (h *HarnessMiner) GetNumTxsFromMempool(n int) []*wire.MsgTx {
return txes
}
// NewMinerAddress creates a new address for the miner and asserts.
func (h *HarnessMiner) NewMinerAddress() btcutil.Address {
addr, err := h.NewAddress()
require.NoError(h, err, "failed to create new miner address")
return addr
}

View File

@ -319,3 +319,39 @@ func (h *HarnessRPC) ChannelAcceptor() (AcceptorClient, context.CancelFunc) {
return resp, cancel
}
// SendCoins sends a given amount of money to the specified address from the
// passed node.
func (h *HarnessRPC) SendCoins(
req *lnrpc.SendCoinsRequest) *lnrpc.SendCoinsResponse {
ctxt, cancel := context.WithTimeout(h.runCtx, DefaultTimeout)
defer cancel()
resp, err := h.LN.SendCoins(ctxt, req)
require.NoErrorf(h, err, "node %s failed to send coins to address %s",
h.Name, req.Addr)
return resp
}
// SendCoinsAssertErr sends a given amount of money to the specified address
// from the passed node and asserts an error has returned.
func (h *HarnessRPC) SendCoinsAssertErr(req *lnrpc.SendCoinsRequest) {
ctxt, cancel := context.WithTimeout(h.runCtx, DefaultTimeout)
defer cancel()
_, err := h.LN.SendCoins(ctxt, req)
require.Error(h, err, "node %s didn't not return an error", h.Name)
}
// GetTransactions makes a RPC call to GetTransactions and asserts.
func (h *HarnessRPC) GetTransactions() *lnrpc.TransactionDetails {
ctxt, cancel := context.WithTimeout(h.runCtx, DefaultTimeout)
defer cancel()
resp, err := h.LN.GetTransactions(ctxt, &lnrpc.GetTransactionsRequest{})
require.NoErrorf(h, err, "failed to GetTransactions for %s", h.Name)
return resp
}

View File

@ -5,6 +5,7 @@ import (
"github.com/lightningnetwork/lnd/lnrpc/signrpc"
"github.com/lightningnetwork/lnd/lnrpc/walletrpc"
"github.com/stretchr/testify/require"
)
// =====================
@ -73,3 +74,27 @@ func (h *HarnessRPC) FinalizePsbt(
return resp
}
// LabelTransactionAssertErr makes a RPC call to the node's LabelTransaction
// and asserts an error is returned. It then returns the error.
func (h *HarnessRPC) LabelTransactionAssertErr(
req *walletrpc.LabelTransactionRequest) error {
ctxt, cancel := context.WithTimeout(h.runCtx, DefaultTimeout)
defer cancel()
_, err := h.WalletKit.LabelTransaction(ctxt, req)
require.Error(h, err, "expected error returned")
return err
}
// LabelTransaction makes a RPC call to the node's LabelTransaction
// and asserts no error is returned.
func (h *HarnessRPC) LabelTransaction(req *walletrpc.LabelTransactionRequest) {
ctxt, cancel := context.WithTimeout(h.runCtx, DefaultTimeout)
defer cancel()
_, err := h.WalletKit.LabelTransaction(ctxt, req)
h.NoError(err, "LabelTransaction")
}

View File

@ -71,4 +71,8 @@ var allTestCasesTemp = []*lntemp.TestCase{
Name: "data loss protection",
TestFunc: testDataLossProtection,
},
{
Name: "sweep coins",
TestFunc: testSweepAllCoins,
},
}

View File

@ -22,6 +22,7 @@ import (
"github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/lnrpc/routerrpc"
"github.com/lightningnetwork/lnd/lnrpc/walletrpc"
"github.com/lightningnetwork/lnd/lntemp"
"github.com/lightningnetwork/lnd/lntest"
"github.com/lightningnetwork/lnd/lntest/wait"
"github.com/lightningnetwork/lnd/lnwallet"
@ -1180,26 +1181,23 @@ func testAbandonChannel(net *lntest.NetworkHarness, t *harnessTest) {
// testSweepAllCoins tests that we're able to properly sweep all coins from the
// wallet into a single target address at the specified fee rate.
func testSweepAllCoins(net *lntest.NetworkHarness, t *harnessTest) {
ctxb := context.Background()
//
// TODO(yy): expand this test to also use P2TR.
func testSweepAllCoins(ht *lntemp.HarnessTest) {
// First, we'll make a new node, ainz who'll we'll use to test wallet
// sweeping.
ainz := net.NewNode(t.t, "Ainz", nil)
defer shutdownAndAssert(net, t, ainz)
//
// NOTE: we won't use standby nodes here since the test will change
// each of the node's wallet state.
ainz := ht.NewNode("Ainz", nil)
// Next, we'll give Ainz exactly 2 utxos of 1 BTC each, with one of
// them being p2wkh and the other being a n2wpkh address.
net.SendCoins(t.t, btcutil.SatoshiPerBitcoin, ainz)
net.SendCoinsNP2WKH(t.t, btcutil.SatoshiPerBitcoin, ainz)
ht.FundCoins(btcutil.SatoshiPerBitcoin, ainz)
ht.FundCoinsNP2WKH(btcutil.SatoshiPerBitcoin, ainz)
// Ensure that we can't send coins to our own Pubkey.
ctxt, _ := context.WithTimeout(ctxb, defaultTimeout)
info, err := ainz.GetInfo(ctxt, &lnrpc.GetInfoRequest{})
if err != nil {
t.Fatalf("unable to get node info: %v", err)
}
info := ainz.RPC.GetInfo()
// Create a label that we will used to label the transaction with.
sendCoinsLabel := "send all coins"
@ -1209,171 +1207,132 @@ func testSweepAllCoins(net *lntest.NetworkHarness, t *harnessTest) {
SendAll: true,
Label: sendCoinsLabel,
}
_, err = ainz.SendCoins(ctxt, sweepReq)
if err == nil {
t.Fatalf("expected SendCoins to users own pubkey to fail")
}
ainz.RPC.SendCoinsAssertErr(sweepReq)
// Ensure that we can't send coins to another users Pubkey.
info, err = net.Alice.GetInfo(ctxt, &lnrpc.GetInfoRequest{})
if err != nil {
t.Fatalf("unable to get node info: %v", err)
}
// Ensure that we can't send coins to another user's Pubkey.
info = ht.Alice.RPC.GetInfo()
sweepReq = &lnrpc.SendCoinsRequest{
Addr: info.IdentityPubkey,
SendAll: true,
Label: sendCoinsLabel,
}
_, err = ainz.SendCoins(ctxt, sweepReq)
if err == nil {
t.Fatalf("expected SendCoins to Alices pubkey to fail")
}
ainz.RPC.SendCoinsAssertErr(sweepReq)
// With the two coins above mined, we'll now instruct ainz to sweep all
// the coins to an external address not under its control.
// We will first attempt to send the coins to addresses that are not
// compatible with the current network. This is to test that the wallet
// will prevent any onchain transactions to addresses that are not on the
// the coins to an external address not under its control. We will
// first attempt to send the coins to addresses that are not compatible
// with the current network. This is to test that the wallet will
// prevent any onchain transactions to addresses that are not on the
// same network as the user.
// Send coins to a testnet3 address.
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
sweepReq = &lnrpc.SendCoinsRequest{
Addr: "tb1qfc8fusa98jx8uvnhzavxccqlzvg749tvjw82tg",
SendAll: true,
Label: sendCoinsLabel,
}
_, err = ainz.SendCoins(ctxt, sweepReq)
if err == nil {
t.Fatalf("expected SendCoins to different network to fail")
}
ainz.RPC.SendCoinsAssertErr(sweepReq)
// Send coins to a mainnet address.
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
sweepReq = &lnrpc.SendCoinsRequest{
Addr: "1MPaXKp5HhsLNjVSqaL7fChE3TVyrTMRT3",
SendAll: true,
Label: sendCoinsLabel,
}
_, err = ainz.SendCoins(ctxt, sweepReq)
if err == nil {
t.Fatalf("expected SendCoins to different network to fail")
}
ainz.RPC.SendCoinsAssertErr(sweepReq)
// Send coins to a compatible address.
minerAddr, err := net.Miner.NewAddress()
if err != nil {
t.Fatalf("unable to create new miner addr: %v", err)
}
minerAddr := ht.Miner.NewMinerAddress()
sweepReq = &lnrpc.SendCoinsRequest{
Addr: minerAddr.String(),
SendAll: true,
Label: sendCoinsLabel,
}
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
_, err = ainz.SendCoins(ctxt, sweepReq)
if err != nil {
t.Fatalf("unable to sweep coins: %v", err)
}
ainz.RPC.SendCoins(sweepReq)
// We'll mine a block which should include the sweep transaction we
// generated above.
block := mineBlocks(t, net, 1, 1)[0]
block := ht.MineBlocksAndAssertNumTxes(1, 1)[0]
// The sweep transaction should have exactly two inputs as we only had
// two UTXOs in the wallet.
sweepTx := block.Transactions[1]
if len(sweepTx.TxIn) != 2 {
t.Fatalf("expected 2 inputs instead have %v", len(sweepTx.TxIn))
require.Len(ht, sweepTx.TxIn, 2, "expected 2 inputs")
// assertTxLabel is a helper function which finds a target tx in our
// set of transactions and checks that it has the desired label.
assertTxLabel := func(targetTx, label string) {
// List all transactions relevant to our wallet, and find the
// tx so that we can check the correct label has been set.
txResp := ainz.RPC.GetTransactions()
// Find our transaction in the set of transactions returned and
// check its label.
for _, txn := range txResp.Transactions {
if txn.TxHash == targetTx {
require.Equal(ht, label, txn.Label,
"labels not match")
}
}
}
sweepTxStr := sweepTx.TxHash().String()
assertTxLabel(t, ainz, sweepTxStr, sendCoinsLabel)
assertTxLabel(sweepTxStr, sendCoinsLabel)
// While we are looking at labels, we test our label transaction command
// to make sure it is behaving as expected. First, we try to label our
// transaction with an empty label, and check that we fail as expected.
// While we are looking at labels, we test our label transaction
// command to make sure it is behaving as expected. First, we try to
// label our transaction with an empty label, and check that we fail as
// expected.
sweepHash := sweepTx.TxHash()
_, err = ainz.WalletKitClient.LabelTransaction(
ctxt, &walletrpc.LabelTransactionRequest{
Txid: sweepHash[:],
Label: "",
Overwrite: false,
},
)
if err == nil {
t.Fatalf("expected error for zero transaction label")
req := &walletrpc.LabelTransactionRequest{
Txid: sweepHash[:],
Label: "",
Overwrite: false,
}
err := ainz.RPC.LabelTransactionAssertErr(req)
// Our error will be wrapped in a rpc error, so we check that it
// contains the error we expect.
errZeroLabel := "cannot label transaction with empty label"
if !strings.Contains(err.Error(), errZeroLabel) {
t.Fatalf("expected: zero label error, got: %v", err)
}
require.Contains(ht, err.Error(), errZeroLabel,
"expected: zero label errorv")
// Next, we try to relabel our transaction without setting the overwrite
// boolean. We expect this to fail, because the wallet requires setting
// of this param to prevent accidental overwrite of labels.
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
_, err = ainz.WalletKitClient.LabelTransaction(
ctxt, &walletrpc.LabelTransactionRequest{
Txid: sweepHash[:],
Label: "label that will not work",
Overwrite: false,
},
)
if err == nil {
t.Fatalf("expected error for tx already labelled")
req = &walletrpc.LabelTransactionRequest{
Txid: sweepHash[:],
Label: "label that will not work",
Overwrite: false,
}
err = ainz.RPC.LabelTransactionAssertErr(req)
// Our error will be wrapped in a rpc error, so we check that it
// contains the error we expect.
if !strings.Contains(err.Error(), wallet.ErrTxLabelExists.Error()) {
t.Fatalf("expected: label exists, got: %v", err)
}
require.Contains(ht, err.Error(), wallet.ErrTxLabelExists.Error())
// Finally, we overwrite our label with a new label, which should not
// fail.
newLabel := "new sweep tx label"
_, err = ainz.WalletKitClient.LabelTransaction(
ctxt, &walletrpc.LabelTransactionRequest{
Txid: sweepHash[:],
Label: newLabel,
Overwrite: true,
},
)
if err != nil {
t.Fatalf("could not label tx: %v", err)
req = &walletrpc.LabelTransactionRequest{
Txid: sweepHash[:],
Label: newLabel,
Overwrite: true,
}
ainz.RPC.LabelTransaction(req)
assertTxLabel(t, ainz, sweepTxStr, newLabel)
assertTxLabel(sweepTxStr, newLabel)
// Finally, Ainz should now have no coins at all within his wallet.
balReq := &lnrpc.WalletBalanceRequest{}
resp, err := ainz.WalletBalance(ctxt, balReq)
if err != nil {
t.Fatalf("unable to get ainz's balance: %v", err)
}
switch {
case resp.ConfirmedBalance != 0:
t.Fatalf("expected no confirmed balance, instead have %v",
resp.ConfirmedBalance)
case resp.UnconfirmedBalance != 0:
t.Fatalf("expected no unconfirmed balance, instead have %v",
resp.UnconfirmedBalance)
}
resp := ainz.RPC.WalletBalance()
require.Zero(ht, resp.ConfirmedBalance, "wrong confirmed balance")
require.Zero(ht, resp.UnconfirmedBalance, "wrong unconfirmed balance")
// If we try again, but this time specifying an amount, then the call
// should fail.
sweepReq.Amount = 10000
_, err = ainz.SendCoins(ctxt, sweepReq)
if err == nil {
t.Fatalf("sweep attempt should fail")
}
ainz.RPC.SendCoinsAssertErr(sweepReq)
}
// testListAddresses tests that we get all the addresses and their

View File

@ -4,10 +4,6 @@
package itest
var allTestCases = []*testCase{
{
name: "sweep coins",
test: testSweepAllCoins,
},
{
name: "list addresses",
test: testListAddresses,