diff --git a/lnwallet/channel.go b/lnwallet/channel.go index 0cc512d5..01be6117 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -6030,9 +6030,19 @@ func (lc *LightningChannel) availableBalance() (lnwire.MilliSatoshi, int64) { // add updates concurrently, causing our balance to go down if we're // the initiator, but this is a problem on the protocol level. ourLocalCommitBalance, commitWeight := lc.availableCommitmentBalance( - htlcView, + htlcView, false, ) + // Do the same calculation from the remote commitment point of view. + ourRemoteCommitBalance, _ := lc.availableCommitmentBalance( + htlcView, true, + ) + + // Return which ever balance is lowest. + if ourRemoteCommitBalance < ourLocalCommitBalance { + return ourRemoteCommitBalance, commitWeight + } + return ourLocalCommitBalance, commitWeight } @@ -6043,14 +6053,14 @@ func (lc *LightningChannel) availableBalance() (lnwire.MilliSatoshi, int64) { // commitment, increasing the commitment fee we must pay as an initiator, // eating into our balance. It will make sure we won't violate the channel // reserve constraints for this amount. -func (lc *LightningChannel) availableCommitmentBalance(view *htlcView) ( - lnwire.MilliSatoshi, int64) { +func (lc *LightningChannel) availableCommitmentBalance(view *htlcView, + remoteChain bool) (lnwire.MilliSatoshi, int64) { // Compute the current balances for this commitment. This will take // into account HTLCs to determine the commit weight, which the // initiator must pay the fee for. ourBalance, theirBalance, commitWeight, filteredView, err := lc.computeView( - view, false, false, + view, remoteChain, false, ) if err != nil { lc.log.Errorf("Unable to fetch available balance: %v", err) @@ -6118,6 +6128,17 @@ func (lc *LightningChannel) availableCommitmentBalance(view *htlcView) ( htlcTimeoutFee(feePerKw), ) + // If we are looking at the remote commitment, we must use the remote + // dust limit and the fee for adding an HTLC success transaction. + if remoteChain { + dustlimit = lnwire.NewMSatFromSatoshis( + lc.channelState.RemoteChanCfg.DustLimit, + ) + htlcFee = lnwire.NewMSatFromSatoshis( + htlcSuccessFee(feePerKw), + ) + } + // The HTLC output will be manifested on the commitment if it // is non-dust after paying the HTLC fee. nonDustHtlcAmt := dustlimit + htlcFee diff --git a/lnwallet/channel_test.go b/lnwallet/channel_test.go index be739520..c4c88670 100644 --- a/lnwallet/channel_test.go +++ b/lnwallet/channel_test.go @@ -4749,9 +4749,6 @@ func TestChanAvailableBalanceNearHtlcFee(t *testing.T) { aliceDustlimit := lnwire.NewMSatFromSatoshis( aliceChannel.channelState.LocalChanCfg.DustLimit, ) - bobDustlimit := lnwire.NewMSatFromSatoshis( - bobChannel.channelState.LocalChanCfg.DustLimit, - ) feeRate := chainfee.SatPerKWeight( aliceChannel.channelState.LocalCommitment.FeePerKw, ) @@ -4764,6 +4761,9 @@ func TestChanAvailableBalanceNearHtlcFee(t *testing.T) { htlcTimeoutFee := lnwire.NewMSatFromSatoshis( htlcTimeoutFee(feeRate), ) + htlcSuccessFee := lnwire.NewMSatFromSatoshis( + htlcSuccessFee(feeRate), + ) // Helper method to check the current reported balance. checkBalance := func(t *testing.T, expBalanceAlice, @@ -4889,7 +4889,12 @@ func TestChanAvailableBalanceNearHtlcFee(t *testing.T) { // HTLC from Bob to the commitment transaction. Bob's balance should // reflect this, by only reporting dust amount being available. Alice // should still report a zero balance. - bobNonDustHtlc := bobDustlimit + htlcTimeoutFee + + // Since the dustlimit is different for the two commitments, the + // largest HTLC Bob can send that Alice can afford on both commitments + // (remember she cannot afford to pay the HTLC fee) is the largest dust + // HTLC on Alice's commitemnt, since her dust limit is lower. + bobNonDustHtlc := aliceDustlimit + htlcSuccessFee expBobBalance = bobNonDustHtlc - 1 expAliceBalance = 0 checkBalance(t, expAliceBalance, expBobBalance)