mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-03-27 18:22:24 +01:00
multi: refactor testSweepAllCoins
This commit is contained in:
parent
00006adeda
commit
bbe69f1bd3
@ -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.
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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")
|
||||
}
|
||||
|
@ -71,4 +71,8 @@ var allTestCasesTemp = []*lntemp.TestCase{
|
||||
Name: "data loss protection",
|
||||
TestFunc: testDataLossProtection,
|
||||
},
|
||||
{
|
||||
Name: "sweep coins",
|
||||
TestFunc: testSweepAllCoins,
|
||||
},
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -4,10 +4,6 @@
|
||||
package itest
|
||||
|
||||
var allTestCases = []*testCase{
|
||||
{
|
||||
name: "sweep coins",
|
||||
test: testSweepAllCoins,
|
||||
},
|
||||
{
|
||||
name: "list addresses",
|
||||
test: testListAddresses,
|
||||
|
Loading…
x
Reference in New Issue
Block a user