Skip to content

Commit

Permalink
Trampoline/MPP DB changes (#1287)
Browse files Browse the repository at this point in the history
With MPP and Trampoline (and particularly the combination of the two),
we need to keep track of multiple amounts, recipients and fees.
There's a trampoline fee and a fee to reach the first trampoline node.
The trampoline nodes must appear in the route, but not as payment recipients.

Adding new fields to payment events and DB structs lets us distinguish those.

We also relax the spec requirement about feature graph dependencies.
The requirement to include `var_onion_optin` in invoice feature bits
was added after the first Phoenix release.
Phoenix users will thus have non spec-compliant invoices in their
payment history.
We accept invoices that don't set this field; this is a harmless
spec violation (as long as we set it in new invoices).
  • Loading branch information
t-bast committed Jan 29, 2020
1 parent 16456bb commit 453a7c6
Show file tree
Hide file tree
Showing 35 changed files with 1,036 additions and 535 deletions.
4 changes: 3 additions & 1 deletion eclair-core/src/main/scala/fr/acinq/eclair/Features.scala
Expand Up @@ -90,7 +90,9 @@ object Features {
// Features may depend on other features, as specified in Bolt 9.
private val featuresDependency = Map(
ChannelRangeQueriesExtended -> (ChannelRangeQueries :: Nil),
PaymentSecret -> (VariableLengthOnion :: Nil),
// This dependency requirement was added to the spec after the Phoenix release, which means Phoenix users have "invalid"
// invoices in their payment history. We choose to treat such invoices as valid; this is a harmless spec violation.
// PaymentSecret -> (VariableLengthOnion :: Nil),
BasicMultiPartPayment -> (PaymentSecret :: Nil),
TrampolinePayment -> (PaymentSecret :: Nil)
)
Expand Down
52 changes: 37 additions & 15 deletions eclair-core/src/main/scala/fr/acinq/eclair/db/PaymentsDb.scala
Expand Up @@ -22,7 +22,7 @@ import java.util.UUID
import fr.acinq.bitcoin.ByteVector32
import fr.acinq.bitcoin.Crypto.PublicKey
import fr.acinq.eclair.payment._
import fr.acinq.eclair.router.ChannelHop
import fr.acinq.eclair.router.{ChannelHop, Hop, NodeHop}
import fr.acinq.eclair.{MilliSatoshi, ShortChannelId}

import scala.compat.Platform
Expand All @@ -31,7 +31,7 @@ trait PaymentsDb extends IncomingPaymentsDb with OutgoingPaymentsDb with Payment

trait IncomingPaymentsDb {
/** Add a new expected incoming payment (not yet received). */
def addIncomingPayment(pr: PaymentRequest, preimage: ByteVector32): Unit
def addIncomingPayment(pr: PaymentRequest, preimage: ByteVector32, paymentType: String = PaymentType.Standard): Unit

/**
* Mark an incoming payment as received (paid). The received amount may exceed the payment request amount.
Expand Down Expand Up @@ -80,18 +80,26 @@ trait OutgoingPaymentsDb {

}

case object PaymentType {
val Standard = "Standard"
val SwapIn = "SwapIn"
val SwapOut = "SwapOut"
}

/**
* An incoming payment received by this node.
* At first it is in a pending state once the payment request has been generated, then will become either a success (if
* we receive a valid HTLC) or a failure (if the payment request expires).
*
* @param paymentRequest Bolt 11 payment request.
* @param paymentPreimage pre-image associated with the payment request's payment_hash.
* @param paymentType distinguish different payment types (standard, swaps, etc).
* @param createdAt absolute time in milli-seconds since UNIX epoch when the payment request was generated.
* @param status current status of the payment.
*/
case class IncomingPayment(paymentRequest: PaymentRequest,
paymentPreimage: ByteVector32,
paymentType: String,
createdAt: Long,
status: IncomingPaymentStatus)

Expand Down Expand Up @@ -119,22 +127,26 @@ object IncomingPaymentStatus {
* An outgoing payment sent by this node.
* At first it is in a pending state, then will become either a success or a failure.
*
* @param id internal payment identifier.
* @param parentId internal identifier of a parent payment, or [[id]] if single-part payment.
* @param externalId external payment identifier: lets lightning applications reconcile payments with their own db.
* @param paymentHash payment_hash.
* @param amount amount of the payment, in milli-satoshis.
* @param targetNodeId node ID of the payment recipient.
* @param createdAt absolute time in milli-seconds since UNIX epoch when the payment was created.
* @param paymentRequest Bolt 11 payment request (if paying from an invoice).
* @param status current status of the payment.
* @param id internal payment identifier.
* @param parentId internal identifier of a parent payment, or [[id]] if single-part payment.
* @param externalId external payment identifier: lets lightning applications reconcile payments with their own db.
* @param paymentHash payment_hash.
* @param paymentType distinguish different payment types (standard, swaps, etc).
* @param amount amount that will be received by the target node, will be different from recipientAmount for trampoline payments.
* @param recipientAmount amount that will be received by the final recipient.
* @param recipientNodeId id of the final recipient.
* @param createdAt absolute time in milli-seconds since UNIX epoch when the payment was created.
* @param paymentRequest Bolt 11 payment request (if paying from an invoice).
* @param status current status of the payment.
*/
case class OutgoingPayment(id: UUID,
parentId: UUID,
externalId: Option[String],
paymentHash: ByteVector32,
paymentType: String,
amount: MilliSatoshi,
targetNodeId: PublicKey,
recipientAmount: MilliSatoshi,
recipientNodeId: PublicKey,
createdAt: Long,
paymentRequest: Option[PaymentRequest],
status: OutgoingPaymentStatus)
Expand All @@ -151,8 +163,9 @@ object OutgoingPaymentStatus {
* We now have a valid proof-of-payment.
*
* @param paymentPreimage the preimage of the payment_hash.
* @param feesPaid total amount of fees paid to intermediate routing nodes.
* @param route payment route.
* @param feesPaid fees paid to route to the target node (which not necessarily the final recipient, e.g. when
* trampoline is used).
* @param route payment route used.
* @param completedAt absolute time in milli-seconds since UNIX epoch when the payment was completed.
*/
case class Succeeded(paymentPreimage: ByteVector32, feesPaid: MilliSatoshi, route: Seq[HopSummary], completedAt: Long) extends OutgoingPaymentStatus
Expand All @@ -176,7 +189,13 @@ case class HopSummary(nodeId: PublicKey, nextNodeId: PublicKey, shortChannelId:
}

object HopSummary {
def apply(h: ChannelHop): HopSummary = HopSummary(h.nodeId, h.nextNodeId, Some(h.lastUpdate.shortChannelId))
def apply(h: Hop): HopSummary = {
val shortChannelId = h match {
case ChannelHop(_, _, channelUpdate) => Some(channelUpdate.shortChannelId)
case _: NodeHop => None
}
HopSummary(h.nodeId, h.nextNodeId, shortChannelId)
}
}

/** A minimal representation of a payment failure (suitable to store in a database). */
Expand Down Expand Up @@ -210,13 +229,15 @@ trait PaymentsOverviewDb {
*/
sealed trait PlainPayment {
val paymentHash: ByteVector32
val paymentType: String
val paymentRequest: Option[String]
val finalAmount: Option[MilliSatoshi]
val createdAt: Long
val completedAt: Option[Long]
}

case class PlainIncomingPayment(paymentHash: ByteVector32,
paymentType: String,
finalAmount: Option[MilliSatoshi],
paymentRequest: Option[String],
status: IncomingPaymentStatus,
Expand All @@ -227,6 +248,7 @@ case class PlainIncomingPayment(paymentHash: ByteVector32,
case class PlainOutgoingPayment(parentId: Option[UUID],
externalId: Option[String],
paymentHash: ByteVector32,
paymentType: String,
finalAmount: Option[MilliSatoshi],
paymentRequest: Option[String],
status: OutgoingPaymentStatus,
Expand Down

0 comments on commit 453a7c6

Please sign in to comment.