diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/api/JsonSerializers.scala b/eclair-core/src/main/scala/fr/acinq/eclair/api/JsonSerializers.scala index e2b0d5e7e..97353c824 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/api/JsonSerializers.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/api/JsonSerializers.scala @@ -21,13 +21,13 @@ import java.net.InetSocketAddress import com.google.common.net.HostAndPort import fr.acinq.bitcoin.Crypto.{Point, PrivateKey, PublicKey, Scalar} import fr.acinq.bitcoin.{BinaryData, OutPoint, Transaction} -import fr.acinq.eclair.{ShortChannelId, UInt64} import fr.acinq.eclair.channel.State import fr.acinq.eclair.crypto.ShaChain import fr.acinq.eclair.router.RouteResponse import fr.acinq.eclair.transactions.Transactions.{InputInfo, TransactionWithInputInfo} -import fr.acinq.eclair.wire.Color -import org.json4s.JsonAST.{JArray, JInt, JNull, JObject, JString} +import fr.acinq.eclair.wire.{Color, FailureMessage} +import fr.acinq.eclair.{ShortChannelId, UInt64} +import org.json4s.JsonAST._ import org.json4s.{CustomKeySerializer, CustomSerializer} /** @@ -106,3 +106,12 @@ class RouteResponseSerializer extends CustomSerializer[RouteResponse](format => } JArray(nodeIds.toList.map(n => JString(n.toString))) })) + +class ThrowableSerializer extends CustomSerializer[Throwable](format => ({ null }, { + case t: Throwable if t.getMessage != null => JString(t.getMessage) + case t: Throwable => JString(t.getClass.getSimpleName) +})) + +class FailureMessageSerializer extends CustomSerializer[FailureMessage](format => ({ null }, { + case m: FailureMessage => JString(m.message) +})) diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/api/Service.scala b/eclair-core/src/main/scala/fr/acinq/eclair/api/Service.scala index 5d2ddde10..84d926dc0 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/api/Service.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/api/Service.scala @@ -34,9 +34,9 @@ import fr.acinq.bitcoin.{BinaryData, MilliSatoshi, Satoshi} import fr.acinq.eclair.channel._ import fr.acinq.eclair.io.Peer.{GetPeerInfo, PeerInfo} import fr.acinq.eclair.io.{NodeURI, Peer} -import fr.acinq.eclair.payment.PaymentLifecycle.{CheckPayment, PaymentResult, ReceivePayment, SendPayment} -import fr.acinq.eclair.payment.PaymentRequest -import fr.acinq.eclair.router.{ChannelDesc,RouteRequest,RouteResponse} +import fr.acinq.eclair.payment.PaymentLifecycle._ +import fr.acinq.eclair.payment.{PaymentLifecycle, PaymentRequest} +import fr.acinq.eclair.router.{ChannelDesc, RouteRequest, RouteResponse} import fr.acinq.eclair.wire.{ChannelAnnouncement, ChannelUpdate, NodeAnnouncement} import fr.acinq.eclair.{Kit, ShortChannelId, feerateByte2Kw} import grizzled.slf4j.Logging @@ -70,7 +70,7 @@ trait Service extends Logging { def scheduler: Scheduler implicit val serialization = jackson.Serialization - implicit val formats = org.json4s.DefaultFormats + new BinaryDataSerializer + new UInt64Serializer + new ShortChannelIdSerializer + new StateSerializer + new ShaChainSerializer + new PublicKeySerializer + new PrivateKeySerializer + new ScalarSerializer + new PointSerializer + new TransactionSerializer + new TransactionWithInputInfoSerializer + new InetSocketAddressSerializer + new OutPointSerializer + new OutPointKeySerializer + new InputInfoSerializer + new ColorSerializer + new RouteResponseSerializer + implicit val formats = org.json4s.DefaultFormats + new BinaryDataSerializer + new UInt64Serializer + new ShortChannelIdSerializer + new StateSerializer + new ShaChainSerializer + new PublicKeySerializer + new PrivateKeySerializer + new ScalarSerializer + new PointSerializer + new TransactionSerializer + new TransactionWithInputInfoSerializer + new InetSocketAddressSerializer + new OutPointSerializer + new OutPointKeySerializer + new InputInfoSerializer + new ColorSerializer + new RouteResponseSerializer + new ThrowableSerializer + new FailureMessageSerializer implicit val timeout = Timeout(60 seconds) implicit val shouldWritePretty: ShouldWritePretty = ShouldWritePretty.True @@ -246,7 +246,11 @@ trait Service extends Logging { // user manually sets the payment information case JInt(amountMsat) :: JString(paymentHash) :: JString(nodeId) :: Nil => (Try(BinaryData(paymentHash)), Try(PublicKey(nodeId))) match { - case (Success(ph), Success(pk)) => completeRpcFuture(req.id, (paymentInitiator ? SendPayment(amountMsat.toLong, ph, pk, maxFeePct = nodeParams.maxPaymentFee)).mapTo[PaymentResult]) + case (Success(ph), Success(pk)) => completeRpcFuture(req.id, (paymentInitiator ? + SendPayment(amountMsat.toLong, ph, pk, maxFeePct = nodeParams.maxPaymentFee)).mapTo[PaymentResult].map { + case s: PaymentSucceeded => s + case f: PaymentFailed => f.copy(failures = PaymentLifecycle.transformForUser(f.failures)) + }) case (Failure(_), _) => reject(RpcValidationRejection(req.id, s"invalid payment hash '$paymentHash'")) case _ => reject(RpcValidationRejection(req.id, s"invalid node id '$nodeId'")) } @@ -266,7 +270,10 @@ trait Service extends Logging { case None => SendPayment(amount_msat, pr.paymentHash, pr.nodeId, maxFeePct = nodeParams.maxPaymentFee) case Some(minFinalCltvExpiry) => SendPayment(amount_msat, pr.paymentHash, pr.nodeId, assistedRoutes = Nil, minFinalCltvExpiry, maxFeePct = nodeParams.maxPaymentFee) } - completeRpcFuture(req.id, (paymentInitiator ? sendPayment).mapTo[PaymentResult]) + completeRpcFuture(req.id, (paymentInitiator ? sendPayment).mapTo[PaymentResult].map { + case s: PaymentSucceeded => s + case f: PaymentFailed => f.copy(failures = PaymentLifecycle.transformForUser(f.failures)) + }) case _ => reject(RpcValidationRejection(req.id, s"payment request is not valid")) } case _ => reject(UnknownParamsRejection(req.id, "[amountMsat, paymentHash, nodeId or [paymentRequest] or [paymentRequest, amountMsat]"))