diff --git a/eclair-core/pom.xml b/eclair-core/pom.xml
index 07ded9311..5172a2eb0 100644
--- a/eclair-core/pom.xml
+++ b/eclair-core/pom.xml
@@ -142,6 +142,11 @@
akka-http-json4s_${scala.version.short}
1.19.0
+
+ com.lihaoyi
+ upickle_2.11
+ 0.6.5
+
fr.acinq
@@ -205,12 +210,6 @@
${guava.version}
-
- com.lihaoyi
- upickle_2.11
- 0.6.5
- test
-
com.typesafe.akka
akka-testkit_${scala.version.short}
diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/JsonSerializers.scala b/eclair-core/src/main/scala/fr/acinq/eclair/JsonSerializers.scala
new file mode 100644
index 000000000..4a3640f81
--- /dev/null
+++ b/eclair-core/src/main/scala/fr/acinq/eclair/JsonSerializers.scala
@@ -0,0 +1,109 @@
+package fr.acinq.eclair
+
+import akka.actor.ActorRef
+import com.google.common.net.HostAndPort
+import fr.acinq.bitcoin.Crypto.{Point, PublicKey}
+import fr.acinq.bitcoin.{BinaryData, DeterministicWallet, OutPoint, Satoshi, Transaction, TxOut}
+import fr.acinq.eclair.channel._
+import fr.acinq.eclair.crypto.ShaChain
+import fr.acinq.eclair.payment.Origin
+import fr.acinq.eclair.transactions.Transactions._
+import fr.acinq.eclair.transactions._
+import fr.acinq.eclair.wire.{AcceptChannel, ChannelAnnouncement, ChannelUpdate, ClosingSigned, CommitSig, FundingCreated, FundingLocked, FundingSigned, IPv4, IPv6, Init, NodeAddress, OpenChannel, Padding, Shutdown, Tor2, Tor3, UpdateAddHtlc, UpdateFailHtlc, UpdateMessage}
+
+object JsonSerializers {
+
+ import upickle.default._
+
+ implicit val txReadWrite: ReadWriter[Transaction] = readwriter[String].bimap[Transaction](_.toString(), Transaction.read(_))
+ implicit val outpointReadWrite: ReadWriter[OutPoint] = readwriter[String].bimap[OutPoint](op => s"${op.hash}:${op.index}", s => ???)
+ implicit val publicKeyReadWriter: ReadWriter[PublicKey] = readwriter[String].bimap[PublicKey](_.toString(), s => PublicKey(BinaryData(s)))
+ implicit val pointReadWriter: ReadWriter[Point] = readwriter[String].bimap[Point](_.toString(), s => Point(BinaryData(s)))
+ implicit val keyPathReadWriter: ReadWriter[DeterministicWallet.KeyPath] = readwriter[String].bimap[DeterministicWallet.KeyPath](_.toString(), _ => DeterministicWallet.KeyPath(0L :: Nil))
+ implicit val binarydataReadWriter: ReadWriter[BinaryData] = readwriter[String].bimap[BinaryData](_.toString(), s => BinaryData(s))
+ implicit val uint64ReadWriter: ReadWriter[UInt64] = readwriter[String].bimap[UInt64](_.toString, s => UInt64(s.toLong))
+ implicit val localParamsReadWriter: ReadWriter[LocalParams] = macroRW
+ implicit val remoteParamsReadWriter: ReadWriter[RemoteParams] = macroRW
+ implicit val directionReadWriter: ReadWriter[Direction] = readwriter[String].bimap[Direction](f => f match {
+ case IN =>
+ "INNN"
+ case OUT =>
+ "OUTTT"
+ }, _ match {
+ case "IN" => IN
+ case "OUT" => OUT
+ })
+ implicit val updateAddHtlcReadWriter: ReadWriter[UpdateAddHtlc] = macroRW
+ implicit val updateFailHtlcReadWriter: ReadWriter[UpdateFailHtlc] = macroRW
+ implicit val updateMessageReadWriter: ReadWriter[UpdateMessage] = ReadWriter.merge(macroRW[UpdateAddHtlc], macroRW[UpdateFailHtlc])
+ implicit val directeddHtlcReadWriter: ReadWriter[DirectedHtlc] = macroRW
+ implicit val commitmentSpecReadWriter: ReadWriter[CommitmentSpec] = macroRW
+ implicit val localChangesReadWriter: ReadWriter[LocalChanges] = macroRW
+ implicit val satoshiReadWriter: ReadWriter[Satoshi] = macroRW
+ implicit val txOutReadWriter: ReadWriter[TxOut] = macroRW
+ implicit val inputInfoReadWriter: ReadWriter[InputInfo] = macroRW
+ implicit val transactionWithInputInfoReadWriter: ReadWriter[TransactionWithInputInfo] = ReadWriter.merge(
+ macroRW[CommitTx], macroRW[HtlcSuccessTx], macroRW[HtlcTimeoutTx], macroRW[ClaimHtlcSuccessTx], macroRW[ClaimHtlcTimeoutTx],
+ macroRW[ClaimP2WPKHOutputTx], macroRW[ClaimDelayedOutputTx], macroRW[ClaimDelayedOutputPenaltyTx], macroRW[MainPenaltyTx], macroRW[HtlcPenaltyTx], macroRW[ClosingTx])
+ implicit val hlcTxAndSigsReadWriter: ReadWriter[HtlcTxAndSigs] = macroRW
+ implicit val commitTxReadWriter: ReadWriter[CommitTx] = macroRW
+ implicit val publishableTxsReadWriter: ReadWriter[PublishableTxs] = macroRW
+ implicit val localCommitReadWriter: ReadWriter[LocalCommit] = macroRW
+ implicit val remoteCommitsReadWriter: ReadWriter[RemoteCommit] = macroRW
+ implicit val commitSgReadWriter: ReadWriter[CommitSig] = macroRW
+ implicit val waitingForRevocationReadWriter: ReadWriter[WaitingForRevocation] = macroRW
+ implicit val paymentOriginReadWriter: ReadWriter[Origin] = readwriter[String].bimap[Origin](_.toString, _ => fr.acinq.eclair.payment.Local(None))
+ implicit val remoteChangesReadWriter: ReadWriter[RemoteChanges] = macroRW
+ implicit val shaChainReadWriter: ReadWriter[ShaChain] = macroRW
+ implicit val commitmentsReadWriter: ReadWriter[Commitments] = macroRW
+ implicit val actorRefReadWriter: ReadWriter[ActorRef] = readwriter[String].bimap[ActorRef](_.toString, _ => ActorRef.noSender)
+ implicit val shortChannelIdReadWriter: ReadWriter[ShortChannelId] = readwriter[String].bimap[ShortChannelId](_.toString, s => ShortChannelId(s))
+
+ implicit val initReadWriter: ReadWriter[Init] = macroRW
+ implicit val openChannelReadWriter: ReadWriter[OpenChannel] = macroRW
+ implicit val acceptChannelReadWriter: ReadWriter[AcceptChannel] = macroRW
+ implicit val fundingCreatedReadWriter: ReadWriter[FundingCreated] = macroRW
+ implicit val fundingLockedReadWriter: ReadWriter[FundingLocked] = macroRW
+ implicit val fundingSignedReadWriter: ReadWriter[FundingSigned] = macroRW
+ implicit val channelAnnouncementReadWriter: ReadWriter[ChannelAnnouncement] = macroRW
+ implicit val channelUpdateReadWriter: ReadWriter[ChannelUpdate] = macroRW
+ implicit val shutdownReadWriter: ReadWriter[Shutdown] = macroRW
+ implicit val closingSigndeReadWriter: ReadWriter[ClosingSigned] = macroRW
+ implicit val nodeAddressReadWriter: ReadWriter[NodeAddress] = readwriter[String].bimap[NodeAddress](_ match {
+ case IPv4(a, p) => HostAndPort.fromParts(a.getHostAddress, p).toString
+ case IPv6(a, p) => HostAndPort.fromParts(a.getHostAddress, p).toString
+ case Tor2(b, p) => s"${b.toString}:$p"
+ case Tor3(b, p) => s"${b.toString}:$p"
+ case Padding => ""
+ }, s => null)
+
+ implicit val inputInitFunderReadWriter: ReadWriter[INPUT_INIT_FUNDER] = macroRW
+ implicit val inputInitFundeeReadWriter: ReadWriter[INPUT_INIT_FUNDEE] = macroRW
+
+ implicit val dataWaitForAcceptChannelReadWriter: ReadWriter[DATA_WAIT_FOR_ACCEPT_CHANNEL] = macroRW
+ implicit val dataWaitForOpenChannelReadWriter: ReadWriter[DATA_WAIT_FOR_OPEN_CHANNEL] = macroRW
+ implicit val dataWaitForFundingCreatedReadWriter: ReadWriter[DATA_WAIT_FOR_FUNDING_CREATED] = macroRW
+ implicit val dataWaitForFundingInternalReadWriter: ReadWriter[DATA_WAIT_FOR_FUNDING_INTERNAL] = macroRW
+ implicit val dataWaitForFundingSignedReadWriter: ReadWriter[DATA_WAIT_FOR_FUNDING_SIGNED] = macroRW
+ implicit val dataWaitForFundingConfirmedReadWriter: ReadWriter[DATA_WAIT_FOR_FUNDING_CONFIRMED] = macroRW
+ implicit val dataWaitForFundingLockedReadWriter: ReadWriter[DATA_WAIT_FOR_FUNDING_LOCKED] = macroRW
+ implicit val dataNormalReadWriter: ReadWriter[DATA_NORMAL] = macroRW
+ implicit val dataShutdownReadWriter: ReadWriter[DATA_SHUTDOWN] = macroRW
+ implicit val dataNegociatingReadWriter: ReadWriter[DATA_NEGOTIATING] = macroRW
+ implicit val dataClosingReadWriter: ReadWriter[DATA_CLOSING] = macroRW
+ implicit val closingTxProposedReadWriter: ReadWriter[ClosingTxProposed] = macroRW
+ implicit val localCommitPublishedReadWriter: ReadWriter[LocalCommitPublished] = macroRW
+ implicit val remoteCommitPublishedReadWriter: ReadWriter[RemoteCommitPublished] = macroRW
+ implicit val revokedCommitPublishedReadWriter: ReadWriter[RevokedCommitPublished] = macroRW
+ implicit val channelStateReadWriter: ReadWriter[fr.acinq.eclair.channel.State] = readwriter[String].bimap[fr.acinq.eclair.channel.State](_.toString, _ match {
+ case "NORMAL" => NORMAL
+ case "CLOSING" => CLOSING
+ })
+ implicit val channelDataReadWriter: ReadWriter[fr.acinq.eclair.channel.Data] = ReadWriter.merge(
+ macroRW[DATA_WAIT_FOR_ACCEPT_CHANNEL], macroRW[DATA_WAIT_FOR_FUNDING_CREATED], macroRW[DATA_WAIT_FOR_FUNDING_INTERNAL], macroRW[DATA_WAIT_FOR_FUNDING_SIGNED], macroRW[DATA_WAIT_FOR_FUNDING_LOCKED], macroRW[DATA_WAIT_FOR_FUNDING_CONFIRMED],
+ macroRW[DATA_NORMAL],
+ macroRW[DATA_SHUTDOWN], macroRW[DATA_NEGOTIATING], macroRW[DATA_CLOSING]
+ )
+ implicit val cmdResGetinfoReadWriter: ReadWriter[RES_GETINFO] = macroRW
+
+}
diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/JsonSerializersSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/JsonSerializersSpec.scala
new file mode 100644
index 000000000..500563fdc
--- /dev/null
+++ b/eclair-core/src/test/scala/fr/acinq/eclair/JsonSerializersSpec.scala
@@ -0,0 +1,105 @@
+package fr.acinq.eclair
+
+import java.net.{InetAddress, InetSocketAddress}
+
+import fr.acinq.bitcoin.{BinaryData, DeterministicWallet, OutPoint}
+import fr.acinq.eclair.channel.{LocalChanges, LocalParams, RemoteParams}
+import fr.acinq.eclair.db.ChannelStateSpec
+import fr.acinq.eclair.transactions._
+import fr.acinq.eclair.wire.{NodeAddress, UpdateAddHtlc, UpdateFailHtlc}
+import org.scalatest.FunSuite
+import upickle.default.write
+
+import scala.util.Random
+
+class JsonSerializersSpec extends FunSuite {
+ import JsonSerializers._
+
+ test("deserialize Map[OutPoint, BinaryData]") {
+ val output1 = OutPoint("11418a2d282a40461966e4f578e1fdf633ad15c1b7fb3e771d14361127233be1", 0)
+ val output2 = OutPoint("3d62bd4f71dc63798418e59efbc7532380c900b5e79db3a5521374b161dd0e33", 1)
+
+
+ val map = Map(
+ output1 -> BinaryData("dead"),
+ output2 -> BinaryData("beef")
+ )
+ val json = write(map)
+ assert(json === s"""{"${output1.txid}:0":"dead","${output2.txid}:1":"beef"}""")
+ }
+
+ test("NodeAddress serialization") {
+ val ipv4 = NodeAddress(new InetSocketAddress(InetAddress.getByAddress(Array(10, 0, 0, 1)), 8888))
+ val ipv6LocalHost = NodeAddress(new InetSocketAddress(InetAddress.getByAddress(Array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1)), 9735))
+
+ assert(write(ipv4) === s""""10.0.0.1:8888"""")
+ assert(write(ipv6LocalHost) === s""""[0:0:0:0:0:0:0:1]:9735"""")
+ }
+
+ test("Direction serialization") {
+ assert(write(IN) === s""""IN"""")
+ assert(write(OUT) === s""""OUT"""")
+ }
+
+ test("serialize LocalParams") {
+ val localParams = LocalParams(
+ nodeId = randomKey.publicKey,
+ channelKeyPath = DeterministicWallet.KeyPath(Seq(42L)),
+ dustLimitSatoshis = Random.nextInt(Int.MaxValue),
+ maxHtlcValueInFlightMsat = UInt64(Random.nextInt(Int.MaxValue)),
+ channelReserveSatoshis = Random.nextInt(Int.MaxValue),
+ htlcMinimumMsat = Random.nextInt(Int.MaxValue),
+ toSelfDelay = Random.nextInt(Short.MaxValue),
+ maxAcceptedHtlcs = Random.nextInt(Short.MaxValue),
+ defaultFinalScriptPubKey = randomBytes(10 + Random.nextInt(200)),
+ isFunder = Random.nextBoolean(),
+ globalFeatures = randomBytes(256),
+ localFeatures = randomBytes(256))
+
+ println(write(localParams))
+
+ }
+
+ test("serialize RemoteParams") {
+ val remoteParams = RemoteParams(
+ nodeId = randomKey.publicKey,
+ dustLimitSatoshis = Random.nextInt(Int.MaxValue),
+ maxHtlcValueInFlightMsat = UInt64(Random.nextInt(Int.MaxValue)),
+ channelReserveSatoshis = Random.nextInt(Int.MaxValue),
+ htlcMinimumMsat = Random.nextInt(Int.MaxValue),
+ toSelfDelay = Random.nextInt(Short.MaxValue),
+ maxAcceptedHtlcs = Random.nextInt(Short.MaxValue),
+ fundingPubKey = randomKey.publicKey,
+ revocationBasepoint = randomKey.publicKey.value,
+ paymentBasepoint = randomKey.publicKey.value,
+ delayedPaymentBasepoint = randomKey.publicKey.value,
+ htlcBasepoint = randomKey.publicKey.value,
+ globalFeatures = randomBytes(256),
+ localFeatures = randomBytes(256))
+
+ println(write(remoteParams))
+ }
+
+ test("serialize CommitmentSpec") {
+ val spec = CommitmentSpec(Set(DirectedHtlc(IN, UpdateAddHtlc(randomKey.publicKey.value.toBin(true), 421, 1245, randomBytes(32), 1000, BinaryData("010101")))), feeratePerKw = 1233, toLocalMsat = 100, toRemoteMsat = 200)
+ println(write(spec))
+ }
+
+ test("serialize LocalChanges") {
+ val channelId = randomKey.publicKey.value.toBin(true)
+ val add = UpdateAddHtlc(channelId, 421, 1245, randomBytes(32), 1000, BinaryData("010101"))
+ val fail = UpdateFailHtlc(channelId, 42, BinaryData("0101"))
+ val localChanges = LocalChanges(proposed = add :: add :: fail :: Nil, signed = add :: Nil, acked = fail :: fail :: Nil)
+ println(write(localChanges))
+ }
+
+ test("serialize Commitments") {
+ val commitments = ChannelStateSpec.commitments
+ println(write(commitments))
+ }
+
+ test("serialize DATA_NORMAL") {
+ val data = ChannelStateSpec.normal
+ println(write(data))
+ }
+}