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
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,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 @@ -43,7 +43,7 @@ trait SpendFromChannelAddress {
inputTx <- appKit.wallet.getTransaction(outPoint.txid)
localFundingPubkey = appKit.nodeParams.channelKeyManager.fundingPublicKey(fundingKeyPath, fundingTxIndex)
fundingRedeemScript = multiSig2of2(localFundingPubkey.publicKey, remoteFundingPubkey)
inputInfo = InputInfo(outPoint, inputTx.txOut(outPoint.index.toInt), fundingRedeemScript)
inputInfo = InputInfo.SegwitInput(outPoint, inputTx.txOut(outPoint.index.toInt), fundingRedeemScript)
localSig = appKit.nodeParams.channelKeyManager.sign(
Transactions.SpliceTx(inputInfo, unsignedTx), // classify as splice, doesn't really matter
localFundingPubkey,
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 | Transactions.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
64 changes: 47 additions & 17 deletions eclair-core/src/main/scala/fr/acinq/eclair/channel/Helpers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import fr.acinq.eclair.payment.relay.Relayer.RelayFees
import fr.acinq.eclair.router.Announcements
import fr.acinq.eclair.transactions.DirectedHtlc._
import fr.acinq.eclair.transactions.Scripts._
import fr.acinq.eclair.transactions.Transactions.InputInfo.RedeemPath
import fr.acinq.eclair.transactions.Transactions._
import fr.acinq.eclair.transactions._
import fr.acinq.eclair.wire.protocol._
Expand Down Expand Up @@ -275,7 +276,7 @@ object Helpers {

for {
script_opt <- extractShutdownScript(accept.temporaryChannelId, localFeatures, remoteFeatures, accept.upfrontShutdownScript_opt)
fundingScript = Funding.makeFundingPubKeyScript(open.fundingPubkey, accept.fundingPubkey)
fundingScript = Funding.makeFundingPubKeyScript(open.fundingPubkey, accept.fundingPubkey, channelType.commitmentFormat)
liquidityPurchase_opt <- LiquidityAds.validateRemoteFunding(open.requestFunding_opt, remoteNodeId, accept.temporaryChannelId, fundingScript, accept.fundingAmount, open.fundingFeerate, isChannelCreation = true, accept.willFund_opt)
} yield {
val channelFeatures = ChannelFeatures(channelType, localFeatures, remoteFeatures, open.channelFlags.announceChannel)
Expand Down Expand Up @@ -372,13 +373,20 @@ object Helpers {
}

object Funding {
def makeFundingPubKeyScript(localFundingKey: PublicKey, remoteFundingKey: PublicKey, commitmentFormat: CommitmentFormat): ByteVector = commitmentFormat match {
case DefaultCommitmentFormat | _: AnchorOutputsCommitmentFormat => write(pay2wsh(multiSig2of2(localFundingKey, remoteFundingKey)))
case SimpleTaprootChannelCommitmentFormat => write(Taproot.musig2FundingScript(localFundingKey, remoteFundingKey))
}

def makeFundingPubKeyScript(localFundingKey: PublicKey, remoteFundingKey: PublicKey): ByteVector = write(pay2wsh(multiSig2of2(localFundingKey, remoteFundingKey)))

def makeFundingInputInfo(fundingTxId: TxId, fundingTxOutputIndex: Int, fundingSatoshis: Satoshi, fundingPubkey1: PublicKey, fundingPubkey2: PublicKey): InputInfo.SegwitInput = {
val fundingScript = multiSig2of2(fundingPubkey1, fundingPubkey2)
val fundingTxOut = TxOut(fundingSatoshis, pay2wsh(fundingScript))
InputInfo.SegwitInput(OutPoint(fundingTxId, fundingTxOutputIndex), fundingTxOut, write(fundingScript))
def makeFundingInputInfo(fundingTxId: TxId, fundingTxOutputIndex: Int, fundingSatoshis: Satoshi, fundingPubkey1: PublicKey, fundingPubkey2: PublicKey, commitmentFormat: CommitmentFormat): InputInfo = commitmentFormat match {
case SimpleTaprootChannelCommitmentFormat =>
val fundingScript = Taproot.musig2FundingScript(fundingPubkey1, fundingPubkey2)
val fundingTxOut = TxOut(fundingSatoshis, fundingScript)
InputInfo.TaprootInput(OutPoint(fundingTxId, fundingTxOutputIndex), fundingTxOut, Taproot.musig2Aggregate(fundingPubkey1, fundingPubkey2), RedeemPath.KeyPath(None))
case DefaultCommitmentFormat | _: AnchorOutputsCommitmentFormat =>
val fundingScript = multiSig2of2(fundingPubkey1, fundingPubkey2)
val fundingTxOut = TxOut(fundingSatoshis, pay2wsh(fundingScript))
InputInfo.SegwitInput(OutPoint(fundingTxId, fundingTxOutputIndex), fundingTxOut, write(fundingScript))
}

/**
Expand Down Expand Up @@ -441,7 +449,7 @@ object Helpers {

val fundingPubKey = keyManager.fundingPublicKey(localParams.fundingKeyPath, fundingTxIndex)
val channelKeyPath = keyManager.keyPath(localParams, channelConfig)
val commitmentInput = makeFundingInputInfo(fundingTxId, fundingTxOutputIndex, fundingAmount, fundingPubKey.publicKey, remoteFundingPubKey)
val commitmentInput = makeFundingInputInfo(fundingTxId, fundingTxOutputIndex, fundingAmount, fundingPubKey.publicKey, remoteFundingPubKey, params.commitmentFormat)
val localPerCommitmentPoint = keyManager.commitmentPoint(channelKeyPath, localCommitmentIndex)
val (localCommitTx, _) = Commitment.makeLocalTxs(keyManager, channelConfig, channelFeatures, localCommitmentIndex, localParams, remoteParams, fundingTxIndex, remoteFundingPubKey, commitmentInput, localPerCommitmentPoint, localSpec)
val (remoteCommitTx, htlcTxs) = Commitment.makeRemoteTxs(keyManager, channelConfig, channelFeatures, remoteCommitmentIndex, localParams, remoteParams, fundingTxIndex, remoteFundingPubKey, commitmentInput, remotePerCommitmentPoint, remoteSpec)
Expand Down Expand Up @@ -679,7 +687,7 @@ object Helpers {
case DefaultCommitmentFormat =>
// we "MUST set fee_satoshis less than or equal to the base fee of the final commitment transaction"
requestedFeerate.min(commitment.localCommit.spec.commitTxFeerate)
case _: AnchorOutputsCommitmentFormat => requestedFeerate
case _: AnchorOutputsCommitmentFormat | SimpleTaprootChannelCommitmentFormat => requestedFeerate
}
// NB: we choose a minimum fee that ensures the tx will easily propagate while allowing low fees since we can
// always use CPFP to speed up confirmation if necessary.
Expand Down Expand Up @@ -909,13 +917,25 @@ object Helpers {

def claimAnchors(keyManager: ChannelKeyManager, commitment: FullCommitment, lcp: LocalCommitPublished, confirmationTarget: ConfirmationTarget)(implicit log: LoggingAdapter): LocalCommitPublished = {
if (shouldUpdateAnchorTxs(lcp.claimAnchorTxs, confirmationTarget)) {
val localFundingPubKey = keyManager.fundingPublicKey(commitment.localParams.fundingKeyPath, commitment.fundingTxIndex).publicKey
val (localAnchorKey, remoteAnchorKey) = commitment.params.commitmentFormat match {
case DefaultCommitmentFormat | _: AnchorOutputsCommitmentFormat =>
// The public keys used in this case are the channel funding public keys.
val localFundingPubkey = keyManager.fundingPublicKey(commitment.localParams.fundingKeyPath, commitment.fundingTxIndex).publicKey
(localFundingPubkey, commitment.remoteFundingPubKey)
case SimpleTaprootChannelCommitmentFormat =>
// The public keys used in this case are the payment public keys: we don't want to reveal individual
// funding public keys since we're using musig2.
val channelKeyPath = keyManager.keyPath(commitment.localParams, commitment.params.channelConfig)
val localPerCommitmentPoint = keyManager.commitmentPoint(channelKeyPath, commitment.localCommit.index)
val localDelayedPaymentPubkey = Generators.derivePubKey(keyManager.delayedPaymentPoint(channelKeyPath).publicKey, localPerCommitmentPoint)
(localDelayedPaymentPubkey, commitment.remoteParams.paymentBasepoint)
}
val claimAnchorTxs = List(
withTxGenerationLog("local-anchor") {
Transactions.makeClaimLocalAnchorOutputTx(lcp.commitTx, localFundingPubKey, confirmationTarget)
Transactions.makeClaimLocalAnchorOutputTx(lcp.commitTx, localAnchorKey, confirmationTarget)
},
withTxGenerationLog("remote-anchor") {
Transactions.makeClaimRemoteAnchorOutputTx(lcp.commitTx, commitment.remoteFundingPubKey)
Transactions.makeClaimRemoteAnchorOutputTx(lcp.commitTx, remoteAnchorKey)
}
).flatten
lcp.copy(claimAnchorTxs = claimAnchorTxs)
Expand Down Expand Up @@ -1038,13 +1058,23 @@ object Helpers {

def claimAnchors(keyManager: ChannelKeyManager, commitment: FullCommitment, rcp: RemoteCommitPublished, confirmationTarget: ConfirmationTarget)(implicit log: LoggingAdapter): RemoteCommitPublished = {
if (shouldUpdateAnchorTxs(rcp.claimAnchorTxs, confirmationTarget)) {
val localFundingPubkey = keyManager.fundingPublicKey(commitment.localParams.fundingKeyPath, commitment.fundingTxIndex).publicKey
val (localAnchorKey, remoteAnchorKey) = commitment.params.commitmentFormat match {
case DefaultCommitmentFormat | _: AnchorOutputsCommitmentFormat =>
// The public keys used in this case are the channel funding public keys.
val localFundingPubkey = keyManager.fundingPublicKey(commitment.localParams.fundingKeyPath, commitment.fundingTxIndex).publicKey
(localFundingPubkey, commitment.remoteFundingPubKey)
case SimpleTaprootChannelCommitmentFormat =>
val channelKeyPath = keyManager.keyPath(commitment.localParams, commitment.params.channelConfig)
val localPaymentPubkey = keyManager.paymentPoint(channelKeyPath).publicKey
val remoteDelayedPaymentPubkey = Generators.derivePubKey(commitment.remoteParams.delayedPaymentBasepoint, commitment.remoteCommit.remotePerCommitmentPoint)
(localPaymentPubkey, remoteDelayedPaymentPubkey)
}
val claimAnchorTxs = List(
withTxGenerationLog("local-anchor") {
Transactions.makeClaimLocalAnchorOutputTx(rcp.commitTx, localFundingPubkey, confirmationTarget)
Transactions.makeClaimLocalAnchorOutputTx(rcp.commitTx, localAnchorKey, confirmationTarget)
},
withTxGenerationLog("remote-anchor") {
Transactions.makeClaimRemoteAnchorOutputTx(rcp.commitTx, commitment.remoteFundingPubKey)
Transactions.makeClaimRemoteAnchorOutputTx(rcp.commitTx, remoteAnchorKey)
}
).flatten
rcp.copy(claimAnchorTxs = claimAnchorTxs)
Expand Down Expand Up @@ -1077,7 +1107,7 @@ object Helpers {
Transactions.addSigs(claimMain, localPubkey, sig)
})
}
case _: AnchorOutputsCommitmentFormat => withTxGenerationLog("remote-main-delayed") {
case _: AnchorOutputsCommitmentFormat | SimpleTaprootChannelCommitmentFormat => withTxGenerationLog("remote-main-delayed") {
Transactions.makeClaimRemoteDelayedOutputTx(tx, params.localParams.dustLimit, localPaymentPoint, finalScriptPubKey, feeratePerKwMain).map(claimMain => {
val sig = keyManager.sign(claimMain, keyManager.paymentPoint(channelKeyPath), TxOwner.Local, params.commitmentFormat, Map.empty)
Transactions.addSigs(claimMain, sig)
Expand Down Expand Up @@ -1224,7 +1254,7 @@ object Helpers {
Transactions.addSigs(claimMain, localPaymentPubkey, sig)
})
}
case _: AnchorOutputsCommitmentFormat => withTxGenerationLog("remote-main-delayed") {
case _: AnchorOutputsCommitmentFormat | SimpleTaprootChannelCommitmentFormat => withTxGenerationLog("remote-main-delayed") {
Transactions.makeClaimRemoteDelayedOutputTx(commitTx, localParams.dustLimit, localPaymentPoint, finalScriptPubKey, feerateMain).map(claimMain => {
val sig = keyManager.sign(claimMain, keyManager.paymentPoint(channelKeyPath), TxOwner.Local, commitmentFormat, Map.empty)
Transactions.addSigs(claimMain, sig)
Expand Down
Loading
Loading