From 5f4053b2e43363d2cb547a6cb433666c6a320b64 Mon Sep 17 00:00:00 2001 From: benthecarman Date: Thu, 13 Jan 2022 05:43:05 -0600 Subject: [PATCH] Create FallbackFeeRateApi (#3974) * Create AggregateFeeRateApi * Rename, add docs --- .../feeprovider/FeeRateProviderTest.scala | 12 ++++++ .../feeprovider/FallbackFeeRateApi.scala | 37 +++++++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 fee-provider/src/main/scala/org/bitcoins/feeprovider/FallbackFeeRateApi.scala diff --git a/fee-provider-test/src/test/scala/org/bitcoins/feeprovider/FeeRateProviderTest.scala b/fee-provider-test/src/test/scala/org/bitcoins/feeprovider/FeeRateProviderTest.scala index c1533bd3d5..30421a2ea6 100644 --- a/fee-provider-test/src/test/scala/org/bitcoins/feeprovider/FeeRateProviderTest.scala +++ b/fee-provider-test/src/test/scala/org/bitcoins/feeprovider/FeeRateProviderTest.scala @@ -99,6 +99,18 @@ class FeeRateProviderTest extends BitcoinSAsyncTest { } } + it must "use an aggregate of fee providers" in { + val expected = SatoshisPerByte(Satoshis(4)) + val providerA = ConstantFeeRateProvider(expected) + val providerB = ConstantFeeRateProvider(SatoshisPerByte(Satoshis(2))) + + val provider = FallbackFeeRateApi(Vector(providerA, providerB)) + + provider.getFeeRate().map { feeRate => + assert(feeRate == expected) + } + } + private def testProvider(provider: FeeRateApi): Future[Assertion] = { provider.getFeeRate().map { feeRate => assert(feeRate.toLong > 0) diff --git a/fee-provider/src/main/scala/org/bitcoins/feeprovider/FallbackFeeRateApi.scala b/fee-provider/src/main/scala/org/bitcoins/feeprovider/FallbackFeeRateApi.scala new file mode 100644 index 0000000000..bc36cd2a7b --- /dev/null +++ b/fee-provider/src/main/scala/org/bitcoins/feeprovider/FallbackFeeRateApi.scala @@ -0,0 +1,37 @@ +package org.bitcoins.feeprovider + +import org.bitcoins.core.api.feeprovider.FeeRateApi +import org.bitcoins.core.util.FutureUtil +import org.bitcoins.core.wallet.fee.FeeUnit + +import scala.concurrent.{ExecutionContext, Future} +import scala.util.control.NonFatal + +/** Takes multiple [[FeeRateApi FeeRateApis]] and attempts to get a fee rate from + * one in order until one succeeds. + */ +case class FallbackFeeRateApi(providers: Vector[FeeRateApi])(implicit + ec: ExecutionContext) + extends FeeRateApi { + + override def getFeeRate(): Future[FeeUnit] = { + val init: Option[FeeUnit] = None + val retOptF = FutureUtil.foldLeftAsync(init, providers) { + case (ret, provider) => + ret match { + case Some(value) => Future.successful(value).map(Some(_)) + case None => + provider + .getFeeRate() + .map(Some(_)) + .recover { case NonFatal(_) => None } + } + } + + retOptF.map { + case Some(ret) => ret + case None => + sys.error("Failed to get fee rate from any provider") + } + } +}