lnwallet: take remote initiator's balance into account

When we send non-dust HTLCs as the non-initiator, the remote party will
have to pay the extra commitment fee. To account for this we figure out
if they can afford paying this fee, if not we report that we only have
balance available for dust HTLCs, since these HTLCs won't increase the
commitment fee.
This commit is contained in:
Johan T. Halseth 2020-02-19 12:27:42 +01:00
parent 9ff79ae595
commit f94464d987
No known key found for this signature in database
GPG Key ID: 15BAADA29DA20D26
2 changed files with 98 additions and 13 deletions

@ -6049,7 +6049,7 @@ func (lc *LightningChannel) availableCommitmentBalance(view *htlcView) (
// 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, _, commitWeight, filteredView, err := lc.computeView(
ourBalance, theirBalance, commitWeight, filteredView, err := lc.computeView(
view, false, false,
)
if err != nil {
@ -6091,7 +6091,42 @@ func (lc *LightningChannel) availableCommitmentBalance(view *htlcView) (
return 0, commitWeight
}
ourBalance -= htlcCommitFee
return ourBalance - htlcCommitFee, commitWeight
}
// If we're not the initiator, we must check whether the remote has
// enough balance to pay for the fee of our HTLC. We'll start by also
// subtracting our counterparty's reserve from their balance.
theirReserve := lnwire.NewMSatFromSatoshis(
lc.channelState.RemoteChanCfg.ChanReserve,
)
if theirReserve <= theirBalance {
theirBalance -= theirReserve
} else {
theirBalance = 0
}
// We'll use the dustlimit and htlcFee to find the largest HTLC value
// that will be considered dust on the commitment.
dustlimit := lnwire.NewMSatFromSatoshis(
lc.channelState.LocalChanCfg.DustLimit,
)
// For an extra HTLC fee to be paid on our commitment, the HTLC must be
// large enough to make a non-dust HTLC timeout transaction.
htlcFee := lnwire.NewMSatFromSatoshis(
htlcTimeoutFee(feePerKw),
)
// The HTLC output will be manifested on the commitment if it
// is non-dust after paying the HTLC fee.
nonDustHtlcAmt := dustlimit + htlcFee
// If they cannot pay the fee if we add another non-dust HTLC, we'll
// report our available balance just below the non-dust amount, to
// avoid attempting HTLCs larger than this size.
if theirBalance < htlcCommitFee && ourBalance >= nonDustHtlcAmt {
ourBalance = nonDustHtlcAmt - 1
}
return ourBalance, commitWeight

@ -4735,16 +4735,23 @@ func TestChanAvailableBalanceNearHtlcFee(t *testing.T) {
}
defer cleanUp()
// Alice starts with half the channel capacity.
// Alice and Bob start with half the channel capacity.
aliceBalance := lnwire.NewMSatFromSatoshis(5 * btcutil.SatoshiPerBitcoin)
bobBalance := lnwire.NewMSatFromSatoshis(5 * btcutil.SatoshiPerBitcoin)
aliceReserve := lnwire.NewMSatFromSatoshis(
aliceChannel.channelState.LocalChanCfg.ChanReserve,
)
bobReserve := lnwire.NewMSatFromSatoshis(
bobChannel.channelState.LocalChanCfg.ChanReserve,
)
aliceDustlimit := lnwire.NewMSatFromSatoshis(
aliceChannel.channelState.LocalChanCfg.DustLimit,
)
bobDustlimit := lnwire.NewMSatFromSatoshis(
bobChannel.channelState.LocalChanCfg.DustLimit,
)
feeRate := chainfee.SatPerKWeight(
aliceChannel.channelState.LocalCommitment.FeePerKw,
)
@ -4759,12 +4766,20 @@ func TestChanAvailableBalanceNearHtlcFee(t *testing.T) {
)
// Helper method to check the current reported balance.
checkBalance := func(t *testing.T, expBalanceAlice lnwire.MilliSatoshi) {
checkBalance := func(t *testing.T, expBalanceAlice,
expBalanceBob lnwire.MilliSatoshi) {
t.Helper()
balance := aliceChannel.AvailableBalance()
if balance != expBalanceAlice {
t.Fatalf("Expected balance %v, got %v", expBalanceAlice,
balance)
aliceBalance := aliceChannel.AvailableBalance()
if aliceBalance != expBalanceAlice {
t.Fatalf("Expected alice balance %v, got %v",
expBalanceAlice, aliceBalance)
}
bobBalance := bobChannel.AvailableBalance()
if bobBalance != expBalanceBob {
t.Fatalf("Expected bob balance %v, got %v",
expBalanceBob, bobBalance)
}
}
@ -4803,6 +4818,7 @@ func TestChanAvailableBalanceNearHtlcFee(t *testing.T) {
htlcIndex++
aliceBalance -= htlcAmt
bobBalance += htlcAmt
}
// Balance should start out equal to half the channel capacity minus
@ -4811,12 +4827,17 @@ func TestChanAvailableBalanceNearHtlcFee(t *testing.T) {
// reflect that this value must be reserved for any payment above the
// dust limit.
expAliceBalance := aliceBalance - commitFee - aliceReserve - htlcFee
checkBalance(t, expAliceBalance)
// Bob is not the initiator, so he will have all his balance available,
// since Alice pays for fees. Bob only need to keep his balance above
// the reserve.
expBobBalance := bobBalance - bobReserve
checkBalance(t, expAliceBalance, expBobBalance)
// Find the minumim size of a non-dust HTLC.
aliceNonDustHtlc := aliceDustlimit + htlcTimeoutFee
// Send a HTLC leaving the remaining balance just enough to have
// Send a HTLC leaving Alice's remaining balance just enough to have
// nonDustHtlc left after paying the commit fee and htlc fee.
htlcAmt := aliceBalance - (commitFee + aliceReserve + htlcFee + aliceNonDustHtlc)
sendHtlc(htlcAmt)
@ -4826,7 +4847,8 @@ func TestChanAvailableBalanceNearHtlcFee(t *testing.T) {
// reported will just be nonDustHtlc, since the rest of the balance is
// reserved.
expAliceBalance = aliceNonDustHtlc
checkBalance(t, expAliceBalance)
expBobBalance = bobBalance - bobReserve
checkBalance(t, expAliceBalance, expBobBalance)
// Send an HTLC using all but one msat of the reported balance.
htlcAmt = aliceNonDustHtlc - 1
@ -4834,7 +4856,12 @@ func TestChanAvailableBalanceNearHtlcFee(t *testing.T) {
// 1 msat should be left.
expAliceBalance = 1
checkBalance(t, expAliceBalance)
// Bob should still have all his balance available, since even though
// Alice cannot afford to add a non-dust HTLC, she can afford to add a
// non-dust HTLC from Bob.
expBobBalance = bobBalance - bobReserve
checkBalance(t, expAliceBalance, expBobBalance)
// Sendng the last msat.
htlcAmt = 1
@ -4842,7 +4869,30 @@ func TestChanAvailableBalanceNearHtlcFee(t *testing.T) {
// No balance left.
expAliceBalance = 0
checkBalance(t, expAliceBalance)
// We try to always reserve enough for the non-iniitator to be able to
// add an HTLC, hence Bob should still have all his non-reserved
// balance available.
expBobBalance = bobBalance - bobReserve
checkBalance(t, expAliceBalance, expBobBalance)
// Even though Alice has a reported balance of 0, this is because we
// try to avoid getting into the position where she cannot pay the fee
// for Bob adding another HTLC. This means she actually _has_ some
// balance left, and we now force the channel into this situation by
// sending yet another HTLC. In practice this can also happen if a fee
// update eats into Alice's balance.
htlcAmt = 1
sendHtlc(htlcAmt)
// Now Alice balance is so low that she cannot even afford to add a new
// 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
expBobBalance = bobNonDustHtlc - 1
expAliceBalance = 0
checkBalance(t, expAliceBalance, expBobBalance)
}
// TestSignCommitmentFailNotLockedIn tests that a channel will not attempt to