diff --git a/api/src/foreign.rs b/api/src/foreign.rs index 726cc3c..d24ce02 100644 --- a/api/src/foreign.rs +++ b/api/src/foreign.rs @@ -140,7 +140,7 @@ where /// /// // A NodeClient must first be created to handle communication between /// // the wallet and the node. - /// let node_client = HTTPNodeClient::new(&wallet_config.check_node_api_http_addr, None).unwrap(); + /// let node_client = HTTPNodeClient::new(&wallet_config.check_node_api_http_addr, None, wallet_config.api_request_timeout()).unwrap(); /// /// // impls::DefaultWalletImpl is provided for convenience in instantiating the wallet /// // It contains the LMDBBackend, DefaultLCProvider (lifecycle) and ExtKeychain used @@ -506,8 +506,12 @@ macro_rules! doctest_helper_setup_doc_env_foreign { wallet_config.data_file_dir = dir.to_owned(); let pw = ZeroingString::from(""); - let node_client = - HTTPNodeClient::new(&wallet_config.check_node_api_http_addr, None).unwrap(); + let node_client = HTTPNodeClient::new( + &wallet_config.check_node_api_http_addr, + None, + wallet_config.api_request_timeout(), + ) + .unwrap(); let mut wallet = Box::new(DefaultWalletImpl::::new(node_client.clone()).unwrap()) as Box< diff --git a/api/src/owner.rs b/api/src/owner.rs index 853c7e9..9378781 100644 --- a/api/src/owner.rs +++ b/api/src/owner.rs @@ -147,7 +147,7 @@ where /// /// // A NodeClient must first be created to handle communication between /// // the wallet and the node. - /// let node_client = HTTPNodeClient::new(&wallet_config.check_node_api_http_addr, None).unwrap(); + /// let node_client = HTTPNodeClient::new(&wallet_config.check_node_api_http_addr, None, wallet_config.api_request_timeout()).unwrap(); /// /// // impls::DefaultWalletImpl is provided for convenience in instantiating the wallet /// // It contains the WalletBackend, DefaultLCProvider (lifecycle) and ExtKeychain used @@ -2610,8 +2610,12 @@ macro_rules! doctest_helper_setup_doc_env { wallet_config.data_file_dir = dir.to_owned(); let pw = ZeroingString::from(""); - let node_client = - HTTPNodeClient::new(&wallet_config.check_node_api_http_addr, None).unwrap(); + let node_client = HTTPNodeClient::new( + &wallet_config.check_node_api_http_addr, + None, + wallet_config.api_request_timeout(), + ) + .unwrap(); let mut wallet = Box::new(DefaultWalletImpl::::new(node_client.clone()).unwrap()) as Box< diff --git a/config/src/comments.rs b/config/src/comments.rs index 610dfb1..90e985f 100644 --- a/config/src/comments.rs +++ b/config/src/comments.rs @@ -70,6 +70,13 @@ fn comments() -> HashMap { "check_node_api_http_addr".to_string(), " #where the wallet should find a running node +" + .to_string(), + ); + retval.insert( + "node_api_request_timeout_secs".to_string(), + " +#total timeout for requests to node API " .to_string(), ); @@ -252,6 +259,22 @@ fn comments() -> HashMap { .to_string(), ); + retval.insert( + "request_timeout_secs".to_string(), + " +#Tor request timeout in seconds +" + .to_string(), + ); + + retval.insert( + "bootstrap_timeout_secs".to_string(), + " +#Tor bootstrap timeout in seconds +" + .to_string(), + ); + retval.insert( "[tor.bridge]".to_string(), " diff --git a/config/src/types.rs b/config/src/types.rs index 31e5ac4..ed53a97 100644 --- a/config/src/types.rs +++ b/config/src/types.rs @@ -14,12 +14,12 @@ //! Public types for config modules +use crate::core::global::ChainTypes; +use crate::util::logger::LoggingConfig; use std::fmt; use std::io; use std::path::PathBuf; - -use crate::core::global::ChainTypes; -use crate::util::logger::LoggingConfig; +use std::time::Duration; /// Command-line wallet configuration #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] @@ -37,6 +37,8 @@ pub struct WalletConfig { /// The api address of a running server node against which transaction inputs /// will be checked during send pub check_node_api_http_addr: String, + /// Request timeout for requests to node API. + pub node_api_request_timeout_secs: Option, /// Whether to include foreign API endpoints on the Owner API pub owner_api_include_foreign: Option, /// The directory in which wallet files are stored @@ -65,6 +67,7 @@ impl Default for WalletConfig { api_secret_path: Some(".owner_api_secret".to_string()), node_api_secret_path: Some(".foreign_api_secret".to_string()), check_node_api_http_addr: "http://127.0.0.1:3413".to_string(), + node_api_request_timeout_secs: Some(Self::NODE_API_REQUEST_TIMEOUT_SECS), owner_api_include_foreign: Some(false), data_file_dir: ".".to_string(), no_commit_cache: Some(false), @@ -77,6 +80,9 @@ impl Default for WalletConfig { } impl WalletConfig { + /// Default Node API request timeout in seconds. + pub const NODE_API_REQUEST_TIMEOUT_SECS: u64 = 60; + /// API Listen address pub fn api_listen_addr(&self) -> String { format!("127.0.0.1:{}", self.api_listen_port) @@ -108,6 +114,15 @@ impl WalletConfig { self.accept_fee_base .unwrap_or_else(|| WalletConfig::default_accept_fee_base()) } + + /// Node API requests timeout. + pub fn api_request_timeout(&self) -> Duration { + Duration::from_secs( + self.node_api_request_timeout_secs + .filter(|s| *s > 0) + .unwrap_or(Self::NODE_API_REQUEST_TIMEOUT_SECS), + ) + } } /// Error type wrapping config errors. #[derive(Debug)] @@ -172,6 +187,10 @@ pub struct TorConfig { pub socks_proxy_addr: String, /// Send configuration directory pub send_config_dir: String, + /// Total timeout for request in seconds. + pub request_timeout_secs: Option, + /// Timeout for bootstrap in seconds. + pub bootstrap_timeout_secs: Option, /// Tor bridge config #[serde(default)] pub bridge: TorBridgeConfig, @@ -188,6 +207,8 @@ impl Default for TorConfig { use_tor_listener: true, socks_proxy_addr: "127.0.0.1:59050".to_owned(), send_config_dir: ".".into(), + request_timeout_secs: Some(Self::REQUEST_TIMEOUT_SECS), + bootstrap_timeout_secs: Some(Self::BOOTSTRAP_TIMEOUT_SECS), bridge: TorBridgeConfig::default(), proxy: TorProxyConfig::default(), } @@ -195,6 +216,11 @@ impl Default for TorConfig { } impl TorConfig { + /// Tor request timeout in seconds. + const REQUEST_TIMEOUT_SECS: u64 = 60; + /// Tor bootstrap timeout in seconds. + const BOOTSTRAP_TIMEOUT_SECS: u64 = 60; + /// Check if attempt to send over Tor is needed using provided possible argument at priority. pub fn send_tor(&self, skip_arg: Option) -> bool { if let Some(skip_tor) = skip_arg { @@ -202,6 +228,24 @@ impl TorConfig { } !self.skip_send_attempt.unwrap_or(false) } + + /// Request timeout. + pub fn request_timeout(&self) -> Duration { + Duration::from_secs( + self.request_timeout_secs + .filter(|s| *s > 0) + .unwrap_or(Self::REQUEST_TIMEOUT_SECS), + ) + } + + /// Bootstrap timeout. + pub fn bootstrap_timeout(&self) -> Duration { + Duration::from_secs( + self.bootstrap_timeout_secs + .filter(|s| *s > 0) + .unwrap_or(Self::BOOTSTRAP_TIMEOUT_SECS), + ) + } } /// Tor Bridge Config diff --git a/controller/src/command.rs b/controller/src/command.rs index cb76d26..40e0a57 100644 --- a/controller/src/command.rs +++ b/controller/src/command.rs @@ -22,6 +22,7 @@ use crate::error::Error; use crate::impls::PathToSlatepack; use crate::impls::SlateGetter as _; use crate::keychain; +use crate::libwallet::api_impl::types::update_tx_slate_state; use crate::libwallet::{ self, InitTxArgs, IssueInvoiceTxArgs, NodeClient, PaymentProof, Slate, SlateState, Slatepack, SlatepackAddress, Slatepacker, SlatepackerArgs, WalletLCProvider, @@ -40,7 +41,6 @@ use std::sync::Arc; use std::thread; use std::time::Duration; use uuid::Uuid; -use grin_wallet_libwallet::api_impl::types::update_tx_slate_state; fn show_recovery_phrase(phrase: ZeroingString) { println!("Your recovery phrase is:"); diff --git a/controller/src/controller.rs b/controller/src/controller.rs index 182d911..b70ab51 100644 --- a/controller/src/controller.rs +++ b/controller/src/controller.rs @@ -25,7 +25,6 @@ use crate::util::secp::key::SecretKey; use crate::util::{from_hex, static_secp_instance, to_base64, Mutex}; use futures::channel::oneshot; use grin_wallet_api::JsonId; -use grin_wallet_config::types::{TorBridgeConfig, TorProxyConfig}; use grin_wallet_util::OnionV3Address; use hyper::body; use hyper::header::HeaderValue; @@ -88,18 +87,16 @@ fn init_tor_listener( sec_key: SecretKey, tor_dir: String, addr: &str, - bridge: TorBridgeConfig, - tor_proxy: TorProxyConfig, + tor_config: TorConfig, ) -> Result { info!("Starting external Tor Process listener."); let mut process = tor_process::TorProcess::new(); + let tor_timeout = tor_config.bootstrap_timeout().as_secs(); let mut hm_tor_bridge: HashMap = HashMap::new(); - let mut tor_timeout = 20; - if bridge.bridge_line.is_some() { - tor_timeout = 40; - let bridge_config = tor_bridge::TorBridge::try_from(bridge) + if tor_config.bridge.bridge_line.is_some() { + let bridge_config = tor_bridge::TorBridge::try_from(tor_config.bridge) .map_err(|e| Error::TorConfig(format!("{}", e).into()))?; hm_tor_bridge = bridge_config .to_hashmap() @@ -107,8 +104,8 @@ fn init_tor_listener( } let mut hm_tor_poxy: HashMap = HashMap::new(); - if tor_proxy.transport.is_some() || tor_proxy.allowed_port.is_some() { - let proxy_config = tor_proxy::TorProxy::try_from(tor_proxy) + if tor_config.proxy.transport.is_some() || tor_config.proxy.allowed_port.is_some() { + let proxy_config = tor_proxy::TorProxy::try_from(tor_config.proxy) .map_err(|e| Error::TorConfig(format!("{}", e).into()))?; hm_tor_poxy = proxy_config .to_hashmap() @@ -323,13 +320,7 @@ where let res = if use_integrated { start_tor_service(sec_key, &tor_dir, addr, tor_config.clone()) } else { - init_tor_listener( - sec_key, - tor_dir, - addr, - tor_config.bridge.clone(), - tor_config.proxy.clone(), - ) + init_tor_listener(sec_key, tor_dir, addr, tor_config) }; match res { Ok(service) => { diff --git a/impls/src/adapters/tor.rs b/impls/src/adapters/tor.rs index bb233bd..278ccfe 100644 --- a/impls/src/adapters/tor.rs +++ b/impls/src/adapters/tor.rs @@ -12,15 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -use grin_wallet_config::TorConfig; -use serde::Serialize; -use serde_json::{json, Value}; -use std::collections::HashMap; -use std::convert::TryFrom; -use std::net::SocketAddr; -use std::path::PathBuf; -use std::sync::Arc; - use crate::client_utils::{Client, ClientError}; use crate::libwallet::slate_versions::{SlateVersion, VersionedSlate}; use crate::libwallet::{Error, Slate}; @@ -30,6 +21,14 @@ use crate::tor::process::TorProcess; use crate::tor::proxy::TorProxy; use crate::tor::{config as tor_config, Tor}; use crate::SlateSender; +use grin_wallet_config::TorConfig; +use serde::Serialize; +use serde_json::{json, Value}; +use std::collections::HashMap; +use std::convert::TryFrom; +use std::net::SocketAddr; +use std::path::PathBuf; +use std::sync::Arc; #[derive(Clone)] pub struct TorSlateSender { @@ -104,7 +103,7 @@ impl TorSlateSender { path.push("torrc"); tor.torrc_path(path.to_str().unwrap()) .working_dir(tor_dir.to_str().unwrap()) - .timeout(20) + .timeout(config.bootstrap_timeout().as_secs()) .completion_percent(100) .launch() .map_err(|e| Error::TorProcess(format!("{:?}", e)))?; @@ -183,14 +182,15 @@ impl TorSlateSender { SocketAddr::V4(self.config.socks_proxy_addr.parse().map_err(|_| { ClientError::Internal("Socks proxy address is not set".to_string()) })?); - let client = Client::with_proxy(socks_proxy_addr, "socks5h://") + let timeout = self.config.request_timeout(); + let client = Client::with_proxy(socks_proxy_addr, "socks5h://", timeout) .map_err(|_| ClientError::Internal("Unable to create http client".into()))?; let req = client.create_post_request(url, None, &input)?; let res = client.send_request(req)?; res } else { if let Some(client) = &self.tor.client { - tor_post(client.clone(), &input, url) + tor_post(client.clone(), &self.config, &input, url) .map_err(|e| ClientError::RequestError(format!("{:?}", e)))? } else { return Err(ClientError::Internal("Tor is not configured".to_string())); diff --git a/impls/src/client_utils/client.rs b/impls/src/client_utils/client.rs index ffccc14..4bb8f97 100644 --- a/impls/src/client_utils/client.rs +++ b/impls/src/client_utils/client.rs @@ -52,22 +52,26 @@ pub struct Client { impl Client { /// New client - pub fn new() -> Result { - Self::build(None) + pub fn new(request_timeout: Duration) -> Result { + Self::build(None, request_timeout) } - pub fn with_proxy(socks_proxy_addr: SocketAddr, scheme: &'static str) -> Result { - Self::build(Some((socks_proxy_addr, scheme))) + pub fn with_proxy( + socks_proxy_addr: SocketAddr, + scheme: &'static str, + request_timeout: Duration, + ) -> Result { + Self::build(Some((socks_proxy_addr, scheme)), request_timeout) } - fn build(proxy: Option<(SocketAddr, &str)>) -> Result { + fn build(proxy: Option<(SocketAddr, &str)>, request_timeout: Duration) -> Result { let mut headers = HeaderMap::new(); headers.insert(USER_AGENT, HeaderValue::from_static("grin-client")); headers.insert(ACCEPT, HeaderValue::from_static("application/json")); headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/json")); let mut builder = ClientBuilder::new() - .timeout(Duration::from_secs(20)) + .timeout(request_timeout) .use_rustls_tls() .default_headers(headers); diff --git a/impls/src/node_clients/http.rs b/impls/src/node_clients/http.rs index 6b4d82d..b7e29c6 100644 --- a/impls/src/node_clients/http.rs +++ b/impls/src/node_clients/http.rs @@ -14,7 +14,9 @@ //! Client functions, implementations of the NodeClient trait +use super::resp_types::*; use crate::api::{self, LocatedTxKernel, OutputListing, OutputPrintable}; +use crate::client_utils::json_rpc::*; use crate::client_utils::{Client, RUNTIME}; use crate::core::core::{Transaction, TxKernel}; use crate::libwallet; @@ -26,9 +28,7 @@ use futures::TryStreamExt; use std::collections::HashMap; use std::env; use std::net::SocketAddr; - -use super::resp_types::*; -use crate::client_utils::json_rpc::*; +use std::time::Duration; const ENDPOINT: &str = "/v2/foreign"; @@ -45,8 +45,9 @@ impl HTTPNodeClient { pub fn new( node_url: &str, node_api_secret: Option, + request_timeout: Duration, ) -> Result { - Self::new_proxy(node_url, node_api_secret, None) + Self::new_proxy(node_url, node_api_secret, None, request_timeout) } /// Create a new client with proxy @@ -54,16 +55,17 @@ impl HTTPNodeClient { node_url: &str, node_api_secret: Option, proxy: Option<(SocketAddr, &'static str)>, + request_timeout: Duration, ) -> Result { let client = if let Some((a, s)) = proxy { - Client::with_proxy(a, s) + Client::with_proxy(a, s, request_timeout) } else { - Client::new() + Client::new(request_timeout) }; Ok(HTTPNodeClient { client: client.map_err(|_| libwallet::Error::Node)?, node_url: node_url.to_owned(), - node_api_secret: node_api_secret, + node_api_secret, node_version_info: None, }) } @@ -107,18 +109,25 @@ impl NodeClient for HTTPNodeClient { fn node_url(&self) -> &str { &self.node_url } - fn node_api_secret(&self) -> Option { - self.node_api_secret.clone() - } - fn set_node_url(&mut self, node_url: &str) { self.node_url = node_url.to_owned(); } + fn node_api_secret(&self) -> Option { + self.node_api_secret.clone() + } + fn set_node_api_secret(&mut self, node_api_secret: Option) { self.node_api_secret = node_api_secret; } + /// Posts a transaction to a grin node + fn post_tx(&self, tx: &Transaction, fluff: bool) -> Result<(), libwallet::Error> { + let params = json!([tx, fluff]); + self.send_json_request::("push_transaction", ¶ms)?; + Ok(()) + } + fn get_version_info(&mut self) -> Option { if let Some(v) = self.node_version_info.as_ref() { return Some(v.clone()); @@ -135,30 +144,23 @@ impl NodeClient for HTTPNodeClient { // If node isn't available, allow offline functions // unfortunately have to parse string due to error structure let err_string = format!("{}", e); - if err_string.contains("404") { - return Some(NodeVersionInfo { + return if err_string.contains("404") { + Some(NodeVersionInfo { node_version: "1.0.0".into(), block_header_version: 1, verified: Some(false), - }); + }) } else { error!("Unable to contact Node to get version info: {}, check your node is running", e); warn!("Warning: a) Node is offline, or b) 'node_api_secret_path' in 'grin-wallet.toml' is set incorrectly"); - return None; - } + None + }; } }; self.node_version_info = Some(retval.clone()); Some(retval) } - /// Posts a transaction to a grin node - fn post_tx(&self, tx: &Transaction, fluff: bool) -> Result<(), libwallet::Error> { - let params = json!([tx, fluff]); - self.send_json_request::("push_transaction", ¶ms)?; - Ok(()) - } - /// Return the chain tip from a given node fn get_chain_tip(&self) -> Result<(u64, String), libwallet::Error> { let result = self.send_json_request::("get_tip", &serde_json::Value::Null)?; @@ -222,7 +224,7 @@ impl NodeClient for HTTPNodeClient { .collect(); // going to leave this here even though we're moving - // to the json RPC api to keep the functionality of + // to the JSON RPC api to keep the functionality of // parallelizing larger requests. Will raise default // from 200 to 500, however let chunk_default = 500; @@ -420,8 +422,8 @@ mod tests { } } - // Wallet will "push" a transaction to node, serializing the transaction as json. - // We are testing the json structure is what we expect here. + // Wallet will "push" a transaction to node, serializing the transaction as JSON. + // We are testing the JSON structure is what we expect here. #[test] fn test_transaction_json_ser_deser() { let tx1 = tx1i1o_v2_compatible(); diff --git a/impls/src/tor/arti.rs b/impls/src/tor/arti.rs index e5af8d3..ff11e8c 100644 --- a/impls/src/tor/arti.rs +++ b/impls/src/tor/arti.rs @@ -160,12 +160,10 @@ pub fn start_tor_client(tor_dir: &str, config: TorConfig) -> Result }) } -/// Tor request timeout in milliseconds. -const REQUEST_TIMEOUT_MS: u64 = 60000; - /// Make POST request with provided client. pub fn tor_post( client: Arc>, + tor_config: &TorConfig, input: &IN, url: &str, ) -> Result @@ -183,12 +181,13 @@ where Some(h) => h, } .to_string(); + let timeout = tor_config.request_timeout(); let res: Result = thread::spawn(move || { let c = client.clone(); client.runtime().block_on(async move { let res = c .runtime() - .timeout(Duration::from_millis(REQUEST_TIMEOUT_MS), async { + .timeout(timeout, async { let stream = c .connect((host, url.port_u16().unwrap_or(80))) .await @@ -238,21 +237,18 @@ where res } -/// Client Bootstrap timeout in milliseconds. -const BOOTSTRAP_TIMEOUT_MS: u64 = 60000; - /// Create Tor client. fn init_client( state_path: &PathBuf, cache_path: &PathBuf, - config: TorConfig, + tor_config: TorConfig, reuse_client: bool, ) -> Result<(Arc>, TorClientConfig), Error> { let mut builder = TorClientConfigBuilder::from_directories(&state_path, cache_path); builder.address_filter().allow_onion_addrs(true); // Configure bridge. - if let Some(bridge_line) = config.bridge.bridge_line { + if let Some(bridge_line) = tor_config.bridge.bridge_line.as_ref() { let bridge: BridgeConfigBuilder = bridge_line .parse() .map_err(|e| Error::TorConfig(format!("{:?}", e)))?; @@ -266,7 +262,11 @@ fn init_client( } Some(t) => { // Now configure bridge transport. (Requires the "pt-client" feature) - let bin_path = config.bridge.bridge_bin_path.unwrap_or(t.to_owned()); + let bin_path = tor_config + .bridge + .bridge_bin_path + .clone() + .unwrap_or(t.to_owned()); let mut transport = TransportConfigBuilder::default(); transport .protocols(vec![t @@ -294,7 +294,7 @@ fn init_client( *cached_client_config = None; } } - let res = launch_client(config.clone()); + let res = launch_client(config.clone(), &tor_config); return match res { Ok(client) => { cached_client_config.replace((client.clone(), config.clone())); @@ -304,7 +304,7 @@ fn init_client( }; } - let res = launch_client(config.clone()); + let res = launch_client(config.clone(), &tor_config); match res { Ok(client) => Ok((client, config)), Err(e) => Err(e), @@ -312,13 +312,17 @@ fn init_client( } /// Launch tor client from provided configuration. -fn launch_client(config: TorClientConfig) -> Result>, Error> { +fn launch_client( + client_config: TorClientConfig, + tor_config: &TorConfig, +) -> Result>, Error> { let r = runtime()?; let client = TorClient::with_runtime(r) - .config(config) + .config(client_config) .create_unbootstrapped() .map_err(|e| Error::TorProcess(format!("{:?}", e)))?; let c = client.clone(); + let timeout = tor_config.bootstrap_timeout(); let res = client.runtime().block_on(async move { let bootstrap = async || { return match c.bootstrap().await { @@ -339,11 +343,7 @@ fn launch_client(config: TorClientConfig) -> Result Err(e), }; }; - match c - .runtime() - .timeout(Duration::from_millis(BOOTSTRAP_TIMEOUT_MS), bootstrap()) - .await - { + match c.runtime().timeout(timeout, bootstrap()).await { Ok(r) => match r { Err(e) => Err(Error::TorProcess(format!("{:?}", e))), Ok(_) => Ok(c), diff --git a/impls/src/tor/process.rs b/impls/src/tor/process.rs index f964326..4e53f8e 100644 --- a/impls/src/tor/process.rs +++ b/impls/src/tor/process.rs @@ -51,13 +51,13 @@ extern crate timer; use regex::Regex; use std::fs::{self, File}; -use std::io; use std::io::Write; use std::io::{BufRead, BufReader}; use std::path::{Path, MAIN_SEPARATOR}; use std::process::{Child, ChildStdout, Command, Stdio}; use std::sync::mpsc::channel; use std::thread; +use std::{cmp, io}; use sysinfo::{Process, ProcessExt, System, SystemExt}; #[cfg(windows)] @@ -83,7 +83,7 @@ pub struct TorProcess { args: Vec, torrc_path: Option, completion_percent: u8, - timeout: u32, + timeout: u64, working_dir: Option, pub stdout: Option>, pub process: Option, @@ -96,8 +96,8 @@ impl TorProcess { tor_cmd: TOR_EXE_NAME.to_string(), args: vec![], torrc_path: None, - completion_percent: 100 as u8, - timeout: 0 as u32, + completion_percent: 100, + timeout: 0, working_dir: None, stdout: None, process: None, @@ -137,7 +137,7 @@ impl TorProcess { self } - pub fn timeout(&mut self, timeout: u32) -> &mut Self { + pub fn timeout(&mut self, timeout: u64) -> &mut Self { self.timeout = timeout; self } @@ -198,10 +198,11 @@ impl TorProcess { let stdout_timeout_tx = stdout_tx.clone(); let timer = timer::Timer::new(); - let _guard = - timer.schedule_with_delay(chrono::Duration::seconds(self.timeout as i64), move || { - stdout_timeout_tx.send(Err(Error::Timeout)).unwrap_or(()); - }); + // Keep this below chrono's DateTime range used by timer. + let timeout = chrono::Duration::seconds(cmp::min(self.timeout, u32::MAX as u64) as i64); + let _guard = timer.schedule_with_delay(timeout, move || { + stdout_timeout_tx.send(Err(Error::Timeout)).unwrap_or(()); + }); let stdout_thread = thread::spawn(move || { stdout_tx .send(Self::parse_tor_stdout(stdout, completion_percent)) @@ -225,8 +226,8 @@ impl TorProcess { mut stdout: BufReader, completion_perc: u8, ) -> Result, Error> { - let re_bootstrap = Regex::new(r"^\[notice\] Bootstrapped (?P[0-9]+)%(.*): ") - .map_err(Error::Regex)?; + let re_bootstrap = + Regex::new(r"^\[notice] Bootstrapped (?P[0-9]+)%(.*): ").map_err(Error::Regex)?; let timestamp_len = "May 16 02:50:08.792".len(); let mut warnings = Vec::new(); diff --git a/integration/tests/framework.rs b/integration/tests/framework.rs index ffade00..13202ba 100644 --- a/integration/tests/framework.rs +++ b/integration/tests/framework.rs @@ -281,7 +281,7 @@ impl LocalServerContainer { let _ = fs::create_dir_all(self.wallet_config.clone().data_file_dir); let r = wallet::WalletSeed::init_file(&self.wallet_config, 32, None, ""); - let client_n = HTTPNodeClient::new(&self.wallet_config.check_node_api_http_addr, None); + let client_n = HTTPNodeClient::new(&self.wallet_config.check_node_api_http_addr, None, std::time::Duration::from_secs(60)); if let Err(_e) = r { //panic!("Error initializing wallet seed: {}", e); @@ -354,7 +354,7 @@ impl LocalServerContainer { let keychain: keychain::ExtKeychain = wallet_seed .derive_keychain(false) .expect("Failed to derive keychain from seed file and passphrase."); - let client_n = HTTPNodeClient::new(&config.check_node_api_http_addr, None); + let client_n = HTTPNodeClient::new(&config.check_node_api_http_addr, None, std::time::Duration::from_secs(60)); let mut wallet = LMDBBackend::new(config.clone(), "", client_n) .unwrap_or_else(|e| panic!("Error creating wallet: {:?} Config: {:?}", e, config)); wallet.keychain = Some(keychain); @@ -382,7 +382,7 @@ impl LocalServerContainer { .derive_keychain(false) .expect("Failed to derive keychain from seed file and passphrase."); - let client_n = HTTPNodeClient::new(&config.check_node_api_http_addr, None); + let client_n = HTTPNodeClient::new(&config.check_node_api_http_addr, None, std::time::Duration::from_secs(60)); let client_w = HTTPWalletCommAdapter::new(); let max_outputs = 500; diff --git a/integration/tests/simulnet.rs b/integration/tests/simulnet.rs index df407ec..2675f33 100644 --- a/integration/tests/simulnet.rs +++ b/integration/tests/simulnet.rs @@ -908,7 +908,7 @@ fn replicate_tx_fluff_failure() { // Create Wallet 1 (Mining Input) and start it listening // Wallet 1 post to another node, just for fun - let client1 = HTTPNodeClient::new("http://127.0.0.1:23003", None); + let client1 = HTTPNodeClient::new("http://127.0.0.1:23003", None, std::time::Duration::from_secs(60)); let client1_w = HTTPWalletCommAdapter::new(); let wallet1 = create_wallet("target/tmp/tx_fluff/wallet1", client1.clone()); let _wallet1_handle = thread::spawn(move || { @@ -917,7 +917,7 @@ fn replicate_tx_fluff_failure() { }); // Create Wallet 2 (Recipient) and launch - let client2 = HTTPNodeClient::new("http://127.0.0.1:23001", None); + let client2 = HTTPNodeClient::new("http://127.0.0.1:23001", None, std::time::Duration::from_secs(60)); let wallet2 = create_wallet("target/tmp/tx_fluff/wallet2", client2.clone()); let _wallet2_handle = thread::spawn(move || { controller::foreign_listener(wallet2, "127.0.0.1:33001", None) diff --git a/src/bin/grin-wallet.rs b/src/bin/grin-wallet.rs index 9189f02..c6a342e 100644 --- a/src/bin/grin-wallet.rs +++ b/src/bin/grin-wallet.rs @@ -161,6 +161,8 @@ fn real_main() -> i32 { global::init_global_accept_fee_base(config.members.as_ref().unwrap().wallet.accept_fee_base()); let wallet_config = config.clone().members.unwrap().wallet; - let node_client = HTTPNodeClient::new(&wallet_config.check_node_api_http_addr, None).unwrap(); + let timeout = wallet_config.api_request_timeout(); + let node_client = + HTTPNodeClient::new(&wallet_config.check_node_api_http_addr, None, timeout).unwrap(); cmd::wallet_command(&args, config, node_client) }