From b8611f68bed5160892732060e8a006c897ed3f77 Mon Sep 17 00:00:00 2001 From: Thomas HUET Date: Wed, 8 Jan 2025 14:49:44 +0100 Subject: [PATCH] WIP --- .../src/main/scala/fr/acinq/eclair/Eclair.scala | 4 ++-- .../src/main/scala/fr/acinq/eclair/Setup.scala | 1 + .../main/scala/fr/acinq/eclair/db/Databases.scala | 10 +++++----- .../scala/fr/acinq/eclair/db/DualDatabases.scala | 2 +- .../main/scala/fr/acinq/eclair/db/OffersDb.scala | 15 +++++++++++++++ .../eclair/payment/offer/DefaultHandler.scala | 14 ++++++++++---- .../acinq/eclair/payment/offer/OfferCreator.scala | 2 +- .../scala/fr/acinq/eclair/TestDatabases.scala | 2 +- 8 files changed, 36 insertions(+), 14 deletions(-) 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 2cfd257bd7..768402bcc9 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/Eclair.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/Eclair.scala @@ -389,11 +389,11 @@ class EclairImpl(appKit: Kit) extends Eclair with Logging { override def disableOffer(offer: Offer)(implicit timeout: Timeout): Unit = { appKit.offerManager ! OfferManager.DisableOffer(offer) - appKit.nodeParams.db.offers.disableOffer(offer) + appKit.nodeParams.db.managedOffers.disableOffer(offer) } override def listOffers(onlyActive: Boolean = true)(implicit timeout: Timeout): Future[Seq[Offer]] = Future { - appKit.nodeParams.db.offers.listOffers(onlyActive).map(_.offer) + appKit.nodeParams.db.managedOffers.listOffers(onlyActive).map(_.offer) } override def newAddress(): Future[String] = { diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/Setup.scala b/eclair-core/src/main/scala/fr/acinq/eclair/Setup.scala index 9cfc2127f6..4a61ee4d5c 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/Setup.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/Setup.scala @@ -363,6 +363,7 @@ class Setup(val datadir: File, register = system.actorOf(SimpleSupervisor.props(Register.props(), "register", SupervisorStrategy.Resume)) offerManager = system.spawn(Behaviors.supervise(OfferManager(nodeParams, router, paymentTimeout = 1 minute)).onFailure(typed.SupervisorStrategy.resume), name = "offer-manager") defaultOfferHandler = system.spawn(Behaviors.supervise(DefaultHandler(nodeParams, router)).onFailure(typed.SupervisorStrategy.resume), name = "default-offer-handler") + _ = for (offer <- nodeParams.db.managedOffers.listOffers(onlyActive = true)) offerManager ! OfferManager.RegisterOffer(offer.offer, None, offer.pathId_opt, defaultOfferHandler) paymentHandler = system.actorOf(SimpleSupervisor.props(PaymentHandler.props(nodeParams, register, offerManager), "payment-handler", SupervisorStrategy.Resume)) triggerer = system.spawn(Behaviors.supervise(AsyncPaymentTriggerer()).onFailure(typed.SupervisorStrategy.resume), name = "async-payment-triggerer") peerReadyManager = system.spawn(Behaviors.supervise(PeerReadyManager()).onFailure(typed.SupervisorStrategy.restart), name = "peer-ready-manager") diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/db/Databases.scala b/eclair-core/src/main/scala/fr/acinq/eclair/db/Databases.scala index c7f929f572..4ef14b8dd8 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/db/Databases.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/db/Databases.scala @@ -43,7 +43,7 @@ trait Databases { def channels: ChannelsDb def peers: PeersDb def payments: PaymentsDb - def offers: OffersDb + def managedOffers: OffersDb def pendingCommands: PendingCommandsDb def liquidity: LiquidityDb //@formatter:on @@ -67,7 +67,7 @@ object Databases extends Logging { channels: SqliteChannelsDb, peers: SqlitePeersDb, payments: SqlitePaymentsDb, - offers: SqliteOffersDb, + managedOffers: SqliteOffersDb, pendingCommands: SqlitePendingCommandsDb, private val backupConnection: Connection) extends Databases with FileBackup { override def backup(backupFile: File): Unit = SqliteUtils.using(backupConnection.createStatement()) { @@ -87,7 +87,7 @@ object Databases extends Logging { channels = new SqliteChannelsDb(eclairJdbc), peers = new SqlitePeersDb(eclairJdbc), payments = new SqlitePaymentsDb(eclairJdbc), - offers = new SqliteOffersDb(eclairJdbc), + managedOffers = new SqliteOffersDb(eclairJdbc), pendingCommands = new SqlitePendingCommandsDb(eclairJdbc), backupConnection = eclairJdbc ) @@ -100,7 +100,7 @@ object Databases extends Logging { channels: PgChannelsDb, peers: PgPeersDb, payments: PgPaymentsDb, - offers: PgOffersDb, + managedOffers: PgOffersDb, pendingCommands: PgPendingCommandsDb, dataSource: HikariDataSource, lock: PgLock) extends Databases with ExclusiveLock { @@ -161,7 +161,7 @@ object Databases extends Logging { channels = new PgChannelsDb, peers = new PgPeersDb, payments = new PgPaymentsDb, - offers = new PgOffersDb, + managedOffers = new PgOffersDb, pendingCommands = new PgPendingCommandsDb, dataSource = ds, lock = lock) diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/db/DualDatabases.scala b/eclair-core/src/main/scala/fr/acinq/eclair/db/DualDatabases.scala index bed6d5627c..c020998719 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/db/DualDatabases.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/db/DualDatabases.scala @@ -36,7 +36,7 @@ case class DualDatabases(primary: Databases, secondary: Databases) extends Datab override val channels: ChannelsDb = DualChannelsDb(primary.channels, secondary.channels) override val peers: PeersDb = DualPeersDb(primary.peers, secondary.peers) override val payments: PaymentsDb = DualPaymentsDb(primary.payments, secondary.payments) - override val offers: OffersDb = DualOffersDb(primary.offers, secondary.offers) + override val managedOffers: OffersDb = DualOffersDb(primary.managedOffers, secondary.managedOffers) override val pendingCommands: PendingCommandsDb = DualPendingCommandsDb(primary.pendingCommands, secondary.pendingCommands) override val liquidity: LiquidityDb = DualLiquidityDb(primary.liquidity, secondary.liquidity) diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/db/OffersDb.scala b/eclair-core/src/main/scala/fr/acinq/eclair/db/OffersDb.scala index 7ba147a190..2e68cb2609 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/db/OffersDb.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/db/OffersDb.scala @@ -21,10 +21,25 @@ import fr.acinq.eclair.wire.protocol.OfferTypes.Offer case class OfferData(offer: Offer, pathId_opt: Option[ByteVector32]) +/** + * Database for offers fully managed by eclair, as opposed to offers managed by a plugin. + */ trait OffersDb { + /** + * Add an offer managed by eclair. + * @param pathId_opt If the offer uses a blinded path, this is the corresponding pathId. + */ def addOffer(offer: Offer, pathId_opt: Option[ByteVector32]): Unit + /** + * Disable an offer. The offer is still stored but new invoice requests and new payment attempts for already emitted + * invoices will be rejected. To reenable an offer, use `addOffer`. + */ def disableOffer(offer: Offer): Unit + /** + * List offers managed by eclair. + * @param onlyActive Whether to return only active offers or also disabled ones. + */ def listOffers(onlyActive: Boolean): Seq[OfferData] } \ No newline at end of file diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/payment/offer/DefaultHandler.scala b/eclair-core/src/main/scala/fr/acinq/eclair/payment/offer/DefaultHandler.scala index 7e069c13d3..5b6e093a92 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/payment/offer/DefaultHandler.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/payment/offer/DefaultHandler.scala @@ -21,7 +21,7 @@ import akka.actor.typed.scaladsl.Behaviors import akka.actor.{ActorRef, typed} import akka.util.Collections import fr.acinq.eclair.crypto.Sphinx.RouteBlinding.BlindedRoute -import fr.acinq.eclair.payment.receive.MultiPartHandler.ReceivingRoute +import fr.acinq.eclair.payment.receive.MultiPartHandler.{DummyBlindedHop, ReceivingRoute} import fr.acinq.eclair.router.Router import fr.acinq.eclair.router.Router.BlindedRouteRequest import fr.acinq.eclair.wire.protocol.OfferTypes @@ -41,7 +41,7 @@ object DefaultHandler { replyTo ! OfferManager.InvoiceRequestActor.ApproveRequest(amount, Seq(route), None) case OfferTypes.BlindedPath(BlindedRoute(firstNodeId: EncodedNodeId.WithPublicKey, _, _)) => val routeParams = nodeParams.routerConf.pathFindingExperimentConf.getRandomConf().getDefaultRouteParams - router ! BlindedRouteRequest(context.spawnAnonymous(waitForRoute(nodeParams, replyTo, amount)), firstNodeId.publicKey, nodeParams.nodeId, amount, routeParams, pathsToFind = 2) + router ! BlindedRouteRequest(context.spawnAnonymous(waitForRoute(nodeParams, replyTo, amount, routeParams.boundaries.maxRouteLength)), firstNodeId.publicKey, nodeParams.nodeId, amount, routeParams, pathsToFind = 2) case OfferTypes.BlindedPath(BlindedRoute(_: EncodedNodeId.ShortChannelIdDir, _, _)) => context.log.error("unexpected managed offer with compact first node id") replyTo ! OfferManager.InvoiceRequestActor.RejectRequest("internal error") @@ -54,12 +54,18 @@ object DefaultHandler { ) } - def waitForRoute(nodeParams: NodeParams, replyTo: typed.ActorRef[OfferManager.InvoiceRequestActor.Command], amount: MilliSatoshi): Behavior[Router.PaymentRouteResponse] = { + def waitForRoute(nodeParams: NodeParams, replyTo: typed.ActorRef[OfferManager.InvoiceRequestActor.Command], amount: MilliSatoshi, maxRouteLength: Int): Behavior[Router.PaymentRouteResponse] = { Behaviors.receiveMessage { case Router.RouteResponse(routes) => val receivingRoutes = routes.map(route => { val nodes = route.hops.head.nodeId +: route.hops.map(_.nextNodeId) - ReceivingRoute(nodes, nodeParams.channelConf.maxExpiryDelta, dummyHops = ???) + val maxFeeBase = route.hops.map(_.params.relayFees.feeBase).max + val maxFeeProportional = route.hops.map(_.params.relayFees.feeProportionalMillionths).max + val maxCltvExpiryDelta = route.hops.map(_.params.cltvExpiryDelta).max + val dummyHops = (1 to maxRouteLength - route.hops.size).map(i => { + DummyBlindedHop() + }) + ReceivingRoute(nodes, nodeParams.channelConf.maxExpiryDelta, dummyHops) }) replyTo ! OfferManager.InvoiceRequestActor.ApproveRequest(amount, receivingRoutes, None) Behaviors.stopped diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/payment/offer/OfferCreator.scala b/eclair-core/src/main/scala/fr/acinq/eclair/payment/offer/OfferCreator.scala index 763cc11157..5503262dce 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/payment/offer/OfferCreator.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/payment/offer/OfferCreator.scala @@ -112,7 +112,7 @@ private class OfferCreator(context: ActorContext[OfferCreator.Command], } private def registerOffer(offer: Offer, pathId_opt: Option[ByteVector32]): Behavior[Command] = { - nodeParams.db.offers.addOffer(offer, pathId_opt) + nodeParams.db.managedOffers.addOffer(offer, pathId_opt) offerManager ! OfferManager.RegisterOffer(offer, None, pathId_opt, defaultOfferHandler) replyTo ! Right(offer) Behaviors.stopped diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/TestDatabases.scala b/eclair-core/src/test/scala/fr/acinq/eclair/TestDatabases.scala index 179fe1a036..100d697db5 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/TestDatabases.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/TestDatabases.scala @@ -33,7 +33,7 @@ sealed trait TestDatabases extends Databases { override def channels: ChannelsDb = db.channels override def peers: PeersDb = db.peers override def payments: PaymentsDb = db.payments - override def offers: OffersDb = db.offers + override def managedOffers: OffersDb = db.managedOffers override def pendingCommands: PendingCommandsDb = db.pendingCommands override def liquidity: LiquidityDb = db.liquidity def close(): Unit