Skip to content

Commit 95f6261

Browse files
committed
Correctly set next_commitment_number during splice reconnect
As pointed out in lightning/bolts#1214, when reconnecting a partially signed `interactive-tx` session, we should set `next_commitment_number` to the current commitment number if we haven't received our peer's `commit_sig`, which tells them they need to retransmit it. More importantly, if our peer behaves correctly and sends us the current commitment number, we must not think that they're late and halt, waiting for them to send `error`. This commit fixes that by allowing our peers to use the current commitment number when they set `next_funding_txid`. Note that we keep retransmitting our `commit_sig` regardless of the value our peer set in `next_commitment_number`, because we need to wait for them to have an opportunity to upgrade. In a future commit we will stop sending spurious `commit_sig`.
1 parent ba75224 commit 95f6261

File tree

5 files changed

+184
-32
lines changed

5 files changed

+184
-32
lines changed

modules/core/src/commonMain/kotlin/fr/acinq/lightning/channel/InteractiveTx.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1028,6 +1028,11 @@ data class InteractiveTxSigningSession(
10281028
is Either.Left -> localCommit.value.commitTx.input
10291029
is Either.Right -> localCommit.value.publishableTxs.commitTx.input
10301030
}
1031+
// This value tells our peer whether we need them to retransmit their commit_sig on reconnection or not.
1032+
val reconnectNextLocalCommitmentNumber = when (localCommit) {
1033+
is Either.Left -> localCommit.value.index
1034+
is Either.Right -> localCommit.value.index + 1
1035+
}
10311036

10321037
fun receiveCommitSig(channelKeys: KeyManager.ChannelKeys, channelParams: ChannelParams, remoteCommitSig: CommitSig, currentBlockHeight: Long, logger: MDCLogger): Pair<InteractiveTxSigningSession, InteractiveTxSigningSessionAction> {
10331038
return when (localCommit) {

modules/core/src/commonMain/kotlin/fr/acinq/lightning/channel/states/Channel.kt

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,12 @@ import fr.acinq.lightning.channel.Helpers.Closing.claimRevokedRemoteCommitTxOutp
1616
import fr.acinq.lightning.channel.Helpers.Closing.getRemotePerCommitmentSecret
1717
import fr.acinq.lightning.crypto.KeyManager
1818
import fr.acinq.lightning.db.ChannelClosingType
19-
import fr.acinq.lightning.logging.*
19+
import fr.acinq.lightning.logging.LoggingContext
20+
import fr.acinq.lightning.logging.MDCLogger
2021
import fr.acinq.lightning.serialization.channel.Encryption.from
2122
import fr.acinq.lightning.transactions.Transactions.TransactionWithInputInfo.ClosingTx
22-
import fr.acinq.lightning.utils.*
23+
import fr.acinq.lightning.utils.msat
24+
import fr.acinq.lightning.utils.sat
2325
import fr.acinq.lightning.wire.*
2426
import kotlinx.coroutines.flow.StateFlow
2527
import kotlinx.coroutines.flow.filterNotNull
@@ -306,7 +308,7 @@ sealed class PersistedChannelState : ChannelState() {
306308
val myFirstPerCommitmentPoint = keyManager.channelKeys(state.channelParams.localParams.fundingKeyPath).commitmentPoint(0)
307309
ChannelReestablish(
308310
channelId = channelId,
309-
nextLocalCommitmentNumber = 1,
311+
nextLocalCommitmentNumber = state.signingSession.reconnectNextLocalCommitmentNumber,
310312
nextRemoteRevocationNumber = 0,
311313
yourLastCommitmentSecret = PrivateKey(ByteVector32.Zeroes),
312314
myCurrentPerCommitmentPoint = myFirstPerCommitmentPoint,
@@ -316,15 +318,28 @@ sealed class PersistedChannelState : ChannelState() {
316318
is ChannelStateWithCommitments -> {
317319
val yourLastPerCommitmentSecret = state.commitments.remotePerCommitmentSecrets.lastIndex?.let { state.commitments.remotePerCommitmentSecrets.getHash(it) } ?: ByteVector32.Zeroes
318320
val myCurrentPerCommitmentPoint = keyManager.channelKeys(state.commitments.params.localParams.fundingKeyPath).commitmentPoint(state.commitments.localCommitIndex)
321+
// If we disconnected while signing a funding transaction, we may need our peer to retransmit their commit_sig.
322+
val nextLocalCommitmentNumber = when (state) {
323+
is WaitForFundingConfirmed -> when (state.rbfStatus) {
324+
is RbfStatus.WaitingForSigs -> state.rbfStatus.session.reconnectNextLocalCommitmentNumber
325+
else -> state.commitments.localCommitIndex + 1
326+
}
327+
is Normal -> when (state.spliceStatus) {
328+
is SpliceStatus.WaitingForSigs -> state.spliceStatus.session.reconnectNextLocalCommitmentNumber
329+
else -> state.commitments.localCommitIndex + 1
330+
}
331+
else -> state.commitments.localCommitIndex + 1
332+
}
333+
// If we disconnected while signing a funding transaction, we may need our peer to (re)transmit their tx_signatures.
319334
val unsignedFundingTxId = when (state) {
320335
is WaitForFundingConfirmed -> state.getUnsignedFundingTxId()
321-
is Normal -> state.getUnsignedFundingTxId() // a splice was in progress, we tell our peer that we are remembering it and are expecting signatures
336+
is Normal -> state.getUnsignedFundingTxId()
322337
else -> null
323338
}
324339
val tlvs: TlvStream<ChannelReestablishTlv> = unsignedFundingTxId?.let { TlvStream(ChannelReestablishTlv.NextFunding(it)) } ?: TlvStream.empty()
325340
ChannelReestablish(
326341
channelId = channelId,
327-
nextLocalCommitmentNumber = state.commitments.localCommitIndex + 1,
342+
nextLocalCommitmentNumber = nextLocalCommitmentNumber,
328343
nextRemoteRevocationNumber = state.commitments.remoteCommitIndex,
329344
yourLastCommitmentSecret = PrivateKey(yourLastPerCommitmentSecret),
330345
myCurrentPerCommitmentPoint = myCurrentPerCommitmentPoint,

modules/core/src/commonMain/kotlin/fr/acinq/lightning/channel/states/Syncing.kt

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -406,7 +406,7 @@ data class Syncing(val state: PersistedChannelState, val channelReestablishSent:
406406
// we resend the same updates and the same sig, and preserve the same ordering
407407
val signedUpdates = commitments.changes.localChanges.signed
408408
val commitSigs = commitments.active.map { it.nextRemoteCommit }.filterIsInstance<NextRemoteCommit>().map { it.sig }
409-
SyncResult.Success(retransmit = when (retransmitRevocation) {
409+
val retransmit = when (retransmitRevocation) {
410410
null -> buildList {
411411
addAll(signedUpdates)
412412
addAll(commitSigs)
@@ -424,7 +424,8 @@ data class Syncing(val state: PersistedChannelState, val channelReestablishSent:
424424
addAll(commitSigs)
425425
}
426426
}
427-
})
427+
}
428+
SyncResult.Success(retransmit)
428429
}
429430
remoteChannelReestablish.nextLocalCommitmentNumber == (commitments.nextRemoteCommitIndex + 1) -> {
430431
// we just sent a new commit_sig, they have received it but we haven't received their revocation
@@ -449,6 +450,11 @@ data class Syncing(val state: PersistedChannelState, val channelReestablishSent:
449450
// they have acknowledged the last commit_sig we sent
450451
SyncResult.Success(retransmit = listOfNotNull(retransmitRevocation))
451452
}
453+
remoteChannelReestablish.nextLocalCommitmentNumber == commitments.remoteCommitIndex && remoteChannelReestablish.nextFundingTxId != null -> {
454+
// they haven't received the commit_sig we sent as part of signing a splice transaction
455+
// we will retransmit it before exchanging tx_signatures
456+
SyncResult.Success(retransmit = listOfNotNull(retransmitRevocation))
457+
}
452458
remoteChannelReestablish.nextLocalCommitmentNumber < (commitments.remoteCommitIndex + 1) -> {
453459
// they are behind
454460
SyncResult.Failure.RemoteLate

0 commit comments

Comments
 (0)