Skip to content

Commit e28b326

Browse files
committed
Rework nonce generation
1 parent 334fe59 commit e28b326

File tree

4 files changed

+56
-55
lines changed

4 files changed

+56
-55
lines changed

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

Lines changed: 6 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@ case class LocalCommit(index: Long, spec: CommitmentSpec, commitTxAndRemoteSig:
246246
object LocalCommit {
247247
def fromCommitSig(keyManager: ChannelKeyManager, params: ChannelParams, fundingTxId: TxId,
248248
fundingTxIndex: Long, remoteFundingPubKey: PublicKey, commitInput: InputInfo,
249-
commit: CommitSig, localCommitIndex: Long, spec: CommitmentSpec, localPerCommitmentPoint: PublicKey, localNonce_opt: Option[(SecretNonce, IndividualNonce)])(implicit log: LoggingAdapter): Either[ChannelException, LocalCommit] = {
249+
commit: CommitSig, localCommitIndex: Long, spec: CommitmentSpec, localPerCommitmentPoint: PublicKey)(implicit log: LoggingAdapter): Either[ChannelException, LocalCommit] = {
250250
val (localCommitTx, htlcTxs) = Commitment.makeLocalTxs(keyManager, params.channelConfig, params.channelFeatures, localCommitIndex, params.localParams, params.remoteParams, fundingTxIndex, remoteFundingPubKey, commitInput, localPerCommitmentPoint, spec)
251251
commit.sigOrPartialSig match {
252252
case Left(sig) =>
@@ -255,12 +255,9 @@ object LocalCommit {
255255
}
256256
case Right(psig) =>
257257
val fundingPubkey = keyManager.fundingPublicKey(params.localParams.fundingKeyPath, fundingTxIndex).publicKey
258-
val Some(localNonce) = localNonce_opt
258+
val localNonce = keyManager.verificationNonce(params.localParams.fundingKeyPath, fundingTxIndex, keyManager.keyPath(params.localParams, params.channelConfig), localCommitIndex)
259259
if (!localCommitTx.checkPartialSignature(psig, fundingPubkey, localNonce._2, remoteFundingPubKey)) {
260260
log.debug(s"fromCommitSig: invalid partial signature $psig fundingPubkey = $fundingPubkey, fundingTxIndex = $fundingTxIndex localCommitIndex = $localCommitIndex localNonce = $localNonce remoteFundingPubKey = $remoteFundingPubKey")
261-
262-
val localNonce1 = keyManager.verificationNonce(params.localParams.fundingKeyPath, fundingTxIndex, keyManager.keyPath(params.localParams, params.channelConfig), localCommitIndex)
263-
log.debug(s"with $localNonce1 ${localCommitTx.checkPartialSignature(psig, fundingPubkey, localNonce1._2, remoteFundingPubKey)}")
264261
return Left(InvalidCommitmentSignature(params.channelId, fundingTxId, fundingTxIndex, localCommitTx.tx))
265262
}
266263
}
@@ -705,7 +702,7 @@ case class Commitment(fundingTxIndex: Long,
705702
(copy(nextRemoteCommit_opt = Some(nextRemoteCommit)), commitSig)
706703
}
707704

708-
def receiveCommit(keyManager: ChannelKeyManager, params: ChannelParams, changes: CommitmentChanges, localPerCommitmentPoint: PublicKey, commit: CommitSig, localNonce_opt: Option[(SecretNonce, IndividualNonce)])(implicit log: LoggingAdapter): Either[ChannelException, Commitment] = {
705+
def receiveCommit(keyManager: ChannelKeyManager, params: ChannelParams, changes: CommitmentChanges, localPerCommitmentPoint: PublicKey, commit: CommitSig)(implicit log: LoggingAdapter): Either[ChannelException, Commitment] = {
709706
// they sent us a signature for *their* view of *our* next commit tx
710707
// so in terms of rev.hashes and indexes we have:
711708
// ourCommit.index -> our current revocation hash, which is about to become our old revocation hash
@@ -716,7 +713,7 @@ case class Commitment(fundingTxIndex: Long,
716713
// and will increment our index
717714
val localCommitIndex = localCommit.index + 1
718715
val spec = CommitmentSpec.reduce(localCommit.spec, changes.localChanges.acked, changes.remoteChanges.proposed)
719-
LocalCommit.fromCommitSig(keyManager, params, fundingTxId, fundingTxIndex, remoteFundingPubKey, commitInput, commit, localCommitIndex, spec, localPerCommitmentPoint, localNonce_opt).map { localCommit1 =>
716+
LocalCommit.fromCommitSig(keyManager, params, fundingTxId, fundingTxIndex, remoteFundingPubKey, commitInput, commit, localCommitIndex, spec, localPerCommitmentPoint).map { localCommit1 =>
720717
log.info(s"built local commit number=$localCommitIndex toLocalMsat=${spec.toLocal.toLong} toRemoteMsat=${spec.toRemote.toLong} htlc_in={} htlc_out={} feeratePerKw=${spec.commitTxFeerate} txid=${localCommit1.commitTxAndRemoteSig.commitTx.tx.txid} fundingTxId=$fundingTxId", spec.htlcs.collect(DirectedHtlc.incoming).map(_.id).mkString(","), spec.htlcs.collect(DirectedHtlc.outgoing).map(_.id).mkString(","))
721718
copy(localCommit = localCommit1)
722719
}
@@ -1112,12 +1109,7 @@ case class Commitments(params: ChannelParams,
11121109

11131110
// Signatures are sent in order (most recent first), calling `zip` will drop trailing sigs that are for deactivated/pruned commitments.
11141111
val active1 = active.zip(commits).map { case (commitment, commit) =>
1115-
val localNonce_opt = if (params.commitmentFormat.useTaproot) {
1116-
Some(keyManager.verificationNonce(params.localParams.fundingKeyPath, commitment.fundingTxIndex, channelKeyPath, localCommitIndex + 1))
1117-
} else {
1118-
None
1119-
}
1120-
commitment.receiveCommit(keyManager, params, changes, localPerCommitmentPoint, commit, localNonce_opt) match {
1112+
commitment.receiveCommit(keyManager, params, changes, localPerCommitmentPoint, commit) match {
11211113
case Left(f) => return Left(f)
11221114
case Right(commitment1) => commitment1
11231115
}
@@ -1268,7 +1260,7 @@ case class Commitments(params: ChannelParams,
12681260
// we cannot compare partial signatures directly as they are not deterministic (a new signing nonce is used every time a signature is computed)
12691261
// => instead we simply check that the provided partial signature is valid for our latest commit tx
12701262
val localFundingKey = keyManager.fundingPublicKey(params.localParams.fundingKeyPath, latest.fundingTxIndex).publicKey
1271-
val Some(localNonce) = generateLocalNonce(keyManager, latest.fundingTxIndex, latest.localCommit.index)
1263+
val localNonce = keyManager.verificationNonce(params.localParams.fundingKeyPath, latest.fundingTxIndex, keyManager.keyPath(params.localParams, params.channelConfig), latest.localCommit.index)._2
12721264
latest.localCommit.commitTxAndRemoteSig.commitTx.checkPartialSignature(psig, localFundingKey, localNonce, latest.remoteFundingPubKey)
12731265
}
12741266

@@ -1387,35 +1379,6 @@ case class Commitments(params: ChannelParams,
13871379
def resolveCommitment(spendingTx: Transaction): Option[Commitment] = {
13881380
all.find(c => spendingTx.txIn.map(_.outPoint).contains(c.commitInput.outPoint))
13891381
}
1390-
1391-
/**
1392-
* Generate local verification nonces for a specific funding tx index and commit tx index
1393-
*
1394-
* @param keyManager key manager that will generate actual nonces
1395-
* @param fundingIndex funding tx index
1396-
* @param commitIndex commit tx index
1397-
* @return a public nonce for thr provided fundint tx index and commit tx index if taproot is used, None otherwise
1398-
*/
1399-
def generateLocalNonce(keyManager: ChannelKeyManager, fundingIndex: Long, commitIndex: Long): Option[IndividualNonce] = {
1400-
if (latest.params.commitmentFormat.useTaproot) {
1401-
Some(keyManager.verificationNonce(params.localParams.fundingKeyPath, fundingIndex, keyManager.keyPath(params.localParams, params.channelConfig), commitIndex)._2)
1402-
} else {
1403-
None
1404-
}
1405-
}
1406-
1407-
/**
1408-
* Create local verification nonces a specific funding tx index and a range of commit tx indexes
1409-
*
1410-
* @param keyManager key manager that will generate actual nonces
1411-
* @param fundingIndex funding tx index
1412-
* @param commitIndexes range of commit tx indexes
1413-
* @return a list of nonces if raproot is used, or an empty list
1414-
*/
1415-
def generateLocalNonces(keyManager: ChannelKeyManager, fundingIndex: Long, commitIndexes: Long*): List[IndividualNonce] = {
1416-
commitIndexes.toList.flatMap(commitIndex => generateLocalNonce(keyManager, fundingIndex, commitIndex))
1417-
}
1418-
14191382
}
14201383

14211384
object Commitments {

eclair-core/src/main/scala/fr/acinq/eclair/channel/fsm/Channel.scala

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1031,7 +1031,14 @@ class Channel(val nodeParams: NodeParams, val wallet: OnChainChannelFunder with
10311031
val parentCommitment = d.commitments.latest.commitment
10321032
val localFundingPubKey = nodeParams.channelKeyManager.fundingPublicKey(d.commitments.params.localParams.fundingKeyPath, parentCommitment.fundingTxIndex + 1).publicKey
10331033
val fundingScript = Funding.makeFundingPubKeyScript(localFundingPubKey, msg.fundingPubKey, d.commitments.latest.params.commitmentFormat)
1034-
val nextLocalNonces = d.commitments.generateLocalNonces(keyManager, parentCommitment.fundingTxIndex + 1, parentCommitment.localCommit.index, parentCommitment.localCommit.index + 1)
1034+
val nextLocalNonces = if (d.commitments.latest.params.commitmentFormat.useTaproot) {
1035+
List(
1036+
keyManager.verificationNonce(d.commitments.params.localParams.fundingKeyPath, parentCommitment.fundingTxIndex + 1, keyManager.keyPath(d.commitments.params.localParams, d.commitments.params.channelConfig), parentCommitment.localCommit.index)._2,
1037+
keyManager.verificationNonce(d.commitments.params.localParams.fundingKeyPath, parentCommitment.fundingTxIndex + 1, keyManager.keyPath(d.commitments.params.localParams, d.commitments.params.channelConfig), parentCommitment.localCommit.index + 1)._2,
1038+
)
1039+
} else {
1040+
List.empty[IndividualNonce]
1041+
}
10351042
val sharedInput = if (d.commitments.latest.params.commitmentFormat.useTaproot) {
10361043
Musig2Input(parentCommitment)
10371044
} else {
@@ -1188,7 +1195,14 @@ class Channel(val nodeParams: NodeParams, val wallet: OnChainChannelFunder with
11881195
// Otherwise we keep the same contribution we made to the previous funding transaction.
11891196
val fundingContribution = willFund_opt.map(_.purchase.amount).getOrElse(rbf.latestFundingTx.fundingParams.localContribution)
11901197
log.info("accepting rbf with remote.in.amount={} local.in.amount={}", msg.fundingContribution, fundingContribution)
1191-
val localNonces = d.commitments.generateLocalNonces(keyManager, rbf.parentCommitment.fundingTxIndex + 1, rbf.localCommitIndex, rbf.localCommitIndex + 1)
1198+
val localNonces = if (d.commitments.latest.params.commitmentFormat.useTaproot) {
1199+
List(
1200+
keyManager.verificationNonce(d.commitments.params.localParams.fundingKeyPath, rbf.parentCommitment.fundingTxIndex + 1, keyManager.keyPath(d.commitments.params.localParams, d.commitments.params.channelConfig), rbf.localCommitIndex)._2,
1201+
keyManager.verificationNonce(d.commitments.params.localParams.fundingKeyPath, rbf.parentCommitment.fundingTxIndex + 1, keyManager.keyPath(d.commitments.params.localParams, d.commitments.params.channelConfig), rbf.localCommitIndex + 1)._2,
1202+
)
1203+
} else {
1204+
List.empty[IndividualNonce]
1205+
}
11921206
val txAckRbf = TxAckRbf(d.channelId, fundingContribution, rbf.latestFundingTx.fundingParams.requireConfirmedInputs.forRemote, willFund_opt.map(_.willFund), localNonces)
11931207
val sharedInput = if (d.commitments.latest.params.commitmentFormat.useTaproot) {
11941208
Musig2Input(rbf.parentCommitment)
@@ -3208,7 +3222,14 @@ class Channel(val nodeParams: NodeParams, val wallet: OnChainChannelFunder with
32083222
Left(InvalidSpliceRequest(d.channelId))
32093223
} else {
32103224
log.info(s"initiating splice with local.in.amount=${cmd.additionalLocalFunding} local.in.push=${cmd.pushAmount} local.out.amount=${cmd.spliceOut_opt.map(_.amount).sum}")
3211-
val nextLocalNonces = d.commitments.generateLocalNonces(keyManager, parentCommitment.fundingTxIndex + 1, parentCommitment.localCommit.index, parentCommitment.localCommit.index + 1)
3225+
val nextLocalNonces = if (d.commitments.latest.params.commitmentFormat.useTaproot) {
3226+
List(
3227+
keyManager.verificationNonce(d.commitments.params.localParams.fundingKeyPath, parentCommitment.fundingTxIndex + 1, keyManager.keyPath(d.commitments.params.localParams, d.commitments.params.channelConfig), parentCommitment.localCommit.index)._2,
3228+
keyManager.verificationNonce(d.commitments.params.localParams.fundingKeyPath, parentCommitment.fundingTxIndex + 1, keyManager.keyPath(d.commitments.params.localParams, d.commitments.params.channelConfig), parentCommitment.localCommit.index + 1)._2,
3229+
)
3230+
} else {
3231+
List.empty[IndividualNonce]
3232+
}
32123233
val spliceInit = SpliceInit(d.channelId,
32133234
fundingContribution = fundingContribution,
32143235
lockTime = nodeParams.currentBlockHeight.toLong,
@@ -3236,7 +3257,14 @@ class Channel(val nodeParams: NodeParams, val wallet: OnChainChannelFunder with
32363257
log.warning(s"cannot do rbf: insufficient funds (commitTxFees=$commitTxFees reserve=${rbf.parentCommitment.localChannelReserve(d.commitments.params)})")
32373258
Left(InvalidSpliceRequest(d.channelId))
32383259
} else {
3239-
val nextLocalNonces = d.commitments.generateLocalNonces(keyManager, rbf.parentCommitment.fundingTxIndex + 1, rbf.localCommitIndex, rbf.localCommitIndex + 1)
3260+
val nextLocalNonces = if (d.commitments.latest.params.commitmentFormat.useTaproot) {
3261+
List(
3262+
keyManager.verificationNonce(d.commitments.params.localParams.fundingKeyPath, rbf.parentCommitment.fundingTxIndex + 1, keyManager.keyPath(d.commitments.params.localParams, d.commitments.params.channelConfig), rbf.localCommitIndex)._2,
3263+
keyManager.verificationNonce(d.commitments.params.localParams.fundingKeyPath, rbf.parentCommitment.fundingTxIndex + 1, keyManager.keyPath(d.commitments.params.localParams, d.commitments.params.channelConfig), rbf.localCommitIndex + 1)._2,
3264+
)
3265+
} else {
3266+
List.empty[IndividualNonce]
3267+
}
32403268
val txInitRbf = TxInitRbf(d.channelId, cmd.lockTime, cmd.targetFeerate, fundingContribution, rbf.latestFundingTx.fundingParams.requireConfirmedInputs.forRemote, cmd.requestFunding_opt, nextLocalNonces)
32413269
Right(txInitRbf)
32423270
}

eclair-core/src/main/scala/fr/acinq/eclair/channel/fsm/ChannelOpenDualFunded.scala

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package fr.acinq.eclair.channel.fsm
1818

1919
import akka.actor.typed.scaladsl.adapter.{ClassicActorContextOps, actorRefAdapter}
2020
import com.softwaremill.quicklens.{ModifyPimp, QuicklensAt}
21+
import fr.acinq.bitcoin.crypto.musig2.IndividualNonce
2122
import fr.acinq.bitcoin.scalacompat.SatoshiLong
2223
import fr.acinq.eclair.blockchain.bitcoind.ZmqWatcher._
2324
import fr.acinq.eclair.channel.Helpers.Funding
@@ -533,7 +534,14 @@ trait ChannelOpenDualFunded extends DualFundingHandlers with ErrorHandlers {
533534
cmd.replyTo ! RES_FAILURE(cmd, InvalidRbfFeerate(d.channelId, cmd.targetFeerate, minNextFeerate))
534535
stay()
535536
} else {
536-
val localNonces = d.commitments.generateLocalNonces(keyManager, d.commitments.latest.fundingTxIndex, d.commitments.localCommitIndex, d.commitments.localCommitIndex + 1)
537+
val localNonces = if (d.commitments.latest.params.commitmentFormat.useTaproot) {
538+
List(
539+
keyManager.verificationNonce(d.commitments.params.localParams.fundingKeyPath, d.commitments.latest.fundingTxIndex, keyManager.keyPath(d.commitments.params.localParams, d.commitments.params.channelConfig), d.commitments.localCommitIndex)._2,
540+
keyManager.verificationNonce(d.commitments.params.localParams.fundingKeyPath, d.commitments.latest.fundingTxIndex, keyManager.keyPath(d.commitments.params.localParams, d.commitments.params.channelConfig), d.commitments.localCommitIndex + 1)._2,
541+
)
542+
} else {
543+
List.empty[IndividualNonce]
544+
}
537545
val txInitRbf = TxInitRbf(d.channelId, cmd.lockTime, cmd.targetFeerate, d.latestFundingTx.fundingParams.localContribution, d.latestFundingTx.fundingParams.requireConfirmedInputs.forRemote, cmd.requestFunding_opt, localNonces)
538546
stay() using d.copy(status = DualFundingStatus.RbfRequested(cmd)) sending txInitRbf
539547
}
@@ -604,7 +612,14 @@ trait ChannelOpenDualFunded extends DualFundingHandlers with ErrorHandlers {
604612
wallet,
605613
msg.firstRemoteNonce))
606614
txBuilder ! InteractiveTxBuilder.Start(self)
607-
val nextLocalNonces = d.commitments.generateLocalNonces(keyManager, d.commitments.latest.fundingTxIndex, d.commitments.localCommitIndex, d.commitments.localCommitIndex + 1)
615+
val nextLocalNonces = if (d.commitments.latest.params.commitmentFormat.useTaproot) {
616+
List(
617+
keyManager.verificationNonce(d.commitments.params.localParams.fundingKeyPath, d.commitments.latest.fundingTxIndex, keyManager.keyPath(d.commitments.params.localParams, d.commitments.params.channelConfig), d.commitments.localCommitIndex)._2,
618+
keyManager.verificationNonce(d.commitments.params.localParams.fundingKeyPath, d.commitments.latest.fundingTxIndex, keyManager.keyPath(d.commitments.params.localParams, d.commitments.params.channelConfig), d.commitments.localCommitIndex + 1)._2,
619+
)
620+
} else {
621+
List.empty[IndividualNonce]
622+
}
608623
setRemoteNextLocalNonces("received TxInitRbf", msg.secondRemoteNonce.toList)
609624
val toSend = Seq(
610625
Some(TxAckRbf(d.channelId, fundingParams.localContribution, d.latestFundingTx.fundingParams.requireConfirmedInputs.forRemote, willFund_opt.map(_.willFund), nextLocalNonces)),

eclair-core/src/main/scala/fr/acinq/eclair/channel/fund/InteractiveTxBuilder.scala

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1194,12 +1194,7 @@ object InteractiveTxSigningSession {
11941194
case Left(unsignedLocalCommit) =>
11951195
val channelKeyPath = nodeParams.channelKeyManager.keyPath(channelParams.localParams, channelParams.channelConfig)
11961196
val localPerCommitmentPoint = nodeParams.channelKeyManager.commitmentPoint(channelKeyPath, localCommitIndex)
1197-
val localNonce_opt = if (channelParams.commitmentFormat.useTaproot) {
1198-
Some(nodeParams.channelKeyManager.verificationNonce(channelParams.localParams.fundingKeyPath, fundingTxIndex, channelKeyPath, localCommitIndex))
1199-
} else {
1200-
None
1201-
}
1202-
LocalCommit.fromCommitSig(nodeParams.channelKeyManager, channelParams, fundingTx.txId, fundingTxIndex, fundingParams.remoteFundingPubKey, commitInput, remoteCommitSig, localCommitIndex, unsignedLocalCommit.spec, localPerCommitmentPoint, localNonce_opt).map { signedLocalCommit =>
1197+
LocalCommit.fromCommitSig(nodeParams.channelKeyManager, channelParams, fundingTx.txId, fundingTxIndex, fundingParams.remoteFundingPubKey, commitInput, remoteCommitSig, localCommitIndex, unsignedLocalCommit.spec, localPerCommitmentPoint).map { signedLocalCommit =>
12031198
if (shouldSignFirst(fundingParams.isInitiator, channelParams, fundingTx.tx)) {
12041199
val fundingStatus = LocalFundingStatus.DualFundedUnconfirmedFundingTx(fundingTx, nodeParams.currentBlockHeight, fundingParams, liquidityPurchase_opt)
12051200
val commitment = Commitment(fundingTxIndex, remoteCommit.index, fundingParams.remoteFundingPubKey, fundingStatus, RemoteFundingStatus.NotLocked, signedLocalCommit, remoteCommit, None)

0 commit comments

Comments
 (0)