Skip to content

Commit 0b46790

Browse files
authored
Add more logs around commit_sig (#834)
We have a few force-close caused by invalid commit_sig messages. This may happen when multiple devices are used in parallel, but some reports don't seem to be using multiple devices. Unfortunately, we didn't log the details of the commit tx we're building in every case. Also, we only logged details of our local commit tx after validating the remote `commit_sig`, which means we cannot troubleshoot the case where we reject that remote `commit_sig`. We now log details of the commit tx construction in every scenario, which should allow detecting discrepancies between the local and remote states.
1 parent 9d90631 commit 0b46790

File tree

7 files changed

+51
-30
lines changed

7 files changed

+51
-30
lines changed

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

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ data class LocalCommit(val index: Long, val spec: CommitmentSpec, val txId: TxId
112112
localCommitIndex: Long,
113113
commitmentFormat: Transactions.CommitmentFormat,
114114
spec: CommitmentSpec,
115-
log: MDCLogger
115+
logger: MDCLogger
116116
): Either<ChannelException, LocalCommit> {
117117
val (localCommitTx, sortedHtlcTxs) = Commitments.makeLocalTxs(
118118
channelParams = channelParams,
@@ -125,6 +125,11 @@ data class LocalCommit(val index: Long, val spec: CommitmentSpec, val txId: TxId
125125
commitmentFormat = commitmentFormat,
126126
spec = spec,
127127
)
128+
logger.info {
129+
val htlcsIn = spec.htlcs.incomings().map { it.id }.joinToString(",")
130+
val htlcsOut = spec.htlcs.outgoings().map { it.id }.joinToString(",")
131+
"built local commit number=$localCommitIndex toLocalMsat=${spec.toLocal.toLong()} toRemoteMsat=${spec.toRemote.toLong()} htlc_in=$htlcsIn htlc_out=$htlcsOut feeratePerKw=${spec.feerate} txid=${localCommitTx.tx.txid} fundingTxId=${commitInput.outPoint.txid}"
132+
}
128133
val remoteSigOk = when (commitmentFormat) {
129134
Transactions.CommitmentFormat.AnchorOutputs -> localCommitTx.checkRemoteSig(fundingKey.publicKey(), remoteFundingPubKey, commit.signature)
130135
Transactions.CommitmentFormat.SimpleTaprootChannels -> when (val remoteSig = commit.sigOrPartialSig) {
@@ -136,7 +141,7 @@ data class LocalCommit(val index: Long, val spec: CommitmentSpec, val txId: TxId
136141
}
137142
}
138143
if (!remoteSigOk) {
139-
log.error { "remote signature $commit is invalid" }
144+
logger.error { "remote signature $commit is invalid" }
140145
return Either.Left(InvalidCommitmentSignature(channelParams.channelId, localCommitTx.tx.txid))
141146
}
142147
if (commit.htlcSignatures.size != sortedHtlcTxs.size) {
@@ -164,6 +169,7 @@ data class RemoteCommit(val index: Long, val spec: CommitmentSpec, val txid: TxI
164169
commitmentFormat: Transactions.CommitmentFormat,
165170
batchSize: Int,
166171
remoteNonce: IndividualNonce?,
172+
logger: MDCLogger
167173
): Either<ChannelException, CommitSig> {
168174
val fundingKey = channelKeys.fundingKey(fundingTxIndex)
169175
val commitKeys = channelKeys.remoteCommitmentKeys(channelParams, remotePerCommitmentPoint)
@@ -178,6 +184,12 @@ data class RemoteCommit(val index: Long, val spec: CommitmentSpec, val txid: TxI
178184
commitmentFormat = commitmentFormat,
179185
spec = spec
180186
)
187+
// NB: IN/OUT htlcs are inverted because this is the remote commit
188+
logger.info {
189+
val htlcsIn = spec.htlcs.outgoings().map { it.id }.joinToString(",")
190+
val htlcsOut = spec.htlcs.incomings().map { it.id }.joinToString(",")
191+
"built remote commit number=$index toLocalMsat=${spec.toLocal.toLong()} toRemoteMsat=${spec.toRemote.toLong()} htlc_in=$htlcsIn htlc_out=$htlcsOut feeratePerKw=${spec.feerate} txId=${remoteCommitTx.tx.txid} fundingTxId=${commitInput.outPoint.txid}"
192+
}
181193
val htlcSigs = sortedHtlcTxs.map { it.localSig(commitKeys) }
182194
return when (commitmentFormat) {
183195
Transactions.CommitmentFormat.AnchorOutputs -> {
@@ -197,7 +209,7 @@ data class RemoteCommit(val index: Long, val spec: CommitmentSpec, val txid: TxI
197209
}
198210
}
199211

200-
fun sign(channelParams: ChannelParams, channelKeys: ChannelKeys, signingSession: InteractiveTxSigningSession, remoteNonce: IndividualNonce?): Either<ChannelException, CommitSig> {
212+
fun sign(channelParams: ChannelParams, channelKeys: ChannelKeys, signingSession: InteractiveTxSigningSession, remoteNonce: IndividualNonce?, logger: MDCLogger): Either<ChannelException, CommitSig> {
201213
return sign(
202214
channelParams,
203215
signingSession.remoteCommitParams,
@@ -207,7 +219,8 @@ data class RemoteCommit(val index: Long, val spec: CommitmentSpec, val txid: TxI
207219
signingSession.commitInput(channelKeys),
208220
signingSession.fundingParams.commitmentFormat,
209221
batchSize = 1,
210-
remoteNonce
222+
remoteNonce,
223+
logger
211224
)
212225
}
213226
}
@@ -552,7 +565,7 @@ data class Commitment(
552565
remoteNextPerCommitmentPoint: PublicKey,
553566
batchSize: Int,
554567
nextRemoteNonce: IndividualNonce?,
555-
log: MDCLogger
568+
logger: MDCLogger
556569
): Either<ChannelException, Pair<Commitment, CommitSig>> {
557570
val fundingKey = localFundingKey(channelKeys)
558571
// remote commitment will include all local changes + remote acked changes
@@ -583,7 +596,7 @@ data class Commitment(
583596
}
584597
val htlcSigs = sortedHtlcTxs.map { it.localSig(commitKeys) }
585598
// NB: IN/OUT htlcs are inverted because this is the remote commit
586-
log.info {
599+
logger.info {
587600
val htlcsIn = spec.htlcs.outgoings().map { it.id }.joinToString(",")
588601
val htlcsOut = spec.htlcs.incomings().map { it.id }.joinToString(",")
589602
"built remote commit number=${remoteCommit.index + 1} toLocalMsat=${spec.toLocal.toLong()} toRemoteMsat=${spec.toRemote.toLong()} htlc_in=$htlcsIn htlc_out=$htlcsOut feeratePerKw=${spec.feerate} txId=${remoteCommitTx.tx.txid} fundingTxId=$fundingTxId"
@@ -593,7 +606,7 @@ data class Commitment(
593606
return Either.Right(Pair(commitment1, commitSig))
594607
}
595608

596-
fun receiveCommit(params: ChannelParams, channelKeys: ChannelKeys, commitKeys: LocalCommitmentKeys, changes: CommitmentChanges, commit: CommitSig, log: MDCLogger): Either<ChannelException, Commitment> {
609+
fun receiveCommit(params: ChannelParams, channelKeys: ChannelKeys, commitKeys: LocalCommitmentKeys, changes: CommitmentChanges, commit: CommitSig, logger: MDCLogger): Either<ChannelException, Commitment> {
597610
// they sent us a signature for *their* view of *our* next commit tx
598611
// so in terms of rev.hashes and indexes we have:
599612
// ourCommit.index -> our current revocation hash, which is about to become our old revocation hash
@@ -604,12 +617,7 @@ data class Commitment(
604617
// and will increment our index
605618
val fundingKey = localFundingKey(channelKeys)
606619
val spec = CommitmentSpec.reduce(localCommit.spec, changes.localChanges.acked, changes.remoteChanges.proposed)
607-
return LocalCommit.fromCommitSig(params, localCommitParams, commitKeys, fundingKey, remoteFundingPubkey, commitInput(fundingKey), commit, localCommit.index + 1, commitmentFormat, spec, log).map { localCommit1 ->
608-
log.info {
609-
val htlcsIn = spec.htlcs.incomings().map { it.id }.joinToString(",")
610-
val htlcsOut = spec.htlcs.outgoings().map { it.id }.joinToString(",")
611-
"built local commit number=${localCommit.index + 1} toLocalMsat=${spec.toLocal.toLong()} toRemoteMsat=${spec.toRemote.toLong()} htlc_in=$htlcsIn htlc_out=$htlcsOut feeratePerKw=${spec.feerate} txid=${localCommit1.txId} fundingTxId=$fundingTxId"
612-
}
620+
return LocalCommit.fromCommitSig(params, localCommitParams, commitKeys, fundingKey, remoteFundingPubkey, commitInput(fundingKey), commit, localCommit.index + 1, commitmentFormat, spec, logger).map { localCommit1 ->
613621
copy(localCommit = localCommit1)
614622
}
615623
}
@@ -854,13 +862,13 @@ data class Commitments(
854862
return failure?.let { Either.Left(it) } ?: Either.Right(copy(changes = changes1))
855863
}
856864

857-
fun sendCommit(channelKeys: ChannelKeys, remoteCommitNonces: Map<TxId, IndividualNonce>, log: MDCLogger): Either<ChannelException, Pair<Commitments, CommitSigs>> {
865+
fun sendCommit(channelKeys: ChannelKeys, remoteCommitNonces: Map<TxId, IndividualNonce>, logger: MDCLogger): Either<ChannelException, Pair<Commitments, CommitSigs>> {
858866
val remoteNextPerCommitmentPoint = remoteNextCommitInfo.right ?: return Either.Left(CannotSignBeforeRevocation(channelId))
859867
if (!changes.localHasChanges()) return Either.Left(CannotSignWithoutChanges(channelId))
860868
val (active1, sigs) = active.map { c ->
861869
val commitKeys = channelKeys.remoteCommitmentKeys(channelParams, remoteNextPerCommitmentPoint)
862870
val remoteNonce = remoteCommitNonces[c.fundingTxId]
863-
when (val res = c.sendCommit(channelParams, channelKeys, commitKeys, changes, remoteNextPerCommitmentPoint, active.size, remoteNonce, log)) {
871+
when (val res = c.sendCommit(channelParams, channelKeys, commitKeys, changes, remoteNextPerCommitmentPoint, active.size, remoteNonce, logger)) {
864872
is Either.Left -> return Either.Left(res.left)
865873
is Either.Right -> res.value
866874
}
@@ -876,7 +884,7 @@ data class Commitments(
876884
return Either.Right(Pair(commitments1, CommitSigs.fromSigs(sigs)))
877885
}
878886

879-
fun receiveCommit(commits: CommitSigs, channelKeys: ChannelKeys, log: MDCLogger): Either<ChannelException, Pair<Commitments, RevokeAndAck>> {
887+
fun receiveCommit(commits: CommitSigs, channelKeys: ChannelKeys, logger: MDCLogger): Either<ChannelException, Pair<Commitments, RevokeAndAck>> {
880888
// We may receive more commit_sig than the number of active commitments, because there can be a race where we send splice_locked
881889
// while our peer is sending us a batch of commit_sig. When that happens, we simply need to discard the commit_sig that belong
882890
// to commitments we deactivated.
@@ -890,7 +898,7 @@ data class Commitments(
890898
val commitKeys = channelKeys.localCommitmentKeys(channelParams, localCommitIndex + 1)
891899
// Signatures are sent in order (most recent first), calling `zip` will drop trailing sigs that are for deactivated/pruned commitments.
892900
val active1 = active.zip(sigs).map {
893-
when (val commitment1 = it.first.receiveCommit(channelParams, channelKeys, commitKeys, changes, it.second, log)) {
901+
when (val commitment1 = it.first.receiveCommit(channelParams, channelKeys, commitKeys, changes, it.second, logger)) {
894902
is Either.Left -> return Either.Left(commitment1.value)
895903
is Either.Right -> commitment1.value
896904
}

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

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1149,7 +1149,7 @@ data class InteractiveTxSigningSession(
11491149
localCommitIndex = localCommitIndex,
11501150
commitmentFormat = fundingParams.commitmentFormat,
11511151
spec = localCommit.value.spec,
1152-
log = logger
1152+
logger = logger
11531153
)) {
11541154
is Either.Left -> Pair(this, InteractiveTxSigningSessionAction.AbortFundingAttempt(signedLocalCommit.value))
11551155
is Either.Right -> {
@@ -1226,7 +1226,8 @@ data class InteractiveTxSigningSession(
12261226
remoteCommitmentIndex: Long,
12271227
commitTxFeerate: FeeratePerKw,
12281228
remotePerCommitmentPoint: PublicKey,
1229-
localHtlcs: Set<DirectedHtlc>
1229+
localHtlcs: Set<DirectedHtlc>,
1230+
logger: MDCLogger
12301231
): Either<ChannelException, Pair<InteractiveTxSigningSession, CommitSig>> {
12311232
val fundingKey = session.localFundingKey
12321233
val localCommitKeys = session.channelKeys.localCommitmentKeys(channelParams, localCommitmentIndex)
@@ -1265,6 +1266,13 @@ data class InteractiveTxSigningSession(
12651266
}
12661267
}
12671268
val localSigsOfRemoteHtlcTxs = firstCommitTx.remoteHtlcTxs.map { it.localSig(remoteCommitKeys) }
1269+
// NB: IN/OUT htlcs are inverted because this is the remote commit
1270+
logger.info {
1271+
val spec = firstCommitTx.remoteSpec
1272+
val htlcsIn = spec.htlcs.outgoings().map { it.id }.joinToString(",")
1273+
val htlcsOut = spec.htlcs.incomings().map { it.id }.joinToString(",")
1274+
"built remote commit number=$remoteCommitmentIndex toLocalMsat=${spec.toLocal.toLong()} toRemoteMsat=${spec.toRemote.toLong()} htlc_in=$htlcsIn htlc_out=$htlcsOut feeratePerKw=${spec.feerate} txId=${firstCommitTx.remoteCommitTx.tx.txid} fundingTxId=${unsignedTx.txid}"
1275+
}
12681276
val commitSig = CommitSig(channelParams.channelId, localSigOfRemoteCommitTx, localSigsOfRemoteHtlcTxs, batchSize = 1)
12691277
// We haven't received the remote commit_sig: we don't have local htlc txs yet.
12701278
val unsignedLocalCommit = UnsignedLocalCommit(localCommitmentIndex, firstCommitTx.localSpec, firstCommitTx.localCommitTx.tx.txid)

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -639,7 +639,8 @@ data class Normal(
639639
remoteCommitmentIndex = parentCommitment.remoteCommit.index,
640640
parentCommitment.localCommit.spec.feerate,
641641
parentCommitment.remoteCommit.remotePerCommitmentPoint,
642-
localHtlcs = parentCommitment.localCommit.spec.htlcs
642+
localHtlcs = parentCommitment.localCommit.spec.htlcs,
643+
logger
643644
)
644645
when (signingSession) {
645646
is Either.Left -> {

0 commit comments

Comments
 (0)