From c693d08221b5b27ae555e184df7179b24e74ce70 Mon Sep 17 00:00:00 2001 From: Andrea Date: Fri, 20 Sep 2019 15:51:46 +0200 Subject: [PATCH] Improve error handling when we couldn't find all the channels for a supplied route in /sendtoroute API --- .../scala/fr/acinq/eclair/router/Router.scala | 13 +++++++++---- .../eclair/payment/PaymentLifecycleSpec.scala | 17 +++++++++++++++++ 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/router/Router.scala b/eclair-core/src/main/scala/fr/acinq/eclair/router/Router.scala index c907524ccd..ee998b4d75 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/router/Router.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/router/Router.scala @@ -476,10 +476,15 @@ class Router(val nodeParams: NodeParams, watcher: ActorRef, initialized: Option[ stay case Event(FinalizeRoute(partialHops), d) => - // split into sublists [(a,b),(b,c), ...] then get the edges between each of those pairs, then select the largest edge between them - val edges = partialHops.sliding(2).map { case List(v1, v2) => d.graph.getEdgesBetween(v1, v2).maxBy(_.update.htlcMaximumMsat.getOrElse(0 msat)) } - val hops = edges.map(d => Hop(d.desc.a, d.desc.b, d.update)).toSeq - sender ! RouteResponse(hops, Set.empty, Set.empty) + // split into sublists [(a,b),(b,c), ...] then get the edges between each of those pairs + partialHops.sliding(2).map { case List(v1, v2) => d.graph.getEdgesBetween(v1, v2) }.toList match { + case edges if edges.nonEmpty && edges.forall(_.nonEmpty) => + val selectedEdges = edges.map(_.maxBy(_.update.htlcMaximumMsat.getOrElse(0 msat))) // select the largest edge + val hops = selectedEdges.map(d => Hop(d.desc.a, d.desc.b, d.update)) + sender ! RouteResponse(hops, Set.empty, Set.empty) + case _ => // some nodes in the supplied route aren't connected in our graph + sender ! Status.Failure(new IllegalArgumentException("Not all the nodes in the supplied route are connected with public channels")) + } stay case Event(RouteRequest(start, end, amount, assistedRoutes, ignoreNodes, ignoreChannels, params_opt), d) => diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/payment/PaymentLifecycleSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/payment/PaymentLifecycleSpec.scala index 66ee7fecc4..91483fb0d5 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/payment/PaymentLifecycleSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/payment/PaymentLifecycleSpec.scala @@ -82,6 +82,23 @@ class PaymentLifecycleSpec extends BaseRouterSpec { awaitCond(paymentDb.getOutgoingPayment(id).exists(_.status.isInstanceOf[OutgoingPaymentStatus.Succeeded])) } + test("send to route (edges not found in the graph)") { fixture => + import fixture._ + + val nodeParams = TestConstants.Alice.nodeParams.copy(keyManager = testKeyManager) + val id = UUID.randomUUID() + val progressHandler = PaymentLifecycle.DefaultPaymentProgressHandler(id, defaultPaymentRequest, nodeParams.db.payments) + val paymentFSM = system.actorOf(PaymentLifecycle.props(nodeParams, progressHandler, router, TestProbe().ref)) + val sender = TestProbe() + val eventListener = TestProbe() + system.eventStream.subscribe(eventListener.ref, classOf[PaymentEvent]) + + val brokenRoute = SendPaymentToRoute(randomBytes32, Seq(randomKey.publicKey, randomKey.publicKey, randomKey.publicKey), FinalLegacyPayload(defaultAmountMsat, defaultExpiryDelta.toCltvExpiry(nodeParams.currentBlockHeight))) + sender.send(paymentFSM, brokenRoute) + val failureMessage = eventListener.expectMsgType[PaymentFailed].failures.head.asInstanceOf[LocalFailure].t.getMessage + assert(failureMessage == "Not all the nodes in the supplied route are connected with public channels") + } + test("payment failed (route not found)") { fixture => import fixture._ val nodeParams = TestConstants.Alice.nodeParams.copy(keyManager = testKeyManager)