mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-05-05 09:20:21 +02:00
The itest has both sides try to close multiple times, each time with increasing fee rates. We also test the reconnection case, bad RBF updates, and instances where the local party can't actually pay for fees.
134 lines
4.8 KiB
Go
134 lines
4.8 KiB
Go
package itest
|
|
|
|
import (
|
|
"github.com/btcsuite/btcd/btcutil"
|
|
"github.com/lightningnetwork/lnd/lntest"
|
|
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func testCoopCloseRbf(ht *lntest.HarnessTest) {
|
|
rbfCoopFlags := []string{"--protocol.rbf-coop-close"}
|
|
|
|
// Set the fee estimate to 1sat/vbyte. This ensures that our manually
|
|
// initiated RBF attempts will always be successful.
|
|
ht.SetFeeEstimate(250)
|
|
ht.SetFeeEstimateWithConf(250, 6)
|
|
|
|
// To kick things off, we'll create two new nodes, then fund them with
|
|
// enough coins to make a 50/50 channel.
|
|
cfgs := [][]string{rbfCoopFlags, rbfCoopFlags}
|
|
params := lntest.OpenChannelParams{
|
|
Amt: btcutil.Amount(1000000),
|
|
PushAmt: btcutil.Amount(1000000 / 2),
|
|
}
|
|
chanPoints, nodes := ht.CreateSimpleNetwork(cfgs, params)
|
|
alice, bob := nodes[0], nodes[1]
|
|
chanPoint := chanPoints[0]
|
|
|
|
// Now that both sides are active with a funded channel, we can kick
|
|
// off the test.
|
|
//
|
|
// To start, we'll have Alice try to close the channel, with a fee rate
|
|
// of 5 sat/byte.
|
|
aliceFeeRate := chainfee.SatPerVByte(5)
|
|
aliceCloseStream, aliceCloseUpdate := ht.CloseChannelAssertPending(
|
|
alice, chanPoint, false,
|
|
lntest.WithCoopCloseFeeRate(aliceFeeRate),
|
|
lntest.WithLocalTxNotify(),
|
|
)
|
|
|
|
// Confirm that this new update was at 5 sat/vb.
|
|
alicePendingUpdate := aliceCloseUpdate.GetClosePending()
|
|
require.NotNil(ht, aliceCloseUpdate)
|
|
require.Equal(
|
|
ht, int64(aliceFeeRate), alicePendingUpdate.FeePerVbyte,
|
|
)
|
|
require.True(ht, alicePendingUpdate.LocalCloseTx)
|
|
|
|
// Now, we'll have Bob attempt to RBF the close transaction with a
|
|
// higher fee rate, double that of Alice's.
|
|
bobFeeRate := aliceFeeRate * 2
|
|
bobCloseStream, bobCloseUpdate := ht.CloseChannelAssertPending(
|
|
bob, chanPoint, false, lntest.WithCoopCloseFeeRate(bobFeeRate),
|
|
lntest.WithLocalTxNotify(),
|
|
)
|
|
|
|
// Confirm that this new update was at 10 sat/vb.
|
|
bobPendingUpdate := bobCloseUpdate.GetClosePending()
|
|
require.NotNil(ht, bobCloseUpdate)
|
|
require.Equal(ht, bobPendingUpdate.FeePerVbyte, int64(bobFeeRate))
|
|
require.True(ht, bobPendingUpdate.LocalCloseTx)
|
|
|
|
var err error
|
|
|
|
// Alice should've also received a similar update that Bob has
|
|
// increased the closing fee rate to 10 sat/vb with his settled funds.
|
|
aliceCloseUpdate, err = ht.ReceiveCloseChannelUpdate(aliceCloseStream)
|
|
require.NoError(ht, err)
|
|
alicePendingUpdate = aliceCloseUpdate.GetClosePending()
|
|
require.NotNil(ht, aliceCloseUpdate)
|
|
require.Equal(ht, alicePendingUpdate.FeePerVbyte, int64(bobFeeRate))
|
|
require.False(ht, alicePendingUpdate.LocalCloseTx)
|
|
|
|
// We'll now attempt to make a fee update that increases Alice's fee
|
|
// rate by 6 sat/vb, which should be rejected as it is too small of an
|
|
// increase for the RBF rules. The RPC API however will return the new
|
|
// fee. We'll skip the mempool check here as it won't make it in.
|
|
aliceRejectedFeeRate := aliceFeeRate + 1
|
|
_, aliceCloseUpdate = ht.CloseChannelAssertPending(
|
|
alice, chanPoint, false,
|
|
lntest.WithCoopCloseFeeRate(aliceRejectedFeeRate),
|
|
lntest.WithLocalTxNotify(), lntest.WithSkipMempoolCheck(),
|
|
)
|
|
alicePendingUpdate = aliceCloseUpdate.GetClosePending()
|
|
require.NotNil(ht, aliceCloseUpdate)
|
|
require.Equal(
|
|
ht, alicePendingUpdate.FeePerVbyte,
|
|
int64(aliceRejectedFeeRate),
|
|
)
|
|
require.True(ht, alicePendingUpdate.LocalCloseTx)
|
|
|
|
_, err = ht.ReceiveCloseChannelUpdate(bobCloseStream)
|
|
require.NoError(ht, err)
|
|
|
|
// We'll now attempt a fee update that we can't actually pay for. This
|
|
// will actually show up as an error to the remote party.
|
|
aliceRejectedFeeRate = 100_000
|
|
_, _ = ht.CloseChannelAssertPending(
|
|
alice, chanPoint, false,
|
|
lntest.WithCoopCloseFeeRate(aliceRejectedFeeRate),
|
|
lntest.WithLocalTxNotify(),
|
|
lntest.WithExpectedErrString("cannot pay for fee"),
|
|
)
|
|
|
|
// At this point, we'll have Alice+Bob reconnect so we can ensure that
|
|
// we can continue to do RBF bumps even after a reconnection.
|
|
ht.DisconnectNodes(alice, bob)
|
|
ht.ConnectNodes(alice, bob)
|
|
|
|
// Next, we'll have Alice double that fee rate again to 20 sat/vb.
|
|
aliceFeeRate = bobFeeRate * 2
|
|
aliceCloseStream, aliceCloseUpdate = ht.CloseChannelAssertPending(
|
|
alice, chanPoint, false,
|
|
lntest.WithCoopCloseFeeRate(aliceFeeRate),
|
|
lntest.WithLocalTxNotify(),
|
|
)
|
|
|
|
alicePendingUpdate = aliceCloseUpdate.GetClosePending()
|
|
require.NotNil(ht, aliceCloseUpdate)
|
|
require.Equal(
|
|
ht, alicePendingUpdate.FeePerVbyte, int64(aliceFeeRate),
|
|
)
|
|
require.True(ht, alicePendingUpdate.LocalCloseTx)
|
|
|
|
// To conclude, we'll mine a block which should now confirm Alice's
|
|
// version of the coop close transaction.
|
|
block := ht.MineBlocksAndAssertNumTxes(1, 1)[0]
|
|
|
|
// Both Alice and Bob should trigger a final close update to signal the
|
|
// closing transaction has confirmed.
|
|
aliceClosingTxid := ht.WaitForChannelCloseEvent(aliceCloseStream)
|
|
ht.AssertTxInBlock(block, aliceClosingTxid)
|
|
}
|