link+lnwallet: move bandwidth channel reserve validation into channel

Since we want to handle the edge case where paying the HTLC fee would
take the initiator below the reserve, we move the subtraction of the
reserve into availableBalance where this calculation will be performed.
This commit is contained in:
Johan T. Halseth 2020-02-19 12:27:42 +01:00
parent 58dec10680
commit 5e89d5b6c2
No known key found for this signature in database
GPG Key ID: 15BAADA29DA20D26
3 changed files with 33 additions and 16 deletions

@ -2127,26 +2127,21 @@ func (l *channelLink) ChanID() lnwire.ChannelID {
// //
// NOTE: Part of the ChannelLink interface. // NOTE: Part of the ChannelLink interface.
func (l *channelLink) Bandwidth() lnwire.MilliSatoshi { func (l *channelLink) Bandwidth() lnwire.MilliSatoshi {
// Get the balance available on the channel for new HTLCs. This takes
// the channel reserve into account so HTLCs up to this value won't
// violate it.
channelBandwidth := l.channel.AvailableBalance() channelBandwidth := l.channel.AvailableBalance()
overflowBandwidth := l.overflowQueue.TotalHtlcAmount()
// To compute the total bandwidth, we'll take the current available // To compute the total bandwidth, we'll take the current available
// bandwidth, then subtract the overflow bandwidth as we'll eventually // bandwidth, then subtract the overflow bandwidth as we'll eventually
// also need to evaluate those HTLC's once space on the commitment // also need to evaluate those HTLC's once space on the commitment
// transaction is free. // transaction is free.
linkBandwidth := channelBandwidth - overflowBandwidth overflowBandwidth := l.overflowQueue.TotalHtlcAmount()
if channelBandwidth < overflowBandwidth {
// If the channel reserve is greater than the total available balance
// of the link, just return 0.
reserve := lnwire.NewMSatFromSatoshis(l.channel.LocalChanReserve())
if linkBandwidth < reserve {
return 0 return 0
} }
// Else the amount that is available to flow through the link at this return channelBandwidth - overflowBandwidth
// point is the available balance minus the reserve amount we are
// required to keep as collateral.
return linkBandwidth - reserve
} }
// AttachMailBox updates the current mailbox used by this link, and hooks up // AttachMailBox updates the current mailbox used by this link, and hooks up

@ -6057,6 +6057,17 @@ func (lc *LightningChannel) availableCommitmentBalance(view *htlcView) (
return 0, 0 return 0, 0
} }
// We can never spend from the channel reserve, so we'll subtract it
// from our available balance.
ourReserve := lnwire.NewMSatFromSatoshis(
lc.channelState.LocalChanCfg.ChanReserve,
)
if ourReserve <= ourBalance {
ourBalance -= ourReserve
} else {
ourBalance = 0
}
// Given the commitment weight, find the commitment fee in case of no // Given the commitment weight, find the commitment fee in case of no
// added HTLC output. // added HTLC output.
feePerKw := filteredView.feePerKw feePerKw := filteredView.feePerKw
@ -6067,6 +6078,9 @@ func (lc *LightningChannel) availableCommitmentBalance(view *htlcView) (
// If we are the channel initiator, we must to subtract the commitment // If we are the channel initiator, we must to subtract the commitment
// fee from our available balance. // fee from our available balance.
if lc.channelState.IsInitiator { if lc.channelState.IsInitiator {
if ourBalance < baseCommitFee {
return 0, commitWeight
}
ourBalance -= baseCommitFee ourBalance -= baseCommitFee
} }
@ -6278,13 +6292,14 @@ func (lc *LightningChannel) MaxFeeRate(maxAllocation float64) chainfee.SatPerKWe
// The maximum fee depends of the available balance that can be // The maximum fee depends of the available balance that can be
// committed towards fees. // committed towards fees.
balance, weight := lc.availableBalance() commit := lc.channelState.LocalCommitment
feeBalance := float64( feeBalance := float64(
balance.ToSatoshis() + lc.channelState.LocalCommitment.CommitFee, commit.LocalBalance.ToSatoshis() + commit.CommitFee,
) )
maxFee := feeBalance * maxAllocation maxFee := feeBalance * maxAllocation
// Ensure the fee rate doesn't dip below the fee floor. // Ensure the fee rate doesn't dip below the fee floor.
_, weight := lc.availableBalance()
maxFeeRate := maxFee / (float64(weight) / 1000) maxFeeRate := maxFee / (float64(weight) / 1000)
return chainfee.SatPerKWeight( return chainfee.SatPerKWeight(
math.Max(maxFeeRate, float64(chainfee.FeePerKwFloor)), math.Max(maxFeeRate, float64(chainfee.FeePerKwFloor)),

@ -4599,6 +4599,10 @@ func TestChanAvailableBandwidth(t *testing.T) {
} }
defer cleanUp() defer cleanUp()
aliceReserve := lnwire.NewMSatFromSatoshis(
aliceChannel.channelState.LocalChanCfg.ChanReserve,
)
assertBandwidthEstimateCorrect := func(aliceInitiate bool) { assertBandwidthEstimateCorrect := func(aliceInitiate bool) {
// With the HTLC's added, we'll now query the AvailableBalance // With the HTLC's added, we'll now query the AvailableBalance
// method for the current available channel bandwidth from // method for the current available channel bandwidth from
@ -4625,11 +4629,14 @@ func TestChanAvailableBandwidth(t *testing.T) {
// Now, we'll obtain the current available bandwidth in Alice's // Now, we'll obtain the current available bandwidth in Alice's
// latest commitment and compare that to the prior estimate. // latest commitment and compare that to the prior estimate.
aliceBalance := aliceChannel.channelState.LocalCommitment.LocalBalance aliceBalance := aliceChannel.channelState.LocalCommitment.LocalBalance
if aliceBalance != aliceAvailableBalance {
// The balance we have available for new HTLCs should be the
// current local commitment balance, minus the channel reserve.
expBalance := aliceBalance - aliceReserve
if expBalance != aliceAvailableBalance {
_, _, line, _ := runtime.Caller(1) _, _, line, _ := runtime.Caller(1)
t.Fatalf("line: %v, incorrect balance: expected %v, "+ t.Fatalf("line: %v, incorrect balance: expected %v, "+
"got %v", line, aliceBalance, "got %v", line, expBalance, aliceAvailableBalance)
aliceAvailableBalance)
} }
} }