More aggressive peer scorer idle channels management (#3295)
What changed, and why it matters
This commit tweaks Eclair's automated liquidity-management logic so the node becomes more aggressive about lowering fees and closing channels that sit idle. It also turns off a feature that automatically re-funds old peers by default. The changes are operational/economic policy adjustments, not a fix for a software vulnerability that an attacker can exploit. There is a small operational risk that a node could close channels or lower fees more eagerly than before, but that is a business-logic trade-off rather than a security flaw.
Treat this as a routine operational improvement, not a security patch. Operators upgrading should review the new default `revive-old-peers = false` and `idle-channel-closing-threshold-percent = 0.075` to ensure the more aggressive idle-channel management matches their liquidity strategy. No emergency deployment is warranted.
Security signals we found
Operational policy change, not a vulnerability fix
New configuration parameter idle-channel-closing-threshold-percent with a 0-50% guard
Default behavior change: old-peer revival disabled by default
Tighter thresholds may increase channel closure / fee-change frequency
API response type changed from Boolean to String for configurePeerScorer
Evidence from the diff
The patch changes PeerScorer thresholds and durations: idle-channel closing now uses a configurable percentage (default 7.5%) of weekly capacity instead of a hard-coded 5%; the minimum-fee age threshold for closing drops from 5 days to 1 day; the idle threshold for fee decreases changes from 25% to 20% local balance and from 24 hours to 12 hours since the last update; and old-peer revival is gated by a new revive-old-peers flag defaulting to false. It also exposes these settings in reference.conf, NodeParams, the API form handler, and improves the API response string for configurePeerScorer. No cryptographic, network, or access-control code is modified.
Changed components
eclair-core/src/main/scala/fr/acinq/eclair/profit/PeerScorer.scalaeclair-core/src/main/scala/fr/acinq/eclair/NodeParams.scalaeclair-core/src/main/resources/reference.confeclair-node/src/main/scala/fr/acinq/eclair/api/handlers/Control.scalaeclair-core/src/main/scala/fr/acinq/eclair/Eclair.scalaInspect captured patch +59 / −31
diff --git a/docs/release-notes/eclair-vnext.md b/docs/release-notes/eclair-vnext.md
index 9ec1a25..74a3dd4 100644
--- a/docs/release-notes/eclair-vnext.md
+++ b/docs/release-notes/eclair-vnext.md
@@ -113,12 +113,16 @@ eclair.peer-scoring {
local-balance-closing-threshold-satoshis = 10000000 // 0.1 btc
// We won't close channels where the remote balance exceeds this amount.
remote-balance-closing-threshold-satoshis = 5000000 // 0.05 btc
+ // We won't close channels if this percentage of the channel capacity has been used during the past week.
+ idle-channel-closing-threshold-percent = 0.075 // 7.5%
// We stop funding channels if our on-chain balance is below this amount.
min-on-chain-balance-satoshis = 50000000 // 0.5 btc
// We stop funding channels if the on-chain feerate is above this value.
max-feerate-sat-per-byte = 5
// Rate-limit the number of funding transactions we make per day (on average).
max-funding-tx-per-day = 6
+ // If true, we will occasionally try to fund idle large capacity peers that have most funds on their side.
+ revive-old-peers = false
// Minimum time between funding the same peer, to evaluate whether the previous funding was effective.
funding-cooldown = 72 hours
}
diff --git a/eclair-core/src/main/resources/reference.conf b/eclair-core/src/main/resources/reference.conf
index 0c51fc9..757f1ec 100644
--- a/eclair-core/src/main/resources/reference.conf
+++ b/eclair-core/src/main/resources/reference.conf
@@ -698,12 +698,16 @@ eclair {
local-balance-closing-threshold-satoshis = 10000000 // 0.1 btc
// We won't close channels where the remote balance exceeds this amount.
remote-balance-closing-threshold-satoshis = 5000000 // 0.05 btc
+ // We won't close channels if this percentage of the channel capacity has been used during the past week.
+ idle-channel-closing-threshold-percent = 0.075 // 7.5%
// We stop funding channels if our on-chain balance is below this amount.
min-on-chain-balance-satoshis = 50000000 // 0.5 btc
// We stop funding channels if the on-chain feerate is above this value.
max-feerate-sat-per-byte = 5
// Rate-limit the number of funding transactions we make per day (on average).
max-funding-tx-per-day = 6
+ // If true, we will occasionally try to fund idle large capacity peers that have most funds on their side.
+ revive-old-peers = false
// Minimum time between funding the same peer, to evaluate whether the previous funding was effective.
funding-cooldown = 72 hours
}
diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/Eclair.scala b/eclair-core/src/main/scala/fr/acinq/eclair/Eclair.scala
index 81be9b5..568f7d4 100644
--- a/eclair-core/src/main/scala/fr/acinq/eclair/Eclair.scala
+++ b/eclair-core/src/main/scala/fr/acinq/eclair/Eclair.scala
@@ -216,7 +216,7 @@ trait Eclair {
def spendFromChannelAddress(fundingKeyPath: DeterministicWallet.KeyPath, fundingTxIndex: Long, remoteFundingPubkey: PublicKey, localNonce_opt: Option[IndividualNonce], remoteSig: ChannelSpendSignature, unsignedTx: Transaction): Future[SpendFromChannelResult]
- def configurePeerScorer(cfg: PeerScorer.ConfigOverrides)(implicit timeout: Timeout): Future[Boolean]
+ def configurePeerScorer(cfg: PeerScorer.ConfigOverrides)(implicit timeout: Timeout): Future[String]
}
@@ -855,10 +855,10 @@ class EclairImpl(val appKit: Kit) extends Eclair with Logging with SpendFromChan
}
}
- override def configurePeerScorer(cfg: PeerScorer.ConfigOverrides)(implicit timeout: Timeout): Future[Boolean] = {
+ override def configurePeerScorer(cfg: PeerScorer.ConfigOverrides)(implicit timeout: Timeout): Future[String] = {
appKit.peerScorer_opt match {
- case Some(scorer) => scorer.ask((ref: typed.ActorRef[Boolean]) => PeerScorer.UpdateConfig(ref, cfg))
- case None => Future.successful(false)
+ case Some(scorer) => scorer.ask((ref: typed.ActorRef[Boolean]) => PeerScorer.UpdateConfig(ref, cfg)).map(res => if (res) "ok" else "could not update config: please retry")
+ case None => Future.successful("peer scorer is disabled: you should enable it first")
}
}
diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/NodeParams.scala b/eclair-core/src/main/scala/fr/acinq/eclair/NodeParams.scala
index b28bee5..f411ac9 100644
--- a/eclair-core/src/main/scala/fr/acinq/eclair/NodeParams.scala
+++ b/eclair-core/src/main/scala/fr/acinq/eclair/NodeParams.scala
@@ -733,9 +733,11 @@ object NodeParams extends Logging {
maxPerPeerCapacity = config.getLong("peer-scoring.liquidity.max-per-peer-capacity-satoshis").sat,
localBalanceClosingThreshold = config.getLong("peer-scoring.liquidity.local-balance-closing-threshold-satoshis").sat,
remoteBalanceClosingThreshold = config.getLong("peer-scoring.liquidity.remote-balance-closing-threshold-satoshis").sat,
+ idleChannelClosingThresholdPct = config.getDouble("peer-scoring.liquidity.idle-channel-closing-threshold-percent"),
maxFundingTxPerDay = config.getInt("peer-scoring.liquidity.max-funding-tx-per-day"),
minOnChainBalance = config.getLong("peer-scoring.liquidity.min-on-chain-balance-satoshis").sat,
maxFeerate = FeeratePerByte(config.getLong("peer-scoring.liquidity.max-feerate-sat-per-byte").sat).perKw,
+ reviveOldPeers = config.getBoolean("peer-scoring.liquidity.revive-old-peers"),
fundingCooldown = FiniteDuration(config.getDuration("peer-scoring.liquidity.funding-cooldown").getSeconds, TimeUnit.SECONDS),
),
relayFees = PeerScorer.RelayFeesConfig(
diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/profit/PeerScorer.scala b/eclair-core/src/main/scala/fr/acinq/eclair/profit/PeerScorer.scala
index c0703f8..cafea52 100644
--- a/eclair-core/src/main/scala/fr/acinq/eclair/profit/PeerScorer.scala
+++ b/eclair-core/src/main/scala/fr/acinq/eclair/profit/PeerScorer.scala
@@ -63,17 +63,19 @@ object PeerScorer {
relayFees: RelayFeesConfig)
/**
- * @param autoFund if true, we will automatically fund channels.
- * @param autoClose if true, we will automatically close unused channels to reclaim liquidity.
- * @param minFundingAmount we always fund channels with at least this amount.
- * @param maxFundingAmount we never fund channels with more than this amount.
- * @param maxPerPeerCapacity maximum total capacity (across all channels) per peer.
- * @param maxFundingTxPerDay we rate-limit the number of transactions we make per day (on average).
- * @param localBalanceClosingThreshold we won't close channels if our local balance is below this amount.
- * @param remoteBalanceClosingThreshold we won't close channels where the remote balance exceeds this amount.
- * @param minOnChainBalance we stop funding channels if our on-chain balance is below this amount.
- * @param maxFeerate we stop funding channels if the on-chain feerate is above this value.
- * @param fundingCooldown minimum time between funding the same peer, to evaluate effectiveness.
+ * @param autoFund if true, we will automatically fund channels.
+ * @param autoClose if true, we will automatically close unused channels to reclaim liquidity.
+ * @param minFundingAmount we always fund channels with at least this amount.
+ * @param maxFundingAmount we never fund channels with more than this amount.
+ * @param maxPerPeerCapacity maximum total capacity (across all channels) per peer.
+ * @param maxFundingTxPerDay we rate-limit the number of transactions we make per day (on average).
+ * @param localBalanceClosingThreshold we won't close channels if our local balance is below this amount.
+ * @param remoteBalanceClosingThreshold we won't close channels where the remote balance exceeds this amount.
+ * @param idleChannelClosingThresholdPct we won't close channels if this percentage of the channel capacity has been used during the past week.
+ * @param minOnChainBalance we stop funding channels if our on-chain balance is below this amount.
+ * @param maxFeerate we stop funding channels if the on-chain feerate is above this value.
+ * @param reviveOldPeers if true, we will occasionally try to fund idle large capacity peers that have most funds on their side.
+ * @param fundingCooldown minimum time between funding the same peer, to evaluate effectiveness.
*/
case class LiquidityConfig(autoFund: Boolean,
autoClose: Boolean,
@@ -83,9 +85,13 @@ object PeerScorer {
maxFundingTxPerDay: Int,
localBalanceClosingThreshold: Satoshi,
remoteBalanceClosingThreshold: Satoshi,
+ idleChannelClosingThresholdPct: Double,
minOnChainBalance: Satoshi,
maxFeerate: FeeratePerKw,
- fundingCooldown: FiniteDuration)
+ reviveOldPeers: Boolean,
+ fundingCooldown: FiniteDuration) {
+ require(0.0 <= idleChannelClosingThresholdPct && idleChannelClosingThresholdPct <= 0.5, "idle-channel-closing-threshold-pct cannot be greater than 50% of the channel capacity")
+ }
/**
* @param autoUpdate if true, we will automatically update our relay fees.
@@ -111,8 +117,10 @@ object PeerScorer {
maxFundingTxPerDayOverride_opt: Option[Int],
localBalanceClosingThresholdOverride_opt: Option[Satoshi],
remoteBalanceClosingThresholdOverride_opt: Option[Satoshi],
+ idleChannelClosingThresholdPctOverride_opt: Option[Double],
minOnChainBalanceOverride_opt: Option[Satoshi],
maxFeerateOverride_opt: Option[FeeratePerKw],
+ reviveOldPeersOverride_opt: Option[Boolean],
fundingCooldownOverride_opt: Option[FiniteDuration])
private case class FundingProposal(peer: PeerInfo, fundingAmount: Satoshi) {
@@ -184,8 +192,10 @@ private class PeerScorer(nodeParams: NodeParams, wallet: OnChainBalanceChecker,
maxFundingTxPerDay = cfg.maxFundingTxPerDayOverride_opt.getOrElse(config.liquidity.maxFundingTxPerDay),
localBalanceClosingThreshold = cfg.localBalanceClosingThresholdOverride_opt.getOrElse(config.liquidity.localBalanceClosingThreshold),
remoteBalanceClosingThreshold = cfg.remoteBalanceClosingThresholdOverride_opt.getOrElse(config.liquidity.remoteBalanceClosingThreshold),
+ idleChannelClosingThresholdPct = cfg.idleChannelClosingThresholdPctOverride_opt.getOrElse(config.liquidity.idleChannelClosingThresholdPct),
minOnChainBalance = cfg.minOnChainBalanceOverride_opt.getOrElse(config.liquidity.minOnChainBalance),
maxFeerate = cfg.maxFeerateOverride_opt.getOrElse(config.liquidity.maxFeerate),
+ reviveOldPeers = cfg.reviveOldPeersOverride_opt.getOrElse(config.liquidity.reviveOldPeers),
fundingCooldown = cfg.fundingCooldownOverride_opt.getOrElse(config.liquidity.fundingCooldown),
),
relayFees = config.relayFees.copy(
@@ -323,7 +333,7 @@ private class PeerScorer(nodeParams: NodeParams, wallet: OnChainBalanceChecker,
// We only close channels when we have more than one.
.filter(_.channels.size > 1)
// We only close channels for which most of the liquidity is idle on our side.
- .filter(p => p.canSend >= p.capacity * 0.8 && p.stats.map(_.totalAmountOut).sum <= p.capacity * 0.05)
+ .filter(p => p.canSend >= p.capacity * 0.8 && p.stats.map(_.totalAmountOut).sum <= p.capacity * config.liquidity.idleChannelClosingThresholdPct)
.foreach(p => {
val channels = sortChannelsToClose(p.channels)
// We keep the best channel and close the others, unless their balance doesn't match our thresholds.
@@ -344,9 +354,9 @@ private class PeerScorer(nodeParams: NodeParams, wallet: OnChainBalanceChecker,
// We only close channels when we have more than one.
.filter(_.channels.size > 1)
// We only close channels for which liquidity is idle.
- .filter(p => p.stats.map(_.totalAmountOut).sum <= p.capacity * 0.05 && p.stats.map(_.totalAmountIn).sum <= p.capacity * 0.05)
+ .filter(p => p.stats.map(_.totalAmountOut).sum <= p.capacity * config.liquidity.idleChannelClosingThresholdPct && p.stats.map(_.totalAmountIn).sum <= p.capacity * config.liquidity.idleChannelClosingThresholdPct)
// And relay fees have been minimal for long enough to give a chance for routing to catch up.
- .filter(p => p.latestUpdate_opt.exists(u => u.relayFees.feeProportionalMillionths <= config.relayFees.minRelayFees.feeProportionalMillionths && u.timestamp <= TimestampSecond.now() - 5.days))
+ .filter(p => p.latestUpdate_opt.exists(u => u.relayFees.feeProportionalMillionths <= config.relayFees.minRelayFees.feeProportionalMillionths && u.timestamp <= TimestampSecond.now() - 1.day))
.foreach(p => {
// We keep the best channel and close the others.
val toClose = sortChannelsToClose(p.channels).tail
@@ -491,13 +501,13 @@ private class PeerScorer(nodeParams: NodeParams, wallet: OnChainBalanceChecker,
private def decreaseIdleChannelsRelayFeesIfNeeded(peers: Seq[PeerInfo], history: DecisionHistory): DecisionHistory = {
val feeDecreases = peers
// We're only interested in channels for which liquidity is idle.
- // We ignore peers for which more than 75% of the funds are on their side: they have a higher incentive than us to
+ // We ignore peers for which more than 80% of the funds are on their side: they have a higher incentive than us to
// close those channels if they aren't useful, so we'll wait for them to do so.
- .filter(p => p.stats.map(_.totalAmountOut).sum <= p.capacity * 0.05 && p.stats.map(_.totalAmountIn).sum <= p.capacity * 0.05 && p.canSend >= p.capacity * 0.25)
+ .filter(p => p.canSend >= p.capacity * 0.2 && p.stats.map(_.totalAmountOut).sum <= p.capacity * config.liquidity.idleChannelClosingThresholdPct && p.stats.map(_.totalAmountIn).sum <= p.capacity * config.liquidity.idleChannelClosingThresholdPct)
// And relay fees aren't already minimal.
.filter(p => p.latestUpdate_opt.exists(u => u.relayFees.feeProportionalMillionths > config.relayFees.minRelayFees.feeProportionalMillionths))
// And relay fees haven't been updated recently.
- .filter(p => p.latestUpdate_opt.exists(u => u.timestamp <= TimestampSecond.now() - 1.day))
+ .filter(p => p.latestUpdate_opt.exists(u => u.timestamp <= TimestampSecond.now() - 12.hours))
.flatMap(p => {
p.latestUpdate_opt match {
case Some(u) =>
@@ -547,7 +557,7 @@ private class PeerScorer(nodeParams: NodeParams, wallet: OnChainBalanceChecker,
}
val toReviveNotAlreadySelected = toRevive.filterNot(p => bestPeers.exists(_.remoteNodeId == p.remoteNodeId) || smallPeerToFund_opt.exists(_.remoteNodeId == p.remoteNodeId) || p.peer.capacity >= config.liquidity.maxPerPeerCapacity)
val toRevive_opt = toReviveNotAlreadySelected.headOption match {
- case Some(_) if Random.nextDouble() <= (1.0 / (scoringPerDay * 5)) => Random.shuffle(toReviveNotAlreadySelected.take(3)).headOption
+ case Some(_) if config.liquidity.reviveOldPeers && Random.nextDouble() <= (1.0 / (scoringPerDay * 5)) => Random.shuffle(toReviveNotAlreadySelected.take(3)).headOption
case _ => None
}
(bestPeersToFund ++ toRevive_opt ++ smallPeerToFund_opt).distinctBy(_.remoteNodeId)
diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/TestConstants.scala b/eclair-core/src/test/scala/fr/acinq/eclair/TestConstants.scala
index f3a6b69..051ba6e 100644
--- a/eclair-core/src/test/scala/fr/acinq/eclair/TestConstants.scala
+++ b/eclair-core/src/test/scala/fr/acinq/eclair/TestConstants.scala
@@ -278,8 +278,10 @@ object TestConstants {
minOnChainBalance = 5_000_000 sat, // 0.05 BTC
localBalanceClosingThreshold = 1_000_000 sat, // 0.01 BTC
remoteBalanceClosingThreshold = 1_000_000 sat, // 0.01 BTC
+ idleChannelClosingThresholdPct = 0.1, // 10%
maxFeerate = FeeratePerByte(100 sat).perKw,
maxFundingTxPerDay = 100,
+ reviveOldPeers = false,
fundingCooldown = 72 hours,
),
relayFees = PeerScorer.RelayFeesConfig(
@@ -498,8 +500,10 @@ object TestConstants {
minOnChainBalance = 5_000_000 sat, // 0.05 BTC
localBalanceClosingThreshold = 1_000_000 sat, // 0.01 BTC
remoteBalanceClosingThreshold = 1_000_000 sat, // 0.01 BTC
+ idleChannelClosingThresholdPct = 0.1, // 10%
maxFeerate = FeeratePerByte(100 sat).perKw,
maxFundingTxPerDay = 100,
+ reviveOldPeers = false,
fundingCooldown = 72 hours,
),
relayFees = PeerScorer.RelayFeesConfig(
diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/profit/PeerScorerSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/profit/PeerScorerSpec.scala
index c4d13c9..d6515be 100644
--- a/eclair-core/src/test/scala/fr/acinq/eclair/profit/PeerScorerSpec.scala
+++ b/eclair-core/src/test/scala/fr/acinq/eclair/profit/PeerScorerSpec.scala
@@ -45,8 +45,10 @@ class PeerScorerSpec extends ScalaTestWithActorTestKit(ConfigFactory.load("appli
maxPerPeerCapacity = 1_000_000_000 sat, // 10 BTC
localBalanceClosingThreshold = 10_000_000 sat, // 0.1 BTC
remoteBalanceClosingThreshold = 20_000_000 sat, // 0.2 BTC
+ idleChannelClosingThresholdPct = 0.05, // 5%
maxFeerate = FeeratePerByte(100 sat).perKw,
maxFundingTxPerDay = 100,
+ reviveOldPeers = true,
fundingCooldown = 72 hours,
),
relayFees = PeerScorer.RelayFeesConfig(
@@ -607,15 +609,15 @@ class PeerScorerSpec extends ScalaTestWithActorTestKit(ConfigFactory.load("appli
val c1b = channelInfo(canSend = 0.5 btc, canReceive = 0.5 btc)
val c2a = channelInfo(canSend = 1 btc, canReceive = 1 btc)
val c2b = channelInfo(canSend = 0.5 btc, canReceive = 0.5 btc)
- val c3a = channelInfo(canSend = 0.2 btc, canReceive = 0.8 btc)
- val c3b = channelInfo(canSend = 0.2 btc, canReceive = 0.8 btc)
+ val c3a = channelInfo(canSend = 0.2 btc, canReceive = 0.9 btc)
+ val c3b = channelInfo(canSend = 0.2 btc, canReceive = 0.9 btc)
// Our first peer's channels have very low volume and the last channel update already used our minimum fees: we should close it.
val peerInfo1 = PeerInfo(
remoteNodeId = remoteNodeId1,
stats = Seq.fill(weeklyBuckets)(peerStats(totalAmountOut = 1_000 sat, totalAmountIn = 1_000 sat, relayFeeEarned = 10 sat)),
channels = Seq(c1a, c1b),
- latestUpdate_opt = Some(channelUpdate(c1a.capacity, RelayFees(1 msat, 600), TimestampSecond.now() - 6.days)),
+ latestUpdate_opt = Some(channelUpdate(c1a.capacity, RelayFees(1 msat, 600), TimestampSecond.now() - 2.days)),
hasPendingChannel = false
)
// Our second peer's channels have very low volume, but we're not yet using our minimum fees: we should decrease them.
@@ -623,15 +625,15 @@ class PeerScorerSpec extends ScalaTestWithActorTestKit(ConfigFactory.load("appli
remoteNodeId = remoteNodeId2,
stats = Seq.fill(weeklyBuckets)(peerStats(totalAmountOut = 1_000 sat, totalAmountIn = 1_000 sat, relayFeeEarned = 10 sat)),
channels = Seq(c2a, c2b),
- latestUpdate_opt = Some(channelUpdate(c2a.capacity, RelayFees(1 msat, 750), TimestampSecond.now() - 4.days)),
+ latestUpdate_opt = Some(channelUpdate(c2a.capacity, RelayFees(1 msat, 750), TimestampSecond.now() - 1.day)),
hasPendingChannel = false
)
- // Our last peer's channels have very low volume, but we have less than 25% of the funds: we shouldn't do anything yet.
+ // Our last peer's channels have very low volume, but we have less than 20% of the funds: we shouldn't do anything yet.
val peerInfo3 = PeerInfo(
remoteNodeId = remoteNodeId3,
stats = Seq.fill(weeklyBuckets)(peerStats(totalAmountOut = 1_000 sat, totalAmountIn = 1_000 sat, relayFeeEarned = 10 sat)),
channels = Seq(c3a, c3b),
- latestUpdate_opt = Some(channelUpdate(c3a.capacity, RelayFees(1 msat, 750), TimestampSecond.now() - 4.days)),
+ latestUpdate_opt = Some(channelUpdate(c3a.capacity, RelayFees(1 msat, 750), TimestampSecond.now() - 1.day)),
hasPendingChannel = false
)
diff --git a/eclair-node/src/main/scala/fr/acinq/eclair/api/handlers/Control.scala b/eclair-node/src/main/scala/fr/acinq/eclair/api/handlers/Control.scala
index 78f4baa..0e2cfa9 100644
--- a/eclair-node/src/main/scala/fr/acinq/eclair/api/handlers/Control.scala
+++ b/eclair-node/src/main/scala/fr/acinq/eclair/api/handlers/Control.scala
@@ -85,8 +85,8 @@ trait Control {
}
val configurePeerScorer: Route = postRequest("configurepeerscorer") { implicit t =>
- formFields("autoFund".as[Boolean] ?, "autoClose".as[Boolean] ?, "autoUpdateFees".as[Boolean] ?, "addWhitelistedPeers".as[List[PublicKey]](pubkeyListUnmarshaller).?, "removeWhitelistedPeers".as[List[PublicKey]](pubkeyListUnmarshaller).?, "minFundingAmount".as[Satoshi] ?, "maxFundingAmount".as[Satoshi] ?, "maxPerPeerCapacity".as[Satoshi] ?, "maxFundingTxPerDay".as[Int] ?, "localBalanceClosingThreshold".as[Satoshi] ?, "remoteBalanceClosingThreshold".as[Satoshi] ?, "minOnChainBalance".as[Satoshi] ?, "maxFeerate".as[FeeratePerByte] ?, "fundingCooldown".as[Int] ?) {
- (autoFund_opt, autoClose_opt, autoUpdateFees_opt, addWhitelistedPeers_opt, removeWhitelistedPeers_opt, minFundingAmount_opt, maxFundingAmount_opt, maxPerPeerCapacity_opt, maxFundingTxPerDay_opt, localBalanceClosingThreshold_opt, remoteBalanceClosingThreshold_opt, minOnChainBalance_opt, maxFeerate_opt, fundingCooldown_opt) =>
+ formFields("autoFund".as[Boolean] ?, "autoClose".as[Boolean] ?, "autoUpdateFees".as[Boolean] ?, "addWhitelistedPeers".as[List[PublicKey]](pubkeyListUnmarshaller).?, "removeWhitelistedPeers".as[List[PublicKey]](pubkeyListUnmarshaller).?, "minFundingAmount".as[Satoshi] ?, "maxFundingAmount".as[Satoshi] ?, "maxPerPeerCapacity".as[Satoshi] ?, "maxFundingTxPerDay".as[Int] ?, "localBalanceClosingThreshold".as[Satoshi] ?, "remoteBalanceClosingThreshold".as[Satoshi] ?, "idleChannelClosingThresholdPercent".as[Double]?, "minOnChainBalance".as[Satoshi] ?, "maxFeerate".as[FeeratePerByte] ?, "reviveOldPeers".as[Boolean] ?, "fundingCooldown".as[Int] ?) {
+ (autoFund_opt, autoClose_opt, autoUpdateFees_opt, addWhitelistedPeers_opt, removeWhitelistedPeers_opt, minFundingAmount_opt, maxFundingAmount_opt, maxPerPeerCapacity_opt, maxFundingTxPerDay_opt, localBalanceClosingThreshold_opt, remoteBalanceClosingThreshold_opt, idleChannelClosingThresholdPct_opt, minOnChainBalance_opt, maxFeerate_opt, reviveOldPeers_opt, fundingCooldown_opt) =>
val cfg = PeerScorer.ConfigOverrides(
autoFundOverride_opt = autoFund_opt,
autoCloseOverride_opt = autoClose_opt,
@@ -99,8 +99,10 @@ trait Control {
maxFundingTxPerDayOverride_opt = maxFundingTxPerDay_opt,
localBalanceClosingThresholdOverride_opt = localBalanceClosingThreshold_opt,
remoteBalanceClosingThresholdOverride_opt = remoteBalanceClosingThreshold_opt,
+ idleChannelClosingThresholdPctOverride_opt = idleChannelClosingThresholdPct_opt,
minOnChainBalanceOverride_opt = minOnChainBalance_opt,
maxFeerateOverride_opt = maxFeerate_opt.map(_.perKw),
+ reviveOldPeersOverride_opt = reviveOldPeers_opt,
fundingCooldownOverride_opt = fundingCooldown_opt.map(hours => FiniteDuration(hours, TimeUnit.HOURS)),
)
complete(eclairApi.configurePeerScorer(cfg))
Why this scored 24/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.