Skip to content

Commit

Permalink
Set htlc tx fee to zero
Browse files Browse the repository at this point in the history
When the channel type is anchor outputs with zero fee htlc txs, we set
the fees for the htlc txs to 0.

An important side-effect is that it changes the trimmed to dust calculation,
and outputs that were previously dust can now be included in the commit tx.
  • Loading branch information
t-bast committed Sep 2, 2021
1 parent 559df01 commit 57140b3
Show file tree
Hide file tree
Showing 24 changed files with 494 additions and 255 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ package fr.acinq.eclair.blockchain.fee
import fr.acinq.bitcoin.Crypto.PublicKey
import fr.acinq.bitcoin.Satoshi
import fr.acinq.eclair.blockchain.CurrentFeerates
import fr.acinq.eclair.channel.{ChannelType, ChannelTypes, SupportedChannelType}
import fr.acinq.eclair.channel.{ChannelTypes, SupportedChannelType}
import fr.acinq.eclair.transactions.Transactions.AnchorOutputsCommitmentFormat

trait FeeEstimator {
// @formatter:off
Expand Down Expand Up @@ -68,12 +69,12 @@ case class OnChainFeeConf(feeTargets: FeeTargets, feeEstimator: FeeEstimator, cl
* @param channelType channel type
* @param currentFeerates_opt if provided, will be used to compute the most up-to-date network fee, otherwise we rely on the fee estimator
*/
def getCommitmentFeerate(remoteNodeId: PublicKey, channelType: ChannelType, channelCapacity: Satoshi, currentFeerates_opt: Option[CurrentFeerates]): FeeratePerKw = {
def getCommitmentFeerate(remoteNodeId: PublicKey, channelType: SupportedChannelType, channelCapacity: Satoshi, currentFeerates_opt: Option[CurrentFeerates]): FeeratePerKw = {
val networkFeerate = currentFeerates_opt match {
case Some(currentFeerates) => currentFeerates.feeratesPerKw.feePerBlock(feeTargets.commitmentBlockTarget)
case None => feeEstimator.getFeeratePerKw(feeTargets.commitmentBlockTarget)
}
if (channelType == ChannelTypes.AnchorOutputs || channelType == ChannelTypes.AnchorOutputsZeroFeeHtlcTxs) {
if (channelType.commitmentFormat == AnchorOutputsCommitmentFormat) {
networkFeerate.min(feerateToleranceFor(remoteNodeId).anchorOutputMaxCommitFeerate)
} else {
networkFeerate
Expand Down
20 changes: 10 additions & 10 deletions eclair-core/src/main/scala/fr/acinq/eclair/channel/Channel.scala
Original file line number Diff line number Diff line change
Expand Up @@ -801,8 +801,8 @@ class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId
val nextCommitNumber = nextRemoteCommit.index
// we persist htlc data in order to be able to claim htlc outputs in case a revoked tx is published by our
// counterparty, so only htlcs above remote's dust_limit matter
val trimmedHtlcs = Transactions.trimOfferedHtlcs(d.commitments.remoteParams.dustLimit, nextRemoteCommit.spec, d.commitments.commitmentFormat) ++
Transactions.trimReceivedHtlcs(d.commitments.remoteParams.dustLimit, nextRemoteCommit.spec, d.commitments.commitmentFormat)
val trimmedHtlcs = Transactions.trimOfferedHtlcs(d.commitments.remoteParams.dustLimit, nextRemoteCommit.spec, d.commitments.channelType) ++
Transactions.trimReceivedHtlcs(d.commitments.remoteParams.dustLimit, nextRemoteCommit.spec, d.commitments.channelType)
trimmedHtlcs.map(_.add).foreach { htlc =>
log.info(s"adding paymentHash=${htlc.paymentHash} cltvExpiry=${htlc.cltvExpiry} to htlcs db for commitNumber=$nextCommitNumber")
nodeParams.db.channels.addHtlcInfo(d.channelId, nextCommitNumber, htlc.paymentHash, htlc.cltvExpiry)
Expand Down Expand Up @@ -1159,8 +1159,8 @@ class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId
val nextCommitNumber = nextRemoteCommit.index
// we persist htlc data in order to be able to claim htlc outputs in case a revoked tx is published by our
// counterparty, so only htlcs above remote's dust_limit matter
val trimmedHtlcs = Transactions.trimOfferedHtlcs(d.commitments.remoteParams.dustLimit, nextRemoteCommit.spec, d.commitments.commitmentFormat) ++
Transactions.trimReceivedHtlcs(d.commitments.remoteParams.dustLimit, nextRemoteCommit.spec, d.commitments.commitmentFormat)
val trimmedHtlcs = Transactions.trimOfferedHtlcs(d.commitments.remoteParams.dustLimit, nextRemoteCommit.spec, d.commitments.channelType) ++
Transactions.trimReceivedHtlcs(d.commitments.remoteParams.dustLimit, nextRemoteCommit.spec, d.commitments.channelType)
trimmedHtlcs.map(_.add).foreach { htlc =>
log.info(s"adding paymentHash=${htlc.paymentHash} cltvExpiry=${htlc.cltvExpiry} to htlcs db for commitNumber=$nextCommitNumber")
nodeParams.db.channels.addHtlcInfo(d.channelId, nextCommitNumber, htlc.paymentHash, htlc.cltvExpiry)
Expand Down Expand Up @@ -1432,8 +1432,8 @@ class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId
}
// we may need to fail some htlcs in case a commitment tx was published and they have reached the timeout threshold
val timedOutHtlcs = Closing.isClosingTypeAlreadyKnown(d1) match {
case Some(c: Closing.LocalClose) => Closing.timedOutHtlcs(d.commitments.commitmentFormat, c.localCommit, c.localCommitPublished, d.commitments.localParams.dustLimit, tx)
case Some(c: Closing.RemoteClose) => Closing.timedOutHtlcs(d.commitments.commitmentFormat, c.remoteCommit, c.remoteCommitPublished, d.commitments.remoteParams.dustLimit, tx)
case Some(c: Closing.LocalClose) => Closing.timedOutHtlcs(d.commitments.channelType, c.localCommit, c.localCommitPublished, d.commitments.localParams.dustLimit, tx)
case Some(c: Closing.RemoteClose) => Closing.timedOutHtlcs(d.commitments.channelType, c.remoteCommit, c.remoteCommitPublished, d.commitments.remoteParams.dustLimit, tx)
case _ => Set.empty[UpdateAddHtlc] // we lose htlc outputs in dataloss protection scenarii (future remote commit)
}
timedOutHtlcs.foreach { add =>
Expand Down Expand Up @@ -1684,7 +1684,7 @@ class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId
// we send it (if needed) when reconnected.
val shutdownInProgress = d.localShutdown.nonEmpty || d.remoteShutdown.nonEmpty
if (d.commitments.localParams.isFunder && !shutdownInProgress) {
val currentFeeratePerKw = d.commitments.localCommit.spec.feeratePerKw
val currentFeeratePerKw = d.commitments.localCommit.spec.commitTxFeerate
val networkFeeratePerKw = nodeParams.onChainFeeConf.getCommitmentFeerate(remoteNodeId, d.commitments.channelType, d.commitments.capacity, None)
if (nodeParams.onChainFeeConf.shouldUpdateFee(currentFeeratePerKw, networkFeeratePerKw)) {
self ! CMD_UPDATE_FEE(networkFeeratePerKw, commit = true)
Expand Down Expand Up @@ -1977,7 +1977,7 @@ class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId

private def handleCurrentFeerate(c: CurrentFeerates, d: HasCommitments) = {
val networkFeeratePerKw = nodeParams.onChainFeeConf.getCommitmentFeerate(remoteNodeId, d.commitments.channelType, d.commitments.capacity, Some(c))
val currentFeeratePerKw = d.commitments.localCommit.spec.feeratePerKw
val currentFeeratePerKw = d.commitments.localCommit.spec.commitTxFeerate
val shouldUpdateFee = d.commitments.localParams.isFunder && nodeParams.onChainFeeConf.shouldUpdateFee(currentFeeratePerKw, networkFeeratePerKw)
val shouldClose = !d.commitments.localParams.isFunder &&
nodeParams.onChainFeeConf.feerateToleranceFor(d.commitments.remoteNodeId).isFeeDiffTooHigh(d.commitments.channelType, networkFeeratePerKw, currentFeeratePerKw) &&
Expand All @@ -1986,7 +1986,7 @@ class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId
self ! CMD_UPDATE_FEE(networkFeeratePerKw, commit = true)
stay()
} else if (shouldClose) {
handleLocalError(FeerateTooDifferent(d.channelId, localFeeratePerKw = networkFeeratePerKw, remoteFeeratePerKw = d.commitments.localCommit.spec.feeratePerKw), d, Some(c))
handleLocalError(FeerateTooDifferent(d.channelId, localFeeratePerKw = networkFeeratePerKw, remoteFeeratePerKw = d.commitments.localCommit.spec.commitTxFeerate), d, Some(c))
} else {
stay()
}
Expand All @@ -2001,7 +2001,7 @@ class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId
*/
private def handleOfflineFeerate(c: CurrentFeerates, d: HasCommitments) = {
val networkFeeratePerKw = nodeParams.onChainFeeConf.getCommitmentFeerate(remoteNodeId, d.commitments.channelType, d.commitments.capacity, Some(c))
val currentFeeratePerKw = d.commitments.localCommit.spec.feeratePerKw
val currentFeeratePerKw = d.commitments.localCommit.spec.commitTxFeerate
// if the network fees are too high we risk to not be able to confirm our current commitment
val shouldClose = networkFeeratePerKw > currentFeeratePerKw &&
nodeParams.onChainFeeConf.feerateToleranceFor(d.commitments.remoteNodeId).isFeeDiffTooHigh(d.commitments.channelType, networkFeeratePerKw, currentFeeratePerKw) &&
Expand Down
Loading

0 comments on commit 57140b3

Please sign in to comment.