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

Trampoline payments #1034

Merged
merged 8 commits into from
Aug 16, 2024
Merged

Trampoline payments #1034

merged 8 commits into from
Aug 16, 2024

Conversation

JssDWt
Copy link
Contributor

@JssDWt JssDWt commented Jul 8, 2024

This PR adds trampoline payment functionality to the SDK.

If a client sends a payment, normally the greenlight node will find routes through the gossip graph, and attempt to pay over those routes. If a route fails, it tries again. For every attempt there are multiple rounds where the greenlight node has to communicate with the signer. Trampoline payments have the advantage there will be only one outgoing payment attempt, and the LSP will do the retrying, therefore less communication rounds are necessary, hopefully improving payment performance. The tradeoff is the user may pay more fees for the route, and the user loses some privacy in the current setup, because the LSP will learn the payment destination. The initial default trampoline policy will be 0,5%.

This PR leans on the corresponding greenlight PR for trampoline payments, which is still a work in process: Blockstream/greenlight#475

The LSP will need to run this plugin: https://github.com/breez/trampoline

Some notes about this PR:

  • Trampoline payments should only be attempted when the LSP is not in the route hint. This is because the LSP side trampoline plugin doesn't cooperate nicely with lspd in it's current state.
  • The trampoline payments are automatically fetched with listpays. That's nice, because it doesn't require any changes to sync. The payments from listpays have the wrong amount_msat due to the way trampoline payments work. We solve that by putting the actual amount in a payment label for trampoline payments.

TODO:

  • needs update of the greenlight commit id before getting merged, since it's pointing at a PR

Copy link
Collaborator

@dangeross dangeross left a comment

Choose a reason for hiding this comment

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

Looking good. I would suggest also:

  • Checking that the LSP can do trampoline payments (via an LSPInformation flag?)
  • Having a config option to not use trampoline payments (for privacy preserving)
  • Is it worth prechecking the outbound liquidity to the LSP before the trampoline payment or can the LSP more efficiently do that? (This is think about the edge case of switching LSP and maybe most/all of the liquidity is still with the old LSP)

@roeierez
Copy link
Member

Good points @dangeross .
Regarding checking lsp liquidity since this check comes with some latency price for each payment and it is an edge case I would consider trying trampoline and if failing for liquidity fallback to regular payment. I think it should fail fast since the node plugin should check that before sending any htlcs

@JssDWt JssDWt force-pushed the jssdwt-trampoline branch 2 times, most recently from 8a68398 to 5371c3c Compare July 12, 2024 07:41
@JssDWt
Copy link
Contributor Author

JssDWt commented Jul 12, 2024

@dangeross @roeierez please check my last 7 commits.
Instead of calling lsp_info (which is a network call) I added two changes:

  • the LSP pubkey is now cached for quick lookup.
  • the node_state now contains the peer's 'features' as well (currently only trampoline is a supported feature)

With these two pieces of information we can check:

  • does the route hint contain a hint to our LSP?
  • Does the LSP support trampoline?

Next to that I added a flag in the SendPaymentRequest and LnurlPayRequest to signal the user doesn't want to use trampoline.

Copy link
Collaborator

@dangeross dangeross left a comment

Choose a reason for hiding this comment

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

Looking good @JssDWt

Comment on lines 1260 to 1302
let features = self
.node_api
.connect_peer(node_id.clone(), address.clone())
.await
.map_err(|e| SdkError::ServiceConnectivity {
err: format!("(LSP: {node_id}) Failed to connect: {e}"),
})?;
node_state.connected_peers.push(ConnectedPeer {
id: node_id.clone(),
features,
});
self.persister.set_node_state(&node_state)?;
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm wondering if updating the node_state's connected peers could done in node_api.connect_peer(), but AFAIK this is the only time its called

libs/sdk-core/src/models.rs Outdated Show resolved Hide resolved
@JssDWt
Copy link
Contributor Author

JssDWt commented Jul 12, 2024

I had a case where the trampoline payment was not attempted. I don't think this mechanism is robust enough. Maybe the peer feature check has to be removed?

@JssDWt JssDWt changed the title DRAFT: Trampoline payments Trampoline payments Jul 12, 2024
@JssDWt JssDWt marked this pull request as ready for review July 12, 2024 21:29
@JssDWt
Copy link
Contributor Author

JssDWt commented Jul 13, 2024

The status of this PR today is:

  • Trampoline payments work.
  • There is a transport error from greenlight's trampoline plugin that makes it seem the payment failed.
  • The peer feature check doesn't seem robust enough. Trampoline payments were skipped at least once when testing. It could be we have to remove the following lines so the greenlight trampoline plugin is in charge of the feature check. If these lines are removed, the corresponding peerfeatures migration and the udl change can also be removed.
    https://github.com/breez/breez-sdk/pull/1034/files#diff-6f499d3ded5e493c1bd95b827a0a93aeb3d2bfa3d04a6f26be62d87aff026e43R370-R384
  • I wonder whether trampoline_pay has the same assurences as pay in the sense that it should always work and wait for the node to be available.

@JssDWt JssDWt force-pushed the jssdwt-trampoline branch 5 times, most recently from bd8b83f to 11607cd Compare August 12, 2024 11:02
@JssDWt
Copy link
Contributor Author

JssDWt commented Aug 12, 2024

  • Trampoline payments now are opt-in through the use_trampoline flag.
  • Removed the feature bit check on the client side, meaning the client will always call trampoline_pay when opting in to trampoline payments. Even if the LSP doesn't support them. The call should fail fast though.

@JssDWt JssDWt requested a review from dangeross August 12, 2024 11:11
@JssDWt
Copy link
Contributor Author

JssDWt commented Aug 12, 2024

Re-requesting review from @roeierez and @dangeross

Copy link
Collaborator

@dangeross dangeross left a comment

Choose a reason for hiding this comment

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

Looks good I think, just 2 issues with the comments

libs/sdk-common/src/lnurl/specs/pay.rs Outdated Show resolved Hide resolved
libs/sdk-core/src/models.rs Outdated Show resolved Hide resolved
Copy link
Collaborator

@dangeross dangeross left a comment

Choose a reason for hiding this comment

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

LGTM.

I imagine at some point people will ask about LSP trampoline fees. Maybe we should follow up at some point to add the fee rate to the LSP info?

@JssDWt
Copy link
Contributor Author

JssDWt commented Aug 12, 2024

I imagine at some point people will ask about LSP trampoline fees. Maybe we should follow up at some point to add the fee rate to the LSP info?

Yes.
The fee rate is currently not advertised in our custom protocol. That could be an additional feature. We assume a fixed fee of 0.5%.

Copy link
Member

@roeierez roeierez left a comment

Choose a reason for hiding this comment

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

Looks good!

Comment on lines 1260 to 1261
self.persister.set_lsp_id(lsp.id)?;
self.persister.set_lsp_pubkey(lsp.pubkey.clone())?;
Copy link
Member

Choose a reason for hiding this comment

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

As it seems these functions are always being called together now, perhaps better to have one function that takes these two arguments?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is currently in 2 places, and setting lsp id without pubkey is in one place here. If you still think we should make that a single function, I'll fix that. We could make the function check whether the new lsp id is the same, if it's not, erase the lsp pubkey if not supplied.

Copy link
Member

Choose a reason for hiding this comment

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

I see, I missed that. Then it is less priority but I think it is slightly better to have one function.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Now setting these in a single function in 4a0a150

// TODO: Ensure this works with trampoline payments
// NOTE: If this doesn't work with trampoline payments, the sync also
// needs updating.
let payment = Self::fetch_outgoing_payment_with_retry(client, result.payment_hash).await?;
Copy link
Member

Choose a reason for hiding this comment

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

About the comment, do we know if that works with trampoline?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not yet, for that we need the transport error to be resolved.


// For trampoline payments the amount_msat doesn't match the actual
// amount. If it's a trampoline payment, take the amount from the label.
let (payment_amount, client_label) = serde_json::from_str::<PaymentLabel>(payment.label())
Copy link
Member

Choose a reason for hiding this comment

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

Do we have the bolt11 we paid in case of trampoline? If so perhaps better to take the amount from there? If we don't perhaps it is better to persist it as part of the label? I think from UX perspective the user needs the paid invoice.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The invoice is there, but possibly it's a zero amount invoice (which we support). Having the amount in the label handles both cases.

Copy link
Member

Choose a reason for hiding this comment

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

Right. I think we have a mechanism that adds the amount (in case of zero invoice) to the extrenal_info (persist_pending_payment).
But I am not against of having that in the label which I think is better.

@JssDWt JssDWt requested a review from dangeross August 15, 2024 08:29
@JssDWt
Copy link
Contributor Author

JssDWt commented Aug 15, 2024

@dangeross re-requesting review due to this added commit: 4a0a150

@JssDWt
Copy link
Contributor Author

JssDWt commented Aug 15, 2024

Bumped greenlight because Blockstream/greenlight#475 was merged

extract trampoline payment data from label
This update ensures that lsp pubkey and lsp id are
always set in the same call. Sometimes the lsp
pubkey is not yet available. Then only the lsp id
will be set. If the lsp id changes, the pubkey is
then removed. If the lsp id remains the same, the
node pubkey will still be persisted.
@JssDWt JssDWt merged commit 985bfed into main Aug 16, 2024
9 checks passed
erdemyerebasmaz added a commit to breez/breez-sdk-liquid that referenced this pull request Aug 20, 2024
erdemyerebasmaz added a commit to breez/breez-sdk-liquid that referenced this pull request Aug 20, 2024
erdemyerebasmaz added a commit to breez/breez-sdk-liquid that referenced this pull request Aug 20, 2024
@JssDWt JssDWt deleted the jssdwt-trampoline branch October 14, 2024 10:43
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