Skip to content

Commit

Permalink
Merge pull request #53 from chainwayxyz/rpc_server_fixes
Browse files Browse the repository at this point in the history
RPC server fixes
  • Loading branch information
ceyhunsen authored Aug 26, 2024
2 parents c688ed4 + 95aa1fc commit 88a97dd
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 42 deletions.
17 changes: 6 additions & 11 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,25 +1,20 @@
//! # RPC Server Starter
use bitcoin_mock_rpc::rpc::spawn_rpc_server;
use std::{env, time::Duration};
use std::env;

#[tokio::main]
async fn main() {
fn main() {
println!("Bitcoin Mock Rpc (C) Chainway, 2024");
println!(
"Usage: {} [HOST] [PORT]",
env::args().collect::<Vec<String>>().first().unwrap()
);

let server_info = handle_args();
let address = spawn_rpc_server(server_info.0.as_deref(), server_info.1)
.await
.unwrap();
println!("Server started at {}", address);

loop {
std::thread::sleep(Duration::from_secs(100));
}
let server = spawn_rpc_server(server_info.0.as_deref(), server_info.1).unwrap();
println!("Server started at {}", server.0);

server.1.join().unwrap()
}

fn handle_args() -> (Option<String>, Option<u16>) {
Expand Down
78 changes: 59 additions & 19 deletions src/rpc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use crate::{Client, RpcApiWrapper};
use jsonrpsee::server::middleware::rpc::RpcServiceT;
use jsonrpsee::server::{RpcServiceBuilder, Server};
use jsonrpsee::types::Request;
use std::thread::JoinHandle;
use std::{io::Error, net::SocketAddr, net::TcpListener};
use traits::RpcServer;

Expand Down Expand Up @@ -41,39 +42,78 @@ where
/// # Parameters
///
/// - host: Optional host. If is `None`, `127.0.0.1` will be used
/// - port: Optional port. If is `None`, first available port for `host` will be used
/// - port: Optional port. If is `None`, first available port for `host` will be
/// used
///
/// # Returns
///
/// URL on success, `std::io::Error` otherwise.
/// - `SocketAddr`: Address of the server
/// - `JoinHandle`: Server's handle that **must not be dropped** as long as
/// server lives
#[tracing::instrument]
pub async fn spawn_rpc_server(host: Option<&str>, port: Option<u16>) -> Result<SocketAddr, Error> {
pub fn spawn_rpc_server(
host: Option<&str>,
port: Option<u16>,
) -> Result<(SocketAddr, JoinHandle<()>), Error> {
let host = host.unwrap_or("127.0.0.1");
let port = match port {
Some(p) => p,
None => find_empty_port(host)?,
};
let url = format!("{}:{}", host, port);

start_server(url.as_str()).await
Ok(start_server_thread(url))
}

async fn start_server(url: &str) -> Result<SocketAddr, Error> {
let rpc_middleware = RpcServiceBuilder::new().layer_fn(Logger);
/// Starts a thread that hosts RPC server.
///
/// # Parameters
///
/// - url: Server's intended address
///
/// # Returns
///
/// - `SocketAddr`: Address of the server
/// - `JoinHandle`: Server's handle that must live as long as server
pub fn start_server_thread(url: String) -> (SocketAddr, JoinHandle<()>) {
let (tx, rx) = std::sync::mpsc::channel();

let server = Server::builder()
.set_rpc_middleware(rpc_middleware)
.build(url)
.await?;
let handle = std::thread::spawn(move || {
let mut rt = tokio::runtime::Builder::new_multi_thread();
rt.enable_all();
let rt = rt.build().unwrap();
tracing::trace!("New Tokio runtime is created for server with URL {url}");

let addr = server.local_addr()?;
rt.block_on(async {
let rpc_middleware = RpcServiceBuilder::new().layer_fn(Logger);

let client = Client::new(url, bitcoincore_rpc::Auth::None).unwrap();
let handle = server.start(client.into_rpc());
let server = Server::builder()
.set_rpc_middleware(rpc_middleware)
.build(url.clone())
.await
.unwrap();

tokio::spawn(handle.stopped());
let address = server.local_addr().unwrap();

Ok(addr)
// Start server.
let client = Client::new(&url, bitcoincore_rpc::Auth::None).unwrap();
let handle = server.start(client.into_rpc());

// Server is up and we can notify that it is.
tx.send(address).expect("Could not send socket address.");

// Run forever.
handle.stopped().await
});
});

let address = rx
.recv()
.expect("Could not receive socket address from channel.");

tracing::trace!("Server started for URL {address:?}");

(address, handle)
}

/// Finds the first empty port for the given `host`.
Expand Down Expand Up @@ -103,9 +143,9 @@ mod tests {
);
}

#[tokio::test]
async fn spawn_rpc_server() {
let address = super::spawn_rpc_server(None, None).await.unwrap();
println!("Server started at {}", address);
#[test]
fn spawn_rpc_server() {
let server = super::spawn_rpc_server(None, None).unwrap();
println!("Server started at {}", server.0);
}
}
38 changes: 26 additions & 12 deletions tests/rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,29 +7,43 @@
use bitcoin_mock_rpc::rpc::spawn_rpc_server;
use bitcoincore_rpc::RpcApi;
use jsonrpsee::core::client::ClientT;
use jsonrpsee::{http_client::HttpClient, rpc_params};

#[tokio::test]
async fn create_connection() {
let address = spawn_rpc_server(None, None).await.unwrap();
let url = address.to_string();
async fn check_server_availability() {
let server = spawn_rpc_server(None, None).unwrap();
let url = format!("http://{}", server.0);
println!("Server URL: {url}");

let client = HttpClient::builder().build(url).unwrap();
let params = rpc_params![];

let response: String = client.request("getnewaddress", params).await.unwrap();
println!("Server response: {:?}", response);
}

#[test]
fn create_connection() {
let server = spawn_rpc_server(None, None).unwrap();
let url = server.0.to_string();
println!("Server started at {url}");

let _should_not_panic =
bitcoincore_rpc::Client::new(url.as_str(), bitcoincore_rpc::Auth::None).unwrap();
}

#[tokio::test]
#[ignore = "causes infinite loop"]
async fn address_related() {
let address = spawn_rpc_server(None, None).await.unwrap();
let url = address.to_string();
#[test]
fn address_related() {
let server = spawn_rpc_server(None, None).unwrap();
let url = server.0.to_string();
println!("Server started at {url}");

let client = bitcoincore_rpc::Client::new(url.as_str(), bitcoincore_rpc::Auth::None).unwrap();

let address = client.get_new_address(None, None).unwrap();
let _address = client.get_new_address(None, None).unwrap();

client
.generate_to_address(101, &address.assume_checked())
.unwrap();
// client
// .generate_to_address(101, &address.assume_checked())
// .unwrap();
}

0 comments on commit 88a97dd

Please sign in to comment.