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

Use sttp lib instead of akka-http-client (#720)

Also updated json4s-jackson to 3.6.0
This commit is contained in:
Pierre-Marie Padiou 2018-10-25 16:45:27 +02:00 committed by GitHub
parent 5141cd7d4d
commit 2de7c6b07d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 71 additions and 91 deletions

View File

@ -126,22 +126,34 @@
<artifactId>akka-slf4j_${scala.version.short}</artifactId>
<version>${akka.version}</version>
</dependency>
<!-- HTTP SERVER -->
<dependency>
<groupId>com.typesafe.akka</groupId>
<artifactId>akka-http-core_${scala.version.short}</artifactId>
<version>10.0.11</version>
</dependency>
<!-- HTTP CLIENT -->
<dependency>
<groupId>com.softwaremill.sttp</groupId>
<artifactId>async-http-client-backend-future_${scala.version.short}</artifactId>
<version>${sttp.version}</version>
</dependency>
<!-- JSON -->
<dependency>
<groupId>org.json4s</groupId>
<artifactId>json4s-jackson_${scala.version.short}</artifactId>
<version>3.5.3</version>
<version>3.6.0</version>
</dependency>
<dependency>
<groupId>de.heikoseeberger</groupId>
<artifactId>akka-http-json4s_${scala.version.short}</artifactId>
<version>1.19.0</version>
</dependency>
<dependency>
<groupId>com.softwaremill.sttp</groupId>
<artifactId>json4s_${scala.version.short}</artifactId>
<version>${sttp.version}</version>
</dependency>
<!-- BITCOIN -->
<dependency>
<groupId>fr.acinq</groupId>

View File

@ -24,6 +24,7 @@ import akka.http.scaladsl.Http
import akka.pattern.after
import akka.stream.{ActorMaterializer, BindFailedException}
import akka.util.Timeout
import com.softwaremill.sttp.asynchttpclient.future.AsyncHttpClientFutureBackend
import com.typesafe.config.{Config, ConfigFactory}
import fr.acinq.bitcoin.{BinaryData, Block}
import fr.acinq.eclair.NodeParams.{BITCOIND, ELECTRUM}
@ -81,10 +82,8 @@ class Setup(datadir: File,
// this will force the secure random instance to initialize itself right now, making sure it doesn't hang later (see comment in package.scala)
secureRandom.nextInt()
implicit val materializer = ActorMaterializer()
implicit val timeout = Timeout(30 seconds)
implicit val formats = org.json4s.DefaultFormats
implicit val ec = ExecutionContext.Implicits.global
implicit val sttpBackend = AsyncHttpClientFutureBackend()
val bitcoin = nodeParams.watcherType match {
case BITCOIND =>
@ -93,6 +92,8 @@ class Setup(datadir: File,
password = config.getString("bitcoind.rpcpassword"),
host = config.getString("bitcoind.host"),
port = config.getInt("bitcoind.rpcport"))
implicit val timeout = Timeout(30 seconds)
implicit val formats = org.json4s.DefaultFormats
val future = for {
json <- bitcoinClient.invoke("getblockchaininfo").recover { case _ => throw BitcoinRPCConnectionException }
// Make sure wallet support is enabled in bitcoind.
@ -192,6 +193,7 @@ class Setup(datadir: File,
case Bitcoind(bitcoinClient) => new BitcoinCoreWallet(bitcoinClient)
case Electrum(electrumClient) =>
val electrumWallet = system.actorOf(ElectrumWallet.props(seed, electrumClient, ElectrumWallet.WalletParameters(nodeParams.chainHash)), "electrum-wallet")
implicit val timeout = Timeout(30 seconds)
new ElectrumEclairWallet(electrumWallet, nodeParams.chainHash)
}
_ = wallet.getFinalAddress.map {
@ -233,6 +235,7 @@ class Setup(datadir: File,
_ <- Future.firstCompletedOf(tcpBound.future :: tcpTimeout :: Nil)
_ <- if (config.getBoolean("api.enabled")) {
logger.info(s"json-rpc api enabled on port=${config.getInt("api.port")}")
implicit val materializer = ActorMaterializer()
val api = new Service {
override def scheduler = system.scheduler

View File

@ -16,7 +16,6 @@
package fr.acinq.eclair.blockchain.bitcoind
import akka.actor.ActorSystem
import fr.acinq.bitcoin._
import fr.acinq.eclair._
import fr.acinq.eclair.blockchain._
@ -30,11 +29,10 @@ import scala.concurrent.{ExecutionContext, Future}
/**
* Created by PM on 06/07/2017.
*/
class BitcoinCoreWallet(rpcClient: BitcoinJsonRPCClient)(implicit system: ActorSystem, ec: ExecutionContext) extends EclairWallet with Logging {
class BitcoinCoreWallet(rpcClient: BitcoinJsonRPCClient)(implicit ec: ExecutionContext) extends EclairWallet with Logging {
import BitcoinCoreWallet._
def fundTransaction(hex: String, lockUnspents: Boolean, feeRatePerKw: Long): Future[FundTransactionResponse] = {
val feeRatePerKB = BigDecimal(feerateKw2KB(feeRatePerKw))
rpcClient.invoke("fundrawtransaction", hex, Options(lockUnspents, feeRatePerKB.bigDecimal.scaleByPowerOfTen(-8))).map(json => {

View File

@ -16,52 +16,19 @@
package fr.acinq.eclair.blockchain.bitcoind.rpc
import akka.actor.ActorSystem
import akka.event.Logging
import akka.http.scaladsl.Http
import akka.http.scaladsl.marshalling.Marshal
import akka.http.scaladsl.model._
import akka.http.scaladsl.model.headers.{Authorization, BasicHttpCredentials}
import akka.http.scaladsl.unmarshalling.Unmarshal
import akka.stream.scaladsl.{Keep, Sink, Source}
import akka.stream.{ActorMaterializer, OverflowStrategy, QueueOfferResult}
import de.heikoseeberger.akkahttpjson4s.Json4sSupport._
import com.softwaremill.sttp._
import com.softwaremill.sttp.json4s._
import org.json4s.DefaultFormats
import org.json4s.JsonAST.JValue
import org.json4s.{DefaultFormats, jackson}
import org.json4s.jackson.Serialization
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.{ExecutionContext, Future, Promise}
import scala.util.{Failure, Success}
import scala.concurrent.{ExecutionContext, Future}
class BasicBitcoinJsonRPCClient(user: String, password: String, host: String = "127.0.0.1", port: Int = 8332, ssl: Boolean = false)(implicit system: ActorSystem) extends BitcoinJsonRPCClient {
class BasicBitcoinJsonRPCClient(user: String, password: String, host: String = "127.0.0.1", port: Int = 8332, ssl: Boolean = false)(implicit http: SttpBackend[Future, Nothing]) extends BitcoinJsonRPCClient {
val scheme = if (ssl) "https" else "http"
val uri = Uri(s"$scheme://$host:$port")
implicit val serialization = jackson.Serialization
implicit val formats = DefaultFormats.withBigDecimal
val log = Logging(system, classOf[BasicBitcoinJsonRPCClient])
implicit val materializer = ActorMaterializer()
val httpClientFlow = Http().cachedHostConnectionPool[Promise[HttpResponse]](host, port)
val queueSize = 256
val queue = Source.queue[(HttpRequest, Promise[HttpResponse])](queueSize, OverflowStrategy.dropNew)
.via(httpClientFlow)
.toMat(Sink.foreach({
case ((Success(resp), p)) => p.success(resp)
case ((Failure(e), p)) => p.failure(e)
}))(Keep.left)
.run()
def queueRequest(request: HttpRequest): Future[HttpResponse] = {
val responsePromise = Promise[HttpResponse]()
queue.offer(request -> responsePromise).flatMap {
case QueueOfferResult.Enqueued => responsePromise.future
case QueueOfferResult.Dropped => Future.failed(new RuntimeException("Queue overflowed. Try again later."))
case QueueOfferResult.Failure(ex) => Future.failed(ex)
case QueueOfferResult.QueueClosed => Future.failed(new RuntimeException("Queue was closed (pool shut down) while running the request. Try again later."))
}
}
implicit val serialization = Serialization
override def invoke(method: String, params: Any*)(implicit ec: ExecutionContext): Future[JValue] =
invoke(Seq(JsonRPCRequest(method = method, params = params))).map(l => jsonResponse2Exception(l.head).result)
@ -73,12 +40,12 @@ class BasicBitcoinJsonRPCClient(user: String, password: String, host: String = "
def invoke(requests: Seq[JsonRPCRequest])(implicit ec: ExecutionContext): Future[Seq[JsonRPCResponse]] =
for {
entity <- Marshal(requests).to[RequestEntity]
_ = log.debug("sending rpc request with body={}", entity)
httpRes <- queueRequest(HttpRequest(uri = "/", method = HttpMethods.POST).addHeader(Authorization(BasicHttpCredentials(user, password))).withEntity(entity))
jsonRpcRes <- Unmarshal(httpRes).to[Seq[JsonRPCResponse]].recover {
case t: Throwable if httpRes.status == StatusCodes.Unauthorized => throw new RuntimeException("bitcoind replied with 401/Unauthorized (bad user/password?)", t)
}
} yield jsonRpcRes
res <- sttp
.post(uri"$scheme://$host:$port")
.body(requests)
.auth.basic(user, password)
.response(asJson[Seq[JsonRPCResponse]])
.send()
} yield res.unsafeBody
}

View File

@ -17,7 +17,7 @@
package fr.acinq.eclair.blockchain.fee
import fr.acinq.bitcoin._
import fr.acinq.eclair.blockchain.bitcoind.rpc.{BitcoinJsonRPCClient, Error, JsonRPCError}
import fr.acinq.eclair.blockchain.bitcoind.rpc.BitcoinJsonRPCClient
import org.json4s.DefaultFormats
import org.json4s.JsonAST._

View File

@ -16,37 +16,33 @@
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 com.softwaremill.sttp._
import com.softwaremill.sttp.json4s._
import fr.acinq.bitcoin.{BinaryData, Block}
import org.json4s.DefaultFormats
import org.json4s.JsonAST.{JInt, JValue}
import org.json4s.{DefaultFormats, jackson}
import org.json4s.jackson.Serialization
import scala.concurrent.{ExecutionContext, Future}
class BitgoFeeProvider(chainHash: BinaryData)(implicit system: ActorSystem, ec: ExecutionContext) extends FeeProvider {
class BitgoFeeProvider(chainHash: BinaryData)(implicit http: SttpBackend[Future, Nothing], ec: ExecutionContext) extends FeeProvider {
import BitgoFeeProvider._
implicit val materializer = ActorMaterializer()
val httpClient = Http(system)
implicit val serialization = jackson.Serialization
implicit val formats = DefaultFormats
implicit val serialization = Serialization
val uri = chainHash match {
case Block.LivenetGenesisBlock.hash => Uri("https://www.bitgo.com/api/v2/btc/tx/fee")
case _ => Uri("https://test.bitgo.com/api/v2/tbtc/tx/fee")
case Block.LivenetGenesisBlock.hash => uri"https://www.bitgo.com/api/v2/btc/tx/fee"
case _ => uri"https://test.bitgo.com/api/v2/tbtc/tx/fee"
}
override def getFeerates: Future[FeeratesPerKB] =
for {
httpRes <- httpClient.singleRequest(HttpRequest(uri = uri, method = HttpMethods.GET))
json <- Unmarshal(httpRes).to[JValue]
feeRanges = parseFeeRanges(json)
res <- sttp.get(uri)
.response(asJson[JValue])
.send()
feeRanges = parseFeeRanges(res.unsafeBody)
} yield extractFeerates(feeRanges)
}

View File

@ -16,34 +16,32 @@
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 com.softwaremill.sttp._
import com.softwaremill.sttp.json4s._
import org.json4s.DefaultFormats
import org.json4s.JsonAST.{JArray, JInt, JValue}
import org.json4s.{DefaultFormats, jackson}
import org.json4s.jackson.Serialization
import scala.concurrent.{ExecutionContext, Future}
/**
* Created by PM on 16/11/2017.
*/
class EarnDotComFeeProvider(implicit system: ActorSystem, ec: ExecutionContext) extends FeeProvider {
class EarnDotComFeeProvider(implicit http: SttpBackend[Future, Nothing], ec: ExecutionContext) extends FeeProvider {
import EarnDotComFeeProvider._
implicit val materializer = ActorMaterializer()
val httpClient = Http(system)
implicit val serialization = jackson.Serialization
implicit val formats = DefaultFormats
implicit val serialization = Serialization
val uri = uri"https://bitcoinfees.earn.com/api/v1/fees/list"
override def getFeerates: Future[FeeratesPerKB] =
for {
httpRes <- httpClient.singleRequest(HttpRequest(uri = Uri("https://bitcoinfees.earn.com/api/v1/fees/list"), method = HttpMethods.GET))
json <- Unmarshal(httpRes).to[JValue]
feeRanges = parseFeeRanges(json)
json <- sttp.get(uri)
.response(asJson[JValue])
.send()
feeRanges = parseFeeRanges(json.unsafeBody)
} yield extractFeerates(feeRanges)
}

View File

@ -27,7 +27,7 @@ import scala.concurrent.{ExecutionContext, Future}
/**
* Created by PM on 26/04/2016.
*/
class TestBitcoinClient()(implicit system: ActorSystem) extends ExtendedBitcoinClient(new BasicBitcoinJsonRPCClient("", "", "", 0)) {
class TestBitcoinClient()(implicit system: ActorSystem) extends ExtendedBitcoinClient(new BasicBitcoinJsonRPCClient("", "", "", 0)(http = null)) {
import scala.concurrent.ExecutionContext.Implicits.global

View File

@ -48,7 +48,6 @@ class BitcoinCoreWalletSpec extends TestKit(ActorSystem("test")) with BitcoindSe
implicit val formats = DefaultFormats
override def beforeAll(): Unit = {
startBitcoind()
}

View File

@ -23,6 +23,7 @@ import java.util.UUID
import akka.actor.{Actor, ActorRef, ActorSystem, Props}
import akka.pattern.pipe
import akka.testkit.{TestKitBase, TestProbe}
import com.softwaremill.sttp.asynchttpclient.future.AsyncHttpClientFutureBackend
import fr.acinq.eclair.blockchain.bitcoind.rpc.{BasicBitcoinJsonRPCClient, BitcoinJsonRPCClient}
import fr.acinq.eclair.integration.IntegrationSpec
import grizzled.slf4j.Logging
@ -35,6 +36,7 @@ trait BitcoindService extends Logging {
self: TestKitBase =>
implicit val system: ActorSystem
implicit val sttpBackend = AsyncHttpClientFutureBackend()
import scala.sys.process._

View File

@ -28,6 +28,7 @@ import fr.acinq.eclair.blockchain.bitcoind.{BitcoinCoreWallet, BitcoindService}
import fr.acinq.eclair.blockchain.electrum.ElectrumClient.{BroadcastTransaction, BroadcastTransactionResponse}
import grizzled.slf4j.Logging
import org.json4s.JsonAST.{JDecimal, JString, JValue}
import org.scalatest.junit.JUnitRunner
import org.scalatest.{BeforeAndAfterAll, FunSuiteLike}
import scala.concurrent.Await

View File

@ -16,8 +16,8 @@
package fr.acinq.eclair.blockchain.fee
import akka.actor.ActorSystem
import akka.util.Timeout
import com.softwaremill.sttp.asynchttpclient.future.AsyncHttpClientFutureBackend
import grizzled.slf4j.Logging
import org.json4s.DefaultFormats
import org.scalatest.FunSuite
@ -70,7 +70,7 @@ class EarnDotComFeeProviderSpec extends FunSuite with Logging {
test("make sure API hasn't changed") {
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._
implicit val system = ActorSystem()
implicit val sttpBackend = AsyncHttpClientFutureBackend()
implicit val timeout = Timeout(30 seconds)
val provider = new EarnDotComFeeProvider()
logger.info("earn.com livenet fees: " + Await.result(provider.getFeerates, 10 seconds))

View File

@ -19,7 +19,8 @@ package fr.acinq.eclair.integration
import java.io.{File, PrintWriter}
import java.util.Properties
import akka.actor.{ActorRef, ActorSystem}
import akka.actor.{ActorRef, ActorSystem, Terminated}
import akka.pattern.pipe
import akka.testkit.{TestKit, TestProbe}
import com.google.common.net.HostAndPort
import com.typesafe.config.{Config, ConfigFactory}

View File

@ -19,6 +19,7 @@ package fr.acinq.eclair.router
import akka.actor.ActorSystem
import akka.pattern.pipe
import akka.testkit.TestProbe
import com.softwaremill.sttp.asynchttpclient.future.AsyncHttpClientFutureBackend
import fr.acinq.bitcoin.Crypto.PrivateKey
import fr.acinq.bitcoin.{BinaryData, Block, Satoshi, Script, Transaction}
import fr.acinq.eclair.blockchain.ValidateResult
@ -44,6 +45,7 @@ class AnnouncementsBatchValidationSpec extends FunSuite {
import scala.concurrent.ExecutionContext.Implicits.global
implicit val system = ActorSystem()
implicit val sttpBackend = AsyncHttpClientFutureBackend()
implicit val extendedBitcoinClient = new ExtendedBitcoinClient(new BasicBitcoinJsonRPCClient(user = "foo", password = "bar", host = "localhost", port = 18332))
val channels = for (i <- 0 until 50) yield {
@ -76,7 +78,7 @@ object AnnouncementsBatchValidationSpec {
def generateBlocks(numBlocks: Int)(implicit extendedBitcoinClient: ExtendedBitcoinClient, ec: ExecutionContext) =
Await.result(extendedBitcoinClient.rpcClient.invoke("generate", numBlocks), 10 seconds)
def simulateChannel()(implicit extendedBitcoinClient: ExtendedBitcoinClient, ec: ExecutionContext, system: ActorSystem): SimulatedChannel = {
def simulateChannel()(implicit extendedBitcoinClient: ExtendedBitcoinClient, ec: ExecutionContext): SimulatedChannel = {
val node1Key = randomKey
val node2Key = randomKey
val node1BitcoinKey = randomKey

View File

@ -66,6 +66,7 @@
<scala.version>2.11.11</scala.version>
<scala.version.short>2.11</scala.version.short>
<akka.version>2.4.20</akka.version>
<sttp.version>1.3.9</sttp.version>
<bitcoinlib.version>0.9.17</bitcoinlib.version>
<guava.version>24.0-android</guava.version>
</properties>