lnwallet/channel: account for HTLC fee when reporting available balance
This commit is contained in:
parent
5e89d5b6c2
commit
9ff79ae595
@ -1982,8 +1982,11 @@ func TestChannelLinkBandwidthConsistency(t *testing.T) {
|
||||
)
|
||||
|
||||
// The starting bandwidth of the channel should be exactly the amount
|
||||
// that we created the channel between her and Bob.
|
||||
expectedBandwidth := lnwire.NewMSatFromSatoshis(chanAmt - defaultCommitFee)
|
||||
// that we created the channel between her and Bob, minus the
|
||||
// commitment fee and fee for adding an additional HTLC.
|
||||
expectedBandwidth := lnwire.NewMSatFromSatoshis(
|
||||
chanAmt-defaultCommitFee,
|
||||
) - htlcFee
|
||||
assertLinkBandwidth(t, aliceLink, expectedBandwidth)
|
||||
|
||||
// Next, we'll create an HTLC worth 1 BTC, and send it into the link as
|
||||
@ -2656,8 +2659,10 @@ func TestChannelLinkTrimCircuitsPending(t *testing.T) {
|
||||
|
||||
// The starting bandwidth of the channel should be exactly the amount
|
||||
// that we created the channel between her and Bob, minus the commitment
|
||||
// fee.
|
||||
expectedBandwidth := lnwire.NewMSatFromSatoshis(chanAmt - defaultCommitFee)
|
||||
// fee and fee of adding an HTLC.
|
||||
expectedBandwidth := lnwire.NewMSatFromSatoshis(
|
||||
chanAmt-defaultCommitFee,
|
||||
) - htlcFee
|
||||
assertLinkBandwidth(t, alice.link, expectedBandwidth)
|
||||
|
||||
// Capture Alice's starting bandwidth to perform later, relative
|
||||
@ -2935,8 +2940,10 @@ func TestChannelLinkTrimCircuitsNoCommit(t *testing.T) {
|
||||
|
||||
// The starting bandwidth of the channel should be exactly the amount
|
||||
// that we created the channel between her and Bob, minus the commitment
|
||||
// fee.
|
||||
expectedBandwidth := lnwire.NewMSatFromSatoshis(chanAmt - defaultCommitFee)
|
||||
// fee and fee for adding an additional HTLC.
|
||||
expectedBandwidth := lnwire.NewMSatFromSatoshis(
|
||||
chanAmt-defaultCommitFee,
|
||||
) - htlcFee
|
||||
assertLinkBandwidth(t, alice.link, expectedBandwidth)
|
||||
|
||||
// Capture Alice's starting bandwidth to perform later, relative
|
||||
@ -3191,9 +3198,9 @@ func TestChannelLinkBandwidthChanReserve(t *testing.T) {
|
||||
|
||||
// The starting bandwidth of the channel should be exactly the amount
|
||||
// that we created the channel between her and Bob, minus the channel
|
||||
// reserve.
|
||||
// reserve, commitment fee and fee for adding an additional HTLC.
|
||||
expectedBandwidth := lnwire.NewMSatFromSatoshis(
|
||||
chanAmt - defaultCommitFee - chanReserve)
|
||||
chanAmt-defaultCommitFee-chanReserve) - htlcFee
|
||||
assertLinkBandwidth(t, aliceLink, expectedBandwidth)
|
||||
|
||||
// Next, we'll create an HTLC worth 3 BTC, and send it into the link as
|
||||
|
@ -6068,20 +6068,30 @@ func (lc *LightningChannel) availableCommitmentBalance(view *htlcView) (
|
||||
ourBalance = 0
|
||||
}
|
||||
|
||||
// Given the commitment weight, find the commitment fee in case of no
|
||||
// added HTLC output.
|
||||
// Calculate the commitment fee in the case where we would add another
|
||||
// HTLC to the commitment, as only the balance remaining after this fee
|
||||
// has been paid is actually available for sending.
|
||||
feePerKw := filteredView.feePerKw
|
||||
baseCommitFee := lnwire.NewMSatFromSatoshis(
|
||||
feePerKw.FeeForWeight(commitWeight),
|
||||
htlcCommitFee := lnwire.NewMSatFromSatoshis(
|
||||
feePerKw.FeeForWeight(commitWeight + input.HTLCWeight),
|
||||
)
|
||||
|
||||
// If we are the channel initiator, we must to subtract the commitment
|
||||
// fee from our available balance.
|
||||
// If we are the channel initiator, we must to subtract this commitment
|
||||
// fee from our available balance in order to ensure we can afford both
|
||||
// the value of the HTLC and the additional commitment fee from adding
|
||||
// the HTLC.
|
||||
if lc.channelState.IsInitiator {
|
||||
if ourBalance < baseCommitFee {
|
||||
// There is an edge case where our non-zero balance is lower
|
||||
// than the htlcCommitFee, where we could still be sending dust
|
||||
// HTLCs, but we return 0 in this case. This is to avoid
|
||||
// lowering our balance even further, as this takes us into a
|
||||
// bad state wehere neither we nor our channel counterparty can
|
||||
// add HTLCs.
|
||||
if ourBalance < htlcCommitFee {
|
||||
return 0, commitWeight
|
||||
}
|
||||
ourBalance -= baseCommitFee
|
||||
|
||||
ourBalance -= htlcCommitFee
|
||||
}
|
||||
|
||||
return ourBalance, commitWeight
|
||||
|
@ -4602,6 +4602,12 @@ func TestChanAvailableBandwidth(t *testing.T) {
|
||||
aliceReserve := lnwire.NewMSatFromSatoshis(
|
||||
aliceChannel.channelState.LocalChanCfg.ChanReserve,
|
||||
)
|
||||
feeRate := chainfee.SatPerKWeight(
|
||||
aliceChannel.channelState.LocalCommitment.FeePerKw,
|
||||
)
|
||||
htlcFee := lnwire.NewMSatFromSatoshis(
|
||||
feeRate.FeeForWeight(input.HTLCWeight),
|
||||
)
|
||||
|
||||
assertBandwidthEstimateCorrect := func(aliceInitiate bool) {
|
||||
// With the HTLC's added, we'll now query the AvailableBalance
|
||||
@ -4631,8 +4637,9 @@ func TestChanAvailableBandwidth(t *testing.T) {
|
||||
aliceBalance := aliceChannel.channelState.LocalCommitment.LocalBalance
|
||||
|
||||
// The balance we have available for new HTLCs should be the
|
||||
// current local commitment balance, minus the channel reserve.
|
||||
expBalance := aliceBalance - aliceReserve
|
||||
// current local commitment balance, minus the channel reserve
|
||||
// and the fee for adding an HTLC.
|
||||
expBalance := aliceBalance - aliceReserve - htlcFee
|
||||
if expBalance != aliceAvailableBalance {
|
||||
_, _, line, _ := runtime.Caller(1)
|
||||
t.Fatalf("line: %v, incorrect balance: expected %v, "+
|
||||
@ -4714,6 +4721,130 @@ func TestChanAvailableBandwidth(t *testing.T) {
|
||||
// TODO(roasbeef): additional tests from diff starting conditions
|
||||
}
|
||||
|
||||
// TestChanAvailableBalanceNearHtlcFee checks that we get the expected reported
|
||||
// balance when it is close to the htlc fee.
|
||||
func TestChanAvailableBalanceNearHtlcFee(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Create a test channel which will be used for the duration of this
|
||||
// unittest. The channel will be funded evenly with Alice having 5 BTC,
|
||||
// and Bob having 5 BTC.
|
||||
aliceChannel, bobChannel, cleanUp, err := CreateTestChannels(true)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create test channels: %v", err)
|
||||
}
|
||||
defer cleanUp()
|
||||
|
||||
// Alice starts with half the channel capacity.
|
||||
aliceBalance := lnwire.NewMSatFromSatoshis(5 * btcutil.SatoshiPerBitcoin)
|
||||
|
||||
aliceReserve := lnwire.NewMSatFromSatoshis(
|
||||
aliceChannel.channelState.LocalChanCfg.ChanReserve,
|
||||
)
|
||||
|
||||
aliceDustlimit := lnwire.NewMSatFromSatoshis(
|
||||
aliceChannel.channelState.LocalChanCfg.DustLimit,
|
||||
)
|
||||
feeRate := chainfee.SatPerKWeight(
|
||||
aliceChannel.channelState.LocalCommitment.FeePerKw,
|
||||
)
|
||||
htlcFee := lnwire.NewMSatFromSatoshis(
|
||||
feeRate.FeeForWeight(input.HTLCWeight),
|
||||
)
|
||||
commitFee := lnwire.NewMSatFromSatoshis(
|
||||
aliceChannel.channelState.LocalCommitment.CommitFee,
|
||||
)
|
||||
htlcTimeoutFee := lnwire.NewMSatFromSatoshis(
|
||||
htlcTimeoutFee(feeRate),
|
||||
)
|
||||
|
||||
// Helper method to check the current reported balance.
|
||||
checkBalance := func(t *testing.T, expBalanceAlice lnwire.MilliSatoshi) {
|
||||
t.Helper()
|
||||
balance := aliceChannel.AvailableBalance()
|
||||
if balance != expBalanceAlice {
|
||||
t.Fatalf("Expected balance %v, got %v", expBalanceAlice,
|
||||
balance)
|
||||
}
|
||||
}
|
||||
|
||||
// Helper method to send an HTLC from Alice to Bob, decreasing Alice's
|
||||
// balance.
|
||||
htlcIndex := uint64(0)
|
||||
sendHtlc := func(htlcAmt lnwire.MilliSatoshi) {
|
||||
t.Helper()
|
||||
|
||||
htlc, preImage := createHTLC(int(htlcIndex), htlcAmt)
|
||||
if _, err := aliceChannel.AddHTLC(htlc, nil); err != nil {
|
||||
t.Fatalf("unable to add htlc: %v", err)
|
||||
}
|
||||
if _, err := bobChannel.ReceiveHTLC(htlc); err != nil {
|
||||
t.Fatalf("unable to recv htlc: %v", err)
|
||||
}
|
||||
|
||||
if err := ForceStateTransition(aliceChannel, bobChannel); err != nil {
|
||||
t.Fatalf("unable to complete alice's state "+
|
||||
"transition: %v", err)
|
||||
}
|
||||
|
||||
err = bobChannel.SettleHTLC(preImage, htlcIndex, nil, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to settle htlc: %v", err)
|
||||
}
|
||||
err = aliceChannel.ReceiveHTLCSettle(preImage, htlcIndex)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to settle htlc: %v", err)
|
||||
}
|
||||
|
||||
if err := ForceStateTransition(aliceChannel, bobChannel); err != nil {
|
||||
t.Fatalf("unable to complete alice's state "+
|
||||
"transition: %v", err)
|
||||
}
|
||||
|
||||
htlcIndex++
|
||||
aliceBalance -= htlcAmt
|
||||
}
|
||||
|
||||
// Balance should start out equal to half the channel capacity minus
|
||||
// the commitment fee Alice must pay and the channel reserve. In
|
||||
// addition the HTLC fee will be subtracted fromt the balance to
|
||||
// reflect that this value must be reserved for any payment above the
|
||||
// dust limit.
|
||||
expAliceBalance := aliceBalance - commitFee - aliceReserve - htlcFee
|
||||
checkBalance(t, expAliceBalance)
|
||||
|
||||
// Find the minumim size of a non-dust HTLC.
|
||||
aliceNonDustHtlc := aliceDustlimit + htlcTimeoutFee
|
||||
|
||||
// Send a HTLC leaving the remaining balance just enough to have
|
||||
// nonDustHtlc left after paying the commit fee and htlc fee.
|
||||
htlcAmt := aliceBalance - (commitFee + aliceReserve + htlcFee + aliceNonDustHtlc)
|
||||
sendHtlc(htlcAmt)
|
||||
|
||||
// Now the real balance left will be
|
||||
// nonDustHtlc+commitfee+aliceReserve+htlcfee. The available balance
|
||||
// reported will just be nonDustHtlc, since the rest of the balance is
|
||||
// reserved.
|
||||
expAliceBalance = aliceNonDustHtlc
|
||||
checkBalance(t, expAliceBalance)
|
||||
|
||||
// Send an HTLC using all but one msat of the reported balance.
|
||||
htlcAmt = aliceNonDustHtlc - 1
|
||||
sendHtlc(htlcAmt)
|
||||
|
||||
// 1 msat should be left.
|
||||
expAliceBalance = 1
|
||||
checkBalance(t, expAliceBalance)
|
||||
|
||||
// Sendng the last msat.
|
||||
htlcAmt = 1
|
||||
sendHtlc(htlcAmt)
|
||||
|
||||
// No balance left.
|
||||
expAliceBalance = 0
|
||||
checkBalance(t, expAliceBalance)
|
||||
}
|
||||
|
||||
// TestSignCommitmentFailNotLockedIn tests that a channel will not attempt to
|
||||
// create a new state if it doesn't yet know of the next revocation point for
|
||||
// the remote party.
|
||||
|
Loading…
Reference in New Issue
Block a user