From ee913bebb7214cac04948fe4f0f73cc811289c83 Mon Sep 17 00:00:00 2001 From: Nick Gheorghita Date: Mon, 1 Mar 2021 14:55:52 +0000 Subject: [PATCH 1/6] Basic http server functionality --- src/cli.rs | 99 +++++++++++++++++++++++++++++++++++++++++++++++------ src/main.rs | 16 ++++++++- 2 files changed, 104 insertions(+), 11 deletions(-) diff --git a/src/cli.rs b/src/cli.rs index 8fc35d1f9..b4f405477 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -3,20 +3,27 @@ use reqwest::blocking as reqwest; use serde_json; use std::fs; use std::io::{self, Read, Write}; +use std::io::prelude::*; use std::os::unix; use threadpool::ThreadPool; +use std::net::TcpListener; +use std::net::TcpStream; -// TODO: things to configure: -// - infura project id (not just env var?) -// - rpc endpoint port -// - rpc endpoint type (tcp, ws, ipc) -// - max concurrent requests (ie~ threadpool size) -pub fn launch_trin(infura_project_id: String) { - println!("Launching with infura key: '{}'", infura_project_id); +pub fn launch_trin(infura_project_id: String, protocol: String) { + println!("Launching with infura key: '{}' using protocol: '{}'", infura_project_id, protocol); let pool = ThreadPool::new(2); + + match &protocol[..] { + "ipc" => launch_ipc_client(pool, infura_project_id), + "http" => launch_http_client(pool, infura_project_id), + _ => panic!("invalid"), + } +} + +fn launch_ipc_client(pool: ThreadPool, infura_project_id: String) { let path = "/tmp/trin-jsonrpc.ipc"; let listener_result = unix::net::UnixListener::bind(path); let listener = match listener_result { @@ -33,7 +40,7 @@ pub fn launch_trin(infura_project_id: String) { panic!("Could not serve from path '{}': {:?}", path, err); } }; - + for stream in listener.incoming() { let stream = stream.unwrap(); let infura_project_id = infura_project_id.clone(); @@ -41,12 +48,84 @@ pub fn launch_trin(infura_project_id: String) { let infura_url = format!("https://mainnet.infura.io:443/v3/{}", infura_project_id); let mut rx = stream.try_clone().unwrap(); let mut tx = stream; - serve_client(&mut rx, &mut tx, &infura_url); + serve_ipc_client(&mut rx, &mut tx, &infura_url); + }); + } +} + +fn launch_http_client(pool: ThreadPool, infura_project_id: String) { + println!("launching http"); + let listener = TcpListener::bind("127.0.0.1:7878").unwrap(); + for stream in listener.incoming() { + let stream = stream.unwrap(); + let infura_project_id = infura_project_id.clone(); + pool.execute(move || { + let infura_url = format!("https://mainnet.infura.io:443/v3/{}", infura_project_id); + serve_http_client(stream, &infura_url); }); } } -fn serve_client(rx: &mut impl Read, tx: &mut impl Write, infura_url: &String) { +fn serve_http_client(mut stream: TcpStream, infura_url: &String) { + let mut buffer = [0; 1024]; + + stream.read(&mut buffer).unwrap(); + + let request = String::from_utf8_lossy(&buffer[..]); + let json_request = match request.lines().last() { + None => panic!("failed"), + Some(last_line) => last_line.split('\u{0}').nth(0).unwrap(), + }; + + let deser = serde_json::Deserializer::from_str(&json_request); + for obj in deser.into_iter::() { + let obj = obj.unwrap(); + assert!(obj.is_object()); + assert_eq!(obj["jsonrpc"], "2.0"); + let request_id = obj.get("id").unwrap(); + let method = obj.get("method").unwrap(); + + let response = match method.as_str().unwrap() { + "web3_clientVersion" => { + let contents = format!( + r#"{{"jsonrpc":"2.0","id":{},"result":"trin 0.0.1-alpha"}}"#, + request_id + ); + format!( + "HTTP/1.1 200 OK\r\nContent-Length: {}\r\n\r\n{}", + contents.len(), + contents, + ).into_bytes() + }, + _ => { + //Re-encode json to proxy to Infura + let request = obj.to_string(); + match proxy_to_url(request, infura_url) { + // test infura error handling? + Ok(result_body) => { + let contents = String::from_utf8_lossy(&result_body); + format!( + "HTTP/1.1 200 OK\r\nContent-Length: {}\r\n\r\n{}", + contents.len(), + contents, + ).into_bytes() + }, + Err(err) => { + format!( + r#"{{"jsonrpc":"2.0","id":"{}","error":"Infura failure: {}"}}"#, + request_id, + err.to_string(), + ).into_bytes() + } + } + } + }; + stream.write(&response).unwrap(); + stream.flush().unwrap(); + } +} + +fn serve_ipc_client(rx: &mut impl Read, tx: &mut impl Write, infura_url: &String) { println!("Welcoming..."); let deser = serde_json::Deserializer::from_reader(rx); for obj in deser.into_iter::() { diff --git a/src/main.rs b/src/main.rs index 0ce4d2f3d..9193e4170 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,8 +3,22 @@ use std::env; mod cli; fn main() { + let protocol = std::env::args().nth(1).expect("no protocol given"); + + // TODO: things to configure: + // use clap library for arg handling? + // - infura project id (not just env var?) + // - rpc endpoint port + // - rpc endpoint type (tcp, ws, ipc) + // - max concurrent requests (ie~ threadpool size) + match &protocol[..] { + "http" => println!("using http"), + "ipc" => println!("using ipc"), + _ => panic!("unsupported protocol: {}", protocol), + } + match env::var("TRIN_INFURA_PROJECT_ID") { - Ok(val) => cli::launch_trin(val), + Ok(val) => cli::launch_trin(val, protocol), Err(_) => println!( "Must supply Infura key as environment variable, like:\n\ TRIN_INFURA_PROJECT_ID=\"your-key-here\" trin" From 800cb4bb5e47482d53d04ce250334693704fad16 Mon Sep 17 00:00:00 2001 From: Nick Gheorghita Date: Mon, 1 Mar 2021 15:57:54 +0000 Subject: [PATCH 2/6] Add cli arg parser --- Cargo.toml | 1 + src/cli.rs | 33 ++++++++++++++----------------- src/main.rs | 56 ++++++++++++++++++++++++++++++++++++++++++----------- 3 files changed, 61 insertions(+), 29 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c36d7c85b..7cb35b0c0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,3 +10,4 @@ edition = "2018" reqwest = { version = "0.11.0", features = ["blocking"] } serde_json = "1.0.59" threadpool = "1.8.1" +clap = "2.33.3" diff --git a/src/cli.rs b/src/cli.rs index b4f405477..d45d06be0 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -3,19 +3,16 @@ use reqwest::blocking as reqwest; use serde_json; use std::fs; use std::io::{self, Read, Write}; -use std::io::prelude::*; -use std::os::unix; -use threadpool::ThreadPool; use std::net::TcpListener; use std::net::TcpStream; - +use std::os::unix; +use threadpool::ThreadPool; pub fn launch_trin(infura_project_id: String, protocol: String) { - println!("Launching with infura key: '{}' using protocol: '{}'", infura_project_id, protocol); + println!("Launching with infura key: '{}'", infura_project_id); let pool = ThreadPool::new(2); - match &protocol[..] { "ipc" => launch_ipc_client(pool, infura_project_id), "http" => launch_http_client(pool, infura_project_id), @@ -40,7 +37,7 @@ fn launch_ipc_client(pool: ThreadPool, infura_project_id: String) { panic!("Could not serve from path '{}': {:?}", path, err); } }; - + for stream in listener.incoming() { let stream = stream.unwrap(); let infura_project_id = infura_project_id.clone(); @@ -54,7 +51,6 @@ fn launch_ipc_client(pool: ThreadPool, infura_project_id: String) { } fn launch_http_client(pool: ThreadPool, infura_project_id: String) { - println!("launching http"); let listener = TcpListener::bind("127.0.0.1:7878").unwrap(); for stream in listener.incoming() { let stream = stream.unwrap(); @@ -95,8 +91,9 @@ fn serve_http_client(mut stream: TcpStream, infura_url: &String) { "HTTP/1.1 200 OK\r\nContent-Length: {}\r\n\r\n{}", contents.len(), contents, - ).into_bytes() - }, + ) + .into_bytes() + } _ => { //Re-encode json to proxy to Infura let request = obj.to_string(); @@ -108,15 +105,15 @@ fn serve_http_client(mut stream: TcpStream, infura_url: &String) { "HTTP/1.1 200 OK\r\nContent-Length: {}\r\n\r\n{}", contents.len(), contents, - ).into_bytes() - }, - Err(err) => { - format!( - r#"{{"jsonrpc":"2.0","id":"{}","error":"Infura failure: {}"}}"#, - request_id, - err.to_string(), - ).into_bytes() + ) + .into_bytes() } + Err(err) => format!( + r#"{{"jsonrpc":"2.0","id":"{}","error":"Infura failure: {}"}}"#, + request_id, + err.to_string(), + ) + .into_bytes(), } } }; diff --git a/src/main.rs b/src/main.rs index 9193e4170..01ee792e5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,24 +1,58 @@ +use clap::{App, Arg}; use std::env; mod cli; fn main() { - let protocol = std::env::args().nth(1).expect("no protocol given"); - // TODO: things to configure: - // use clap library for arg handling? // - infura project id (not just env var?) - // - rpc endpoint port - // - rpc endpoint type (tcp, ws, ipc) - // - max concurrent requests (ie~ threadpool size) - match &protocol[..] { - "http" => println!("using http"), - "ipc" => println!("using ipc"), - _ => panic!("unsupported protocol: {}", protocol), + let matches = App::new("trin") + .version("0.0.1") + .author("carver") + .about("super lightweight eth client thingy") + .arg( + Arg::with_name("protocol") + .short("p") + .long("protocol") + .help("select transport protocol") + .takes_value(true) + .default_value("http"), + ) + .arg( + Arg::with_name("endpoint") + .short("e") + .long("endpoint") + .help("http port") + .takes_value(true) + .default_value("7878"), + ) + .arg( + Arg::with_name("pool_size") + .short("s") + .long("pool_size") + .help("max size of threadpool") + .takes_value(true) + .default_value("2"), + ) + .get_matches(); + + println!("Launching Trin..."); + let protocol = matches.value_of("protocol").unwrap(); + let endpoint = matches.value_of("endpoint").unwrap(); + let pool_size = matches.value_of("pool_size").unwrap(); + + match protocol { + "http" => println!("Protocol: {}\nEndpoint: {}", protocol, endpoint), + "ipc" => match endpoint { + "7878" => println!("Protocol: {}", protocol), + _ => panic!("No ports for ipc connection"), + }, + _ => panic!("Unsupported protocol: {}, supported protocols include http & ipc."), } + println!("Pool Size: {}", pool_size); match env::var("TRIN_INFURA_PROJECT_ID") { - Ok(val) => cli::launch_trin(val, protocol), + Ok(val) => cli::launch_trin(val, protocol.to_string()), Err(_) => println!( "Must supply Infura key as environment variable, like:\n\ TRIN_INFURA_PROJECT_ID=\"your-key-here\" trin" From 7bbced5becc20cffdad28eceb0cfac049d4a2f09 Mon Sep 17 00:00:00 2001 From: Nick Gheorghita Date: Wed, 3 Mar 2021 12:22:06 +0000 Subject: [PATCH 3/6] Add some tests for cli arg parsing --- src/cli.rs | 140 ++++++++++++++++++-------------- src/main.rs | 229 ++++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 257 insertions(+), 112 deletions(-) diff --git a/src/cli.rs b/src/cli.rs index d45d06be0..c97e0824e 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -8,19 +8,25 @@ use std::net::TcpStream; use std::os::unix; use threadpool::ThreadPool; -pub fn launch_trin(infura_project_id: String, protocol: String) { - println!("Launching with infura key: '{}'", infura_project_id); +#[derive(Debug, PartialEq)] +pub struct TrinConfig { + pub protocol: String, + pub infura_project_id: String, + pub endpoint: u32, + pub pool_size: u32, +} - let pool = ThreadPool::new(2); +pub fn launch_trin(trin_config: TrinConfig) { + let pool = ThreadPool::new(trin_config.pool_size as usize); - match &protocol[..] { - "ipc" => launch_ipc_client(pool, infura_project_id), - "http" => launch_http_client(pool, infura_project_id), - _ => panic!("invalid"), + match &trin_config.protocol[..] { + "ipc" => launch_ipc_client(pool, trin_config), + "http" => launch_http_client(pool, trin_config), + val => panic!("Unsupported protocol: {}", val), } } -fn launch_ipc_client(pool: ThreadPool, infura_project_id: String) { +fn launch_ipc_client(pool: ThreadPool, trin_config: TrinConfig) { let path = "/tmp/trin-jsonrpc.ipc"; let listener_result = unix::net::UnixListener::bind(path); let listener = match listener_result { @@ -40,7 +46,7 @@ fn launch_ipc_client(pool: ThreadPool, infura_project_id: String) { for stream in listener.incoming() { let stream = stream.unwrap(); - let infura_project_id = infura_project_id.clone(); + let infura_project_id = trin_config.infura_project_id.clone(); pool.execute(move || { let infura_url = format!("https://mainnet.infura.io:443/v3/{}", infura_project_id); let mut rx = stream.try_clone().unwrap(); @@ -50,15 +56,58 @@ fn launch_ipc_client(pool: ThreadPool, infura_project_id: String) { } } -fn launch_http_client(pool: ThreadPool, infura_project_id: String) { - let listener = TcpListener::bind("127.0.0.1:7878").unwrap(); +fn serve_ipc_client(rx: &mut impl Read, tx: &mut impl Write, infura_url: &String) { + println!("Welcoming..."); + let deser = serde_json::Deserializer::from_reader(rx); + for obj in deser.into_iter::() { + let obj = obj.unwrap(); + assert!(obj.is_object()); + assert_eq!(obj["jsonrpc"], "2.0"); + let request_id = obj.get("id").unwrap(); + let method = obj.get("method").unwrap(); + + let response = match method.as_str().unwrap() { + "web3_clientVersion" => format!( + r#"{{"jsonrpc":"2.0","id":{},"result":"trin 0.0.1-alpha"}}"#, + request_id, + ) + .into_bytes(), + _ => { + //Re-encode json to proxy to Infura + let request = obj.to_string(); + match proxy_to_url(request, infura_url) { + Ok(result_body) => result_body, + Err(err) => format!( + r#"{{"jsonrpc":"2.0","id":"{}","error":"Infura failure: {}"}}"#, + request_id, + err.to_string(), + ) + .into_bytes(), + } + } + }; + tx.write_all(&response).unwrap(); + } + println!("Clean exit"); +} + +fn launch_http_client(pool: ThreadPool, trin_config: TrinConfig) { + let uri = format!("127.0.0.1:{}", trin_config.endpoint); + let listener = TcpListener::bind(uri).unwrap(); for stream in listener.incoming() { - let stream = stream.unwrap(); - let infura_project_id = infura_project_id.clone(); - pool.execute(move || { - let infura_url = format!("https://mainnet.infura.io:443/v3/{}", infura_project_id); - serve_http_client(stream, &infura_url); - }); + match stream { + Ok(stream) => { + let infura_project_id = trin_config.infura_project_id.clone(); + pool.execute(move || { + let infura_url = + format!("https://mainnet.infura.io:443/v3/{}", infura_project_id); + serve_http_client(stream, &infura_url); + }); + } + Err(e) => { + panic!("HTTP connection failed: {}", e) + } + } } } @@ -69,7 +118,7 @@ fn serve_http_client(mut stream: TcpStream, infura_url: &String) { let request = String::from_utf8_lossy(&buffer[..]); let json_request = match request.lines().last() { - None => panic!("failed"), + None => panic!("Invalid json request."), Some(last_line) => last_line.split('\u{0}').nth(0).unwrap(), }; @@ -98,7 +147,6 @@ fn serve_http_client(mut stream: TcpStream, infura_url: &String) { //Re-encode json to proxy to Infura let request = obj.to_string(); match proxy_to_url(request, infura_url) { - // test infura error handling? Ok(result_body) => { let contents = String::from_utf8_lossy(&result_body); format!( @@ -108,12 +156,19 @@ fn serve_http_client(mut stream: TcpStream, infura_url: &String) { ) .into_bytes() } - Err(err) => format!( - r#"{{"jsonrpc":"2.0","id":"{}","error":"Infura failure: {}"}}"#, - request_id, - err.to_string(), - ) - .into_bytes(), + Err(err) => { + let contents = format!( + r#"{{"jsonrpc":"2.0","id":"{}","error":"Infura failure: {}"}}"#, + request_id, + err.to_string(), + ); + format!( + "HTTP/1.1 502 BAD GATEWAY\r\nContent-Length: {}\r\n\r\n{}", + contents.len(), + contents, + ) + .into_bytes() + } } } }; @@ -122,41 +177,6 @@ fn serve_http_client(mut stream: TcpStream, infura_url: &String) { } } -fn serve_ipc_client(rx: &mut impl Read, tx: &mut impl Write, infura_url: &String) { - println!("Welcoming..."); - let deser = serde_json::Deserializer::from_reader(rx); - for obj in deser.into_iter::() { - let obj = obj.unwrap(); - assert!(obj.is_object()); - assert_eq!(obj["jsonrpc"], "2.0"); - let request_id = obj.get("id").unwrap(); - let method = obj.get("method").unwrap(); - - let response = match method.as_str().unwrap() { - "web3_clientVersion" => format!( - r#"{{"jsonrpc":"2.0","id":{},"result":"trin 0.0.1-alpha"}}"#, - request_id, - ) - .into_bytes(), - _ => { - //Re-encode json to proxy to Infura - let request = obj.to_string(); - match proxy_to_url(request, infura_url) { - Ok(result_body) => result_body, - Err(err) => format!( - r#"{{"jsonrpc":"2.0","id":"{}","error":"Infura failure: {}"}}"#, - request_id, - err.to_string(), - ) - .into_bytes(), - } - } - }; - tx.write_all(&response).unwrap(); - } - println!("Clean exit"); -} - fn proxy_to_url(request: String, url: &String) -> io::Result> { let client = reqwest::Client::new(); match client.post(url).body(request).send() { diff --git a/src/main.rs b/src/main.rs index 01ee792e5..7b961be99 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,61 +1,186 @@ use clap::{App, Arg}; use std::env; +use std::ffi::OsString; mod cli; +use cli::TrinConfig; + +impl TrinConfig { + fn new() -> Self { + Self::new_from(std::env::args_os().into_iter()).unwrap_or_else(|e| e.exit()) + } + + fn new_from(args: I) -> Result + where + I: Iterator, + T: Into + Clone, + { + // TODO: things to configure: + // - infura project id (not just env var?) + let matches = App::new("trin") + .version("0.0.1") + .author("carver") + .about("super lightweight eth portal") + .arg( + Arg::with_name("protocol") + .short("p") + .long("protocol") + .help("select transport protocol") + .takes_value(true) + .default_value("http"), + ) + .arg( + Arg::with_name("endpoint") + .short("e") + .long("endpoint") + .help("http port") + .takes_value(true) + .default_value("7878"), + ) + .arg( + Arg::with_name("pool_size") + .short("s") + .long("pool-size") + .help("max size of threadpool") + .takes_value(true) + .default_value("2"), + ) + .get_matches_from_safe(args) + .unwrap_or_else(|e| panic!("Unable to parse args: {}", e)); + + println!("Launching Trin..."); + let protocol = matches.value_of("protocol").unwrap(); + let endpoint = matches.value_of("endpoint").unwrap(); + let endpoint = match endpoint.parse::() { + Ok(n) => n, + Err(_) => panic!("Provided endpoint arg is not a number"), + }; + let pool_size = matches.value_of("pool_size").unwrap(); + let pool_size = match pool_size.parse::() { + Ok(n) => n, + Err(_) => panic!("Provided pool size arg is not a number"), + }; + + // parse protocol & endpoint + match protocol { + "http" => println!("Protocol: {}\nEndpoint: {}", protocol, endpoint), + "ipc" => match endpoint { + 7878 => println!("Protocol: {}", protocol), + _ => panic!("No ports for ipc connection"), + }, + val => panic!( + "Unsupported protocol: {}, supported protocols include http & ipc.", + val + ), + } + println!("Pool Size: {}", pool_size); + + let infura_project_id = match env::var("TRIN_INFURA_PROJECT_ID") { + Ok(val) => val, + Err(_) => panic!( + "Must supply Infura key as environment variable, like:\n\ + TRIN_INFURA_PROJECT_ID=\"your-key-here\" trin" + ), + }; + + Ok(TrinConfig { + endpoint: endpoint, + infura_project_id: infura_project_id, + pool_size: pool_size, + protocol: protocol.to_string(), + }) + } +} + fn main() { - // TODO: things to configure: - // - infura project id (not just env var?) - let matches = App::new("trin") - .version("0.0.1") - .author("carver") - .about("super lightweight eth client thingy") - .arg( - Arg::with_name("protocol") - .short("p") - .long("protocol") - .help("select transport protocol") - .takes_value(true) - .default_value("http"), - ) - .arg( - Arg::with_name("endpoint") - .short("e") - .long("endpoint") - .help("http port") - .takes_value(true) - .default_value("7878"), - ) - .arg( - Arg::with_name("pool_size") - .short("s") - .long("pool_size") - .help("max size of threadpool") - .takes_value(true) - .default_value("2"), + let trin_config = TrinConfig::new(); + cli::launch_trin(trin_config); +} + +#[cfg(test)] +mod test { + use super::*; + + fn env_is_set() -> bool { + match env::var("TRIN_INFURA_PROJECT_ID") { + Ok(_) => true, + _ => false, + } + } + + #[test] + fn test_default_args() { + assert!(env_is_set()); + let expected_config = TrinConfig { + protocol: "http".to_string(), + infura_project_id: "".to_string(), + endpoint: 7878, + pool_size: 2, + }; + let actual_config = TrinConfig::new_from(["trin"].iter()).unwrap(); + assert_eq!(actual_config.protocol, expected_config.protocol); + assert_eq!(actual_config.endpoint, expected_config.endpoint); + assert_eq!(actual_config.pool_size, expected_config.pool_size); + assert!(!actual_config.infura_project_id.is_empty()); + } + + #[test] + #[should_panic(expected = "Unsupported protocol: xxx")] + fn test_invalid_protocol() { + assert!(env_is_set()); + TrinConfig::new_from(["trin", "--protocol", "xxx"].iter()).unwrap_err(); + } + + #[test] + fn test_custom_http_args() { + assert!(env_is_set()); + let expected_config = TrinConfig { + protocol: "http".to_string(), + infura_project_id: "".to_string(), + endpoint: 8080, + pool_size: 3, + }; + let actual_config = TrinConfig::new_from( + [ + "trin", + "--protocol", + "http", + "--endpoint", + "8080", + "--pool-size", + "3", + ] + .iter(), ) - .get_matches(); - - println!("Launching Trin..."); - let protocol = matches.value_of("protocol").unwrap(); - let endpoint = matches.value_of("endpoint").unwrap(); - let pool_size = matches.value_of("pool_size").unwrap(); - - match protocol { - "http" => println!("Protocol: {}\nEndpoint: {}", protocol, endpoint), - "ipc" => match endpoint { - "7878" => println!("Protocol: {}", protocol), - _ => panic!("No ports for ipc connection"), - }, - _ => panic!("Unsupported protocol: {}, supported protocols include http & ipc."), - } - println!("Pool Size: {}", pool_size); - - match env::var("TRIN_INFURA_PROJECT_ID") { - Ok(val) => cli::launch_trin(val, protocol.to_string()), - Err(_) => println!( - "Must supply Infura key as environment variable, like:\n\ - TRIN_INFURA_PROJECT_ID=\"your-key-here\" trin" - ), + .unwrap(); + assert_eq!(actual_config.protocol, expected_config.protocol); + assert_eq!(actual_config.endpoint, expected_config.endpoint); + assert_eq!(actual_config.pool_size, expected_config.pool_size); + assert!(!actual_config.infura_project_id.is_empty()); + } + + #[test] + fn test_ipc_protocol() { + assert!(env_is_set()); + let actual_config = TrinConfig::new_from(["trin", "--protocol", "ipc"].iter()).unwrap(); + let expected_config = TrinConfig { + protocol: "ipc".to_string(), + infura_project_id: "".to_string(), + endpoint: 7878, + pool_size: 2, + }; + assert_eq!(actual_config.protocol, expected_config.protocol); + assert_eq!(actual_config.endpoint, expected_config.endpoint); + assert_eq!(actual_config.pool_size, expected_config.pool_size); + assert!(!actual_config.infura_project_id.is_empty()); + } + + #[test] + #[should_panic(expected = "No ports for ipc connection")] + fn test_ipc_protocol_rejects_custom_endpoint() { + assert!(env_is_set()); + TrinConfig::new_from(["trin", "--protocol", "ipc", "--endpoint", "7879"].iter()) + .unwrap_err(); } } From a2001022c0e8b2957ba72a1dabdf7bd79fa72eb4 Mon Sep 17 00:00:00 2001 From: Nick Gheorghita Date: Sun, 7 Mar 2021 12:01:21 +0000 Subject: [PATCH 4/6] Add readme --- README.md | 27 ++++++++ src/cli.rs | 190 ++++++++++++++++++++++++++++++++++++++++++++++++++-- src/main.rs | 178 ------------------------------------------------ 3 files changed, 211 insertions(+), 184 deletions(-) diff --git a/README.md b/README.md index 4cbf8f2a2..a665ac03f 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,22 @@ cd trin TRIN_INFURA_PROJECT_ID="YoUr-Id-HeRe" cargo run ``` +## CLI Options +```sh +USAGE: + trin [OPTIONS] + +FLAGS: + -h, --help Prints help information + -V, --version Prints version information + +OPTIONS: + -e, --endpoint http port [default: 7878] + -s, --pool-size max size of threadpool [default: 2] + -p, --protocol select transport protocol [default: http] +``` + +### Connect over IPC In a python shell: ```py >>> from web3 import Web3 @@ -41,6 +57,17 @@ In a python shell: 11870768 ``` +### Connect over HTTP +In a python shell: +```py +>>> from web3 import Web3 +>>> w3 = Web3(Web3.HTTPProvider("http://127.0.0.1:7878")) +>>> w3.clientVersion +'trin 0.0.1-alpha' +>>> w3.eth.blockNumber +11870768 +``` + The client version responds immediately, from the trin client. The block number is retrieved more slowly, by proxying to Infura. To interact with trin at the lowest possible level, try netcat: diff --git a/src/cli.rs b/src/cli.rs index c97e0824e..312f6f03d 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,6 +1,9 @@ //use web3::{Web3, transports}; +use clap::{App, Arg}; use reqwest::blocking as reqwest; use serde_json; +use std::env; +use std::ffi::OsString; use std::fs; use std::io::{self, Read, Write}; use std::net::TcpListener; @@ -16,17 +19,102 @@ pub struct TrinConfig { pub pool_size: u32, } +impl TrinConfig { + pub fn new() -> Self { + Self::new_from(std::env::args_os().into_iter()).unwrap_or_else(|e| e.exit()) + } + + fn new_from(args: I) -> Result + where + I: Iterator, + T: Into + Clone, + { + let matches = App::new("trin") + .version("0.0.1") + .author("carver") + .about("super lightweight eth portal") + .arg( + Arg::with_name("protocol") + .short("p") + .long("protocol") + .help("select transport protocol") + .takes_value(true) + .default_value("http"), + ) + .arg( + Arg::with_name("endpoint") + .short("e") + .long("endpoint") + .help("http port") + .takes_value(true) + .default_value("7878"), + ) + .arg( + Arg::with_name("pool_size") + .short("s") + .long("pool-size") + .help("max size of threadpool") + .takes_value(true) + .default_value("2"), + ) + .get_matches_from_safe(args) + .unwrap_or_else(|e| panic!("Unable to parse args: {}", e)); + + println!("Launching Trin..."); + let protocol = matches.value_of("protocol").unwrap(); + let endpoint = matches.value_of("endpoint").unwrap(); + let endpoint = match endpoint.parse::() { + Ok(n) => n, + Err(_) => panic!("Provided endpoint arg is not a number"), + }; + let pool_size = matches.value_of("pool_size").unwrap(); + let pool_size = match pool_size.parse::() { + Ok(n) => n, + Err(_) => panic!("Provided pool size arg is not a number"), + }; + + // parse protocol & endpoint + match protocol { + "http" => println!("Protocol: {}\nEndpoint: {}", protocol, endpoint), + "ipc" => match endpoint { + 7878 => println!("Protocol: {}", protocol), + _ => panic!("No ports for ipc connection"), + }, + val => panic!( + "Unsupported protocol: {}, supported protocols include http & ipc.", + val + ), + } + println!("Pool Size: {}", pool_size); + + let infura_project_id = match env::var("TRIN_INFURA_PROJECT_ID") { + Ok(val) => val, + Err(_) => panic!( + "Must supply Infura key as environment variable, like:\n\ + TRIN_INFURA_PROJECT_ID=\"your-key-here\" trin" + ), + }; + + Ok(TrinConfig { + endpoint: endpoint, + infura_project_id: infura_project_id, + pool_size: pool_size, + protocol: protocol.to_string(), + }) + } +} + pub fn launch_trin(trin_config: TrinConfig) { let pool = ThreadPool::new(trin_config.pool_size as usize); match &trin_config.protocol[..] { - "ipc" => launch_ipc_client(pool, trin_config), + "ipc" => launch_ipc_client(pool, trin_config.infura_project_id), "http" => launch_http_client(pool, trin_config), val => panic!("Unsupported protocol: {}", val), } } -fn launch_ipc_client(pool: ThreadPool, trin_config: TrinConfig) { +fn launch_ipc_client(pool: ThreadPool, infura_project_id: String) { let path = "/tmp/trin-jsonrpc.ipc"; let listener_result = unix::net::UnixListener::bind(path); let listener = match listener_result { @@ -46,9 +134,9 @@ fn launch_ipc_client(pool: ThreadPool, trin_config: TrinConfig) { for stream in listener.incoming() { let stream = stream.unwrap(); - let infura_project_id = trin_config.infura_project_id.clone(); + let infura_project_id = infura_project_id.clone(); pool.execute(move || { - let infura_url = format!("https://mainnet.infura.io:443/v3/{}", infura_project_id); + let infura_url = get_infura_url(&infura_project_id); let mut rx = stream.try_clone().unwrap(); let mut tx = stream; serve_ipc_client(&mut rx, &mut tx, &infura_url); @@ -99,8 +187,7 @@ fn launch_http_client(pool: ThreadPool, trin_config: TrinConfig) { Ok(stream) => { let infura_project_id = trin_config.infura_project_id.clone(); pool.execute(move || { - let infura_url = - format!("https://mainnet.infura.io:443/v3/{}", infura_project_id); + let infura_url = get_infura_url(&infura_project_id); serve_http_client(stream, &infura_url); }); } @@ -204,3 +291,94 @@ fn proxy_to_url(request: String, url: &String) -> io::Result> { )), } } + +fn get_infura_url(infura_project_id: &String) -> String { + return format!("https://mainnet.infura.io:443/v3/{}", infura_project_id); +} + +#[cfg(test)] +mod test { + use super::*; + + fn env_is_set() -> bool { + match env::var("TRIN_INFURA_PROJECT_ID") { + Ok(_) => true, + _ => false, + } + } + + #[test] + fn test_default_args() { + assert!(env_is_set()); + let expected_config = TrinConfig { + protocol: "http".to_string(), + infura_project_id: "".to_string(), + endpoint: 7878, + pool_size: 2, + }; + let actual_config = TrinConfig::new_from(["trin"].iter()).unwrap(); + assert_eq!(actual_config.protocol, expected_config.protocol); + assert_eq!(actual_config.endpoint, expected_config.endpoint); + assert_eq!(actual_config.pool_size, expected_config.pool_size); + assert!(!actual_config.infura_project_id.is_empty()); + } + + #[test] + #[should_panic(expected = "Unsupported protocol: xxx")] + fn test_invalid_protocol() { + assert!(env_is_set()); + TrinConfig::new_from(["trin", "--protocol", "xxx"].iter()).unwrap_err(); + } + + #[test] + fn test_custom_http_args() { + assert!(env_is_set()); + let expected_config = TrinConfig { + protocol: "http".to_string(), + infura_project_id: "".to_string(), + endpoint: 8080, + pool_size: 3, + }; + let actual_config = TrinConfig::new_from( + [ + "trin", + "--protocol", + "http", + "--endpoint", + "8080", + "--pool-size", + "3", + ] + .iter(), + ) + .unwrap(); + assert_eq!(actual_config.protocol, expected_config.protocol); + assert_eq!(actual_config.endpoint, expected_config.endpoint); + assert_eq!(actual_config.pool_size, expected_config.pool_size); + assert!(!actual_config.infura_project_id.is_empty()); + } + + #[test] + fn test_ipc_protocol() { + assert!(env_is_set()); + let actual_config = TrinConfig::new_from(["trin", "--protocol", "ipc"].iter()).unwrap(); + let expected_config = TrinConfig { + protocol: "ipc".to_string(), + infura_project_id: "".to_string(), + endpoint: 7878, + pool_size: 2, + }; + assert_eq!(actual_config.protocol, expected_config.protocol); + assert_eq!(actual_config.endpoint, expected_config.endpoint); + assert_eq!(actual_config.pool_size, expected_config.pool_size); + assert!(!actual_config.infura_project_id.is_empty()); + } + + #[test] + #[should_panic(expected = "No ports for ipc connection")] + fn test_ipc_protocol_rejects_custom_endpoint() { + assert!(env_is_set()); + TrinConfig::new_from(["trin", "--protocol", "ipc", "--endpoint", "7879"].iter()) + .unwrap_err(); + } +} diff --git a/src/main.rs b/src/main.rs index 7b961be99..c7e6f3f8c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,186 +1,8 @@ -use clap::{App, Arg}; -use std::env; -use std::ffi::OsString; - mod cli; use cli::TrinConfig; -impl TrinConfig { - fn new() -> Self { - Self::new_from(std::env::args_os().into_iter()).unwrap_or_else(|e| e.exit()) - } - - fn new_from(args: I) -> Result - where - I: Iterator, - T: Into + Clone, - { - // TODO: things to configure: - // - infura project id (not just env var?) - let matches = App::new("trin") - .version("0.0.1") - .author("carver") - .about("super lightweight eth portal") - .arg( - Arg::with_name("protocol") - .short("p") - .long("protocol") - .help("select transport protocol") - .takes_value(true) - .default_value("http"), - ) - .arg( - Arg::with_name("endpoint") - .short("e") - .long("endpoint") - .help("http port") - .takes_value(true) - .default_value("7878"), - ) - .arg( - Arg::with_name("pool_size") - .short("s") - .long("pool-size") - .help("max size of threadpool") - .takes_value(true) - .default_value("2"), - ) - .get_matches_from_safe(args) - .unwrap_or_else(|e| panic!("Unable to parse args: {}", e)); - - println!("Launching Trin..."); - let protocol = matches.value_of("protocol").unwrap(); - let endpoint = matches.value_of("endpoint").unwrap(); - let endpoint = match endpoint.parse::() { - Ok(n) => n, - Err(_) => panic!("Provided endpoint arg is not a number"), - }; - let pool_size = matches.value_of("pool_size").unwrap(); - let pool_size = match pool_size.parse::() { - Ok(n) => n, - Err(_) => panic!("Provided pool size arg is not a number"), - }; - - // parse protocol & endpoint - match protocol { - "http" => println!("Protocol: {}\nEndpoint: {}", protocol, endpoint), - "ipc" => match endpoint { - 7878 => println!("Protocol: {}", protocol), - _ => panic!("No ports for ipc connection"), - }, - val => panic!( - "Unsupported protocol: {}, supported protocols include http & ipc.", - val - ), - } - println!("Pool Size: {}", pool_size); - - let infura_project_id = match env::var("TRIN_INFURA_PROJECT_ID") { - Ok(val) => val, - Err(_) => panic!( - "Must supply Infura key as environment variable, like:\n\ - TRIN_INFURA_PROJECT_ID=\"your-key-here\" trin" - ), - }; - - Ok(TrinConfig { - endpoint: endpoint, - infura_project_id: infura_project_id, - pool_size: pool_size, - protocol: protocol.to_string(), - }) - } -} - fn main() { let trin_config = TrinConfig::new(); cli::launch_trin(trin_config); } - -#[cfg(test)] -mod test { - use super::*; - - fn env_is_set() -> bool { - match env::var("TRIN_INFURA_PROJECT_ID") { - Ok(_) => true, - _ => false, - } - } - - #[test] - fn test_default_args() { - assert!(env_is_set()); - let expected_config = TrinConfig { - protocol: "http".to_string(), - infura_project_id: "".to_string(), - endpoint: 7878, - pool_size: 2, - }; - let actual_config = TrinConfig::new_from(["trin"].iter()).unwrap(); - assert_eq!(actual_config.protocol, expected_config.protocol); - assert_eq!(actual_config.endpoint, expected_config.endpoint); - assert_eq!(actual_config.pool_size, expected_config.pool_size); - assert!(!actual_config.infura_project_id.is_empty()); - } - - #[test] - #[should_panic(expected = "Unsupported protocol: xxx")] - fn test_invalid_protocol() { - assert!(env_is_set()); - TrinConfig::new_from(["trin", "--protocol", "xxx"].iter()).unwrap_err(); - } - - #[test] - fn test_custom_http_args() { - assert!(env_is_set()); - let expected_config = TrinConfig { - protocol: "http".to_string(), - infura_project_id: "".to_string(), - endpoint: 8080, - pool_size: 3, - }; - let actual_config = TrinConfig::new_from( - [ - "trin", - "--protocol", - "http", - "--endpoint", - "8080", - "--pool-size", - "3", - ] - .iter(), - ) - .unwrap(); - assert_eq!(actual_config.protocol, expected_config.protocol); - assert_eq!(actual_config.endpoint, expected_config.endpoint); - assert_eq!(actual_config.pool_size, expected_config.pool_size); - assert!(!actual_config.infura_project_id.is_empty()); - } - - #[test] - fn test_ipc_protocol() { - assert!(env_is_set()); - let actual_config = TrinConfig::new_from(["trin", "--protocol", "ipc"].iter()).unwrap(); - let expected_config = TrinConfig { - protocol: "ipc".to_string(), - infura_project_id: "".to_string(), - endpoint: 7878, - pool_size: 2, - }; - assert_eq!(actual_config.protocol, expected_config.protocol); - assert_eq!(actual_config.endpoint, expected_config.endpoint); - assert_eq!(actual_config.pool_size, expected_config.pool_size); - assert!(!actual_config.infura_project_id.is_empty()); - } - - #[test] - #[should_panic(expected = "No ports for ipc connection")] - fn test_ipc_protocol_rejects_custom_endpoint() { - assert!(env_is_set()); - TrinConfig::new_from(["trin", "--protocol", "ipc", "--endpoint", "7879"].iter()) - .unwrap_err(); - } -} From 422fafd5393e3ea36344c9ce9e4fd9deeda8cf32 Mon Sep 17 00:00:00 2001 From: Nick Gheorghita Date: Sun, 14 Mar 2021 15:20:56 +0000 Subject: [PATCH 5/6] PR feedback --- README.md | 36 ++++++++-------- src/cli.rs | 116 ++++++++++++++++++++++------------------------------ src/main.rs | 13 +++++- 3 files changed, 80 insertions(+), 85 deletions(-) diff --git a/README.md b/README.md index a665ac03f..8721b1ac9 100644 --- a/README.md +++ b/README.md @@ -31,21 +31,6 @@ cd trin TRIN_INFURA_PROJECT_ID="YoUr-Id-HeRe" cargo run ``` -## CLI Options -```sh -USAGE: - trin [OPTIONS] - -FLAGS: - -h, --help Prints help information - -V, --version Prints version information - -OPTIONS: - -e, --endpoint http port [default: 7878] - -s, --pool-size max size of threadpool [default: 2] - -p, --protocol select transport protocol [default: http] -``` - ### Connect over IPC In a python shell: ```py @@ -61,7 +46,7 @@ In a python shell: In a python shell: ```py >>> from web3 import Web3 ->>> w3 = Web3(Web3.HTTPProvider("http://127.0.0.1:7878")) +>>> w3 = Web3(Web3.HTTPProvider("http://127.0.0.1:8545")) >>> w3.clientVersion 'trin 0.0.1-alpha' >>> w3.eth.blockNumber @@ -79,6 +64,25 @@ nc -U /tmp/trin-jsonrpc.ipc {"jsonrpc":"2.0","id":"85","result":"trin 0.0.1-alpha"}^C ``` +## CLI Options +```sh +trin 0.0.1 +carver +super lightweight eth portal + +USAGE: + trin [OPTIONS] + +FLAGS: + --help Prints help information + -V, --version Prints version information + +OPTIONS: + -h, --http-port port to accept http connections [default: 8545] + -s, --pool-size max size of threadpool [default: 2] + -p, --protocol select transport protocol [default: http] [possible values: http, ipc] +``` + ## Gotchas - There is a limit on concurrent connections given by the threadpool. At last diff --git a/src/cli.rs b/src/cli.rs index 312f6f03d..29e26cbac 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,5 +1,4 @@ -//use web3::{Web3, transports}; -use clap::{App, Arg}; +use clap::{value_t, App, Arg, Error, ErrorKind}; use reqwest::blocking as reqwest; use serde_json; use std::env; @@ -14,14 +13,13 @@ use threadpool::ThreadPool; #[derive(Debug, PartialEq)] pub struct TrinConfig { pub protocol: String, - pub infura_project_id: String, - pub endpoint: u32, + pub http_port: u32, pub pool_size: u32, } impl TrinConfig { pub fn new() -> Self { - Self::new_from(std::env::args_os().into_iter()).unwrap_or_else(|e| e.exit()) + Self::new_from(std::env::args_os().into_iter()).expect("Could not parse trin arguments") } fn new_from(args: I) -> Result @@ -38,16 +36,17 @@ impl TrinConfig { .short("p") .long("protocol") .help("select transport protocol") + .possible_values(&["http", "ipc"]) .takes_value(true) .default_value("http"), ) .arg( - Arg::with_name("endpoint") - .short("e") - .long("endpoint") - .help("http port") + Arg::with_name("http_port") + .short("h") + .long("http-port") + .help("port to accept http connections") .takes_value(true) - .default_value("7878"), + .default_value("8545"), ) .arg( Arg::with_name("pool_size") @@ -58,58 +57,47 @@ impl TrinConfig { .default_value("2"), ) .get_matches_from_safe(args) - .unwrap_or_else(|e| panic!("Unable to parse args: {}", e)); + .unwrap_or_else(|err| match err.kind { + ErrorKind::HelpDisplayed => Error::exit(&err), + ErrorKind::VersionDisplayed => Error::exit(&err), + _ => panic!("Unable to parse trin arguments: {}", err) + }); - println!("Launching Trin..."); - let protocol = matches.value_of("protocol").unwrap(); - let endpoint = matches.value_of("endpoint").unwrap(); - let endpoint = match endpoint.parse::() { - Ok(n) => n, - Err(_) => panic!("Provided endpoint arg is not a number"), - }; - let pool_size = matches.value_of("pool_size").unwrap(); - let pool_size = match pool_size.parse::() { - Ok(n) => n, - Err(_) => panic!("Provided pool size arg is not a number"), - }; + println!("Launching trin..."); + let pool_size = value_t!(matches.value_of("pool_size"), u32) + .expect("Invalid type for pool-size argument."); + let http_port = value_t!(matches.value_of("http_port"), u32) + .expect("Invalid type for http-port argument."); + let protocol = value_t!(matches.value_of("protocol"), String) + .expect("Invalid type for protocol argument."); - // parse protocol & endpoint - match protocol { - "http" => println!("Protocol: {}\nEndpoint: {}", protocol, endpoint), - "ipc" => match endpoint { - 7878 => println!("Protocol: {}", protocol), - _ => panic!("No ports for ipc connection"), + match protocol.as_str() { + "http" => { + println!("Protocol: {}\nHTTP port: {}", protocol, http_port) + } + "ipc" => match http_port { + 8545 => println!("Protocol: {}", protocol), + _ => panic!("Must not supply an http port when using ipc protocol"), }, - val => panic!( - "Unsupported protocol: {}, supported protocols include http & ipc.", - val - ), + val => panic!("Unsupported protocol: {}", val), } - println!("Pool Size: {}", pool_size); - let infura_project_id = match env::var("TRIN_INFURA_PROJECT_ID") { - Ok(val) => val, - Err(_) => panic!( - "Must supply Infura key as environment variable, like:\n\ - TRIN_INFURA_PROJECT_ID=\"your-key-here\" trin" - ), - }; + println!("Pool Size: {}", pool_size); Ok(TrinConfig { - endpoint: endpoint, - infura_project_id: infura_project_id, + http_port: http_port, pool_size: pool_size, protocol: protocol.to_string(), }) } } -pub fn launch_trin(trin_config: TrinConfig) { +pub fn launch_trin(trin_config: TrinConfig, infura_project_id: String) { let pool = ThreadPool::new(trin_config.pool_size as usize); - match &trin_config.protocol[..] { - "ipc" => launch_ipc_client(pool, trin_config.infura_project_id), - "http" => launch_http_client(pool, trin_config), + match trin_config.protocol.as_str() { + "ipc" => launch_ipc_client(pool, infura_project_id), + "http" => launch_http_client(pool, infura_project_id, trin_config), val => panic!("Unsupported protocol: {}", val), } } @@ -179,13 +167,13 @@ fn serve_ipc_client(rx: &mut impl Read, tx: &mut impl Write, infura_url: &String println!("Clean exit"); } -fn launch_http_client(pool: ThreadPool, trin_config: TrinConfig) { - let uri = format!("127.0.0.1:{}", trin_config.endpoint); +fn launch_http_client(pool: ThreadPool, infura_project_id: String, trin_config: TrinConfig) { + let uri = format!("127.0.0.1:{}", trin_config.http_port); let listener = TcpListener::bind(uri).unwrap(); for stream in listener.incoming() { match stream { Ok(stream) => { - let infura_project_id = trin_config.infura_project_id.clone(); + let infura_project_id = infura_project_id.clone(); pool.execute(move || { let infura_url = get_infura_url(&infura_project_id); serve_http_client(stream, &infura_url); @@ -312,19 +300,17 @@ mod test { assert!(env_is_set()); let expected_config = TrinConfig { protocol: "http".to_string(), - infura_project_id: "".to_string(), - endpoint: 7878, + http_port: 8545, pool_size: 2, }; let actual_config = TrinConfig::new_from(["trin"].iter()).unwrap(); assert_eq!(actual_config.protocol, expected_config.protocol); - assert_eq!(actual_config.endpoint, expected_config.endpoint); + assert_eq!(actual_config.http_port, expected_config.http_port); assert_eq!(actual_config.pool_size, expected_config.pool_size); - assert!(!actual_config.infura_project_id.is_empty()); } #[test] - #[should_panic(expected = "Unsupported protocol: xxx")] + #[should_panic(expected = "Unable to parse trin arguments")] fn test_invalid_protocol() { assert!(env_is_set()); TrinConfig::new_from(["trin", "--protocol", "xxx"].iter()).unwrap_err(); @@ -335,8 +321,7 @@ mod test { assert!(env_is_set()); let expected_config = TrinConfig { protocol: "http".to_string(), - infura_project_id: "".to_string(), - endpoint: 8080, + http_port: 8080, pool_size: 3, }; let actual_config = TrinConfig::new_from( @@ -344,7 +329,7 @@ mod test { "trin", "--protocol", "http", - "--endpoint", + "--http-port", "8080", "--pool-size", "3", @@ -353,9 +338,8 @@ mod test { ) .unwrap(); assert_eq!(actual_config.protocol, expected_config.protocol); - assert_eq!(actual_config.endpoint, expected_config.endpoint); + assert_eq!(actual_config.http_port, expected_config.http_port); assert_eq!(actual_config.pool_size, expected_config.pool_size); - assert!(!actual_config.infura_project_id.is_empty()); } #[test] @@ -364,21 +348,19 @@ mod test { let actual_config = TrinConfig::new_from(["trin", "--protocol", "ipc"].iter()).unwrap(); let expected_config = TrinConfig { protocol: "ipc".to_string(), - infura_project_id: "".to_string(), - endpoint: 7878, + http_port: 8545, pool_size: 2, }; assert_eq!(actual_config.protocol, expected_config.protocol); - assert_eq!(actual_config.endpoint, expected_config.endpoint); + assert_eq!(actual_config.http_port, expected_config.http_port); assert_eq!(actual_config.pool_size, expected_config.pool_size); - assert!(!actual_config.infura_project_id.is_empty()); } #[test] - #[should_panic(expected = "No ports for ipc connection")] - fn test_ipc_protocol_rejects_custom_endpoint() { + #[should_panic(expected = "Must not supply an http port when using ipc")] + fn test_ipc_protocol_rejects_custom_http_port() { assert!(env_is_set()); - TrinConfig::new_from(["trin", "--protocol", "ipc", "--endpoint", "7879"].iter()) + TrinConfig::new_from(["trin", "--protocol", "ipc", "--http-port", "7879"].iter()) .unwrap_err(); } } diff --git a/src/main.rs b/src/main.rs index c7e6f3f8c..d570c8469 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,8 +1,17 @@ +use std::env; mod cli; - use cli::TrinConfig; fn main() { let trin_config = TrinConfig::new(); - cli::launch_trin(trin_config); + + let infura_project_id = match env::var("TRIN_INFURA_PROJECT_ID") { + Ok(val) => val, + Err(_) => panic!( + "Must supply Infura key as environment variable, like:\n\ + TRIN_INFURA_PROJECT_ID=\"your-key-here\" trin" + ), + }; + + cli::launch_trin(trin_config, infura_project_id); } From 47b2141f03ff9905f160937ee01220378fde359a Mon Sep 17 00:00:00 2001 From: Nick Gheorghita Date: Wed, 17 Mar 2021 15:17:52 +0000 Subject: [PATCH 6/6] Refactor error handling on clap cli parser --- src/cli.rs | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/src/cli.rs b/src/cli.rs index 29e26cbac..ff19e9234 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,4 +1,4 @@ -use clap::{value_t, App, Arg, Error, ErrorKind}; +use clap::{value_t, App, Arg}; use reqwest::blocking as reqwest; use serde_json; use std::env; @@ -56,12 +56,7 @@ impl TrinConfig { .takes_value(true) .default_value("2"), ) - .get_matches_from_safe(args) - .unwrap_or_else(|err| match err.kind { - ErrorKind::HelpDisplayed => Error::exit(&err), - ErrorKind::VersionDisplayed => Error::exit(&err), - _ => panic!("Unable to parse trin arguments: {}", err) - }); + .get_matches_from(args); println!("Launching trin..."); let pool_size = value_t!(matches.value_of("pool_size"), u32) @@ -309,13 +304,6 @@ mod test { assert_eq!(actual_config.pool_size, expected_config.pool_size); } - #[test] - #[should_panic(expected = "Unable to parse trin arguments")] - fn test_invalid_protocol() { - assert!(env_is_set()); - TrinConfig::new_from(["trin", "--protocol", "xxx"].iter()).unwrap_err(); - } - #[test] fn test_custom_http_args() { assert!(env_is_set());