From bafa4557dcbfd8675589e3b55e4454ffd9d606e2 Mon Sep 17 00:00:00 2001 From: Fabrice Drouin Date: Tue, 3 Apr 2018 19:43:37 +0200 Subject: [PATCH] Enforce a minimum fee rate (#530) * Enforce a minimum fee rate, with a default at 1 satoshi/byte Same value as bitcoin core's minimum relay fee * add `minFeeratePerByte` to `FallbackFeeProvider` and require that `FeeratesPerByte` and `FeeratesPerKw` be always > 0 --- eclair-core/src/main/resources/reference.conf | 1 + .../main/scala/fr/acinq/eclair/Setup.scala | 143 ++++++++++-------- .../blockchain/fee/BitgoFeeProvider.scala | 3 +- .../fee/EarnDotComFeeProvider.scala | 4 +- .../blockchain/fee/FallbackFeeProvider.scala | 21 ++- .../eclair/blockchain/fee/FeeProvider.scala | 8 +- .../fr/acinq/eclair/channel/Helpers.scala | 9 +- .../main/scala/fr/acinq/eclair/package.scala | 2 + .../fr/acinq/eclair/TestkitBaseClass.scala | 2 +- .../fee/FallbackFeeProviderSpec.scala | 8 +- .../channel/states/e/NormalStateSpec.scala | 10 -- .../channel/states/f/ShutdownStateSpec.scala | 10 -- .../interop/rustytests/RustyTestsSpec.scala | 2 +- 13 files changed, 120 insertions(+), 103 deletions(-) diff --git a/eclair-core/src/main/resources/reference.conf b/eclair-core/src/main/resources/reference.conf index e80f8ff96..5a2d48488 100644 --- a/eclair-core/src/main/resources/reference.conf +++ b/eclair-core/src/main/resources/reference.conf @@ -35,6 +35,7 @@ eclair { 72 = 20 } } + min-feerate = 1 // minimum feerate in satoshis per byte (same default value as bitcoin core's minimum relay fee) node-alias = "eclair" node-color = "49daaa" diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/Setup.scala b/eclair-core/src/main/scala/fr/acinq/eclair/Setup.scala index 4608c9fc0..4c0ac76df 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/Setup.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/Setup.scala @@ -126,74 +126,83 @@ class Setup(datadir: File, overrideDefaults: Config = ConfigFactory.empty(), act } def bootstrap: Future[Kit] = { - val zmqConnected = Promise[Boolean]() - val tcpBound = Promise[Unit]() - - val defaultFeerates = FeeratesPerByte(block_1 = config.getLong("default-feerates.delay-blocks.1"), blocks_2 = config.getLong("default-feerates.delay-blocks.2"), blocks_6 = config.getLong("default-feerates.delay-blocks.6"), blocks_12 = config.getLong("default-feerates.delay-blocks.12"), blocks_36 = config.getLong("default-feerates.delay-blocks.36"), blocks_72 = config.getLong("default-feerates.delay-blocks.72")) - Globals.feeratesPerByte.set(defaultFeerates) - Globals.feeratesPerKw.set(FeeratesPerKw(defaultFeerates)) - logger.info(s"initial feeratesPerByte=${Globals.feeratesPerByte.get()}") - val feeProvider = (nodeParams.chainHash, bitcoin) match { - case (Block.RegtestGenesisBlock.hash, _) => new ConstantFeeProvider(defaultFeerates) - case (_, Bitcoind(bitcoinClient)) => new FallbackFeeProvider(new BitgoFeeProvider(nodeParams.chainHash) :: new EarnDotComFeeProvider() :: new BitcoinCoreFeeProvider(bitcoinClient, defaultFeerates) :: new ConstantFeeProvider(defaultFeerates) :: Nil) // order matters! - case _ => new FallbackFeeProvider(new BitgoFeeProvider(nodeParams.chainHash) :: new EarnDotComFeeProvider() :: new ConstantFeeProvider(defaultFeerates) :: Nil) // order matters! - } - system.scheduler.schedule(0 seconds, 10 minutes)(feeProvider.getFeerates.map { - case feerates: FeeratesPerByte => - Globals.feeratesPerByte.set(feerates) - Globals.feeratesPerKw.set(FeeratesPerKw(feerates)) - system.eventStream.publish(CurrentFeerates(Globals.feeratesPerKw.get)) - logger.info(s"current feeratesPerByte=${Globals.feeratesPerByte.get()}") - }) - - val watcher = bitcoin match { - case Bitcoind(bitcoinClient) => - system.actorOf(SimpleSupervisor.props(Props(new ZMQActor(config.getString("bitcoind.zmq"), Some(zmqConnected))), "zmq", SupervisorStrategy.Restart)) - system.actorOf(SimpleSupervisor.props(ZmqWatcher.props(new ExtendedBitcoinClient(new BatchingBitcoinJsonRPCClient(bitcoinClient))), "watcher", SupervisorStrategy.Resume)) - case Electrum(electrumClient) => - zmqConnected.success(true) - system.actorOf(SimpleSupervisor.props(Props(new ElectrumWatcher(electrumClient)), "watcher", SupervisorStrategy.Resume)) - } - - val wallet = bitcoin match { - case Bitcoind(bitcoinClient) => new BitcoinCoreWallet(bitcoinClient) - case Electrum(electrumClient) => - val electrumWallet = system.actorOf(ElectrumWallet.props(seed, electrumClient, ElectrumWallet.WalletParameters(nodeParams.chainHash)), "electrum-wallet") - new ElectrumEclairWallet(electrumWallet, nodeParams.chainHash) - } - wallet.getFinalAddress.map { - case address => logger.info(s"initial wallet address=$address") - } - - val paymentHandler = system.actorOf(SimpleSupervisor.props(config.getString("payment-handler") match { - case "local" => LocalPaymentHandler.props(nodeParams) - case "noop" => Props[NoopPaymentHandler] - }, "payment-handler", SupervisorStrategy.Resume)) - val register = system.actorOf(SimpleSupervisor.props(Props(new Register), "register", SupervisorStrategy.Resume)) - val relayer = system.actorOf(SimpleSupervisor.props(Relayer.props(nodeParams, register, paymentHandler), "relayer", SupervisorStrategy.Resume)) - val router = system.actorOf(SimpleSupervisor.props(Router.props(nodeParams, watcher), "router", SupervisorStrategy.Resume)) - val authenticator = system.actorOf(SimpleSupervisor.props(Authenticator.props(nodeParams), "authenticator", SupervisorStrategy.Resume)) - val switchboard = system.actorOf(SimpleSupervisor.props(Switchboard.props(nodeParams, authenticator, watcher, router, relayer, wallet), "switchboard", SupervisorStrategy.Resume)) - val server = system.actorOf(SimpleSupervisor.props(Server.props(nodeParams, authenticator, new InetSocketAddress(config.getString("server.binding-ip"), config.getInt("server.port")), Some(tcpBound)), "server", SupervisorStrategy.Restart)) - val paymentInitiator = system.actorOf(SimpleSupervisor.props(PaymentInitiator.props(nodeParams.nodeId, router, register), "payment-initiator", SupervisorStrategy.Restart)) - - val kit = Kit( - nodeParams = nodeParams, - system = system, - watcher = watcher, - paymentHandler = paymentHandler, - register = register, - relayer = relayer, - router = router, - switchboard = switchboard, - paymentInitiator = paymentInitiator, - server = server, - wallet = wallet) - - val zmqTimeout = after(5 seconds, using = system.scheduler)(Future.failed(BitcoinZMQConnectionTimeoutException)) - val tcpTimeout = after(5 seconds, using = system.scheduler)(Future.failed(TCPBindException(config.getInt("server.port")))) - for { + _ <- Future.successful(true) + feeratesRetrieved = Promise[Boolean]() + zmqConnected = Promise[Boolean]() + tcpBound = Promise[Unit]() + + defaultFeerates = FeeratesPerByte( + block_1 = config.getLong("default-feerates.delay-blocks.1"), + blocks_2 = config.getLong("default-feerates.delay-blocks.2"), + blocks_6 = config.getLong("default-feerates.delay-blocks.6"), + blocks_12 = config.getLong("default-feerates.delay-blocks.12"), + blocks_36 = config.getLong("default-feerates.delay-blocks.36"), + blocks_72 = config.getLong("default-feerates.delay-blocks.72") + ) + minFeeratePerByte = config.getLong("min-feerate") + feeProvider = (nodeParams.chainHash, bitcoin) match { + case (Block.RegtestGenesisBlock.hash, _) => new ConstantFeeProvider(defaultFeerates) + case (_, Bitcoind(bitcoinClient)) => new FallbackFeeProvider(new BitgoFeeProvider(nodeParams.chainHash) :: new EarnDotComFeeProvider() :: new BitcoinCoreFeeProvider(bitcoinClient, defaultFeerates) :: new ConstantFeeProvider(defaultFeerates) :: Nil, minFeeratePerByte) // order matters! + case _ => new FallbackFeeProvider(new BitgoFeeProvider(nodeParams.chainHash) :: new EarnDotComFeeProvider() :: new ConstantFeeProvider(defaultFeerates) :: Nil, minFeeratePerByte) // order matters! + } + _ = system.scheduler.schedule(0 seconds, 10 minutes)(feeProvider.getFeerates.map { + case feerates: FeeratesPerByte => + Globals.feeratesPerByte.set(feerates) + Globals.feeratesPerKw.set(FeeratesPerKw(feerates)) + system.eventStream.publish(CurrentFeerates(Globals.feeratesPerKw.get)) + logger.info(s"current feeratesPerByte=${Globals.feeratesPerByte.get()}") + feeratesRetrieved.trySuccess(true) + }) + _ <- feeratesRetrieved.future + + watcher = bitcoin match { + case Bitcoind(bitcoinClient) => + system.actorOf(SimpleSupervisor.props(Props(new ZMQActor(config.getString("bitcoind.zmq"), Some(zmqConnected))), "zmq", SupervisorStrategy.Restart)) + system.actorOf(SimpleSupervisor.props(ZmqWatcher.props(new ExtendedBitcoinClient(new BatchingBitcoinJsonRPCClient(bitcoinClient))), "watcher", SupervisorStrategy.Resume)) + case Electrum(electrumClient) => + zmqConnected.success(true) + system.actorOf(SimpleSupervisor.props(Props(new ElectrumWatcher(electrumClient)), "watcher", SupervisorStrategy.Resume)) + } + + wallet = bitcoin match { + case Bitcoind(bitcoinClient) => new BitcoinCoreWallet(bitcoinClient) + case Electrum(electrumClient) => + val electrumWallet = system.actorOf(ElectrumWallet.props(seed, electrumClient, ElectrumWallet.WalletParameters(nodeParams.chainHash)), "electrum-wallet") + new ElectrumEclairWallet(electrumWallet, nodeParams.chainHash) + } + _ = wallet.getFinalAddress.map { + case address => logger.info(s"initial wallet address=$address") + } + + paymentHandler = system.actorOf(SimpleSupervisor.props(config.getString("payment-handler") match { + case "local" => LocalPaymentHandler.props(nodeParams) + case "noop" => Props[NoopPaymentHandler] + }, "payment-handler", SupervisorStrategy.Resume)) + register = system.actorOf(SimpleSupervisor.props(Props(new Register), "register", SupervisorStrategy.Resume)) + relayer = system.actorOf(SimpleSupervisor.props(Relayer.props(nodeParams, register, paymentHandler), "relayer", SupervisorStrategy.Resume)) + router = system.actorOf(SimpleSupervisor.props(Router.props(nodeParams, watcher), "router", SupervisorStrategy.Resume)) + authenticator = system.actorOf(SimpleSupervisor.props(Authenticator.props(nodeParams), "authenticator", SupervisorStrategy.Resume)) + switchboard = system.actorOf(SimpleSupervisor.props(Switchboard.props(nodeParams, authenticator, watcher, router, relayer, wallet), "switchboard", SupervisorStrategy.Resume)) + server = system.actorOf(SimpleSupervisor.props(Server.props(nodeParams, authenticator, new InetSocketAddress(config.getString("server.binding-ip"), config.getInt("server.port")), Some(tcpBound)), "server", SupervisorStrategy.Restart)) + paymentInitiator = system.actorOf(SimpleSupervisor.props(PaymentInitiator.props(nodeParams.nodeId, router, register), "payment-initiator", SupervisorStrategy.Restart)) + + kit = Kit( + nodeParams = nodeParams, + system = system, + watcher = watcher, + paymentHandler = paymentHandler, + register = register, + relayer = relayer, + router = router, + switchboard = switchboard, + paymentInitiator = paymentInitiator, + server = server, + wallet = wallet) + + zmqTimeout = after(5 seconds, using = system.scheduler)(Future.failed(BitcoinZMQConnectionTimeoutException)) + tcpTimeout = after(5 seconds, using = system.scheduler)(Future.failed(TCPBindException(config.getInt("server.port")))) + _ <- Future.firstCompletedOf(zmqConnected.future :: zmqTimeout :: Nil) _ <- Future.firstCompletedOf(tcpBound.future :: tcpTimeout :: Nil) _ <- if (config.getBoolean("api.enabled")) { diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/blockchain/fee/BitgoFeeProvider.scala b/eclair-core/src/main/scala/fr/acinq/eclair/blockchain/fee/BitgoFeeProvider.scala index c9dc4633f..e34f46bbd 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/blockchain/fee/BitgoFeeProvider.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/blockchain/fee/BitgoFeeProvider.scala @@ -23,6 +23,7 @@ import akka.http.scaladsl.unmarshalling.Unmarshal import akka.stream.ActorMaterializer import de.heikoseeberger.akkahttpjson4s.Json4sSupport._ import fr.acinq.bitcoin.{BinaryData, Block} +import fr.acinq.eclair.feerateKbToByte import org.json4s.JsonAST.{JInt, JValue} import org.json4s.{DefaultFormats, jackson} @@ -58,7 +59,7 @@ object BitgoFeeProvider { val blockTargets = json \ "feeByBlockTarget" blockTargets.foldField(Seq.empty[BlockTarget]) { // we divide by 1024 because bitgo returns estimates in Satoshi/Kb and we use estimates in Satoshi/Byte - case (list, (strBlockTarget, JInt(feePerKb))) => list :+ BlockTarget(strBlockTarget.toInt, feePerKb.longValue() / 1024) + case (list, (strBlockTarget, JInt(feePerKb))) => list :+ BlockTarget(strBlockTarget.toInt, feerateKbToByte(feePerKb.longValue())) } } diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/blockchain/fee/EarnDotComFeeProvider.scala b/eclair-core/src/main/scala/fr/acinq/eclair/blockchain/fee/EarnDotComFeeProvider.scala index 06fcd4664..036a9aaa7 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/blockchain/fee/EarnDotComFeeProvider.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/blockchain/fee/EarnDotComFeeProvider.scala @@ -66,8 +66,8 @@ object EarnDotComFeeProvider { def extractFeerate(feeRanges: Seq[FeeRange], maxBlockDelay: Int): Long = { // first we keep only fee ranges with a max block delay below the limit val belowLimit = feeRanges.filter(_.maxDelay <= maxBlockDelay) - // out of all the remaining fee ranges, we select the one with the minimum higher bound - belowLimit.minBy(_.maxFee).maxFee + // out of all the remaining fee ranges, we select the one with the minimum higher bound and make sure it is > 0 + Math.max(belowLimit.minBy(_.maxFee).maxFee, 1) } def extractFeerates(feeRanges: Seq[FeeRange]): FeeratesPerByte = diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/blockchain/fee/FallbackFeeProvider.scala b/eclair-core/src/main/scala/fr/acinq/eclair/blockchain/fee/FallbackFeeProvider.scala index d79ebe679..2321cd905 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/blockchain/fee/FallbackFeeProvider.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/blockchain/fee/FallbackFeeProvider.scala @@ -20,10 +20,14 @@ import scala.concurrent.{ExecutionContext, Future} /** * This provider will try all child providers in sequence, until one of them works + * + * @param providers a sequence of providers; they will be tried one after the others until one of them succeeds + * @param minFeeratePerByte a configurable minimum value for feerates */ -class FallbackFeeProvider(providers: Seq[FeeProvider])(implicit ec: ExecutionContext) extends FeeProvider { +class FallbackFeeProvider(providers: Seq[FeeProvider], minFeeratePerByte: Long)(implicit ec: ExecutionContext) extends FeeProvider { require(providers.size >= 1, "need at least one fee provider") + require(minFeeratePerByte > 0, "minimum fee rate must be strictly greater than 0") def getFeerates(fallbacks: Seq[FeeProvider]): Future[FeeratesPerByte] = fallbacks match { @@ -31,6 +35,19 @@ class FallbackFeeProvider(providers: Seq[FeeProvider])(implicit ec: ExecutionCon case head +: remaining => head.getFeerates.recoverWith { case _ => getFeerates(remaining) } } - override def getFeerates: Future[FeeratesPerByte] = getFeerates(providers) + override def getFeerates: Future[FeeratesPerByte] = getFeerates(providers).map(FallbackFeeProvider.enforceMinimumFeerate(_, minFeeratePerByte)) + +} + +object FallbackFeeProvider { + + def enforceMinimumFeerate(feeratesPerByte: FeeratesPerByte, minFeeratePerByte: Long) : FeeratesPerByte = feeratesPerByte.copy( + block_1 = Math.max(feeratesPerByte.block_1, minFeeratePerByte), + blocks_2 = Math.max(feeratesPerByte.blocks_2, minFeeratePerByte), + blocks_6 = Math.max(feeratesPerByte.blocks_6, minFeeratePerByte), + blocks_12 = Math.max(feeratesPerByte.blocks_12, minFeeratePerByte), + blocks_36 = Math.max(feeratesPerByte.blocks_36, minFeeratePerByte), + blocks_72 = Math.max(feeratesPerByte.blocks_72, minFeeratePerByte) + ) } diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/blockchain/fee/FeeProvider.scala b/eclair-core/src/main/scala/fr/acinq/eclair/blockchain/fee/FeeProvider.scala index 212c881b8..dbd3880cd 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/blockchain/fee/FeeProvider.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/blockchain/fee/FeeProvider.scala @@ -29,9 +29,13 @@ trait FeeProvider { } -case class FeeratesPerByte(block_1: Long, blocks_2: Long, blocks_6: Long, blocks_12: Long, blocks_36: Long, blocks_72: Long) +case class FeeratesPerByte(block_1: Long, blocks_2: Long, blocks_6: Long, blocks_12: Long, blocks_36: Long, blocks_72: Long) { + require(block_1 > 0 && blocks_2 > 0 && blocks_6 > 0 && blocks_12 > 0 && blocks_36 > 0 && blocks_72 > 0, "all feerates must be strictly greater than 0") +} -case class FeeratesPerKw(block_1: Long, blocks_2: Long, blocks_6: Long, blocks_12: Long, blocks_36: Long, blocks_72: Long) +case class FeeratesPerKw(block_1: Long, blocks_2: Long, blocks_6: Long, blocks_12: Long, blocks_36: Long, blocks_72: Long) { + require(block_1 > 0 && blocks_2 > 0 && blocks_6 > 0 && blocks_12 > 0 && blocks_36 > 0 && blocks_72 > 0, "all feerates must be strictly greater than 0") +} object FeeratesPerKw { def apply(feerates: FeeratesPerByte): FeeratesPerKw = FeeratesPerKw( diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/channel/Helpers.scala b/eclair-core/src/main/scala/fr/acinq/eclair/channel/Helpers.scala index 0a50edb20..66da99157 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/channel/Helpers.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/channel/Helpers.scala @@ -96,8 +96,7 @@ object Helpers { Math.abs((2.0 * (remoteFeeratePerKw - localFeeratePerKw)) / (localFeeratePerKw + remoteFeeratePerKw)) def shouldUpdateFee(commitmentFeeratePerKw: Long, networkFeeratePerKw: Long, updateFeeMinDiffRatio: Double): Boolean = - // negative feerate can happen in regtest mode - networkFeeratePerKw > 0 && feeRateMismatch(networkFeeratePerKw, commitmentFeeratePerKw) > updateFeeMinDiffRatio + feeRateMismatch(networkFeeratePerKw, commitmentFeeratePerKw) > updateFeeMinDiffRatio /** * @@ -107,10 +106,8 @@ object Helpers { * @return true if the difference between local and remote fee rates is too high. * the actual check is |remote - local| / avg(local, remote) > mismatch ratio */ - def isFeeDiffTooHigh(remoteFeeratePerKw: Long, localFeeratePerKw: Long, maxFeerateMismatchRatio: Double): Boolean = { - // negative feerate can happen in regtest mode - remoteFeeratePerKw > 0 && feeRateMismatch(remoteFeeratePerKw, localFeeratePerKw) > maxFeerateMismatchRatio - } + def isFeeDiffTooHigh(remoteFeeratePerKw: Long, localFeeratePerKw: Long, maxFeerateMismatchRatio: Double): Boolean = + feeRateMismatch(remoteFeeratePerKw, localFeeratePerKw) > maxFeerateMismatchRatio def makeAnnouncementSignatures(nodeParams: NodeParams, commitments: Commitments, shortChannelId: ShortChannelId) = { val features = BinaryData.empty // empty features for now diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/package.scala b/eclair-core/src/main/scala/fr/acinq/eclair/package.scala index fd976a210..09048d515 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/package.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/package.scala @@ -51,6 +51,8 @@ package object eclair { case Attempt.Failure(cause) => throw new RuntimeException(s"serialization error: $cause") } + def feerateKbToByte(feeratePerKb: Long): Long = Math.max(feeratePerKb / 1024, 1) + /** * Converts feerate in satoshi-per-bytes to feerate in satoshi-per-kw * diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/TestkitBaseClass.scala b/eclair-core/src/test/scala/fr/acinq/eclair/TestkitBaseClass.scala index 979c3efb6..1db586a19 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/TestkitBaseClass.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/TestkitBaseClass.scala @@ -44,7 +44,7 @@ abstract class TestkitBaseClass extends TestKit(ActorSystem("test")) with fixtur override def afterAll { TestKit.shutdownActorSystem(system) - Globals.feeratesPerKw.set(FeeratesPerKw.single(0)) + Globals.feeratesPerKw.set(FeeratesPerKw.single(1)) } } diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/blockchain/fee/FallbackFeeProviderSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/blockchain/fee/FallbackFeeProviderSpec.scala index 185ad4851..128c652f9 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/blockchain/fee/FallbackFeeProviderSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/blockchain/fee/FallbackFeeProviderSpec.scala @@ -56,7 +56,7 @@ class FallbackFeeProviderSpec extends FunSuite { val provider5 = new FailingFeeProvider(5, dummyFeerates) // fails after 5 tries val provider7 = new FailingFeeProvider(Int.MaxValue, dummyFeerates) // "never" fails - val fallbackFeeProvider = new FallbackFeeProvider(provider0 :: provider1 :: provider3 :: provider5 :: provider7 :: Nil) + val fallbackFeeProvider = new FallbackFeeProvider(provider0 :: provider1 :: provider3 :: provider5 :: provider7 :: Nil, 1) assert(await(fallbackFeeProvider.getFeerates) === provider1.feeratesPerByte) @@ -74,5 +74,11 @@ class FallbackFeeProviderSpec extends FunSuite { } + test("ensure minimum feerate") { + val constantFeeProvider = new ConstantFeeProvider(FeeratesPerByte(1, 1, 1, 1, 1, 1)) + val fallbackFeeProvider = new FallbackFeeProvider(constantFeeProvider :: Nil, 2) + assert(await(fallbackFeeProvider.getFeerates) === FeeratesPerByte(2, 2, 2, 2, 2, 2)) + } + } diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/e/NormalStateSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/e/NormalStateSpec.scala index 785519f54..d4f597e48 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/e/NormalStateSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/e/NormalStateSpec.scala @@ -1576,16 +1576,6 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { } } - test("recv CurrentFeerate (ignore negative feerate)") { case (alice, _, alice2bob, _, _, _, _) => - within(30 seconds) { - val sender = TestProbe() - // this happens when in regtest mode - val event = CurrentFeerates(FeeratesPerKw.single(-1)) - sender.send(alice, event) - alice2bob.expectNoMsg(500 millis) - } - } - test("recv BITCOIN_FUNDING_SPENT (their commit w/ htlc)") { case (alice, bob, alice2bob, bob2alice, alice2blockchain, bob2blockchain, _) => within(30 seconds) { val sender = TestProbe() diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/f/ShutdownStateSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/f/ShutdownStateSpec.scala index 307c7e2f3..187045e30 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/f/ShutdownStateSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/f/ShutdownStateSpec.scala @@ -616,16 +616,6 @@ class ShutdownStateSpec extends TestkitBaseClass with StateTestsHelperMethods { } } - test("recv CurrentFeerate (ignore negative feerate)") { case (alice, _, alice2bob, _, _, _, _) => - within(30 seconds) { - val sender = TestProbe() - // this happens when in regtest mode - val event = CurrentFeerates(FeeratesPerKw.single(-1)) - sender.send(alice, event) - alice2bob.expectNoMsg(500 millis) - } - } - test("recv BITCOIN_FUNDING_SPENT (their commit)") { case (alice, bob, alice2bob, bob2alice, alice2blockchain, _, _) => within(30 seconds) { // bob publishes his current commit tx, which contains two pending htlcs alice->bob diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/interop/rustytests/RustyTestsSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/interop/rustytests/RustyTestsSpec.scala index 12a4524c9..ddee3188b 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/interop/rustytests/RustyTestsSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/interop/rustytests/RustyTestsSpec.scala @@ -83,7 +83,7 @@ class RustyTestsSpec extends TestKit(ActorSystem("test")) with Matchers with fix } override def afterAll { - Globals.feeratesPerKw.set(FeeratesPerKw.single(0)) + Globals.feeratesPerKw.set(FeeratesPerKw.single(1)) TestKit.shutdownActorSystem(system) }