Skip to content

Commit

Permalink
Refactor commitments and meta-commitments (#2579)
Browse files Browse the repository at this point in the history
Move all the logic that was previously inside `Commitments` to `MetaCommitments`.
The code changes are quite minimal, with a few subtleties: we need to update the
local/remote commit indices separately of the commitments themselves, because
they're not in the same data structure (yet).

Some of these changes show where the current model doesn't work well:

- `remotePerCommitmentPoint` is duplicated across all remote commits:
  it's not obvious, but it has to be the same value
- htlcs should be shared between commitments
- the coupling between `remoteNextCommitInfo` and `nextRemoteCommit_opt`
  is really weird to work with (especially in `receiveRevocation`): this can be fixed
  by slightly re-working the model

The data model will be updated in future commits to fix these issues.
  • Loading branch information
t-bast authored Jan 24, 2023
1 parent 9611f9e commit e3e1ee5
Show file tree
Hide file tree
Showing 10 changed files with 941 additions and 980 deletions.
6 changes: 1 addition & 5 deletions eclair-core/src/main/scala/fr/acinq/eclair/DBChecker.scala
Original file line number Diff line number Diff line change
Expand Up @@ -27,22 +27,18 @@ object DBChecker extends Logging {
* Tests if the channels data in the DB is valid (and throws an exception if not):
* - it is compatible with the current version of eclair
* - channel keys can be re-generated from the channel seed
*
* @param nodeParams node parameters
*/
def checkChannelsDB(nodeParams: NodeParams): Seq[PersistentChannelData] = {
Try(nodeParams.db.channels.listLocalChannels()) match {
case Success(channels) =>
channels.foreach(data => if (!data.commitments.validateSeed(nodeParams.channelKeyManager)) throw InvalidChannelSeedException(data.channelId))
channels.foreach(data => if (!data.metaCommitments.validateSeed(nodeParams.channelKeyManager)) throw InvalidChannelSeedException(data.channelId))
channels
case Failure(_) => throw IncompatibleDBException
}
}

/**
* Tests if the network database is readable.
*
* @param nodeParams
*/
def checkNetworkDB(nodeParams: NodeParams): Unit =
Try(nodeParams.db.network.listChannels(), nodeParams.db.network.listNodes()) match {
Expand Down
861 changes: 22 additions & 839 deletions eclair-core/src/main/scala/fr/acinq/eclair/channel/Commitments.scala

Large diffs are not rendered by default.

12 changes: 6 additions & 6 deletions eclair-core/src/main/scala/fr/acinq/eclair/channel/Helpers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -454,8 +454,8 @@ object Helpers {
val channelKeyPath = keyManager.keyPath(localParams, channelConfig)
val commitmentInput = makeFundingInputInfo(fundingTxHash, fundingTxOutputIndex, fundingAmount, fundingPubKey.publicKey, remoteParams.fundingPubKey)
val localPerCommitmentPoint = keyManager.commitmentPoint(channelKeyPath, commitmentIndex)
val (localCommitTx, _) = Commitments.makeLocalTxs(keyManager, channelConfig, channelFeatures, commitmentIndex, localParams, remoteParams, commitmentInput, localPerCommitmentPoint, localSpec)
val (remoteCommitTx, _) = Commitments.makeRemoteTxs(keyManager, channelConfig, channelFeatures, commitmentIndex, localParams, remoteParams, commitmentInput, remotePerCommitmentPoint, remoteSpec)
val (localCommitTx, _) = Commitment.makeLocalTxs(keyManager, channelConfig, channelFeatures, commitmentIndex, localParams, remoteParams, commitmentInput, localPerCommitmentPoint, localSpec)
val (remoteCommitTx, _) = Commitment.makeRemoteTxs(keyManager, channelConfig, channelFeatures, commitmentIndex, localParams, remoteParams, commitmentInput, remotePerCommitmentPoint, remoteSpec)

Right(localSpec, localCommitTx, remoteSpec, remoteCommitTx)
}
Expand Down Expand Up @@ -502,13 +502,13 @@ object Helpers {
case Some(revocation) =>
SyncResult.Success(retransmit = revocation +: signedUpdates :+ commitSig)
}
case Left(waitingForRevocation) if remoteChannelReestablish.nextLocalCommitmentNumber == (common.nextRemoteCommitIndex + 1) =>
case Left(_) if remoteChannelReestablish.nextLocalCommitmentNumber == (common.nextRemoteCommitIndex + 1) =>
// we just sent a new commit_sig, they have received it but we haven't received their revocation
SyncResult.Success(retransmit = retransmitRevocation_opt.toSeq)
case Left(waitingForRevocation) if remoteChannelReestablish.nextLocalCommitmentNumber < common.nextRemoteCommitIndex =>
case Left(_) if remoteChannelReestablish.nextLocalCommitmentNumber < common.nextRemoteCommitIndex =>
// they are behind
SyncResult.RemoteLate
case Left(waitingForRevocation) =>
case Left(_) =>
// we are behind
SyncResult.LocalLateUnproven(
ourRemoteCommitmentNumber = common.nextRemoteCommitIndex,
Expand Down Expand Up @@ -982,7 +982,7 @@ object Helpers {
* Claim our htlc outputs only
*/
def claimHtlcOutputs(keyManager: ChannelKeyManager, commitments: Commitments, remoteCommit: RemoteCommit, feeEstimator: FeeEstimator)(implicit log: LoggingAdapter): Map[OutPoint, Option[ClaimHtlcTx]] = {
val (remoteCommitTx, _) = Commitments.makeRemoteTxs(keyManager, commitments.channelConfig, commitments.channelFeatures, remoteCommit.index, commitments.localParams, commitments.remoteParams, commitments.commitInput, remoteCommit.remotePerCommitmentPoint, remoteCommit.spec)
val (remoteCommitTx, _) = Commitment.makeRemoteTxs(keyManager, commitments.channelConfig, commitments.channelFeatures, remoteCommit.index, commitments.localParams, commitments.remoteParams, commitments.commitInput, remoteCommit.remotePerCommitmentPoint, remoteCommit.spec)
require(remoteCommitTx.tx.txid == remoteCommit.txid, "txid mismatch, cannot recompute the current remote commit tx")
val channelKeyPath = keyManager.keyPath(commitments.localParams, commitments.channelConfig)
val localFundingPubkey = keyManager.fundingPublicKey(commitments.localParams.fundingKeyPath).publicKey
Expand Down
Loading

0 comments on commit e3e1ee5

Please sign in to comment.