Skip to content

Commit

Permalink
Allow funder to dip into reserve if increased fee
Browse files Browse the repository at this point in the history
See lightning/bolts#728

Allow a funder to dip into its channel reserve to pay the increased
commit tx fee for one incoming HTLC.
This prevents being stuck with an unusable channel.
  • Loading branch information
t-bast committed Feb 24, 2020
1 parent cc4a4ab commit 7520b04
Show file tree
Hide file tree
Showing 2 changed files with 20 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,8 @@ object Commitments {
} else if (missingForReceiver < 0.msat) {
if (commitments.localParams.isFunder) {
// receiver is fundee; it is ok if it can't maintain its channel_reserve for now, as long as its balance is increasing, which is the case if it is receiving a payment
} else if (missingForReceiver + commitments1.localParams.channelReserve > 0.msat && outgoingHtlcs.size <= 1) {
// receiver is funder; it is allowed to dip into its channel reserve to pay the fee for a single non-dust HTLC
} else {
return Left(RemoteCannotAffordFeesForNewHtlc(commitments.channelId, amount = cmd.amount, missing = -missingForReceiver.truncateToSatoshi, reserve = commitments1.remoteParams.channelReserve, fees = fees))
}
Expand Down Expand Up @@ -230,10 +232,12 @@ object Commitments {
if (missingForSender < 0.sat) {
throw InsufficientFunds(commitments.channelId, amount = add.amountMsat, missing = -missingForSender.truncateToSatoshi, reserve = commitments1.localParams.channelReserve, fees = if (commitments1.localParams.isFunder) 0.sat else fees)
} else if (missingForReceiver < 0.sat) {
if (commitments.localParams.isFunder) {
throw CannotAffordFees(commitments.channelId, missing = -missingForReceiver.truncateToSatoshi, reserve = commitments1.remoteParams.channelReserve, fees = fees)
} else {
if (!commitments.localParams.isFunder) {
// receiver is fundee; it is ok if it can't maintain its channel_reserve for now, as long as its balance is increasing, which is the case if it is receiving a payment
} else if (missingForReceiver + commitments1.remoteParams.channelReserve > 0.msat && incomingHtlcs.size <= 1) {
// receiver is funder; it is allowed to dip into its channel reserve to pay the fee for a single non-dust HTLC
} else {
throw CannotAffordFees(commitments.channelId, missing = -missingForReceiver.truncateToSatoshi, reserve = commitments1.remoteParams.channelReserve, fees = fees)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -216,10 +216,21 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods {

// actual test begins
// at this point alice has the minimal amount to sustain a channel (29000 sat ~= alice reserve + commit fee)
val add = CMD_ADD_HTLC(120000000 msat, randomBytes32, CltvExpiry(400144), TestConstants.emptyOnionPacket, Upstream.Local(UUID.randomUUID()))
// alice should be allowed to dip into her reserve to pay for the commit tx fee increase (see https://github.com/lightningnetwork/lightning-rfc/issues/728)
sender.send(bob, CMD_ADD_HTLC(85000000 msat, randomBytes32, CltvExpiry(400144), TestConstants.emptyOnionPacket, Upstream.Local(UUID.randomUUID())))
sender.expectMsg("ok")
val htlc = bob2alice.expectMsgType[UpdateAddHtlc]
bob2alice.forward(alice)

// but only one pending HTLC is allowed to dip into alice's reserve; bob must wait for that HTLC to settle before sending another one
val add = CMD_ADD_HTLC(80000000 msat, randomBytes32, CltvExpiry(400144), TestConstants.emptyOnionPacket, Upstream.Local(UUID.randomUUID()))
sender.send(bob, add)
val error = RemoteCannotAffordFeesForNewHtlc(channelId(bob), add.amount, missing = 1680 sat, 10000 sat, 10680 sat)
val error = RemoteCannotAffordFeesForNewHtlc(channelId(bob), add.amount, missing = 3400 sat, 10000 sat, 12400 sat)
sender.expectMsg(Failure(AddHtlcFailed(channelId(bob), add.paymentHash, error, Origin.Local(add.upstream.asInstanceOf[Upstream.Local].id, Some(sender.ref)), Some(bob.stateData.asInstanceOf[DATA_NORMAL].channelUpdate), Some(add))))
bob2alice.expectNoMsg(100 millis)

// alice should accept the first incoming htlc
awaitCond(alice.stateData.asInstanceOf[HasCommitments].commitments.remoteChanges.proposed.contains(htlc))
}

test("recv CMD_ADD_HTLC (insufficient funds w/ pending htlcs and 0 balance)") { f =>
Expand Down

0 comments on commit 7520b04

Please sign in to comment.