Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reestablish partially signed splice with current remote_commitment_number #2965

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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 @@ -507,6 +507,10 @@ object Helpers {
case Right(_) if remoteChannelReestablish.nextLocalCommitmentNumber == (commitments.remoteCommitIndex + 1) =>
// they have acknowledged the last commit_sig we sent
SyncResult.Success(retransmit = retransmitRevocation_opt.toSeq)
case Right(_) if remoteChannelReestablish.nextLocalCommitmentNumber == commitments.remoteCommitIndex && remoteChannelReestablish.nextFundingTxId_opt.nonEmpty =>
// they haven't received the commit_sig we sent as part of signing a splice transaction
// we will retransmit it before exchanging tx_signatures
SyncResult.Success(retransmit = retransmitRevocation_opt.toSeq)
case Right(_) if remoteChannelReestablish.nextLocalCommitmentNumber < (commitments.remoteCommitIndex + 1) =>
// they are behind
SyncResult.RemoteLate
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,23 @@ class WaitForDualFundingSignedStateSpec extends TestKitBaseClass with FixtureAny
reconnect(f, fundingTxId)
}

test("recv INPUT_DISCONNECTED (commit_sig not received, next_commitment_number = 0)", Tag(ChannelStateTestsTags.DualFunding)) { f =>
import f._

val fundingTxId = alice.stateData.asInstanceOf[DATA_WAIT_FOR_DUAL_FUNDING_SIGNED].signingSession.fundingTx.txId
alice2bob.expectMsgType[CommitSig] // Bob doesn't receive Alice's commit_sig
bob2alice.expectMsgType[CommitSig] // Alice doesn't receive Bob's commit_sig
awaitCond(alice.stateName == WAIT_FOR_DUAL_FUNDING_SIGNED)
awaitCond(bob.stateName == WAIT_FOR_DUAL_FUNDING_SIGNED)

alice ! INPUT_DISCONNECTED
awaitCond(alice.stateName == OFFLINE)
bob ! INPUT_DISCONNECTED
awaitCond(bob.stateName == OFFLINE)

reconnect(f, fundingTxId, aliceCommitmentNumber = 0, bobCommitmentNumber = 0)
}

test("recv INPUT_DISCONNECTED (commit_sig partially received)", Tag(ChannelStateTestsTags.DualFunding)) { f =>
import f._

Expand All @@ -397,6 +414,25 @@ class WaitForDualFundingSignedStateSpec extends TestKitBaseClass with FixtureAny
reconnect(f, fundingTxId)
}

test("recv INPUT_DISCONNECTED (commit_sig partially received, next_commitment_number = 0)", Tag(ChannelStateTestsTags.DualFunding)) { f =>
import f._

val fundingTxId = alice.stateData.asInstanceOf[DATA_WAIT_FOR_DUAL_FUNDING_SIGNED].signingSession.fundingTx.txId
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)

alice ! INPUT_DISCONNECTED
awaitCond(alice.stateName == OFFLINE)
bob ! INPUT_DISCONNECTED
awaitCond(bob.stateName == OFFLINE)

reconnect(f, fundingTxId, aliceCommitmentNumber = 0)
}

test("recv INPUT_DISCONNECTED (commit_sig received)", Tag(ChannelStateTestsTags.DualFunding)) { f =>
import f._

Expand Down Expand Up @@ -454,7 +490,7 @@ class WaitForDualFundingSignedStateSpec extends TestKitBaseClass with FixtureAny
assert(listener.expectMsgType[TransactionPublished].tx.txid == fundingTxId)
}

private def reconnect(f: FixtureParam, fundingTxId: TxId): Unit = {
private def reconnect(f: FixtureParam, fundingTxId: TxId, aliceCommitmentNumber: Long = 1, bobCommitmentNumber: Long = 1): Unit = {
import f._

val listener = TestProbe()
Expand All @@ -467,11 +503,11 @@ class WaitForDualFundingSignedStateSpec extends TestKitBaseClass with FixtureAny
val channelReestablishAlice = alice2bob.expectMsgType[ChannelReestablish]
assert(channelReestablishAlice.nextFundingTxId_opt.contains(fundingTxId))
assert(channelReestablishAlice.nextLocalCommitmentNumber == 1)
alice2bob.forward(bob)
alice2bob.forward(bob, channelReestablishAlice.copy(nextLocalCommitmentNumber = aliceCommitmentNumber))
val channelReestablishBob = bob2alice.expectMsgType[ChannelReestablish]
assert(channelReestablishBob.nextFundingTxId_opt.contains(fundingTxId))
assert(channelReestablishBob.nextLocalCommitmentNumber == 1)
bob2alice.forward(alice)
bob2alice.forward(alice, channelReestablishBob.copy(nextLocalCommitmentNumber = bobCommitmentNumber))
bob2alice.expectMsgType[CommitSig]
bob2alice.forward(alice)
alice2bob.expectMsgType[CommitSig]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1560,17 +1560,17 @@ class NormalSplicesStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLik
awaitCond(bob.stateName == OFFLINE)
}

private def reconnect(f: FixtureParam): (ChannelReestablish, ChannelReestablish) = {
private def reconnect(f: FixtureParam, sendReestablish: Boolean = true): (ChannelReestablish, ChannelReestablish) = {
import f._

val aliceInit = Init(alice.stateData.asInstanceOf[ChannelDataWithCommitments].commitments.params.localParams.initFeatures)
val bobInit = Init(bob.stateData.asInstanceOf[ChannelDataWithCommitments].commitments.params.localParams.initFeatures)
alice ! INPUT_RECONNECTED(alice2bob.ref, aliceInit, bobInit)
bob ! INPUT_RECONNECTED(bob2alice.ref, bobInit, aliceInit)
val channelReestablishAlice = alice2bob.expectMsgType[ChannelReestablish]
alice2bob.forward(bob)
if (sendReestablish) alice2bob.forward(bob)
val channelReestablishBob = bob2alice.expectMsgType[ChannelReestablish]
bob2alice.forward(alice)
if (sendReestablish) bob2alice.forward(alice)
(channelReestablishAlice, channelReestablishBob)
}

Expand Down Expand Up @@ -1638,6 +1638,54 @@ class NormalSplicesStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLik
resolveHtlcs(f, htlcs)
}

test("disconnect (commit_sig not received, reestablish with previous commitment_number)") { f =>
import f._

val htlcs = setupHtlcs(f)
val aliceCommitIndex = alice.stateData.asInstanceOf[DATA_NORMAL].commitments.localCommitIndex
val bobCommitIndex = bob.stateData.asInstanceOf[DATA_NORMAL].commitments.localCommitIndex

val sender = initiateSpliceWithoutSigs(f, spliceIn_opt = Some(SpliceIn(500_000 sat)), spliceOut_opt = Some(SpliceOut(100_000 sat, defaultSpliceOutScriptPubKey)))
alice2bob.expectMsgType[CommitSig] // Bob doesn't receive Alice's commit_sig
bob2alice.expectMsgType[CommitSig] // Alice doesn't receive Bob's commit_sig
awaitCond(alice.stateData.asInstanceOf[DATA_NORMAL].spliceStatus.isInstanceOf[SpliceStatus.SpliceWaitingForSigs])
val spliceStatus = alice.stateData.asInstanceOf[DATA_NORMAL].spliceStatus.asInstanceOf[SpliceStatus.SpliceWaitingForSigs]

disconnect(f)
val (channelReestablishAlice, channelReestablishBob) = reconnect(f, sendReestablish = false)
assert(channelReestablishAlice.nextFundingTxId_opt.contains(spliceStatus.signingSession.fundingTx.txId))
assert(channelReestablishAlice.nextLocalCommitmentNumber == aliceCommitIndex + 1)
alice2bob.forward(bob, channelReestablishAlice.copy(nextLocalCommitmentNumber = aliceCommitIndex))
assert(channelReestablishBob.nextFundingTxId_opt.contains(spliceStatus.signingSession.fundingTx.txId))
assert(channelReestablishBob.nextLocalCommitmentNumber == bobCommitIndex + 1)
bob2alice.forward(alice, channelReestablishBob.copy(nextLocalCommitmentNumber = bobCommitIndex))

// Alice and Bob retransmit commit_sig and tx_signatures.
alice2bob.expectMsgType[CommitSig]
alice2bob.forward(bob)
bob2alice.expectMsgType[CommitSig]
bob2alice.forward(alice)
bob2alice.expectMsgType[TxSignatures]
bob2alice.forward(alice)
alice2bob.expectMsgType[TxSignatures]
alice2bob.forward(bob)
sender.expectMsgType[RES_SPLICE]

val spliceTx = alice.stateData.asInstanceOf[DATA_NORMAL].commitments.latest.localFundingStatus.signedTx_opt.get
alice2blockchain.expectWatchFundingConfirmed(spliceTx.txid)
bob2blockchain.expectWatchFundingConfirmed(spliceTx.txid)
alice ! WatchFundingConfirmedTriggered(BlockHeight(42), 0, spliceTx)
alice2bob.expectMsgType[SpliceLocked]
alice2bob.forward(bob)
bob ! WatchFundingConfirmedTriggered(BlockHeight(42), 0, spliceTx)
bob2alice.expectMsgType[SpliceLocked]
bob2alice.forward(alice)
awaitCond(alice.stateData.asInstanceOf[DATA_NORMAL].commitments.active.size == 1)
awaitCond(bob.stateData.asInstanceOf[DATA_NORMAL].commitments.active.size == 1)

resolveHtlcs(f, htlcs)
}

test("disconnect (commit_sig received by alice)") { f =>
import f._

Expand Down Expand Up @@ -1686,6 +1734,56 @@ class NormalSplicesStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLik
resolveHtlcs(f, htlcs)
}

test("disconnect (commit_sig received by alice, reestablish with previous commitment_number)") { f =>
import f._

val htlcs = setupHtlcs(f)
val aliceCommitIndex = alice.stateData.asInstanceOf[DATA_NORMAL].commitments.localCommitIndex
val bobCommitIndex = bob.stateData.asInstanceOf[DATA_NORMAL].commitments.localCommitIndex
assert(aliceCommitIndex != bobCommitIndex)

val sender = initiateSpliceWithoutSigs(f, spliceIn_opt = Some(SpliceIn(500_000 sat)), spliceOut_opt = Some(SpliceOut(100_000 sat, defaultSpliceOutScriptPubKey)))
alice2bob.expectMsgType[CommitSig] // Bob doesn't receive Alice's commit_sig
bob2alice.expectMsgType[CommitSig]
bob2alice.forward(alice)
awaitCond(alice.stateData.asInstanceOf[DATA_NORMAL].spliceStatus.isInstanceOf[SpliceStatus.SpliceWaitingForSigs])
val spliceStatus = alice.stateData.asInstanceOf[DATA_NORMAL].spliceStatus.asInstanceOf[SpliceStatus.SpliceWaitingForSigs]

disconnect(f)
val (channelReestablishAlice, channelReestablishBob) = reconnect(f, sendReestablish = false)
assert(channelReestablishAlice.nextFundingTxId_opt.contains(spliceStatus.signingSession.fundingTx.txId))
assert(channelReestablishAlice.nextLocalCommitmentNumber == aliceCommitIndex + 1)
alice2bob.forward(bob, channelReestablishAlice)
assert(channelReestablishBob.nextFundingTxId_opt.contains(spliceStatus.signingSession.fundingTx.txId))
assert(channelReestablishBob.nextLocalCommitmentNumber == bobCommitIndex + 1)
bob2alice.forward(alice, channelReestablishBob.copy(nextLocalCommitmentNumber = bobCommitIndex))

// Alice and Bob retransmit commit_sig and tx_signatures.
alice2bob.expectMsgType[CommitSig]
alice2bob.forward(bob)
bob2alice.expectMsgType[CommitSig]
bob2alice.forward(alice)
bob2alice.expectMsgType[TxSignatures]
bob2alice.forward(alice)
alice2bob.expectMsgType[TxSignatures]
alice2bob.forward(bob)
sender.expectMsgType[RES_SPLICE]

val spliceTx = alice.stateData.asInstanceOf[DATA_NORMAL].commitments.latest.localFundingStatus.signedTx_opt.get
alice2blockchain.expectWatchFundingConfirmed(spliceTx.txid)
bob2blockchain.expectWatchFundingConfirmed(spliceTx.txid)
alice ! WatchFundingConfirmedTriggered(BlockHeight(42), 0, spliceTx)
alice2bob.expectMsgType[SpliceLocked]
alice2bob.forward(bob)
bob ! WatchFundingConfirmedTriggered(BlockHeight(42), 0, spliceTx)
bob2alice.expectMsgType[SpliceLocked]
bob2alice.forward(alice)
awaitCond(alice.stateData.asInstanceOf[DATA_NORMAL].commitments.active.size == 1)
awaitCond(bob.stateData.asInstanceOf[DATA_NORMAL].commitments.active.size == 1)

resolveHtlcs(f, htlcs)
}

test("disconnect (tx_signatures sent by bob)") { f =>
import f._

Expand Down
Loading