mirror of
https://github.com/ACINQ/eclair.git
synced 2025-03-04 09:58:02 +01:00
Move http APIs to subproject eclair-node (#1102)
* Move Service and FormParamExtractor to eclair-node * Move dependency akka-http-json4s into eclair-node * Move json serializers to eclair-node
This commit is contained in:
parent
d67ba48fc0
commit
74af0304bd
20 changed files with 263 additions and 113 deletions
|
@ -126,12 +126,6 @@
|
||||||
<artifactId>akka-slf4j_${scala.version.short}</artifactId>
|
<artifactId>akka-slf4j_${scala.version.short}</artifactId>
|
||||||
<version>${akka.version}</version>
|
<version>${akka.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<!-- HTTP SERVER -->
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.typesafe.akka</groupId>
|
|
||||||
<artifactId>akka-http-core_${scala.version.short}</artifactId>
|
|
||||||
<version>${akka.http.version}</version>
|
|
||||||
</dependency>
|
|
||||||
<!-- HTTP CLIENT -->
|
<!-- HTTP CLIENT -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.softwaremill.sttp</groupId>
|
<groupId>com.softwaremill.sttp</groupId>
|
||||||
|
@ -144,11 +138,6 @@
|
||||||
<artifactId>json4s-jackson_${scala.version.short}</artifactId>
|
<artifactId>json4s-jackson_${scala.version.short}</artifactId>
|
||||||
<version>3.6.0</version>
|
<version>3.6.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>de.heikoseeberger</groupId>
|
|
||||||
<artifactId>akka-http-json4s_${scala.version.short}</artifactId>
|
|
||||||
<version>1.19.0</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.softwaremill.sttp</groupId>
|
<groupId>com.softwaremill.sttp</groupId>
|
||||||
<artifactId>json4s_${scala.version.short}</artifactId>
|
<artifactId>json4s_${scala.version.short}</artifactId>
|
||||||
|
@ -254,12 +243,6 @@
|
||||||
<version>1.2.3</version>
|
<version>1.2.3</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>com.typesafe.akka</groupId>
|
|
||||||
<artifactId>akka-http-testkit_${scala.version.short}</artifactId>
|
|
||||||
<version>${akka.http.version}</version>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.mockito</groupId>
|
<groupId>org.mockito</groupId>
|
||||||
<artifactId>mockito-scala-scalatest_2.11</artifactId>
|
<artifactId>mockito-scala-scalatest_2.11</artifactId>
|
||||||
|
|
|
@ -23,15 +23,12 @@ import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
import akka.Done
|
import akka.Done
|
||||||
import akka.actor.{ActorRef, ActorSystem, Props, SupervisorStrategy}
|
import akka.actor.{ActorRef, ActorSystem, Props, SupervisorStrategy}
|
||||||
import akka.http.scaladsl.Http
|
|
||||||
import akka.pattern.after
|
import akka.pattern.after
|
||||||
import akka.stream.{ActorMaterializer, BindFailedException}
|
|
||||||
import akka.util.Timeout
|
import akka.util.Timeout
|
||||||
import com.softwaremill.sttp.okhttp.OkHttpFutureBackend
|
import com.softwaremill.sttp.okhttp.OkHttpFutureBackend
|
||||||
import com.typesafe.config.{Config, ConfigFactory}
|
import com.typesafe.config.{Config, ConfigFactory}
|
||||||
import fr.acinq.bitcoin.{Block, ByteVector32}
|
import fr.acinq.bitcoin.{Block, ByteVector32}
|
||||||
import fr.acinq.eclair.NodeParams.{BITCOIND, ELECTRUM}
|
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.rpc.{BasicBitcoinJsonRPCClient, BatchingBitcoinJsonRPCClient, ExtendedBitcoinClient}
|
||||||
import fr.acinq.eclair.blockchain.bitcoind.zmq.ZMQActor
|
import fr.acinq.eclair.blockchain.bitcoind.zmq.ZMQActor
|
||||||
import fr.acinq.eclair.blockchain.bitcoind.{BitcoinCoreWallet, ZmqWatcher}
|
import fr.acinq.eclair.blockchain.bitcoind.{BitcoinCoreWallet, ZmqWatcher}
|
||||||
|
@ -71,7 +68,6 @@ class Setup(datadir: File,
|
||||||
seed_opt: Option[ByteVector] = None,
|
seed_opt: Option[ByteVector] = None,
|
||||||
db: Option[Databases] = None)(implicit system: ActorSystem) extends Logging {
|
db: Option[Databases] = None)(implicit system: ActorSystem) extends Logging {
|
||||||
|
|
||||||
implicit val materializer = ActorMaterializer()
|
|
||||||
implicit val timeout = Timeout(30 seconds)
|
implicit val timeout = Timeout(30 seconds)
|
||||||
implicit val formats = org.json4s.DefaultFormats
|
implicit val formats = org.json4s.DefaultFormats
|
||||||
implicit val ec = ExecutionContext.Implicits.global
|
implicit val ec = ExecutionContext.Implicits.global
|
||||||
|
@ -287,32 +283,6 @@ class Setup(datadir: File,
|
||||||
_ <- Future.firstCompletedOf(zmqBlockConnected.future :: zmqBlockTimeout :: Nil)
|
_ <- Future.firstCompletedOf(zmqBlockConnected.future :: zmqBlockTimeout :: Nil)
|
||||||
_ <- Future.firstCompletedOf(zmqTxConnected.future :: zmqTxTimeout :: Nil)
|
_ <- Future.firstCompletedOf(zmqTxConnected.future :: zmqTxTimeout :: Nil)
|
||||||
_ <- Future.firstCompletedOf(tcpBound.future :: tcpTimeout :: 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
|
} yield kit
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,6 @@ import akka.actor.ActorRef
|
||||||
import fr.acinq.bitcoin.Crypto.PublicKey
|
import fr.acinq.bitcoin.Crypto.PublicKey
|
||||||
import fr.acinq.bitcoin.{ByteVector32, Satoshi, Transaction}
|
import fr.acinq.bitcoin.{ByteVector32, Satoshi, Transaction}
|
||||||
import fr.acinq.eclair.{MilliSatoshi, ShortChannelId}
|
import fr.acinq.eclair.{MilliSatoshi, ShortChannelId}
|
||||||
import fr.acinq.eclair.api.MilliSatoshiSerializer
|
|
||||||
import fr.acinq.eclair.channel.Channel.ChannelError
|
import fr.acinq.eclair.channel.Channel.ChannelError
|
||||||
import fr.acinq.eclair.channel.Helpers.Closing.ClosingType
|
import fr.acinq.eclair.channel.Helpers.Closing.ClosingType
|
||||||
import fr.acinq.eclair.wire.{ChannelAnnouncement, ChannelUpdate}
|
import fr.acinq.eclair.wire.{ChannelAnnouncement, ChannelUpdate}
|
||||||
|
|
|
@ -16,22 +16,25 @@
|
||||||
|
|
||||||
package fr.acinq.eclair.wire
|
package fr.acinq.eclair.wire
|
||||||
|
|
||||||
|
import java.net.InetSocketAddress
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
import akka.actor.ActorSystem
|
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.DeterministicWallet.KeyPath
|
||||||
import fr.acinq.bitcoin.{Block, ByteVector32, Crypto, DeterministicWallet, OutPoint, Satoshi, Transaction}
|
import fr.acinq.bitcoin.{Block, ByteVector32, ByteVector64, Crypto, DeterministicWallet, OutPoint, Satoshi, Transaction}
|
||||||
import fr.acinq.eclair.api.JsonSupport
|
|
||||||
import fr.acinq.eclair.channel.Helpers.Funding
|
import fr.acinq.eclair.channel.Helpers.Funding
|
||||||
import fr.acinq.eclair.channel._
|
import fr.acinq.eclair.channel._
|
||||||
import fr.acinq.eclair.crypto.{LocalKeyManager, ShaChain}
|
import fr.acinq.eclair.crypto.{LocalKeyManager, ShaChain}
|
||||||
import fr.acinq.eclair.payment.{Local, Relayed}
|
import fr.acinq.eclair.payment.{Local, Relayed}
|
||||||
import fr.acinq.eclair.router.Announcements
|
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.transactions._
|
||||||
import fr.acinq.eclair.wire.ChannelCodecs._
|
import fr.acinq.eclair.wire.ChannelCodecs._
|
||||||
import fr.acinq.eclair.{TestConstants, UInt64, randomBytes, randomBytes32, randomKey, _}
|
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.json4s.jackson.Serialization
|
||||||
import org.scalatest.FunSuite
|
import org.scalatest.FunSuite
|
||||||
import scodec.bits._
|
import scodec.bits._
|
||||||
|
@ -43,8 +46,8 @@ import scala.io.Source
|
||||||
import scala.util.Random
|
import scala.util.Random
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by PM on 31/05/2016.
|
* Created by PM on 31/05/2016.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class ChannelCodecsSpec extends FunSuite {
|
class ChannelCodecsSpec extends FunSuite {
|
||||||
|
|
||||||
|
@ -333,9 +336,7 @@ class ChannelCodecsSpec extends FunSuite {
|
||||||
assert(oldjson === refjson)
|
assert(oldjson === refjson)
|
||||||
assert(newjson === refjson)
|
assert(newjson === refjson)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
object ChannelCodecsSpec {
|
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 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)
|
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
|
||||||
|
}
|
||||||
|
}
|
|
@ -123,11 +123,6 @@ class LightningMessageCodecsSpec extends FunSuite {
|
||||||
case class TestItem(msg: Any, hex: String)
|
case class TestItem(msg: Any, hex: String)
|
||||||
|
|
||||||
test("test vectors for extended channel queries ") {
|
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 = QueryChannelRange(Block.RegtestGenesisBlock.blockId, 100000, 1500, TlvStream.empty)
|
||||||
val query_channel_range_timestamps_checksums = QueryChannelRange(Block.RegtestGenesisBlock.blockId,
|
val query_channel_range_timestamps_checksums = QueryChannelRange(Block.RegtestGenesisBlock.blockId,
|
||||||
35000,
|
35000,
|
||||||
|
|
|
@ -22,7 +22,7 @@ import akka.actor.ActorSystem
|
||||||
import fr.acinq.eclair.gui.{FxApp, FxPreloader}
|
import fr.acinq.eclair.gui.{FxApp, FxPreloader}
|
||||||
import grizzled.slf4j.Logging
|
import grizzled.slf4j.Logging
|
||||||
import javafx.application.Application
|
import javafx.application.Application
|
||||||
|
import scala.concurrent.ExecutionContext.Implicits.global
|
||||||
/**
|
/**
|
||||||
* Created by PM on 25/01/2016.
|
* Created by PM on 25/01/2016.
|
||||||
*/
|
*/
|
||||||
|
@ -33,7 +33,10 @@ object JavafxBoot extends App with Logging {
|
||||||
|
|
||||||
if (headless) {
|
if (headless) {
|
||||||
implicit val system = ActorSystem("eclair-node-gui")
|
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 {
|
} else {
|
||||||
System.setProperty("javafx.preloader", classOf[FxPreloader].getName)
|
System.setProperty("javafx.preloader", classOf[FxPreloader].getName)
|
||||||
Application.launch(classOf[FxApp], datadir.getAbsolutePath)
|
Application.launch(classOf[FxApp], datadir.getAbsolutePath)
|
||||||
|
|
|
@ -104,7 +104,8 @@ class FxApp extends Application with Logging {
|
||||||
system.eventStream.subscribe(guiUpdater, classOf[ElectrumEvent])
|
system.eventStream.subscribe(guiUpdater, classOf[ElectrumEvent])
|
||||||
pKit.completeWith(setup.bootstrap)
|
pKit.completeWith(setup.bootstrap)
|
||||||
pKit.future.onComplete {
|
pKit.future.onComplete {
|
||||||
case Success(_) =>
|
case Success(kit) =>
|
||||||
|
Boot.startApiServiceIfEnabled(setup.config, kit)
|
||||||
Platform.runLater(new Runnable {
|
Platform.runLater(new Runnable {
|
||||||
override def run(): Unit = {
|
override def run(): Unit = {
|
||||||
val scene = new Scene(mainRoot)
|
val scene = new Scene(mainRoot)
|
||||||
|
|
|
@ -91,5 +91,29 @@
|
||||||
<artifactId>janino</artifactId>
|
<artifactId>janino</artifactId>
|
||||||
<version>3.0.7</version>
|
<version>3.0.7</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<!-- http server -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.typesafe.akka</groupId>
|
||||||
|
<artifactId>akka-http-core_${scala.version.short}</artifactId>
|
||||||
|
<version>${akka.http.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>de.heikoseeberger</groupId>
|
||||||
|
<artifactId>akka-http-json4s_${scala.version.short}</artifactId>
|
||||||
|
<version>1.19.0</version>
|
||||||
|
</dependency>
|
||||||
|
<!-- test dependencies -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.typesafe.akka</groupId>
|
||||||
|
<artifactId>akka-http-testkit_${scala.version.short}</artifactId>
|
||||||
|
<version>${akka.http.version}</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.mockito</groupId>
|
||||||
|
<artifactId>mockito-scala-scalatest_2.11</artifactId>
|
||||||
|
<version>1.4.1</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</project>
|
</project>
|
||||||
|
|
|
@ -19,9 +19,12 @@ package fr.acinq.eclair
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
import akka.actor.ActorSystem
|
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 grizzled.slf4j.Logging
|
||||||
|
import scala.concurrent.{Await, ExecutionContext}
|
||||||
import scala.concurrent.ExecutionContext
|
|
||||||
import scala.util.{Failure, Success}
|
import scala.util.{Failure, Success}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -39,13 +42,45 @@ object Boot extends App with Logging {
|
||||||
val setup = new Setup(datadir)
|
val setup = new Setup(datadir)
|
||||||
plugins.foreach(_.onSetup(setup))
|
plugins.foreach(_.onSetup(setup))
|
||||||
setup.bootstrap onComplete {
|
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)
|
case Failure(t) => onError(t)
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
case t: Throwable => onError(t)
|
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 = {
|
def onError(t: Throwable): Unit = {
|
||||||
val errorMsg = if (t.getMessage != null) t.getMessage else t.getClass.getSimpleName
|
val errorMsg = if (t.getMessage != null) t.getMessage else t.getClass.getSimpleName
|
||||||
System.err.println(s"fatal error: $errorMsg")
|
System.err.println(s"fatal error: $errorMsg")
|
||||||
|
|
|
@ -26,7 +26,6 @@ import fr.acinq.eclair.{MilliSatoshi, ShortChannelId}
|
||||||
import fr.acinq.eclair.api.FormParamExtractors.{sha256HashUnmarshaller, shortChannelIdUnmarshaller}
|
import fr.acinq.eclair.api.FormParamExtractors.{sha256HashUnmarshaller, shortChannelIdUnmarshaller}
|
||||||
import fr.acinq.eclair.api.JsonSupport._
|
import fr.acinq.eclair.api.JsonSupport._
|
||||||
import fr.acinq.eclair.payment.PaymentRequest
|
import fr.acinq.eclair.payment.PaymentRequest
|
||||||
|
|
||||||
import scala.concurrent.Future
|
import scala.concurrent.Future
|
||||||
import scala.util.{Failure, Success}
|
import scala.util.{Failure, Success}
|
||||||
|
|
|
@ -18,16 +18,15 @@ package fr.acinq.eclair.api
|
||||||
|
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
import JsonSupport._
|
|
||||||
import akka.http.scaladsl.unmarshalling.Unmarshaller
|
import akka.http.scaladsl.unmarshalling.Unmarshaller
|
||||||
import akka.util.Timeout
|
import akka.util.Timeout
|
||||||
import fr.acinq.bitcoin.{ByteVector32, Satoshi}
|
|
||||||
import fr.acinq.bitcoin.Crypto.PublicKey
|
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.io.NodeURI
|
||||||
import fr.acinq.eclair.payment.PaymentRequest
|
import fr.acinq.eclair.payment.PaymentRequest
|
||||||
|
import fr.acinq.eclair.{MilliSatoshi, ShortChannelId}
|
||||||
import scodec.bits.ByteVector
|
import scodec.bits.ByteVector
|
||||||
|
|
||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
import scala.util.{Failure, Success, Try}
|
import scala.util.{Failure, Success, Try}
|
||||||
|
|
|
@ -21,7 +21,6 @@ import java.util.UUID
|
||||||
|
|
||||||
import com.google.common.net.HostAndPort
|
import com.google.common.net.HostAndPort
|
||||||
import de.heikoseeberger.akkahttpjson4s.Json4sSupport
|
import de.heikoseeberger.akkahttpjson4s.Json4sSupport
|
||||||
import de.heikoseeberger.akkahttpjson4s.Json4sSupport.ShouldWritePretty
|
|
||||||
import fr.acinq.bitcoin.Crypto.{PrivateKey, PublicKey}
|
import fr.acinq.bitcoin.Crypto.{PrivateKey, PublicKey}
|
||||||
import fr.acinq.bitcoin.{ByteVector32, ByteVector64, OutPoint, Satoshi, Transaction}
|
import fr.acinq.bitcoin.{ByteVector32, ByteVector64, OutPoint, Satoshi, Transaction}
|
||||||
import fr.acinq.eclair.channel.{ChannelVersion, State}
|
import fr.acinq.eclair.channel.{ChannelVersion, State}
|
||||||
|
@ -225,8 +224,6 @@ object JsonSupport extends Json4sSupport {
|
||||||
new JavaUUIDSerializer +
|
new JavaUUIDSerializer +
|
||||||
new OutgoingPaymentStatusSerializer
|
new OutgoingPaymentStatusSerializer
|
||||||
|
|
||||||
implicit val shouldWritePretty: ShouldWritePretty = ShouldWritePretty.True
|
|
||||||
|
|
||||||
case class CustomTypeHints(custom: Map[Class[_], String]) extends TypeHints {
|
case class CustomTypeHints(custom: Map[Class[_], String]) extends TypeHints {
|
||||||
val reverse: Map[String, Class[_]] = custom.map(_.swap)
|
val reverse: Map[String, Class[_]] = custom.map(_.swap)
|
||||||
|
|
|
@ -31,6 +31,7 @@ import akka.stream.scaladsl.{BroadcastHub, Flow, Keep, Source}
|
||||||
import akka.stream.{ActorMaterializer, OverflowStrategy}
|
import akka.stream.{ActorMaterializer, OverflowStrategy}
|
||||||
import akka.util.Timeout
|
import akka.util.Timeout
|
||||||
import com.google.common.net.HostAndPort
|
import com.google.common.net.HostAndPort
|
||||||
|
import de.heikoseeberger.akkahttpjson4s.Json4sSupport
|
||||||
import fr.acinq.bitcoin.Crypto.PublicKey
|
import fr.acinq.bitcoin.Crypto.PublicKey
|
||||||
import fr.acinq.bitcoin.{ByteVector32, Satoshi}
|
import fr.acinq.bitcoin.{ByteVector32, Satoshi}
|
||||||
import fr.acinq.eclair.api.FormParamExtractors._
|
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.payment.{PaymentReceived, PaymentRequest, _}
|
||||||
import fr.acinq.eclair.{CltvExpiryDelta, Eclair, MilliSatoshi}
|
import fr.acinq.eclair.{CltvExpiryDelta, Eclair, MilliSatoshi}
|
||||||
import grizzled.slf4j.Logging
|
import grizzled.slf4j.Logging
|
||||||
import org.json4s.jackson.Serialization
|
|
||||||
import scodec.bits.ByteVector
|
import scodec.bits.ByteVector
|
||||||
|
|
||||||
import scala.concurrent.Future
|
import scala.concurrent.Future
|
||||||
|
@ -103,8 +103,8 @@ trait Service extends ExtraDirectives with Logging {
|
||||||
}
|
}
|
||||||
|
|
||||||
def receive: Receive = {
|
def receive: Receive = {
|
||||||
case message: PaymentFailed => flowInput.offer(Serialization.write(message)(formatsWithTypeHint))
|
case message: PaymentFailed => flowInput.offer(serialization.write(message)(formatsWithTypeHint))
|
||||||
case message: PaymentEvent => flowInput.offer(Serialization.write(message)(formatsWithTypeHint))
|
case message: PaymentEvent => flowInput.offer(serialization.write(message)(formatsWithTypeHint))
|
||||||
}
|
}
|
||||||
|
|
||||||
}))
|
}))
|
|
@ -25,22 +25,20 @@ import akka.http.scaladsl.model.headers.BasicHttpCredentials
|
||||||
import akka.http.scaladsl.server.Route
|
import akka.http.scaladsl.server.Route
|
||||||
import akka.http.scaladsl.testkit.{RouteTestTimeout, ScalatestRouteTest, WSProbe}
|
import akka.http.scaladsl.testkit.{RouteTestTimeout, ScalatestRouteTest, WSProbe}
|
||||||
import akka.stream.ActorMaterializer
|
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.Crypto.PublicKey
|
||||||
import fr.acinq.bitcoin.{ByteVector32, Satoshi}
|
import fr.acinq.bitcoin.{ByteVector32, Satoshi}
|
||||||
import fr.acinq.eclair.TestConstants._
|
|
||||||
import fr.acinq.eclair._
|
import fr.acinq.eclair._
|
||||||
import fr.acinq.eclair.io.NodeURI
|
import fr.acinq.eclair.io.NodeURI
|
||||||
import fr.acinq.eclair.io.Peer.PeerInfo
|
import fr.acinq.eclair.io.Peer.PeerInfo
|
||||||
import fr.acinq.eclair.payment.PaymentLifecycle.PaymentFailed
|
import fr.acinq.eclair.payment.PaymentLifecycle.PaymentFailed
|
||||||
import fr.acinq.eclair.payment._
|
import fr.acinq.eclair.payment._
|
||||||
import fr.acinq.eclair.wire.NodeAddress
|
import fr.acinq.eclair.wire.NodeAddress
|
||||||
import org.json4s.jackson.Serialization
|
|
||||||
import org.mockito.scalatest.IdiomaticMockito
|
import org.mockito.scalatest.IdiomaticMockito
|
||||||
import org.scalatest.{FunSuite, Matchers}
|
import org.scalatest.{FunSuite, Matchers}
|
||||||
import scodec.bits._
|
import scodec.bits._
|
||||||
|
import scala.concurrent.{Await, Future}
|
||||||
import scala.concurrent.Future
|
|
||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
import scala.io.Source
|
import scala.io.Source
|
||||||
import scala.reflect.ClassTag
|
import scala.reflect.ClassTag
|
||||||
|
@ -50,11 +48,11 @@ class ApiServiceSpec extends FunSuite with ScalatestRouteTest with IdiomaticMock
|
||||||
|
|
||||||
implicit val formats = JsonSupport.formats
|
implicit val formats = JsonSupport.formats
|
||||||
implicit val serialization = JsonSupport.serialization
|
implicit val serialization = JsonSupport.serialization
|
||||||
implicit val marshaller = JsonSupport.marshaller
|
|
||||||
implicit val unmarshaller = JsonSupport.unmarshaller
|
|
||||||
|
|
||||||
implicit val routeTestTimeout = RouteTestTimeout(3 seconds)
|
implicit val routeTestTimeout = RouteTestTimeout(3 seconds)
|
||||||
|
|
||||||
|
val aliceNodeId = PublicKey(hex"03af0ed6052cf28d670665549bc86f4b721c9fdb309d40c58f5811f63966e005d0")
|
||||||
|
val bobNodeId = PublicKey(hex"039dc0e0b1d25905e44fdf6f8e89755a5e219685840d0bc1d28d3308f9628a3585")
|
||||||
|
|
||||||
class MockService(eclair: Eclair) extends Service {
|
class MockService(eclair: Eclair) extends Service {
|
||||||
override val eclairApi: Eclair = eclair
|
override val eclairApi: Eclair = eclair
|
||||||
|
|
||||||
|
@ -100,7 +98,7 @@ class ApiServiceSpec extends FunSuite with ScalatestRouteTest with IdiomaticMock
|
||||||
check {
|
check {
|
||||||
assert(handled)
|
assert(handled)
|
||||||
assert(status == BadRequest)
|
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")
|
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(handled)
|
||||||
assert(status == BadRequest)
|
assert(status == BadRequest)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
test("'peers' should ask the switchboard for current known peers") {
|
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)
|
val mockService = new MockService(eclair)
|
||||||
eclair.peersInfo()(any[Timeout]) returns Future.successful(List(
|
eclair.peersInfo()(any[Timeout]) returns Future.successful(List(
|
||||||
PeerInfo(
|
PeerInfo(
|
||||||
nodeId = Alice.nodeParams.nodeId,
|
nodeId = aliceNodeId,
|
||||||
state = "CONNECTED",
|
state = "CONNECTED",
|
||||||
address = Some(Alice.nodeParams.publicAddresses.head.socketAddress),
|
address = Some(NodeAddress.fromParts("localhost", 9731).get.socketAddress),
|
||||||
channels = 1),
|
channels = 1),
|
||||||
PeerInfo(
|
PeerInfo(
|
||||||
nodeId = Bob.nodeParams.nodeId,
|
nodeId = bobNodeId,
|
||||||
state = "DISCONNECTED",
|
state = "DISCONNECTED",
|
||||||
address = None,
|
address = None,
|
||||||
channels = 1)))
|
channels = 1)))
|
||||||
|
@ -148,8 +145,8 @@ class ApiServiceSpec extends FunSuite with ScalatestRouteTest with IdiomaticMock
|
||||||
val eclair = mock[Eclair]
|
val eclair = mock[Eclair]
|
||||||
val mockService = new MockService(eclair)
|
val mockService = new MockService(eclair)
|
||||||
eclair.usableBalances()(any[Timeout]) returns Future.successful(List(
|
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 = 100000000 msat, canReceive = 20000000 msat, shortChannelId = ShortChannelId(1), remoteNodeId = aliceNodeId, isPublic = true),
|
||||||
UsableBalances(canSend = 400000000 msat, canReceive = 30000000 msat, shortChannelId = ShortChannelId(2), remoteNodeId = TestConstants.Alice.keyManager.nodeKey.publicKey, isPublic = false)
|
UsableBalances(canSend = 400000000 msat, canReceive = 30000000 msat, shortChannelId = ShortChannelId(2), remoteNodeId = aliceNodeId, isPublic = false)
|
||||||
))
|
))
|
||||||
|
|
||||||
Post("/usablebalances") ~>
|
Post("/usablebalances") ~>
|
||||||
|
@ -169,9 +166,9 @@ class ApiServiceSpec extends FunSuite with ScalatestRouteTest with IdiomaticMock
|
||||||
val eclair = mock[Eclair]
|
val eclair = mock[Eclair]
|
||||||
val mockService = new MockService(eclair)
|
val mockService = new MockService(eclair)
|
||||||
eclair.getInfoResponse()(any[Timeout]) returns Future.successful(GetInfoResponse(
|
eclair.getInfoResponse()(any[Timeout]) returns Future.successful(GetInfoResponse(
|
||||||
nodeId = Alice.nodeParams.nodeId,
|
nodeId = aliceNodeId,
|
||||||
alias = Alice.nodeParams.alias,
|
alias = "alice",
|
||||||
chainHash = Alice.nodeParams.chainHash,
|
chainHash = ByteVector32(hex"06226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f"),
|
||||||
blockHeight = 9999,
|
blockHeight = 9999,
|
||||||
publicAddresses = NodeAddress.fromParts("localhost", 9731).get :: Nil
|
publicAddresses = NodeAddress.fromParts("localhost", 9731).get :: Nil
|
||||||
))
|
))
|
||||||
|
@ -183,7 +180,7 @@ class ApiServiceSpec extends FunSuite with ScalatestRouteTest with IdiomaticMock
|
||||||
assert(handled)
|
assert(handled)
|
||||||
assert(status == OK)
|
assert(status == OK)
|
||||||
val resp = entityAs[String]
|
val resp = entityAs[String]
|
||||||
assert(resp.toString.contains(Alice.nodeParams.nodeId.toString))
|
assert(resp.toString.contains(aliceNodeId.toString))
|
||||||
eclair.getInfoResponse()(any[Timeout]).wasCalled(once)
|
eclair.getInfoResponse()(any[Timeout]).wasCalled(once)
|
||||||
matchTestJson("getinfo", resp)
|
matchTestJson("getinfo", resp)
|
||||||
}
|
}
|
||||||
|
@ -195,7 +192,7 @@ class ApiServiceSpec extends FunSuite with ScalatestRouteTest with IdiomaticMock
|
||||||
val channelId = "56d7d6eda04d80138270c49709f1eadb5ab4939e5061309ccdacdb98ce637d0e"
|
val channelId = "56d7d6eda04d80138270c49709f1eadb5ab4939e5061309ccdacdb98ce637d0e"
|
||||||
|
|
||||||
val eclair = mock[Eclair]
|
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)
|
val mockService = new MockService(eclair)
|
||||||
|
|
||||||
Post("/close", FormData("shortChannelId" -> shortChannelIdSerialized).toEntity) ~>
|
Post("/close", FormData("shortChannelId" -> shortChannelIdSerialized).toEntity) ~>
|
||||||
|
@ -206,7 +203,7 @@ class ApiServiceSpec extends FunSuite with ScalatestRouteTest with IdiomaticMock
|
||||||
assert(handled)
|
assert(handled)
|
||||||
assert(status == OK)
|
assert(status == OK)
|
||||||
val resp = entityAs[String]
|
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)
|
eclair.close(Right(ShortChannelId(shortChannelIdSerialized)), None)(any[Timeout]).wasCalled(once)
|
||||||
matchTestJson("close", resp)
|
matchTestJson("close", resp)
|
||||||
}
|
}
|
||||||
|
@ -219,7 +216,7 @@ class ApiServiceSpec extends FunSuite with ScalatestRouteTest with IdiomaticMock
|
||||||
assert(handled)
|
assert(handled)
|
||||||
assert(status == OK)
|
assert(status == OK)
|
||||||
val resp = entityAs[String]
|
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)
|
eclair.close(Left(ByteVector32.fromValidHex(channelId)), None)(any[Timeout]).wasCalled(once)
|
||||||
matchTestJson("close", resp)
|
matchTestJson("close", resp)
|
||||||
}
|
}
|
||||||
|
@ -297,7 +294,7 @@ class ApiServiceSpec extends FunSuite with ScalatestRouteTest with IdiomaticMock
|
||||||
check {
|
check {
|
||||||
assert(handled)
|
assert(handled)
|
||||||
assert(status == NotFound)
|
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"))
|
assert(resp == ErrorResponse("Not found"))
|
||||||
eclair.receivedInfo(ByteVector32.Zeroes)(any[Timeout]).wasCalled(once)
|
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 pf = PaymentFailed(fixedUUID, ByteVector32.Zeroes, failures = Seq.empty)
|
||||||
val expectedSerializedPf = """{"type":"payment-failed","id":"487da196-a4dc-4b1e-92b4-3e5e905e9f3f","paymentHash":"0000000000000000000000000000000000000000000000000000000000000000","failures":[]}"""
|
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)
|
system.eventStream.publish(pf)
|
||||||
wsClient.expectMessage(expectedSerializedPf)
|
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 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}"""
|
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)
|
system.eventStream.publish(ps)
|
||||||
wsClient.expectMessage(expectedSerializedPs)
|
wsClient.expectMessage(expectedSerializedPs)
|
||||||
|
|
||||||
val prel = PaymentRelayed(amountIn = 21 msat, amountOut = 20 msat, paymentHash = ByteVector32.Zeroes, fromChannelId = ByteVector32.Zeroes, ByteVector32.One, timestamp = 1553784963659L)
|
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}"""
|
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)
|
system.eventStream.publish(prel)
|
||||||
wsClient.expectMessage(expectedSerializedPrel)
|
wsClient.expectMessage(expectedSerializedPrel)
|
||||||
|
|
||||||
val precv = PaymentReceived(amount = 21 msat, paymentHash = ByteVector32.Zeroes, fromChannelId = ByteVector32.One, timestamp = 1553784963659L)
|
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}"""
|
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)
|
system.eventStream.publish(precv)
|
||||||
wsClient.expectMessage(expectedSerializedPrecv)
|
wsClient.expectMessage(expectedSerializedPrecv)
|
||||||
|
|
||||||
val pset = PaymentSettlingOnChain(fixedUUID, amount = 21 msat, paymentHash = ByteVector32.One, timestamp = 1553785442676L)
|
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}"""
|
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)
|
system.eventStream.publish(pset)
|
||||||
wsClient.expectMessage(expectedSerializedPset)
|
wsClient.expectMessage(expectedSerializedPset)
|
||||||
}
|
}
|
|
@ -21,9 +21,8 @@ import java.util.UUID
|
||||||
|
|
||||||
import fr.acinq.bitcoin.{ByteVector32, OutPoint, Transaction}
|
import fr.acinq.bitcoin.{ByteVector32, OutPoint, Transaction}
|
||||||
import fr.acinq.eclair._
|
import fr.acinq.eclair._
|
||||||
import fr.acinq.eclair.payment.{PaymentRequest, PaymentSettlingOnChain}
|
|
||||||
import fr.acinq.eclair.api.JsonSupport.CustomTypeHints
|
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.transactions.{IN, OUT}
|
||||||
import fr.acinq.eclair.wire.{NodeAddress, Tor2, Tor3}
|
import fr.acinq.eclair.wire.{NodeAddress, Tor2, Tor3}
|
||||||
import org.json4s.jackson.Serialization
|
import org.json4s.jackson.Serialization
|
||||||
|
@ -84,10 +83,7 @@ class JsonSerializersSpec extends FunSuite with Matchers {
|
||||||
|
|
||||||
test("transaction serializer") {
|
test("transaction serializer") {
|
||||||
implicit val formats = JsonSupport.formats
|
implicit val formats = JsonSupport.formats
|
||||||
|
|
||||||
val tx = Transaction.read("0200000001c8a8934fb38a44b969528252bc37be66ee166c7897c57384d1e561449e110c93010000006b483045022100dc6c50f445ed53d2fb41067fdcb25686fe79492d90e6e5db43235726ace247210220773d35228af0800c257970bee9cf75175d75217de09a8ecd83521befd040c4ca012102082b751372fe7e3b012534afe0bb8d1f2f09c724b1a10a813ce704e5b9c217ccfdffffff0247ba2300000000001976a914f97a7641228e6b17d4b0b08252ae75bd62a95fe788ace3de24000000000017a914a9fefd4b9a9282a1d7a17d2f14ac7d1eb88141d287f7d50800")
|
val tx = Transaction.read("0200000001c8a8934fb38a44b969528252bc37be66ee166c7897c57384d1e561449e110c93010000006b483045022100dc6c50f445ed53d2fb41067fdcb25686fe79492d90e6e5db43235726ace247210220773d35228af0800c257970bee9cf75175d75217de09a8ecd83521befd040c4ca012102082b751372fe7e3b012534afe0bb8d1f2f09c724b1a10a813ce704e5b9c217ccfdffffff0247ba2300000000001976a914f97a7641228e6b17d4b0b08252ae75bd62a95fe788ace3de24000000000017a914a9fefd4b9a9282a1d7a17d2f14ac7d1eb88141d287f7d50800")
|
||||||
|
|
||||||
assert(JsonSupport.serialization.write(tx) == "{\"txid\":\"3ef63b5d297c9dcf93f33b45b9f102733c36e8ef61da1ccf2bc132a10584be18\",\"tx\":\"0200000001c8a8934fb38a44b969528252bc37be66ee166c7897c57384d1e561449e110c93010000006b483045022100dc6c50f445ed53d2fb41067fdcb25686fe79492d90e6e5db43235726ace247210220773d35228af0800c257970bee9cf75175d75217de09a8ecd83521befd040c4ca012102082b751372fe7e3b012534afe0bb8d1f2f09c724b1a10a813ce704e5b9c217ccfdffffff0247ba2300000000001976a914f97a7641228e6b17d4b0b08252ae75bd62a95fe788ace3de24000000000017a914a9fefd4b9a9282a1d7a17d2f14ac7d1eb88141d287f7d50800\"}")
|
assert(JsonSupport.serialization.write(tx) == "{\"txid\":\"3ef63b5d297c9dcf93f33b45b9f102733c36e8ef61da1ccf2bc132a10584be18\",\"tx\":\"0200000001c8a8934fb38a44b969528252bc37be66ee166c7897c57384d1e561449e110c93010000006b483045022100dc6c50f445ed53d2fb41067fdcb25686fe79492d90e6e5db43235726ace247210220773d35228af0800c257970bee9cf75175d75217de09a8ecd83521befd040c4ca012102082b751372fe7e3b012534afe0bb8d1f2f09c724b1a10a813ce704e5b9c217ccfdffffff0247ba2300000000001976a914f97a7641228e6b17d4b0b08252ae75bd62a95fe788ace3de24000000000017a914a9fefd4b9a9282a1d7a17d2f14ac7d1eb88141d287f7d50800\"}")
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Add table
Reference in a new issue