Skip to content

Commit 8292732

Browse files
committed
Update dual-funding and splicing workflows
We extends the interactive tx session to include: - an optional funding nonce for the shared input (i.e. the funding tx that is being spent) - a nonce for the commit tx that is being created, and another nonce that will become the channel's "next remote nonce" once the session completes The funding nonce is random and its lifecycle is bound to the interactive session. revoke_and_ack is extended to include a list of funding_tx_id -> nonce tuples (one for each active commitment). channel_restablish is extended to include the same list of funding_tx_id -> nonce tuples, as well as an optional "current commit nonce" if we got disconnected while a splice was in progress before both nodes exchanged their commit signatures: if that is the case, we need to re-send our peer's current signature and will use this nonce to compute it.
1 parent 5c21a4d commit 8292732

File tree

15 files changed

+324
-169
lines changed

15 files changed

+324
-169
lines changed

eclair-core/src/main/scala/fr/acinq/eclair/channel/Commitments.scala

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ object LocalCommit {
222222

223223
/** The remote commitment maps to a commitment transaction that only our peer can sign and broadcast. */
224224
case class RemoteCommit(index: Long, spec: CommitmentSpec, txId: TxId, remotePerCommitmentPoint: PublicKey) {
225-
def sign(params: ChannelParams, channelKeys: ChannelKeys, fundingTxIndex: Long, remoteFundingPubKey: PublicKey, commitInput: InputInfo): CommitSig = {
225+
def sign(params: ChannelParams, channelKeys: ChannelKeys, fundingTxIndex: Long, remoteFundingPubKey: PublicKey, commitInput: InputInfo, remoteNonce_opt: Option[IndividualNonce]): CommitSig = {
226226
val fundingKey = channelKeys.fundingKey(fundingTxIndex)
227227
val commitKeys = RemoteCommitmentKeys(params, channelKeys, remotePerCommitmentPoint)
228228
val (remoteCommitTx, htlcTxs) = Commitment.makeRemoteTxs(params, commitKeys, index, fundingKey, remoteFundingPubKey, commitInput, spec)
@@ -232,7 +232,10 @@ case class RemoteCommit(index: Long, spec: CommitmentSpec, txId: TxId, remotePer
232232
case _: SegwitV0CommitmentFormat =>
233233
val sig = remoteCommitTx.sign(fundingKey, remoteFundingPubKey).sig
234234
CommitSig(params.channelId, sig, htlcSigs.toList)
235-
case _: SimpleTaprootChannelCommitmentFormat => ???
235+
case _: SimpleTaprootChannelCommitmentFormat =>
236+
val localNonce = NonceGenerator.signingNonce(fundingKey.publicKey)
237+
val Right(psig) = remoteCommitTx.partialSign(fundingKey, remoteFundingPubKey, Map.empty, localNonce, Seq(localNonce.publicNonce, remoteNonce_opt.get))
238+
CommitSig(params.channelId, ByteVector64.Zeroes, htlcSigs.toList, TlvStream[CommitSigTlv](CommitSigTlv.PartialSignatureWithNonceTlv(psig)))
236239
}
237240
}
238241
}
@@ -666,14 +669,14 @@ case class Commitment(fundingTxIndex: Long,
666669
case _ =>
667670
None
668671
}
669-
val tlvs = Set(
670-
if (batchSize > 1) Some(CommitSigTlv.BatchTlv(batchSize)) else None,
671-
partialSig
672-
).flatten[CommitSigTlv]
673672
val sig = params.commitmentFormat match {
674673
case _: SimpleTaprootChannelCommitmentFormat => ByteVector64.Zeroes
675674
case _: SegwitV0CommitmentFormat => remoteCommitTx.sign(fundingKey, remoteFundingPubKey).sig
676675
}
676+
val tlvs = Set(
677+
if (batchSize > 1) Some(CommitSigTlv.BatchTlv(batchSize)) else None,
678+
partialSig
679+
).flatten[CommitSigTlv]
677680
val commitSig = CommitSig(params.channelId, sig, htlcSigs.toList, TlvStream(tlvs))
678681
val nextRemoteCommit = NextRemoteCommit(commitSig, RemoteCommit(remoteCommit.index + 1, spec, remoteCommitTx.tx.txid, remoteNextPerCommitmentPoint))
679682
(copy(nextRemoteCommit_opt = Some(nextRemoteCommit)), commitSig)
@@ -1056,18 +1059,14 @@ case class Commitments(params: ChannelParams,
10561059
}
10571060
}
10581061

1059-
def sendCommit(channelKeys: ChannelKeys, nextRemoteNonces: List[IndividualNonce] = List.empty)(implicit log: LoggingAdapter): Either[ChannelException, (Commitments, CommitSigs)] = {
1062+
def sendCommit(channelKeys: ChannelKeys, nextRemoteNonces: Map[TxId, IndividualNonce] = Map.empty)(implicit log: LoggingAdapter): Either[ChannelException, (Commitments, CommitSigs)] = {
10601063
remoteNextCommitInfo match {
10611064
case Right(_) if !changes.localHasChanges => Left(CannotSignWithoutChanges(channelId))
10621065
case Right(remoteNextPerCommitmentPoint) =>
10631066
val commitKeys = RemoteCommitmentKeys(params, channelKeys, remoteNextPerCommitmentPoint)
1064-
var nonceIndex = 0
10651067

10661068
def remoteNonce(c: Commitment) = this.params.commitmentFormat match {
1067-
case _: SimpleTaprootChannelCommitmentFormat =>
1068-
val n = nextRemoteNonces(nonceIndex)
1069-
nonceIndex = nonceIndex + 1
1070-
Some(n)
1069+
case _: SimpleTaprootChannelCommitmentFormat => nextRemoteNonces.get(c.fundingTxId)
10711070
case _ => None
10721071
}
10731072

@@ -1112,7 +1111,7 @@ case class Commitments(params: ChannelParams,
11121111
val fundingKey = channelKeys.fundingKey(c.fundingTxIndex)
11131112
val n = NonceGenerator.verificationNonce(c.fundingTxId, fundingKey, localCommitIndex + 2).publicNonce
11141113
log.debug(s"revokeandack: creating verification nonce $n fundingIndex = ${c.fundingTxIndex} commit index = ${localCommitIndex + 2}")
1115-
n
1114+
c.fundingTxId -> n
11161115
})
11171116
TlvStream(RevokeAndAckTlv.NextLocalNoncesTlv(nonces.toList))
11181117
case _ =>
@@ -1239,17 +1238,17 @@ case class Commitments(params: ChannelParams,
12391238
/** This function should be used to ignore a commit_sig that we've already received. */
12401239
def ignoreRetransmittedCommitSig(channelKeys: ChannelKeys, commitSig: CommitSig): Boolean = {
12411240
val isLatestSig = latest.localCommit.remoteSig match {
1242-
case ChannelSpendSignature.IndividualSignature(latestRemoteSig) => latestRemoteSig == commitSig.signature
1241+
case ChannelSpendSignature.IndividualSignature(latestRemoteSig) => false // latestRemoteSig == commitSig.signature
12431242
case psig: ChannelSpendSignature.PartialSignatureWithNonce =>
1244-
val fundingKey = channelKeys.fundingKey((latest.fundingTxIndex))
1243+
val fundingKey = channelKeys.fundingKey(latest.fundingTxIndex)
12451244
val fundingTxId = if (!this.params.channelFeatures.hasFeature(Features.DualFunding) && localCommitIndex == 0 && latest.fundingTxIndex == 0) {
12461245
TxId(ByteVector32.Zeroes)
12471246
} else latest.fundingTxId
12481247
val localNonce = NonceGenerator.verificationNonce(fundingTxId, fundingKey, localCommitIndex)
12491248
val commitKeys = LocalCommitmentKeys(params, channelKeys, latest.localCommit.index)
12501249
val (commitTx, _) = Commitment.makeLocalTxs(params, commitKeys, latest.localCommit.index, fundingKey, latest.remoteFundingPubKey, latest.localCommit.input, latest.localCommit.spec)
12511250
val result = commitTx.checkRemotePartialSignature(fundingKey.publicKey, latest.remoteFundingPubKey, psig, localNonce.publicNonce)
1252-
result
1251+
false // TODO: is this valid ?
12531252
}
12541253
params.channelFeatures.hasFeature(Features.DualFunding) && isLatestSig
12551254
}

0 commit comments

Comments
 (0)