mirror of
https://github.com/ACINQ/eclair.git
synced 2025-02-21 22:11:46 +01:00
Retransmit signatures during 0-conf disconnect
We must retransmit `tx_signatures` (and, if requested, `commit_sig`) after sending `channel_ready` in the 0-conf case.
This commit is contained in:
parent
9080f98de5
commit
8f586d58ae
3 changed files with 122 additions and 7 deletions
|
@ -2324,14 +2324,31 @@ class Channel(val nodeParams: NodeParams, val wallet: OnChainChannelFunder with
|
|||
}
|
||||
|
||||
case Event(_: ChannelReestablish, d: DATA_WAIT_FOR_CHANNEL_READY) =>
|
||||
log.debug("re-sending channelReady")
|
||||
log.debug("re-sending channel_ready")
|
||||
val channelReady = createChannelReady(d.aliases, d.commitments.params)
|
||||
goto(WAIT_FOR_CHANNEL_READY) sending channelReady
|
||||
|
||||
case Event(_: ChannelReestablish, d: DATA_WAIT_FOR_DUAL_FUNDING_READY) =>
|
||||
log.debug("re-sending channelReady")
|
||||
case Event(channelReestablish: ChannelReestablish, d: DATA_WAIT_FOR_DUAL_FUNDING_READY) =>
|
||||
log.debug("re-sending channel_ready")
|
||||
val channelReady = createChannelReady(d.aliases, d.commitments.params)
|
||||
goto(WAIT_FOR_DUAL_FUNDING_READY) sending channelReady
|
||||
// We've already received their commit_sig and sent our tx_signatures. We retransmit our tx_signatures
|
||||
// and our commit_sig if they haven't received it already.
|
||||
channelReestablish.nextFundingTxId_opt match {
|
||||
case Some(fundingTxId) if fundingTxId == d.commitments.latest.fundingTxId =>
|
||||
d.commitments.latest.localFundingStatus.localSigs_opt match {
|
||||
case Some(txSigs) if channelReestablish.nextLocalCommitmentNumber == 0 =>
|
||||
log.info("re-sending commit_sig and tx_signatures for fundingTxIndex={} fundingTxId={}", d.commitments.latest.fundingTxIndex, d.commitments.latest.fundingTxId)
|
||||
val commitSig = d.commitments.latest.remoteCommit.sign(keyManager, d.commitments.params, d.commitments.latest.fundingTxIndex, d.commitments.latest.remoteFundingPubKey, d.commitments.latest.commitInput)
|
||||
goto(WAIT_FOR_DUAL_FUNDING_READY) sending Seq(commitSig, txSigs, channelReady)
|
||||
case Some(txSigs) =>
|
||||
log.info("re-sending tx_signatures for fundingTxIndex={} fundingTxId={}", d.commitments.latest.fundingTxIndex, d.commitments.latest.fundingTxId)
|
||||
goto(WAIT_FOR_DUAL_FUNDING_READY) sending Seq(txSigs, channelReady)
|
||||
case None =>
|
||||
log.warning("cannot retransmit tx_signatures, we don't have them (status={})", d.commitments.latest.localFundingStatus)
|
||||
goto(WAIT_FOR_DUAL_FUNDING_READY) sending channelReady
|
||||
}
|
||||
case _ => goto(WAIT_FOR_DUAL_FUNDING_READY) sending channelReady
|
||||
}
|
||||
|
||||
case Event(channelReestablish: ChannelReestablish, d: DATA_NORMAL) =>
|
||||
Syncing.checkSync(keyManager, d.commitments, channelReestablish) match {
|
||||
|
|
|
@ -21,7 +21,7 @@ import akka.testkit.{TestFSMRef, TestProbe}
|
|||
import fr.acinq.bitcoin.scalacompat.{ByteVector32, ByteVector64, SatoshiLong, TxId}
|
||||
import fr.acinq.eclair.TestUtils.randomTxId
|
||||
import fr.acinq.eclair.blockchain.SingleKeyOnChainWallet
|
||||
import fr.acinq.eclair.blockchain.bitcoind.ZmqWatcher.{WatchFundingConfirmed, WatchPublished}
|
||||
import fr.acinq.eclair.blockchain.bitcoind.ZmqWatcher.{WatchFundingConfirmed, WatchPublished, WatchPublishedTriggered}
|
||||
import fr.acinq.eclair.blockchain.fee.FeeratePerKw
|
||||
import fr.acinq.eclair.channel._
|
||||
import fr.acinq.eclair.channel.fsm.Channel
|
||||
|
@ -415,6 +415,59 @@ class WaitForDualFundingSignedStateSpec extends TestKitBaseClass with FixtureAny
|
|||
reconnect(f, fundingTxId, aliceExpectsCommitSig = true, bobExpectsCommitSig = false)
|
||||
}
|
||||
|
||||
test("recv INPUT_DISCONNECTED (commit_sig received by Bob, zero-conf)", Tag(ChannelStateTestsTags.DualFunding), Tag(ChannelStateTestsTags.ZeroConf), Tag(ChannelStateTestsTags.AnchorOutputsZeroFeeHtlcTxs)) { f =>
|
||||
import f._
|
||||
|
||||
alice2bob.expectMsgType[CommitSig]
|
||||
alice2bob.forward(bob)
|
||||
bob2alice.expectMsgType[CommitSig] // Alice doesn't receive Bob's commit_sig
|
||||
bob2alice.expectMsgType[TxSignatures] // Alice doesn't receive Bob's tx_signatures
|
||||
awaitCond(alice.stateName == WAIT_FOR_DUAL_FUNDING_SIGNED)
|
||||
awaitCond(bob.stateName == WAIT_FOR_DUAL_FUNDING_CONFIRMED)
|
||||
|
||||
// Note that this case can only happen when Bob doesn't need Alice's signatures to publish the transaction (when
|
||||
// Bob was the only one to contribute to the funding transaction).
|
||||
val fundingTx = bob.stateData.asInstanceOf[DATA_WAIT_FOR_DUAL_FUNDING_CONFIRMED].latestFundingTx.sharedTx.tx.buildUnsignedTx()
|
||||
assert(bob2blockchain.expectMsgType[WatchPublished].txId == fundingTx.txid)
|
||||
bob ! WatchPublishedTriggered(fundingTx)
|
||||
assert(bob2blockchain.expectMsgType[WatchFundingConfirmed].txId == fundingTx.txid)
|
||||
bob2alice.expectMsgType[ChannelReady]
|
||||
awaitCond(bob.stateName == WAIT_FOR_DUAL_FUNDING_READY)
|
||||
|
||||
alice ! INPUT_DISCONNECTED
|
||||
awaitCond(alice.stateName == OFFLINE)
|
||||
bob ! INPUT_DISCONNECTED
|
||||
awaitCond(bob.stateName == OFFLINE)
|
||||
|
||||
val listener = TestProbe()
|
||||
alice.underlyingActor.context.system.eventStream.subscribe(listener.ref, classOf[TransactionPublished])
|
||||
|
||||
val aliceInit = Init(alice.underlyingActor.nodeParams.features.initFeatures())
|
||||
val bobInit = Init(bob.underlyingActor.nodeParams.features.initFeatures())
|
||||
alice ! INPUT_RECONNECTED(bob, aliceInit, bobInit)
|
||||
bob ! INPUT_RECONNECTED(alice, bobInit, aliceInit)
|
||||
val channelReestablishAlice = alice2bob.expectMsgType[ChannelReestablish]
|
||||
assert(channelReestablishAlice.nextFundingTxId_opt.contains(fundingTx.txid))
|
||||
assert(channelReestablishAlice.nextLocalCommitmentNumber == 0)
|
||||
alice2bob.forward(bob, channelReestablishAlice)
|
||||
val channelReestablishBob = bob2alice.expectMsgType[ChannelReestablish]
|
||||
assert(channelReestablishBob.nextFundingTxId_opt.isEmpty)
|
||||
assert(channelReestablishBob.nextLocalCommitmentNumber == 1)
|
||||
bob2alice.forward(alice, channelReestablishBob)
|
||||
|
||||
bob2alice.expectMsgType[CommitSig]
|
||||
bob2alice.forward(alice)
|
||||
bob2alice.expectMsgType[TxSignatures]
|
||||
bob2alice.forward(alice)
|
||||
alice2bob.expectMsgType[TxSignatures]
|
||||
alice2bob.forward(bob)
|
||||
|
||||
awaitCond(alice.stateName == WAIT_FOR_DUAL_FUNDING_CONFIRMED)
|
||||
awaitCond(bob.stateName == WAIT_FOR_DUAL_FUNDING_READY)
|
||||
assert(alice2blockchain.expectMsgType[WatchFundingConfirmed].txId == fundingTx.txid)
|
||||
assert(listener.expectMsgType[TransactionPublished].tx.txid == fundingTx.txid)
|
||||
}
|
||||
|
||||
test("recv INPUT_DISCONNECTED (commit_sig received)", Tag(ChannelStateTestsTags.DualFunding)) { f =>
|
||||
import f._
|
||||
|
||||
|
@ -448,7 +501,7 @@ class WaitForDualFundingSignedStateSpec extends TestKitBaseClass with FixtureAny
|
|||
bob2alice.forward(alice)
|
||||
bob2alice.expectMsgType[TxSignatures]
|
||||
bob2alice.forward(alice)
|
||||
alice2bob.expectMsgType[TxSignatures]
|
||||
alice2bob.expectMsgType[TxSignatures] // Bob doesn't receive Alice's tx_signatures
|
||||
awaitCond(alice.stateName == WAIT_FOR_DUAL_FUNDING_CONFIRMED)
|
||||
awaitCond(bob.stateName == WAIT_FOR_DUAL_FUNDING_CONFIRMED)
|
||||
|
||||
|
@ -472,6 +525,51 @@ class WaitForDualFundingSignedStateSpec extends TestKitBaseClass with FixtureAny
|
|||
assert(listener.expectMsgType[TransactionPublished].tx.txid == fundingTxId)
|
||||
}
|
||||
|
||||
test("recv INPUT_DISCONNECTED (tx_signatures received, zero-conf)", Tag(ChannelStateTestsTags.DualFunding), Tag(ChannelStateTestsTags.ZeroConf), Tag(ChannelStateTestsTags.AnchorOutputsZeroFeeHtlcTxs)) { f =>
|
||||
import f._
|
||||
|
||||
val listener = TestProbe()
|
||||
bob.underlyingActor.context.system.eventStream.subscribe(listener.ref, classOf[TransactionPublished])
|
||||
|
||||
alice2bob.expectMsgType[CommitSig]
|
||||
alice2bob.forward(bob)
|
||||
bob2alice.expectMsgType[CommitSig]
|
||||
bob2alice.forward(alice)
|
||||
bob2alice.expectMsgType[TxSignatures]
|
||||
bob2alice.forward(alice)
|
||||
alice2bob.expectMsgType[TxSignatures] // Bob doesn't receive Alice's tx_signatures
|
||||
awaitCond(alice.stateName == WAIT_FOR_DUAL_FUNDING_CONFIRMED)
|
||||
awaitCond(bob.stateName == WAIT_FOR_DUAL_FUNDING_CONFIRMED)
|
||||
|
||||
val fundingTx = alice.stateData.asInstanceOf[DATA_WAIT_FOR_DUAL_FUNDING_CONFIRMED].latestFundingTx.signedTx_opt.get
|
||||
assert(alice2blockchain.expectMsgType[WatchPublished].txId == fundingTx.txid)
|
||||
alice ! WatchPublishedTriggered(fundingTx)
|
||||
assert(alice2blockchain.expectMsgType[WatchFundingConfirmed].txId == fundingTx.txid)
|
||||
alice2bob.expectMsgType[ChannelReady]
|
||||
awaitCond(alice.stateName == WAIT_FOR_DUAL_FUNDING_READY)
|
||||
|
||||
alice ! INPUT_DISCONNECTED
|
||||
awaitCond(alice.stateName == OFFLINE)
|
||||
bob ! INPUT_DISCONNECTED
|
||||
awaitCond(bob.stateName == OFFLINE)
|
||||
|
||||
val aliceInit = Init(alice.underlyingActor.nodeParams.features.initFeatures())
|
||||
val bobInit = Init(bob.underlyingActor.nodeParams.features.initFeatures())
|
||||
alice ! INPUT_RECONNECTED(bob, aliceInit, bobInit)
|
||||
bob ! INPUT_RECONNECTED(alice, bobInit, aliceInit)
|
||||
|
||||
assert(alice2bob.expectMsgType[ChannelReestablish].nextFundingTxId_opt.isEmpty)
|
||||
alice2bob.forward(bob)
|
||||
assert(bob2alice.expectMsgType[ChannelReestablish].nextFundingTxId_opt.contains(fundingTx.txid))
|
||||
bob2alice.forward(alice)
|
||||
alice2bob.expectMsgType[TxSignatures]
|
||||
alice2bob.forward(bob)
|
||||
alice2bob.expectMsgType[ChannelReady]
|
||||
alice2bob.forward(bob)
|
||||
assert(bob2blockchain.expectMsgType[WatchPublished].txId == fundingTx.txid)
|
||||
assert(listener.expectMsgType[TransactionPublished].tx.txid == fundingTx.txid)
|
||||
}
|
||||
|
||||
private def reconnect(f: FixtureParam, fundingTxId: TxId, aliceExpectsCommitSig: Boolean, bobExpectsCommitSig: Boolean): Unit = {
|
||||
import f._
|
||||
|
||||
|
|
|
@ -2200,7 +2200,7 @@ class NormalSplicesStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLik
|
|||
resolveHtlcs(f, htlcs)
|
||||
}
|
||||
|
||||
test("disconnect (RBF commit_sig received by bob)", Tag(ChannelStateTestsTags.FundingDeeplyBuried)) { f =>
|
||||
test("disconnect (RBF commit_sig received by bob)") { f =>
|
||||
import f._
|
||||
|
||||
val htlcs = setupHtlcs(f)
|
||||
|
|
Loading…
Add table
Reference in a new issue