Skip to content

Commit

Permalink
use slow/medium/fast notation in FeeratesPerKw
Browse files Browse the repository at this point in the history
  • Loading branch information
pm47 committed Jun 16, 2023
1 parent f6eb2fe commit a652c76
Show file tree
Hide file tree
Showing 19 changed files with 106 additions and 137 deletions.
18 changes: 7 additions & 11 deletions eclair-core/src/main/scala/fr/acinq/eclair/Setup.scala
Original file line number Diff line number Diff line change
Expand Up @@ -207,15 +207,11 @@ class Setup(val datadir: File,

defaultFeerates = {
val confDefaultFeerates = FeeratesPerKB(
mempoolMinFee = FeeratePerKB(Satoshi(config.getLong("on-chain-fees.default-feerates.1008"))),
block_1 = FeeratePerKB(Satoshi(config.getLong("on-chain-fees.default-feerates.1"))),
blocks_2 = FeeratePerKB(Satoshi(config.getLong("on-chain-fees.default-feerates.2"))),
blocks_6 = FeeratePerKB(Satoshi(config.getLong("on-chain-fees.default-feerates.6"))),
blocks_12 = FeeratePerKB(Satoshi(config.getLong("on-chain-fees.default-feerates.12"))),
blocks_36 = FeeratePerKB(Satoshi(config.getLong("on-chain-fees.default-feerates.36"))),
blocks_72 = FeeratePerKB(Satoshi(config.getLong("on-chain-fees.default-feerates.72"))),
blocks_144 = FeeratePerKB(Satoshi(config.getLong("on-chain-fees.default-feerates.144"))),
blocks_1008 = FeeratePerKB(Satoshi(config.getLong("on-chain-fees.default-feerates.1008"))),
minimum = FeeratePerKB(Satoshi(config.getLong("on-chain-fees.default-feerates.1008"))),
fastest = FeeratePerKB(Satoshi(config.getLong("on-chain-fees.default-feerates.1"))),
fast = FeeratePerKB(Satoshi(config.getLong("on-chain-fees.default-feerates.2"))),
medium = FeeratePerKB(Satoshi(config.getLong("on-chain-fees.default-feerates.12"))),
slow = FeeratePerKB(Satoshi(config.getLong("on-chain-fees.default-feerates.1008"))),
)
feeratesPerKw.set(FeeratesPerKw(confDefaultFeerates))
confDefaultFeerates
Expand All @@ -231,8 +227,8 @@ class Setup(val datadir: File,
_ = system.scheduler.scheduleWithFixedDelay(0 seconds, 10 minutes)(() => feeProvider.getFeerates.onComplete {
case Success(feeratesPerKB) =>
feeratesPerKw.set(FeeratesPerKw(feeratesPerKB))
channel.Monitoring.Metrics.LocalFeeratePerKw.withoutTags().update(feeratesPerKw.get.blocks_2.toLong.toDouble)
blockchain.Monitoring.Metrics.MempoolMinFeeratePerKw.withoutTags().update(feeratesPerKw.get.mempoolMinFee.toLong.toDouble)
channel.Monitoring.Metrics.LocalFeeratePerKw.withoutTags().update(feeratesPerKw.get.fast.toLong.toDouble)
blockchain.Monitoring.Metrics.MempoolMinFeeratePerKw.withoutTags().update(feeratesPerKw.get.minimum.toLong.toDouble)
system.eventStream.publish(CurrentFeerates(feeratesPerKw.get))
logger.info(s"current feeratesPerKB=${feeratesPerKB} feeratesPerKw=${feeratesPerKw.get}")
feeratesRetrieved.trySuccess(Done)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,22 +50,14 @@ case class BitcoinCoreFeeProvider(rpcClient: BitcoinJsonRPCClient, defaultFeerat
mempoolMinFee <- mempoolMinFee()
block_1 <- estimateSmartFee(1)
blocks_2 <- estimateSmartFee(2)
blocks_6 <- estimateSmartFee(6)
blocks_12 <- estimateSmartFee(12)
blocks_36 <- estimateSmartFee(36)
blocks_72 <- estimateSmartFee(72)
blocks_144 <- estimateSmartFee(144)
blocks_1008 <- estimateSmartFee(1008)
} yield FeeratesPerKB(
mempoolMinFee = if (mempoolMinFee.feerate > 0.sat) mempoolMinFee else defaultFeerates.mempoolMinFee,
block_1 = if (block_1.feerate > 0.sat) block_1 else defaultFeerates.block_1,
blocks_2 = if (blocks_2.feerate > 0.sat) blocks_2 else defaultFeerates.blocks_2,
blocks_6 = if (blocks_6.feerate > 0.sat) blocks_6 else defaultFeerates.blocks_6,
blocks_12 = if (blocks_12.feerate > 0.sat) blocks_12 else defaultFeerates.blocks_12,
blocks_36 = if (blocks_36.feerate > 0.sat) blocks_36 else defaultFeerates.blocks_36,
blocks_72 = if (blocks_72.feerate > 0.sat) blocks_72 else defaultFeerates.blocks_72,
blocks_144 = if (blocks_144.feerate > 0.sat) blocks_144 else defaultFeerates.blocks_144,
blocks_1008 = if (blocks_1008.feerate > 0.sat) blocks_1008 else defaultFeerates.blocks_1008)
minimum = if (mempoolMinFee.feerate > 0.sat) mempoolMinFee else defaultFeerates.minimum,
fastest = if (block_1.feerate > 0.sat) block_1 else defaultFeerates.fastest,
fast = if (blocks_2.feerate > 0.sat) blocks_2 else defaultFeerates.fast,
medium = if (blocks_12.feerate > 0.sat) blocks_12 else defaultFeerates.medium,
slow = if (blocks_1008.feerate > 0.sat) blocks_1008 else defaultFeerates.slow)
}

object BitcoinCoreFeeProvider {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,15 +44,11 @@ case class FallbackFeeProvider(providers: Seq[FeeProvider], minFeeratePerByte: F
object FallbackFeeProvider {

private def enforceMinimumFeerate(feeratesPerKB: FeeratesPerKB, minFeeratePerKB: FeeratePerKB): FeeratesPerKB = FeeratesPerKB(
mempoolMinFee = feeratesPerKB.mempoolMinFee.max(minFeeratePerKB),
block_1 = feeratesPerKB.block_1.max(minFeeratePerKB),
blocks_2 = feeratesPerKB.blocks_2.max(minFeeratePerKB),
blocks_6 = feeratesPerKB.blocks_6.max(minFeeratePerKB),
blocks_12 = feeratesPerKB.blocks_12.max(minFeeratePerKB),
blocks_36 = feeratesPerKB.blocks_36.max(minFeeratePerKB),
blocks_72 = feeratesPerKB.blocks_72.max(minFeeratePerKB),
blocks_144 = feeratesPerKB.blocks_144.max(minFeeratePerKB),
blocks_1008 = feeratesPerKB.blocks_1008.max(minFeeratePerKB)
minimum = feeratesPerKB.minimum.max(minFeeratePerKB),
fastest = feeratesPerKB.fastest.max(minFeeratePerKB),
fast = feeratesPerKB.fast.max(minFeeratePerKB),
medium = feeratesPerKB.medium.max(minFeeratePerKB),
slow = feeratesPerKB.slow.max(minFeeratePerKB)
)

}
Original file line number Diff line number Diff line change
Expand Up @@ -110,36 +110,36 @@ object FeeratePerKw {
* The mempoolMinFee is the minimal fee required for a tx to enter the mempool (and then be relayed to other nodes and eventually get confirmed).
* If our fee provider doesn't expose this data, using its biggest block target should be a good enough estimation.
*/
case class FeeratesPerKB(mempoolMinFee: FeeratePerKB, block_1: FeeratePerKB, blocks_2: FeeratePerKB, blocks_6: FeeratePerKB, blocks_12: FeeratePerKB, blocks_36: FeeratePerKB, blocks_72: FeeratePerKB, blocks_144: FeeratePerKB, blocks_1008: FeeratePerKB) {
require(mempoolMinFee.feerate > 0.sat && block_1.feerate > 0.sat && blocks_2.feerate > 0.sat && blocks_6.feerate > 0.sat && blocks_12.feerate > 0.sat && blocks_36.feerate > 0.sat && blocks_72.feerate > 0.sat && blocks_144.feerate > 0.sat && blocks_1008.feerate > 0.sat, "all feerates must be strictly greater than 0")
case class FeeratesPerKB(minimum: FeeratePerKB,
fastest: FeeratePerKB,
fast: FeeratePerKB,
medium: FeeratePerKB,
slow: FeeratePerKB) {
require(minimum.feerate > 0.sat && fastest.feerate > 0.sat && fast.feerate > 0.sat && medium.feerate > 0.sat && slow.feerate > 0.sat, "all feerates must be strictly greater than 0")
}

/** Fee rates in satoshi-per-kilo-weight (1 kw = 1000 weight units). */
case class FeeratesPerKw(mempoolMinFee: FeeratePerKw, block_1: FeeratePerKw, blocks_2: FeeratePerKw, blocks_6: FeeratePerKw, blocks_12: FeeratePerKw, blocks_36: FeeratePerKw, blocks_72: FeeratePerKw, blocks_144: FeeratePerKw, blocks_1008: FeeratePerKw) {
require(mempoolMinFee.feerate > 0.sat && block_1.feerate > 0.sat && blocks_2.feerate > 0.sat && blocks_6.feerate > 0.sat && blocks_12.feerate > 0.sat && blocks_36.feerate > 0.sat && blocks_72.feerate > 0.sat && blocks_144.feerate > 0.sat && blocks_1008.feerate > 0.sat, "all feerates must be strictly greater than 0")
case class FeeratesPerKw(minimum: FeeratePerKw,
fastest: FeeratePerKw,
fast: FeeratePerKw,
medium: FeeratePerKw,
slow: FeeratePerKw) {
require(minimum.feerate > 0.sat && fastest.feerate > 0.sat && fast.feerate > 0.sat && medium.feerate > 0.sat && slow.feerate > 0.sat, "all feerates must be strictly greater than 0")
}

object FeeratesPerKw {
def apply(feerates: FeeratesPerKB): FeeratesPerKw = FeeratesPerKw(
mempoolMinFee = FeeratePerKw(feerates.mempoolMinFee),
block_1 = FeeratePerKw(feerates.block_1),
blocks_2 = FeeratePerKw(feerates.blocks_2),
blocks_6 = FeeratePerKw(feerates.blocks_6),
blocks_12 = FeeratePerKw(feerates.blocks_12),
blocks_36 = FeeratePerKw(feerates.blocks_36),
blocks_72 = FeeratePerKw(feerates.blocks_72),
blocks_144 = FeeratePerKw(feerates.blocks_144),
blocks_1008 = FeeratePerKw(feerates.blocks_1008))
minimum = FeeratePerKw(feerates.minimum),
fastest = FeeratePerKw(feerates.fastest),
fast = FeeratePerKw(feerates.fast),
medium = FeeratePerKw(feerates.medium),
slow = FeeratePerKw(feerates.slow))

/** Used in tests */
def single(feeratePerKw: FeeratePerKw): FeeratesPerKw = FeeratesPerKw(
mempoolMinFee = feeratePerKw,
block_1 = feeratePerKw,
blocks_2 = feeratePerKw,
blocks_6 = feeratePerKw,
blocks_12 = feeratePerKw,
blocks_36 = feeratePerKw,
blocks_72 = feeratePerKw,
blocks_144 = feeratePerKw,
blocks_1008 = feeratePerKw)
minimum = feeratePerKw,
fastest = feeratePerKw,
fast = feeratePerKw,
medium = feeratePerKw,
slow = feeratePerKw)
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ import fr.acinq.eclair.transactions.Transactions
// @formatter:off
sealed trait ConfirmationPriority {
def getFeerate(feerates: FeeratesPerKw): FeeratePerKw = this match {
case ConfirmationPriority.Slow => feerates.blocks_1008
case ConfirmationPriority.Medium => feerates.blocks_12
case ConfirmationPriority.Fast => feerates.blocks_2
case ConfirmationPriority.Slow => feerates.slow
case ConfirmationPriority.Medium => feerates.medium
case ConfirmationPriority.Fast => feerates.fast
}
override def toString: String = super.toString.toLowerCase
}
Expand Down Expand Up @@ -89,8 +89,8 @@ case class OnChainFeeConf(feeTargets: FeeTargets, safeUtxosThreshold: Int, spend
* @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(feerates: FeeratesPerKw, remoteNodeId: PublicKey, channelType: SupportedChannelType, channelCapacity: Satoshi): FeeratePerKw = {
val networkFeerate = feerates.blocks_2
val networkMinFee = feerates.mempoolMinFee
val networkFeerate = feerates.fast
val networkMinFee = feerates.minimum

channelType.commitmentFormat match {
case Transactions.DefaultCommitmentFormat => networkFeerate
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,10 @@ object SmoothFeeProvider {

def smooth(rates: Seq[FeeratesPerKB]): FeeratesPerKB =
FeeratesPerKB(
mempoolMinFee = avg(rates.map(_.mempoolMinFee)),
block_1 = avg(rates.map(_.block_1)),
blocks_2 = avg(rates.map(_.blocks_2)),
blocks_6 = avg(rates.map(_.blocks_6)),
blocks_12 = avg(rates.map(_.blocks_12)),
blocks_36 = avg(rates.map(_.blocks_36)),
blocks_72 = avg(rates.map(_.blocks_72)),
blocks_144 = avg(rates.map(_.blocks_144)),
blocks_1008 = avg(rates.map(_.blocks_1008)))
minimum = avg(rates.map(_.minimum)),
fastest = avg(rates.map(_.fastest)),
fast = avg(rates.map(_.fast)),
medium = avg(rates.map(_.medium)),
slow = avg(rates.map(_.slow)))

}
Original file line number Diff line number Diff line change
Expand Up @@ -932,7 +932,7 @@ object Helpers {
val localPaymentPubkey = Generators.derivePubKey(keyManager.paymentPoint(channelKeyPath).publicKey, remoteCommit.remotePerCommitmentPoint)
val outputs = makeCommitTxOutputs(!commitment.localParams.isInitiator, commitment.remoteParams.dustLimit, remoteRevocationPubkey, commitment.localParams.toSelfDelay, remoteDelayedPaymentPubkey, localPaymentPubkey, remoteHtlcPubkey, localHtlcPubkey, commitment.remoteFundingPubKey, localFundingPubkey, remoteCommit.spec, commitment.params.commitmentFormat)
// we need to use a rather high fee for htlc-claim because we compete with the counterparty
val feeratePerKwHtlc = feerates.blocks_2
val feeratePerKwHtlc = feerates.fast

// those are the preimages to existing received htlcs
val hash2Preimage: Map[ByteVector32, ByteVector32] = commitment.changes.localChanges.all.collect { case u: UpdateFulfillHtlc => u.paymentPreimage }.map(r => Crypto.sha256(r) -> r).toMap
Expand Down Expand Up @@ -1028,7 +1028,7 @@ object Helpers {

val feerateMain = onChainFeeConf.getClosingFeerate(feerates)
// we need to use a high fee here for punishment txs because after a delay they can be spent by the counterparty
val feeratePenalty = feerates.blocks_2
val feeratePenalty = feerates.fast

// first we will claim our main output right away
val mainTx = channelFeatures match {
Expand Down Expand Up @@ -1125,7 +1125,7 @@ object Helpers {
val remoteRevocationPubkey = Generators.revocationPubKey(keyManager.revocationPoint(channelKeyPath).publicKey, remotePerCommitmentPoint)

// we need to use a high fee here for punishment txs because after a delay they can be spent by the counterparty
val feeratePerKwPenalty = feerates.block_1
val feeratePerKwPenalty = feerates.fastest

val penaltyTxs = Transactions.makeClaimHtlcDelayedOutputPenaltyTxs(htlcTx, localParams.dustLimit, remoteRevocationPubkey, localParams.toSelfDelay, remoteDelayedPaymentPubkey, finalScriptPubKey, feeratePerKwPenalty).flatMap(claimHtlcDelayedOutputPenaltyTx => {
withTxGenerationLog("htlc-delayed-penalty") {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -829,7 +829,7 @@ class Channel(val nodeParams: NodeParams, val wallet: OnChainChannelFunder with
if (!d.commitments.isIdle) {
log.info("rejecting splice request: channel not idle")
stay() using d.copy(spliceStatus = SpliceStatus.SpliceAborted) sending TxAbort(d.channelId, InvalidSpliceRequest(d.channelId).getMessage)
} else if (msg.feerate < nodeParams.currentFeerates.mempoolMinFee) {
} else if (msg.feerate < nodeParams.currentFeerates.minimum) {
log.info("rejecting splice request: feerate too low")
stay() using d.copy(spliceStatus = SpliceStatus.SpliceAborted) sending TxAbort(d.channelId, InvalidSpliceRequest(d.channelId).getMessage)
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,21 +83,18 @@ object ReplaceableTxPublisher {
if (hasEnoughSafeUtxos) {
remainingBlocks match {
// If our target is still very far in the future, no need to rush
case t if t >= 144 => feerates.blocks_144
case t if t >= 72 => feerates.blocks_72
case t if t >= 36 => feerates.blocks_36
case t if t >= 36 => feerates.slow
// However, if we get closer to the target, we start being more aggressive
case t if t >= 18 => feerates.blocks_12
case t if t >= 12 => feerates.blocks_6
case t if t >= 2 => feerates.blocks_2
case _ => feerates.block_1
case t if t >= 12 => feerates.medium
case t if t >= 2 => feerates.fast
case _ => feerates.fastest
}
} else {
// We don't have many safe utxos so we want the transaction to confirm quickly.
if (remainingBlocks <= 1) {
feerates.block_1
feerates.fastest
} else {
feerates.blocks_2
feerates.fast
}
}
case ConfirmationTarget.Priority(priority) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,26 +74,22 @@ class BitcoinCoreFeeProviderSpec extends TestKitBaseClass with BitcoindService w
)

val ref = FeeratesPerKB(
mempoolMinFee = FeeratePerKB(300 sat),
block_1 = fees(1),
blocks_2 = fees(2),
blocks_6 = fees(6),
blocks_12 = fees(12),
blocks_36 = fees(36),
blocks_72 = fees(72),
blocks_144 = fees(144),
blocks_1008 = fees(1008)
minimum = FeeratePerKB(300 sat),
fastest = fees(1),
fast = fees(2),
medium = fees(12),
slow = fees(1008)
)

val mockBitcoinClient = createMockBitcoinClient(fees, ref.mempoolMinFee)
val mockBitcoinClient = createMockBitcoinClient(fees, ref.minimum)

val mockProvider = BitcoinCoreFeeProvider(mockBitcoinClient, FeeratesPerKB(FeeratePerKB(1 sat), FeeratePerKB(1 sat), FeeratePerKB(2 sat), FeeratePerKB(3 sat), FeeratePerKB(4 sat), FeeratePerKB(5 sat), FeeratePerKB(6 sat), FeeratePerKB(7 sat), FeeratePerKB(8 sat)))
val mockProvider = BitcoinCoreFeeProvider(mockBitcoinClient, FeeratesPerKB(FeeratePerKB(1 sat), FeeratePerKB(5 sat), FeeratePerKB(4 sat), FeeratePerKB(3 sat), FeeratePerKB(2 sat)))
mockProvider.getFeerates.pipeTo(sender.ref)
assert(sender.expectMsgType[FeeratesPerKB] == ref)
}

test("get mempool minimum fee") {
val regtestProvider = BitcoinCoreFeeProvider(bitcoinrpcclient, FeeratesPerKB(FeeratePerKB(1 sat), FeeratePerKB(1 sat), FeeratePerKB(2 sat), FeeratePerKB(3 sat), FeeratePerKB(4 sat), FeeratePerKB(5 sat), FeeratePerKB(6 sat), FeeratePerKB(7 sat), FeeratePerKB(8 sat)))
val regtestProvider = BitcoinCoreFeeProvider(bitcoinrpcclient, FeeratesPerKB(FeeratePerKB(1 sat), FeeratePerKB(5 sat), FeeratePerKB(4 sat), FeeratePerKB(3 sat), FeeratePerKB(2 sat)))
val sender = TestProbe()
regtestProvider.mempoolMinFee().pipeTo(sender.ref)
val mempoolMinFee = sender.expectMsgType[FeeratePerKB]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ class FallbackFeeProviderSpec extends AnyFunSuite {

def dummyFeerate = FeeratePerKB(1000.sat + Random.nextInt(10000).sat)

def dummyFeerates = FeeratesPerKB(dummyFeerate, dummyFeerate, dummyFeerate, dummyFeerate, dummyFeerate, dummyFeerate, dummyFeerate, dummyFeerate, dummyFeerate)
def dummyFeerates = FeeratesPerKB(dummyFeerate, dummyFeerate, dummyFeerate, dummyFeerate, dummyFeerate)

def await[T](f: Future[T]): T = Await.result(f, 3 seconds)

Expand Down Expand Up @@ -71,11 +71,11 @@ class FallbackFeeProviderSpec extends AnyFunSuite {
}

test("ensure minimum feerate") {
val constantFeeProvider = ConstantFeeProvider(FeeratesPerKB(FeeratePerKB(64000 sat), FeeratePerKB(32000 sat), FeeratePerKB(16000 sat), FeeratePerKB(8000 sat), FeeratePerKB(4000 sat), FeeratePerKB(2000 sat), FeeratePerKB(1500 sat), FeeratePerKB(1000 sat), FeeratePerKB(1000 sat)))
val constantFeeProvider = ConstantFeeProvider(FeeratesPerKB(FeeratePerKB(1000 sat), FeeratePerKB(64_000 sat), FeeratePerKB(32_000 sat), FeeratePerKB(16_000 sat), FeeratePerKB(8_000 sat)))
val minFeeratePerByte = FeeratePerByte(2 sat)
val minFeeratePerKB = FeeratePerKB(minFeeratePerByte)
val fallbackFeeProvider = new FallbackFeeProvider(constantFeeProvider :: Nil, minFeeratePerByte)
assert(await(fallbackFeeProvider.getFeerates) == FeeratesPerKB(FeeratePerKB(64000 sat), FeeratePerKB(32000 sat), FeeratePerKB(16000 sat), FeeratePerKB(8000 sat), FeeratePerKB(4000 sat), minFeeratePerKB, minFeeratePerKB, minFeeratePerKB, minFeeratePerKB))
assert(await(fallbackFeeProvider.getFeerates) == FeeratesPerKB(minFeeratePerKB, FeeratePerKB(64_000 sat), FeeratePerKB(32_000 sat), FeeratePerKB(16_000 sat), FeeratePerKB(8_000 sat)))
}

}
Loading

0 comments on commit a652c76

Please sign in to comment.