1
0
Fork 0
mirror of https://github.com/ACINQ/eclair.git synced 2025-03-13 19:37:35 +01:00

Improve MPP-send for direct channels (#1599) (#1601)

When the recipient is a direct peer, we can use the accurate knowledge
of our local channels instead of hard-coded MPP parameters to choose
the number of routes and the minimum route amount.

This change makes it possible to easily send all local funds in one
payment when we're directly connected to the recipient.
This commit is contained in:
Bastien Teinturier 2020-11-19 12:31:49 +01:00 committed by GitHub
parent ffab50ab5b
commit fccd20df0b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 21 additions and 6 deletions

View file

@ -301,15 +301,23 @@ object RouteCalculation {
routeParams: RouteParams,
currentBlockHeight: Long): Either[RouterException, Seq[Route]] = {
// We use Yen's k-shortest paths to find many paths for chunks of the total amount.
val numRoutes = {
val directChannelsCount = g.getEdgesBetween(localNodeId, targetNodeId).length
routeParams.mpp.maxParts.max(directChannelsCount) // if we have direct channels to the target, we can use them all
// When the recipient is a direct peer, we have complete visibility on our local channels so we can use more accurate MPP parameters.
val routeParams1 = {
case class DirectChannel(balance: MilliSatoshi, isEmpty: Boolean)
val directChannels = g.getEdgesBetween(localNodeId, targetNodeId).collect {
// We should always have balance information available for local channels.
case GraphEdge(_, update, _, Some(balance)) => DirectChannel(balance, balance < update.htlcMinimumMsat)
}
// If we have direct channels to the target, we can use them all.
val numRoutes = routeParams.mpp.maxParts.max(directChannels.length)
// If we have direct channels to the target, we can use them all, even if they have only a small balance left.
val minPartAmount = (amount +: routeParams.mpp.minPartAmount +: directChannels.filter(!_.isEmpty).map(_.balance)).min
routeParams.copy(mpp = MultiPartParams(minPartAmount, numRoutes))
}
val routeAmount = routeParams.mpp.minPartAmount.min(amount)
findRouteInternal(g, localNodeId, targetNodeId, routeAmount, maxFee, numRoutes, extraEdges, ignoredEdges, ignoredVertices, routeParams, currentBlockHeight) match {
findRouteInternal(g, localNodeId, targetNodeId, routeParams1.mpp.minPartAmount, maxFee, routeParams1.mpp.maxParts, extraEdges, ignoredEdges, ignoredVertices, routeParams1, currentBlockHeight) match {
case Right(routes) =>
// We use these shortest paths to find a set of non-conflicting HTLCs that send the total amount.
split(amount, mutable.Queue(routes: _*), initializeUsedCapacity(pendingHtlcs), routeParams) match {
split(amount, mutable.Queue(routes: _*), initializeUsedCapacity(pendingHtlcs), routeParams1) match {
case Right(routes) if validateMultiPartRoute(amount, maxFee, routes) => Right(routes)
case _ => Left(RouteNotFound)
}

View file

@ -981,6 +981,13 @@ class RouteCalculationSpec extends AnyFunSuite with ParallelTestExecution {
assert(routes.forall(_.length == 1), routes)
checkRouteAmounts(routes, amount, 0 msat)
}
{
// We set min-part-amount to a value that would exclude channels 1 and 4, but it should be ignored when sending to a direct neighbor.
val Success(routes) = findMultiPartRoute(g, a, b, amount, 1 msat, routeParams = routeParams.copy(mpp = MultiPartParams(20000 msat, 3)), currentBlockHeight = 400000)
assert(routes.length === 4, routes)
assert(routes.forall(_.length == 1), routes)
checkRouteAmounts(routes, amount, 0 msat)
}
}
test("calculate multipart route to neighbor (single channel, known balance)") {