Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
t-bast committed Jul 11, 2024
1 parent eaa9e40 commit d33f661
Show file tree
Hide file tree
Showing 20 changed files with 144 additions and 77 deletions.
14 changes: 14 additions & 0 deletions TODO.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# TODO

- I removed `enable-trampoline-payments`, so the ACINQ node must:
- activate `trampoline_payment`
- activate `trampoline_payment_prototype` to support previous Phoenix versions
- add trampoline to Bolt 12 features (can be requested in `invoice_request`)
- add blinded relay with trampoline onion (when intermediate node)
- write BOLT spec
- create official test vector for trampoline-to-blinded-path
- add trampoline failures defined in the spec
- `lightning-kmp`:
- must support both options for a while (to allow being paid by older Phoenix)
- Bolt 11 invoices will simply set both feature bits
- Bolt 12 invoices will do it based on what the `invoice_request` contains
8 changes: 1 addition & 7 deletions docs/TrampolinePayments.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,7 @@

Eclair started supporting [trampoline payments](https://github.com/lightning/bolts/pull/829) in v0.3.3.

It is disabled by default, as it is still being reviewed for spec acceptance. However, if you want to experiment with it, here is what you can do.

First of all, you need to activate the feature for any node that will act as a trampoline node. Update your `eclair.conf` with the following values:

```conf
eclair.trampoline-payments-enable=true
```
It is now active by default, since it has been added to the [BOLTs](https://github.com/lightning/bolts/pull/836).

## Sending trampoline payments

Expand Down
4 changes: 4 additions & 0 deletions docs/release-notes/eclair-vnext.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ To enable CoinGrinder at all fee rates and prevent the automatic consolidation o
consolidatefeerate=0
```

### Trampoline payments

TODO: link to final spec

### API changes

<insert changes>
Expand Down
2 changes: 1 addition & 1 deletion eclair-core/src/main/resources/reference.conf
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ eclair {
node-alias = "eclair"
node-color = "49daaa"

trampoline-payments-enable = false // TODO: @t-bast: once spec-ed this should use a global feature flag
// see https://github.com/lightningnetwork/lightning-rfc/blob/master/09-features.md
features {
// option_upfront_shutdown_script is not activated by default.
Expand Down Expand Up @@ -81,6 +80,7 @@ eclair {
// node that you trust using override-init-features (see below).
option_zeroconf = disabled
keysend = disabled
trampoline_routing = optional
trampoline_payment_prototype = disabled
async_payment_prototype = disabled
}
Expand Down
18 changes: 11 additions & 7 deletions eclair-core/src/main/scala/fr/acinq/eclair/Features.scala
Original file line number Diff line number Diff line change
Expand Up @@ -300,13 +300,15 @@ object Features {
val mandatory = 54
}

// TODO: @t-bast: update feature bits once spec-ed (currently reserved here: https://github.com/lightningnetwork/lightning-rfc/issues/605)
// We're not advertising these bits yet in our announcements, clients have to assume support.
// This is why we haven't added them yet to `areSupported`.
// The version of trampoline enabled by this feature bit does not match the latest spec PR: once the spec is accepted,
// we will introduce a new version of trampoline that will work in parallel to this legacy one, until we can safely
// deprecate it.
case object TrampolinePaymentPrototype extends Feature with InitFeature with NodeFeature with Bolt11Feature {
case object TrampolinePayment extends Feature with InitFeature with NodeFeature with Bolt11Feature {
val rfcName = "trampoline_routing"
val mandatory = 56
}

// We don't advertise this feature bit in our announcements (clients assumed support).
// We should keep supporting this version until clients have all migrated to the official version above.
// TODO: remove this and the related onion TLVs once it is unused.
case object TrampolinePaymentPrototype extends Feature with InitFeature with Bolt11Feature {
val rfcName = "trampoline_payment_prototype"
val mandatory = 148
}
Expand Down Expand Up @@ -346,6 +348,7 @@ object Features {
PaymentMetadata,
ZeroConf,
KeySend,
TrampolinePayment,
TrampolinePaymentPrototype,
AsyncPaymentPrototype,
SplicePrototype,
Expand All @@ -361,6 +364,7 @@ object Features {
RouteBlinding -> (VariableLengthOnion :: Nil),
TrampolinePaymentPrototype -> (PaymentSecret :: Nil),
KeySend -> (VariableLengthOnion :: Nil),
TrampolinePayment -> (BasicMultiPartPayment :: Nil),
AsyncPaymentPrototype -> (TrampolinePaymentPrototype :: Nil)
)

Expand Down
2 changes: 0 additions & 2 deletions eclair-core/src/main/scala/fr/acinq/eclair/NodeParams.scala
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,6 @@ case class NodeParams(nodeKeyManager: NodeKeyManager,
socksProxy_opt: Option[Socks5ProxyParams],
maxPaymentAttempts: Int,
paymentFinalExpiry: PaymentFinalExpiryConf,
enableTrampolinePayment: Boolean,
balanceCheckInterval: FiniteDuration,
blockchainWatchdogThreshold: Int,
blockchainWatchdogSources: Seq[String],
Expand Down Expand Up @@ -596,7 +595,6 @@ object NodeParams extends Logging {
socksProxy_opt = socksProxy_opt,
maxPaymentAttempts = config.getInt("max-payment-attempts"),
paymentFinalExpiry = PaymentFinalExpiryConf(CltvExpiryDelta(config.getInt("send.recipient-final-expiry.min-delta")), CltvExpiryDelta(config.getInt("send.recipient-final-expiry.max-delta"))),
enableTrampolinePayment = config.getBoolean("trampoline-payments-enable"),
balanceCheckInterval = FiniteDuration(config.getDuration("balance-check-interval").getSeconds, TimeUnit.SECONDS),
blockchainWatchdogThreshold = config.getInt("blockchain-watchdog.missing-blocks-threshold"),
blockchainWatchdogSources = config.getStringList("blockchain-watchdog.sources").asScala.toSeq,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -329,11 +329,6 @@ object MultiPartHandler {
val paymentHash = Crypto.sha256(paymentPreimage)
val expirySeconds = r.expirySeconds_opt.getOrElse(nodeParams.invoiceExpiry.toSeconds)
val paymentMetadata = hex"2a"
val featuresTrampolineOpt = if (nodeParams.enableTrampolinePayment) {
nodeParams.features.bolt11Features().add(Features.TrampolinePaymentPrototype, FeatureSupport.Optional)
} else {
nodeParams.features.bolt11Features()
}
val invoice = Bolt11Invoice(
nodeParams.chainHash,
r.amount_opt,
Expand All @@ -345,7 +340,7 @@ object MultiPartHandler {
expirySeconds = Some(expirySeconds),
extraHops = r.extraHops,
paymentMetadata = Some(paymentMetadata),
features = featuresTrampolineOpt
features = nodeParams.features.bolt11Features()
)
context.log.debug("generated invoice={} from amount={}", invoice.toString, r.amount_opt)
nodeParams.db.payments.addIncomingPayment(invoice, paymentPreimage, r.paymentType)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import fr.acinq.eclair.channel._
import fr.acinq.eclair.db.PendingCommandsDb
import fr.acinq.eclair.payment._
import fr.acinq.eclair.wire.protocol._
import fr.acinq.eclair.{CltvExpiryDelta, Logs, MilliSatoshi, NodeParams}
import fr.acinq.eclair.{CltvExpiryDelta, Features, Logs, MilliSatoshi, NodeParams}
import grizzled.slf4j.Logging

import scala.concurrent.Promise
Expand Down Expand Up @@ -71,7 +71,7 @@ class Relayer(nodeParams: NodeParams, router: ActorRef, register: ActorRef, paym
case Right(r: IncomingPaymentPacket.ChannelRelayPacket) =>
channelRelayer ! ChannelRelayer.Relay(r)
case Right(r: IncomingPaymentPacket.NodeRelayPacket) =>
if (!nodeParams.enableTrampolinePayment) {
if (!nodeParams.features.hasFeature(Features.TrampolinePayment) && !nodeParams.features.hasFeature(Features.TrampolinePaymentPrototype)) {
log.warning(s"rejecting htlc #${add.id} from channelId=${add.channelId} reason=trampoline disabled")
PendingCommandsDb.safeSend(register, nodeParams.db.pendingCommands, add.channelId, CMD_FAIL_HTLC(add.id, Right(RequiredNodeFeatureMissing()), commit = true))
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ class PaymentInitiator(nodeParams: NodeParams, outgoingPaymentFactory: PaymentIn
r.trampolineAttempts match {
case Nil =>
r.replyTo ! PaymentFailed(paymentId, r.paymentHash, LocalFailure(r.recipientAmount, Nil, TrampolineFeesMissing) :: Nil)
case _ if !r.invoice.features.hasFeature(Features.TrampolinePaymentPrototype) && r.invoice.amount_opt.isEmpty =>
case _ if !r.invoice.features.hasFeature(Features.TrampolinePayment) && r.invoice.amount_opt.isEmpty =>
r.replyTo ! PaymentFailed(paymentId, r.paymentHash, LocalFailure(r.recipientAmount, Nil, TrampolineLegacyAmountLessInvoice) :: Nil)
case (trampolineFees, trampolineExpiryDelta) :: remainingAttempts =>
log.info(s"sending trampoline payment with trampoline fees=$trampolineFees and expiry delta=$trampolineExpiryDelta")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,9 @@ case class TrampolineRecipient(invoice: Invoice,
private def createTrampolinePacket(paymentHash: ByteVector32, trampolineHop: NodeHop): Either[OutgoingPaymentError, Sphinx.PacketAndSecrets] = {
invoice match {
case invoice: Bolt11Invoice =>
if (invoice.features.hasFeature(Features.TrampolinePaymentPrototype)) {
if (invoice.features.hasFeature(Features.TrampolinePayment)) {
???
} else if (invoice.features.hasFeature(Features.TrampolinePaymentPrototype)) {
// This is the payload the final recipient will receive, so we use the invoice's payment secret.
val finalPayload = NodePayload(nodeId, FinalPayload.Standard.createPayload(totalAmount, totalAmount, expiry, invoice.paymentSecret, invoice.paymentMetadata, customTlvs))
val trampolinePayload = NodePayload(trampolineHop.nodeId, IntermediatePayload.NodeRelay.Standard(totalAmount, expiry, nodeId))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,7 @@ object PaymentOnion {
}

object NodeRelay {
// TODO: rename Legacy?
case class Standard(records: TlvStream[OnionPaymentPayloadTlv]) extends NodeRelay {
val outgoingNodeId = records.get[OutgoingNodeId].get.nodeId
// The following fields are only included in the trampoline-to-legacy case.
Expand Down Expand Up @@ -553,9 +554,12 @@ object PaymentOnionCodecs {
.typecase(UInt64(8), paymentData)
.typecase(UInt64(10), encryptedRecipientData)
.typecase(UInt64(12), blindingPoint)
.typecase(UInt64(14), outgoingNodeId)
.typecase(UInt64(16), paymentMetadata)
.typecase(UInt64(18), totalAmount)
.typecase(UInt64(20), trampolineOnion)
// Types below aren't specified - use cautiously when deploying (be careful with backwards-compatibility).
// TODO: need a "Legacy" version of all of those types!
.typecase(UInt64(66097), invoiceFeatures)
.typecase(UInt64(66098), outgoingNodeId)
.typecase(UInt64(66099), invoiceRoutingInfo)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ object TestConstants {
Wumbo -> Optional,
PaymentMetadata -> Optional,
RouteBlinding -> Optional,
TrampolinePayment -> Optional,
),
unknown = Set(UnknownFeature(TestFeature.optional))
),
Expand Down Expand Up @@ -217,7 +218,6 @@ object TestConstants {
socksProxy_opt = None,
maxPaymentAttempts = 5,
paymentFinalExpiry = PaymentFinalExpiryConf(CltvExpiryDelta(1), CltvExpiryDelta(1)),
enableTrampolinePayment = true,
instanceId = UUID.fromString("aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"),
balanceCheckInterval = 1 hour,
blockchainWatchdogThreshold = 6,
Expand Down Expand Up @@ -271,6 +271,7 @@ object TestConstants {
Wumbo -> Optional,
PaymentMetadata -> Optional,
RouteBlinding -> Optional,
TrampolinePayment -> Optional,
),
pluginParams = Nil,
overrideInitFeatures = Map.empty,
Expand Down Expand Up @@ -384,7 +385,6 @@ object TestConstants {
socksProxy_opt = None,
maxPaymentAttempts = 5,
paymentFinalExpiry = PaymentFinalExpiryConf(CltvExpiryDelta(1), CltvExpiryDelta(1)),
enableTrampolinePayment = true,
instanceId = UUID.fromString("bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"),
balanceCheckInterval = 1 hour,
blockchainWatchdogThreshold = 6,
Expand Down
Loading

0 comments on commit d33f661

Please sign in to comment.