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

Lightning Service Provider Specification #206

Merged
merged 8 commits into from
Aug 21, 2023

Conversation

ErikDeSmedt
Copy link
Collaborator

@ErikDeSmedt ErikDeSmedt commented Jul 12, 2023

Draft on how to implement the LSPS-specification in greenlight.

Todo:

  • Write integration tests
  • Detect Lightning Service Providers by lookint at feature bit 729

@ErikDeSmedt ErikDeSmedt marked this pull request as draft July 12, 2023 09:00
Copy link
Collaborator

@cdecker cdecker left a comment

Choose a reason for hiding this comment

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

Very nice JSON-RPC implementation 👍 Some of the concepts are very cool, and I might backport some of them into cln-rpc which also has a tiny JSON-RPC implementation.

libs/gl-client/src/lsps/json_rpc.rs Outdated Show resolved Hide resolved
libs/gl-client/src/lsps/transport.rs Outdated Show resolved Hide resolved
@cdecker
Copy link
Collaborator

cdecker commented Jul 12, 2023

I think if you have a small test that sends and receives messages from one node to the other we can already merge this, so we can pipeline changes in and avoid large PRs :-)

@ErikDeSmedt ErikDeSmedt marked this pull request as ready for review August 14, 2023 13:13
@cdecker cdecker enabled auto-merge (rebase) August 17, 2023 13:51
The code to serialize and deserialize messages in the JSON-rpc.

I have decided not to include a separate json_rpc crate.

The first reason is that we will be using noise_xk as a transport layer.
I did not want to include unneeded dependencies that allow us to do json_rpc over
https, tcp, web-sockets.

The second reason is that LSPS-1 depends on a subset of json_rpc 2.0.
Batching isn't allowed, a request id MUST be provided, etc... It is
quite straight-forward to implement the logic using these constraints.

The third reason is that we only need to implement at the client for a
specific situation. I would like to enforce some parts in the
type-system which some json_rpc crates seem to enforce at run-time.
LSPS0 describes the transport-layer used by nodes should interact with
their Lightning Service Provider.

https://github.com/BitcoinAndLightningLayerSpecs/lsp/tree/main/LSPS0

The general idea is that it is a JSON-RPC 2.0 which is implemented over
BOLT-8 messages.
cdecker suggested to automatically derive the From-trait using
this_error during code-review.
The goal is to achieve language bindings (foreign function interface).

The JSON-rpc code is strongly typed and relies heavily on generics.
This is tricky to write language bindings. Using `py03` we cannot use
the `#[pyclass]` macro to generate the bindings. (This is fundamentally
broken, it is not a pyo3 limitation).

To get the language bindings working we must ensure that all the
required types are instantiated at compile-time (See `crate::lsps::message`).

The other-challenge is that at run-time only the method-name is
available. And each method-name translates to a `JsonRpcMethod<'a, I, O,
E>` of which we do not know the type.

We cannot write a handle-request function because we do not know the
types.

```
fn handle_request(&self, method_name : &str, params : ????) {

}
```

We solve this trick using a trait that does type-erasure.
The trait `JsonRpcMethodErased` implements the same functionality.
But instead of working with the concrete type it always uses
`serde_json::Value` as representation.

The `JsonRpcMethodUnerased` takes it one step further.
It allows us to write library components that can either use the generic
or erased-methods.

I also removed the life-time from JSonRPCMethod. I do not see any
use-case for creating new RpcMethods at run-time and managing all the
life-times is really annoying.
I've implemented an integration test for LSPS0 which replicates the
list-protocols behavior.

To ensure our test-case is reproducible I have removed the dependency on
`generate_random_rpc_id`. When writing tests you can use
`rpc_call_with_id`. In production the use of `rpc_call` is recommended.

This split is present in multiple methods for the creation of
JsonRpcRequests.

Previously, I made the mistake of misnaming the `jsonrpc` field.
The spurious underscore is removed everywhere.

I used the

```serde_json::from_value()```

in transport.rs when deserializing a JSON-rpc response. This did not
work as expected when the type has been erased.

I replaced this to
```
method.parse_json_response_value(,,.)
```

See transport.rs line 139. This ensures the underlying types are checked
even when a type-erased version is used.
The test was flakey.
The key problem is that the LSPS-server send the list-protocols message
before the LSP-client could send the message
@cdecker cdecker merged commit fb7de2b into Blockstream:main Aug 21, 2023
17 checks passed
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.

2 participants