You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
In an "outdated commitment" scenario where we are on the up-to-date side, we always react by force-closing the channel immediately, not giving our peer a chance to fix their data and restart. On top of that, we consider this a commitment sync error, instead of clearly logging that our counterparty is using outdated data.
Addressing this turned out to be rabbit-holey: our sync code is quite complicated and is a bit redundant because we separate between:
- checking whether we are late
- deciding what messages we need to retransmit
Also, discovered a missing corner case when syncing in SHUTDOWN state.
// if next_remote_revocation_number is greater than our local commitment index, it means that either we are using an outdated commitment, or they are lying
1670
-
// but first we need to make sure that the last per_commitment_secret that they claim to have received from us is correct for that next_remote_revocation_number minus 1
1671
-
if (keyManager.commitmentSecret(channelKeyPath, nextRemoteRevocationNumber -1) == yourLastPerCommitmentSecret) {
1672
-
log.warning(s"counterparty proved that we have an outdated (revoked) local commitment!!! ourCommitmentNumber=${d.commitments.localCommit.index} theirCommitmentNumber=$nextRemoteRevocationNumber")
1673
-
// their data checks out, we indeed seem to be using an old revoked commitment, and must absolutely *NOT* publish it, because that would be a cheating attempt and they
1674
-
// would punish us by taking all the funds in the channel
1675
-
valexc=PleasePublishYourCommitment(d.channelId)
1676
-
valerror=Error(d.channelId, exc.getMessage)
1677
-
goto(WAIT_FOR_REMOTE_PUBLISH_FUTURE_COMMITMENT) using DATA_WAIT_FOR_REMOTE_PUBLISH_FUTURE_COMMITMENT(d.commitments, channelReestablish) storing() sending error
1678
-
} else {
1679
-
// they lied! the last per_commitment_secret they claimed to have received from us is invalid
// if next_local_commit_number is more than one more our remote commitment index, it means that either we are using an outdated commitment, or they are lying
1684
-
log.warning(s"counterparty says that they have a more recent commitment than the one we know of!!! ourCommitmentNumber=${d.commitments.remoteNextCommitInfo.left.toOption.map(_.nextRemoteCommit.index).getOrElse(d.commitments.remoteCommit.index)} theirCommitmentNumber=$nextLocalCommitmentNumber")
1685
-
// there is no way to make sure that they are saying the truth, the best thing to do is ask them to publish their commitment right now
1686
-
// maybe they will publish their commitment, in that case we need to remember their commitment point in order to be able to claim our outputs
1687
-
// not that if they don't comply, we could publish our own commitment (it is not stale, otherwise we would be in the case above)
1688
-
valexc=PleasePublishYourCommitment(d.channelId)
1689
-
valerror=Error(d.channelId, exc.getMessage)
1690
-
goto(WAIT_FOR_REMOTE_PUBLISH_FUTURE_COMMITMENT) using DATA_WAIT_FOR_REMOTE_PUBLISH_FUTURE_COMMITMENT(d.commitments, channelReestablish) storing() sending error
1691
-
case _ =>
1666
+
Syncing.checkSync(keyManager, d, channelReestablish) match {
if (channelReestablish.nextLocalCommitmentNumber ==1&& d.commitments.localCommit.index ==0) {
1694
1673
// If next_local_commitment_number is 1 in both the channel_reestablish it sent and received, then the node MUST retransmit funding_locked, otherwise it MUST NOT
log.error(s"counterparty proved that we have an outdated (revoked) local commitment!!! ourLocalCommitmentNumber=${res.ourLocalCommitmentNumber} theirRemoteCommitmentNumber=${res.theirRemoteCommitmentNumber}")
2235
+
// their data checks out, we indeed seem to be using an old revoked commitment, and must absolutely *NOT* publish it, because that would be a cheating attempt and they
2236
+
// would punish us by taking all the funds in the channel
2237
+
handleOutdatedCommitment(channelReestablish, d)
2238
+
caseres: Syncing.SyncResult.LocalLateUnproven=>
2239
+
log.error(s"our local commitment is in sync, but counterparty says that they have a more recent remote commitment than the one we know of (they could be lying)!!! ourRemoteCommitmentNumber=${res.ourRemoteCommitmentNumber} theirCommitmentNumber=${res.theirLocalCommitmentNumber}")
2240
+
// there is no way to make sure that they are saying the truth, the best thing to do is "call their bluff" and
2241
+
// ask them to publish their commitment right now. If they weren't lying and they do publish their commitment,
2242
+
// we need to remember their commitment point in order to be able to claim our outputs
2243
+
handleOutdatedCommitment(channelReestablish, d)
2244
+
caseres: Syncing.SyncResult.RemoteLying=>
2245
+
log.error(s"counterparty is lying about us having an outdated commitment!!! ourLocalCommitmentNumber=${res.ourLocalCommitmentNumber} theirRemoteCommitmentNumber=${res.theirRemoteCommitmentNumber}")
2246
+
// they are deliberately trying to fool us into thinking we have a late commitment
case_: ForcedLocalCommit=> log.warning(s"force-closing channel at user request")
2270
-
case _ if stateName ==WAIT_FOR_OPEN_CHANNEL=> log.warning(s"${cause.getMessage} while processing msg=${msg.getOrElse("n/a").getClass.getSimpleName} in state=$stateName")
2271
-
case _ => log.error(s"${cause.getMessage} while processing msg=${msg.getOrElse("n/a").getClass.getSimpleName} in state=$stateName")
2272
-
}
2273
-
cause match {
2274
-
case_: ChannelException=> ()
2275
-
case _ => log.error(cause, s"msg=${msg.getOrElse("n/a")} stateData=$stateData")
2292
+
case _ if msg.exists(_.isInstanceOf[OpenChannel]) || msg.exists(_.isInstanceOf[AcceptChannel]) =>
2293
+
// invalid remote channel parameters are logged as warning
2294
+
log.warning(s"${cause.getMessage} while processing msg=${msg.getOrElse("n/a").getClass.getSimpleName} in state=$stateName")
2295
+
case_: ChannelException=>
2296
+
log.error(s"${cause.getMessage} while processing msg=${msg.getOrElse("n/a").getClass.getSimpleName} in state=$stateName")
2297
+
case _ =>
2298
+
// unhandled error: we dump the channel data, and print the stack trace
Copy file name to clipboardExpand all lines: eclair-core/src/main/scala/fr/acinq/eclair/channel/ChannelExceptions.scala
+1-3Lines changed: 1 addition & 3 deletions
Original file line number
Diff line number
Diff line change
@@ -91,9 +91,7 @@ case class CannotSignWithoutChanges (override val channelId: Byte
91
91
caseclassCannotSignBeforeRevocation (overridevalchannelId:ByteVector32) extendsChannelException(channelId, "cannot sign until next revocation hash is received")
caseclassInvalidRevokedCommitProof (overridevalchannelId:ByteVector32, ourCommitmentNumber: Long, theirCommitmentNumber: Long, perCommitmentSecret: PrivateKey) extendsChannelException(channelId, s"counterparty claimed that we have a revoked commit but their proof doesn't check out: ourCommitmentNumber=$ourCommitmentNumber theirCommitmentNumber=$theirCommitmentNumber perCommitmentSecret=$perCommitmentSecret")
caseclassInvalidRevokedCommitProof (overridevalchannelId:ByteVector32, ourLocalCommitmentNumber: Long, theirRemoteCommitmentNumber: Long, invalidPerCommitmentSecret: PrivateKey) extendsChannelException(channelId, s"counterparty claimed that we have a revoked commit but their proof doesn't check out: ourCommitmentNumber=$ourLocalCommitmentNumber theirCommitmentNumber=$theirRemoteCommitmentNumber perCommitmentSecret=$invalidPerCommitmentSecret")
97
95
caseclassInvalidFailureCode (overridevalchannelId:ByteVector32) extendsChannelException(channelId, "UpdateFailMalformedHtlc message doesn't have BADONION bit set")
98
96
caseclassPleasePublishYourCommitment (overridevalchannelId:ByteVector32) extendsChannelException(channelId, "please publish your local commitment")
0 commit comments