Skip to content

Commit 3b9cbcd

Browse files
committed
Check that splice out does not reduce local balance below the funder fee buffer
- see lightning/bolts#728
1 parent 363c62b commit 3b9cbcd

File tree

2 files changed

+29
-2
lines changed

2 files changed

+29
-2
lines changed

eclair-core/src/main/scala/fr/acinq/eclair/channel/fsm/Channel.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -2621,7 +2621,7 @@ class Channel(val nodeParams: NodeParams, val wallet: OnChainChannelFunder with
26212621
spliceInAmount = cmd.additionalLocalFunding,
26222622
spliceOut = cmd.spliceOutputs,
26232623
targetFeerate = targetFeerate)
2624-
if (parentCommitment.localCommit.spec.toLocal + fundingContribution < parentCommitment.localChannelReserve(d.commitments.params)) {
2624+
if (fundingContribution + d.commitments.availableBalanceForSend.truncateToSatoshi < 0.sat) {
26252625
log.warning("cannot do splice: insufficient funds")
26262626
Left(InvalidSpliceRequest(d.channelId))
26272627
} else if (cmd.spliceOut_opt.map(_.scriptPubKey).exists(!MutualClose.isValidFinalScriptPubkey(_, allowAnySegwit = true))) {

eclair-core/src/test/scala/fr/acinq/eclair/channel/states/e/NormalSplicesStateSpec.scala

+28-1
Original file line numberDiff line numberDiff line change
@@ -185,14 +185,41 @@ class NormalSplicesStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLik
185185
test("recv CMD_SPLICE (splice-out, would go below reserve)") { f =>
186186
import f._
187187

188+
val availableToSpend = alice.stateData.asInstanceOf[DATA_NORMAL].commitments.availableBalanceForSend
189+
188190
val initialState = alice.stateData.asInstanceOf[DATA_NORMAL]
189191
assert(initialState.commitments.latest.capacity == 1_500_000.sat)
190192
assert(initialState.commitments.latest.localCommit.spec.toLocal == 800_000_000.msat)
191193
assert(initialState.commitments.latest.localCommit.spec.toRemote == 700_000_000.msat)
192194

193195
val sender = TestProbe()
194-
val cmd = CMD_SPLICE(sender.ref, spliceIn_opt = None, Some(SpliceOut(790_000 sat, defaultSpliceOutScriptPubKey)))
196+
val cmd = CMD_SPLICE(sender.ref, spliceIn_opt = Some(SpliceIn(1000.sat)), Some(SpliceOut(1001.sat + availableToSpend.truncateToSatoshi, defaultSpliceOutScriptPubKey)))
197+
alice ! cmd
198+
sender.expectMsgType[RES_FAILURE[_, _]]
199+
}
200+
201+
test("recv CMD_SPLICE (splice-out, would go below reserve, quiescent)", Tag(ChannelStateTestsTags.Quiescence), Tag(NoMaxHtlcValueInFlight)) { f =>
202+
import f._
203+
204+
val availableToSpend = alice.stateData.asInstanceOf[DATA_NORMAL].commitments.availableBalanceForSend
205+
206+
// add htlcs to make alice's balance go below reserve
207+
addHtlc(1_000.msat, alice, bob, alice2bob, bob2alice)
208+
crossSign(alice, bob, alice2bob, bob2alice)
209+
210+
val initialState = alice.stateData.asInstanceOf[DATA_NORMAL]
211+
assert(initialState.commitments.latest.capacity == 1_500_000.sat)
212+
assert(initialState.commitments.latest.localCommit.spec.toLocal == 799_999_000.msat)
213+
assert(initialState.commitments.latest.localCommit.spec.toRemote == 700_000_000.msat)
214+
215+
val sender = TestProbe()
216+
217+
val cmd = CMD_SPLICE(sender.ref, spliceIn_opt = Some(SpliceIn(1000 sat)), Some(SpliceOut(1000.sat + availableToSpend.truncateToSatoshi, defaultSpliceOutScriptPubKey)))
195218
alice ! cmd
219+
alice2bob.expectMsgType[Stfu]
220+
alice2bob.forward(bob)
221+
bob2alice.expectMsgType[Stfu]
222+
bob2alice.forward(alice)
196223
sender.expectMsgType[RES_FAILURE[_, _]]
197224
}
198225

0 commit comments

Comments
 (0)