1
0
Fork 0
mirror of https://github.com/ACINQ/eclair.git synced 2025-02-23 14:40:34 +01:00

Route computation: fix fee check (#1101)

* Route computation: fix fee check

Fee check during route computation is:
- fee is below maximum value
- OR fee is below amout * maximum percentage

The second check was buggy and route computation would failed when fees we above maximum value but below maximum percentage of amount being paid.
This commit is contained in:
Fabrice Drouin 2019-08-20 15:56:56 +02:00 committed by GitHub
parent 4929febbd1
commit 290ac3dbb2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 42 additions and 8 deletions

View file

@ -170,6 +170,7 @@ package object eclair {
def +(other: MilliSatoshi) = MilliSatoshi(amount + other.amount)
def -(other: MilliSatoshi) = MilliSatoshi(amount - other.amount)
def *(m: Long) = MilliSatoshi(amount * m)
def *(m: Double) = MilliSatoshi((amount * m).toLong)
def /(d: Long) = MilliSatoshi(amount / d)
def compare(other: MilliSatoshi): Int = if (amount == other.amount) 0 else if (amount < other.amount) -1 else 1
def <= (that: MilliSatoshi): Boolean = compare(that) <= 0

View file

@ -19,9 +19,9 @@ package fr.acinq.eclair.router
import akka.Done
import akka.actor.{ActorRef, Props, Status}
import akka.event.Logging.MDC
import fr.acinq.bitcoin.{ByteVector32, ByteVector64, Satoshi}
import fr.acinq.bitcoin.Crypto.PublicKey
import fr.acinq.bitcoin.Script.{pay2wsh, write}
import fr.acinq.bitcoin.{ByteVector32, ByteVector64, Satoshi}
import fr.acinq.eclair._
import fr.acinq.eclair.blockchain._
import fr.acinq.eclair.channel._
@ -33,7 +33,6 @@ import fr.acinq.eclair.router.Graph.GraphStructure.{DirectedGraph, GraphEdge}
import fr.acinq.eclair.router.Graph.{RichWeight, WeightRatios}
import fr.acinq.eclair.transactions.Scripts
import fr.acinq.eclair.wire._
import scodec.bits.ByteVector
import scala.collection.immutable.{SortedMap, TreeMap}
import scala.collection.{SortedSet, mutable}
@ -868,10 +867,21 @@ object Router {
val currentBlockHeight = Globals.blockCount.get()
def feeBaseOk(fee: MilliSatoshi): Boolean = fee <= routeParams.maxFeeBase
def feePctOk(fee: MilliSatoshi, amount: MilliSatoshi): Boolean = {
val maxFee = amount * routeParams.maxFeePct
fee <= maxFee
}
def feeOk(fee: MilliSatoshi, amount: MilliSatoshi): Boolean = feeBaseOk(fee) || feePctOk(fee, amount)
def lengthOk(length: Int): Boolean = length <= routeParams.routeMaxLength && length <= ROUTE_MAX_LENGTH
def cltvOk(cltv: Int): Boolean = cltv <= routeParams.routeMaxCltv
val boundaries: RichWeight => Boolean = { weight =>
((weight.cost - amount) < routeParams.maxFeeBase || (weight.cost - amount) < amount * routeParams.maxFeePct.toLong) &&
weight.length <= routeParams.routeMaxLength && weight.length <= ROUTE_MAX_LENGTH &&
weight.cltv <= routeParams.routeMaxCltv
feeOk(weight.cost - amount, amount) && lengthOk(weight.length) && cltvOk(weight.cltv)
}
val foundRoutes = Graph.yenKshortestPaths(g, localNodeId, targetNodeId, amount, ignoredEdges, extraEdges, numRoutes, routeParams.ratios, currentBlockHeight, boundaries).toList match {

View file

@ -16,8 +16,8 @@
package fr.acinq.eclair.router
import fr.acinq.bitcoin.Crypto.{PrivateKey, PublicKey}
import fr.acinq.bitcoin.{Block, ByteVector32, ByteVector64, Crypto}
import fr.acinq.bitcoin.Crypto.PublicKey
import fr.acinq.bitcoin.{Block, ByteVector32, ByteVector64}
import fr.acinq.eclair.payment.PaymentRequest.ExtraHop
import fr.acinq.eclair.router.Graph.GraphStructure.DirectedGraph.graphEdgeToHop
import fr.acinq.eclair.router.Graph.GraphStructure.{DirectedGraph, GraphEdge}
@ -56,6 +56,30 @@ class RouteCalculationSpec extends FunSuite {
assert(route.map(hops2Ids) === Success(1 :: 2 :: 3 :: 4 :: Nil))
}
test("check fee against max pct properly") {
// fee is acceptable is it is either
// - below our maximum fee base
// - below our maximum fraction of the paid amount
// here we have a maximum fee base of 1 msat, and all our updates have a base fee of 10 msat
// so our fee will always be above the base fee, and we will always check that it is below our maximum percentage
// of the amount being paid
val updates = List(
makeUpdate(1L, a, b, MilliSatoshi(10), 10, cltvDelta = 1),
makeUpdate(2L, b, c, MilliSatoshi(10), 10, cltvDelta = 1),
makeUpdate(3L, c, d, MilliSatoshi(10), 10, cltvDelta = 1),
makeUpdate(4L, d, e, MilliSatoshi(10), 10, cltvDelta = 1)
).toMap
val g = makeGraph(updates)
val route = Router.findRoute(g, a, e, DEFAULT_AMOUNT_MSAT, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS.copy(maxFeeBase = MilliSatoshi(1)))
assert(route.map(hops2Ids) === Success(1 :: 2 :: 3 :: 4 :: Nil))
}
test("calculate the shortest path (correct fees)") {
val (a, b, c, d, e, f) = (
@ -920,7 +944,6 @@ class RouteCalculationSpec extends FunSuite {
assert(route.size == 2)
assert(route.last.nextNodeId == targetNode)
}
}
object RouteCalculationSpec {