diff --git a/eclair-core/pom.xml b/eclair-core/pom.xml
index 9df8a928e..037c642ff 100644
--- a/eclair-core/pom.xml
+++ b/eclair-core/pom.xml
@@ -126,12 +126,6 @@
akka-slf4j_${scala.version.short}
${akka.version}
-
-
- com.typesafe.akka
- akka-http-core_${scala.version.short}
- ${akka.http.version}
-
com.softwaremill.sttp
@@ -144,11 +138,6 @@
json4s-jackson_${scala.version.short}
3.6.0
-
- de.heikoseeberger
- akka-http-json4s_${scala.version.short}
- 1.19.0
-
com.softwaremill.sttp
json4s_${scala.version.short}
@@ -254,12 +243,6 @@
1.2.3
test
-
- com.typesafe.akka
- akka-http-testkit_${scala.version.short}
- ${akka.http.version}
- test
-
org.mockito
mockito-scala-scalatest_2.11
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 bc8ee08f4..4c770f4fa 100644
--- a/eclair-core/src/main/scala/fr/acinq/eclair/Setup.scala
+++ b/eclair-core/src/main/scala/fr/acinq/eclair/Setup.scala
@@ -23,15 +23,12 @@ import java.util.concurrent.TimeUnit
import akka.Done
import akka.actor.{ActorRef, ActorSystem, Props, SupervisorStrategy}
-import akka.http.scaladsl.Http
import akka.pattern.after
-import akka.stream.{ActorMaterializer, BindFailedException}
import akka.util.Timeout
import com.softwaremill.sttp.okhttp.OkHttpFutureBackend
import com.typesafe.config.{Config, ConfigFactory}
import fr.acinq.bitcoin.{Block, ByteVector32}
import fr.acinq.eclair.NodeParams.{BITCOIND, ELECTRUM}
-import fr.acinq.eclair.api._
import fr.acinq.eclair.blockchain.bitcoind.rpc.{BasicBitcoinJsonRPCClient, BatchingBitcoinJsonRPCClient, ExtendedBitcoinClient}
import fr.acinq.eclair.blockchain.bitcoind.zmq.ZMQActor
import fr.acinq.eclair.blockchain.bitcoind.{BitcoinCoreWallet, ZmqWatcher}
@@ -71,7 +68,6 @@ class Setup(datadir: File,
seed_opt: Option[ByteVector] = None,
db: Option[Databases] = None)(implicit system: ActorSystem) extends Logging {
- implicit val materializer = ActorMaterializer()
implicit val timeout = Timeout(30 seconds)
implicit val formats = org.json4s.DefaultFormats
implicit val ec = ExecutionContext.Implicits.global
@@ -287,32 +283,6 @@ class Setup(datadir: File,
_ <- Future.firstCompletedOf(zmqBlockConnected.future :: zmqBlockTimeout :: Nil)
_ <- Future.firstCompletedOf(zmqTxConnected.future :: zmqTxTimeout :: Nil)
_ <- 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 getInfo = GetInfoResponse(nodeId = nodeParams.nodeId,
- alias = nodeParams.alias,
- chainHash = nodeParams.chainHash,
- blockHeight = Globals.blockCount.intValue(),
- publicAddresses = nodeParams.publicAddresses)
- val apiPassword = config.getString("api.password") match {
- case "" => throw EmptyAPIPasswordException
- case valid => valid
- }
- val apiRoute = new Service {
- override val actorSystem = kit.system
- override val mat = materializer
- override val password = apiPassword
- override val eclairApi: Eclair = new EclairImpl(kit)
- }.route
- val httpBound = Http().bindAndHandle(apiRoute, config.getString("api.binding-ip"), config.getInt("api.port")).recover {
- case _: BindFailedException => throw TCPBindException(config.getInt("api.port"))
- }
- val httpTimeout = after(5 seconds, using = system.scheduler)(Future.failed(TCPBindException(config.getInt("api.port"))))
- Future.firstCompletedOf(httpBound :: httpTimeout :: Nil)
- } else {
- Future.successful(logger.info("json-rpc api is disabled"))
- }
} yield kit
}
diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/channel/ChannelEvents.scala b/eclair-core/src/main/scala/fr/acinq/eclair/channel/ChannelEvents.scala
index e80ff51ec..c3b3d7997 100644
--- a/eclair-core/src/main/scala/fr/acinq/eclair/channel/ChannelEvents.scala
+++ b/eclair-core/src/main/scala/fr/acinq/eclair/channel/ChannelEvents.scala
@@ -20,7 +20,6 @@ import akka.actor.ActorRef
import fr.acinq.bitcoin.Crypto.PublicKey
import fr.acinq.bitcoin.{ByteVector32, Satoshi, Transaction}
import fr.acinq.eclair.{MilliSatoshi, ShortChannelId}
-import fr.acinq.eclair.api.MilliSatoshiSerializer
import fr.acinq.eclair.channel.Channel.ChannelError
import fr.acinq.eclair.channel.Helpers.Closing.ClosingType
import fr.acinq.eclair.wire.{ChannelAnnouncement, ChannelUpdate}
diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/wire/ChannelCodecsSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/wire/ChannelCodecsSpec.scala
index 82e007499..4fd04a778 100644
--- a/eclair-core/src/test/scala/fr/acinq/eclair/wire/ChannelCodecsSpec.scala
+++ b/eclair-core/src/test/scala/fr/acinq/eclair/wire/ChannelCodecsSpec.scala
@@ -16,22 +16,25 @@
package fr.acinq.eclair.wire
+import java.net.InetSocketAddress
import java.util.UUID
import akka.actor.ActorSystem
-import fr.acinq.bitcoin.Crypto.PrivateKey
+import com.google.common.net.HostAndPort
+import fr.acinq.bitcoin.Crypto.{PrivateKey, PublicKey}
import fr.acinq.bitcoin.DeterministicWallet.KeyPath
-import fr.acinq.bitcoin.{Block, ByteVector32, Crypto, DeterministicWallet, OutPoint, Satoshi, Transaction}
-import fr.acinq.eclair.api.JsonSupport
+import fr.acinq.bitcoin.{Block, ByteVector32, ByteVector64, Crypto, DeterministicWallet, OutPoint, Satoshi, Transaction}
import fr.acinq.eclair.channel.Helpers.Funding
import fr.acinq.eclair.channel._
import fr.acinq.eclair.crypto.{LocalKeyManager, ShaChain}
import fr.acinq.eclair.payment.{Local, Relayed}
import fr.acinq.eclair.router.Announcements
-import fr.acinq.eclair.transactions.Transactions.CommitTx
+import fr.acinq.eclair.transactions.Transactions.{CommitTx, InputInfo, TransactionWithInputInfo}
import fr.acinq.eclair.transactions._
import fr.acinq.eclair.wire.ChannelCodecs._
import fr.acinq.eclair.{TestConstants, UInt64, randomBytes, randomBytes32, randomKey, _}
+import org.json4s.{CustomKeySerializer, CustomSerializer}
+import org.json4s.JsonAST._
import org.json4s.jackson.Serialization
import org.scalatest.FunSuite
import scodec.bits._
@@ -43,8 +46,8 @@ import scala.io.Source
import scala.util.Random
/**
- * Created by PM on 31/05/2016.
- */
+ * Created by PM on 31/05/2016.
+ */
class ChannelCodecsSpec extends FunSuite {
@@ -333,9 +336,7 @@ class ChannelCodecsSpec extends FunSuite {
assert(oldjson === refjson)
assert(newjson === refjson)
}
-
}
-
}
object ChannelCodecsSpec {
@@ -402,4 +403,155 @@ object ChannelCodecsSpec {
val channelUpdate = Announcements.makeChannelUpdate(ByteVector32(ByteVector.fill(32)(1)), randomKey, randomKey.publicKey, ShortChannelId(142553), CltvExpiryDelta(42), 15 msat, 575 msat, 53, Channel.MAX_FUNDING.toMilliSatoshi)
val normal = DATA_NORMAL(commitments, ShortChannelId(42), true, None, channelUpdate, None, None)
-}
+
+ object JsonSupport {
+
+ class ByteVectorSerializer extends CustomSerializer[ByteVector](format => ( {
+ null
+ }, {
+ case x: ByteVector => JString(x.toHex)
+ }))
+
+ class ByteVector32Serializer extends CustomSerializer[ByteVector32](format => ( {
+ null
+ }, {
+ case x: ByteVector32 => JString(x.toHex)
+ }))
+
+ class ByteVector64Serializer extends CustomSerializer[ByteVector64](format => ( {
+ null
+ }, {
+ case x: ByteVector64 => JString(x.toHex)
+ }))
+
+ class UInt64Serializer extends CustomSerializer[UInt64](format => ( {
+ null
+ }, {
+ case x: UInt64 => JInt(x.toBigInt)
+ }))
+
+ class SatoshiSerializer extends CustomSerializer[Satoshi](format => ( {
+ null
+ }, {
+ case x: Satoshi => JInt(x.toLong)
+ }))
+
+ class MilliSatoshiSerializer extends CustomSerializer[MilliSatoshi](format => ( {
+ null
+ }, {
+ case x: MilliSatoshi => JInt(x.toLong)
+ }))
+
+ class CltvExpirySerializer extends CustomSerializer[CltvExpiry](format => ( {
+ null
+ }, {
+ case x: CltvExpiry => JLong(x.toLong)
+ }))
+
+ class CltvExpiryDeltaSerializer extends CustomSerializer[CltvExpiryDelta](format => ( {
+ null
+ }, {
+ case x: CltvExpiryDelta => JInt(x.toInt)
+ }))
+
+ class ShortChannelIdSerializer extends CustomSerializer[ShortChannelId](format => ( {
+ null
+ }, {
+ case x: ShortChannelId => JString(x.toString())
+ }))
+
+ class StateSerializer extends CustomSerializer[State](format => ( {
+ null
+ }, {
+ case x: State => JString(x.toString())
+ }))
+
+ class ShaChainSerializer extends CustomSerializer[ShaChain](format => ( {
+ null
+ }, {
+ case x: ShaChain => JNull
+ }))
+
+ class PublicKeySerializer extends CustomSerializer[PublicKey](format => ( {
+ null
+ }, {
+ case x: PublicKey => JString(x.toString())
+ }))
+
+ class PrivateKeySerializer extends CustomSerializer[PrivateKey](format => ( {
+ null
+ }, {
+ case x: PrivateKey => JString("XXX")
+ }))
+
+ class ChannelVersionSerializer extends CustomSerializer[ChannelVersion](format => ( {
+ null
+ }, {
+ case x: ChannelVersion => JString(x.bits.toBin)
+ }))
+
+ class TransactionSerializer extends CustomSerializer[TransactionWithInputInfo](ser = format => ( {
+ null
+ }, {
+ case x: Transaction => JObject(List(
+ JField("txid", JString(x.txid.toHex)),
+ JField("tx", JString(x.toString()))
+ ))
+ }))
+
+ class TransactionWithInputInfoSerializer extends CustomSerializer[TransactionWithInputInfo](ser = format => ( {
+ null
+ }, {
+ case x: TransactionWithInputInfo => JObject(List(
+ JField("txid", JString(x.tx.txid.toHex)),
+ JField("tx", JString(x.tx.toString()))
+ ))
+ }))
+
+ class InetSocketAddressSerializer extends CustomSerializer[InetSocketAddress](format => ( {
+ null
+ }, {
+ case address: InetSocketAddress => JString(HostAndPort.fromParts(address.getHostString, address.getPort).toString)
+ }))
+
+ class OutPointSerializer extends CustomSerializer[OutPoint](format => ( {
+ null
+ }, {
+ case x: OutPoint => JString(s"${x.txid}:${x.index}")
+ }))
+
+ class OutPointKeySerializer extends CustomKeySerializer[OutPoint](format => ( {
+ null
+ }, {
+ case x: OutPoint => s"${x.txid}:${x.index}"
+ }))
+
+ class InputInfoSerializer extends CustomSerializer[InputInfo](format => ( {
+ null
+ }, {
+ case x: InputInfo => JObject(("outPoint", JString(s"${x.outPoint.txid}:${x.outPoint.index}")), ("amountSatoshis", JInt(x.txOut.amount.toLong)))
+ }))
+
+ implicit val formats = org.json4s.DefaultFormats +
+ new ByteVectorSerializer +
+ new ByteVector32Serializer +
+ new ByteVector64Serializer +
+ new UInt64Serializer +
+ new SatoshiSerializer +
+ new MilliSatoshiSerializer +
+ new CltvExpirySerializer +
+ new CltvExpiryDeltaSerializer +
+ new ShortChannelIdSerializer +
+ new StateSerializer +
+ new ShaChainSerializer +
+ new PublicKeySerializer +
+ new PrivateKeySerializer +
+ new TransactionSerializer +
+ new TransactionWithInputInfoSerializer +
+ new InetSocketAddressSerializer +
+ new OutPointSerializer +
+ new OutPointKeySerializer +
+ new ChannelVersionSerializer +
+ new InputInfoSerializer
+ }
+}
\ No newline at end of file
diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/wire/LightningMessageCodecsSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/wire/LightningMessageCodecsSpec.scala
index 9bdaa28e3..03f01c3d5 100644
--- a/eclair-core/src/test/scala/fr/acinq/eclair/wire/LightningMessageCodecsSpec.scala
+++ b/eclair-core/src/test/scala/fr/acinq/eclair/wire/LightningMessageCodecsSpec.scala
@@ -123,11 +123,6 @@ class LightningMessageCodecsSpec extends FunSuite {
case class TestItem(msg: Any, hex: String)
test("test vectors for extended channel queries ") {
- import org.json4s.{CustomSerializer, ShortTypeHints}
- import org.json4s.JsonAST.JString
- import org.json4s.jackson.Serialization
- import fr.acinq.eclair.api._
-
val query_channel_range = QueryChannelRange(Block.RegtestGenesisBlock.blockId, 100000, 1500, TlvStream.empty)
val query_channel_range_timestamps_checksums = QueryChannelRange(Block.RegtestGenesisBlock.blockId,
35000,
diff --git a/eclair-node-gui/src/main/scala/fr/acinq/eclair/JavafxBoot.scala b/eclair-node-gui/src/main/scala/fr/acinq/eclair/JavafxBoot.scala
index c38040b47..9152e82ca 100644
--- a/eclair-node-gui/src/main/scala/fr/acinq/eclair/JavafxBoot.scala
+++ b/eclair-node-gui/src/main/scala/fr/acinq/eclair/JavafxBoot.scala
@@ -22,7 +22,7 @@ import akka.actor.ActorSystem
import fr.acinq.eclair.gui.{FxApp, FxPreloader}
import grizzled.slf4j.Logging
import javafx.application.Application
-
+import scala.concurrent.ExecutionContext.Implicits.global
/**
* Created by PM on 25/01/2016.
*/
@@ -33,7 +33,10 @@ object JavafxBoot extends App with Logging {
if (headless) {
implicit val system = ActorSystem("eclair-node-gui")
- new Setup(datadir).bootstrap
+ val setup = new Setup(datadir)
+ setup.bootstrap.map { kit =>
+ Boot.startApiServiceIfEnabled(setup.config, kit)
+ }
} else {
System.setProperty("javafx.preloader", classOf[FxPreloader].getName)
Application.launch(classOf[FxApp], datadir.getAbsolutePath)
diff --git a/eclair-node-gui/src/main/scala/fr/acinq/eclair/gui/FxApp.scala b/eclair-node-gui/src/main/scala/fr/acinq/eclair/gui/FxApp.scala
index 30f4f1df7..163d8c14a 100644
--- a/eclair-node-gui/src/main/scala/fr/acinq/eclair/gui/FxApp.scala
+++ b/eclair-node-gui/src/main/scala/fr/acinq/eclair/gui/FxApp.scala
@@ -104,7 +104,8 @@ class FxApp extends Application with Logging {
system.eventStream.subscribe(guiUpdater, classOf[ElectrumEvent])
pKit.completeWith(setup.bootstrap)
pKit.future.onComplete {
- case Success(_) =>
+ case Success(kit) =>
+ Boot.startApiServiceIfEnabled(setup.config, kit)
Platform.runLater(new Runnable {
override def run(): Unit = {
val scene = new Scene(mainRoot)
diff --git a/eclair-node/pom.xml b/eclair-node/pom.xml
index de0631970..e2f893519 100644
--- a/eclair-node/pom.xml
+++ b/eclair-node/pom.xml
@@ -91,5 +91,29 @@
janino
3.0.7
+
+
+ com.typesafe.akka
+ akka-http-core_${scala.version.short}
+ ${akka.http.version}
+
+
+ de.heikoseeberger
+ akka-http-json4s_${scala.version.short}
+ 1.19.0
+
+
+
+ com.typesafe.akka
+ akka-http-testkit_${scala.version.short}
+ ${akka.http.version}
+ test
+
+
+ org.mockito
+ mockito-scala-scalatest_2.11
+ 1.4.1
+ test
+
diff --git a/eclair-node/src/main/scala/fr/acinq/eclair/Boot.scala b/eclair-node/src/main/scala/fr/acinq/eclair/Boot.scala
index 3241994b3..45ad4bde2 100644
--- a/eclair-node/src/main/scala/fr/acinq/eclair/Boot.scala
+++ b/eclair-node/src/main/scala/fr/acinq/eclair/Boot.scala
@@ -19,9 +19,12 @@ package fr.acinq.eclair
import java.io.File
import akka.actor.ActorSystem
+import akka.http.scaladsl.Http
+import akka.stream.{ActorMaterializer, BindFailedException}
+import com.typesafe.config.Config
+import fr.acinq.eclair.api.Service
import grizzled.slf4j.Logging
-
-import scala.concurrent.ExecutionContext
+import scala.concurrent.{Await, ExecutionContext}
import scala.util.{Failure, Success}
/**
@@ -39,13 +42,45 @@ object Boot extends App with Logging {
val setup = new Setup(datadir)
plugins.foreach(_.onSetup(setup))
setup.bootstrap onComplete {
- case Success(kit) => plugins.foreach(_.onKit(kit))
+ case Success(kit) =>
+ startApiServiceIfEnabled(setup.config, kit)
+ plugins.foreach(_.onKit(kit))
case Failure(t) => onError(t)
}
} catch {
case t: Throwable => onError(t)
}
+ /**
+ * Starts the http APIs service if enabled in the configuration
+ *
+ * @param config
+ * @param kit
+ * @param system
+ * @param ec
+ */
+ def startApiServiceIfEnabled(config: Config, kit: Kit)(implicit system: ActorSystem, ec: ExecutionContext) = {
+ if(config.getBoolean("api.enabled")){
+ logger.info(s"json API enabled on port=${config.getInt("api.port")}")
+ implicit val materializer = ActorMaterializer()
+ val apiPassword = config.getString("api.password") match {
+ case "" => throw EmptyAPIPasswordException
+ case valid => valid
+ }
+ val apiRoute = new Service {
+ override val actorSystem = system
+ override val mat = materializer
+ override val password = apiPassword
+ override val eclairApi: Eclair = new EclairImpl(kit)
+ }.route
+ Http().bindAndHandle(apiRoute, config.getString("api.binding-ip"), config.getInt("api.port")).onFailure {
+ case _: BindFailedException => onError(TCPBindException(config.getInt("api.port")))
+ }
+ } else {
+ logger.info("json API disabled")
+ }
+ }
+
def onError(t: Throwable): Unit = {
val errorMsg = if (t.getMessage != null) t.getMessage else t.getClass.getSimpleName
System.err.println(s"fatal error: $errorMsg")
diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/api/ExtraDirectives.scala b/eclair-node/src/main/scala/fr/acinq/eclair/api/ExtraDirectives.scala
similarity index 99%
rename from eclair-core/src/main/scala/fr/acinq/eclair/api/ExtraDirectives.scala
rename to eclair-node/src/main/scala/fr/acinq/eclair/api/ExtraDirectives.scala
index 2973b53ec..6d308f44a 100644
--- a/eclair-core/src/main/scala/fr/acinq/eclair/api/ExtraDirectives.scala
+++ b/eclair-node/src/main/scala/fr/acinq/eclair/api/ExtraDirectives.scala
@@ -26,7 +26,6 @@ import fr.acinq.eclair.{MilliSatoshi, ShortChannelId}
import fr.acinq.eclair.api.FormParamExtractors.{sha256HashUnmarshaller, shortChannelIdUnmarshaller}
import fr.acinq.eclair.api.JsonSupport._
import fr.acinq.eclair.payment.PaymentRequest
-
import scala.concurrent.Future
import scala.util.{Failure, Success}
diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/api/FormParamExtractors.scala b/eclair-node/src/main/scala/fr/acinq/eclair/api/FormParamExtractors.scala
similarity index 98%
rename from eclair-core/src/main/scala/fr/acinq/eclair/api/FormParamExtractors.scala
rename to eclair-node/src/main/scala/fr/acinq/eclair/api/FormParamExtractors.scala
index cbe045faa..d5d92c8e3 100644
--- a/eclair-core/src/main/scala/fr/acinq/eclair/api/FormParamExtractors.scala
+++ b/eclair-node/src/main/scala/fr/acinq/eclair/api/FormParamExtractors.scala
@@ -18,16 +18,15 @@ package fr.acinq.eclair.api
import java.util.UUID
-import JsonSupport._
import akka.http.scaladsl.unmarshalling.Unmarshaller
import akka.util.Timeout
-import fr.acinq.bitcoin.{ByteVector32, Satoshi}
import fr.acinq.bitcoin.Crypto.PublicKey
-import fr.acinq.eclair.{MilliSatoshi, ShortChannelId}
+import fr.acinq.bitcoin.{ByteVector32, Satoshi}
+import fr.acinq.eclair.api.JsonSupport._
import fr.acinq.eclair.io.NodeURI
import fr.acinq.eclair.payment.PaymentRequest
+import fr.acinq.eclair.{MilliSatoshi, ShortChannelId}
import scodec.bits.ByteVector
-
import scala.concurrent.duration._
import scala.util.{Failure, Success, Try}
diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/api/JsonSerializers.scala b/eclair-node/src/main/scala/fr/acinq/eclair/api/JsonSerializers.scala
similarity index 98%
rename from eclair-core/src/main/scala/fr/acinq/eclair/api/JsonSerializers.scala
rename to eclair-node/src/main/scala/fr/acinq/eclair/api/JsonSerializers.scala
index 13059d838..73c85f0c8 100644
--- a/eclair-core/src/main/scala/fr/acinq/eclair/api/JsonSerializers.scala
+++ b/eclair-node/src/main/scala/fr/acinq/eclair/api/JsonSerializers.scala
@@ -21,7 +21,6 @@ import java.util.UUID
import com.google.common.net.HostAndPort
import de.heikoseeberger.akkahttpjson4s.Json4sSupport
-import de.heikoseeberger.akkahttpjson4s.Json4sSupport.ShouldWritePretty
import fr.acinq.bitcoin.Crypto.{PrivateKey, PublicKey}
import fr.acinq.bitcoin.{ByteVector32, ByteVector64, OutPoint, Satoshi, Transaction}
import fr.acinq.eclair.channel.{ChannelVersion, State}
@@ -225,8 +224,6 @@ object JsonSupport extends Json4sSupport {
new JavaUUIDSerializer +
new OutgoingPaymentStatusSerializer
- implicit val shouldWritePretty: ShouldWritePretty = ShouldWritePretty.True
-
case class CustomTypeHints(custom: Map[Class[_], String]) extends TypeHints {
val reverse: Map[String, Class[_]] = custom.map(_.swap)
diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/api/Service.scala b/eclair-node/src/main/scala/fr/acinq/eclair/api/Service.scala
similarity index 98%
rename from eclair-core/src/main/scala/fr/acinq/eclair/api/Service.scala
rename to eclair-node/src/main/scala/fr/acinq/eclair/api/Service.scala
index fe252a026..1e12e7f17 100644
--- a/eclair-core/src/main/scala/fr/acinq/eclair/api/Service.scala
+++ b/eclair-node/src/main/scala/fr/acinq/eclair/api/Service.scala
@@ -31,6 +31,7 @@ import akka.stream.scaladsl.{BroadcastHub, Flow, Keep, Source}
import akka.stream.{ActorMaterializer, OverflowStrategy}
import akka.util.Timeout
import com.google.common.net.HostAndPort
+import de.heikoseeberger.akkahttpjson4s.Json4sSupport
import fr.acinq.bitcoin.Crypto.PublicKey
import fr.acinq.bitcoin.{ByteVector32, Satoshi}
import fr.acinq.eclair.api.FormParamExtractors._
@@ -40,7 +41,6 @@ import fr.acinq.eclair.payment.PaymentLifecycle.PaymentFailed
import fr.acinq.eclair.payment.{PaymentReceived, PaymentRequest, _}
import fr.acinq.eclair.{CltvExpiryDelta, Eclair, MilliSatoshi}
import grizzled.slf4j.Logging
-import org.json4s.jackson.Serialization
import scodec.bits.ByteVector
import scala.concurrent.Future
@@ -103,8 +103,8 @@ trait Service extends ExtraDirectives with Logging {
}
def receive: Receive = {
- case message: PaymentFailed => flowInput.offer(Serialization.write(message)(formatsWithTypeHint))
- case message: PaymentEvent => flowInput.offer(Serialization.write(message)(formatsWithTypeHint))
+ case message: PaymentFailed => flowInput.offer(serialization.write(message)(formatsWithTypeHint))
+ case message: PaymentEvent => flowInput.offer(serialization.write(message)(formatsWithTypeHint))
}
}))
diff --git a/eclair-core/src/test/resources/api/close b/eclair-node/src/test/resources/api/close
similarity index 100%
rename from eclair-core/src/test/resources/api/close
rename to eclair-node/src/test/resources/api/close
diff --git a/eclair-core/src/test/resources/api/getinfo b/eclair-node/src/test/resources/api/getinfo
similarity index 100%
rename from eclair-core/src/test/resources/api/getinfo
rename to eclair-node/src/test/resources/api/getinfo
diff --git a/eclair-core/src/test/resources/api/help b/eclair-node/src/test/resources/api/help
similarity index 100%
rename from eclair-core/src/test/resources/api/help
rename to eclair-node/src/test/resources/api/help
diff --git a/eclair-core/src/test/resources/api/peers b/eclair-node/src/test/resources/api/peers
similarity index 100%
rename from eclair-core/src/test/resources/api/peers
rename to eclair-node/src/test/resources/api/peers
diff --git a/eclair-core/src/test/resources/api/usablebalances b/eclair-node/src/test/resources/api/usablebalances
similarity index 100%
rename from eclair-core/src/test/resources/api/usablebalances
rename to eclair-node/src/test/resources/api/usablebalances
diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/api/ApiServiceSpec.scala b/eclair-node/src/test/scala/fr/acinq/eclair/api/ApiServiceSpec.scala
similarity index 91%
rename from eclair-core/src/test/scala/fr/acinq/eclair/api/ApiServiceSpec.scala
rename to eclair-node/src/test/scala/fr/acinq/eclair/api/ApiServiceSpec.scala
index f88f72abc..5b7d26cc0 100644
--- a/eclair-core/src/test/scala/fr/acinq/eclair/api/ApiServiceSpec.scala
+++ b/eclair-node/src/test/scala/fr/acinq/eclair/api/ApiServiceSpec.scala
@@ -25,22 +25,20 @@ import akka.http.scaladsl.model.headers.BasicHttpCredentials
import akka.http.scaladsl.server.Route
import akka.http.scaladsl.testkit.{RouteTestTimeout, ScalatestRouteTest, WSProbe}
import akka.stream.ActorMaterializer
-import akka.util.Timeout
+import akka.util.{ByteString, Timeout}
+import de.heikoseeberger.akkahttpjson4s.Json4sSupport
import fr.acinq.bitcoin.Crypto.PublicKey
import fr.acinq.bitcoin.{ByteVector32, Satoshi}
-import fr.acinq.eclair.TestConstants._
import fr.acinq.eclair._
import fr.acinq.eclair.io.NodeURI
import fr.acinq.eclair.io.Peer.PeerInfo
import fr.acinq.eclair.payment.PaymentLifecycle.PaymentFailed
import fr.acinq.eclair.payment._
import fr.acinq.eclair.wire.NodeAddress
-import org.json4s.jackson.Serialization
import org.mockito.scalatest.IdiomaticMockito
import org.scalatest.{FunSuite, Matchers}
import scodec.bits._
-
-import scala.concurrent.Future
+import scala.concurrent.{Await, Future}
import scala.concurrent.duration._
import scala.io.Source
import scala.reflect.ClassTag
@@ -50,11 +48,11 @@ class ApiServiceSpec extends FunSuite with ScalatestRouteTest with IdiomaticMock
implicit val formats = JsonSupport.formats
implicit val serialization = JsonSupport.serialization
- implicit val marshaller = JsonSupport.marshaller
- implicit val unmarshaller = JsonSupport.unmarshaller
-
implicit val routeTestTimeout = RouteTestTimeout(3 seconds)
+ val aliceNodeId = PublicKey(hex"03af0ed6052cf28d670665549bc86f4b721c9fdb309d40c58f5811f63966e005d0")
+ val bobNodeId = PublicKey(hex"039dc0e0b1d25905e44fdf6f8e89755a5e219685840d0bc1d28d3308f9628a3585")
+
class MockService(eclair: Eclair) extends Service {
override val eclairApi: Eclair = eclair
@@ -100,7 +98,7 @@ class ApiServiceSpec extends FunSuite with ScalatestRouteTest with IdiomaticMock
check {
assert(handled)
assert(status == BadRequest)
- val resp = entityAs[ErrorResponse](JsonSupport.unmarshaller, ClassTag(classOf[ErrorResponse]))
+ val resp = entityAs[ErrorResponse](Json4sSupport.unmarshaller, ClassTag(classOf[ErrorResponse]))
assert(resp.error == "The form field 'channelId' was malformed:\nInvalid hexadecimal character 'h' at index 0")
}
@@ -112,7 +110,6 @@ class ApiServiceSpec extends FunSuite with ScalatestRouteTest with IdiomaticMock
assert(handled)
assert(status == BadRequest)
}
-
}
test("'peers' should ask the switchboard for current known peers") {
@@ -121,12 +118,12 @@ class ApiServiceSpec extends FunSuite with ScalatestRouteTest with IdiomaticMock
val mockService = new MockService(eclair)
eclair.peersInfo()(any[Timeout]) returns Future.successful(List(
PeerInfo(
- nodeId = Alice.nodeParams.nodeId,
+ nodeId = aliceNodeId,
state = "CONNECTED",
- address = Some(Alice.nodeParams.publicAddresses.head.socketAddress),
+ address = Some(NodeAddress.fromParts("localhost", 9731).get.socketAddress),
channels = 1),
PeerInfo(
- nodeId = Bob.nodeParams.nodeId,
+ nodeId = bobNodeId,
state = "DISCONNECTED",
address = None,
channels = 1)))
@@ -148,8 +145,8 @@ class ApiServiceSpec extends FunSuite with ScalatestRouteTest with IdiomaticMock
val eclair = mock[Eclair]
val mockService = new MockService(eclair)
eclair.usableBalances()(any[Timeout]) returns Future.successful(List(
- UsableBalances(canSend = 100000000 msat, canReceive = 20000000 msat, shortChannelId = ShortChannelId(1), remoteNodeId = TestConstants.Alice.keyManager.nodeKey.publicKey, isPublic = true),
- UsableBalances(canSend = 400000000 msat, canReceive = 30000000 msat, shortChannelId = ShortChannelId(2), remoteNodeId = TestConstants.Alice.keyManager.nodeKey.publicKey, isPublic = false)
+ UsableBalances(canSend = 100000000 msat, canReceive = 20000000 msat, shortChannelId = ShortChannelId(1), remoteNodeId = aliceNodeId, isPublic = true),
+ UsableBalances(canSend = 400000000 msat, canReceive = 30000000 msat, shortChannelId = ShortChannelId(2), remoteNodeId = aliceNodeId, isPublic = false)
))
Post("/usablebalances") ~>
@@ -169,9 +166,9 @@ class ApiServiceSpec extends FunSuite with ScalatestRouteTest with IdiomaticMock
val eclair = mock[Eclair]
val mockService = new MockService(eclair)
eclair.getInfoResponse()(any[Timeout]) returns Future.successful(GetInfoResponse(
- nodeId = Alice.nodeParams.nodeId,
- alias = Alice.nodeParams.alias,
- chainHash = Alice.nodeParams.chainHash,
+ nodeId = aliceNodeId,
+ alias = "alice",
+ chainHash = ByteVector32(hex"06226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f"),
blockHeight = 9999,
publicAddresses = NodeAddress.fromParts("localhost", 9731).get :: Nil
))
@@ -183,7 +180,7 @@ class ApiServiceSpec extends FunSuite with ScalatestRouteTest with IdiomaticMock
assert(handled)
assert(status == OK)
val resp = entityAs[String]
- assert(resp.toString.contains(Alice.nodeParams.nodeId.toString))
+ assert(resp.toString.contains(aliceNodeId.toString))
eclair.getInfoResponse()(any[Timeout]).wasCalled(once)
matchTestJson("getinfo", resp)
}
@@ -195,7 +192,7 @@ class ApiServiceSpec extends FunSuite with ScalatestRouteTest with IdiomaticMock
val channelId = "56d7d6eda04d80138270c49709f1eadb5ab4939e5061309ccdacdb98ce637d0e"
val eclair = mock[Eclair]
- eclair.close(any, any)(any[Timeout]) returns Future.successful(Alice.nodeParams.nodeId.toString())
+ eclair.close(any, any)(any[Timeout]) returns Future.successful(aliceNodeId.toString())
val mockService = new MockService(eclair)
Post("/close", FormData("shortChannelId" -> shortChannelIdSerialized).toEntity) ~>
@@ -206,7 +203,7 @@ class ApiServiceSpec extends FunSuite with ScalatestRouteTest with IdiomaticMock
assert(handled)
assert(status == OK)
val resp = entityAs[String]
- assert(resp.contains(Alice.nodeParams.nodeId.toString))
+ assert(resp.contains(aliceNodeId.toString))
eclair.close(Right(ShortChannelId(shortChannelIdSerialized)), None)(any[Timeout]).wasCalled(once)
matchTestJson("close", resp)
}
@@ -219,7 +216,7 @@ class ApiServiceSpec extends FunSuite with ScalatestRouteTest with IdiomaticMock
assert(handled)
assert(status == OK)
val resp = entityAs[String]
- assert(resp.contains(Alice.nodeParams.nodeId.toString))
+ assert(resp.contains(aliceNodeId.toString))
eclair.close(Left(ByteVector32.fromValidHex(channelId)), None)(any[Timeout]).wasCalled(once)
matchTestJson("close", resp)
}
@@ -297,7 +294,7 @@ class ApiServiceSpec extends FunSuite with ScalatestRouteTest with IdiomaticMock
check {
assert(handled)
assert(status == NotFound)
- val resp = entityAs[ErrorResponse](JsonSupport.unmarshaller, ClassTag(classOf[ErrorResponse]))
+ val resp = entityAs[ErrorResponse](Json4sSupport.unmarshaller, ClassTag(classOf[ErrorResponse]))
assert(resp == ErrorResponse("Not found"))
eclair.receivedInfo(ByteVector32.Zeroes)(any[Timeout]).wasCalled(once)
}
@@ -353,31 +350,31 @@ class ApiServiceSpec extends FunSuite with ScalatestRouteTest with IdiomaticMock
val pf = PaymentFailed(fixedUUID, ByteVector32.Zeroes, failures = Seq.empty)
val expectedSerializedPf = """{"type":"payment-failed","id":"487da196-a4dc-4b1e-92b4-3e5e905e9f3f","paymentHash":"0000000000000000000000000000000000000000000000000000000000000000","failures":[]}"""
- Serialization.write(pf)(mockService.formatsWithTypeHint) === expectedSerializedPf
+ serialization.write(pf)(mockService.formatsWithTypeHint) === expectedSerializedPf
system.eventStream.publish(pf)
wsClient.expectMessage(expectedSerializedPf)
val ps = PaymentSent(fixedUUID, amount = 21 msat, feesPaid = 1 msat, paymentHash = ByteVector32.Zeroes, paymentPreimage = ByteVector32.One, toChannelId = ByteVector32.Zeroes, timestamp = 1553784337711L)
val expectedSerializedPs = """{"type":"payment-sent","id":"487da196-a4dc-4b1e-92b4-3e5e905e9f3f","amount":21,"feesPaid":1,"paymentHash":"0000000000000000000000000000000000000000000000000000000000000000","paymentPreimage":"0100000000000000000000000000000000000000000000000000000000000000","toChannelId":"0000000000000000000000000000000000000000000000000000000000000000","timestamp":1553784337711}"""
- Serialization.write(ps)(mockService.formatsWithTypeHint) === expectedSerializedPs
+ serialization.write(ps)(mockService.formatsWithTypeHint) === expectedSerializedPs
system.eventStream.publish(ps)
wsClient.expectMessage(expectedSerializedPs)
val prel = PaymentRelayed(amountIn = 21 msat, amountOut = 20 msat, paymentHash = ByteVector32.Zeroes, fromChannelId = ByteVector32.Zeroes, ByteVector32.One, timestamp = 1553784963659L)
val expectedSerializedPrel = """{"type":"payment-relayed","amountIn":21,"amountOut":20,"paymentHash":"0000000000000000000000000000000000000000000000000000000000000000","fromChannelId":"0000000000000000000000000000000000000000000000000000000000000000","toChannelId":"0100000000000000000000000000000000000000000000000000000000000000","timestamp":1553784963659}"""
- Serialization.write(prel)(mockService.formatsWithTypeHint) === expectedSerializedPrel
+ serialization.write(prel)(mockService.formatsWithTypeHint) === expectedSerializedPrel
system.eventStream.publish(prel)
wsClient.expectMessage(expectedSerializedPrel)
val precv = PaymentReceived(amount = 21 msat, paymentHash = ByteVector32.Zeroes, fromChannelId = ByteVector32.One, timestamp = 1553784963659L)
val expectedSerializedPrecv = """{"type":"payment-received","amount":21,"paymentHash":"0000000000000000000000000000000000000000000000000000000000000000","fromChannelId":"0100000000000000000000000000000000000000000000000000000000000000","timestamp":1553784963659}"""
- Serialization.write(precv)(mockService.formatsWithTypeHint) === expectedSerializedPrecv
+ serialization.write(precv)(mockService.formatsWithTypeHint) === expectedSerializedPrecv
system.eventStream.publish(precv)
wsClient.expectMessage(expectedSerializedPrecv)
val pset = PaymentSettlingOnChain(fixedUUID, amount = 21 msat, paymentHash = ByteVector32.One, timestamp = 1553785442676L)
val expectedSerializedPset = """{"type":"payment-settling-onchain","id":"487da196-a4dc-4b1e-92b4-3e5e905e9f3f","amount":21,"paymentHash":"0100000000000000000000000000000000000000000000000000000000000000","timestamp":1553785442676}"""
- Serialization.write(pset)(mockService.formatsWithTypeHint) === expectedSerializedPset
+ serialization.write(pset)(mockService.formatsWithTypeHint) === expectedSerializedPset
system.eventStream.publish(pset)
wsClient.expectMessage(expectedSerializedPset)
}
diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/api/JsonSerializersSpec.scala b/eclair-node/src/test/scala/fr/acinq/eclair/api/JsonSerializersSpec.scala
similarity index 99%
rename from eclair-core/src/test/scala/fr/acinq/eclair/api/JsonSerializersSpec.scala
rename to eclair-node/src/test/scala/fr/acinq/eclair/api/JsonSerializersSpec.scala
index 16b3542fa..da2169739 100644
--- a/eclair-core/src/test/scala/fr/acinq/eclair/api/JsonSerializersSpec.scala
+++ b/eclair-node/src/test/scala/fr/acinq/eclair/api/JsonSerializersSpec.scala
@@ -21,9 +21,8 @@ import java.util.UUID
import fr.acinq.bitcoin.{ByteVector32, OutPoint, Transaction}
import fr.acinq.eclair._
-import fr.acinq.eclair.payment.{PaymentRequest, PaymentSettlingOnChain}
import fr.acinq.eclair.api.JsonSupport.CustomTypeHints
-import fr.acinq.eclair.payment.PaymentRequest
+import fr.acinq.eclair.payment.{PaymentRequest, PaymentSettlingOnChain}
import fr.acinq.eclair.transactions.{IN, OUT}
import fr.acinq.eclair.wire.{NodeAddress, Tor2, Tor3}
import org.json4s.jackson.Serialization
@@ -84,10 +83,7 @@ class JsonSerializersSpec extends FunSuite with Matchers {
test("transaction serializer") {
implicit val formats = JsonSupport.formats
-
val tx = Transaction.read("0200000001c8a8934fb38a44b969528252bc37be66ee166c7897c57384d1e561449e110c93010000006b483045022100dc6c50f445ed53d2fb41067fdcb25686fe79492d90e6e5db43235726ace247210220773d35228af0800c257970bee9cf75175d75217de09a8ecd83521befd040c4ca012102082b751372fe7e3b012534afe0bb8d1f2f09c724b1a10a813ce704e5b9c217ccfdffffff0247ba2300000000001976a914f97a7641228e6b17d4b0b08252ae75bd62a95fe788ace3de24000000000017a914a9fefd4b9a9282a1d7a17d2f14ac7d1eb88141d287f7d50800")
-
assert(JsonSupport.serialization.write(tx) == "{\"txid\":\"3ef63b5d297c9dcf93f33b45b9f102733c36e8ef61da1ccf2bc132a10584be18\",\"tx\":\"0200000001c8a8934fb38a44b969528252bc37be66ee166c7897c57384d1e561449e110c93010000006b483045022100dc6c50f445ed53d2fb41067fdcb25686fe79492d90e6e5db43235726ace247210220773d35228af0800c257970bee9cf75175d75217de09a8ecd83521befd040c4ca012102082b751372fe7e3b012534afe0bb8d1f2f09c724b1a10a813ce704e5b9c217ccfdffffff0247ba2300000000001976a914f97a7641228e6b17d4b0b08252ae75bd62a95fe788ace3de24000000000017a914a9fefd4b9a9282a1d7a17d2f14ac7d1eb88141d287f7d50800\"}")
-
}
-}
+}
\ No newline at end of file