mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-02-22 06:21:40 +01:00
itest: add testSendSelectedCoins
to check selected utxos
This commit is contained in:
parent
0041426e7e
commit
e542351149
2 changed files with 200 additions and 0 deletions
|
@ -49,6 +49,14 @@ var allTestCases = []*lntest.TestCase{
|
|||
Name: "send all coins",
|
||||
TestFunc: testSendAllCoins,
|
||||
},
|
||||
{
|
||||
Name: "send selected coins",
|
||||
TestFunc: testSendSelectedCoins,
|
||||
},
|
||||
{
|
||||
Name: "send selected coins channel reserve",
|
||||
TestFunc: testSendSelectedCoinsChannelReserve,
|
||||
},
|
||||
{
|
||||
Name: "disconnecting target peer",
|
||||
TestFunc: testDisconnectingTargetPeer,
|
||||
|
|
|
@ -1308,3 +1308,195 @@ func testNativeSQLNoMigration(ht *lntest.HarnessTest) {
|
|||
alice.SetExtraArgs(nil)
|
||||
require.NoError(ht, alice.Start(ht.Context()))
|
||||
}
|
||||
|
||||
// testSendSelectedCoins tests that we're able to properly send the selected
|
||||
// coins from the wallet to a single target address.
|
||||
func testSendSelectedCoins(ht *lntest.HarnessTest) {
|
||||
// First, we'll make a new node, Alice who'll we'll use to test wallet
|
||||
// sweeping.
|
||||
alice := ht.NewNode("Alice", nil)
|
||||
|
||||
// Next, we'll give Alice exactly 3 utxos of 1 BTC of different address
|
||||
// types.
|
||||
ht.FundCoins(btcutil.SatoshiPerBitcoin, alice)
|
||||
ht.FundCoinsNP2WKH(btcutil.SatoshiPerBitcoin, alice)
|
||||
ht.FundCoinsP2TR(btcutil.SatoshiPerBitcoin, alice)
|
||||
|
||||
// Get all the utxos in the wallet and assert there are three.
|
||||
utxos := ht.AssertNumUTXOs(alice, 3)
|
||||
|
||||
// Ensure that we can't send duplicate coins.
|
||||
//
|
||||
// Create duplciate outpoints.
|
||||
dupOutpoints := []*lnrpc.OutPoint{
|
||||
utxos[0].Outpoint,
|
||||
utxos[0].Outpoint,
|
||||
}
|
||||
|
||||
// Send the duplicate outpoints and assert there's an error.
|
||||
err := alice.RPC.SendCoinsAssertErr(&lnrpc.SendCoinsRequest{
|
||||
Addr: ht.NewMinerAddress().String(),
|
||||
Outpoints: dupOutpoints,
|
||||
})
|
||||
require.ErrorContains(ht, err, "selected outpoints contain duplicate")
|
||||
|
||||
// Send a selected coin with a specific amount.
|
||||
//
|
||||
// We'll send the first utxo with an amount of 0.5 BTC.
|
||||
amt := btcutil.Amount(0.5 * btcutil.SatoshiPerBitcoin)
|
||||
alice.RPC.SendCoins(&lnrpc.SendCoinsRequest{
|
||||
Addr: ht.NewMinerAddress().String(),
|
||||
Outpoints: []*lnrpc.OutPoint{
|
||||
utxos[0].Outpoint,
|
||||
},
|
||||
Amount: int64(amt),
|
||||
})
|
||||
|
||||
// We expect to see the above tx in the mempool.
|
||||
tx := ht.GetNumTxsFromMempool(1)[0]
|
||||
|
||||
// Assert the tx has the expected shape. It should have 1 input and 2
|
||||
// outputs - the input is the selected UTXO, and the outputs are the
|
||||
// specified amount and the change amount.
|
||||
require.Len(ht, tx.TxIn, 1)
|
||||
require.Len(ht, tx.TxOut, 2)
|
||||
|
||||
// Check it's using the selected UTXO as input.
|
||||
require.Equal(ht, utxos[0].Outpoint.TxidStr,
|
||||
tx.TxIn[0].PreviousOutPoint.Hash.String())
|
||||
|
||||
// Mine a block to confirm the above tx.
|
||||
ht.MineBlocksAndAssertNumTxes(1, 1)
|
||||
|
||||
// We now test we can send the selected coins to a single target
|
||||
// address with `SendAll` flag.
|
||||
//
|
||||
// Get all the utxos in the wallet and assert there are three.
|
||||
utxos = ht.AssertNumUTXOs(alice, 3)
|
||||
|
||||
// Select the first two coins and send them to a single target address.
|
||||
alice.RPC.SendCoins(&lnrpc.SendCoinsRequest{
|
||||
Addr: ht.NewMinerAddress().String(),
|
||||
Outpoints: []*lnrpc.OutPoint{
|
||||
utxos[0].Outpoint,
|
||||
utxos[1].Outpoint,
|
||||
},
|
||||
SendAll: true,
|
||||
})
|
||||
|
||||
// We expect to see the above tx in the mempool.
|
||||
tx = ht.GetNumTxsFromMempool(1)[0]
|
||||
|
||||
// Assert the tx has the expected shape. It should have 2 inputs and 1
|
||||
// output.
|
||||
require.Len(ht, tx.TxIn, 2)
|
||||
require.Len(ht, tx.TxOut, 1)
|
||||
|
||||
// Check it's using the selected UTXOs as inputs.
|
||||
prevOutpoint1 := tx.TxIn[0].PreviousOutPoint.Hash.String()
|
||||
prevOutpoint2 := tx.TxIn[1].PreviousOutPoint.Hash.String()
|
||||
|
||||
if prevOutpoint1 == utxos[0].Outpoint.TxidStr {
|
||||
require.Equal(ht, utxos[1].Outpoint.TxidStr, prevOutpoint2)
|
||||
} else {
|
||||
require.Equal(ht, utxos[1].Outpoint.TxidStr, prevOutpoint1)
|
||||
require.Equal(ht, utxos[0].Outpoint.TxidStr, prevOutpoint2)
|
||||
}
|
||||
|
||||
// Mine a block to confirm the above tx.
|
||||
ht.MineBlocksAndAssertNumTxes(1, 1)
|
||||
|
||||
// Since the first two UTXOs have been sent to an address outside her
|
||||
// wallet, Alice should see a single UTXO now.
|
||||
ht.AssertNumUTXOs(alice, 1)
|
||||
}
|
||||
|
||||
// testSendSelectedCoinsChannelReserve tests that if sending selected coins
|
||||
// would violate the channel reserve requirement the RPC call will fail. It
|
||||
// also checks that change outputs will be created automatically if `SendAll`
|
||||
// flag is set.
|
||||
func testSendSelectedCoinsChannelReserve(ht *lntest.HarnessTest) {
|
||||
chanAmt := btcutil.Amount(100_000)
|
||||
|
||||
// Create a two-hop network: Alice -> Bob.
|
||||
//
|
||||
// NOTE: Alice will have one UTXO after the funding.
|
||||
_, nodes := createSimpleNetwork(
|
||||
ht, []string{"--protocol.anchors"}, 2,
|
||||
lntest.OpenChannelParams{
|
||||
Amt: chanAmt,
|
||||
},
|
||||
)
|
||||
|
||||
alice := nodes[0]
|
||||
|
||||
// Fund Alice one more UTXO.
|
||||
ht.FundCoins(btcutil.SatoshiPerBitcoin, alice)
|
||||
|
||||
// Get all the utxos in the wallet and assert there are two:
|
||||
// - one from the above `FundCoins`.
|
||||
// - one from the change output after the channel funding.
|
||||
utxos := ht.AssertNumUTXOs(alice, 2)
|
||||
|
||||
// Get all the outpoints.
|
||||
outpoints := []*lnrpc.OutPoint{
|
||||
utxos[0].Outpoint,
|
||||
utxos[1].Outpoint,
|
||||
}
|
||||
|
||||
// Calculate the total amount of the two UTXOs.
|
||||
totalAmt := utxos[0].AmountSat + utxos[1].AmountSat
|
||||
|
||||
// Send the total amount of the two UTXOs and expect an error since
|
||||
// fees cannot be covered.
|
||||
err := alice.RPC.SendCoinsAssertErr(&lnrpc.SendCoinsRequest{
|
||||
Addr: ht.NewMinerAddress().String(),
|
||||
Outpoints: outpoints,
|
||||
Amount: totalAmt,
|
||||
})
|
||||
require.ErrorContains(ht, err, "insufficient funds available to "+
|
||||
"construct transaction")
|
||||
|
||||
// Get the required reserve amount.
|
||||
resp := alice.RPC.RequiredReserve(
|
||||
&walletrpc.RequiredReserveRequest{
|
||||
AdditionalPublicChannels: 1,
|
||||
},
|
||||
)
|
||||
|
||||
// Calculate an amount which, after sending it, would violate the
|
||||
// channel reserve requirement.
|
||||
amt := totalAmt - resp.RequiredReserve
|
||||
|
||||
// Send the amount and expect an error since the channel reserve cannot
|
||||
// be covered.
|
||||
err = alice.RPC.SendCoinsAssertErr(&lnrpc.SendCoinsRequest{
|
||||
Addr: ht.NewMinerAddress().String(),
|
||||
Outpoints: outpoints,
|
||||
Amount: amt,
|
||||
})
|
||||
require.ErrorContains(ht, err, "reserved wallet balance invalidated")
|
||||
|
||||
// Finally, check that we can send all the selected coins with the help
|
||||
// of the `SendAll` flag as it will automatically handle reserving
|
||||
// change outputs based on the channel reserve requirements.
|
||||
alice.RPC.SendCoins(&lnrpc.SendCoinsRequest{
|
||||
Addr: ht.NewMinerAddress().String(),
|
||||
Outpoints: outpoints,
|
||||
SendAll: true,
|
||||
})
|
||||
|
||||
// We expect to see the above tx in the mempool.
|
||||
tx := ht.GetNumTxsFromMempool(1)[0]
|
||||
|
||||
// Assert the tx has the expected shape. It should have 2 inputs and 2
|
||||
// outputs.
|
||||
require.Len(ht, tx.TxIn, 2)
|
||||
require.Len(ht, tx.TxOut, 2)
|
||||
|
||||
// Mine a block to confirm the above tx.
|
||||
ht.MineBlocksAndAssertNumTxes(1, 1)
|
||||
|
||||
// Alice should have one reserved UTXO now.
|
||||
ht.AssertNumUTXOs(alice, 1)
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue