-
Notifications
You must be signed in to change notification settings - Fork 311
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 Socks5 proxy transport for RPC blockchain #493
Conversation
The `ProxyTransport` implements a socks5 stream connection with the core rpc host, and request/receive http data from it. `Transport` trait is implemented on `ProxyTransport` to make it compatible with existing `bitcoincore-rpc::Client`.
Handles proxy options to create a socks5 stream through proxy to rpc host. Creates regular http connection otherwise.
6f7af02
to
b99f1cf
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.
It seems a bit redundant to reimplement an http client in here: can you first try opening a pr to the jsonrpc library to see if they somehow let you build a Client
from an existing TcpStream?
// Fetch the RPC address:port and wallet path from url | ||
let (target_addr, wallet_path) = { | ||
// the url will be of form "http://<rpc-host>:<rpc-port>/wallet/<wallet-file-name>" | ||
(rpc_url[7..22].to_owned(), rpc_url[22..].to_owned()) |
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.
Ideally here I would parse the url entirely, it's not reliable to just index into the string
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 does feel clunky. But this seemed easier for now without brain storming too much on a parser. Can you suggest a better way? Would update that in next revise.
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.
A quick way without any extra dependencies could be:
- check the prefix. It should be either "http://" or nothing
- replace "http://" with ""
- splitn with n = 2, separator = "/"
- first part is the host, second part the wallet path
// fetch username password from rpc authentication | ||
let rpc_auth = { | ||
if let (Some(user), Some(pass)) = Self::get_user_pass(rpc_auth)? { | ||
let mut auth = user; |
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.
Why not format!("{}:{}", user, pass)
?
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 because the RPC server expects the auth string to be formatted in a certain way which is base64(b"Basic {user}:{pass}")
. I just broke down the steps. This can be written in shorter lines.
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.
Yeah it just seems a bit redundant here since the steps are so simple
while deadline > Instant::now() { | ||
match reader.read_line(&mut line) { | ||
// EOF reached for now, try again later | ||
Ok(0) => std::thread::sleep(Duration::from_millis(5)), |
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.
When EOF is reached there's nothing more to wait for, you should return an error here
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.
The function is blocking, so it will internally wait for data and only return a string when there's something: https://doc.rust-lang.org/std/io/trait.BufRead.html#method.read_line
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 wrote it this way because I wanted to introduce a timeout into it. Socks doesn't work with timeouts. Is there a way we can do timeouts without this?
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 don't think so, read_line()
is blocking and won't return until either the fd is closed or there's a new line available.
The timeout is usually set on the TcpStream
, if that's not exposed by the socks library we can't do anything.
Thanks @afilini for the look.
I was looking for ways of how to do this easier in one of our upstream The So weather we wanna do it in So instead of putting the whole thing in the upstream, I think its better to maintain it in our own repo for our need. The upstreams (both |
Sorry I got confused, I meant the http client: https://github.com/apoelstra/rust-jsonrpc/blob/ee78ede9a1eb543e0d64c4658019a6e4f7868d09/src/simple_http.rs Looking at it they don't store the TcpStream, so maybe that's not feasible.. but it sucks to have to just repeat the same code twice (also, not sure if there are licensing issues if we just copy their stuff in here). |
The only thing similar with their There is another way I can think of is to propose changing their simplehttp client to include Socks5Stream inside depending on weather proxy options are provided. Then jsonrpc itself will be able provide proxy support. Do you think that would be a better approach? Also any clue on the I have checked that my local tor proxy at port 9050 is working fine with curl. |
Now that #501 is in you can rebase on |
Yeah but I am still not sure if this is the best way to go for this.. |
No problem, we can leave as draft until you decide how you want to proceed. |
My 2 cents on this is I don't think there is a compelling use-case for it. For mobile devices I think the preferred method will be to connect to public or home hosted bitcoind+electrs servers, or use compact block filters. But I'm not completely opposed to it if you have other uses in mind and/or find others who would use it. |
One use case I can have in mind, is a personal mobile wallet connected to a home Umbrel node.. Umbrel can't be exposed outside home network without Tor and electrum server overhead just for personal usage doesn't make sense.. Bitcoin RPC is enough to operate a wallet that only handles someone's personal data.. Electrums are useful when they wanna expose blockchain service to public.. And then also users need trusted reputed 3rd party Electrum servers and that server have user transaction data available, so that creates potential privacy leak.. (Tor doesn't help there, because the endpoint itself is the point of failure) Many of the home use cases with vanilla core (Umbrel or something else) with mobile wallet integration will require Tor support for our RPC blcockhain.. They ideally will need Tor in the mobile too.. But not having Tor support for RPC restricts them to
I personally don't find the electrum way friendly especially for home use cases.. Its an extra overhead (another 400 gb of storage) for home node runners with no added benefits.. I forgot which one but Specter/Sparrow wallet allows to connect to a home RPC over Tor.. That makes it very useful to have it operated from anywhere in the world.. And it doesn't need electrum too.. |
According to the All that said, if you really think this is something folks will use then I'm not against it on any technical grounds, my primary concern is that it won't be used and doesn't look like a simple thing to add. EDIT: The compact_filters Peer looks like it does already support connecting via a TOR proxy. |
Yes, the Compact Filter way seems like another possibility, even though isn't meant for this purpose. Users also have to configure their node to allow compact filters since by default its not active for core. But then, it would open compact filter service of the node to public too, and that will cost them good bandwidth (recently pointed out by Murch in a core review, as there are not much CBF nodes out there, the burden of existing node is more, hopefully that will improve in future). But most home node setup packages comes with CBF disabled. Yes I agree, its not a easy thing to get into and also not dependent on BDK in any way. Tor support for RPC has to be done in lower level crates, and I am not sure if it will ever get done there too. Its not difficult to implement, but its difficult to organize PR and reviews in all those crates. But pushing the ball on that front in apoelstra/rust-jsonrpc#57 Lets see if it can get somewhere. I really think it will be very useful. Otherwise the |
@rajarshimaitra here's an example of a socks proxy server, and is even in rust, you may be able to use to help with integration testing this feature without involving TOR. You'd have to spin up a socks5-server instance that proxies back to your localhost bitcoind. At least you should be able to verify the socks5 proxy protocol and auth is working (as long as socks5-server is correctly implementing the protocol from the server side). https://crates.io/crates/socks5-server |
Thanks @notmandatory , I think this PR can be closed if you wanna clean up the PR list.. We are not going with this approach.. And I will try to test the socks5 proxy manually with your suggestion.. Update: It worked 🎉 .. rust-bitcoin/rust-bitcoincore-rpc#249 (comment) Thanks for the suggestion.. Opened the upstream PR for review.. :) |
Glad to hear the socks5 proxy suggestion helped. Closing this PR in favor of your rust-bitcoincore-rpc PR. |
Description
This attempts to solve #373.
Because
bitcoincore_rpc::Client
doesn't support socks5 proxy internally, the easiest option for us is to implement our ownsocks5 transport and make it compatible with
bitcoincore_rpc::Client
.The
Transport
trait is implemented forProxyTransport
to make it compatible with core rpc Client.ProxyTransport
is a simple Socks5 wrapper overTcpStream
and handles HTTP data like regular connection, but passes it through a given proxy.I have implemented the basic structure but getting a generic error while trying to connect through local tor proxy running at
127.0.0.1:9050
.Error
The sample test code is here https://github.com/rajarshimaitra/bdk-example/blob/main/src/main.rs
I am investigating on the error.
Notes to the reviewers
Making the proxy transport probably warrant a dedicated module for rpc. Once the current approach is finalized, that refactoring can be done on a follow up PR. Suggestions welcome.
Checklists
All Submissions:
cargo fmt
andcargo clippy
before committingNew Features:
CHANGELOG.md