1
0
mirror of https://github.com/ACINQ/eclair.git synced 2024-11-20 10:39:19 +01:00

Added bitgo fee provider (#237)

* added bitgo fee provider and set it as default, keeping `earn.com` as fallback.
This commit is contained in:
Pierre-Marie Padiou 2017-11-27 18:17:38 +01:00 committed by Dominique
parent 8b151eb5c0
commit 40b18aed8b
3 changed files with 124 additions and 2 deletions

View File

@ -107,8 +107,8 @@ class Setup(datadir: File, overrideDefaults: Config = ConfigFactory.empty(), act
logger.info(s"initial feeratesPerByte=${Globals.feeratesPerByte.get()}")
val feeProvider = (chain, bitcoin) match {
case ("regtest", _) => new ConstantFeeProvider(defaultFeerates)
case (_, Bitcoind(client)) => new FallbackFeeProvider(new EarnDotComFeeProvider() :: new BitcoinCoreFeeProvider(client.rpcClient, defaultFeerates) :: new ConstantFeeProvider(defaultFeerates) :: Nil) // order matters!
case _ => new FallbackFeeProvider(new EarnDotComFeeProvider() :: new ConstantFeeProvider(defaultFeerates) :: Nil) // order matters!
case (_, Bitcoind(client)) => new FallbackFeeProvider(new BitgoFeeProvider() :: new EarnDotComFeeProvider() :: new BitcoinCoreFeeProvider(client.rpcClient, defaultFeerates) :: new ConstantFeeProvider(defaultFeerates) :: Nil) // order matters!
case _ => new FallbackFeeProvider(new BitgoFeeProvider() :: new EarnDotComFeeProvider() :: new ConstantFeeProvider(defaultFeerates) :: Nil) // order matters!
}
system.scheduler.schedule(0 seconds, 10 minutes)(feeProvider.getFeerates.map {
case feerates: FeeratesPerByte =>

View File

@ -0,0 +1,58 @@
package fr.acinq.eclair.blockchain.fee
import akka.actor.ActorSystem
import akka.http.scaladsl.Http
import akka.http.scaladsl.model._
import akka.http.scaladsl.unmarshalling.Unmarshal
import akka.stream.ActorMaterializer
import de.heikoseeberger.akkahttpjson4s.Json4sSupport._
import org.json4s.JsonAST.{JArray, JInt, JValue}
import org.json4s.{DefaultFormats, jackson}
import scala.concurrent.{ExecutionContext, Future}
class BitgoFeeProvider(implicit system: ActorSystem, ec: ExecutionContext) extends FeeProvider {
import BitgoFeeProvider._
implicit val materializer = ActorMaterializer()
val httpClient = Http(system)
implicit val serialization = jackson.Serialization
implicit val formats = DefaultFormats
override def getFeerates: Future[FeeratesPerByte] =
for {
httpRes <- httpClient.singleRequest(HttpRequest(uri = Uri("https://www.bitgo.com/api/v1/tx/fee"), method = HttpMethods.GET))
json <- Unmarshal(httpRes).to[JValue]
feeRanges = parseFeeRanges(json)
} yield extractFeerates(feeRanges)
}
object BitgoFeeProvider {
case class BlockTarget(block: Int, fee: Long)
def parseFeeRanges(json: JValue): Seq[BlockTarget] = {
val blockTargets = json \ "feeByBlockTarget"
blockTargets.foldField(Seq.empty[BlockTarget]) {
case (list, (strBlockTarget, JInt(feePerKb))) => list :+ BlockTarget(strBlockTarget.toInt, feePerKb.longValue() / 1024)
}
}
def extractFeerate(feeRanges: Seq[BlockTarget], maxBlockDelay: Int): Long = {
// first we keep only fee ranges with a max block delay below the limit
val belowLimit = feeRanges.filter(_.block <= maxBlockDelay)
// out of all the remaining fee ranges, we select the one with the minimum higher bound
belowLimit.map(_.fee).min
}
def extractFeerates(feeRanges: Seq[BlockTarget]): FeeratesPerByte =
FeeratesPerByte(
block_1 = extractFeerate(feeRanges, 1),
blocks_2 = extractFeerate(feeRanges, 2),
blocks_6 = extractFeerate(feeRanges, 6),
blocks_12 = extractFeerate(feeRanges, 12),
blocks_36 = extractFeerate(feeRanges, 36),
blocks_72 = extractFeerate(feeRanges, 72))
}

View File

@ -0,0 +1,64 @@
package fr.acinq.eclair.blockchain.fee
import akka.actor.ActorSystem
import akka.util.Timeout
import org.json4s.DefaultFormats
import org.junit.runner.RunWith
import org.scalatest.FunSuite
import org.scalatest.junit.JUnitRunner
import scala.concurrent.Await
/**
* Created by PM on 27/01/2017.
*/
@RunWith(classOf[JUnitRunner])
class BitgoFeeProviderSpec extends FunSuite {
import BitgoFeeProvider._
import org.json4s.jackson.JsonMethods.parse
implicit val formats = DefaultFormats
val sample_response =
"""
{"feePerKb":136797,"cpfpFeePerKb":136797,"numBlocks":2,"confidence":80,"multiplier":1,"feeByBlockTarget":{"1":149453,"2":136797,"5":122390,"6":105566,"8":100149,"9":96254,"10":122151,"13":116855,"15":110860,"17":87402,"27":82635,"33":71098,"42":105782,"49":68182,"73":59207,"97":17336,"121":16577,"193":13545,"313":12268,"529":11122,"553":9139,"577":5395,"793":5070}}
"""
test("parse test") {
val json = parse(sample_response)
val feeRanges = parseFeeRanges(json)
assert(feeRanges.size === 23)
}
test("extract fee for a particular block delay") {
val json = parse(sample_response)
val feeRanges = parseFeeRanges(json)
val fee = extractFeerate(feeRanges, 6)
assert(fee === 103)
}
test("extract all fees") {
val json = parse(sample_response)
val feeRanges = parseFeeRanges(json)
val feerates = extractFeerates(feeRanges)
val ref = FeeratesPerByte(
block_1 = 145,
blocks_2 = 133,
blocks_6 = 103,
blocks_12 = 93,
blocks_36 = 69,
blocks_72 = 66)
assert(feerates === ref)
}
test("make sure API hasn't changed") {
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._
implicit val system = ActorSystem()
implicit val timeout = Timeout(30 seconds)
val provider = new BitgoFeeProvider()
Await.result(provider.getFeerates, 10 seconds)
}
}