Skip to content

Commit

Permalink
Refactor Payment classes.
Browse files Browse the repository at this point in the history
The PaymentOptions embeds the amount and expiry and additional tlvs.
The PaymentInitiator translates payment parameters before forwarding to PaymentLifecycle.
This make it easier to add new payment capabilities before the PaymentLifecycle layer (AMP, Loop, Trampoline, etc).
  • Loading branch information
t-bast committed Aug 29, 2019
1 parent f235d97 commit f1d68ea
Show file tree
Hide file tree
Showing 14 changed files with 233 additions and 146 deletions.
18 changes: 9 additions & 9 deletions eclair-core/src/main/scala/fr/acinq/eclair/Eclair.scala
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ import fr.acinq.eclair.channel._
import fr.acinq.eclair.db.{IncomingPayment, NetworkFee, OutgoingPayment, Stats}
import fr.acinq.eclair.io.Peer.{GetPeerInfo, PeerInfo}
import fr.acinq.eclair.io.{NodeURI, Peer}
import fr.acinq.eclair.payment.PaymentLifecycle._
import fr.acinq.eclair.payment.PaymentInitiator.SendPaymentRequest
import fr.acinq.eclair.payment.PaymentLifecycle.ReceivePayment
import fr.acinq.eclair.payment._
import fr.acinq.eclair.router.{ChannelDesc, RouteRequest, RouteResponse, Router}
import fr.acinq.eclair.wire.{ChannelAnnouncement, ChannelUpdate, NodeAddress, NodeAnnouncement}
Expand All @@ -53,7 +54,6 @@ object TimestampQueryFilters {
}
}


trait Eclair {

def connect(target: Either[NodeURI, PublicKey])(implicit timeout: Timeout): Future[String]
Expand Down Expand Up @@ -187,7 +187,7 @@ class EclairImpl(appKit: Kit) extends Eclair {
}

override def sendToRoute(route: Seq[PublicKey], amount: MilliSatoshi, paymentHash: ByteVector32, finalCltvExpiryDelta: CltvExpiryDelta)(implicit timeout: Timeout): Future[UUID] = {
(appKit.paymentInitiator ? SendPaymentToRoute(amount, paymentHash, route, finalCltvExpiryDelta)).mapTo[UUID]
(appKit.paymentInitiator ? SendPaymentRequest(amount, paymentHash, route.last, 1, finalCltvExpiryDelta, route)).mapTo[UUID]
}

override def send(recipientNodeId: PublicKey, amount: MilliSatoshi, paymentHash: ByteVector32, assistedRoutes: Seq[Seq[PaymentRequest.ExtraHop]] = Seq.empty, minFinalCltvExpiryDelta_opt: Option[CltvExpiryDelta], maxAttempts_opt: Option[Int], feeThreshold_opt: Option[Satoshi], maxFeePct_opt: Option[Double])(implicit timeout: Timeout): Future[UUID] = {
Expand All @@ -200,8 +200,8 @@ class EclairImpl(appKit: Kit) extends Eclair {
)

val sendPayment = minFinalCltvExpiryDelta_opt match {
case Some(minCltv) => SendPayment(amount, paymentHash, recipientNodeId, assistedRoutes, finalCltvExpiryDelta = minCltv, maxAttempts = maxAttempts, routeParams = Some(routeParams))
case None => SendPayment(amount, paymentHash, recipientNodeId, assistedRoutes, maxAttempts = maxAttempts, routeParams = Some(routeParams))
case Some(minCltv) => SendPaymentRequest(amount, paymentHash, recipientNodeId, maxAttempts, minCltv, assistedRoutes = assistedRoutes, routeParams = Some(routeParams))
case None => SendPaymentRequest(amount, paymentHash, recipientNodeId, maxAttempts, assistedRoutes = assistedRoutes, routeParams = Some(routeParams))
}
(appKit.paymentInitiator ? sendPayment).mapTo[UUID]
}
Expand Down Expand Up @@ -252,10 +252,10 @@ class EclairImpl(appKit: Kit) extends Eclair {
}

/**
* Sends a request to a channel and expects a response
*
* @param channelIdentifier either a shortChannelId (BOLT encoded) or a channelId (32-byte hex encoded)
*/
* Sends a request to a channel and expects a response
*
* @param channelIdentifier either a shortChannelId (BOLT encoded) or a channelId (32-byte hex encoded)
*/
def sendToChannel(channelIdentifier: Either[ByteVector32, ShortChannelId], request: Any)(implicit timeout: Timeout): Future[Any] = channelIdentifier match {
case Left(channelId) => appKit.register ? Forward(channelId, request)
case Right(shortChannelId) => appKit.register ? ForwardShortId(shortChannelId, request)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,18 @@ package fr.acinq.eclair.payment
import akka.actor.{Actor, ActorLogging, ActorRef, Props}
import fr.acinq.bitcoin.Crypto.PublicKey
import fr.acinq.eclair.crypto.Sphinx.DecryptedFailurePacket
import fr.acinq.eclair.payment.PaymentLifecycle.{PaymentFailed, PaymentResult, RemoteFailure, SendPayment}
import fr.acinq.eclair.payment.PaymentInitiator.SendPaymentRequest
import fr.acinq.eclair.payment.PaymentLifecycle.{PaymentFailed, PaymentResult, RemoteFailure}
import fr.acinq.eclair.router.{Announcements, Data, PublicChannel}
import fr.acinq.eclair.wire.IncorrectOrUnknownPaymentDetails
import fr.acinq.eclair.{LongToBtcAmount, NodeParams, randomBytes32, secureRandom}

import scala.concurrent.duration._

/**
* This actor periodically probes the network by sending payments to random nodes. The payments will eventually fail
* because the recipient doesn't know the preimage, but it allows us to test channels and improve routing for real payments.
*/
* This actor periodically probes the network by sending payments to random nodes. The payments will eventually fail
* because the recipient doesn't know the preimage, but it allows us to test channels and improve routing for real payments.
*/
class Autoprobe(nodeParams: NodeParams, router: ActorRef, paymentInitiator: ActorRef) extends Actor with ActorLogging {

import Autoprobe._
Expand All @@ -54,7 +55,7 @@ class Autoprobe(nodeParams: NodeParams, router: ActorRef, paymentInitiator: Acto
case Some(targetNodeId) =>
val paymentHash = randomBytes32 // we don't even know the preimage (this needs to be a secure random!)
log.info(s"sending payment probe to node=$targetNodeId payment_hash=$paymentHash")
paymentInitiator ! SendPayment(PAYMENT_AMOUNT_MSAT, paymentHash, targetNodeId, maxAttempts = 1)
paymentInitiator ! SendPaymentRequest(PAYMENT_AMOUNT_MSAT, paymentHash, targetNodeId, maxAttempts = 1)
case None =>
log.info(s"could not find a destination, re-scheduling")
scheduleProbe()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,25 +17,45 @@
package fr.acinq.eclair.payment

import java.util.UUID

import akka.actor.{Actor, ActorLogging, ActorRef, Props}
import fr.acinq.eclair.NodeParams
import fr.acinq.eclair.payment.PaymentLifecycle.GenericSendPayment
import fr.acinq.bitcoin.ByteVector32
import fr.acinq.bitcoin.Crypto.PublicKey
import fr.acinq.eclair.channel.Channel
import fr.acinq.eclair.payment.PaymentLifecycle.{LegacyPayload, SendPayment, SendPaymentToRoute}
import fr.acinq.eclair.payment.PaymentRequest.ExtraHop
import fr.acinq.eclair.router.RouteParams
import fr.acinq.eclair.{CltvExpiryDelta, MilliSatoshi, NodeParams}

/**
* Created by PM on 29/08/2016.
*/
* Created by PM on 29/08/2016.
*/
class PaymentInitiator(nodeParams: NodeParams, router: ActorRef, register: ActorRef) extends Actor with ActorLogging {

override def receive: Receive = {
case c: GenericSendPayment =>
case p: PaymentInitiator.SendPaymentRequest =>
val paymentId = UUID.randomUUID()
val payFsm = context.actorOf(PaymentLifecycle.props(nodeParams, paymentId, router, register))
payFsm forward c
p.predefinedRoute match {
case Nil => payFsm forward SendPayment(p.paymentHash, p.targetNodeId, LegacyPayload(p.amount, p.finalExpiryDelta.toCltvExpiry), p.maxAttempts, p.assistedRoutes, p.routeParams)
case hops => payFsm forward SendPaymentToRoute(p.paymentHash, hops, LegacyPayload(p.amount, p.finalExpiryDelta.toCltvExpiry))
}
sender ! paymentId
}

}

object PaymentInitiator {

def props(nodeParams: NodeParams, router: ActorRef, register: ActorRef) = Props(classOf[PaymentInitiator], nodeParams, router, register)

case class SendPaymentRequest(amount: MilliSatoshi,
paymentHash: ByteVector32,
targetNodeId: PublicKey,
maxAttempts: Int,
finalExpiryDelta: CltvExpiryDelta = Channel.MIN_CLTV_EXPIRY_DELTA,
predefinedRoute: Seq[PublicKey] = Nil,
assistedRoutes: Seq[Seq[ExtraHop]] = Nil,
routeParams: Option[RouteParams] = None)

}
Loading

0 comments on commit f1d68ea

Please sign in to comment.