Skip to content

Commit

Permalink
lnd: add max fee rate check to closechannel rpc
Browse files Browse the repository at this point in the history
  • Loading branch information
ziggie1984 committed Feb 12, 2025
1 parent 6666356 commit 34322f3
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 0 deletions.
4 changes: 4 additions & 0 deletions itest/list_on_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -602,6 +602,10 @@ var allTestCases = []*lntest.TestCase{
Name: "coop close with htlcs",
TestFunc: testCoopCloseWithHtlcs,
},
{
Name: "coop close exceeds max fee",
TestFunc: testCoopCloseExceedsMaxFee,
},
{
Name: "open channel locked balance",
TestFunc: testOpenChannelLockedBalance,
Expand Down
69 changes: 69 additions & 0 deletions itest/lnd_coop_close_with_htlcs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/lightningnetwork/lnd/lntest"
"github.com/lightningnetwork/lnd/lntest/wait"
"github.com/lightningnetwork/lnd/lntypes"
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
"github.com/stretchr/testify/require"
)

Expand Down Expand Up @@ -243,3 +244,71 @@ func coopCloseWithHTLCsWithRestart(ht *lntest.HarnessTest) {
// Show that the address used is the one she requested.
require.Equal(ht, outputDetail.Address, newAddr.Address)
}

// testCoopCloseExceedsMaxFee tests that we fail the coop close process if
// the max fee rate exceeds the expected fee rate for the initial closing fee
// proposal.
func testCoopCloseExceedsMaxFee(ht *lntest.HarnessTest) {
alice := ht.NewNodeWithCoins("Alice", nil)
bob := ht.NewNodeWithCoins("bob", nil)
ht.ConnectNodes(alice, bob)

// Here we set up a channel between Alice and Bob, beginning with a
// balance on Bob's side.
chanPoint := ht.OpenChannel(bob, alice, lntest.OpenChannelParams{
Amt: btcutil.Amount(1000000),
})

// Wait for Bob to understand that the channel is ready to use.
ht.AssertChannelInGraph(bob, chanPoint)

// Set the fee estimate for one block to 10 sat/vbyte.
ht.SetFeeEstimateWithConf(chainfee.SatPerVByte(10).FeePerKWeight(), 1)

// Have alice attempt to close the channel but we the expected fee rate
// exceeds the max fee rate so we fail the closing process.
closeClient := alice.RPC.CloseChannel(&lnrpc.CloseChannelRequest{
ChannelPoint: chanPoint,
NoWait: true,
TargetConf: 1,
MaxFeePerVbyte: 5,
})
_, err := closeClient.Recv()
require.Error(ht, err)

// Now close the channel with a appropriate max fee rate.
closeClient = alice.RPC.CloseChannel(&lnrpc.CloseChannelRequest{
ChannelPoint: chanPoint,
NoWait: true,
TargetConf: 1,
MaxFeePerVbyte: 10,
})

// Pull the instant update off the wire to clear the path for the
// close pending update.
_, err = closeClient.Recv()
require.NoError(ht, err)

// Wait for the channel to be closed.
update, err := closeClient.Recv()
require.NoError(ht, err)

// This next update should be a GetClosePending as it should be the
// negotiation of the coop close tx.
closePending := update.GetClosePending()
require.NotNil(ht, closePending)

// Convert the txid we get from the PendingUpdate to a Hash so we can
// wait for it to be mined.
var closeTxid chainhash.Hash
require.NoError(
ht, closeTxid.SetBytes(closePending.Txid),
"invalid closing txid",
)

// Wait for the close tx to be in the Mempool.
ht.AssertTxInMempool(closeTxid)

// Wait for it to get mined and finish tearing down.
ht.AssertStreamChannelCoopClosed(alice, chanPoint, false, closeClient)
}
10 changes: 10 additions & 0 deletions rpcserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -2872,6 +2872,13 @@ func (r *rpcServer) CloseChannel(in *lnrpc.CloseChannelRequest,
maxFee := chainfee.SatPerKVByte(
in.MaxFeePerVbyte * 1000,
).FeePerKWeight()

if maxFee != 0 && maxFee < feeRate {
return fmt.Errorf("max_fee_per_vbyte (%d) is less "+
"than the required fee rate (%d)", maxFee,
feeRate)
}

updateChan, errChan = r.server.htlcSwitch.CloseLink(
chanPoint, contractcourt.CloseRegular, feeRate,
maxFee, deliveryScript,
Expand All @@ -2896,7 +2903,9 @@ out:
case err := <-errChan:
rpcsLog.Errorf("[closechannel] unable to close "+
"ChannelPoint(%v): %v", chanPoint, err)

return err

case closingUpdate := <-updateChan:
rpcClosingUpdate, err := createRPCCloseUpdate(
closingUpdate,
Expand Down Expand Up @@ -2935,6 +2944,7 @@ out:
"txid(%v)", h)
break out
}

case <-r.quit:
return nil
}
Expand Down

0 comments on commit 34322f3

Please sign in to comment.