-
Notifications
You must be signed in to change notification settings - Fork 84
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
Payjoin integration #257
Payjoin integration #257
Conversation
This sequence diagram might help those getting familiar with the how lightning channels can be opened from payjoin transactions
|
67e887f
to
5e0bbc6
Compare
A few points to discuss:
|
Hyper serves https and makes use of the async tokio runtime being used in LDK node already. This makes more sense to me than rewriting an http stack. Indeed, a payjoin receiver needs to share concurrent state. I suggest writing down exactly what state needs to be shared and what each component it's shared with needs to do with it, and whether or not it needs its own thread.
Indeed, this is the first documentation of who needs what state.
shared state can be passed as you figured out Clone would be a bit of a hack since channel_manager is a singleton service that needs to have its shared state altered while the program is running. I would guess that the channel_manager would be a dependency of whatever batched scheduler service you come up with. To share the state, choose an appropriate memory container and access pattern. This flow chart has helped me a ton. Rather than wrap each field of shared state in its own |
I have yet to properly review and digest the whole proposed scheme. However, I'm generally very skeptical to go down the outlined route in respect to adding an HTTP server stack to LDK Node itself. If a reachable HTTP endpoint is a hard requirement for the proposed PJ scheme the handler probably should live in a different crate and we need to find a good interface in order to configure LDK node with it. This would be very similar to what we do with |
The big issue with Version 1 payjoin is exactly this dependency on an HTTP server stack. Fortunately the Payjoin Version 2 BIP forgoes this dependency for a mere HTTP client. This draft uses V1 but I imagine the merged PR will use V2. While the |
src/lib.rs
Outdated
use tokio::net::TcpListener; | ||
|
||
// Start the HTTP server for payjoin | ||
let (payjoin_queue_sender, mut payjoin_queue_receiver) = mpsc::channel::<PayjoinRequest>(1); |
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.
@DanGould starting a channel here that is accessed from the payjoin endpoint. the payjoin endpoint will add PayjoinRequest
to the queue, and wait for response(funding psbt) via different channel from create_channel_from_pj
.
src/lib.rs
Outdated
) { | ||
let psbt = pj_req.clone().original_psbt(); | ||
let pj_response = PayjoinResponse::new(psbt); | ||
// Send OpenChannel message |
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.
This is where the channel negotiations should happen and the queue
the response back to the http handler
src/lib.rs
Outdated
} | ||
|
||
/// Request a new channel to be opened with a remote peer. | ||
pub fn payjoin_channel( |
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.
this function is currently doing two things which is not great.
it should be broke into two functions and in the "payjoin_uri" docs we should mention that the pending_channels
array should be filled first
@DanGould In the last commit I addressed the scheduler we discussed in the last discussion. Added a new struct You can see now other new structs like
|
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.
My biggest confusion with the design is spawning loops to listen for mpsc channel updates. Rather than spawn a loop listening on an mpsc channel, you can use an async function like PayjoinScheduler::queue_channel and avoid that complexity. This can encapsulate create_channel_from_pj
within a payjoin module.
I have the same concern with using mpsc loop to respond to payjoin requests. POST requests to /payjoin
shouldn't schedule anything, they should check against the queue from the scheduler to attempt to pop from it and open a channel, otherwise accept the funds into a single-sig address. HTTP response must be synchronous anyhow, it does you no good to wait for a channel update if you can't respond to the HTTP request with it. There is no need to manually parse the PSBT from the request body, the payjoin crate has all of the methods to do that for you. Just marshal HTTP I/O data into and out of the payjoin crate methods. Define a function that takes the request and returns a response.
I think what you are trying to do is separate the scheduler logic from the http server, which does make sense. However, the server could reference the scheduler directly as a field instead of an opaque mpsc::Sender. This lets it interface with rich function signatures instead of channels in loops. Both the receiver server and the Node should share reference to the Scheduler and interface with its methods rather than mpsc channels.
src/pj.rs
Outdated
let res = match (http_request.method(), http_request.uri().path()) { | ||
_ => payjoin_handler(http_request, state), | ||
}; |
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 think methods are being matched twice here for no reason. Unless you're going to transform a custom error type into an http response, you should probably call payjoin_handler directly without the match
I would like not to rely on v2 for this integration for the following reasons:
The idea of having a separate crate for a payjoin-server is very reasonable. We can start with v1 and once v2 is more mature we can upgrade the payjoin-server and do minimal changes in ldk-node. |
fefdaf6
to
b605919
Compare
@DanGould @tnull There are two main files to look into: Integration tests added in You can run the integration tests locally The Needed to make a few changes in binding to reflect the changes in |
I'm attempting a comprehensive design review of the abstraction intended to enable payjoin receipt for lightning channel opens funded directly by outside funds. Async DesignThe past review raised concerns with the async design, suggesting the use of async functions and scoped memory containers instead of mpsc channels. I am satisfied with the direction of this abstraction 🎊🚀 it's much easier to read, reason about, and should be easier to change. New crate@tnull voiced skepticism with regard to the inclusion of an HTTP server to receive payjoin in LDK-Node.
Assuming the stack is a hard requirement, I agree, an HTTP stack would benefit from its own crate. This could be shared between However, a payjoin v2 receiver is viable for this PR exclude the requirement for an http stack. @jbesraa raised 2 points I would like to respond to.
BIPs don't have an "approval" process per se, but an open workflow. Sure, the actual file can get approved to be included in the repository. But even that process includes implementing the BIP in the wild by multiple implementations before being merged.
It's a good idea to narrow the scope of this PR to do one thing. For that reason, it makes sense to continue to document how a new crate might help, but in my opinion also suggests that the development of a new crate is outside of scope. Regarding "communicating to their self over a relay," doing so forgoes the http server requirement and the TLS/onion hidden service setup that would otherwise be required. What exactly are the downsides you predict in using a relay? I have listened to concerns about IP and privacy leaks, but BIP 77 explicitly addresses these with OHTTP and authenticated encryption. Is latency a concern? I believe that's addressed by the asynchronous nature of BIP 77 as well. Even with payjoin v2, a Payjoin ExecuterI'm not certain the The
|
2256634
to
aa49b19
Compare
1bf6c10
to
289f096
Compare
281bb18
to
3d5b58d
Compare
I am breaking this PR into smaller PRs. First PR regarding the payjoin sender is opened here #295, and ill create another two prs to integrate the receiver, one for regular payjoin receiving and another for receiving with opening a lightning channel. Ill leave it open for a bit longer as i am using it to check the e2e flow and how the CI is responding to changes, until I open the first payjoin receiver PR. |
Sounds like a good approach! |
closing in favor of #301 |
April 18 Update:
This pull request adds two new features:
The first 3 commits are about adding the new
lightning_payjoin
crate and will be moved to a different repo once the code is approved.Added new crate
lightning_payjoin
responsible for gluing togetherldk-node
andrust-payjoin
. The library exports aReceiver
trait and aLightningPayjoin
struct which gives the ability to handle payjoin transactions.The main code is basically living in this crate including the code for accepting normal payjoin transactions, handling lightning channel opening from payjoin transaction, validating transactions and other http stuff.
The current flow for opening a channel with payjoin transaction:
open_payjoin_channel
functionopen_payjoin_channel
will:1.1 Add new channel to scheduler
1.2 The channel manager will open a new channel and stop after
ChannelAccepted
msg because its part of theChannelScheduler
1.3 Function will return BIP21 for the user to pay
payjoin_server_port
its listening onChannelScheduler
and change theoutput_script
to the funding tx multisigchannel_manager.funding_generation_ready
FundingSigned
received from counterparty nodeWe could change the order so when the user calls
open_payjoin_channel
its only adding a new channel to theChannelScheduler
and when the payments comes in we could start the channel opening and finalize it before we return a response. It would require exposing a bit more functionality to thePayjoinHandler
likePeerManager
and probably theLogger
. there will be some duplicate code if we change it as the channel opening defined inlib
would also be inpayjoin_handler
. I dont have a strong feeling about either.The current implementation is following Payjoin V1 which requires the receiver to run an http server where in Payjoin V2 its possible to only have an http client. IMO its worth first supporting v1 as thats whats currently widely used by wallets and using v2 now could limit the usage of this feature. as all of the http code is setting in
lightning-payjoin
I think its a good choice for now to go with v1.Original
The main goal of this pull request is to allow users to create channel with funds outside the node.
Flow that should be covered:
FundingSigned
from Bob** This PR should cover Alice part **
There are few tricky points, mainly around 6 and 7. Currently transactions are immediately broadcasted after
FundingSigned
, that wont work with payjoin because the transaction wont be valid.The channel opened is Outbound channel from Alice -> Bob but it will be broadcasted by Charlie, so we need to add some functionality to let Alice node know that its an Outbound channel.
some reference for payjoin integration with lnd
https://github.com/payjoin/nolooking