mirror of
https://github.com/ACINQ/eclair.git
synced 2025-03-13 19:37:35 +01:00
High fee tolerance for small channels (#1595)
For small channels we blindly accept feerate from funder. This is a temporary fix to allow making payment in high fees environment and will be removed when "anchor outputs" is deployed
This commit is contained in:
parent
bef94ed9c2
commit
e876b10ffa
5 changed files with 45 additions and 9 deletions
|
@ -1843,7 +1843,7 @@ class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId
|
|||
val shouldUpdateFee = d.commitments.localParams.isFunder &&
|
||||
Helpers.shouldUpdateFee(currentFeeratePerKw, networkFeeratePerKw, nodeParams.onChainFeeConf.updateFeeMinDiffRatio)
|
||||
val shouldClose = !d.commitments.localParams.isFunder &&
|
||||
Helpers.isFeeDiffTooHigh(networkFeeratePerKw, currentFeeratePerKw, nodeParams.onChainFeeConf.maxFeerateMismatch) &&
|
||||
Helpers.isFeeDiffTooHigh(networkFeeratePerKw, currentFeeratePerKw, nodeParams.onChainFeeConf.maxFeerateMismatch, d.commitments.commitInput.txOut.amount) &&
|
||||
d.commitments.hasPendingOrProposedHtlcs // we close only if we have HTLCs potentially at risk
|
||||
if (shouldUpdateFee) {
|
||||
self ! CMD_UPDATE_FEE(networkFeeratePerKw, commit = true)
|
||||
|
@ -1867,7 +1867,7 @@ class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId
|
|||
val currentFeeratePerKw = d.commitments.localCommit.spec.feeratePerKw
|
||||
// if the network fees are too high we risk to not be able to confirm our current commitment
|
||||
val shouldClose = networkFeeratePerKw > currentFeeratePerKw &&
|
||||
Helpers.isFeeDiffTooHigh(networkFeeratePerKw, currentFeeratePerKw, nodeParams.onChainFeeConf.maxFeerateMismatch) &&
|
||||
Helpers.isFeeDiffTooHigh(networkFeeratePerKw, currentFeeratePerKw, nodeParams.onChainFeeConf.maxFeerateMismatch, d.commitments.commitInput.txOut.amount) &&
|
||||
d.commitments.hasPendingOrProposedHtlcs // we close only if we have HTLCs potentially at risk
|
||||
if (shouldClose) {
|
||||
if (nodeParams.onChainFeeConf.closeOnOfflineMismatch) {
|
||||
|
|
|
@ -205,7 +205,7 @@ object Commitments {
|
|||
// we allowed mismatches between our feerates and our remote's as long as commitments didn't contain any HTLC at risk
|
||||
// we need to verify that we're not disagreeing on feerates anymore before offering new HTLCs
|
||||
val localFeeratePerKw = feeConf.feeEstimator.getFeeratePerKw(target = feeConf.feeTargets.commitmentBlockTarget)
|
||||
if (Helpers.isFeeDiffTooHigh(localFeeratePerKw, commitments.localCommit.spec.feeratePerKw, feeConf.maxFeerateMismatch)) {
|
||||
if (Helpers.isFeeDiffTooHigh(localFeeratePerKw, commitments.localCommit.spec.feeratePerKw, feeConf.maxFeerateMismatch, commitments.commitInput.txOut.amount)) {
|
||||
return Failure(FeerateTooDifferent(commitments.channelId, localFeeratePerKw = localFeeratePerKw, remoteFeeratePerKw = commitments.localCommit.spec.feeratePerKw))
|
||||
}
|
||||
|
||||
|
@ -264,7 +264,7 @@ object Commitments {
|
|||
// we allowed mismatches between our feerates and our remote's as long as commitments didn't contain any HTLC at risk
|
||||
// we need to verify that we're not disagreeing on feerates anymore before accepting new HTLCs
|
||||
val localFeeratePerKw = feeConf.feeEstimator.getFeeratePerKw(target = feeConf.feeTargets.commitmentBlockTarget)
|
||||
if (Helpers.isFeeDiffTooHigh(localFeeratePerKw, commitments.localCommit.spec.feeratePerKw, feeConf.maxFeerateMismatch)) {
|
||||
if (Helpers.isFeeDiffTooHigh(localFeeratePerKw, commitments.localCommit.spec.feeratePerKw, feeConf.maxFeerateMismatch, commitments.commitInput.txOut.amount)) {
|
||||
throw FeerateTooDifferent(commitments.channelId, localFeeratePerKw = localFeeratePerKw, remoteFeeratePerKw = commitments.localCommit.spec.feeratePerKw)
|
||||
}
|
||||
|
||||
|
@ -426,7 +426,7 @@ object Commitments {
|
|||
Metrics.RemoteFeeratePerKw.withoutTags().record(fee.feeratePerKw)
|
||||
val localFeeratePerKw = feeConf.feeEstimator.getFeeratePerKw(target = feeConf.feeTargets.commitmentBlockTarget)
|
||||
log.info("remote feeratePerKw={}, local feeratePerKw={}, ratio={}", fee.feeratePerKw, localFeeratePerKw, fee.feeratePerKw.toDouble / localFeeratePerKw)
|
||||
if (Helpers.isFeeDiffTooHigh(localFeeratePerKw, fee.feeratePerKw, feeConf.maxFeerateMismatch) && commitments.hasPendingOrProposedHtlcs) {
|
||||
if (Helpers.isFeeDiffTooHigh(localFeeratePerKw, fee.feeratePerKw, feeConf.maxFeerateMismatch, commitments.commitInput.txOut.amount) && commitments.hasPendingOrProposedHtlcs) {
|
||||
Failure(FeerateTooDifferent(commitments.channelId, localFeeratePerKw = localFeeratePerKw, remoteFeeratePerKw = fee.feeratePerKw))
|
||||
} else {
|
||||
// NB: we check that the funder can afford this new fee even if spec allows to do it at next signature
|
||||
|
|
|
@ -142,7 +142,7 @@ object Helpers {
|
|||
}
|
||||
|
||||
val localFeeratePerKw = nodeParams.onChainFeeConf.feeEstimator.getFeeratePerKw(target = nodeParams.onChainFeeConf.feeTargets.commitmentBlockTarget)
|
||||
if (isFeeDiffTooHigh(localFeeratePerKw, open.feeratePerKw, nodeParams.onChainFeeConf.maxFeerateMismatch)) throw FeerateTooDifferent(open.temporaryChannelId, localFeeratePerKw, open.feeratePerKw)
|
||||
if (isFeeDiffTooHigh(localFeeratePerKw, open.feeratePerKw, nodeParams.onChainFeeConf.maxFeerateMismatch, open.fundingSatoshis)) throw FeerateTooDifferent(open.temporaryChannelId, localFeeratePerKw, open.feeratePerKw)
|
||||
// only enforce dust limit check on mainnet
|
||||
if (nodeParams.chainHash == Block.LivenetGenesisBlock.hash) {
|
||||
if (open.dustLimitSatoshis < Channel.MIN_DUSTLIMIT) throw DustLimitTooSmall(open.temporaryChannelId, open.dustLimitSatoshis, Channel.MIN_DUSTLIMIT)
|
||||
|
@ -216,10 +216,18 @@ object Helpers {
|
|||
* @param referenceFeePerKw reference fee rate per kiloweight
|
||||
* @param currentFeePerKw current fee rate per kiloweight
|
||||
* @param maxFeerateMismatch maximum fee rate mismatch tolerated
|
||||
* @param channelCapacity capacity of the channel
|
||||
* @return true if the difference between proposed and reference fee rates is too high.
|
||||
*/
|
||||
def isFeeDiffTooHigh(referenceFeePerKw: Long, currentFeePerKw: Long, maxFeerateMismatch: FeerateTolerance): Boolean =
|
||||
currentFeePerKw < referenceFeePerKw * maxFeerateMismatch.ratioLow || referenceFeePerKw * maxFeerateMismatch.ratioHigh < currentFeePerKw
|
||||
def isFeeDiffTooHigh(referenceFeePerKw: Long, currentFeePerKw: Long, maxFeerateMismatch: FeerateTolerance, channelCapacity: Satoshi): Boolean = {
|
||||
if (channelCapacity < Satoshi(1000000)) {
|
||||
// TODO: for small channels we blindly accept feerate from funder.
|
||||
// This is a temporary fix to allow making payment in high fes environments and will be removed when "anchor outputs" is deployed
|
||||
false
|
||||
} else {
|
||||
currentFeePerKw < referenceFeePerKw * maxFeerateMismatch.ratioLow || referenceFeePerKw * maxFeerateMismatch.ratioHigh < currentFeePerKw
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param remoteFeeratePerKw remote fee rate per kiloweight
|
||||
|
|
|
@ -95,6 +95,7 @@ trait StateTestsHelperMethods extends TestKitBase with FixtureTestSuite with Par
|
|||
if (tags.contains("zero_reserve")) ChannelVersion.ZERO_RESERVE else ChannelVersion.ZEROES
|
||||
).reduce(_ | _)
|
||||
val channelFlags = if (tags.contains("channels_public")) ChannelFlags.AnnounceChannel else ChannelFlags.Empty
|
||||
val fundingSatoshis = if (tags.contains("small_channel")) 500000.sat else TestConstants.fundingSatoshis
|
||||
val pushMsat = if (tags.contains("no_push_msat")) 0.msat else TestConstants.pushMsat
|
||||
val aliceParams = Alice.channelParams
|
||||
.modify(_.channelReserve).setToIf(channelVersion.hasZeroReserve)(0.sat)
|
||||
|
@ -105,7 +106,7 @@ trait StateTestsHelperMethods extends TestKitBase with FixtureTestSuite with Par
|
|||
.modify(_.staticPaymentBasepoint).setToIf(channelVersion.hasStaticRemotekey)(Some(Helpers.getWalletPaymentBasepoint(wallet)))
|
||||
val aliceInit = Init(aliceParams.features)
|
||||
val bobInit = Init(bobParams.features)
|
||||
alice ! INPUT_INIT_FUNDER(ByteVector32.Zeroes, TestConstants.fundingSatoshis, pushMsat, TestConstants.feeratePerKw, TestConstants.feeratePerKw, aliceParams, alice2bob.ref, bobInit, channelFlags, channelVersion)
|
||||
alice ! INPUT_INIT_FUNDER(ByteVector32.Zeroes, fundingSatoshis, pushMsat, TestConstants.feeratePerKw, TestConstants.feeratePerKw, aliceParams, alice2bob.ref, bobInit, channelFlags, channelVersion)
|
||||
bob ! INPUT_INIT_FUNDEE(ByteVector32.Zeroes, bobParams, bob2alice.ref, aliceInit)
|
||||
alice2bob.expectMsgType[OpenChannel]
|
||||
alice2bob.forward(bob)
|
||||
|
|
|
@ -2166,6 +2166,19 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
|
|||
awaitCond(bob.stateName == CLOSING)
|
||||
}
|
||||
|
||||
test("recv CurrentFeerate (when fundee, commit-fee/network-fee are very different, with HTLCs, small channel)", Tag("small_channel"), Tag("no_push_msat")) { f =>
|
||||
import f._
|
||||
|
||||
addHtlc(10000000 msat, alice, bob, alice2bob, bob2alice)
|
||||
crossSign(alice, bob, alice2bob, bob2alice)
|
||||
|
||||
val sender = TestProbe()
|
||||
bob.feeEstimator.setFeerate(FeeratesPerKw.single(14000))
|
||||
val event = CurrentFeerates(FeeratesPerKw.single(14000))
|
||||
sender.send(bob, event)
|
||||
bob2alice.expectNoMsg(500 millis)
|
||||
}
|
||||
|
||||
test("recv CurrentFeerate (when fundee, commit-fee/network-fee are very different, without HTLCs)") { f =>
|
||||
import f._
|
||||
|
||||
|
@ -2184,6 +2197,20 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
|
|||
awaitCond(bob.stateName == CLOSING)
|
||||
}
|
||||
|
||||
test("recv CurrentFeerate (when fundee, commit-fee/network-fee are very different, without HTLCs, small channel)", Tag("small_channel"), Tag("no_push_msat")) { f =>
|
||||
import f._
|
||||
|
||||
val sender = TestProbe()
|
||||
bob.feeEstimator.setFeerate(FeeratesPerKw.single(1000))
|
||||
val event = CurrentFeerates(FeeratesPerKw.single(1000))
|
||||
sender.send(bob, event)
|
||||
bob2alice.expectNoMsg(250 millis)
|
||||
|
||||
// when we try to add an HTLC, it works even if the feerate is very different because this is a small channel
|
||||
alice2bob.send(bob, UpdateAddHtlc(ByteVector32.Zeroes, 0, 2500000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket))
|
||||
bob2alice.expectNoMsg(250 millis)
|
||||
}
|
||||
|
||||
test("recv BITCOIN_FUNDING_SPENT (their commit w/ htlc)") { f =>
|
||||
import f._
|
||||
val sender = TestProbe()
|
||||
|
|
Loading…
Add table
Reference in a new issue