Skip to content

Commit

Permalink
Rework nonce generation
Browse files Browse the repository at this point in the history
  • Loading branch information
sstone committed Jan 29, 2025
1 parent 334fe59 commit e28b326
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 55 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ case class LocalCommit(index: Long, spec: CommitmentSpec, commitTxAndRemoteSig:
object LocalCommit {
def fromCommitSig(keyManager: ChannelKeyManager, params: ChannelParams, fundingTxId: TxId,
fundingTxIndex: Long, remoteFundingPubKey: PublicKey, commitInput: InputInfo,
commit: CommitSig, localCommitIndex: Long, spec: CommitmentSpec, localPerCommitmentPoint: PublicKey, localNonce_opt: Option[(SecretNonce, IndividualNonce)])(implicit log: LoggingAdapter): Either[ChannelException, LocalCommit] = {
commit: CommitSig, localCommitIndex: Long, spec: CommitmentSpec, localPerCommitmentPoint: PublicKey)(implicit log: LoggingAdapter): Either[ChannelException, LocalCommit] = {
val (localCommitTx, htlcTxs) = Commitment.makeLocalTxs(keyManager, params.channelConfig, params.channelFeatures, localCommitIndex, params.localParams, params.remoteParams, fundingTxIndex, remoteFundingPubKey, commitInput, localPerCommitmentPoint, spec)
commit.sigOrPartialSig match {
case Left(sig) =>
Expand All @@ -255,12 +255,9 @@ object LocalCommit {
}
case Right(psig) =>
val fundingPubkey = keyManager.fundingPublicKey(params.localParams.fundingKeyPath, fundingTxIndex).publicKey
val Some(localNonce) = localNonce_opt
val localNonce = keyManager.verificationNonce(params.localParams.fundingKeyPath, fundingTxIndex, keyManager.keyPath(params.localParams, params.channelConfig), localCommitIndex)
if (!localCommitTx.checkPartialSignature(psig, fundingPubkey, localNonce._2, remoteFundingPubKey)) {
log.debug(s"fromCommitSig: invalid partial signature $psig fundingPubkey = $fundingPubkey, fundingTxIndex = $fundingTxIndex localCommitIndex = $localCommitIndex localNonce = $localNonce remoteFundingPubKey = $remoteFundingPubKey")

val localNonce1 = keyManager.verificationNonce(params.localParams.fundingKeyPath, fundingTxIndex, keyManager.keyPath(params.localParams, params.channelConfig), localCommitIndex)
log.debug(s"with $localNonce1 ${localCommitTx.checkPartialSignature(psig, fundingPubkey, localNonce1._2, remoteFundingPubKey)}")
return Left(InvalidCommitmentSignature(params.channelId, fundingTxId, fundingTxIndex, localCommitTx.tx))
}
}
Expand Down Expand Up @@ -705,7 +702,7 @@ case class Commitment(fundingTxIndex: Long,
(copy(nextRemoteCommit_opt = Some(nextRemoteCommit)), commitSig)
}

def receiveCommit(keyManager: ChannelKeyManager, params: ChannelParams, changes: CommitmentChanges, localPerCommitmentPoint: PublicKey, commit: CommitSig, localNonce_opt: Option[(SecretNonce, IndividualNonce)])(implicit log: LoggingAdapter): Either[ChannelException, Commitment] = {
def receiveCommit(keyManager: ChannelKeyManager, params: ChannelParams, changes: CommitmentChanges, localPerCommitmentPoint: PublicKey, commit: CommitSig)(implicit log: LoggingAdapter): Either[ChannelException, Commitment] = {
// they sent us a signature for *their* view of *our* next commit tx
// so in terms of rev.hashes and indexes we have:
// ourCommit.index -> our current revocation hash, which is about to become our old revocation hash
Expand All @@ -716,7 +713,7 @@ case class Commitment(fundingTxIndex: Long,
// and will increment our index
val localCommitIndex = localCommit.index + 1
val spec = CommitmentSpec.reduce(localCommit.spec, changes.localChanges.acked, changes.remoteChanges.proposed)
LocalCommit.fromCommitSig(keyManager, params, fundingTxId, fundingTxIndex, remoteFundingPubKey, commitInput, commit, localCommitIndex, spec, localPerCommitmentPoint, localNonce_opt).map { localCommit1 =>
LocalCommit.fromCommitSig(keyManager, params, fundingTxId, fundingTxIndex, remoteFundingPubKey, commitInput, commit, localCommitIndex, spec, localPerCommitmentPoint).map { localCommit1 =>
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(","))
copy(localCommit = localCommit1)
}
Expand Down Expand Up @@ -1112,12 +1109,7 @@ case class Commitments(params: ChannelParams,

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

Expand Down Expand Up @@ -1387,35 +1379,6 @@ case class Commitments(params: ChannelParams,
def resolveCommitment(spendingTx: Transaction): Option[Commitment] = {
all.find(c => spendingTx.txIn.map(_.outPoint).contains(c.commitInput.outPoint))
}

/**
* Generate local verification nonces for a specific funding tx index and commit tx index
*
* @param keyManager key manager that will generate actual nonces
* @param fundingIndex funding tx index
* @param commitIndex commit tx index
* @return a public nonce for thr provided fundint tx index and commit tx index if taproot is used, None otherwise
*/
def generateLocalNonce(keyManager: ChannelKeyManager, fundingIndex: Long, commitIndex: Long): Option[IndividualNonce] = {
if (latest.params.commitmentFormat.useTaproot) {
Some(keyManager.verificationNonce(params.localParams.fundingKeyPath, fundingIndex, keyManager.keyPath(params.localParams, params.channelConfig), commitIndex)._2)
} else {
None
}
}

/**
* Create local verification nonces a specific funding tx index and a range of commit tx indexes
*
* @param keyManager key manager that will generate actual nonces
* @param fundingIndex funding tx index
* @param commitIndexes range of commit tx indexes
* @return a list of nonces if raproot is used, or an empty list
*/
def generateLocalNonces(keyManager: ChannelKeyManager, fundingIndex: Long, commitIndexes: Long*): List[IndividualNonce] = {
commitIndexes.toList.flatMap(commitIndex => generateLocalNonce(keyManager, fundingIndex, commitIndex))
}

}

object Commitments {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1031,7 +1031,14 @@ class Channel(val nodeParams: NodeParams, val wallet: OnChainChannelFunder with
val parentCommitment = d.commitments.latest.commitment
val localFundingPubKey = nodeParams.channelKeyManager.fundingPublicKey(d.commitments.params.localParams.fundingKeyPath, parentCommitment.fundingTxIndex + 1).publicKey
val fundingScript = Funding.makeFundingPubKeyScript(localFundingPubKey, msg.fundingPubKey, d.commitments.latest.params.commitmentFormat)
val nextLocalNonces = d.commitments.generateLocalNonces(keyManager, parentCommitment.fundingTxIndex + 1, parentCommitment.localCommit.index, parentCommitment.localCommit.index + 1)
val nextLocalNonces = if (d.commitments.latest.params.commitmentFormat.useTaproot) {
List(
keyManager.verificationNonce(d.commitments.params.localParams.fundingKeyPath, parentCommitment.fundingTxIndex + 1, keyManager.keyPath(d.commitments.params.localParams, d.commitments.params.channelConfig), parentCommitment.localCommit.index)._2,
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,
)
} else {
List.empty[IndividualNonce]
}
val sharedInput = if (d.commitments.latest.params.commitmentFormat.useTaproot) {
Musig2Input(parentCommitment)
} else {
Expand Down Expand Up @@ -1188,7 +1195,14 @@ class Channel(val nodeParams: NodeParams, val wallet: OnChainChannelFunder with
// Otherwise we keep the same contribution we made to the previous funding transaction.
val fundingContribution = willFund_opt.map(_.purchase.amount).getOrElse(rbf.latestFundingTx.fundingParams.localContribution)
log.info("accepting rbf with remote.in.amount={} local.in.amount={}", msg.fundingContribution, fundingContribution)
val localNonces = d.commitments.generateLocalNonces(keyManager, rbf.parentCommitment.fundingTxIndex + 1, rbf.localCommitIndex, rbf.localCommitIndex + 1)
val localNonces = if (d.commitments.latest.params.commitmentFormat.useTaproot) {
List(
keyManager.verificationNonce(d.commitments.params.localParams.fundingKeyPath, rbf.parentCommitment.fundingTxIndex + 1, keyManager.keyPath(d.commitments.params.localParams, d.commitments.params.channelConfig), rbf.localCommitIndex)._2,
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,
)
} else {
List.empty[IndividualNonce]
}
val txAckRbf = TxAckRbf(d.channelId, fundingContribution, rbf.latestFundingTx.fundingParams.requireConfirmedInputs.forRemote, willFund_opt.map(_.willFund), localNonces)
val sharedInput = if (d.commitments.latest.params.commitmentFormat.useTaproot) {
Musig2Input(rbf.parentCommitment)
Expand Down Expand Up @@ -3208,7 +3222,14 @@ class Channel(val nodeParams: NodeParams, val wallet: OnChainChannelFunder with
Left(InvalidSpliceRequest(d.channelId))
} else {
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}")
val nextLocalNonces = d.commitments.generateLocalNonces(keyManager, parentCommitment.fundingTxIndex + 1, parentCommitment.localCommit.index, parentCommitment.localCommit.index + 1)
val nextLocalNonces = if (d.commitments.latest.params.commitmentFormat.useTaproot) {
List(
keyManager.verificationNonce(d.commitments.params.localParams.fundingKeyPath, parentCommitment.fundingTxIndex + 1, keyManager.keyPath(d.commitments.params.localParams, d.commitments.params.channelConfig), parentCommitment.localCommit.index)._2,
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,
)
} else {
List.empty[IndividualNonce]
}
val spliceInit = SpliceInit(d.channelId,
fundingContribution = fundingContribution,
lockTime = nodeParams.currentBlockHeight.toLong,
Expand Down Expand Up @@ -3236,7 +3257,14 @@ class Channel(val nodeParams: NodeParams, val wallet: OnChainChannelFunder with
log.warning(s"cannot do rbf: insufficient funds (commitTxFees=$commitTxFees reserve=${rbf.parentCommitment.localChannelReserve(d.commitments.params)})")
Left(InvalidSpliceRequest(d.channelId))
} else {
val nextLocalNonces = d.commitments.generateLocalNonces(keyManager, rbf.parentCommitment.fundingTxIndex + 1, rbf.localCommitIndex, rbf.localCommitIndex + 1)
val nextLocalNonces = if (d.commitments.latest.params.commitmentFormat.useTaproot) {
List(
keyManager.verificationNonce(d.commitments.params.localParams.fundingKeyPath, rbf.parentCommitment.fundingTxIndex + 1, keyManager.keyPath(d.commitments.params.localParams, d.commitments.params.channelConfig), rbf.localCommitIndex)._2,
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,
)
} else {
List.empty[IndividualNonce]
}
val txInitRbf = TxInitRbf(d.channelId, cmd.lockTime, cmd.targetFeerate, fundingContribution, rbf.latestFundingTx.fundingParams.requireConfirmedInputs.forRemote, cmd.requestFunding_opt, nextLocalNonces)
Right(txInitRbf)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package fr.acinq.eclair.channel.fsm

import akka.actor.typed.scaladsl.adapter.{ClassicActorContextOps, actorRefAdapter}
import com.softwaremill.quicklens.{ModifyPimp, QuicklensAt}
import fr.acinq.bitcoin.crypto.musig2.IndividualNonce
import fr.acinq.bitcoin.scalacompat.SatoshiLong
import fr.acinq.eclair.blockchain.bitcoind.ZmqWatcher._
import fr.acinq.eclair.channel.Helpers.Funding
Expand Down Expand Up @@ -533,7 +534,14 @@ trait ChannelOpenDualFunded extends DualFundingHandlers with ErrorHandlers {
cmd.replyTo ! RES_FAILURE(cmd, InvalidRbfFeerate(d.channelId, cmd.targetFeerate, minNextFeerate))
stay()
} else {
val localNonces = d.commitments.generateLocalNonces(keyManager, d.commitments.latest.fundingTxIndex, d.commitments.localCommitIndex, d.commitments.localCommitIndex + 1)
val localNonces = if (d.commitments.latest.params.commitmentFormat.useTaproot) {
List(
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,
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,
)
} else {
List.empty[IndividualNonce]
}
val txInitRbf = TxInitRbf(d.channelId, cmd.lockTime, cmd.targetFeerate, d.latestFundingTx.fundingParams.localContribution, d.latestFundingTx.fundingParams.requireConfirmedInputs.forRemote, cmd.requestFunding_opt, localNonces)
stay() using d.copy(status = DualFundingStatus.RbfRequested(cmd)) sending txInitRbf
}
Expand Down Expand Up @@ -604,7 +612,14 @@ trait ChannelOpenDualFunded extends DualFundingHandlers with ErrorHandlers {
wallet,
msg.firstRemoteNonce))
txBuilder ! InteractiveTxBuilder.Start(self)
val nextLocalNonces = d.commitments.generateLocalNonces(keyManager, d.commitments.latest.fundingTxIndex, d.commitments.localCommitIndex, d.commitments.localCommitIndex + 1)
val nextLocalNonces = if (d.commitments.latest.params.commitmentFormat.useTaproot) {
List(
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,
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,
)
} else {
List.empty[IndividualNonce]
}
setRemoteNextLocalNonces("received TxInitRbf", msg.secondRemoteNonce.toList)
val toSend = Seq(
Some(TxAckRbf(d.channelId, fundingParams.localContribution, d.latestFundingTx.fundingParams.requireConfirmedInputs.forRemote, willFund_opt.map(_.willFund), nextLocalNonces)),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1194,12 +1194,7 @@ object InteractiveTxSigningSession {
case Left(unsignedLocalCommit) =>
val channelKeyPath = nodeParams.channelKeyManager.keyPath(channelParams.localParams, channelParams.channelConfig)
val localPerCommitmentPoint = nodeParams.channelKeyManager.commitmentPoint(channelKeyPath, localCommitIndex)
val localNonce_opt = if (channelParams.commitmentFormat.useTaproot) {
Some(nodeParams.channelKeyManager.verificationNonce(channelParams.localParams.fundingKeyPath, fundingTxIndex, channelKeyPath, localCommitIndex))
} else {
None
}
LocalCommit.fromCommitSig(nodeParams.channelKeyManager, channelParams, fundingTx.txId, fundingTxIndex, fundingParams.remoteFundingPubKey, commitInput, remoteCommitSig, localCommitIndex, unsignedLocalCommit.spec, localPerCommitmentPoint, localNonce_opt).map { signedLocalCommit =>
LocalCommit.fromCommitSig(nodeParams.channelKeyManager, channelParams, fundingTx.txId, fundingTxIndex, fundingParams.remoteFundingPubKey, commitInput, remoteCommitSig, localCommitIndex, unsignedLocalCommit.spec, localPerCommitmentPoint).map { signedLocalCommit =>
if (shouldSignFirst(fundingParams.isInitiator, channelParams, fundingTx.tx)) {
val fundingStatus = LocalFundingStatus.DualFundedUnconfirmedFundingTx(fundingTx, nodeParams.currentBlockHeight, fundingParams, liquidityPurchase_opt)
val commitment = Commitment(fundingTxIndex, remoteCommit.index, fundingParams.remoteFundingPubKey, fundingStatus, RemoteFundingStatus.NotLocked, signedLocalCommit, remoteCommit, None)
Expand Down

0 comments on commit e28b326

Please sign in to comment.