Skip to content

Commit 3374a21

Browse files
committed
Simplify how to create taproot transactions
1 parent bfc7c84 commit 3374a21

File tree

10 files changed

+318
-320
lines changed

10 files changed

+318
-320
lines changed

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

Lines changed: 18 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,7 @@ case class RemoteCommit(index: Long, spec: CommitmentSpec, txid: TxId, remotePer
289289
def sign(keyManager: ChannelKeyManager, params: ChannelParams, fundingTxIndex: Long, remoteFundingPubKey: PublicKey, commitInput: InputInfo, remoteNonce_opt: Option[IndividualNonce])(implicit log: LoggingAdapter): CommitSig = {
290290
val (remoteCommitTx, htlcTxs) = Commitment.makeRemoteTxs(keyManager, params.channelConfig, params.channelFeatures, index, params.localParams, params.remoteParams, fundingTxIndex, remoteFundingPubKey, commitInput, remotePerCommitmentPoint, spec)
291291
val localFundingPubKey = keyManager.fundingPublicKey(params.localParams.fundingKeyPath, fundingTxIndex)
292-
val (sig, tlvStream) = if (params.commitmentFormat.useTaproot) {
292+
val (sig, tlvStream) = if (commitInput.isP2tr) {
293293
val localNonce = keyManager.signingNonce(localFundingPubKey.publicKey)
294294
val Some(remoteNonce) = remoteNonce_opt
295295
val Right(localPartialSigOfRemoteTx) = keyManager.partialSign(remoteCommitTx, localFundingPubKey, remoteFundingPubKey, TxOwner.Remote, localNonce, remoteNonce)
@@ -704,12 +704,12 @@ case class Commitment(fundingTxIndex: Long,
704704
val spec = CommitmentSpec.reduce(remoteCommit.spec, changes.remoteChanges.acked, changes.localChanges.proposed)
705705
val (remoteCommitTx, htlcTxs) = Commitment.makeRemoteTxs(keyManager, params.channelConfig, params.channelFeatures, remoteCommit.index + 1, params.localParams, params.remoteParams, fundingTxIndex, remoteFundingPubKey, commitInput, remoteNextPerCommitmentPoint, spec)
706706
val localFundingPubKey = keyManager.fundingPublicKey(params.localParams.fundingKeyPath, fundingTxIndex)
707-
val sig = if (params.commitmentFormat.useTaproot) {
707+
val sig = if (remoteCommitTx.input.isP2tr) {
708708
ByteVector64.Zeroes
709709
} else {
710710
keyManager.sign(remoteCommitTx, localFundingPubKey, TxOwner.Remote, params.commitmentFormat)
711711
}
712-
val partialSig: Set[CommitSigTlv] = if (params.commitmentFormat.useTaproot) {
712+
val partialSig: Set[CommitSigTlv] = if (remoteCommitTx.input.isP2tr) {
713713
val localNonce = keyManager.signingNonce(localFundingPubKey.publicKey)
714714
val Some(remoteNonce) = nextRemoteNonce_opt
715715
val Right(psig) = keyManager.partialSign(remoteCommitTx, localFundingPubKey, remoteFundingPubKey, TxOwner.Remote, localNonce, remoteNonce)
@@ -802,7 +802,7 @@ object Commitment {
802802
val localFundingPubkey = keyManager.fundingPublicKey(localParams.fundingKeyPath, fundingTxIndex).publicKey
803803
val localDelayedPaymentPubkey = Generators.derivePubKey(keyManager.delayedPaymentPoint(channelKeyPath).publicKey, localPerCommitmentPoint)
804804
val localHtlcPubkey = Generators.derivePubKey(keyManager.htlcPoint(channelKeyPath).publicKey, localPerCommitmentPoint)
805-
val remotePaymentPubkey = if (channelFeatures.hasFeature(Features.StaticRemoteKey) || channelFeatures.hasFeature(Features.SimpleTaprootStaging)) {
805+
val remotePaymentPubkey = if (channelFeatures.hasFeature(Features.StaticRemoteKey) || channelFeatures.hasFeature(Features.SimpleTaproot) || channelFeatures.hasFeature(Features.SimpleTaprootStaging)) {
806806
remoteParams.paymentBasepoint
807807
} else {
808808
Generators.derivePubKey(remoteParams.paymentBasepoint, localPerCommitmentPoint)
@@ -1122,12 +1122,16 @@ case class Commitments(params: ChannelParams,
11221122
remoteNextCommitInfo match {
11231123
case Right(_) if !changes.localHasChanges => Left(CannotSignWithoutChanges(channelId))
11241124
case Right(remoteNextPerCommitmentPoint) =>
1125-
val (active1, sigs) = if (this.params.commitmentFormat.useTaproot) {
1126-
require(active.size <= nextRemoteNonces.size, s"we have ${active.size} commitments but ${nextRemoteNonces.size} remote musig2 nonces")
1127-
active.zip(nextRemoteNonces).map { case (c, n) => c.sendCommit(keyManager, params, changes, remoteNextPerCommitmentPoint, active.size, Some(n)) } unzip
1128-
} else {
1129-
active.map(_.sendCommit(keyManager, params, changes, remoteNextPerCommitmentPoint, active.size, None)).unzip
1130-
}
1125+
var nonceIndex = 0
1126+
val (active1, sigs) = active.map { c =>
1127+
val remoteNonce_opt = if (c.commitInput.isP2tr) {
1128+
val n = nextRemoteNonces(nonceIndex)
1129+
nonceIndex = nonceIndex + 1
1130+
Some(n)
1131+
} else None
1132+
c.sendCommit(keyManager, params, changes, remoteNextPerCommitmentPoint, active.size, remoteNonce_opt)
1133+
} unzip
1134+
11311135
val commitments1 = copy(
11321136
changes = changes.copy(
11331137
localChanges = changes.localChanges.copy(proposed = Nil, signed = changes.localChanges.proposed),
@@ -1164,16 +1168,14 @@ case class Commitments(params: ChannelParams,
11641168
// we will send our revocation preimage + our next revocation hash
11651169
val localPerCommitmentSecret = keyManager.commitmentSecret(channelKeyPath, localCommitIndex)
11661170
val localNextPerCommitmentPoint = keyManager.commitmentPoint(channelKeyPath, localCommitIndex + 2)
1167-
val tlvStream: TlvStream[RevokeAndAckTlv] = if (params.commitmentFormat.useTaproot) {
1168-
val nonces = this.active.map(c => {
1171+
val tlvStream: TlvStream[RevokeAndAckTlv] = {
1172+
val nonces = this.active.filter(_.commitInput.isP2tr).map(c => {
11691173
val fundingPubkey = keyManager.fundingPublicKey(params.localParams.fundingKeyPath, c.fundingTxIndex).publicKey
11701174
val n = keyManager.verificationNonce(c.fundingTxId, fundingPubkey, localCommitIndex + 2)
11711175
log.debug(s"revokeandack: creating verification nonce ${n._2} fundingIndex = ${c.fundingTxIndex} commit index = ${localCommitIndex + 2}")
11721176
n
11731177
})
1174-
TlvStream(RevokeAndAckTlv.NextLocalNoncesTlv(nonces.map(_._2).toList))
1175-
} else {
1176-
TlvStream.empty
1178+
if (nonces.isEmpty) TlvStream.empty else TlvStream(RevokeAndAckTlv.NextLocalNoncesTlv(nonces.map(_._2).toList))
11771179
}
11781180
val revocation = RevokeAndAck(
11791181
channelId = channelId,
@@ -1196,7 +1198,7 @@ case class Commitments(params: ChannelParams,
11961198
remoteNextCommitInfo match {
11971199
case Right(_) => Left(UnexpectedRevocation(channelId))
11981200
case Left(_) if revocation.perCommitmentSecret.publicKey != active.head.remoteCommit.remotePerCommitmentPoint => Left(InvalidRevocation(channelId))
1199-
case Left(_) if this.params.commitmentFormat.useTaproot && revocation.nexLocalNonces.isEmpty => Left(MissingNextLocalNonce(channelId))
1201+
case Left(_) if this.active.exists(_.commitInput.isP2tr) && revocation.nexLocalNonces.isEmpty => Left(MissingNextLocalNonce(channelId))
12001202
case Left(_) =>
12011203
// Since htlcs are shared across all commitments, we generate the actions only once based on the first commitment.
12021204
val receivedHtlcs = changes.remoteChanges.signed.collect {
@@ -1446,36 +1448,6 @@ case class Commitments(params: ChannelParams,
14461448
def resolveCommitment(shortChannelId: RealShortChannelId): Option[Commitment] = {
14471449
all.find(c => c.shortChannelId_opt.contains(shortChannelId))
14481450
}
1449-
1450-
/**
1451-
* Generate local verification nonces for a specific funding tx index and commit tx index
1452-
*
1453-
* @param keyManager key manager that will generate actual nonces
1454-
* @param fundingIndex funding tx index
1455-
* @param commitIndex commit tx index
1456-
* @return a public nonce for thr provided fundint tx index and commit tx index if taproot is used, None otherwise
1457-
*/
1458-
def generateLocalNonce(keyManager: ChannelKeyManager, fundingIndex: Long, commitIndex: Long): Option[IndividualNonce] = {
1459-
if (latest.params.commitmentFormat.useTaproot) {
1460-
val localFundingKey = keyManager.fundingPublicKey(params.localParams.fundingKeyPath, latest.fundingTxIndex).publicKey
1461-
Some(keyManager.verificationNonce(latest.fundingTxId, localFundingKey, commitIndex)._2)
1462-
} else {
1463-
None
1464-
}
1465-
}
1466-
1467-
/**
1468-
* Create local verification nonces a specific funding tx index and a range of commit tx indexes
1469-
*
1470-
* @param keyManager key manager that will generate actual nonces
1471-
* @param fundingIndex funding tx index
1472-
* @param commitIndexes range of commit tx indexes
1473-
* @return a list of nonces if raproot is used, or an empty list
1474-
*/
1475-
def generateLocalNonces(keyManager: ChannelKeyManager, fundingIndex: Long, commitIndexes: Long*): List[IndividualNonce] = {
1476-
commitIndexes.toList.flatMap(commitIndex => generateLocalNonce(keyManager, fundingIndex, commitIndex))
1477-
}
1478-
14791451
}
14801452

14811453
object Commitments {

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

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -463,7 +463,7 @@ object Helpers {
463463
/**
464464
* Check whether we are in sync with our peer.
465465
*/
466-
def checkSync(keyManager: ChannelKeyManager, commitments: Commitments, remoteChannelReestablish: ChannelReestablish)(implicit log: LoggingAdapter): SyncResult = {
466+
def checkSync(keyManager: ChannelKeyManager, commitments: Commitments, remoteChannelReestablish: ChannelReestablish): SyncResult = {
467467

468468
// This is done in two steps:
469469
// - step 1: we check our local commitment
@@ -526,14 +526,14 @@ object Helpers {
526526
val channelKeyPath = keyManager.keyPath(commitments.params.localParams, commitments.params.channelConfig)
527527
val localPerCommitmentSecret = keyManager.commitmentSecret(channelKeyPath, commitments.localCommitIndex - 1)
528528
val localNextPerCommitmentPoint = keyManager.commitmentPoint(channelKeyPath, commitments.localCommitIndex + 1)
529-
val tlvStream: TlvStream[RevokeAndAckTlv] = if (commitments.params.commitmentFormat.useTaproot) {
530-
val nonces = commitments.active.map(c => {
531-
val fundingPubkey = keyManager.fundingPublicKey(commitments.params.localParams.fundingKeyPath, c.fundingTxIndex).publicKey
532-
keyManager.verificationNonce(c.fundingTxId, fundingPubkey, commitments.localCommitIndex + 1)
533-
})
534-
TlvStream(RevokeAndAckTlv.NextLocalNoncesTlv(nonces.map(_._2).toList))
535-
} else {
529+
val nonces = commitments.active.filter(_.commitInput.isP2tr).map { c =>
530+
val fundingPubkey = keyManager.fundingPublicKey(commitments.params.localParams.fundingKeyPath, c.fundingTxIndex).publicKey
531+
keyManager.verificationNonce(c.fundingTxId, fundingPubkey, commitments.localCommitIndex + 1)
532+
}
533+
val tlvStream: TlvStream[RevokeAndAckTlv] = if (nonces.isEmpty) {
536534
TlvStream.empty
535+
} else {
536+
TlvStream(RevokeAndAckTlv.NextLocalNoncesTlv(nonces.map(_._2).toList))
537537
}
538538
val revocation = RevokeAndAck(
539539
channelId = commitments.channelId,
@@ -723,7 +723,7 @@ object Helpers {
723723
val dummyClosingTxs = Transactions.makeSimpleClosingTxs(commitment.commitInput, commitment.localCommit.spec, SimpleClosingTxFee.PaidByUs(0 sat), currentBlockHeight.toLong, localScriptPubkey, remoteScriptPubkey)
724724
dummyClosingTxs.preferred_opt match {
725725
case Some(dummyTx) =>
726-
val dummySignedTx = if (commitment.params.commitmentFormat.useTaproot) {
726+
val dummySignedTx = if (commitment.commitInput.isP2tr) {
727727
Transactions.addAggregatedSignature(dummyTx, Transactions.PlaceHolderSig)
728728
} else {
729729
Transactions.addSigs(dummyTx, Transactions.PlaceHolderPubKey, Transactions.PlaceHolderPubKey, Transactions.PlaceHolderSig, Transactions.PlaceHolderSig)
@@ -739,7 +739,7 @@ object Helpers {
739739
case _ => return Left(CannotGenerateClosingTx(commitment.channelId))
740740
}
741741
val localFundingPubKey = keyManager.fundingPublicKey(commitment.localParams.fundingKeyPath, commitment.fundingTxIndex)
742-
val tlvs = if (commitment.params.commitmentFormat.useTaproot) {
742+
val tlvs = if (commitment.commitInput.isP2tr) {
743743
def partialSign(tx: ClosingTx) = {
744744
val Right(psig) = keyManager.partialSign(tx, localFundingPubKey, commitment.remoteFundingPubKey, TxOwner.Local, localClosingNonce_opt.get, remoteClosingNonce_opt.get)
745745
psig
@@ -773,7 +773,7 @@ object Helpers {
773773
val closingTxs = Transactions.makeSimpleClosingTxs(commitment.commitInput, commitment.localCommit.spec, closingFee, closingComplete.lockTime, localScriptPubkey, remoteScriptPubkey)
774774
// If our output isn't dust, they must provide a signature for a transaction that includes it.
775775
// Note that we're the closee, so we look for signatures including the closee output.
776-
if (commitment.params.commitmentFormat.useTaproot) {
776+
if (commitment.commitInput.isP2tr) {
777777
(closingTxs.localAndRemote_opt, closingTxs.localOnly_opt) match {
778778
case (Some(_), Some(_)) if closingComplete.closerAndCloseeOutputsPartialSig_opt.isEmpty && closingComplete.closeeOutputOnlyPartialSig_opt.isEmpty => return Left(MissingCloseSignature(commitment.channelId))
779779
case (Some(_), None) if closingComplete.closerAndCloseeOutputsPartialSig_opt.isEmpty => return Left(MissingCloseSignature(commitment.channelId))
@@ -789,7 +789,7 @@ object Helpers {
789789
}
790790
}
791791

792-
if (commitment.params.commitmentFormat.useTaproot) {
792+
if (commitment.commitInput.isP2tr) {
793793
// We choose the closing signature that matches our preferred closing transaction.
794794
val closingTxsWithSigs = Seq(
795795
closingComplete.closerAndCloseeOutputsPartialSig_opt.flatMap(remoteSig => closingTxs.localAndRemote_opt.map(tx => (tx, remoteSig, localSig => ClosingTlv.CloserAndCloseeOutputsPartialSignature(localSig)))),
@@ -838,7 +838,7 @@ object Helpers {
838838
* for their next closing_sig that will match our latest closing_complete.
839839
*/
840840
def receiveSimpleClosingSig(keyManager: ChannelKeyManager, commitment: FullCommitment, closingTxs: ClosingTxs, closingSig: ClosingSig, localNonce: Option[(SecretNonce, IndividualNonce)] = None, remoteNonce: Option[IndividualNonce] = None): Either[ChannelException, ClosingTx] = {
841-
if (commitment.params.commitmentFormat.useTaproot) {
841+
if (commitment.commitInput.isP2tr) {
842842
val closingTxsWithSig = Seq(
843843
closingSig.closerAndCloseeOutputsPartialSig_opt.flatMap(sig => closingTxs.localAndRemote_opt.map(tx => (tx, sig))),
844844
closingSig.closerOutputOnlyPartialSig_opt.flatMap(sig => closingTxs.localOnly_opt.map(tx => (tx, sig))),
@@ -948,7 +948,7 @@ object Helpers {
948948

949949
// first we will claim our main output as soon as the delay is over
950950
val mainDelayedTx = withTxGenerationLog("local-main-delayed") {
951-
Transactions.makeClaimLocalDelayedOutputTx(tx, commitment.localParams.dustLimit, localRevocationPubkey, commitment.remoteParams.toSelfDelay, localDelayedPubkey, finalScriptPubKey, feeratePerKwDelayed, commitment.params.commitmentFormat).map(claimDelayed => {
951+
Transactions.makeClaimLocalDelayedOutputTx(tx, commitment.localParams.dustLimit, localRevocationPubkey, commitment.remoteParams.toSelfDelay, localDelayedPubkey, finalScriptPubKey, feeratePerKwDelayed).map(claimDelayed => {
952952
val sig = keyManager.sign(claimDelayed, keyManager.delayedPaymentPoint(channelKeyPath), localPerCommitmentPoint, TxOwner.Local, commitment.params.commitmentFormat)
953953
Transactions.addSigs(claimDelayed, sig)
954954
})
@@ -992,10 +992,10 @@ object Helpers {
992992
}
993993
val claimAnchorTxs = List(
994994
withTxGenerationLog("local-anchor") {
995-
Transactions.makeClaimLocalAnchorOutputTx(lcp.commitTx, localPaymentKey, confirmationTarget, commitment.params.commitmentFormat)
995+
Transactions.makeClaimLocalAnchorOutputTx(lcp.commitTx, localPaymentKey, confirmationTarget)
996996
},
997997
withTxGenerationLog("remote-anchor") {
998-
Transactions.makeClaimRemoteAnchorOutputTx(lcp.commitTx, remotePaymentKey, commitment.params.commitmentFormat)
998+
Transactions.makeClaimRemoteAnchorOutputTx(lcp.commitTx, remotePaymentKey)
999999
}
10001000
).flatten
10011001
lcp.copy(claimAnchorTxs = claimAnchorTxs)
@@ -1062,7 +1062,7 @@ object Helpers {
10621062
val localRevocationPubkey = Generators.revocationPubKey(commitment.remoteParams.revocationBasepoint, localPerCommitmentPoint)
10631063
val localDelayedPubkey = Generators.derivePubKey(keyManager.delayedPaymentPoint(channelKeyPath).publicKey, localPerCommitmentPoint)
10641064
val htlcDelayedTx = withTxGenerationLog("htlc-delayed") {
1065-
Transactions.makeHtlcDelayedTx(tx, commitment.localParams.dustLimit, localRevocationPubkey, commitment.remoteParams.toSelfDelay, localDelayedPubkey, finalScriptPubKey, feeratePerKwDelayed, commitment.params.commitmentFormat).map(claimDelayed => {
1065+
Transactions.makeHtlcDelayedTx(tx, commitment.localParams.dustLimit, localRevocationPubkey, commitment.remoteParams.toSelfDelay, localDelayedPubkey, finalScriptPubKey, feeratePerKwDelayed).map(claimDelayed => {
10661066
val sig = keyManager.sign(claimDelayed, keyManager.delayedPaymentPoint(channelKeyPath), localPerCommitmentPoint, TxOwner.Local, commitment.params.commitmentFormat)
10671067
Transactions.addSigs(claimDelayed, sig)
10681068
})
@@ -1127,10 +1127,10 @@ object Helpers {
11271127
}
11281128
val claimAnchorTxs = List(
11291129
withTxGenerationLog("local-anchor") {
1130-
Transactions.makeClaimLocalAnchorOutputTx(rcp.commitTx, localPaymentKey, confirmationTarget, commitment.params.commitmentFormat)
1130+
Transactions.makeClaimLocalAnchorOutputTx(rcp.commitTx, localPaymentKey, confirmationTarget)
11311131
},
11321132
withTxGenerationLog("remote-anchor") {
1133-
Transactions.makeClaimRemoteAnchorOutputTx(rcp.commitTx, remotePaymentKey, commitment.params.commitmentFormat)
1133+
Transactions.makeClaimRemoteAnchorOutputTx(rcp.commitTx, remotePaymentKey)
11341134
}
11351135
).flatten
11361136
rcp.copy(claimAnchorTxs = claimAnchorTxs)
@@ -1164,7 +1164,7 @@ object Helpers {
11641164
})
11651165
}
11661166
case _: AnchorOutputsCommitmentFormat => withTxGenerationLog("remote-main-delayed") {
1167-
Transactions.makeClaimRemoteDelayedOutputTx(tx, params.localParams.dustLimit, localPaymentPoint, finalScriptPubKey, feeratePerKwMain, params.commitmentFormat).map(claimMain => {
1167+
Transactions.makeClaimRemoteDelayedOutputTx(tx, params.localParams.dustLimit, localPaymentPoint, finalScriptPubKey, feeratePerKwMain).map(claimMain => {
11681168
val sig = keyManager.sign(claimMain, keyManager.paymentPoint(channelKeyPath), TxOwner.Local, params.commitmentFormat)
11691169
Transactions.addSigs(claimMain, sig)
11701170
})
@@ -1300,7 +1300,7 @@ object Helpers {
13001300
})
13011301
}
13021302
case _: AnchorOutputsCommitmentFormat => withTxGenerationLog("remote-main-delayed") {
1303-
Transactions.makeClaimRemoteDelayedOutputTx(commitTx, localParams.dustLimit, localPaymentPoint, finalScriptPubKey, feerateMain, commitmentFormat).map(claimMain => {
1303+
Transactions.makeClaimRemoteDelayedOutputTx(commitTx, localParams.dustLimit, localPaymentPoint, finalScriptPubKey, feerateMain).map(claimMain => {
13041304
val sig = keyManager.sign(claimMain, keyManager.paymentPoint(channelKeyPath), TxOwner.Local, commitmentFormat)
13051305
Transactions.addSigs(claimMain, sig)
13061306
})
@@ -1310,7 +1310,7 @@ object Helpers {
13101310

13111311
// then we punish them by stealing their main output
13121312
val mainPenaltyTx = withTxGenerationLog("main-penalty") {
1313-
Transactions.makeMainPenaltyTx(commitTx, localParams.dustLimit, remoteRevocationPubkey, finalScriptPubKey, localParams.toSelfDelay, remoteDelayedPaymentPubkey, feeratePenalty, commitmentFormat).map(txinfo => {
1313+
Transactions.makeMainPenaltyTx(commitTx, localParams.dustLimit, remoteRevocationPubkey, finalScriptPubKey, localParams.toSelfDelay, remoteDelayedPaymentPubkey, feeratePenalty).map(txinfo => {
13141314
val sig = keyManager.sign(txinfo, keyManager.revocationPoint(channelKeyPath), remotePerCommitmentSecret, TxOwner.Local, commitmentFormat)
13151315
Transactions.addSigs(txinfo, sig)
13161316
})

0 commit comments

Comments
 (0)