Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Relay Trampoline payments #1220

Merged
merged 8 commits into from
Dec 18, 2019
Merged

Relay Trampoline payments #1220

merged 8 commits into from
Dec 18, 2019

Conversation

t-bast
Copy link
Member

@t-bast t-bast commented Nov 21, 2019

TODO in this PR (MVP, can be merged on master):

  • Recovery after a restart
  • Store correct recipient and fee in the DB
  • Emit a payment-relayed event for Trampoline

To limit the number of changes to review, I'll stop there. At this point (once we've properly reviewed this code) merging to master shouldn't have any negative impact (no DB schema update, trampoline disabled, post-restart should work, etc).

The trampoline UX will be somewhat bad because many improvements/polish are missing.
Some shortcuts were taken, a few hacks here and there need to be fixed, but nothing too scary.
Those improvements will be done in separate PRs to allow a better code review experience.

TODO in subsequent PRs (post-MVP, should be done before release):

  • Store accurate Trampoline data in the DB
  • Send accurate Trampoline data in payment events
  • Send better onion errors (add new ones)
  • Add Kamon instrumentation
  • Re-work send API: needs cleaner MPP and Trampoline integration

@codecov-io
Copy link

codecov-io commented Nov 21, 2019

Codecov Report

Merging #1220 into master will increase coverage by 0.41%.
The diff coverage is 90.8%.

@@            Coverage Diff             @@
##           master    #1220      +/-   ##
==========================================
+ Coverage   76.66%   77.07%   +0.41%     
==========================================
  Files         140      142       +2     
  Lines        9710     9880     +170     
  Branches      390      402      +12     
==========================================
+ Hits         7444     7615     +171     
+ Misses       2266     2265       -1
Impacted Files Coverage Δ
.../scala/fr/acinq/eclair/payment/PaymentEvents.scala 100% <ø> (ø) ⬆️
...in/scala/fr/acinq/eclair/channel/Commitments.scala 88.54% <ø> (ø) ⬆️
...r-core/src/main/scala/fr/acinq/eclair/Eclair.scala 54.02% <0%> (-1.93%) ⬇️
...r/acinq/eclair/payment/send/PaymentInitiator.scala 97.14% <100%> (ø) ⬆️
...c/main/scala/fr/acinq/eclair/payment/Auditor.scala 94.18% <100%> (+0.28%) ⬆️
...q/eclair/payment/receive/MultiPartPaymentFSM.scala 92.5% <100%> (+0.39%) ⬆️
...clair/payment/send/MultiPartPaymentLifecycle.scala 98.22% <100%> (+1.88%) ⬆️
...ain/scala/fr/acinq/eclair/wire/ChannelCodecs.scala 100% <100%> (ø) ⬆️
...re/src/main/scala/fr/acinq/eclair/wire/Onion.scala 96.34% <100%> (ø) ⬆️
...in/scala/fr/acinq/eclair/api/JsonSerializers.scala 95.23% <100%> (ø) ⬆️
... and 19 more

@t-bast t-bast force-pushed the trampoline-routing branch from acced8d to 1c08704 Compare November 21, 2019 13:02
Recovery after a restart isn't implemented yet.
A lot of polish is still needed to send the right errors and emit the correct events.
The previous logic has been moved from Switchboard to payments.

There are two cases to consider:
  * HTLCs not relayed at all: we can fail instantly
  * HTLCs partially relayed: we need to wait for downstream responses
@t-bast t-bast force-pushed the trampoline-routing branch from 1c08704 to 770d32f Compare November 29, 2019 16:24
def main(pendingIncoming: Map[ByteVector32, PendingRelay], pendingOutgoing: Map[UUID, Upstream.TrampolineRelayed]): Receive = {
// We make sure we receive all payment parts before forwarding to the next trampoline node.
case IncomingPacket.NodeRelayPacket(add, outer, inner, next) => outer.paymentSecret match {
case None => rejectHtlc(add.id, add.channelId, add.amountMsat)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

payment secret is mandatory for trampoline?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this is something we need to do whenever we can for security against probing / fee stealing

case MultiPartPaymentFSM.MultiPartHtlcSucceeded(paymentHash, parts) => pendingIncoming.get(paymentHash) match {
case Some(PendingRelay(htlcs, _, nextPayload, nextPacket, handler)) =>
val upstream = Upstream.TrampolineRelayed(htlcs)
handler ! PoisonPill
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still not convinced about this pattern of not immediately shutting down the multipart payment fsm once the payment has succeeded/failed. The race condition with extraneous htlcs arriving is present in both cases. I think we should experiment with a deadletter handler instead.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's worth investigating, but it's not really a trampoline issue - this is rather something we could change in the MultiPartPaymentFSM (independently of this PR)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added that to my post-MVP todo-list

This prevents a conflict between channels being reestablished and post-restart logic.
This is ugly but works (except in one corner-case).
This is a temporary measure: the right way to fix this is to update the DB schema
to correctly account for Trampoline data.

Since DB schema changes are painful, we will aggregate all of them in one change later.
When we do that we will make this more elegant.
This event is stored in the AuditDb and linked to payment metrics.
We're currently loosing some information by collapsing it to a legacy relayed event.
We will update the DB schema in a later change to store more information.
@t-bast t-bast marked this pull request as ready for review December 2, 2019 16:58
})

override def mdc(currentMessage: Any): MDC = {
val paymentHash_opt = currentMessage match {
case IncomingPacket.NodeRelayPacket(add, _, _, _) => Some(add.paymentHash)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: using extractors is nice but it will break everytime the signature of any of the classes changes

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants