diff --git a/api/src/client.rs b/api/src/client.rs index 2cbd8ff71..01a0cbb16 100644 --- a/api/src/client.rs +++ b/api/src/client.rs @@ -25,6 +25,33 @@ use serde::{Deserialize, Serialize}; use std::time::Duration; use tokio::runtime::Builder; +// Client Request Timeout +pub struct TimeOut { + pub connect: Duration, + pub read: Duration, + pub write: Duration, +} + +impl TimeOut { + pub fn new(connect: u64, read: u64, write: u64) -> Self { + Self { + connect: Duration::from_secs(connect), + read: Duration::from_secs(read), + write: Duration::from_secs(write), + } + } +} + +impl Default for TimeOut { + fn default() -> TimeOut { + TimeOut { + connect: Duration::from_secs(20), + read: Duration::from_secs(20), + write: Duration::from_secs(20), + } + } +} + /// Helper function to easily issue a HTTP GET request against a given URL that /// returns a JSON object. Handles request building, JSON deserialization and /// response code checking. @@ -35,7 +62,10 @@ pub fn get(url: &str, api_secret: Option) -> Result where for<'de> T: Deserialize<'de>, { - handle_request(build_request(url, "GET", api_secret, None)?) + handle_request( + build_request(url, "GET", api_secret, None)?, + TimeOut::default(), + ) } /// Helper function to easily issue an async HTTP GET request against a given @@ -52,7 +82,10 @@ where /// on a given URL that returns nothing. Handles request /// building and response code checking. pub fn get_no_ret(url: &str, api_secret: Option) -> Result<(), Error> { - send_request(build_request(url, "GET", api_secret, None)?)?; + send_request( + build_request(url, "GET", api_secret, None)?, + TimeOut::default(), + )?; Ok(()) } @@ -60,13 +93,18 @@ pub fn get_no_ret(url: &str, api_secret: Option) -> Result<(), Error> { /// object as body on a given URL that returns a JSON object. Handles request /// building, JSON serialization and deserialization, and response code /// checking. -pub fn post(url: &str, api_secret: Option, input: &IN) -> Result +pub fn post( + url: &str, + api_secret: Option, + input: &IN, + timeout: TimeOut, +) -> Result where IN: Serialize, for<'de> OUT: Deserialize<'de>, { let req = create_post_request(url, api_secret, input)?; - handle_request(req) + handle_request(req, timeout) } /// Helper function to easily issue an async HTTP POST request with the @@ -94,7 +132,10 @@ pub fn post_no_ret(url: &str, api_secret: Option, input: &IN) -> Res where IN: Serialize, { - send_request(create_post_request(url, api_secret, input)?)?; + send_request( + create_post_request(url, api_secret, input)?, + TimeOut::default(), + )?; Ok(()) } @@ -110,7 +151,11 @@ pub async fn post_no_ret_async( where IN: Serialize, { - send_request_async(create_post_request(url, api_secret, input)?).await?; + send_request_async( + create_post_request(url, api_secret, input)?, + TimeOut::default(), + ) + .await?; Ok(()) } @@ -155,11 +200,11 @@ where build_request(url, "POST", api_secret, Some(json)) } -fn handle_request(req: Request) -> Result +fn handle_request(req: Request, timeout: TimeOut) -> Result where for<'de> T: Deserialize<'de>, { - let data = send_request(req)?; + let data = send_request(req, timeout)?; serde_json::from_str(&data).map_err(|e| { e.context(ErrorKind::ResponseError("Cannot parse response".to_owned())) .into() @@ -170,18 +215,23 @@ async fn handle_request_async(req: Request) -> Result where for<'de> T: Deserialize<'de> + Send + 'static, { - let data = send_request_async(req).await?; + let data = send_request_async(req, TimeOut::default()).await?; let ser = serde_json::from_str(&data) .map_err(|e| e.context(ErrorKind::ResponseError("Cannot parse response".to_owned())))?; Ok(ser) } -async fn send_request_async(req: Request) -> Result { +async fn send_request_async(req: Request, timeout: TimeOut) -> Result { let https = hyper_rustls::HttpsConnector::new(); + let (connect, read, write) = ( + Some(timeout.connect), + Some(timeout.read), + Some(timeout.write), + ); let mut connector = TimeoutConnector::new(https); - connector.set_connect_timeout(Some(Duration::from_secs(20))); - connector.set_read_timeout(Some(Duration::from_secs(20))); - connector.set_write_timeout(Some(Duration::from_secs(20))); + connector.set_connect_timeout(connect); + connector.set_read_timeout(read); + connector.set_write_timeout(write); let client = Client::builder().build::<_, Body>(connector); let resp = client @@ -205,11 +255,11 @@ async fn send_request_async(req: Request) -> Result { Ok(String::from_utf8_lossy(&raw).to_string()) } -pub fn send_request(req: Request) -> Result { +pub fn send_request(req: Request, timeout: TimeOut) -> Result { let mut rt = Builder::new() .basic_scheduler() .enable_all() .build() .map_err(|e| ErrorKind::RequestError(format!("{}", e)))?; - rt.block_on(send_request_async(req)) + rt.block_on(send_request_async(req, timeout)) } diff --git a/api/src/handlers/chain_api.rs b/api/src/handlers/chain_api.rs index 3820993d1..65b5edf96 100644 --- a/api/src/handlers/chain_api.rs +++ b/api/src/handlers/chain_api.rs @@ -53,9 +53,9 @@ pub struct ChainValidationHandler { } impl ChainValidationHandler { - pub fn validate_chain(&self) -> Result<(), Error> { + pub fn validate_chain(&self, fast_validation: bool) -> Result<(), Error> { w(&self.chain)? - .validate(true) + .validate(fast_validation) .map_err(|_| ErrorKind::Internal("chain error".to_owned()).into()) } } diff --git a/api/src/owner.rs b/api/src/owner.rs index b54a4b0ca..367fa637f 100644 --- a/api/src/owner.rs +++ b/api/src/owner.rs @@ -80,17 +80,20 @@ impl Owner { /// Trigger a validation of the chain state. /// + /// # Arguments + /// * `assume_valid_rangeproofs_kernels` - if false, will validate rangeproofs, kernel signatures and sum of kernel excesses. if true, will only validate the sum of kernel excesses should equal the sum of unspent outputs minus total supply. + /// /// # Returns /// * Result Containing: /// * `Ok(())` if the validation was done successfully /// * or [`Error`](struct.Error.html) if an error is encountered. /// - pub fn validate_chain(&self) -> Result<(), Error> { + pub fn validate_chain(&self, assume_valid_rangeproofs_kernels: bool) -> Result<(), Error> { let chain_validation_handler = ChainValidationHandler { chain: self.chain.clone(), }; - chain_validation_handler.validate_chain() + chain_validation_handler.validate_chain(assume_valid_rangeproofs_kernels) } /// Trigger a compaction of the chain state to regain storage space. diff --git a/api/src/owner_rpc.rs b/api/src/owner_rpc.rs index 9eee836dc..e68aa21b2 100644 --- a/api/src/owner_rpc.rs +++ b/api/src/owner_rpc.rs @@ -83,7 +83,7 @@ pub trait OwnerRpc: Sync + Send { { "jsonrpc": "2.0", "method": "validate_chain", - "params": [], + "params": ["false"], "id": 1 } # "# @@ -100,7 +100,7 @@ pub trait OwnerRpc: Sync + Send { # ); ``` */ - fn validate_chain(&self) -> Result<(), ErrorKind>; + fn validate_chain(&self, assume_valid_rangeproofs_kernels: bool) -> Result<(), ErrorKind>; /** Networked version of [Owner::compact_chain](struct.Owner.html#method.compact_chain). @@ -363,8 +363,8 @@ impl OwnerRpc for Owner { Owner::get_status(self).map_err(|e| e.kind().clone()) } - fn validate_chain(&self) -> Result<(), ErrorKind> { - Owner::validate_chain(self).map_err(|e| e.kind().clone()) + fn validate_chain(&self, assume_valid_rangeproofs_kernels: bool) -> Result<(), ErrorKind> { + Owner::validate_chain(self, assume_valid_rangeproofs_kernels).map_err(|e| e.kind().clone()) } fn reset_chain_head(&self, hash: String) -> Result<(), ErrorKind> { @@ -403,7 +403,7 @@ macro_rules! doctest_helper_json_rpc_owner_assert_response { // create temporary grin server, run jsonrpc request on node api, delete server, return // json response. - { + { /*use grin_servers::test_framework::framework::run_doctest; use grin_util as util; use serde_json; @@ -437,6 +437,6 @@ macro_rules! doctest_helper_json_rpc_owner_assert_response { serde_json::to_string_pretty(&expected_response).unwrap() ); }*/ - } + } }; } diff --git a/servers/src/mining/mine_block.rs b/servers/src/mining/mine_block.rs index 1419cd85a..9f43fbc49 100644 --- a/servers/src/mining/mine_block.rs +++ b/servers/src/mining/mine_block.rs @@ -262,7 +262,8 @@ fn create_coinbase(dest: &str, block_fees: &BlockFees) -> Result trace!("Sending build_coinbase request: {}", req_body); let req = api::client::create_post_request(url.as_str(), None, &req_body)?; - let res: String = api::client::send_request(req).map_err(|e| { + let timeout = api::client::TimeOut::default(); + let res: String = api::client::send_request(req, timeout).map_err(|e| { let report = format!( "Failed to get coinbase from {}. Is the wallet listening? {}", dest, e diff --git a/src/bin/cmd/client.rs b/src/bin/cmd/client.rs index 42ef67473..b403a3dfe 100644 --- a/src/bin/cmd/client.rs +++ b/src/bin/cmd/client.rs @@ -45,10 +45,19 @@ impl HTTPNodeClient { method: &str, params: &serde_json::Value, ) -> Result { + let timeout = match method { + // 6 hours read timeout + "validate_chain" => client::TimeOut::new(20, 21600, 20), + _ => client::TimeOut::default(), + }; let url = format!("http://{}{}", self.node_url, ENDPOINT); let req = build_request(method, params); - let res = - client::post::(url.as_str(), self.node_api_secret.clone(), &req); + let res = client::post::( + url.as_str(), + self.node_api_secret.clone(), + &req, + timeout, + ); match res { Err(e) => { @@ -149,6 +158,27 @@ impl HTTPNodeClient { e.reset().unwrap(); } + pub fn verify_chain(&self, assume_valid_rangeproofs_kernels: bool) { + let mut e = term::stdout().unwrap(); + let params = json!([assume_valid_rangeproofs_kernels]); + writeln!( + e, + "Checking the state of the chain. This might take time..." + ) + .unwrap(); + match self.send_json_request::<()>("validate_chain", ¶ms) { + Ok(_) => { + if assume_valid_rangeproofs_kernels { + writeln!(e, "Successfully validated the sum of kernel excesses! [fast_verification enabled]").unwrap() + } else { + writeln!(e, "Successfully validated the sum of kernel excesses, kernel signature and rangeproofs!").unwrap() + } + } + Err(err) => writeln!(e, "Failed to validate chain: {:?}", err).unwrap(), + } + e.reset().unwrap(); + } + pub fn ban_peer(&self, peer_addr: &SocketAddr) { let mut e = term::stdout().unwrap(); let params = json!([peer_addr]); @@ -191,6 +221,10 @@ pub fn client_command(client_args: &ArgMatches<'_>, global_config: GlobalConfig) let hash = args.value_of("hash").unwrap(); node_client.invalidate_header(hash.to_string()); } + ("verify-chain", Some(args)) => { + let assume_valid_rangeproofs_kernels = args.is_present("fast"); + node_client.verify_chain(assume_valid_rangeproofs_kernels); + } ("ban", Some(peer_args)) => { let peer = peer_args.value_of("peer").unwrap(); diff --git a/src/bin/grin.yml b/src/bin/grin.yml index 78f8cc745..28c7fd0d5 100644 --- a/src/bin/grin.yml +++ b/src/bin/grin.yml @@ -78,9 +78,17 @@ subcommands: - hash: help: The header hash to reset to required: true + - verify-chain: + about: Trigger a verication of the rangeproofs, kernel signatures and excesses. + args: + - fast: + help: if present, will skip verification of rangeproofs, kernel signatures and will only validate the sum of kernel excesses. + short: f + long: fast + takes_value: false - invalidateheader: about: Adds header hash to denylist args: - hash: help: The header hash to invalidate - required: true \ No newline at end of file + required: true