Open matthiasdebernardini opened 1 year ago
PRs are welcome :)
I think the easiest way to add this would be to use axum and rcgen to spin up another server and do all the wiring inside the binary (so electrs is being tunneled through axum), but I am not sure if you would be happy about that.
If I were to go about it this way, would you accept it?
Would it be possible to hide this feature behind a new Cargo feature - so it would be built only if the user needs it?
This is related to #820, and I was actually about to get started on this. A part of me agrees that axum might be a good approach, since it comes with concurrency and async/await support built in, but I'd want it to be compatible with the Esplora API here: https://github.com/Blockstream/electrs/blob/adedee15f1fe460398a7045b292604df2161adc0/src/rest.rs
It might just be easier to try to port over the hyper 0.14 implementation and then update it to 1.0. I'm unsure of the concurrency implications of this without doing a deep dive.
I'm also not sure if there's any other Electrum REST API I can reference as I go to implement this, but that'll be my reference for now.
I'd be happy to gate this work behind a cargo feature. If anyone wants to help, let me know, and I can provide access to my electrs fork.
Looking through the Esplora code, it looks like they're already using tokio and rolling their own server implementation, so I agree, axum is the better choice here.
@romanz Let's say I wanted to get the block tip hash, how might I do it within the approach I've started here? https://github.com/romanz/electrs/pull/911
In particular, what might go here? https://github.com/romanz/electrs/pull/911/files#diff-d74a249fc203937952048161cdcb91480927c1994d7a3e6b1f41404b79962aa2R16
So, after investigating what's needed for putting Rpc state into the axum Router, that's likely going to be a no-go if we're going to use tokio and make it thread-safe. I think an assumption I was making was, consumers of this API would not want to choose between mutually exclusive features of either a TCP JSON-RPC API, or an HTTP REST API. This assumption was informed by the use-case I'm attempting to target, which is self-hosted Start9 devices; I wouldn't want to disrupt any applications presently relying upon a local TCP interface, like the mempool explorer.
Regardless, I think the best way forward is to adapt the REST interface code into a frontend to an RPC consumer client proxy, as briefly described in #820. I'm unsure if all the types needed for serialization and deserialization will be present, however, which is also why I was watching an issue for this, #848.
I'm not really sure this is a good idea. Electrs is not designed to be publicly accessible. So to do this properly it'd have to be a very robust implementation that cannot be DoSed and support client certificates to authenticate the client (does Electrum support them though?). I think that even suggesting people TLS with Nginx is dubious, maybe we should replace that part of the doc with "not recommended" warning.
There's additional problems with having an HTTP REST interface we've come to discover, in that performance is quite terrible for large wallets. Ideally a pubsub and websockets model is used.
To clarify, our use case isn't necessarily for public use, since we have our own mempool instance for that, but we wanted support for self-hosted nodes like Umbrel and StartOS, which include electrs already, and make extensive use of web interfaces hosted on private onion addresses.
Oh, wait, I got confused. Is this issue about TLS or HTTP or both? Electrum protocol is not based on HTTP.
Using HTTP makes it effectively something else than electrum server. Not that it's wrong, just pointing it out. It makes my concern about Electrum and client certificates moot since custom authentication can be used but it adds a lot of design complexity. And indeed, websockets would be required.
But also, if you use onion, TLS is unneeded complication. So it should be possible to turn it off. At which point I don't see a problem with a proxy. Those big projects (and others) can do it already and likely have to do it for other services so why complicate this one?
And I just realized, if this is going to use websockets there's a trivial solution: just wrap the existing jsonrpc messages in websocket messages (line <=> websocket message). I guess the code might be under 100 lines and trivial to review & test. One could build a reverse proxy for this and connect to existing testing infrastructure with Electrum.
Oh, wait, I got confused. Is this issue about TLS or HTTP or both? Electrum protocol is not based on HTTP.
Using HTTP makes it effectively something else than electrum server. Not that it's wrong, just pointing it out. It makes my concern about Electrum and client certificates moot since custom authentication can be used but it adds a lot of design complexity. And indeed, websockets would be required.
You're not wrong, what I'm describing is the Mempool / Esplora API: https://mempool.space/docs/api/rest
Esplora is the heavily modified Blockstream fork of electrs. Mempool then forked that and modified it to suit their own needs even further. But StartOS ships electrs, and so we want to be compatible with that.
But also, if you use onion, TLS is unneeded complication. So it should be possible to turn it off. At which point I don't see a problem with a proxy. Those big projects (and others) can do it already and likely have to do it for other services so why complicate this one?
Makes sense.
And I just realized, if this is going to use websockets there's a trivial solution: just wrap the existing jsonrpc messages in websocket messages (line <=> websocket message). I guess the code might be under 100 lines and trivial to review & test. One could build a reverse proxy for this and connect to existing testing infrastructure with Electrum.
Agreed, that shouldn't be too bad, depending on the client implementation. I also just so happen to have an experimental websockets Electrum client to test with.
Esplora is the heavily modified Blockstream fork of electrs. Mempool then forked that and modified it to suit their own needs even further. But StartOS ships electrs, and so we want to be compatible with that.
Honestly, having to support three APIs (including the original Electrum) and since there's a performance issue anyway, maybe also the fourth websocket one is really annoying. We should start by pinging people to try unify their APIs first.
And FTR I'd like to support mempool in CADR too but that's not a priority and I'd rather just package a proxy which CADR makes very easy, than deal with this API mess.
How about HTTPv2 instead of WebSocket? My team might have some resources to work on this. BTW, this would also enable electrs to be used as a backend to a WebAssembly-based browser-extension bitcoin wallet, right?
Esplora is the heavily modified Blockstream fork of electrs. Mempool then forked that and modified it to suit their own needs even further. But StartOS ships electrs, and so we want to be compatible with that.
Cannot we cherry-pick some relevant commits and propose them for electrs in form of PRs? Maybe the patches don't apply cleanly, but maybe going about this work this way is easier?
@knocte IIUC HTTP 2 requires TLS which is annoying over Tor.
But given the number of incompatible APIs, I think it's better to do this in separate proxies until there's a good agreed-upon API.
IIUC HTTP 2 requires TLS
Are you sure? I have a GRPC server myself that is using HTTP (I didn't enable HTTPS on it on purpose).
until there's a good agreed-upon API.
Why not use the same approach that Blockstream has in their electrs fork? I understand the different trade-offs between electrs and Blockstream's fork with regards to performance/querying, but I don't see why the 2 implementations would need to differ when it comes to API.
I have a GRPC server myself that is using HTTP (I didn't enable HTTPS on it on purpose).
Cool. I didn't see any JS API for doing notifications over HTTP2 so I looked around and found they are [unsupported]:
All in all HTTP/2 push is complicated, especially because of different browser implementations and bugs, so trying to expose push messages to a web application is going to be complicated. There are also many who feel HTTP/2 push has limited use and there are better technologies for most use cases, such as resource hints (for requesting HTTP resources) or web sockets (for two way comms). Chrome are even experimenting with switching it off completely.
So HTTP2 is clearly impossible.
Why not use the same approach that Blockstream has in their electrs fork?
Maybe that one is fine but why is there a mempool fork then?
Also I didn't dig into it but the API ought to support authentication since too many people want to access it over the network.
Also @knocte I still believe that having a small external proxy that just shovels the messages around is a more principled approach.
Oh, wow, the proxy already exists! I even found it by coming up with the same obvious name and trying to search if someone had the same idea. And the best part, it happens to be in Rust. https://github.com/vi/websocat
Looks like we don't have to code anything. Just let everyone package this thing and send PRs there if needed (or worst case fork it).
Oh, wow, the proxy already exists!
Ah great find! But I guess we need to integrate it somehow into electrs? Happy to start working on a PR.
we need to integrate it somehow into electrs?
Not at all! Just run it alongside electrs and put it into serve mode with the client pointing to electrs. But since IIUC it uses stdio you need to launch socat
with it as a command and the other end being electrs. And if you need authentication you need nginx.
Just run it alongside electrs
Ok sure, but would you guys accept a PR that makes this a bit easier to run?
Maybe in the same way that mempool's fork has --http-addr <addr:port>
(see
https://github.com/mempool/electrs/?tab=readme-ov-file#cli-options ), have a new --ws-addr
flag that, when used, launches websocat under the hood?
I'm not the maintainer here but it seems to be mostly a waste of time to me and it also increases maintenance burden. Just document how to run socat
. Everyone who runs electrs has to deal with multiple services anyway, so having one more should be non-issue. If this was a desktop app or something I would think otherwise.
I agree.
Hi all,
Been reading this thread with great attention as we are building a bitcoin wallet browser extension and would like anyone to be able to connect to their own node. I've exposed the problem in this discussion: https://github.com/romanz/electrs/issues/820#issuecomment-2482471786 So to summarize this thread, these are possible answers to the question:
mempool
and connect to it using the Esplora client (@cryptoquick solution?)esplora-electrs
and connect to it using the Esplora clientwebsocat
and proxy the Electrum RPC or the node's RPC to connect to it via websocket (@Kixunil solution?)TBH none of these solutions seem ideal: 1) implies installing 3 apps (bitcoin node, electrs, mempool) to connect to your own infra and you might not actually want the explorer 2) is a bad idea as Esplora is too heavy for personal use, even in light mode 3) implies installing 3 apps (bitcoin node, electrs and websocat) + implementing a websocket client in tools like BDK.
Other indexers like Bitcore or Blockbook ship with a HTTP + websocket API but are not suitable for personal servers.
Is your feature request related to a problem? Please describe. Configuring a proxy to add ssl is too much work for something so necessary and rote, none of the proxy solutions are simple enough.
Describe the solution you'd like To have a https mode be a cli option and not have to configure additional software to get basic security.
Describe alternatives you've considered hitch and nginx
Additional context There are crates in rust that help with this, including creating the certs (which I also shouldn't have to do)