Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion eclair-core/src/main/scala/fr/acinq/eclair/NodeParams.scala
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import fr.acinq.eclair.router.Router._
import fr.acinq.eclair.router.{Graph, PathFindingExperimentConf, Router}
import fr.acinq.eclair.tor.Socks5ProxyParams
import fr.acinq.eclair.transactions.Transactions
import fr.acinq.eclair.transactions.Transactions.SimpleTaprootChannelCommitmentFormat
import fr.acinq.eclair.wire.protocol._
import grizzled.slf4j.Logging
import scodec.bits.ByteVector
Expand Down Expand Up @@ -134,7 +135,7 @@ case class NodeParams(nodeKeyManager: NodeKeyManager,
min = (commitmentFeerate * feerateTolerance.ratioLow).max(minimumFeerate),
max = (commitmentFormat match {
case Transactions.DefaultCommitmentFormat => commitmentFeerate * feerateTolerance.ratioHigh
case _: Transactions.AnchorOutputsCommitmentFormat => (commitmentFeerate * feerateTolerance.ratioHigh).max(feerateTolerance.anchorOutputMaxCommitFeerate)
case _: Transactions.AnchorOutputsCommitmentFormat | Transactions.SimpleTaprootChannelCommitmentFormat => (commitmentFeerate * feerateTolerance.ratioHigh).max(feerateTolerance.anchorOutputMaxCommitFeerate)
}).max(minimumFeerate),
)
RecommendedFeerates(chainHash, fundingFeerate, commitmentFeerate, TlvStream(fundingRange, commitmentRange))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@ package fr.acinq.eclair
import fr.acinq.bitcoin.scalacompat.Crypto.PublicKey
import fr.acinq.bitcoin.scalacompat.{ByteVector64, DeterministicWallet, OutPoint, Satoshi, SatoshiLong, Script, ScriptWitness, Transaction, TxIn, TxOut, addressToPublicKeyScript}
import fr.acinq.eclair.blockchain.fee.FeeratePerKw
import fr.acinq.eclair.channel.ChannelConfig
import fr.acinq.eclair.transactions.Scripts.multiSig2of2
import fr.acinq.eclair.channel.{ChannelConfig, Helpers}
import fr.acinq.eclair.transactions.Transactions._
import fr.acinq.eclair.transactions.{Scripts, Transactions}
import scodec.bits.ByteVector
Expand Down Expand Up @@ -45,10 +44,10 @@ trait SpendFromChannelAddress {
inputTx <- appKit.wallet.getTransaction(outPoint.txid)
channelKeys = appKit.nodeParams.channelKeyManager.channelKeys(ChannelConfig.standard, fundingKeyPath)
localFundingKey = channelKeys.fundingKey(fundingTxIndex)
fundingRedeemScript = multiSig2of2(localFundingKey.publicKey, remoteFundingPubkey)
inputInfo = InputInfo(outPoint, inputTx.txOut(outPoint.index.toInt), fundingRedeemScript)
redeemInfo = Helpers.Funding.makeFundingRedeemInfo(localFundingKey.publicKey, remoteFundingPubkey, DefaultCommitmentFormat)
inputInfo = InputInfo(outPoint, inputTx.txOut(outPoint.index.toInt))
// classify as splice, doesn't really matter
localSig = Transactions.SpliceTx(inputInfo, unsignedTx).sign(localFundingKey, TxOwner.Local, DefaultCommitmentFormat, Map.empty)
localSig = Transactions.SpliceTx(inputInfo, unsignedTx).sign(localFundingKey, redeemInfo, TxOwner.Local, DefaultCommitmentFormat, Map.empty)
witness = Scripts.witness2of2(localSig, remoteSig, localFundingKey.publicKey, remoteFundingPubkey)
signedTx = unsignedTx.updateWitness(0, witness)
} yield SpendFromChannelResult(signedTx)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import fr.acinq.bitcoin.scalacompat.Crypto.PublicKey
import fr.acinq.bitcoin.scalacompat.Satoshi
import fr.acinq.eclair.BlockHeight
import fr.acinq.eclair.transactions.Transactions
import fr.acinq.eclair.transactions.Transactions.{CommitmentFormat, UnsafeLegacyAnchorOutputsCommitmentFormat, ZeroFeeHtlcTxAnchorOutputsCommitmentFormat}
import fr.acinq.eclair.transactions.Transactions.{CommitmentFormat, SimpleTaprootChannelCommitmentFormat, UnsafeLegacyAnchorOutputsCommitmentFormat, ZeroFeeHtlcTxAnchorOutputsCommitmentFormat}

// @formatter:off
sealed trait ConfirmationPriority extends Ordered[ConfirmationPriority] {
Expand Down Expand Up @@ -77,15 +77,15 @@ case class FeerateTolerance(ratioLow: Double, ratioHigh: Double, anchorOutputMax
def isProposedFeerateTooHigh(commitmentFormat: CommitmentFormat, networkFeerate: FeeratePerKw, proposedFeerate: FeeratePerKw): Boolean = {
commitmentFormat match {
case Transactions.DefaultCommitmentFormat => networkFeerate * ratioHigh < proposedFeerate
case ZeroFeeHtlcTxAnchorOutputsCommitmentFormat | UnsafeLegacyAnchorOutputsCommitmentFormat => networkFeerate * ratioHigh < proposedFeerate
case ZeroFeeHtlcTxAnchorOutputsCommitmentFormat | UnsafeLegacyAnchorOutputsCommitmentFormat | SimpleTaprootChannelCommitmentFormat => networkFeerate * ratioHigh < proposedFeerate
}
}

def isProposedFeerateTooLow(commitmentFormat: CommitmentFormat, networkFeerate: FeeratePerKw, proposedFeerate: FeeratePerKw): Boolean = {
commitmentFormat match {
case Transactions.DefaultCommitmentFormat => proposedFeerate < networkFeerate * ratioLow
// When using anchor outputs, we allow low feerates: fees will be set with CPFP and RBF at broadcast time.
case ZeroFeeHtlcTxAnchorOutputsCommitmentFormat | UnsafeLegacyAnchorOutputsCommitmentFormat => false
case ZeroFeeHtlcTxAnchorOutputsCommitmentFormat | UnsafeLegacyAnchorOutputsCommitmentFormat | SimpleTaprootChannelCommitmentFormat => false
}
}
}
Expand Down Expand Up @@ -121,7 +121,7 @@ case class OnChainFeeConf(feeTargets: FeeTargets,

commitmentFormat match {
case Transactions.DefaultCommitmentFormat => networkFeerate
case _: Transactions.AnchorOutputsCommitmentFormat =>
case _: Transactions.AnchorOutputsCommitmentFormat | SimpleTaprootChannelCommitmentFormat =>
val targetFeerate = networkFeerate.min(feerateToleranceFor(remoteNodeId).anchorOutputMaxCommitFeerate)
// We make sure the feerate is always greater than the propagation threshold.
targetFeerate.max(networkMinFee * 1.25)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -194,17 +194,17 @@ object LocalCommit {
def fromCommitSig(params: ChannelParams, commitKeys: LocalCommitmentKeys, fundingTxId: TxId,
fundingKey: PrivateKey, remoteFundingPubKey: PublicKey, commitInput: InputInfo,
commit: CommitSig, localCommitIndex: Long, spec: CommitmentSpec): Either[ChannelException, LocalCommit] = {
val (localCommitTx, htlcTxs) = Commitment.makeLocalTxs(params, commitKeys, localCommitIndex, fundingKey, remoteFundingPubKey, commitInput, spec)
if (!localCommitTx.checkSig(commit.signature, remoteFundingPubKey, TxOwner.Remote, params.commitmentFormat)) {
val ((localCommitTx, redeemInfo), htlcTxs) = Commitment.makeLocalTxs(params, commitKeys, localCommitIndex, fundingKey, remoteFundingPubKey, commitInput, spec)
if (!localCommitTx.checkSig(commit.signature, redeemInfo, remoteFundingPubKey, TxOwner.Remote, params.commitmentFormat)) {
return Left(InvalidCommitmentSignature(params.channelId, fundingTxId, localCommitIndex, localCommitTx.tx))
}
val sortedHtlcTxs = htlcTxs.sortBy(_.input.outPoint.index)
val sortedHtlcTxs = htlcTxs.sortBy(_._1.input.outPoint.index)
if (commit.htlcSignatures.size != sortedHtlcTxs.size) {
return Left(HtlcSigCountMismatch(params.channelId, sortedHtlcTxs.size, commit.htlcSignatures.size))
}
val htlcTxsAndRemoteSigs = sortedHtlcTxs.zip(commit.htlcSignatures).toList.map {
case (htlcTx: HtlcTx, remoteSig) =>
if (!htlcTx.checkSig(remoteSig, commitKeys.theirHtlcPublicKey, TxOwner.Remote, params.commitmentFormat)) {
case ((htlcTx: HtlcTx, redeemInfo: RedeemInfo), remoteSig) =>
if (!htlcTx.checkSig(remoteSig, redeemInfo, commitKeys.theirHtlcPublicKey, TxOwner.Remote, params.commitmentFormat)) {
return Left(InvalidHtlcSignature(params.channelId, htlcTx.tx.txid))
}
HtlcTxAndRemoteSig(htlcTx, remoteSig)
Expand All @@ -218,10 +218,10 @@ case class RemoteCommit(index: Long, spec: CommitmentSpec, txid: TxId, remotePer
def sign(params: ChannelParams, channelKeys: ChannelKeys, fundingTxIndex: Long, remoteFundingPubKey: PublicKey, commitInput: InputInfo): CommitSig = {
val fundingKey = channelKeys.fundingKey(fundingTxIndex)
val commitKeys = RemoteCommitmentKeys(params, channelKeys, remotePerCommitmentPoint)
val (remoteCommitTx, htlcTxs) = Commitment.makeRemoteTxs(params, commitKeys, index, fundingKey, remoteFundingPubKey, commitInput, spec)
val sig = remoteCommitTx.sign(fundingKey, TxOwner.Remote, params.commitmentFormat, Map.empty)
val sortedHtlcTxs = htlcTxs.sortBy(_.input.outPoint.index)
val htlcSigs = sortedHtlcTxs.map(_.sign(commitKeys.ourHtlcKey, TxOwner.Remote, params.commitmentFormat, Map.empty))
val ((remoteCommitTx, redeemInfo), htlcTxs) = Commitment.makeRemoteTxs(params, commitKeys, index, fundingKey, remoteFundingPubKey, commitInput, spec)
val sig = remoteCommitTx.sign(fundingKey, redeemInfo, TxOwner.Remote, params.commitmentFormat, Map.empty)
val sortedHtlcTxs = htlcTxs.sortBy(_._1.input.outPoint.index)
val htlcSigs = sortedHtlcTxs.map{ case (tx, redeemInfo) => tx.sign(commitKeys.ourHtlcKey, redeemInfo, TxOwner.Remote, params.commitmentFormat, Map.empty) }
CommitSig(params.channelId, sig, htlcSigs.toList)
}
}
Expand Down Expand Up @@ -625,11 +625,11 @@ case class Commitment(fundingTxIndex: Long,
// remote commitment will include all local proposed changes + remote acked changes
val spec = CommitmentSpec.reduce(remoteCommit.spec, changes.remoteChanges.acked, changes.localChanges.proposed)
val fundingKey = channelKeys.fundingKey(fundingTxIndex)
val (remoteCommitTx, htlcTxs) = Commitment.makeRemoteTxs(params, commitKeys, remoteCommit.index + 1, fundingKey, remoteFundingPubKey, commitInput, spec)
val sig = remoteCommitTx.sign(fundingKey, TxOwner.Remote, params.commitmentFormat, Map.empty)
val ((remoteCommitTx, redeemInfo), htlcTxs) = Commitment.makeRemoteTxs(params, commitKeys, remoteCommit.index + 1, fundingKey, remoteFundingPubKey, commitInput, spec)
val sig = remoteCommitTx.sign(fundingKey, redeemInfo, TxOwner.Remote, params.commitmentFormat, Map.empty)

val sortedHtlcTxs: Seq[TransactionWithInputInfo] = htlcTxs.sortBy(_.input.outPoint.index)
val htlcSigs = sortedHtlcTxs.map(_.sign(commitKeys.ourHtlcKey, TxOwner.Remote, params.commitmentFormat, Map.empty))
val sortedHtlcTxs = htlcTxs.sortBy(_._1.input.outPoint.index)
val htlcSigs = sortedHtlcTxs.map { case (tx, redeemInfo) => tx.sign(commitKeys.ourHtlcKey, redeemInfo, TxOwner.Remote, params.commitmentFormat, Map.empty) }

// NB: IN/OUT htlcs are inverted because this is the remote commit
log.info(s"built remote commit number=${remoteCommit.index + 1} toLocalMsat=${spec.toLocal.toLong} toRemoteMsat=${spec.toRemote.toLong} htlc_in={} htlc_out={} feeratePerKw=${spec.commitTxFeerate} txid=${remoteCommitTx.tx.txid} fundingTxId=$fundingTxId", spec.htlcs.collect(DirectedHtlc.outgoing).map(_.id).mkString(","), spec.htlcs.collect(DirectedHtlc.incoming).map(_.id).mkString(","))
Expand Down Expand Up @@ -664,7 +664,8 @@ case class Commitment(fundingTxIndex: Long,
def fullySignedLocalCommitTx(params: ChannelParams, channelKeys: ChannelKeys): CommitTx = {
val unsignedCommitTx = localCommit.commitTxAndRemoteSig.commitTx
val fundingKey = channelKeys.fundingKey(fundingTxIndex)
val localSig = unsignedCommitTx.sign(fundingKey, TxOwner.Local, params.commitmentFormat, Map.empty)
val redeemInfo = Helpers.Funding.makeFundingRedeemInfo(fundingKey.publicKey, remoteFundingPubKey, params.commitmentFormat)
val localSig = unsignedCommitTx.sign(fundingKey, redeemInfo, TxOwner.Local, params.commitmentFormat, Map.empty)
val RemoteSignature.FullSignature(remoteSig) = localCommit.commitTxAndRemoteSig.remoteSig
val commitTx = unsignedCommitTx.addSigs(fundingKey.publicKey, remoteFundingPubKey, localSig, remoteSig)
// We verify the remote signature when receiving their commit_sig, so this check should always pass.
Expand All @@ -681,11 +682,12 @@ object Commitment {
localFundingKey: PrivateKey,
remoteFundingPubKey: PublicKey,
commitmentInput: InputInfo,
spec: CommitmentSpec): (CommitTx, Seq[HtlcTx]) = {
spec: CommitmentSpec): ((CommitTx, RedeemInfo), Seq[(HtlcTx, RedeemInfo)]) = {
val outputs = makeCommitTxOutputs(localFundingKey.publicKey, remoteFundingPubKey, commitKeys.publicKeys, params.localParams.paysCommitTxFees, params.localParams.dustLimit, params.remoteParams.toSelfDelay, spec, params.commitmentFormat)
val commitTx = makeCommitTx(commitmentInput, commitTxNumber, commitKeys.ourPaymentBasePoint, params.remoteParams.paymentBasepoint, params.localParams.isChannelOpener, outputs)
val htlcTxs = makeHtlcTxs(commitKeys.publicKeys, commitTx.tx, params.localParams.dustLimit, params.remoteParams.toSelfDelay, spec.htlcTxFeerate(params.commitmentFormat), outputs, params.commitmentFormat)
(commitTx, htlcTxs)
val redeemInfo = Helpers.Funding.makeFundingRedeemInfo(localFundingKey.publicKey, remoteFundingPubKey, params.commitmentFormat)
val htlcTxs = makeHtlcTxs(commitTx.tx, outputs, params.commitmentFormat)
(commitTx -> redeemInfo, htlcTxs)
}

def makeRemoteTxs(params: ChannelParams,
Expand All @@ -694,11 +696,12 @@ object Commitment {
localFundingKey: PrivateKey,
remoteFundingPubKey: PublicKey,
commitmentInput: InputInfo,
spec: CommitmentSpec): (CommitTx, Seq[HtlcTx]) = {
spec: CommitmentSpec): ((CommitTx, RedeemInfo), Seq[(HtlcTx, RedeemInfo)]) = {
val outputs = makeCommitTxOutputs(remoteFundingPubKey, localFundingKey.publicKey, commitKeys.publicKeys, !params.localParams.paysCommitTxFees, params.remoteParams.dustLimit, params.localParams.toSelfDelay, spec, params.commitmentFormat)
val commitTx = makeCommitTx(commitmentInput, commitTxNumber, params.remoteParams.paymentBasepoint, commitKeys.ourPaymentBasePoint, !params.localParams.isChannelOpener, outputs)
val htlcTxs = makeHtlcTxs(commitKeys.publicKeys, commitTx.tx, params.remoteParams.dustLimit, params.localParams.toSelfDelay, spec.htlcTxFeerate(params.commitmentFormat), outputs, params.commitmentFormat)
(commitTx, htlcTxs)
val redeemInfo = Helpers.Funding.makeFundingRedeemInfo(localFundingKey.publicKey, remoteFundingPubKey, params.commitmentFormat)
val htlcTxs = makeHtlcTxs(commitTx.tx, outputs, params.commitmentFormat)
(commitTx -> redeemInfo, htlcTxs)
}
}

Expand Down Expand Up @@ -1131,11 +1134,8 @@ case class Commitments(params: ChannelParams,
active.forall { commitment =>
val localFundingKey = channelKeys.fundingKey(commitment.fundingTxIndex).publicKey
val remoteFundingKey = commitment.remoteFundingPubKey
val fundingScript = Script.write(Scripts.multiSig2of2(localFundingKey, remoteFundingKey))
commitment.commitInput match {
case InputInfo.SegwitInput(_, _, redeemScript) => redeemScript == fundingScript
case _: InputInfo.TaprootInput => false
}
val redeemInfo = Helpers.Funding.makeFundingRedeemInfo(localFundingKey, remoteFundingKey, params.commitmentFormat)
commitment.commitInput.txOut.publicKeyScript == Script.write(redeemInfo.publicKeyScript)
}
}

Expand Down
Loading