-
Notifications
You must be signed in to change notification settings - Fork 93
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
Add bLIP-52 / LSPS2 service-side support #420
base: main
Are you sure you want to change the base?
Add bLIP-52 / LSPS2 service-side support #420
Conversation
1cca737
to
cd2795d
Compare
cd2795d
to
dfdef1d
Compare
dfdef1d
to
7538c83
Compare
Thank you for ushering this along. I am excited for this to be included. |
Previously, we named internal fields/APIs `lspsX_service` as us being the client was implied. Since we're about to also add service-side functionalities, such naming would start to get confusing. We hence rename them to follow a `lspsX_client` scheme, and will add the service-side APIs using the `service` terminology.
c1f2b6c
to
ef2ece2
Compare
Rebased after #418 landed. |
@G8XSU This has now been unblocked. As mentioned offline, while the LSP-facing API is still tbd, I'd be inclined to land this soon with a very simple initial version of the API, perhaps behind a |
/// **Caution**: LSP service support is in **alpha** and is considered an experimental feature. | ||
/// | ||
/// [LSPS2]: https://github.com/BitcoinAndLightningLayerSpecs/lsp/blob/main/LSPS2/README.md | ||
pub fn set_liquidity_provider_lsps2( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know we have probably used setX in names because it is builder but it is getting slightly confusing with lsps client and service.
Personally, I was slightly confused due to the fact "we need to set liquidity provider to act as lsps-client".
no strong opinion, but imo, these methods could be renamed to enable_lsps2_service
, enable_lsps2_client
and so on.
what do you think?
alternatively, we can add another layer here similar to .bolt11() and .bolt12().
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, thanks for bringing this up. I see you point in that theset_liquidity_provider
case that could be a bit confusing, but still think I'd like to keep the set_
pattern everywhere for now. For one, it follows the Rust API guidelines for naming setters. Also note that once we break the pattern, we might need to revisit all of the builder methods, which a) would be a larger refactor out-of-scope of this PR and b) would make the API less predicatable, IMO. But noted.
@@ -165,6 +190,7 @@ where | |||
{ | |||
lsps1_client: Option<LSPS1Client>, | |||
lsps2_client: Option<LSPS2Client>, | |||
lsps2_service: Option<LSPS2Service>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
doubt: what is LiquiditySource here? it contains both liquidity-provider and liquidity-consumer
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, LiquiditySource
is simply the internal object we use to handle everything liquidity-related. Let me know if you'd have a suggestion for a much better name, but for now I decided to not rename it also here.
Reviewed everything before the WIP commit (ef2ece2) |
.. and while we're at it we move the VSS child key indexes to constants.
.. we might eventually want to drop them anyways, but for now we rename them to make them easily discernable from their counterparts in `builder.rs`.
.. for consistency, as we're about to add `LSPS2ServiceConfig` there, too.
We add the capability to configure LSPS2 service mode in `Builder` and `LiquiditySourceBuilder`.
ef2ece2
to
b09be67
Compare
@G8XSU Now finished this initial support with a rudimentary API that let's the user configure via a Given the given limitations I'm still on the fence whether we should already expose this fully as an 'experimental' feature, or if we should hide it behind a |
b09be67
to
2e9a3b2
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
.. and forward it to our `LiquditySource`.
.. to align with other event handling variants: First log, then act, then emit event if everything went okay.
.. so far we just silently fail if something goes wrong, eventually we'll need to implement retrying channel opens to honor buy requests that didn't succeed on the first attempt.
dfee2f6
to
503f3b7
Compare
Added another fixup asserting the channel size in tests, and another one slightly clarifying the docs on the overprovisioning factor field. |
503f3b7
to
bad639a
Compare
@G8XSU Let me know when I can squash the fixups. |
counterparty_node_id, | ||
token, | ||
}) => { | ||
if let Some(lsps2_service_handler) = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO, LSPS might need to handle its events dynamically at runtime, rather than relying on static configuration. In other words, it could evaluate each event, apply the appropriate business logic, and then act accordingly.
While I understand that we may not want to expose actionable events from the LDK Node right now, what is our long-term thinking for this approach?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO, LSPS might need to handle its events dynamically at runtime, rather than relying on static configuration. In other words, it could evaluate each event, apply the appropriate business logic, and then act accordingly.
Well, that's exactly the kind of question I want to figure out with users/based on user feedback. It could be that prospective users would appreciate some ready-to-go automation in some areas, but still want to maintain control over others.
While I understand that we may not want to expose actionable events from the LDK Node right now, what is our long-term thinking for this approach?
I'm not sure we'd want to mirror LDK's "general" actionable events for stuff like this. If we find users demand control over certain parts of the flow, we should create specific interfaces for this in LDK Node. How the corresponding API would look like in Server is tbd, but generally I suspect we can accommodate most of users' requirements without custom logic to begin with.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure we'd want to mirror LDK's "general" actionable events for stuff like this.
FWIW, it is not at all specific to LDK, it is just a generic events interface which is a common pattern.
How the corresponding API would look like in Server is tbd, but generally I suspect we can accommodate most of users' requirements without custom logic to begin with.
For a server/enterprise deployment an event interface might make more sense(I am also on the fence regarding this since it is slightly more complicated), but we will know more with better user requirements. One thing to note is that server can't really support an event interface without ldk-node supporting the same.
) { | ||
Ok(_) => {}, | ||
Err(e) => { | ||
// TODO: We just silently fail here. Eventually we will need to remember |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in cases where we silently fail(pending TODO's), how does the client handle it? iiuc they won't get any failure response, and will wait forever for the response.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes this is why we should retry channel opening upon reconnection of the client (see #473) before finally giving up. But note that clients can also timeout requests and don't lose anything as they'd only pay upon claiming the forwarded payment post channel opening.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But note that clients can also timeout requests and don't lose anything
Just for my understanding, Do they currently timeout requests ?
I understand that they don't lose sats, but a pending unresolved request might consume unnecessary resources on client side.
// We set the forwarding fee to 0 for now as we're getting paid by the channel fee. | ||
// | ||
// TODO: revisit this decision eventually. | ||
config.channel_config.forwarding_fee_base_msat = 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
isn't this something that can be part of service_config?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd rather not put it there as this point, as it doesn't make sense to have the LSP doubly-paid for the same service. It would also complicate all the calculations even more. If we eventually want to allow the LSP to charge forwarding fees, they probably should only be updated after the initial payment flow went through.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lgtm, feel free to squash fixups.
pub struct LSPS2ServiceConfig { | ||
/// A token we may require to be sent by the clients. | ||
/// | ||
/// If set, only requests matching this token will be accepted. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do we need to specify where in request the token must be set?
counterparty_node_id, | ||
token, | ||
}) => { | ||
if let Some(lsps2_service_handler) = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure we'd want to mirror LDK's "general" actionable events for stuff like this.
FWIW, it is not at all specific to LDK, it is just a generic events interface which is a common pattern.
How the corresponding API would look like in Server is tbd, but generally I suspect we can accommodate most of users' requirements without custom logic to begin with.
For a server/enterprise deployment an event interface might make more sense(I am also on the fence regarding this since it is slightly more complicated), but we will know more with better user requirements. One thing to note is that server can't really support an event interface without ldk-node supporting the same.
|
||
let opening_fee_params = RawOpeningFeeParams { | ||
min_fee_msat: service_config.min_channel_opening_fee_msat, | ||
proportional: service_config.channel_opening_fee_ppm, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
imo, we might also need a max_fee_msat in getInfo, since a proportional fee for bigger channels might not make sense. (non-actionable for now)
) { | ||
Ok(_) => {}, | ||
Err(e) => { | ||
// TODO: We just silently fail here. Eventually we will need to remember |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But note that clients can also timeout requests and don't lose anything
Just for my understanding, Do they currently timeout requests ?
I understand that they don't lose sats, but a pending unresolved request might consume unnecessary resources on client side.
}; | ||
|
||
// Fail if we have insufficient onchain funds available. | ||
let over_provisioning_msat = (amt_to_forward_msat |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we might need a max_channel_size as well to avoid draining all the liquidity for a single channel.
) { | ||
Ok(_) => {}, | ||
Err(e) => { | ||
// TODO: We just silently fail here. Eventually we will need to remember |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Eventually we will need to remember the pending requests.
It is a bummer to remember all pending requests, can we timeout after few retries?
This adds initial bLIP-52 / LSPS2 service-side to LDK Node. When configured via
Builder::set_liquidity_provider_lsps2
, the node will serve JIT channels to clients.In this initial version we have the user configure the detail fields via an
LSPS2ServiceConfig
, and do not track and retry honoring pending buy requests. This means that we will simply (silently due to lack of a feedback mechanism in the bLIP-52 / LSPS2 protocol) fail the flow if, e.g., we don't have sufficient liquidity or the user isn't connected to us at the time of handling the payment.