Skip to content

Commit

Permalink
Funder reserve for future fee increase
Browse files Browse the repository at this point in the history
See lightning/bolts#728

Add an additional reserve on the funder to prevent emptying and then
being stuck with an unusable channel.

As fundee we don't verify funders comply with that change.
We may enforce it in the future when we're confident the network as a
whole enforces that.
  • Loading branch information
t-bast committed Feb 11, 2020
1 parent bd05eb1 commit 5acd6f5
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -92,13 +92,16 @@ case class Commitments(channelVersion: ChannelVersion,
if (localParams.isFunder) {
// The funder always pays the on-chain fees, so we must subtract that from the amount we can send.
val commitFees = commitTxFeeMsat(remoteParams.dustLimit, reduced)
// the funder needs to keep an extra reserve to be able to handle fee increase without getting the channel stuck
// (see https://github.com/lightningnetwork/lightning-rfc/issues/728)
val funderFeeReserve = htlcOutputFee(2 * reduced.feeratePerKw)
val htlcFees = htlcOutputFee(reduced.feeratePerKw)
if (balanceNoFees - commitFees < offeredHtlcTrimThreshold(remoteParams.dustLimit, reduced)) {
// htlc will be trimmed
(balanceNoFees - commitFees).max(0 msat)
(balanceNoFees - commitFees - funderFeeReserve).max(0 msat)
} else {
// htlc will have an output in the commitment tx, so there will be additional fees.
(balanceNoFees - commitFees - htlcFees).max(0 msat)
(balanceNoFees - commitFees - funderFeeReserve - htlcFees).max(0 msat)
}
} else {
// The fundee doesn't pay on-chain fees.
Expand All @@ -115,13 +118,16 @@ case class Commitments(channelVersion: ChannelVersion,
} else {
// The funder always pays the on-chain fees, so we must subtract that from the amount we can receive.
val commitFees = commitTxFeeMsat(localParams.dustLimit, reduced)
// we expect the funder to keep an extra reserve to be able to handle fee increase without getting the channel stuck
// (see https://github.com/lightningnetwork/lightning-rfc/issues/728)
val funderFeeReserve = htlcOutputFee(2 * reduced.feeratePerKw)
val htlcFees = htlcOutputFee(reduced.feeratePerKw)
if (balanceNoFees - commitFees < receivedHtlcTrimThreshold(localParams.dustLimit, reduced)) {
// htlc will be trimmed
(balanceNoFees - commitFees).max(0 msat)
(balanceNoFees - commitFees - funderFeeReserve).max(0 msat)
} else {
// htlc will have an output in the commitment tx, so there will be additional fees.
(balanceNoFees - commitFees - htlcFees).max(0 msat)
(balanceNoFees - commitFees - funderFeeReserve - htlcFees).max(0 msat)
}
}
}
Expand Down Expand Up @@ -183,7 +189,10 @@ object Commitments {

// note that the funder pays the fee, so if sender != funder, both sides will have to afford this payment
val fees = commitTxFee(commitments1.remoteParams.dustLimit, reduced)
val missingForSender = reduced.toRemote - commitments1.remoteParams.channelReserve - (if (commitments1.localParams.isFunder) fees else 0.sat)
// the funder needs to keep an extra reserve to be able to handle fee increase without getting the channel stuck
// (see https://github.com/lightningnetwork/lightning-rfc/issues/728)
val funderFeeReserve = htlcOutputFee(2 * reduced.feeratePerKw)
val missingForSender = reduced.toRemote - commitments1.remoteParams.channelReserve - (if (commitments1.localParams.isFunder) fees + funderFeeReserve else 0.msat)
val missingForReceiver = reduced.toLocal - commitments1.localParams.channelReserve - (if (commitments1.localParams.isFunder) 0.sat else fees)
if (missingForSender < 0.msat) {
return Left(InsufficientFunds(commitments.channelId, amount = cmd.amount, missing = -missingForSender.truncateToSatoshi, reserve = commitments1.remoteParams.channelReserve, fees = if (commitments1.localParams.isFunder) fees else 0.sat))
Expand Down Expand Up @@ -225,6 +234,8 @@ object Commitments {

// note that the funder pays the fee, so if sender != funder, both sides will have to afford this payment
val fees = commitTxFee(commitments1.remoteParams.dustLimit, reduced)
// NB: we don't enforce the funderFeeReserve (see sendAdd) because it would confuse a remote funder that doesn't have this mitigation in place
// We could enforce it once we're confident a large portion of the network implements it.
val missingForSender = reduced.toRemote - commitments1.localParams.channelReserve - (if (commitments1.localParams.isFunder) 0.sat else fees)
val missingForReceiver = reduced.toLocal - commitments1.remoteParams.channelReserve - (if (commitments1.localParams.isFunder) fees else 0.sat)
if (missingForSender < 0.sat) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,9 @@ class CommitmentsSpec extends TestkitBaseClass with StateTestsHelperMethods {

test("take additional HTLC fee into account") { f =>
import f._
val htlcOutputFee = 1720000 msat
// The fee for a single HTLC is 1720000 msat but the funder keeps an extra reserve to make sure we're able to handle
// an additional HTLC at twice the feerate (hence the multiplier).
val htlcOutputFee = 3 * 1720000 msat
val a = 772760000 msat // initial balance alice
val ac0 = alice.stateData.asInstanceOf[DATA_NORMAL].commitments
val bc0 = bob.stateData.asInstanceOf[DATA_NORMAL].commitments
Expand All @@ -75,7 +77,8 @@ class CommitmentsSpec extends TestkitBaseClass with StateTestsHelperMethods {
import f._

val fee = 1720000 msat // fee due to the additional htlc output
val a = (772760000 msat) - fee // initial balance alice
val funderFeeReserve = fee * 2 // extra reserve to handle future fee increase
val a = (772760000 msat) - fee - funderFeeReserve // initial balance alice
val b = 190000000 msat // initial balance bob
val p = 42000000 msat // a->b payment

Expand Down Expand Up @@ -159,7 +162,8 @@ class CommitmentsSpec extends TestkitBaseClass with StateTestsHelperMethods {
import f._

val fee = 1720000 msat // fee due to the additional htlc output
val a = (772760000 msat) - fee // initial balance alice
val funderFeeReserve = fee * 2 // extra reserve to handle future fee increase
val a = (772760000 msat) - fee - funderFeeReserve // initial balance alice
val b = 190000000 msat // initial balance bob
val p = 42000000 msat // a->b payment

Expand Down Expand Up @@ -243,7 +247,8 @@ class CommitmentsSpec extends TestkitBaseClass with StateTestsHelperMethods {
import f._

val fee = 1720000 msat // fee due to the additional htlc output
val a = (772760000 msat) - fee // initial balance alice
val funderFeeReserve = fee * 2 // extra reserve to handle future fee increase
val a = (772760000 msat) - fee - funderFeeReserve // initial balance alice
val b = 190000000 msat // initial balance bob
val p1 = 10000000 msat // a->b payment
val p2 = 20000000 msat // a->b payment
Expand Down Expand Up @@ -386,6 +391,23 @@ class CommitmentsSpec extends TestkitBaseClass with StateTestsHelperMethods {
assert(ac16.availableBalanceForReceive == b + p1 - p3)
}

// See https://github.com/lightningnetwork/lightning-rfc/issues/728
test("funder keeps additional reserve to avoid channel being stuck") { f =>
val isFunder = true
val c = CommitmentsSpec.makeCommitments(100000000 msat, 50000000 msat, 2500, 546 sat, isFunder)
val (_, cmdAdd) = makeCmdAdd(c.availableBalanceForSend, randomKey.publicKey, f.currentBlockHeight)
val Right((c1, _)) = sendAdd(c, cmdAdd, Local(UUID.randomUUID, None), f.currentBlockHeight)
assert(c1.availableBalanceForSend === 0.msat)

// We should be able to handle a fee increase.
val (c2, _) = sendFee(c1, CMD_UPDATE_FEE(3000))

// Now we shouldn't be able to send until we receive enough to handle the updated commit tx fee (even trimmed HTLCs shouldn't be sent).
val (_, cmdAdd1) = makeCmdAdd(100 msat, randomKey.publicKey, f.currentBlockHeight)
val Left(e) = sendAdd(c2, cmdAdd1, Local(UUID.randomUUID, None), f.currentBlockHeight)
assert(e.isInstanceOf[InsufficientFunds])
}

test("can send availableForSend") { f =>
for (isFunder <- Seq(true, false)) {
val c = CommitmentsSpec.makeCommitments(702000000 msat, 52000000 msat, 2679, 546 sat, isFunder)
Expand Down

0 comments on commit 5acd6f5

Please sign in to comment.