1
0
Fork 0
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:
Pierre-Marie Padiou 2020-11-09 18:52:54 +01:00 committed by GitHub
parent bef94ed9c2
commit e876b10ffa
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 45 additions and 9 deletions

View file

@ -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) {

View file

@ -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

View file

@ -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

View file

@ -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)

View file

@ -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()