1
0
mirror of https://github.com/ACINQ/eclair.git synced 2024-11-19 01:43:22 +01:00

Ignore commit_sig for aborted splice (#2709)

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`.
This commit is contained in:
Bastien Teinturier 2023-07-10 10:39:17 +02:00 committed by GitHub
parent abf1dd3747
commit cf46b64905
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 69 additions and 2 deletions

View File

@ -515,6 +515,9 @@ class Channel(val nodeParams: NodeParams, val wallet: OnChainChannelFunder with
case s: SpliceStatus.SpliceInProgress =>
log.debug("received their commit_sig, deferring message")
stay() using d.copy(spliceStatus = s.copy(remoteCommitSig = Some(commit)))
case SpliceStatus.SpliceAborted =>
log.warning("received commit_sig after sending tx_abort, they probably sent it before receiving our tx_abort, ignoring...")
stay()
case SpliceStatus.SpliceWaitingForSigs(signingSession) =>
signingSession.receiveCommitSig(nodeParams, d.commitments.params, commit) match {
case Left(f) =>

View File

@ -305,6 +305,54 @@ class NormalSplicesStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLik
awaitCond(bob.stateData.asInstanceOf[DATA_NORMAL].spliceStatus == SpliceStatus.NoSplice)
}
test("recv TxAbort (after CommitSig)") { f =>
import f._
val sender = TestProbe()
alice ! CMD_SPLICE(sender.ref, spliceIn_opt = Some(SpliceIn(50_000 sat)), spliceOut_opt = None)
alice2bob.expectMsgType[SpliceInit]
alice2bob.forward(bob)
bob2alice.expectMsgType[SpliceAck]
bob2alice.forward(alice)
alice2bob.expectMsgType[TxAddInput]
alice2bob.forward(bob)
bob2alice.expectMsgType[TxComplete]
bob2alice.forward(alice)
alice2bob.expectMsgType[TxAddInput]
alice2bob.forward(bob)
bob2alice.expectMsgType[TxComplete]
bob2alice.forward(alice)
val output1 = alice2bob.expectMsgType[TxAddOutput]
alice2bob.forward(bob)
bob2alice.expectMsgType[TxComplete]
bob2alice.forward(alice)
alice2bob.expectMsgType[TxAddOutput]
// We forward a duplicate of the first output, which will make bob abort after receiving tx_complete.
alice2bob.forward(bob, output1.copy(serialId = UInt64(100)))
bob2alice.expectMsgType[TxComplete]
bob2alice.forward(alice)
alice2bob.expectMsgType[TxComplete]
alice2bob.forward(bob)
val commitSigAlice = alice2bob.expectMsgType[CommitSig]
val txAbortBob = bob2alice.expectMsgType[TxAbort]
sender.expectMsgType[RES_SPLICE]
// Bob ignores Alice's commit_sig.
alice2bob.forward(bob, commitSigAlice)
bob2alice.expectNoMessage(100 millis)
bob2blockchain.expectNoMessage(100 millis)
// Alice acks Bob's tx_abort.
bob2alice.forward(alice, txAbortBob)
alice2bob.expectMsgType[TxAbort]
alice2bob.forward(bob)
awaitCond(alice.stateData.asInstanceOf[DATA_NORMAL].spliceStatus == SpliceStatus.NoSplice)
awaitCond(bob.stateData.asInstanceOf[DATA_NORMAL].spliceStatus == SpliceStatus.NoSplice)
assert(alice.stateData.asInstanceOf[DATA_NORMAL].commitments.active.size == 1)
assert(bob.stateData.asInstanceOf[DATA_NORMAL].commitments.active.size == 1)
}
test("recv WatchFundingConfirmedTriggered on splice tx", Tag(NoMaxHtlcValueInFlight)) { f =>
import f._
@ -1370,15 +1418,23 @@ class NormalSplicesStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLik
bob.stop()
alice2blockchain.expectNoMessage(100 millis)
bob2blockchain.expectNoMessage(100 millis)
val alice2 = TestFSMRef(new Channel(aliceNodeParams, wallet, bobNodeParams.nodeId, alice2blockchain.ref, TestProbe().ref, FakeTxPublisherFactory(alice2blockchain)), alicePeer)
alice2 ! INPUT_RESTORED(aliceData)
alice2blockchain.expectMsgType[SetChannelId]
alice2blockchain.expectWatchFundingConfirmed(fundingTx2.txid)
alice2blockchain.expectWatchFundingConfirmed(fundingTx1.txid)
alice2blockchain.expectWatchFundingSpent(fundingTx0.txid)
alice2blockchain.expectNoMessage(100 millis)
val bob2 = TestFSMRef(new Channel(bobNodeParams, wallet, aliceNodeParams.nodeId, bob2blockchain.ref, TestProbe().ref, FakeTxPublisherFactory(bob2blockchain)), bobPeer)
bob2 ! INPUT_RESTORED(bobData)
bob2blockchain.expectMsgType[SetChannelId]
bob2blockchain.expectWatchFundingConfirmed(fundingTx2.txid)
bob2blockchain.expectWatchFundingConfirmed(fundingTx1.txid)
bob2blockchain.expectWatchFundingSpent(fundingTx0.txid)
bob2blockchain.expectNoMessage(100 millis)
}
test("put back watches after restart (inactive)", Tag(ChannelStateTestsTags.ZeroConf), Tag(ChannelStateTestsTags.AnchorOutputsZeroFeeHtlcTxs)) { f =>
@ -1421,15 +1477,23 @@ class NormalSplicesStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLik
bob.stop()
alice2blockchain.expectNoMessage(100 millis)
bob2blockchain.expectNoMessage(100 millis)
val alice2 = TestFSMRef(new Channel(aliceNodeParams, wallet, bobNodeParams.nodeId, alice2blockchain.ref, TestProbe().ref, FakeTxPublisherFactory(alice2blockchain)), alicePeer)
alice2 ! INPUT_RESTORED(aliceData)
alice2blockchain.expectMsgType[SetChannelId]
alice2blockchain.expectWatchPublished(fundingTx2.txid)
alice2blockchain.expectWatchFundingConfirmed(fundingTx1.txid)
alice2blockchain.expectWatchFundingSpent(fundingTx0.txid)
alice2blockchain.expectNoMessage(100 millis)
val bob2 = TestFSMRef(new Channel(bobNodeParams, wallet, aliceNodeParams.nodeId, bob2blockchain.ref, TestProbe().ref, FakeTxPublisherFactory(bob2blockchain)), bobPeer)
bob2 ! INPUT_RESTORED(bobData)
bob2blockchain.expectMsgType[SetChannelId]
bob2blockchain.expectWatchPublished(fundingTx2.txid)
bob2blockchain.expectWatchFundingConfirmed(fundingTx1.txid)
bob2blockchain.expectWatchFundingSpent(fundingTx0.txid)
bob2blockchain.expectNoMessage(100 millis)
}
}