Skip to content

Commit

Permalink
Ignore commit_sig for aborted splice (#508)
Browse files Browse the repository at this point in the history
After exchanging `tx_complete`, we validate the splice transaction before
sending our `commit_sig`. If we consider the transaction invalid, we send
`tx_abort`. But if our peer thinks the transaction is valid, they will send
their `commit_sig`, which we must ignore until they've acked our `tx_abort`.
  • Loading branch information
t-bast committed Jul 10, 2023
1 parent b5769bd commit b1fb1e9
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,10 @@ data class Normal(
is Either.Right -> Pair(this@Normal.copy(commitments = result.value), listOf())
}
is CommitSig -> when {
spliceStatus == SpliceStatus.Aborted -> {
logger.warning { "received commit_sig after sending tx_abort, they probably sent it before receiving our tx_abort, ignoring..." }
Pair(this@Normal, listOf())
}
spliceStatus is SpliceStatus.WaitingForSigs -> {
val (signingSession1, action) = spliceStatus.session.receiveCommitSig(channelKeys(), commitments.params, cmd.message, currentBlockHeight.toLong())
when (action) {
Expand Down Expand Up @@ -530,7 +534,8 @@ data class Normal(
}
else -> when (commitments.latest.localFundingStatus) {
is LocalFundingStatus.UnconfirmedFundingTx -> when (commitments.latest.localFundingStatus.sharedTx) {
is PartiallySignedSharedTransaction -> when (val fullySignedTx = commitments.latest.localFundingStatus.sharedTx.addRemoteSigs(channelKeys(), commitments.latest.localFundingStatus.fundingParams, cmd.message)) {
is PartiallySignedSharedTransaction -> when (val fullySignedTx =
commitments.latest.localFundingStatus.sharedTx.addRemoteSigs(channelKeys(), commitments.latest.localFundingStatus.fundingParams, cmd.message)) {
null -> {
logger.warning { "received invalid remote funding signatures for txId=${cmd.message.txId}" }
logger.warning { "tx=${commitments.latest.localFundingStatus.sharedTx}" }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,39 @@ class SpliceTestsCommon : LightningTestSuite() {
}
}

@Test
fun `abort after tx_complete then receive commit_sig`() {
val cmd = createSpliceOutRequest(50_000.sat)
val (alice, bob) = reachNormal()
val (alice1, actionsAlice1) = alice.process(cmd)
val (bob1, actionsBob1) = bob.process(ChannelCommand.MessageReceived(actionsAlice1.findOutgoingMessage<SpliceInit>()))
val (alice2, actionsAlice2) = alice1.process(ChannelCommand.MessageReceived(actionsBob1.findOutgoingMessage<SpliceAck>()))
val (bob2, actionsBob2) = bob1.process(ChannelCommand.MessageReceived(actionsAlice2.findOutgoingMessage<TxAddInput>()))
val (alice3, actionsAlice3) = alice2.process(ChannelCommand.MessageReceived(actionsBob2.findOutgoingMessage<TxComplete>()))
val txOut1 = actionsAlice3.findOutgoingMessage<TxAddOutput>()
val (bob3, actionsBob3) = bob2.process(ChannelCommand.MessageReceived(txOut1))
val (alice4, actionsAlice4) = alice3.process(ChannelCommand.MessageReceived(actionsBob3.findOutgoingMessage<TxComplete>()))
// Instead of relaying the second output, we duplicate the first one, which will make Bob abort after receiving tx_complete.
actionsAlice4.hasOutgoingMessage<TxAddOutput>()
val (bob4, actionsBob4) = bob3.process(ChannelCommand.MessageReceived(txOut1.copy(serialId = 100)))
val (alice5, actionsAlice5) = alice4.process(ChannelCommand.MessageReceived(actionsBob4.findOutgoingMessage<TxComplete>()))
val commitSigAlice = actionsAlice5.findOutgoingMessage<CommitSig>()
val (bob5, actionsBob5) = bob4.process(ChannelCommand.MessageReceived(actionsAlice5.findOutgoingMessage<TxComplete>()))
val txAbortBob = actionsBob5.findOutgoingMessage<TxAbort>()
val (alice6, actionsAlice6) = alice5.process(ChannelCommand.MessageReceived(txAbortBob))
assertIs<Normal>(alice6.state)
assertEquals(1, alice6.commitments.active.size)
assertEquals(SpliceStatus.None, alice6.state.spliceStatus)
val txAbortAlice = actionsAlice6.findOutgoingMessage<TxAbort>()
val (bob6, actionsBob6) = bob5.process(ChannelCommand.MessageReceived(commitSigAlice))
assertTrue(actionsBob6.isEmpty())
val (bob7, actionsBob7) = bob6.process(ChannelCommand.MessageReceived(txAbortAlice))
assertIs<Normal>(bob7.state)
assertEquals(1, bob7.commitments.active.size)
assertEquals(SpliceStatus.None, bob7.state.spliceStatus)
assertTrue(actionsBob7.isEmpty())
}

@Test
fun `exchange splice_locked`() {
val (alice, bob) = reachNormal()
Expand Down

0 comments on commit b1fb1e9

Please sign in to comment.