Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ import fr.acinq.eclair.blockchain._
import fr.acinq.eclair.blockchain.bitcoind.ZmqWatcher
import fr.acinq.eclair.blockchain.bitcoind.ZmqWatcher._
import fr.acinq.eclair.blockchain.bitcoind.rpc.BitcoinCoreClient
import fr.acinq.eclair.channel.ChannelTypes.SimpleTaprootChannelsPhoenix
import fr.acinq.eclair.channel.Commitments.PostRevocationAction
import fr.acinq.eclair.channel.Helpers.Closing.MutualClose
import fr.acinq.eclair.channel.Helpers.Syncing.SyncResult
Expand Down Expand Up @@ -2720,10 +2719,13 @@ class Channel(val nodeParams: NodeParams, val channelKeys: ChannelKeys, val wall
}

// BOLT 2: A node if it has sent a previous shutdown MUST retransmit shutdown.
d.localShutdown.foreach {
localShutdown =>
log.debug("re-sending local_shutdown")
sendQueue = sendQueue :+ localShutdown
val shutdown_opt = d.localShutdown match {
case None => None
case Some(shutdown) =>
log.debug("re-sending local shutdown")
val shutdown1 = createShutdown(commitments1, shutdown.scriptPubKey)
sendQueue = sendQueue :+ shutdown1
Some(shutdown1)
}

if (d.commitments.announceChannel) {
Expand Down Expand Up @@ -2754,7 +2756,7 @@ class Channel(val nodeParams: NodeParams, val channelKeys: ChannelKeys, val wall
peer ! ChannelReadyForPayments(self, remoteNodeId, d.channelId, fundingTxIndex)
}

goto(NORMAL) using d.copy(commitments = commitments1, spliceStatus = spliceStatus1) sending sendQueue
goto(NORMAL) using d.copy(commitments = commitments1, spliceStatus = spliceStatus1, localShutdown = shutdown_opt) sending sendQueue
}
}

Expand All @@ -2780,9 +2782,11 @@ class Channel(val nodeParams: NodeParams, val channelKeys: ChannelKeys, val wall
handleSyncFailure(channelReestablish, syncFailure, d)
case syncSuccess: SyncResult.Success =>
val commitments1 = d.commitments.discardUnsignedUpdates()
val sendQueue = Queue.empty[LightningMessage] ++ syncSuccess.retransmit :+ d.localShutdown
// We retransmit our shutdown: we may have updated our script and they may not have received it.
val shutdown = createShutdown(commitments1, d.localShutdown.scriptPubKey)
val sendQueue = Queue.empty[LightningMessage] ++ syncSuccess.retransmit :+ shutdown
// BOLT 2: A node if it has sent a previous shutdown MUST retransmit shutdown.
goto(SHUTDOWN) using d.copy(commitments = commitments1) sending sendQueue
goto(SHUTDOWN) using d.copy(commitments = commitments1, localShutdown = shutdown) sending sendQueue
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ import fr.acinq.eclair.reputation.Reputation
import fr.acinq.eclair.testutils.PimpTestProbe.convert
import fr.acinq.eclair.transactions.Transactions
import fr.acinq.eclair.transactions.Transactions._
import fr.acinq.eclair.wire.protocol.{AnnouncementSignatures, ChannelReestablish, ChannelUpdate, ClosingSigned, CommitSig, Error, FailureMessageCodecs, FailureReason, Init, PermanentChannelFailure, RevokeAndAck, Shutdown, UpdateAddHtlc, UpdateFailHtlc, UpdateFailMalformedHtlc, UpdateFee, UpdateFulfillHtlc}
import fr.acinq.eclair.wire.protocol.{AnnouncementSignatures, ChannelReestablish, ChannelUpdate, ClosingComplete, ClosingSig, ClosingSigned, CommitSig, Error, FailureMessageCodecs, FailureReason, Init, PermanentChannelFailure, RevokeAndAck, Shutdown, UpdateAddHtlc, UpdateFailHtlc, UpdateFailMalformedHtlc, UpdateFee, UpdateFulfillHtlc}
import fr.acinq.eclair.{BlockHeight, CltvExpiry, CltvExpiryDelta, MilliSatoshiLong, TestConstants, TestKitBaseClass, randomBytes32, randomKey}
import org.scalatest.funsuite.FixtureAnyFunSuiteLike
import org.scalatest.{Outcome, Tag}
Expand Down Expand Up @@ -1023,10 +1023,14 @@ class ShutdownStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike wit
alice2bob.forward(bob, channelReestablishAlice)
bob2alice.forward(alice, channelReestablishBob)
// They retransmit shutdown.
alice2bob.expectMsgType[Shutdown]
alice2bob.forward(bob)
bob2alice.expectMsgType[Shutdown]
bob2alice.forward(alice)
val shutdownAlice = alice2bob.expectMsgType[Shutdown]
alice2bob.forward(bob, shutdownAlice)
val shutdownBob = bob2alice.expectMsgType[Shutdown]
bob2alice.forward(alice, shutdownBob)
Seq(shutdownAlice, shutdownBob).foreach(shutdown => commitmentFormat match {
case _: SegwitV0CommitmentFormat => assert(shutdown.closeeNonce_opt.isEmpty)
case _: TaprootCommitmentFormat => assert(shutdown.closeeNonce_opt.nonEmpty)
})
// They resume HTLC settlement.
fulfillHtlc(0, r1, bob, alice, bob2alice, alice2bob)
crossSign(bob, alice, bob2alice, alice2bob)
Expand All @@ -1035,6 +1039,14 @@ class ShutdownStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike wit
crossSign(bob, alice, bob2alice, alice2bob)
awaitCond(alice.stateName == NEGOTIATING_SIMPLE)
awaitCond(bob.stateName == NEGOTIATING_SIMPLE)
// They can now sign the closing transaction.
val closingCompleteAlice = alice2bob.expectMsgType[ClosingComplete]
alice2bob.forward(bob, closingCompleteAlice)
bob2alice.expectMsgType[ClosingComplete] // ignored
val closingTx = bob2blockchain.expectMsgType[PublishFinalTx]
val closingSigBob = bob2alice.expectMsgType[ClosingSig]
bob2alice.forward(alice, closingSigBob)
assert(alice2blockchain.expectMsgType[PublishFinalTx].tx.txid == closingTx.tx.txid)
}

test("recv INPUT_RESTORED", Tag(ChannelStateTestsTags.SimpleClose), Tag(ChannelStateTestsTags.AnchorOutputsZeroFeeHtlcTxs)) { f =>
Expand Down