mirror of
https://github.com/mimblewimble/grin.git
synced 2025-01-20 19:11:08 +03:00
verify-chain
node client arg (#3678)
* add parameter fast_validation to the api * arg `verify-chain` on node client * arg verify chain + read_timeout parameter * change timeout to 2hours * update with tromp review * 2nd comment from tromp
This commit is contained in:
parent
c92d2e9fba
commit
a3eebbc0ab
7 changed files with 125 additions and 29 deletions
|
@ -25,6 +25,33 @@ use serde::{Deserialize, Serialize};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use tokio::runtime::Builder;
|
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
|
/// Helper function to easily issue a HTTP GET request against a given URL that
|
||||||
/// returns a JSON object. Handles request building, JSON deserialization and
|
/// returns a JSON object. Handles request building, JSON deserialization and
|
||||||
/// response code checking.
|
/// response code checking.
|
||||||
|
@ -35,7 +62,10 @@ pub fn get<T>(url: &str, api_secret: Option<String>) -> Result<T, Error>
|
||||||
where
|
where
|
||||||
for<'de> T: Deserialize<'de>,
|
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
|
/// 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
|
/// on a given URL that returns nothing. Handles request
|
||||||
/// building and response code checking.
|
/// building and response code checking.
|
||||||
pub fn get_no_ret(url: &str, api_secret: Option<String>) -> Result<(), Error> {
|
pub fn get_no_ret(url: &str, api_secret: Option<String>) -> Result<(), Error> {
|
||||||
send_request(build_request(url, "GET", api_secret, None)?)?;
|
send_request(
|
||||||
|
build_request(url, "GET", api_secret, None)?,
|
||||||
|
TimeOut::default(),
|
||||||
|
)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,13 +93,18 @@ pub fn get_no_ret(url: &str, api_secret: Option<String>) -> Result<(), Error> {
|
||||||
/// object as body on a given URL that returns a JSON object. Handles request
|
/// object as body on a given URL that returns a JSON object. Handles request
|
||||||
/// building, JSON serialization and deserialization, and response code
|
/// building, JSON serialization and deserialization, and response code
|
||||||
/// checking.
|
/// checking.
|
||||||
pub fn post<IN, OUT>(url: &str, api_secret: Option<String>, input: &IN) -> Result<OUT, Error>
|
pub fn post<IN, OUT>(
|
||||||
|
url: &str,
|
||||||
|
api_secret: Option<String>,
|
||||||
|
input: &IN,
|
||||||
|
timeout: TimeOut,
|
||||||
|
) -> Result<OUT, Error>
|
||||||
where
|
where
|
||||||
IN: Serialize,
|
IN: Serialize,
|
||||||
for<'de> OUT: Deserialize<'de>,
|
for<'de> OUT: Deserialize<'de>,
|
||||||
{
|
{
|
||||||
let req = create_post_request(url, api_secret, input)?;
|
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
|
/// Helper function to easily issue an async HTTP POST request with the
|
||||||
|
@ -94,7 +132,10 @@ pub fn post_no_ret<IN>(url: &str, api_secret: Option<String>, input: &IN) -> Res
|
||||||
where
|
where
|
||||||
IN: Serialize,
|
IN: Serialize,
|
||||||
{
|
{
|
||||||
send_request(create_post_request(url, api_secret, input)?)?;
|
send_request(
|
||||||
|
create_post_request(url, api_secret, input)?,
|
||||||
|
TimeOut::default(),
|
||||||
|
)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,7 +151,11 @@ pub async fn post_no_ret_async<IN>(
|
||||||
where
|
where
|
||||||
IN: Serialize,
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -155,11 +200,11 @@ where
|
||||||
build_request(url, "POST", api_secret, Some(json))
|
build_request(url, "POST", api_secret, Some(json))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_request<T>(req: Request<Body>) -> Result<T, Error>
|
fn handle_request<T>(req: Request<Body>, timeout: TimeOut) -> Result<T, Error>
|
||||||
where
|
where
|
||||||
for<'de> T: Deserialize<'de>,
|
for<'de> T: Deserialize<'de>,
|
||||||
{
|
{
|
||||||
let data = send_request(req)?;
|
let data = send_request(req, timeout)?;
|
||||||
serde_json::from_str(&data).map_err(|e| {
|
serde_json::from_str(&data).map_err(|e| {
|
||||||
e.context(ErrorKind::ResponseError("Cannot parse response".to_owned()))
|
e.context(ErrorKind::ResponseError("Cannot parse response".to_owned()))
|
||||||
.into()
|
.into()
|
||||||
|
@ -170,18 +215,23 @@ async fn handle_request_async<T>(req: Request<Body>) -> Result<T, Error>
|
||||||
where
|
where
|
||||||
for<'de> T: Deserialize<'de> + Send + 'static,
|
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)
|
let ser = serde_json::from_str(&data)
|
||||||
.map_err(|e| e.context(ErrorKind::ResponseError("Cannot parse response".to_owned())))?;
|
.map_err(|e| e.context(ErrorKind::ResponseError("Cannot parse response".to_owned())))?;
|
||||||
Ok(ser)
|
Ok(ser)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn send_request_async(req: Request<Body>) -> Result<String, Error> {
|
async fn send_request_async(req: Request<Body>, timeout: TimeOut) -> Result<String, Error> {
|
||||||
let https = hyper_rustls::HttpsConnector::new();
|
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);
|
let mut connector = TimeoutConnector::new(https);
|
||||||
connector.set_connect_timeout(Some(Duration::from_secs(20)));
|
connector.set_connect_timeout(connect);
|
||||||
connector.set_read_timeout(Some(Duration::from_secs(20)));
|
connector.set_read_timeout(read);
|
||||||
connector.set_write_timeout(Some(Duration::from_secs(20)));
|
connector.set_write_timeout(write);
|
||||||
let client = Client::builder().build::<_, Body>(connector);
|
let client = Client::builder().build::<_, Body>(connector);
|
||||||
|
|
||||||
let resp = client
|
let resp = client
|
||||||
|
@ -205,11 +255,11 @@ async fn send_request_async(req: Request<Body>) -> Result<String, Error> {
|
||||||
Ok(String::from_utf8_lossy(&raw).to_string())
|
Ok(String::from_utf8_lossy(&raw).to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn send_request(req: Request<Body>) -> Result<String, Error> {
|
pub fn send_request(req: Request<Body>, timeout: TimeOut) -> Result<String, Error> {
|
||||||
let mut rt = Builder::new()
|
let mut rt = Builder::new()
|
||||||
.basic_scheduler()
|
.basic_scheduler()
|
||||||
.enable_all()
|
.enable_all()
|
||||||
.build()
|
.build()
|
||||||
.map_err(|e| ErrorKind::RequestError(format!("{}", e)))?;
|
.map_err(|e| ErrorKind::RequestError(format!("{}", e)))?;
|
||||||
rt.block_on(send_request_async(req))
|
rt.block_on(send_request_async(req, timeout))
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,9 +53,9 @@ pub struct ChainValidationHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ChainValidationHandler {
|
impl ChainValidationHandler {
|
||||||
pub fn validate_chain(&self) -> Result<(), Error> {
|
pub fn validate_chain(&self, fast_validation: bool) -> Result<(), Error> {
|
||||||
w(&self.chain)?
|
w(&self.chain)?
|
||||||
.validate(true)
|
.validate(fast_validation)
|
||||||
.map_err(|_| ErrorKind::Internal("chain error".to_owned()).into())
|
.map_err(|_| ErrorKind::Internal("chain error".to_owned()).into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -80,17 +80,20 @@ impl Owner {
|
||||||
|
|
||||||
/// Trigger a validation of the chain state.
|
/// 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
|
/// # Returns
|
||||||
/// * Result Containing:
|
/// * Result Containing:
|
||||||
/// * `Ok(())` if the validation was done successfully
|
/// * `Ok(())` if the validation was done successfully
|
||||||
/// * or [`Error`](struct.Error.html) if an error is encountered.
|
/// * 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 {
|
let chain_validation_handler = ChainValidationHandler {
|
||||||
chain: self.chain.clone(),
|
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.
|
/// Trigger a compaction of the chain state to regain storage space.
|
||||||
|
|
|
@ -83,7 +83,7 @@ pub trait OwnerRpc: Sync + Send {
|
||||||
{
|
{
|
||||||
"jsonrpc": "2.0",
|
"jsonrpc": "2.0",
|
||||||
"method": "validate_chain",
|
"method": "validate_chain",
|
||||||
"params": [],
|
"params": ["false"],
|
||||||
"id": 1
|
"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).
|
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())
|
Owner::get_status(self).map_err(|e| e.kind().clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn validate_chain(&self) -> Result<(), ErrorKind> {
|
fn validate_chain(&self, assume_valid_rangeproofs_kernels: bool) -> Result<(), ErrorKind> {
|
||||||
Owner::validate_chain(self).map_err(|e| e.kind().clone())
|
Owner::validate_chain(self, assume_valid_rangeproofs_kernels).map_err(|e| e.kind().clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reset_chain_head(&self, hash: String) -> Result<(), ErrorKind> {
|
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
|
// create temporary grin server, run jsonrpc request on node api, delete server, return
|
||||||
// json response.
|
// json response.
|
||||||
|
|
||||||
{
|
{
|
||||||
/*use grin_servers::test_framework::framework::run_doctest;
|
/*use grin_servers::test_framework::framework::run_doctest;
|
||||||
use grin_util as util;
|
use grin_util as util;
|
||||||
use serde_json;
|
use serde_json;
|
||||||
|
@ -437,6 +437,6 @@ macro_rules! doctest_helper_json_rpc_owner_assert_response {
|
||||||
serde_json::to_string_pretty(&expected_response).unwrap()
|
serde_json::to_string_pretty(&expected_response).unwrap()
|
||||||
);
|
);
|
||||||
}*/
|
}*/
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -262,7 +262,8 @@ fn create_coinbase(dest: &str, block_fees: &BlockFees) -> Result<CbData, Error>
|
||||||
|
|
||||||
trace!("Sending build_coinbase request: {}", req_body);
|
trace!("Sending build_coinbase request: {}", req_body);
|
||||||
let req = api::client::create_post_request(url.as_str(), None, &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!(
|
let report = format!(
|
||||||
"Failed to get coinbase from {}. Is the wallet listening? {}",
|
"Failed to get coinbase from {}. Is the wallet listening? {}",
|
||||||
dest, e
|
dest, e
|
||||||
|
|
|
@ -45,10 +45,19 @@ impl HTTPNodeClient {
|
||||||
method: &str,
|
method: &str,
|
||||||
params: &serde_json::Value,
|
params: &serde_json::Value,
|
||||||
) -> Result<D, Error> {
|
) -> Result<D, Error> {
|
||||||
|
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 url = format!("http://{}{}", self.node_url, ENDPOINT);
|
||||||
let req = build_request(method, params);
|
let req = build_request(method, params);
|
||||||
let res =
|
let res = client::post::<Request, Response>(
|
||||||
client::post::<Request, Response>(url.as_str(), self.node_api_secret.clone(), &req);
|
url.as_str(),
|
||||||
|
self.node_api_secret.clone(),
|
||||||
|
&req,
|
||||||
|
timeout,
|
||||||
|
);
|
||||||
|
|
||||||
match res {
|
match res {
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
@ -149,6 +158,27 @@ impl HTTPNodeClient {
|
||||||
e.reset().unwrap();
|
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) {
|
pub fn ban_peer(&self, peer_addr: &SocketAddr) {
|
||||||
let mut e = term::stdout().unwrap();
|
let mut e = term::stdout().unwrap();
|
||||||
let params = json!([peer_addr]);
|
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();
|
let hash = args.value_of("hash").unwrap();
|
||||||
node_client.invalidate_header(hash.to_string());
|
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)) => {
|
("ban", Some(peer_args)) => {
|
||||||
let peer = peer_args.value_of("peer").unwrap();
|
let peer = peer_args.value_of("peer").unwrap();
|
||||||
|
|
||||||
|
|
|
@ -78,9 +78,17 @@ subcommands:
|
||||||
- hash:
|
- hash:
|
||||||
help: The header hash to reset to
|
help: The header hash to reset to
|
||||||
required: true
|
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:
|
- invalidateheader:
|
||||||
about: Adds header hash to denylist
|
about: Adds header hash to denylist
|
||||||
args:
|
args:
|
||||||
- hash:
|
- hash:
|
||||||
help: The header hash to invalidate
|
help: The header hash to invalidate
|
||||||
required: true
|
required: true
|
||||||
|
|
Loading…
Reference in a new issue