Add `maxCltvExpiryDelta` parameter to `findRoute*` APIs (#3234)
What changed, and why it matters
This commit adds a new optional setting called maxCltvExpiryDelta to Eclair's route-finding APIs. It lets users tell the Lightning node not to return payment routes whose total time-lock delay exceeds a chosen limit. The change is a feature addition, not a fix for an active vulnerability, and it does not alter default behavior when the new parameter is omitted.
No immediate security action required. Treat as a routine feature release. Operators who expose the findRoute* APIs may document the new optional parameter for users who want tighter control over route CLTV deltas.
Security signals we found
New optional route boundary parameter (maxCltvExpiryDelta) added to findRoute* APIs
Parameter is threaded into existing routeParams.boundaries.maxCltv, preserving default behavior when omitted
No default value change; no modification of internal payment path-finding defaults
Release notes describe the change as an API change, not a security fix
Evidence from the diff
The patch threads an optional maxCltvExpiryDelta parameter through findRoute, findRouteToNode, and findRouteBetweenNodes API endpoints into EclairImpl.findRoute/findRouteBetween. When supplied, it overrides routeParams.boundaries.maxCltv; otherwise the existing configured boundary is kept. It also adds a strict string-to-int unmarshaller for CltvExpiryDelta in the API layer and updates tests/mocks accordingly. There is no change to default route-finding logic or to internal payment-sending paths.
Changed components
Eclair API route-finding endpoints: findroute, findroutetonode, findroutebetweennodeseclair-core EclairImpl.findRoute / findRouteBetweenAPI form-field extractors and route handler definitionsInspect captured patch +32 / −30
diff --git a/docs/release-notes/eclair-vnext.md b/docs/release-notes/eclair-vnext.md
index 9665470..062aeb0 100644
--- a/docs/release-notes/eclair-vnext.md
+++ b/docs/release-notes/eclair-vnext.md
@@ -15,7 +15,7 @@ If you still have such channels, eclair won't start: you will need to close thos
### API changes
-<insert changes>
+- `findroute`, `findroutetonode` and `findroutebetweennodes` now include a `maxCltvExpiryDelta` parameter (#3234)
### Miscellaneous improvements and bug fixes
diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/Eclair.scala b/eclair-core/src/main/scala/fr/acinq/eclair/Eclair.scala
index ceb96c1..c3ce569 100644
--- a/eclair-core/src/main/scala/fr/acinq/eclair/Eclair.scala
+++ b/eclair-core/src/main/scala/fr/acinq/eclair/Eclair.scala
@@ -33,7 +33,6 @@ import fr.acinq.eclair.blockchain.bitcoind.ZmqWatcher.WatchFundingSpentTriggered
import fr.acinq.eclair.blockchain.bitcoind.rpc.BitcoinCoreClient
import fr.acinq.eclair.blockchain.bitcoind.rpc.BitcoinCoreClient.{AddressType, Descriptors, WalletTx}
import fr.acinq.eclair.blockchain.fee.{ConfirmationTarget, FeeratePerByte, FeeratePerKw}
-import fr.acinq.eclair.channel.ChannelSpendSignature.PartialSignatureWithNonce
import fr.acinq.eclair.channel._
import fr.acinq.eclair.crypto.Sphinx
import fr.acinq.eclair.db.AuditDb.{NetworkFee, Stats}
@@ -152,9 +151,9 @@ trait Eclair {
def cpfpBumpFees(targetFeeratePerByte: FeeratePerByte, outpoints: Set[OutPoint]): Future[TxId]
- def findRoute(targetNodeId: PublicKey, amount: MilliSatoshi, pathFindingExperimentName_opt: Option[String], extraEdges: Seq[Invoice.ExtraEdge] = Seq.empty, includeLocalChannelCost: Boolean = false, ignoreNodeIds: Seq[PublicKey] = Seq.empty, ignoreShortChannelIds: Seq[ShortChannelId] = Seq.empty, maxFee_opt: Option[MilliSatoshi] = None)(implicit timeout: Timeout): Future[RouteResponse]
+ def findRoute(targetNodeId: PublicKey, amount: MilliSatoshi, pathFindingExperimentName_opt: Option[String], extraEdges: Seq[Invoice.ExtraEdge] = Seq.empty, includeLocalChannelCost: Boolean = false, ignoreNodeIds: Seq[PublicKey] = Seq.empty, ignoreShortChannelIds: Seq[ShortChannelId] = Seq.empty, maxFee_opt: Option[MilliSatoshi] = None, maxCltvExpiryDelta_opt: Option[CltvExpiryDelta] = None)(implicit timeout: Timeout): Future[RouteResponse]
- def findRouteBetween(sourceNodeId: PublicKey, targetNodeId: PublicKey, amount: MilliSatoshi, pathFindingExperimentName_opt: Option[String], extraEdges: Seq[Invoice.ExtraEdge] = Seq.empty, includeLocalChannelCost: Boolean = false, ignoreNodeIds: Seq[PublicKey] = Seq.empty, ignoreShortChannelIds: Seq[ShortChannelId] = Seq.empty, maxFee_opt: Option[MilliSatoshi] = None)(implicit timeout: Timeout): Future[RouteResponse]
+ def findRouteBetween(sourceNodeId: PublicKey, targetNodeId: PublicKey, amount: MilliSatoshi, pathFindingExperimentName_opt: Option[String], extraEdges: Seq[Invoice.ExtraEdge] = Seq.empty, includeLocalChannelCost: Boolean = false, ignoreNodeIds: Seq[PublicKey] = Seq.empty, ignoreShortChannelIds: Seq[ShortChannelId] = Seq.empty, maxFee_opt: Option[MilliSatoshi] = None, maxCltvExpiryDelta_opt: Option[CltvExpiryDelta] = None)(implicit timeout: Timeout): Future[RouteResponse]
def sendToRoute(recipientAmount_opt: Option[MilliSatoshi], externalId_opt: Option[String], parentId_opt: Option[UUID], invoice: Bolt11Invoice, route: PredefinedRoute)(implicit timeout: Timeout): Future[SendPaymentToRouteResponse]
@@ -459,8 +458,8 @@ class EclairImpl(val appKit: Kit) extends Eclair with Logging with SpendFromChan
}
}
- override def findRoute(targetNodeId: PublicKey, amount: MilliSatoshi, pathFindingExperimentName_opt: Option[String], extraEdges: Seq[Invoice.ExtraEdge] = Seq.empty, includeLocalChannelCost: Boolean = false, ignoreNodeIds: Seq[PublicKey] = Seq.empty, ignoreShortChannelIds: Seq[ShortChannelId] = Seq.empty, maxFee_opt: Option[MilliSatoshi] = None)(implicit timeout: Timeout): Future[RouteResponse] =
- findRouteBetween(appKit.nodeParams.nodeId, targetNodeId, amount, pathFindingExperimentName_opt, extraEdges, includeLocalChannelCost, ignoreNodeIds, ignoreShortChannelIds, maxFee_opt)
+ override def findRoute(targetNodeId: PublicKey, amount: MilliSatoshi, pathFindingExperimentName_opt: Option[String], extraEdges: Seq[Invoice.ExtraEdge] = Seq.empty, includeLocalChannelCost: Boolean = false, ignoreNodeIds: Seq[PublicKey] = Seq.empty, ignoreShortChannelIds: Seq[ShortChannelId] = Seq.empty, maxFee_opt: Option[MilliSatoshi] = None, maxCltvExpiryDelta_opt: Option[CltvExpiryDelta] = None)(implicit timeout: Timeout): Future[RouteResponse] =
+ findRouteBetween(appKit.nodeParams.nodeId, targetNodeId, amount, pathFindingExperimentName_opt, extraEdges, includeLocalChannelCost, ignoreNodeIds, ignoreShortChannelIds, maxFee_opt, maxCltvExpiryDelta_opt)
private def getRouteParams(pathFindingExperimentName_opt: Option[String]): Either[IllegalArgumentException, RouteParams] = {
pathFindingExperimentName_opt match {
@@ -472,7 +471,7 @@ class EclairImpl(val appKit: Kit) extends Eclair with Logging with SpendFromChan
}
}
- override def findRouteBetween(sourceNodeId: PublicKey, targetNodeId: PublicKey, amount: MilliSatoshi, pathFindingExperimentName_opt: Option[String], extraEdges: Seq[Invoice.ExtraEdge] = Seq.empty, includeLocalChannelCost: Boolean = false, ignoreNodeIds: Seq[PublicKey] = Seq.empty, ignoreShortChannelIds: Seq[ShortChannelId] = Seq.empty, maxFee_opt: Option[MilliSatoshi] = None)(implicit timeout: Timeout): Future[RouteResponse] = {
+ override def findRouteBetween(sourceNodeId: PublicKey, targetNodeId: PublicKey, amount: MilliSatoshi, pathFindingExperimentName_opt: Option[String], extraEdges: Seq[Invoice.ExtraEdge] = Seq.empty, includeLocalChannelCost: Boolean = false, ignoreNodeIds: Seq[PublicKey] = Seq.empty, ignoreShortChannelIds: Seq[ShortChannelId] = Seq.empty, maxFee_opt: Option[MilliSatoshi] = None, maxCltvExpiryDelta_opt: Option[CltvExpiryDelta] = None)(implicit timeout: Timeout): Future[RouteResponse] = {
getRouteParams(pathFindingExperimentName_opt) match {
case Right(routeParams) =>
val target = ClearRecipient(targetNodeId, Features.empty, amount, CltvExpiry(appKit.nodeParams.currentBlockHeight), ByteVector32.Zeroes, extraEdges)
@@ -480,7 +479,8 @@ class EclairImpl(val appKit: Kit) extends Eclair with Logging with SpendFromChan
includeLocalChannelCost = includeLocalChannelCost,
boundaries = routeParams.boundaries.copy(
maxFeeFlat = maxFee_opt.getOrElse(routeParams.boundaries.maxFeeFlat),
- maxFeeProportional = maxFee_opt.map(_ => 0.0).getOrElse(routeParams.boundaries.maxFeeProportional)
+ maxFeeProportional = maxFee_opt.map(_ => 0.0).getOrElse(routeParams.boundaries.maxFeeProportional),
+ maxCltv = maxCltvExpiryDelta_opt.getOrElse(routeParams.boundaries.maxCltv),
)
)
for {
diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/EclairImplSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/EclairImplSpec.scala
index 6ae9b7d..11de170 100644
--- a/eclair-core/src/test/scala/fr/acinq/eclair/EclairImplSpec.scala
+++ b/eclair-core/src/test/scala/fr/acinq/eclair/EclairImplSpec.scala
@@ -343,7 +343,6 @@ class EclairImplSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with I
val eclair = new EclairImpl(kit)
val route = PredefinedNodeRoute(1000 msat, Seq(randomKey().publicKey))
val parentId = UUID.randomUUID()
- val secret = randomBytes32()
val pr = Bolt11Invoice(Block.LivenetGenesisBlock.hash, Some(1234 msat), ByteVector32.One, randomKey(), Right(randomBytes32()), CltvExpiryDelta(18))
eclair.sendToRoute(Some(1200 msat), Some("42"), Some(parentId), pr, route)
val sendPaymentToRoute = paymentInitiator.expectMsgType[SendPaymentToRoute]
diff --git a/eclair-node/src/main/scala/fr/acinq/eclair/api/directives/ExtraDirectives.scala b/eclair-node/src/main/scala/fr/acinq/eclair/api/directives/ExtraDirectives.scala
index a41c02c..bf18a84 100644
--- a/eclair-node/src/main/scala/fr/acinq/eclair/api/directives/ExtraDirectives.scala
+++ b/eclair-node/src/main/scala/fr/acinq/eclair/api/directives/ExtraDirectives.scala
@@ -29,7 +29,7 @@ import fr.acinq.eclair.api.serde.JsonSupport._
import fr.acinq.eclair.blockchain.fee.ConfirmationPriority
import fr.acinq.eclair.payment.Bolt11Invoice
import fr.acinq.eclair.wire.protocol.OfferTypes.Offer
-import fr.acinq.eclair.{MilliSatoshi, Paginated, ShortChannelId, TimestampSecond}
+import fr.acinq.eclair.{CltvExpiryDelta, MilliSatoshi, Paginated, ShortChannelId, TimestampSecond}
import scala.concurrent.Future
import scala.concurrent.duration.DurationInt
@@ -52,6 +52,7 @@ trait ExtraDirectives extends Directives {
val ignoreNodeIdsFormParam: NameUnmarshallerReceptacle[List[PublicKey]] = "ignoreNodeIds".as[List[PublicKey]](pubkeyListUnmarshaller)
val ignoreShortChannelIdsFormParam: NameUnmarshallerReceptacle[List[ShortChannelId]] = "ignoreShortChannelIds".as[List[ShortChannelId]](shortChannelIdsUnmarshaller)
val maxFeeMsatFormParam: NameReceptacle[MilliSatoshi] = "maxFeeMsat".as[MilliSatoshi]
+ val maxCltvExpiryDeltaFormParam: NameReceptacle[CltvExpiryDelta] = "maxCltvExpiryDelta".as[CltvExpiryDelta]
val countFormParam: NameReceptacle[Int] = "count".as[Int]
val skipFormParam: NameReceptacle[Int] = "skip".as[Int]
val offerFormParam: NameUnmarshallerReceptacle[Offer] = "offer".as[Offer](offerUnmarshaller)
diff --git a/eclair-node/src/main/scala/fr/acinq/eclair/api/handlers/PathFinding.scala b/eclair-node/src/main/scala/fr/acinq/eclair/api/handlers/PathFinding.scala
index 1d21edb..2f6ae89 100644
--- a/eclair-node/src/main/scala/fr/acinq/eclair/api/handlers/PathFinding.scala
+++ b/eclair-node/src/main/scala/fr/acinq/eclair/api/handlers/PathFinding.scala
@@ -32,27 +32,27 @@ trait PathFinding {
private implicit def ec: ExecutionContext = actorSystem.dispatcher
val findRoute: Route = postRequest("findroute") { implicit t =>
- formFields(invoiceFormParam, amountMsatFormParam.?, "pathFindingExperimentName".?, routeFormatFormParam.?, "includeLocalChannelCost".as[Boolean].?, ignoreNodeIdsFormParam.?, ignoreShortChannelIdsFormParam.?, maxFeeMsatFormParam.?) {
- case (invoice, None, pathFindingExperimentName_opt, routeFormat_opt, includeLocalChannelCost_opt, ignoreNodeIds_opt, ignoreChannels_opt, maxFee_opt) if invoice.amount_opt.nonEmpty =>
- complete(eclairApi.findRoute(invoice.nodeId, invoice.amount_opt.get, pathFindingExperimentName_opt, invoice.extraEdges, includeLocalChannelCost_opt.getOrElse(false), ignoreNodeIds = ignoreNodeIds_opt.getOrElse(Nil), ignoreShortChannelIds = ignoreChannels_opt.getOrElse(Nil), maxFee_opt = maxFee_opt).map(r => RouteFormat.format(r, routeFormat_opt)))
- case (invoice, Some(overrideAmount), pathFindingExperimentName_opt, routeFormat_opt, includeLocalChannelCost_opt, ignoreNodeIds_opt, ignoreChannels_opt, maxFee_opt) =>
- complete(eclairApi.findRoute(invoice.nodeId, overrideAmount, pathFindingExperimentName_opt, invoice.extraEdges, includeLocalChannelCost_opt.getOrElse(false), ignoreNodeIds = ignoreNodeIds_opt.getOrElse(Nil), ignoreShortChannelIds = ignoreChannels_opt.getOrElse(Nil), maxFee_opt = maxFee_opt).map(r => RouteFormat.format(r, routeFormat_opt)))
- case _ => reject(MalformedFormFieldRejection(
- "invoice", "The invoice must have an amount or you need to specify one using 'amountMsat'"
- ))
+ formFields(invoiceFormParam, amountMsatFormParam.?, "pathFindingExperimentName".?, routeFormatFormParam.?, "includeLocalChannelCost".as[Boolean].?, ignoreNodeIdsFormParam.?, ignoreShortChannelIdsFormParam.?, maxFeeMsatFormParam.?, maxCltvExpiryDeltaFormParam.?) {
+ case (invoice, None, pathFindingExperimentName_opt, routeFormat_opt, includeLocalChannelCost_opt, ignoreNodeIds_opt, ignoreChannels_opt, maxFee_opt, maxCltv_opt) if invoice.amount_opt.nonEmpty =>
+ complete(eclairApi.findRoute(invoice.nodeId, invoice.amount_opt.get, pathFindingExperimentName_opt, invoice.extraEdges, includeLocalChannelCost_opt.getOrElse(false), ignoreNodeIds = ignoreNodeIds_opt.getOrElse(Nil), ignoreShortChannelIds = ignoreChannels_opt.getOrElse(Nil), maxFee_opt = maxFee_opt, maxCltvExpiryDelta_opt = maxCltv_opt).map(r => RouteFormat.format(r, routeFormat_opt)))
+ case (invoice, Some(overrideAmount), pathFindingExperimentName_opt, routeFormat_opt, includeLocalChannelCost_opt, ignoreNodeIds_opt, ignoreChannels_opt, maxFee_opt, maxCltv_opt) =>
+ complete(eclairApi.findRoute(invoice.nodeId, overrideAmount, pathFindingExperimentName_opt, invoice.extraEdges, includeLocalChannelCost_opt.getOrElse(false), ignoreNodeIds = ignoreNodeIds_opt.getOrElse(Nil), ignoreShortChannelIds = ignoreChannels_opt.getOrElse(Nil), maxFee_opt = maxFee_opt, maxCltvExpiryDelta_opt = maxCltv_opt).map(r => RouteFormat.format(r, routeFormat_opt)))
+ case _ =>
+ reject(MalformedFormFieldRejection("invoice", "The invoice must have an amount or you need to specify one using 'amountMsat'"))
}
}
val findRouteToNode: Route = postRequest("findroutetonode") { implicit t =>
- formFields(nodeIdFormParam, amountMsatFormParam, "pathFindingExperimentName".?, routeFormatFormParam.?, "includeLocalChannelCost".as[Boolean].?, ignoreNodeIdsFormParam.?, ignoreShortChannelIdsFormParam.?, maxFeeMsatFormParam.?) {
- (nodeId, amount, pathFindingExperimentName_opt, routeFormat_opt, includeLocalChannelCost_opt, ignoreNodeIds_opt, ignoreChannels_opt, maxFee_opt) =>
- complete(eclairApi.findRoute(nodeId, amount, pathFindingExperimentName_opt, includeLocalChannelCost = includeLocalChannelCost_opt.getOrElse(false), ignoreNodeIds = ignoreNodeIds_opt.getOrElse(Nil), ignoreShortChannelIds = ignoreChannels_opt.getOrElse(Nil), maxFee_opt = maxFee_opt).map(r => RouteFormat.format(r, routeFormat_opt)))
+ formFields(nodeIdFormParam, amountMsatFormParam, "pathFindingExperimentName".?, routeFormatFormParam.?, "includeLocalChannelCost".as[Boolean].?, ignoreNodeIdsFormParam.?, ignoreShortChannelIdsFormParam.?, maxFeeMsatFormParam.?, maxCltvExpiryDeltaFormParam.?) {
+ (nodeId, amount, pathFindingExperimentName_opt, routeFormat_opt, includeLocalChannelCost_opt, ignoreNodeIds_opt, ignoreChannels_opt, maxFee_opt, maxCltv_opt) =>
+ complete(eclairApi.findRoute(nodeId, amount, pathFindingExperimentName_opt, includeLocalChannelCost = includeLocalChannelCost_opt.getOrElse(false), ignoreNodeIds = ignoreNodeIds_opt.getOrElse(Nil), ignoreShortChannelIds = ignoreChannels_opt.getOrElse(Nil), maxFee_opt = maxFee_opt, maxCltvExpiryDelta_opt = maxCltv_opt).map(r => RouteFormat.format(r, routeFormat_opt)))
}
}
val findRouteBetweenNodes: Route = postRequest("findroutebetweennodes") { implicit t =>
- formFields("sourceNodeId".as[PublicKey], "targetNodeId".as[PublicKey], amountMsatFormParam, "pathFindingExperimentName".?, routeFormatFormParam.?, "includeLocalChannelCost".as[Boolean].?, ignoreNodeIdsFormParam.?, ignoreShortChannelIdsFormParam.?, maxFeeMsatFormParam.?) { (sourceNodeId, targetNodeId, amount, pathFindingExperimentName_opt, routeFormat_opt, includeLocalChannelCost_opt, ignoreNodeIds_opt, ignoreChannels_opt, maxFee_opt) =>
- complete(eclairApi.findRouteBetween(sourceNodeId, targetNodeId, amount, pathFindingExperimentName_opt, includeLocalChannelCost = includeLocalChannelCost_opt.getOrElse(false), ignoreNodeIds = ignoreNodeIds_opt.getOrElse(Nil), ignoreShortChannelIds = ignoreChannels_opt.getOrElse(Nil), maxFee_opt = maxFee_opt).map(r => RouteFormat.format(r, routeFormat_opt)))
+ formFields("sourceNodeId".as[PublicKey], "targetNodeId".as[PublicKey], amountMsatFormParam, "pathFindingExperimentName".?, routeFormatFormParam.?, "includeLocalChannelCost".as[Boolean].?, ignoreNodeIdsFormParam.?, ignoreShortChannelIdsFormParam.?, maxFeeMsatFormParam.?, maxCltvExpiryDeltaFormParam.?) {
+ (sourceNodeId, targetNodeId, amount, pathFindingExperimentName_opt, routeFormat_opt, includeLocalChannelCost_opt, ignoreNodeIds_opt, ignoreChannels_opt, maxFee_opt, maxCltv_opt) =>
+ complete(eclairApi.findRouteBetween(sourceNodeId, targetNodeId, amount, pathFindingExperimentName_opt, includeLocalChannelCost = includeLocalChannelCost_opt.getOrElse(false), ignoreNodeIds = ignoreNodeIds_opt.getOrElse(Nil), ignoreShortChannelIds = ignoreChannels_opt.getOrElse(Nil), maxFee_opt = maxFee_opt, maxCltvExpiryDelta_opt = maxCltv_opt).map(r => RouteFormat.format(r, routeFormat_opt)))
}
}
diff --git a/eclair-node/src/main/scala/fr/acinq/eclair/api/serde/FormParamExtractors.scala b/eclair-node/src/main/scala/fr/acinq/eclair/api/serde/FormParamExtractors.scala
index df97be0..52f1489 100644
--- a/eclair-node/src/main/scala/fr/acinq/eclair/api/serde/FormParamExtractors.scala
+++ b/eclair-node/src/main/scala/fr/acinq/eclair/api/serde/FormParamExtractors.scala
@@ -29,7 +29,7 @@ import fr.acinq.eclair.io.NodeURI
import fr.acinq.eclair.payment.Bolt11Invoice
import fr.acinq.eclair.wire.protocol.OfferCodecs.blindedRouteCodec
import fr.acinq.eclair.wire.protocol.OfferTypes.Offer
-import fr.acinq.eclair.{MilliSatoshi, ShortChannelId, TimestampSecond}
+import fr.acinq.eclair.{CltvExpiryDelta, MilliSatoshi, ShortChannelId, TimestampSecond}
import scodec.bits.ByteVector
import java.util.UUID
@@ -68,6 +68,8 @@ object FormParamExtractors {
implicit val millisatoshiUnmarshaller: Unmarshaller[String, MilliSatoshi] = Unmarshaller.strict { str => MilliSatoshi(str.toLong) }
+ implicit val cltvExpiryDeltaUnmarshaller: Unmarshaller[String, CltvExpiryDelta] = Unmarshaller.strict { str => CltvExpiryDelta(str.toInt) }
+
implicit val feeratePerByteUnmarshaller: Unmarshaller[String, FeeratePerByte] = Unmarshaller.strict { str => FeeratePerByte(Satoshi(str.toLong)) }
implicit val outPointListUnmarshaller: Unmarshaller[String, List[OutPoint]] = listUnmarshaller(outPoint => {
diff --git a/eclair-node/src/test/scala/fr/acinq/eclair/api/ApiServiceSpec.scala b/eclair-node/src/test/scala/fr/acinq/eclair/api/ApiServiceSpec.scala
index 691703c..da1ae28 100644
--- a/eclair-node/src/test/scala/fr/acinq/eclair/api/ApiServiceSpec.scala
+++ b/eclair-node/src/test/scala/fr/acinq/eclair/api/ApiServiceSpec.scala
@@ -1002,7 +1002,7 @@ class ApiServiceSpec extends AnyFunSuite with ScalatestRouteTest with IdiomaticM
val eclair = mock[Eclair]
val mockService = new MockService(eclair)
- eclair.findRoute(any, any, any, any, any, any, any, any)(any[Timeout]) returns Future.successful(Router.RouteResponse(Seq(Router.Route(456.msat, mockHops, None))))
+ eclair.findRoute(any, any, any, any, any, any, any, any, any)(any[Timeout]) returns Future.successful(Router.RouteResponse(Seq(Router.Route(456.msat, mockHops, None))))
// invalid format
Post("/findroute", FormData("format" -> "invalid-output-format", "invoice" -> serializedInvoice, "amountMsat" -> "456")) ~>
@@ -1012,7 +1012,7 @@ class ApiServiceSpec extends AnyFunSuite with ScalatestRouteTest with IdiomaticM
check {
assert(handled)
assert(status == BadRequest)
- eclair.findRoute(invoice.nodeId, 456.msat, any, any, any, any, any, any)(any[Timeout]).wasNever(called)
+ eclair.findRoute(invoice.nodeId, 456.msat, any, any, any, any, any, any, any)(any[Timeout]).wasNever(called)
}
// default format
@@ -1025,7 +1025,7 @@ class ApiServiceSpec extends AnyFunSuite with ScalatestRouteTest with IdiomaticM
assert(status == OK)
val response = entityAs[String]
matchTestJson("findroute-nodeid", response)
- eclair.findRoute(invoice.nodeId, 456.msat, any, any, any, any, any, any)(any[Timeout]).wasCalled(once)
+ eclair.findRoute(invoice.nodeId, 456.msat, any, any, any, any, any, any, any)(any[Timeout]).wasCalled(once)
}
Post("/findroute", FormData("format" -> "nodeId", "invoice" -> serializedInvoice, "amountMsat" -> "456")) ~>
@@ -1037,7 +1037,7 @@ class ApiServiceSpec extends AnyFunSuite with ScalatestRouteTest with IdiomaticM
assert(status == OK)
val response = entityAs[String]
matchTestJson("findroute-nodeid", response)
- eclair.findRoute(invoice.nodeId, 456.msat, any, any, any, any, any, any)(any[Timeout]).wasCalled(twice)
+ eclair.findRoute(invoice.nodeId, 456.msat, any, any, any, any, any, any, any)(any[Timeout]).wasCalled(twice)
}
Post("/findroute", FormData("format" -> "shortChannelId", "invoice" -> serializedInvoice, "amountMsat" -> "456")) ~>
@@ -1049,7 +1049,7 @@ class ApiServiceSpec extends AnyFunSuite with ScalatestRouteTest with IdiomaticM
assert(status == OK)
val response = entityAs[String]
matchTestJson("findroute-scid", response)
- eclair.findRoute(invoice.nodeId, 456.msat, any, any, any, any, any, any)(any[Timeout]).wasCalled(threeTimes)
+ eclair.findRoute(invoice.nodeId, 456.msat, any, any, any, any, any, any, any)(any[Timeout]).wasCalled(threeTimes)
}
Post("/findroute", FormData("format" -> "full", "invoice" -> serializedInvoice, "amountMsat" -> "456")) ~>
@@ -1061,7 +1061,7 @@ class ApiServiceSpec extends AnyFunSuite with ScalatestRouteTest with IdiomaticM
assert(status == OK)
val response = entityAs[String]
matchTestJson("findroute-full", response)
- eclair.findRoute(invoice.nodeId, 456.msat, any, any, any, any, any, any)(any[Timeout]).wasCalled(fourTimes)
+ eclair.findRoute(invoice.nodeId, 456.msat, any, any, any, any, any, any, any)(any[Timeout]).wasCalled(fourTimes)
}
}
Why this scored 18/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.