From c7faf8508249db10c01c230ee7fee666108ff7b7 Mon Sep 17 00:00:00 2001 From: Karan Dhareshwar Date: Mon, 28 Jun 2021 16:01:37 -0500 Subject: [PATCH 01/12] NDRS-1608: Initial wire up of "query_global_state" --- client/lib/error.rs | 4 + client/lib/ffi.rs | 4 + client/lib/lib.rs | 46 ++++++ client/lib/rpc.rs | 80 ++++++++- client/lib/validation.rs | 75 ++++++++- client/src/common.rs | 80 +++++++++ client/src/global_state_query.rs | 111 +++++++++++++ client/src/main.rs | 8 +- client/src/query_state.rs | 93 +---------- node/src/components/rpc_server/http_server.rs | 3 + node/src/components/rpc_server/rpcs/docs.rs | 8 +- node/src/components/rpc_server/rpcs/state.rs | 153 +++++++++++++++++- node/src/types.rs | 5 +- node/src/types/block.rs | 15 +- resources/test/sse_data_schema.json | 1 + 15 files changed, 588 insertions(+), 98 deletions(-) create mode 100644 client/src/global_state_query.rs diff --git a/client/lib/error.rs b/client/lib/error.rs index 7d5843f7df..ff22680e99 100644 --- a/client/lib/error.rs +++ b/client/lib/error.rs @@ -123,6 +123,10 @@ pub enum Error { #[error("Invalid response: {0}")] InvalidResponse(#[from] ValidateResponseError), + /// Failed to identify the hash as either block hash or state root hash. + #[error("Failed to determine state identifier")] + FailedToParseStateIdentifier, + /// Must call FFI's setup function prior to making FFI calls. #[cfg(feature = "ffi")] #[error("Failed to call casper_setup_client()")] diff --git a/client/lib/ffi.rs b/client/lib/ffi.rs index acaa8080ba..b774d62480 100644 --- a/client/lib/ffi.rs +++ b/client/lib/ffi.rs @@ -53,6 +53,7 @@ pub enum casper_error_t { CASPER_FFI_PTR_NULL_BUT_REQUIRED = -22, CASPER_CONFLICTING_ARGUMENTS = -23, CASPER_DEPLOY_SIZE_TOO_LARGE = -24, + CASPER_FAILED_TO_PARSE_STATE_IDENTIFIER = -25, } trait AsFFIError { @@ -86,6 +87,9 @@ impl AsFFIError for Error { Error::FFIPtrNullButRequired(_) => casper_error_t::CASPER_FFI_PTR_NULL_BUT_REQUIRED, Error::ConflictingArguments { .. } => casper_error_t::CASPER_CONFLICTING_ARGUMENTS, Error::DeploySizeTooLarge(_) => casper_error_t::CASPER_DEPLOY_SIZE_TOO_LARGE, + Error::FailedToParseStateIdentifier => { + casper_error_t::CASPER_FAILED_TO_PARSE_STATE_IDENTIFIER + } } } } diff --git a/client/lib/lib.rs b/client/lib/lib.rs index a0160873b5..28faf4da30 100644 --- a/client/lib/lib.rs +++ b/client/lib/lib.rs @@ -490,6 +490,52 @@ pub fn get_account_info( .get_account_info(public_key, maybe_block_id) } +/// Retrieves information from global state using either a Block hash or a state root hash. +/// +/// * `maybe_rpc_id` is the JSON-RPC identifier, applied to the request and returned in the +/// response. If it can be parsed as an `i64` it will be used as a JSON integer. If empty, a +/// random `i64` will be assigned. Otherwise the provided string will be used verbatim. +/// * `node_address` is the hostname or IP and port of the node on which the HTTP service is +/// running, e.g. `"http://127.0.0.1:7777"`. +/// * When `verbosity_level` is `1`, the JSON-RPC request will be printed to `stdout` with long +/// string fields (e.g. hex-formatted raw Wasm bytes) shortened to a string indicating the char +/// count of the field. When `verbosity_level` is greater than `1`, the request will be printed +/// to `stdout` with no abbreviation of long fields. When `verbosity_level` is `0`, the request +/// will not be printed to `stdout`. +/// * `state_identifier` is a str value that identifies the supplied 32-byte hex encoded hash as as +/// either a `Block` hash or a specific `state_root_hash`. +/// * `hash` is a 32-byte hex encoded hash which can either be the block hash or state root hash and +/// is identified by the `state_identifier` arg. +/// * `state_root_hash` must be a hex-encoded, 32-byte hash digest. +/// * `key` must be a formatted [`PublicKey`](https://docs.rs/casper-node/latest/casper-node/crypto/asymmetric_key/enum.PublicKey.html) +/// or [`Key`](https://docs.rs/casper-types/latest/casper-types/enum.PublicKey.html). This will +/// take one of the following forms: +/// ```text +/// 01c9e33693951aaac23c49bee44ad6f863eedcd38c084a3a8f11237716a3df9c2c # PublicKey +/// account-hash-0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20 # Key::Account +/// hash-0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20 # Key::Hash +/// uref-0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20-007 # Key::URef +/// transfer-0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20 # Key::Transfer +/// deploy-0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20 # Key::DeployInfo +/// ``` +/// * `path` is comprised of components starting from the `key`, separated by `/`s. +pub fn global_state_query( + maybe_rpc_id: &str, + node_address: &str, + verbosity_level: u64, + state_identifier: &str, + hash: &str, + key: &str, + path: &str, +) -> Result { + RpcCall::new(maybe_rpc_id, node_address, verbosity_level).query_global_state( + state_identifier, + hash, + key, + path, + ) +} + /// Retrieves information and examples for all currently supported RPCs. /// /// * `maybe_rpc_id` is the JSON-RPC identifier, applied to the request and returned in the diff --git a/client/lib/rpc.rs b/client/lib/rpc.rs index 795942dc50..d2e42c0044 100644 --- a/client/lib/rpc.rs +++ b/client/lib/rpc.rs @@ -1,4 +1,4 @@ -use std::fs::File; +use std::{convert::TryFrom, fs::File}; use futures::executor; use jsonrpc_lite::{Id, JsonRpc, Params}; @@ -20,7 +20,8 @@ use casper_node::{ info::{GetDeploy, GetDeployParams}, state::{ GetAccountInfo, GetAccountInfoParams, GetAuctionInfo, GetAuctionInfoParams, GetBalance, - GetBalanceParams, GetItem, GetItemParams, + GetBalanceParams, GetItem, GetItemParams, GlobalStateIdentifier, QueryGlobalState, + QueryGlobalStateParams, }, RpcWithOptionalParams, RpcWithParams, RpcWithoutParams, RPC_API_PATH, }, @@ -40,6 +41,28 @@ pub(crate) enum TransferTarget { Account(PublicKey), } +/// An enum to identify whether the state identifier provided is a block hash or state root hash. +pub(crate) enum HashMarker { + /// The hash value is a block hash. + Block, + /// The hash provided is a state root hash. + StateRoot, +} + +impl TryFrom<&str> for HashMarker { + type Error = &'static str; + + fn try_from(marker: &str) -> std::result::Result { + if marker == "block" { + Ok(HashMarker::Block) + } else if marker == "state" { + Ok(HashMarker::StateRoot) + } else { + Err("Could not identify the marker") + } + } +} + /// Struct representing a single JSON-RPC call to the casper node. #[derive(Debug)] pub(crate) struct RpcCall { @@ -261,6 +284,42 @@ impl RpcCall { GetAccountInfo::request_with_map_params(self, params) } + pub(crate) fn query_global_state( + self, + state_identifier: &str, + hash: &str, + key: &str, + path: &str, + ) -> Result { + let global_state_identifier = Self::state_identifier(state_identifier, hash)?; + + let key = { + if let Ok(key) = Key::from_formatted_str(key) { + key + } else if let Ok(public_key) = PublicKey::from_hex(key) { + Key::Account(public_key.to_account_hash()) + } else { + return Err(Error::FailedToParseKey); + } + }; + + let path = if path.is_empty() { + vec![] + } else { + path.split('/').map(ToString::to_string).collect() + }; + + let params = QueryGlobalStateParams { + state_identifier: global_state_identifier.clone(), + key: key.to_formatted_string(), + path: path.clone(), + }; + + let response = QueryGlobalState::request_with_map_params(self, params)?; + validation::validate_global_state_query(&response, global_state_identifier, &key, &path)?; + Ok(response) + } + fn block_identifier(maybe_block_identifier: &str) -> Result> { if maybe_block_identifier.is_empty() { return Ok(None); @@ -281,6 +340,18 @@ impl RpcCall { } } + fn state_identifier(state_identifier: &str, hash: &str) -> Result { + let hash = Digest::from_hex(hash).map_err(|error| Error::CryptoError { + context: "state_identifier", + error, + })?; + match HashMarker::try_from(state_identifier) { + Ok(HashMarker::Block) => Ok(GlobalStateIdentifier::Block(BlockHash::new(hash))), + Ok(HashMarker::StateRoot) => Ok(GlobalStateIdentifier::StateRoot(hash)), + Err(_) => Err(Error::FailedToParseStateIdentifier), + } + } + async fn request(self, method: &str, params: Params) -> Result { let url = format!("{}/{}", self.node_address, RPC_API_PATH); let rpc_req = JsonRpc::request_with_params(self.rpc_id, method, params); @@ -389,6 +460,10 @@ impl RpcClient for GetAccountInfo { const RPC_METHOD: &'static str = Self::METHOD; } +impl RpcClient for QueryGlobalState { + const RPC_METHOD: &'static str = Self::METHOD; +} + pub(crate) trait IntoJsonMap: Serialize { fn into_json_map(self) -> Map where @@ -412,3 +487,4 @@ impl IntoJsonMap for GetEraInfoParams {} impl IntoJsonMap for ListRpcs {} impl IntoJsonMap for GetAuctionInfoParams {} impl IntoJsonMap for GetAccountInfoParams {} +impl IntoJsonMap for QueryGlobalStateParams {} diff --git a/client/lib/validation.rs b/client/lib/validation.rs index e36c76dbbb..311e67ab2f 100644 --- a/client/lib/validation.rs +++ b/client/lib/validation.rs @@ -9,14 +9,20 @@ use casper_execution_engine::{ }; use casper_node::{ crypto::hash::Digest, - rpcs::chain::{BlockIdentifier, EraSummary, GetEraInfoResult}, - types::{json_compatibility, Block, BlockValidationError, JsonBlock}, + rpcs::{ + chain::{BlockIdentifier, EraSummary, GetEraInfoResult}, + state::GlobalStateIdentifier, + }, + types::{ + json_compatibility, Block, BlockHeader, BlockValidationError, JsonBlock, JsonBlockHeader, + }, }; use casper_types::{bytesrepr, Key, U512}; const GET_ITEM_RESULT_BALANCE_VALUE: &str = "balance_value"; const GET_ITEM_RESULT_STORED_VALUE: &str = "stored_value"; const GET_ITEM_RESULT_MERKLE_PROOF: &str = "merkle_proof"; +const GLOBAL_STATE_QUERY_BLOCK_HEADER: &str = "block_header"; /// Error that can be returned when validating a block returned from a JSON-RPC method. #[derive(Error, Debug)] @@ -56,6 +62,10 @@ pub enum ValidateResponseError { /// Block height was not as requested. #[error("block height was not as requested")] UnexpectedBlockHeight, + + /// An invalid combination of state identifier and block header response + #[error("Invalid combination of State identifier and block header in response")] + InvalidGlobalStateResponse, } impl From for ValidateResponseError { @@ -174,6 +184,67 @@ pub(crate) fn validate_query_response( .map_err(Into::into) } +pub(crate) fn validate_global_state_query( + response: &JsonRpc, + state_identifier: GlobalStateIdentifier, + key: &Key, + path: &[String], +) -> Result<(), ValidateResponseError> { + let value = response + .get_result() + .ok_or(ValidateResponseError::ValidateResponseFailedToParse)?; + + let object = value + .as_object() + .ok_or(ValidateResponseError::ValidateResponseFailedToParse)?; + + let proofs: Vec> = { + let proof = object + .get(GET_ITEM_RESULT_MERKLE_PROOF) + .ok_or(ValidateResponseError::ValidateResponseFailedToParse)?; + let proof_str = proof + .as_str() + .ok_or(ValidateResponseError::ValidateResponseFailedToParse)?; + let proof_bytes = hex::decode(proof_str) + .map_err(|_| ValidateResponseError::ValidateResponseFailedToParse)?; + bytesrepr::deserialize(proof_bytes)? + }; + + let proof_value: &StoredValue = { + let last_proof = proofs + .last() + .ok_or(ValidateResponseError::ValidateResponseFailedToParse)?; + last_proof.value() + }; + + let json_block_header_value = object + .get(GLOBAL_STATE_QUERY_BLOCK_HEADER) + .ok_or(ValidateResponseError::ValidateResponseFailedToParse)?; + let maybe_json_block_header: Option = + serde_json::from_value(json_block_header_value.to_owned())?; + + let state_root_hash = match (state_identifier, maybe_json_block_header) { + (GlobalStateIdentifier::Block(_), None) + | (GlobalStateIdentifier::StateRoot(_), Some(_)) => { + return Err(ValidateResponseError::InvalidGlobalStateResponse); + } + (GlobalStateIdentifier::Block(_), Some(json_header)) => { + let block_header = BlockHeader::from(json_header); + *block_header.state_root_hash() + } + (GlobalStateIdentifier::StateRoot(hash), None) => hash, + }; + + core::validate_query_proof( + &state_root_hash.to_owned().into(), + &proofs, + key, + path, + proof_value, + ) + .map_err(Into::into) +} + pub(crate) fn validate_get_balance_response( response: &JsonRpc, state_root_hash: &Digest, diff --git a/client/src/common.rs b/client/src/common.rs index ac4b29cf00..b6a3188642 100644 --- a/client/src/common.rs +++ b/client/src/common.rs @@ -246,3 +246,83 @@ pub mod public_key { Ok(value.to_string()) } } + +/// Handles providing the arg for and retrieval of the key. +pub mod key { + use casper_node::crypto::AsymmetricKeyExt; + use casper_types::AsymmetricType; + + use super::*; + + const ARG_NAME: &str = "key"; + const ARG_SHORT: &str = "k"; + const ARG_VALUE_NAME: &str = "FORMATTED STRING or PATH"; + const ARG_HELP: &str = + "The base key for the query. This must be a properly formatted public key, account hash, \ + contract address hash, URef, transfer hash or deploy-info hash. The format for each \ + respectively is \"\", \"account-hash-\", \"hash-\", \ + \"uref--\", \"transfer-\" and \ + \"deploy-\". The public key may instead be read in from a file, in which case \ + enter the path to the file as the --key argument. The file should be one of the two public \ + key files generated via the `keygen` subcommand; \"public_key_hex\" or \"public_key.pem\""; + + pub(crate) fn arg(order: usize) -> Arg<'static, 'static> { + Arg::with_name(ARG_NAME) + .long(ARG_NAME) + .short(ARG_SHORT) + .required(true) + .value_name(ARG_VALUE_NAME) + .help(ARG_HELP) + .display_order(order) + } + + pub(crate) fn get(matches: &ArgMatches) -> Result { + let value = matches + .value_of(ARG_NAME) + .unwrap_or_else(|| panic!("should have {} arg", ARG_NAME)); + + // Try to read as a PublicKey PEM file first. + if let Ok(public_key) = PublicKey::from_file(value) { + return Ok(public_key.to_hex()); + } + + // Try to read as a hex-encoded PublicKey file next. + if let Ok(hex_public_key) = fs::read_to_string(value) { + let _ = PublicKey::from_hex(&hex_public_key).map_err(|error| { + eprintln!( + "Can't parse the contents of {} as a public key: {}", + value, error + ); + Error::FailedToParseKey + })?; + return Ok(hex_public_key); + } + + // Just return the value. + Ok(value.to_string()) + } +} + +/// Handles providing the arg for and retrieval of the key. +pub mod path { + use super::*; + + const ARG_NAME: &str = "query-path"; + const ARG_SHORT: &str = "q"; + const ARG_VALUE_NAME: &str = "PATH/FROM/KEY"; + const ARG_HELP: &str = "The path from the key of the query"; + + pub(crate) fn arg(order: usize) -> Arg<'static, 'static> { + Arg::with_name(ARG_NAME) + .long(ARG_NAME) + .short(ARG_SHORT) + .required(false) + .value_name(ARG_VALUE_NAME) + .help(ARG_HELP) + .display_order(order) + } + + pub(crate) fn get<'a>(matches: &'a ArgMatches) -> &'a str { + matches.value_of(ARG_NAME).unwrap_or_default() + } +} diff --git a/client/src/global_state_query.rs b/client/src/global_state_query.rs new file mode 100644 index 0000000000..ba8488d450 --- /dev/null +++ b/client/src/global_state_query.rs @@ -0,0 +1,111 @@ +use std::str; + +use clap::{App, Arg, ArgMatches, SubCommand}; + +use casper_client::Error; +use casper_node::rpcs::state::QueryGlobalState; + +use crate::{command::ClientCommand, common, Success}; + +/// This struct defines the order in which the args are shown for this subcommand's help message. +enum DisplayOrder { + Verbose, + NodeAddress, + RpcId, + StateIdentifier, + Hash, + Key, + Path, +} + +mod state_identifier { + use super::*; + + const ARG_NAME: &str = "identifier"; + const ARG_VALUE_NAME: &str = "STRING"; + const ARG_HELP: &str = + "Identifies the hash value provided as either a block hash or state root hash. You must provide either a block hash or a state root hash"; + const BLOCK: &str = "block"; + const STATE_ROOT: &str = "state"; + + pub(super) fn arg() -> Arg<'static, 'static> { + Arg::with_name(ARG_NAME) + .required(true) + .possible_value(BLOCK) + .possible_value(STATE_ROOT) + .value_name(ARG_VALUE_NAME) + .help(ARG_HELP) + .display_order(DisplayOrder::StateIdentifier as usize) + } + + pub(super) fn get<'a>(matches: &'a ArgMatches) -> &'a str { + matches + .value_of(ARG_NAME) + .unwrap_or_else(|| panic!("should have {} arg", ARG_NAME)) + } +} + +/// Handles providing the arg for and retrieval of either the block hash or state root hash. +mod hash { + use super::*; + + const ARG_NAME: &str = "hash"; + const ARG_VALUE_NAME: &str = "HEX STRING"; + const ARG_HELP: &str = "Hex-encoded block hash or state root hash"; + + pub(super) fn arg() -> Arg<'static, 'static> { + Arg::with_name(ARG_NAME) + .required(true) + .value_name(ARG_VALUE_NAME) + .help(ARG_HELP) + .display_order(DisplayOrder::Hash as usize) + } + + pub(super) fn get<'a>(matches: &'a ArgMatches) -> &'a str { + matches + .value_of(ARG_NAME) + .unwrap_or_else(|| panic!("should have {} arg", ARG_NAME)) + } +} + +impl<'a, 'b> ClientCommand<'a, 'b> for QueryGlobalState { + const NAME: &'static str = "global-state-query"; + const ABOUT: &'static str = + "Retrieves a stored value from the network using either the state root hash or block hash"; + + fn build(display_order: usize) -> App<'a, 'b> { + SubCommand::with_name(Self::NAME) + .about(Self::ABOUT) + .display_order(display_order) + .arg(common::verbose::arg(DisplayOrder::Verbose as usize)) + .arg(common::node_address::arg( + DisplayOrder::NodeAddress as usize, + )) + .arg(common::rpc_id::arg(DisplayOrder::RpcId as usize)) + .arg(state_identifier::arg()) + .arg(hash::arg()) + .arg(common::key::arg(DisplayOrder::Key as usize)) + .arg(common::path::arg(DisplayOrder::Path as usize)) + } + + fn run(matches: &ArgMatches<'_>) -> Result { + let maybe_rpc_id = common::rpc_id::get(matches); + let node_address = common::node_address::get(matches); + let verbosity_level = common::verbose::get(matches); + let state_identifier = state_identifier::get(matches); + let hash = hash::get(matches); + let key = common::key::get(matches)?; + let path = common::path::get(matches); + + casper_client::global_state_query( + maybe_rpc_id, + node_address, + verbosity_level, + state_identifier, + hash, + &key, + path, + ) + .map(Success::from) + } +} diff --git a/client/src/main.rs b/client/src/main.rs index 6a2413bbd1..e182b7869a 100644 --- a/client/src/main.rs +++ b/client/src/main.rs @@ -10,6 +10,7 @@ mod get_auction_info; mod get_balance; mod get_era_info_by_switch_block; mod get_state_hash; +mod global_state_query; mod keygen; mod query_state; @@ -23,7 +24,7 @@ use casper_node::rpcs::{ chain::{GetBlock, GetBlockTransfers, GetEraInfoBySwitchBlock, GetStateRootHash}, docs::ListRpcs, info::GetDeploy, - state::{GetAccountInfo, GetAuctionInfo, GetBalance, GetItem as QueryState}, + state::{GetAccountInfo, GetAuctionInfo, GetBalance, GetItem as QueryState, QueryGlobalState}, }; use account_address::GenerateAccountHash as AccountAddress; @@ -56,6 +57,7 @@ enum DisplayOrder { GenerateCompletion, GetRpcs, AccountAddress, + QueryGlobalState, } fn cli<'a, 'b>() -> App<'a, 'b> { @@ -90,6 +92,9 @@ fn cli<'a, 'b>() -> App<'a, 'b> { )) .subcommand(ListRpcs::build(DisplayOrder::GetRpcs as usize)) .subcommand(AccountAddress::build(DisplayOrder::AccountAddress as usize)) + .subcommand(QueryGlobalState::build( + DisplayOrder::QueryGlobalState as usize, + )) } #[tokio::main] @@ -118,6 +123,7 @@ async fn main() { (GenerateCompletion::NAME, Some(matches)) => (GenerateCompletion::run(matches), matches), (ListRpcs::NAME, Some(matches)) => (ListRpcs::run(matches), matches), (AccountAddress::NAME, Some(matches)) => (AccountAddress::run(matches), matches), + (QueryGlobalState::NAME, Some(matches)) => (QueryGlobalState::run(matches), matches), _ => { let _ = cli().print_long_help(); println!(); diff --git a/client/src/query_state.rs b/client/src/query_state.rs index f96e2172b9..b0f1d8a22d 100644 --- a/client/src/query_state.rs +++ b/client/src/query_state.rs @@ -1,10 +1,9 @@ -use std::{fs, str}; +use std::str; -use clap::{App, Arg, ArgMatches, SubCommand}; +use clap::{App, ArgMatches, SubCommand}; use casper_client::Error; use casper_node::rpcs::state::GetItem; -use casper_types::PublicKey; use crate::{command::ClientCommand, common, Success}; @@ -18,86 +17,6 @@ enum DisplayOrder { Path, } -/// Handles providing the arg for and retrieval of the key. -mod key { - use casper_node::crypto::AsymmetricKeyExt; - use casper_types::AsymmetricType; - - use super::*; - - const ARG_NAME: &str = "key"; - const ARG_SHORT: &str = "k"; - const ARG_VALUE_NAME: &str = "FORMATTED STRING or PATH"; - const ARG_HELP: &str = - "The base key for the query. This must be a properly formatted public key, account hash, \ - contract address hash, URef, transfer hash or deploy-info hash. The format for each \ - respectively is \"\", \"account-hash-\", \"hash-\", \ - \"uref--\", \"transfer-\" and \ - \"deploy-\". The public key may instead be read in from a file, in which case \ - enter the path to the file as the --key argument. The file should be one of the two public \ - key files generated via the `keygen` subcommand; \"public_key_hex\" or \"public_key.pem\""; - - pub(super) fn arg() -> Arg<'static, 'static> { - Arg::with_name(ARG_NAME) - .long(ARG_NAME) - .short(ARG_SHORT) - .required(true) - .value_name(ARG_VALUE_NAME) - .help(ARG_HELP) - .display_order(DisplayOrder::Key as usize) - } - - pub(super) fn get(matches: &ArgMatches) -> Result { - let value = matches - .value_of(ARG_NAME) - .unwrap_or_else(|| panic!("should have {} arg", ARG_NAME)); - - // Try to read as a PublicKey PEM file first. - if let Ok(public_key) = PublicKey::from_file(value) { - return Ok(public_key.to_hex()); - } - - // Try to read as a hex-encoded PublicKey file next. - if let Ok(hex_public_key) = fs::read_to_string(value) { - let _ = PublicKey::from_hex(&hex_public_key).map_err(|error| { - eprintln!( - "Can't parse the contents of {} as a public key: {}", - value, error - ); - Error::FailedToParseKey - })?; - return Ok(hex_public_key); - } - - // Just return the value. - Ok(value.to_string()) - } -} - -/// Handles providing the arg for and retrieval of the key. -mod path { - use super::*; - - const ARG_NAME: &str = "query-path"; - const ARG_SHORT: &str = "q"; - const ARG_VALUE_NAME: &str = "PATH/FROM/KEY"; - const ARG_HELP: &str = "The path from the key of the query"; - - pub(super) fn arg() -> Arg<'static, 'static> { - Arg::with_name(ARG_NAME) - .long(ARG_NAME) - .short(ARG_SHORT) - .required(false) - .value_name(ARG_VALUE_NAME) - .help(ARG_HELP) - .display_order(DisplayOrder::Path as usize) - } - - pub(super) fn get<'a>(matches: &'a ArgMatches) -> &'a str { - matches.value_of(ARG_NAME).unwrap_or_default() - } -} - impl<'a, 'b> ClientCommand<'a, 'b> for GetItem { const NAME: &'static str = "query-state"; const ABOUT: &'static str = "Retrieves a stored value from the network"; @@ -114,8 +33,8 @@ impl<'a, 'b> ClientCommand<'a, 'b> for GetItem { .arg(common::state_root_hash::arg( DisplayOrder::StateRootHash as usize, )) - .arg(key::arg()) - .arg(path::arg()) + .arg(common::key::arg(DisplayOrder::Key as usize)) + .arg(common::path::arg(DisplayOrder::Path as usize)) } fn run(matches: &ArgMatches<'_>) -> Result { @@ -123,8 +42,8 @@ impl<'a, 'b> ClientCommand<'a, 'b> for GetItem { let node_address = common::node_address::get(matches); let verbosity_level = common::verbose::get(matches); let state_root_hash = common::state_root_hash::get(matches); - let key = key::get(matches)?; - let path = path::get(matches); + let key = common::key::get(matches)?; + let path = common::path::get(matches); casper_client::get_item( maybe_rpc_id, diff --git a/node/src/components/rpc_server/http_server.rs b/node/src/components/rpc_server/http_server.rs index fac585427e..ec55db2a1f 100644 --- a/node/src/components/rpc_server/http_server.rs +++ b/node/src/components/rpc_server/http_server.rs @@ -59,6 +59,8 @@ pub(super) async fn run( let rpc_get_state_root_hash = rpcs::chain::GetStateRootHash::create_filter(effect_builder, api_version); let rpc_get_item = rpcs::state::GetItem::create_filter(effect_builder, api_version); + let rpc_query_global_state = + rpcs::state::QueryGlobalState::create_filter(effect_builder, api_version); let rpc_get_balance = rpcs::state::GetBalance::create_filter(effect_builder, api_version); let rpc_get_account_info = rpcs::state::GetAccountInfo::create_filter(effect_builder, api_version); @@ -95,6 +97,7 @@ pub(super) async fn run( .or(rpc_get_block_transfers) .or(rpc_get_state_root_hash) .or(rpc_get_item) + .or(rpc_query_global_state) .or(rpc_get_balance) .or(rpc_get_deploy) .or(rpc_get_peers) diff --git a/node/src/components/rpc_server/rpcs/docs.rs b/node/src/components/rpc_server/rpcs/docs.rs index db9f1ca675..3e2e32bb09 100644 --- a/node/src/components/rpc_server/rpcs/docs.rs +++ b/node/src/components/rpc_server/rpcs/docs.rs @@ -28,7 +28,10 @@ use super::{ }; use crate::{ effect::EffectBuilder, - rpcs::{chain::GetEraInfoBySwitchBlock, state::GetAccountInfo}, + rpcs::{ + chain::GetEraInfoBySwitchBlock, + state::{GetAccountInfo, QueryGlobalState}, + }, }; pub(crate) const DOCS_EXAMPLE_PROTOCOL_VERSION: ProtocolVersion = @@ -73,6 +76,9 @@ pub(crate) static OPEN_RPC_SCHEMA: Lazy = Lazy::new(|| { schema.push_with_params::("receives a Deploy to be executed by the network"); schema.push_with_params::("returns a Deploy from the network"); schema.push_with_params::("returns an Account from the network"); + schema.push_with_params::( + "a query to global state using either a Block hash or state root hash", + ); schema.push_without_params::("returns a list of peers connected to the node"); schema.push_without_params::("returns the current status of the node"); schema.push_with_optional_params::("returns a Block from the network"); diff --git a/node/src/components/rpc_server/rpcs/state.rs b/node/src/components/rpc_server/rpcs/state.rs index d59eea9ee9..9b4ea43bb5 100644 --- a/node/src/components/rpc_server/rpcs/state.rs +++ b/node/src/components/rpc_server/rpcs/state.rs @@ -35,7 +35,7 @@ use crate::{ }, types::{ json_compatibility::{Account, AuctionState, StoredValue}, - Block, + Block, BlockHash, JsonBlockHeader, }, }; @@ -79,6 +79,19 @@ static GET_ACCOUNT_INFO_RESULT: Lazy = Lazy::new(|| GetAcc account: Account::doc_example().clone(), merkle_proof: MERKLE_PROOF.clone(), }); +static QUERY_GLOBAL_STATE_PARAMS: Lazy = + Lazy::new(|| QueryGlobalStateParams { + state_identifier: GlobalStateIdentifier::Block(*Block::doc_example().hash()), + key: "deploy-af684263911154d26fa05be9963171802801a0b6aff8f199b7391eacb8edc9e1".to_string(), + path: vec![], + }); +static QUERY_GLOBAL_STATE_RESULT: Lazy = + Lazy::new(|| QueryGlobalStateResult { + api_version: DOCS_EXAMPLE_PROTOCOL_VERSION, + block_header: Some(JsonBlockHeader::doc_example().clone()), + stored_value: StoredValue::Account(Account::doc_example().clone()), + merkle_proof: MERKLE_PROOF.clone(), + }); /// Params for "state_get_item" RPC request. #[derive(Serialize, Deserialize, Debug, JsonSchema)] @@ -558,3 +571,141 @@ impl RpcWithParamsExt for GetAccountInfo { .boxed() } } + +/// Identifier for possible ways to query Global State +#[derive(Serialize, Deserialize, Debug, JsonSchema, Clone)] +#[serde(deny_unknown_fields)] +pub enum GlobalStateIdentifier { + /// Query using a block hash. + Block(BlockHash), + /// Query using the state root hash. + StateRoot(Digest), +} + +/// Params for "query_global_state" RPC +#[derive(Serialize, Deserialize, Debug, JsonSchema)] +#[serde(deny_unknown_fields)] +pub struct QueryGlobalStateParams { + /// The identifier used for the query. + pub state_identifier: GlobalStateIdentifier, + /// `casper_types::Key` as formatted string. + pub key: String, + /// The path components starting from the key as base. + #[serde(default)] + pub path: Vec, +} + +impl DocExample for QueryGlobalStateParams { + fn doc_example() -> &'static Self { + &*QUERY_GLOBAL_STATE_PARAMS + } +} + +/// Result for "query_global_state" RPC response. +#[derive(Serialize, Deserialize, Debug, JsonSchema)] +#[serde(deny_unknown_fields)] +pub struct QueryGlobalStateResult { + /// The RPC API version. + #[schemars(with = "String")] + pub api_version: ProtocolVersion, + /// The block header if a Block hash was provided. + pub block_header: Option, + /// The stored value. + pub stored_value: StoredValue, + /// The merkle proof. + pub merkle_proof: String, +} + +impl DocExample for QueryGlobalStateResult { + fn doc_example() -> &'static Self { + &*QUERY_GLOBAL_STATE_RESULT + } +} + +/// "query_global_state" RPC +pub struct QueryGlobalState {} + +impl RpcWithParams for QueryGlobalState { + const METHOD: &'static str = "query_global_state"; + type RequestParams = QueryGlobalStateParams; + type ResponseResult = QueryGlobalStateResult; +} + +impl RpcWithParamsExt for QueryGlobalState { + fn handle_request( + effect_builder: EffectBuilder, + response_builder: Builder, + params: Self::RequestParams, + api_version: ProtocolVersion, + ) -> BoxFuture<'static, Result, Error>> { + async move { + let (state_root_hash, maybe_block_header) = match params.state_identifier { + GlobalStateIdentifier::Block(block_hash) => { + match effect_builder + .get_block_header_from_storage(block_hash) + .await + { + Some(header) => { + let json_block_header = JsonBlockHeader::from(header.clone()); + (*header.state_root_hash(), Some(json_block_header)) + } + None => { + let error_msg = + "query_global_state failed to retrieve specified block header" + .to_string(); + return Ok(response_builder.error(warp_json_rpc::Error::custom( + ErrorCode::NoSuchBlock as i64, + error_msg, + ))?); + } + } + } + GlobalStateIdentifier::StateRoot(state_root_hash) => (state_root_hash, None), + }; + + let base_key = match Key::from_formatted_str(¶ms.key) + .map_err(|error| format!("failed to parse key: {}", error)) + { + Ok(key) => key, + Err(error_msg) => { + info!("{}", error_msg); + return Ok(response_builder.error(warp_json_rpc::Error::custom( + ErrorCode::ParseQueryKey as i64, + error_msg, + ))?); + } + }; + + let query_result = effect_builder + .make_request( + |responder| RpcRequest::QueryGlobalState { + state_root_hash, + base_key, + path: params.path, + responder, + }, + QueueKind::Api, + ) + .await; + + let (stored_value, proof_bytes) = match common::extract_query_result(query_result) { + Ok(tuple) => tuple, + Err((error_code, error_msg)) => { + info!("{}", error_msg); + return Ok(response_builder + .error(warp_json_rpc::Error::custom(error_code as i64, error_msg))?); + } + }; + + let result = Self::ResponseResult { + api_version, + block_header: maybe_block_header, + stored_value, + merkle_proof: hex::encode(proof_bytes), + }; + + Ok(response_builder.success(result)?) + } + .boxed() + } +} diff --git a/node/src/types.rs b/node/src/types.rs index 7933d2c99c..2fb686cf94 100644 --- a/node/src/types.rs +++ b/node/src/types.rs @@ -19,8 +19,9 @@ use rand::{CryptoRng, RngCore}; use rand_chacha::ChaCha20Rng; pub use block::{ - json_compatibility::JsonBlock, Block, BlockBody, BlockHash, BlockHeader, BlockSignatures, - BlockValidationError, FinalitySignature, + json_compatibility::{JsonBlock, JsonBlockHeader}, + Block, BlockBody, BlockHash, BlockHeader, BlockSignatures, BlockValidationError, + FinalitySignature, }; pub(crate) use block::{BlockByHeight, BlockHeaderWithMetadata, BlockPayload, FinalizedBlock}; pub(crate) use chainspec::ActivationPoint; diff --git a/node/src/types/block.rs b/node/src/types/block.rs index 50b920f25f..a587b686bb 100644 --- a/node/src/types/block.rs +++ b/node/src/types/block.rs @@ -46,7 +46,7 @@ use crate::{ AsymmetricKeyExt, }, rpcs::docs::DocExample, - types::{Deploy, DeployHash, DeployOrTransferHash, JsonBlock}, + types::{Deploy, DeployHash, DeployOrTransferHash, JsonBlock, JsonBlockHeader}, utils::DisplayIter, }; @@ -162,6 +162,10 @@ static JSON_BLOCK: Lazy = Lazy::new(|| { JsonBlock::new(block, Some(block_signature)) }); +static JSON_BLOCK_HEADER: Lazy = Lazy::new(|| { + let block_header = Block::doc_example().header().clone(); + JsonBlockHeader::from(block_header) +}); /// Error returned from constructing or validating a `Block`. #[derive(Debug, Error)] @@ -1418,9 +1422,10 @@ pub(crate) mod json_compatibility { } } + /// JSON representation of a block header. #[derive(Serialize, Deserialize, Debug, JsonSchema, Clone, PartialEq, Eq, DataSize)] #[serde(deny_unknown_fields)] - struct JsonBlockHeader { + pub struct JsonBlockHeader { parent_hash: BlockHash, state_root_hash: Digest, body_hash: Digest, @@ -1467,6 +1472,12 @@ pub(crate) mod json_compatibility { } } + impl DocExample for JsonBlockHeader { + fn doc_example() -> &'static Self { + &*JSON_BLOCK_HEADER + } + } + /// A JSON-friendly representation of `Body` #[derive(Serialize, Deserialize, Debug, JsonSchema, Clone, PartialEq, Eq, DataSize)] #[serde(deny_unknown_fields)] diff --git a/resources/test/sse_data_schema.json b/resources/test/sse_data_schema.json index ce649f08fe..94118c009b 100644 --- a/resources/test/sse_data_schema.json +++ b/resources/test/sse_data_schema.json @@ -202,6 +202,7 @@ "additionalProperties": false }, "JsonBlockHeader": { + "description": "JSON representation of a block header.", "type": "object", "required": [ "accumulated_seed", From 100a87d1a7c867f5d1b47dc725c080e15bf3e24e Mon Sep 17 00:00:00 2001 From: Karan Dhareshwar Date: Thu, 1 Jul 2021 11:18:06 -0500 Subject: [PATCH 02/12] NDRS-1608: Modify the client command arg --- client/lib/lib.rs | 36 +++++++++++-- client/lib/rpc.rs | 44 ++-------------- client/src/global_state_query.rs | 90 +++++++++++++++++++------------- 3 files changed, 90 insertions(+), 80 deletions(-) diff --git a/client/lib/lib.rs b/client/lib/lib.rs index 28faf4da30..8dcad51e29 100644 --- a/client/lib/lib.rs +++ b/client/lib/lib.rs @@ -28,9 +28,10 @@ use jsonrpc_lite::JsonRpc; use serde::Serialize; use casper_execution_engine::core::engine_state::ExecutableDeployItem; -use casper_node::types::Deploy; +use casper_node::types::{BlockHash, Deploy}; use casper_types::{UIntParseError, U512}; +use casper_node::{crypto::hash::Digest, rpcs::state::GlobalStateIdentifier}; pub use cl_type::help; pub use deploy::ListDeploysResult; use deploy::{DeployExt, DeployParams}; @@ -523,14 +524,12 @@ pub fn global_state_query( maybe_rpc_id: &str, node_address: &str, verbosity_level: u64, - state_identifier: &str, - hash: &str, + global_state_str_params: GlobalStateStrParams<'_>, key: &str, path: &str, ) -> Result { RpcCall::new(maybe_rpc_id, node_address, verbosity_level).query_global_state( - state_identifier, - hash, + global_state_str_params, key, path, ) @@ -1041,6 +1040,33 @@ impl<'a> SessionStrParams<'a> { } } +/// Container for `GlobalStateIdentifier` construction options. +#[derive(Default, Debug)] +pub struct GlobalStateStrParams<'a> { + /// Identifier to mark the hash as either a Block hash or `state_root_hash` + /// When true, the hash provided is a Block hash. + pub is_block_hash: bool, + /// The hash value. + pub hash_value: &'a str, +} + +impl<'a> TryInto for GlobalStateStrParams<'a> { + type Error = Error; + + fn try_into(self) -> Result { + let hash = Digest::from_hex(self.hash_value).map_err(|error| Error::CryptoError { + context: "block_identifier", + error, + })?; + + if self.is_block_hash { + Ok(GlobalStateIdentifier::Block(BlockHash::new(hash))) + } else { + Ok(GlobalStateIdentifier::StateRoot(hash)) + } + } +} + /// When `verbosity_level` is `1`, the value will be printed to `stdout` with long string fields /// (e.g. hex-formatted raw Wasm bytes) shortened to a string indicating the char count of the /// field. When `verbosity_level` is greater than `1`, the value will be printed to `stdout` with diff --git a/client/lib/rpc.rs b/client/lib/rpc.rs index d2e42c0044..246837378d 100644 --- a/client/lib/rpc.rs +++ b/client/lib/rpc.rs @@ -1,4 +1,4 @@ -use std::{convert::TryFrom, fs::File}; +use std::fs::File; use futures::executor; use jsonrpc_lite::{Id, JsonRpc, Params}; @@ -32,8 +32,9 @@ use casper_types::{AsymmetricType, Key, PublicKey, URef, U512}; use crate::{ deploy::{DeployExt, DeployParams, SendDeploy, Transfer}, error::{Error, Result}, - validation, + validation, GlobalStateStrParams, }; +use std::convert::TryInto; /// Target for a given transfer. pub(crate) enum TransferTarget { @@ -41,28 +42,6 @@ pub(crate) enum TransferTarget { Account(PublicKey), } -/// An enum to identify whether the state identifier provided is a block hash or state root hash. -pub(crate) enum HashMarker { - /// The hash value is a block hash. - Block, - /// The hash provided is a state root hash. - StateRoot, -} - -impl TryFrom<&str> for HashMarker { - type Error = &'static str; - - fn try_from(marker: &str) -> std::result::Result { - if marker == "block" { - Ok(HashMarker::Block) - } else if marker == "state" { - Ok(HashMarker::StateRoot) - } else { - Err("Could not identify the marker") - } - } -} - /// Struct representing a single JSON-RPC call to the casper node. #[derive(Debug)] pub(crate) struct RpcCall { @@ -286,12 +265,11 @@ impl RpcCall { pub(crate) fn query_global_state( self, - state_identifier: &str, - hash: &str, + global_state_str_params: GlobalStateStrParams<'_>, key: &str, path: &str, ) -> Result { - let global_state_identifier = Self::state_identifier(state_identifier, hash)?; + let global_state_identifier: GlobalStateIdentifier = global_state_str_params.try_into()?; let key = { if let Ok(key) = Key::from_formatted_str(key) { @@ -340,18 +318,6 @@ impl RpcCall { } } - fn state_identifier(state_identifier: &str, hash: &str) -> Result { - let hash = Digest::from_hex(hash).map_err(|error| Error::CryptoError { - context: "state_identifier", - error, - })?; - match HashMarker::try_from(state_identifier) { - Ok(HashMarker::Block) => Ok(GlobalStateIdentifier::Block(BlockHash::new(hash))), - Ok(HashMarker::StateRoot) => Ok(GlobalStateIdentifier::StateRoot(hash)), - Err(_) => Err(Error::FailedToParseStateIdentifier), - } - } - async fn request(self, method: &str, params: Params) -> Result { let url = format!("{}/{}", self.node_address, RPC_API_PATH); let rpc_req = JsonRpc::request_with_params(self.rpc_id, method, params); diff --git a/client/src/global_state_query.rs b/client/src/global_state_query.rs index ba8488d450..dd1c40fa42 100644 --- a/client/src/global_state_query.rs +++ b/client/src/global_state_query.rs @@ -1,71 +1,85 @@ use std::str; -use clap::{App, Arg, ArgMatches, SubCommand}; +use clap::{App, Arg, ArgGroup, ArgMatches, SubCommand}; -use casper_client::Error; +use casper_client::{Error, GlobalStateStrParams}; use casper_node::rpcs::state::QueryGlobalState; use crate::{command::ClientCommand, common, Success}; +const ARG_HEX_STRING: &str = "HEX STRING"; + /// This struct defines the order in which the args are shown for this subcommand's help message. enum DisplayOrder { Verbose, NodeAddress, RpcId, - StateIdentifier, - Hash, + BlockHash, + StateRootHash, Key, Path, } -mod state_identifier { +mod state_root_hash { use super::*; - const ARG_NAME: &str = "identifier"; - const ARG_VALUE_NAME: &str = "STRING"; - const ARG_HELP: &str = - "Identifies the hash value provided as either a block hash or state root hash. You must provide either a block hash or a state root hash"; - const BLOCK: &str = "block"; - const STATE_ROOT: &str = "state"; + pub(super) const ARG_NAME: &str = "state-root-hash"; + const ARG_SHORT: &str = "s"; + const ARG_VALUE_NAME: &str = ARG_HEX_STRING; + const ARG_HELP: &str = "Hex-encoded hash of the state root"; pub(super) fn arg() -> Arg<'static, 'static> { Arg::with_name(ARG_NAME) - .required(true) - .possible_value(BLOCK) - .possible_value(STATE_ROOT) + .long(ARG_NAME) + .short(ARG_SHORT) + .required(false) .value_name(ARG_VALUE_NAME) .help(ARG_HELP) - .display_order(DisplayOrder::StateIdentifier as usize) + .display_order(DisplayOrder::StateRootHash as usize) } - pub(super) fn get<'a>(matches: &'a ArgMatches) -> &'a str { - matches - .value_of(ARG_NAME) - .unwrap_or_else(|| panic!("should have {} arg", ARG_NAME)) + pub fn get<'a>(matches: &'a ArgMatches) -> Option<&'a str> { + matches.value_of(ARG_NAME) } } -/// Handles providing the arg for and retrieval of either the block hash or state root hash. -mod hash { +mod block_hash { use super::*; - const ARG_NAME: &str = "hash"; - const ARG_VALUE_NAME: &str = "HEX STRING"; - const ARG_HELP: &str = "Hex-encoded block hash or state root hash"; + pub(super) const ARG_NAME: &str = "block-hash"; + const ARG_SHORT: &str = "b"; + const ARG_VALUE_NAME: &str = ARG_HEX_STRING; + const ARG_HELP: &str = "Hex-encoded hash of the block"; pub(super) fn arg() -> Arg<'static, 'static> { Arg::with_name(ARG_NAME) - .required(true) + .long(ARG_NAME) + .short(ARG_SHORT) + .required(false) .value_name(ARG_VALUE_NAME) .help(ARG_HELP) - .display_order(DisplayOrder::Hash as usize) + .display_order(DisplayOrder::BlockHash as usize) } - pub(super) fn get<'a>(matches: &'a ArgMatches) -> &'a str { - matches - .value_of(ARG_NAME) - .unwrap_or_else(|| panic!("should have {} arg", ARG_NAME)) + pub fn get<'a>(matches: &'a ArgMatches) -> Option<&'a str> { + matches.value_of(ARG_NAME) + } +} + +fn global_state_str_params<'a>(matches: &'a ArgMatches) -> GlobalStateStrParams<'a> { + if let Some(state_root_hash) = state_root_hash::get(matches) { + return GlobalStateStrParams { + is_block_hash: false, + hash_value: state_root_hash, + }; + } + if let Some(block_hash) = block_hash::get(matches) { + return GlobalStateStrParams { + is_block_hash: true, + hash_value: block_hash, + }; } + unreachable!("clap arg groups and parsing should prevent this") } impl<'a, 'b> ClientCommand<'a, 'b> for QueryGlobalState { @@ -82,18 +96,23 @@ impl<'a, 'b> ClientCommand<'a, 'b> for QueryGlobalState { DisplayOrder::NodeAddress as usize, )) .arg(common::rpc_id::arg(DisplayOrder::RpcId as usize)) - .arg(state_identifier::arg()) - .arg(hash::arg()) .arg(common::key::arg(DisplayOrder::Key as usize)) .arg(common::path::arg(DisplayOrder::Path as usize)) + .arg(block_hash::arg()) + .arg(state_root_hash::arg()) + .group( + ArgGroup::with_name("state-identifier") + .arg(state_root_hash::ARG_NAME) + .arg(block_hash::ARG_NAME) + .required(true), + ) } fn run(matches: &ArgMatches<'_>) -> Result { let maybe_rpc_id = common::rpc_id::get(matches); let node_address = common::node_address::get(matches); let verbosity_level = common::verbose::get(matches); - let state_identifier = state_identifier::get(matches); - let hash = hash::get(matches); + let global_state_str_params = global_state_str_params(matches); let key = common::key::get(matches)?; let path = common::path::get(matches); @@ -101,8 +120,7 @@ impl<'a, 'b> ClientCommand<'a, 'b> for QueryGlobalState { maybe_rpc_id, node_address, verbosity_level, - state_identifier, - hash, + global_state_str_params, &key, path, ) From 1084ec8b30cd2e09b617b367a49d9e7e1274707a Mon Sep 17 00:00:00 2001 From: Karan Dhareshwar Date: Thu, 1 Jul 2021 11:30:50 -0500 Subject: [PATCH 03/12] NDRS-1608: Rename client command --- client/src/global_state_query.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/global_state_query.rs b/client/src/global_state_query.rs index dd1c40fa42..28712ac844 100644 --- a/client/src/global_state_query.rs +++ b/client/src/global_state_query.rs @@ -83,7 +83,7 @@ fn global_state_str_params<'a>(matches: &'a ArgMatches) -> GlobalStateStrParams< } impl<'a, 'b> ClientCommand<'a, 'b> for QueryGlobalState { - const NAME: &'static str = "global-state-query"; + const NAME: &'static str = "query-global-state"; const ABOUT: &'static str = "Retrieves a stored value from the network using either the state root hash or block hash"; From 4831fb5ae6104d6c685a693bb5bd6b887e08f84e Mon Sep 17 00:00:00 2001 From: Karan Dhareshwar Date: Wed, 28 Jul 2021 14:16:10 -0500 Subject: [PATCH 04/12] 1608: Deprecate `state_get_item` from client --- client/lib/lib.rs | 5 +++-- client/lib/rpc.rs | 2 +- client/lib/validation.rs | 6 +++--- client/src/main.rs | 11 ++--------- .../{global_state_query.rs => query_global_state.rs} | 2 +- node/src/components/rpc_server/rpcs/docs.rs | 2 +- 6 files changed, 11 insertions(+), 17 deletions(-) rename client/src/{global_state_query.rs => query_global_state.rs} (98%) diff --git a/client/lib/lib.rs b/client/lib/lib.rs index f3a02864d3..8ffb95de71 100644 --- a/client/lib/lib.rs +++ b/client/lib/lib.rs @@ -366,7 +366,8 @@ pub fn get_state_root_hash( RpcCall::new(maybe_rpc_id, node_address, verbosity_level).get_state_root_hash(maybe_block_id) } -/// Retrieves a stored value from the network. +/// Retrieves a stored value from the network. This function is deprecated, use +/// `casper_client::query_global_state` instead. /// /// * `maybe_rpc_id` is the JSON-RPC identifier, applied to the request and returned in the /// response. If it can be parsed as an `i64` it will be used as a JSON integer. If empty, a @@ -533,7 +534,7 @@ pub fn get_account_info( /// deploy-0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20 # Key::DeployInfo /// ``` /// * `path` is comprised of components starting from the `key`, separated by `/`s. -pub fn global_state_query( +pub fn query_global_state( maybe_rpc_id: &str, node_address: &str, verbosity_level: u64, diff --git a/client/lib/rpc.rs b/client/lib/rpc.rs index 97e7da0666..1277122eec 100644 --- a/client/lib/rpc.rs +++ b/client/lib/rpc.rs @@ -315,7 +315,7 @@ impl RpcCall { }; let response = QueryGlobalState::request_with_map_params(self, params)?; - validation::validate_global_state_query(&response, global_state_identifier, &key, &path)?; + validation::validate_query_global_state(&response, global_state_identifier, &key, &path)?; Ok(response) } diff --git a/client/lib/validation.rs b/client/lib/validation.rs index 311e67ab2f..2bbc412624 100644 --- a/client/lib/validation.rs +++ b/client/lib/validation.rs @@ -22,7 +22,7 @@ use casper_types::{bytesrepr, Key, U512}; const GET_ITEM_RESULT_BALANCE_VALUE: &str = "balance_value"; const GET_ITEM_RESULT_STORED_VALUE: &str = "stored_value"; const GET_ITEM_RESULT_MERKLE_PROOF: &str = "merkle_proof"; -const GLOBAL_STATE_QUERY_BLOCK_HEADER: &str = "block_header"; +const QUERY_GLOBAL_STATE_BLOCK_HEADER: &str = "block_header"; /// Error that can be returned when validating a block returned from a JSON-RPC method. #[derive(Error, Debug)] @@ -184,7 +184,7 @@ pub(crate) fn validate_query_response( .map_err(Into::into) } -pub(crate) fn validate_global_state_query( +pub(crate) fn validate_query_global_state( response: &JsonRpc, state_identifier: GlobalStateIdentifier, key: &Key, @@ -218,7 +218,7 @@ pub(crate) fn validate_global_state_query( }; let json_block_header_value = object - .get(GLOBAL_STATE_QUERY_BLOCK_HEADER) + .get(QUERY_GLOBAL_STATE_BLOCK_HEADER) .ok_or(ValidateResponseError::ValidateResponseFailedToParse)?; let maybe_json_block_header: Option = serde_json::from_value(json_block_header_value.to_owned())?; diff --git a/client/src/main.rs b/client/src/main.rs index cd4323b8ca..032d89edde 100644 --- a/client/src/main.rs +++ b/client/src/main.rs @@ -10,10 +10,9 @@ mod get_auction_info; mod get_balance; mod get_era_info_by_switch_block; mod get_state_hash; -mod global_state_query; mod keygen; mod query_dictionary; -mod query_state; +mod query_global_state; use std::process; @@ -25,10 +24,7 @@ use casper_node::rpcs::{ chain::{GetBlock, GetBlockTransfers, GetEraInfoBySwitchBlock, GetStateRootHash}, docs::ListRpcs, info::GetDeploy, - state::{ - GetAccountInfo, GetAuctionInfo, GetBalance, GetDictionaryItem, GetItem as QueryState, - QueryGlobalState, - }, + state::{GetAccountInfo, GetAuctionInfo, GetBalance, GetDictionaryItem, QueryGlobalState}, }; use account_address::GenerateAccountHash as AccountAddress; @@ -52,7 +48,6 @@ enum DisplayOrder { GetBlockTransfers, ListDeploys, GetStateRootHash, - QueryState, GetBalance, GetAccountInfo, GetEraInfo, @@ -86,7 +81,6 @@ fn cli<'a, 'b>() -> App<'a, 'b> { .subcommand(GetStateRootHash::build( DisplayOrder::GetStateRootHash as usize, )) - .subcommand(QueryState::build(DisplayOrder::QueryState as usize)) .subcommand(GetEraInfoBySwitchBlock::build( DisplayOrder::GetEraInfo as usize, )) @@ -122,7 +116,6 @@ async fn main() { (GetBalance::NAME, Some(matches)) => (GetBalance::run(matches), matches), (GetAccountInfo::NAME, Some(matches)) => (GetAccountInfo::run(matches), matches), (GetStateRootHash::NAME, Some(matches)) => (GetStateRootHash::run(matches), matches), - (QueryState::NAME, Some(matches)) => (QueryState::run(matches), matches), (GetEraInfoBySwitchBlock::NAME, Some(matches)) => { (GetEraInfoBySwitchBlock::run(matches), matches) } diff --git a/client/src/global_state_query.rs b/client/src/query_global_state.rs similarity index 98% rename from client/src/global_state_query.rs rename to client/src/query_global_state.rs index 28712ac844..4eb03cf408 100644 --- a/client/src/global_state_query.rs +++ b/client/src/query_global_state.rs @@ -116,7 +116,7 @@ impl<'a, 'b> ClientCommand<'a, 'b> for QueryGlobalState { let key = common::key::get(matches)?; let path = common::path::get(matches); - casper_client::global_state_query( + casper_client::query_global_state( maybe_rpc_id, node_address, verbosity_level, diff --git a/node/src/components/rpc_server/rpcs/docs.rs b/node/src/components/rpc_server/rpcs/docs.rs index 97646a87c8..2c8f31b0aa 100644 --- a/node/src/components/rpc_server/rpcs/docs.rs +++ b/node/src/components/rpc_server/rpcs/docs.rs @@ -89,7 +89,7 @@ pub(crate) static OPEN_RPC_SCHEMA: Lazy = Lazy::new(|| { schema.push_with_optional_params::( "returns a state root hash at a given Block", ); - schema.push_with_params::("returns a stored value from the network"); + schema.push_with_params::("returns a stored value from the network. This RPC is deprecated, use `query_global_state` instead."); schema.push_with_params::("returns a purse's balance from the network"); schema.push_with_optional_params::( "returns an EraInfo from the network", From 657f60e03ae22ea00fa37d1fc0b7b49cb912b425 Mon Sep 17 00:00:00 2001 From: Karan Dhareshwar Date: Wed, 28 Jul 2021 14:52:22 -0500 Subject: [PATCH 05/12] 1608: Remove query-state from client binary --- client/src/query_state.rs | 58 --------------------------------------- 1 file changed, 58 deletions(-) delete mode 100644 client/src/query_state.rs diff --git a/client/src/query_state.rs b/client/src/query_state.rs deleted file mode 100644 index b0f1d8a22d..0000000000 --- a/client/src/query_state.rs +++ /dev/null @@ -1,58 +0,0 @@ -use std::str; - -use clap::{App, ArgMatches, SubCommand}; - -use casper_client::Error; -use casper_node::rpcs::state::GetItem; - -use crate::{command::ClientCommand, common, Success}; - -/// This struct defines the order in which the args are shown for this subcommand's help message. -enum DisplayOrder { - Verbose, - NodeAddress, - RpcId, - StateRootHash, - Key, - Path, -} - -impl<'a, 'b> ClientCommand<'a, 'b> for GetItem { - const NAME: &'static str = "query-state"; - const ABOUT: &'static str = "Retrieves a stored value from the network"; - - fn build(display_order: usize) -> App<'a, 'b> { - SubCommand::with_name(Self::NAME) - .about(Self::ABOUT) - .display_order(display_order) - .arg(common::verbose::arg(DisplayOrder::Verbose as usize)) - .arg(common::node_address::arg( - DisplayOrder::NodeAddress as usize, - )) - .arg(common::rpc_id::arg(DisplayOrder::RpcId as usize)) - .arg(common::state_root_hash::arg( - DisplayOrder::StateRootHash as usize, - )) - .arg(common::key::arg(DisplayOrder::Key as usize)) - .arg(common::path::arg(DisplayOrder::Path as usize)) - } - - fn run(matches: &ArgMatches<'_>) -> Result { - let maybe_rpc_id = common::rpc_id::get(matches); - let node_address = common::node_address::get(matches); - let verbosity_level = common::verbose::get(matches); - let state_root_hash = common::state_root_hash::get(matches); - let key = common::key::get(matches)?; - let path = common::path::get(matches); - - casper_client::get_item( - maybe_rpc_id, - node_address, - verbosity_level, - state_root_hash, - &key, - path, - ) - .map(Success::from) - } -} From 83f2c0a18d06ceb30242346b07c562171a54cca0 Mon Sep 17 00:00:00 2001 From: Karan Dhareshwar Date: Wed, 4 Aug 2021 08:52:49 -0500 Subject: [PATCH 06/12] 1608: Add specificity to error message --- client/src/query_global_state.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/query_global_state.rs b/client/src/query_global_state.rs index 4eb03cf408..6b0d143630 100644 --- a/client/src/query_global_state.rs +++ b/client/src/query_global_state.rs @@ -79,7 +79,7 @@ fn global_state_str_params<'a>(matches: &'a ArgMatches) -> GlobalStateStrParams< hash_value: block_hash, }; } - unreachable!("clap arg groups and parsing should prevent this") + unreachable!("clap arg groups and parsing should prevent this for global state params") } impl<'a, 'b> ClientCommand<'a, 'b> for QueryGlobalState { From 5ac179d2b63bd796ea8ae64b2ac4522ccc03dcbf Mon Sep 17 00:00:00 2001 From: Karan Dhareshwar Date: Wed, 4 Aug 2021 12:16:09 -0500 Subject: [PATCH 07/12] 1608: Resolve merge conflic --- client/lib/lib.rs | 10 ++++------ client/lib/rpc.rs | 4 ++-- client/src/main.rs | 2 +- client/src/query_global_state.rs | 5 ++++- 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/client/lib/lib.rs b/client/lib/lib.rs index b301714247..5c1aca1aec 100644 --- a/client/lib/lib.rs +++ b/client/lib/lib.rs @@ -556,7 +556,7 @@ pub async fn get_account_info( /// deploy-0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20 # Key::DeployInfo /// ``` /// * `path` is comprised of components starting from the `key`, separated by `/`s. -pub fn query_global_state( +pub async fn query_global_state( maybe_rpc_id: &str, node_address: &str, verbosity_level: u64, @@ -564,11 +564,9 @@ pub fn query_global_state( key: &str, path: &str, ) -> Result { - RpcCall::new(maybe_rpc_id, node_address, verbosity_level).query_global_state( - global_state_str_params, - key, - path, - ) + RpcCall::new(maybe_rpc_id, node_address, verbosity_level) + .query_global_state(global_state_str_params, key, path) + .await } /// Retrieves information and examples for all currently supported RPCs. diff --git a/client/lib/rpc.rs b/client/lib/rpc.rs index 4b1f6f6e19..40ebc9564d 100644 --- a/client/lib/rpc.rs +++ b/client/lib/rpc.rs @@ -293,7 +293,7 @@ impl RpcCall { GetAccountInfo::request_with_map_params(self, params).await } - pub(crate) fn query_global_state( + pub(crate) async fn query_global_state( self, global_state_str_params: GlobalStateStrParams<'_>, key: &str, @@ -323,7 +323,7 @@ impl RpcCall { path: path.clone(), }; - let response = QueryGlobalState::request_with_map_params(self, params)?; + let response = QueryGlobalState::request_with_map_params(self, params).await?; validation::validate_query_global_state(&response, global_state_identifier, &key, &path)?; Ok(response) } diff --git a/client/src/main.rs b/client/src/main.rs index f04712f5e3..242260361a 100644 --- a/client/src/main.rs +++ b/client/src/main.rs @@ -118,7 +118,6 @@ async fn main() { (GetBalance::NAME, Some(matches)) => (GetBalance::run(matches).await, matches), (GetAccountInfo::NAME, Some(matches)) => (GetAccountInfo::run(matches).await, matches), (GetStateRootHash::NAME, Some(matches)) => (GetStateRootHash::run(matches).await, matches), - (QueryState::NAME, Some(matches)) => (QueryState::run(matches).await, matches), (GetEraInfoBySwitchBlock::NAME, Some(matches)) => { (GetEraInfoBySwitchBlock::run(matches).await, matches) } @@ -132,6 +131,7 @@ async fn main() { (GetDictionaryItem::NAME, Some(matches)) => { (GetDictionaryItem::run(matches).await, matches) } + (QueryGlobalState::NAME, Some(matches)) => (QueryGlobalState::run(matches).await, matches), _ => { let _ = cli().print_long_help(); diff --git a/client/src/query_global_state.rs b/client/src/query_global_state.rs index 6b0d143630..96e6473414 100644 --- a/client/src/query_global_state.rs +++ b/client/src/query_global_state.rs @@ -1,5 +1,6 @@ use std::str; +use async_trait::async_trait; use clap::{App, Arg, ArgGroup, ArgMatches, SubCommand}; use casper_client::{Error, GlobalStateStrParams}; @@ -82,6 +83,7 @@ fn global_state_str_params<'a>(matches: &'a ArgMatches) -> GlobalStateStrParams< unreachable!("clap arg groups and parsing should prevent this for global state params") } +#[async_trait] impl<'a, 'b> ClientCommand<'a, 'b> for QueryGlobalState { const NAME: &'static str = "query-global-state"; const ABOUT: &'static str = @@ -108,7 +110,7 @@ impl<'a, 'b> ClientCommand<'a, 'b> for QueryGlobalState { ) } - fn run(matches: &ArgMatches<'_>) -> Result { + async fn run(matches: &ArgMatches<'a>) -> Result { let maybe_rpc_id = common::rpc_id::get(matches); let node_address = common::node_address::get(matches); let verbosity_level = common::verbose::get(matches); @@ -124,6 +126,7 @@ impl<'a, 'b> ClientCommand<'a, 'b> for QueryGlobalState { &key, path, ) + .await .map(Success::from) } } From 51b27d87427a83c9d5b2546f57b6b34a3c4753d1 Mon Sep 17 00:00:00 2001 From: Karan Dhareshwar Date: Mon, 9 Aug 2021 13:32:20 -0500 Subject: [PATCH 08/12] 1608: Address PR feedback pt.2 --- client/lib/ffi.rs | 63 ++++++++++ client/lib/lib.rs | 23 ++-- client/lib/validation.rs | 8 +- client/src/common.rs | 80 ------------- client/src/main.rs | 4 +- client/src/query_global_state.rs | 91 +++++++++++++- client/tests/integration_test.rs | 118 ++++++++++++++++++- node/src/components/rpc_server/rpcs/state.rs | 10 +- 8 files changed, 290 insertions(+), 107 deletions(-) diff --git a/client/lib/ffi.rs b/client/lib/ffi.rs index ad50842a96..88e50338d5 100644 --- a/client/lib/ffi.rs +++ b/client/lib/ffi.rs @@ -551,6 +551,8 @@ pub extern "C" fn casper_get_state_root_hash( /// /// See [get_item](super::get_item) for more details. #[no_mangle] +#[allow(deprecated)] +#[deprecated(note = "Users should use `casper_client::query_global_state` instead.")] pub extern "C" fn casper_get_item( maybe_rpc_id: *const c_char, node_address: *const c_char, @@ -584,6 +586,43 @@ pub extern "C" fn casper_get_item( }) } +/// Retrieves information from global state using either a Block hash or a state root hash. +/// +/// See [query_global_state](super::query_global_state) for more info. +#[no_mangle] +pub extern "C" fn casper_query_global_state( + maybe_rpc_id: *const c_char, + node_address: *const c_char, + verbosity_level: u64, + global_state_params: *const casper_global_state_params_t, + key: *const c_char, + path: *const c_char, + response_buf: *mut c_uchar, + response_buf_len: usize, +) -> casper_error_t { + let mut runtime = RUNTIME.lock().expect("should lock"); + let runtime = try_unwrap_option!(&mut *runtime, or_else => Error::FFISetupNotCalled); + let maybe_rpc_id = try_unsafe_arg!(maybe_rpc_id); + let node_address = try_unsafe_arg!(node_address); + let global_state_params = try_arg_into!(global_state_params); + let key = try_unsafe_arg!(key); + let path = try_unsafe_arg!(path); + runtime.block_on(async move { + let result = super::query_global_state( + maybe_rpc_id, + node_address, + verbosity_level, + global_state_params, + key, + path, + ) + .await; + let response = try_unwrap_rpc!(result); + copy_str_to_buf(&response, response_buf, response_buf_len); + casper_error_t::CASPER_SUCCESS + }) +} + /// Retrieves a purse's balance from the network. /// /// See [get_balance](super::get_balance) for more details. @@ -882,3 +921,27 @@ impl TryInto> for casper_session_params_t { }) } } + +/// The two ways to construct a query to global state. +/// +/// See [GlobalStateStrParams](super::GlobalStateStrParams) for more info. +#[allow(non_snake_case)] +#[repr(C)] +#[derive(Clone)] +pub struct casper_global_state_params_t { + is_block_hash: bool, + hash_value: *const c_char, +} + +impl TryInto> for casper_global_state_params_t { + type Error = Error; + + fn try_into(self) -> Result> { + let hash_value = + unsafe_str_arg(self.hash_value, "casper_global_state_params_t.hash_value")?; + Ok(super::GlobalStateStrParams { + hash_value, + is_block_hash: self.is_block_hash, + }) + } +} diff --git a/client/lib/lib.rs b/client/lib/lib.rs index e66e1c9ea7..e8558bd52c 100644 --- a/client/lib/lib.rs +++ b/client/lib/lib.rs @@ -29,12 +29,12 @@ use serde::Serialize; use casper_execution_engine::core::engine_state::ExecutableDeployItem; use casper_node::{ - rpcs::state::DictionaryIdentifier, + crypto::hash::Digest, + rpcs::state::{DictionaryIdentifier, GlobalStateIdentifier}, types::{BlockHash, Deploy}, }; use casper_types::Key; -use casper_node::{crypto::hash::Digest, rpcs::state::GlobalStateIdentifier}; pub use cl_type::help; pub use deploy::ListDeploysResult; use deploy::{DeployExt, DeployParams, OutputKind}; @@ -368,8 +368,7 @@ pub async fn get_state_root_hash( .await } -/// Retrieves a stored value from the network. This function is deprecated, use -/// `casper_client::query_global_state` instead. +/// Retrieves a stored value from the network. /// /// * `maybe_rpc_id` is the JSON-RPC identifier, applied to the request and returned in the /// response. If it can be parsed as an `i64` it will be used as a JSON integer. If empty, a @@ -392,8 +391,10 @@ pub async fn get_state_root_hash( /// uref-0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20-007 # Key::URef /// transfer-0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20 # Key::Transfer /// deploy-0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20 # Key::DeployInfo +/// dictionary-0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20 # Key::Dictionary /// ``` /// * `path` is comprised of components starting from the `key`, separated by `/`s. +#[deprecated(note = "Users should use `casper_client::query_global_state` instead.")] pub async fn get_item( maybe_rpc_id: &str, node_address: &str, @@ -531,7 +532,8 @@ pub async fn get_account_info( /// either a `Block` hash or a specific `state_root_hash`. /// * `hash` is a 32-byte hex encoded hash which can either be the block hash or state root hash and /// is identified by the `state_identifier` arg. -/// * `state_root_hash` must be a hex-encoded, 32-byte hash digest. +/// * `global_state_str_params` contains global state identifier related options for this query. See +/// [`GlobalStateStrParams`](struct.GlobalStateStrParams.html) for more details. /// * `key` must be a formatted [`PublicKey`](https://docs.rs/casper-node/latest/casper-node/crypto/asymmetric_key/enum.PublicKey.html) /// or [`Key`](https://docs.rs/casper-types/latest/casper-types/enum.PublicKey.html). This will /// take one of the following forms: @@ -542,6 +544,7 @@ pub async fn get_account_info( /// uref-0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20-007 # Key::URef /// transfer-0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20 # Key::Transfer /// deploy-0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20 # Key::DeployInfo +/// dictionary-0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20 # Key::Dictionary /// ``` /// * `path` is comprised of components starting from the `key`, separated by `/`s. pub async fn query_global_state( @@ -1135,13 +1138,13 @@ impl<'a> TryInto for DictionaryItemStrParams<'a> { } } -/// Container for `GlobalStateIdentifier` construction options. +/// The two ways of constructing a query to global state. #[derive(Default, Debug)] pub struct GlobalStateStrParams<'a> { /// Identifier to mark the hash as either a Block hash or `state_root_hash` /// When true, the hash provided is a Block hash. pub is_block_hash: bool, - /// The hash value. + /// The hex-encoded hash value. pub hash_value: &'a str, } @@ -1150,14 +1153,14 @@ impl<'a> TryInto for GlobalStateStrParams<'a> { fn try_into(self) -> Result { let hash = Digest::from_hex(self.hash_value).map_err(|error| Error::CryptoError { - context: "block_identifier", + context: "global_state_identifier", error, })?; if self.is_block_hash { - Ok(GlobalStateIdentifier::Block(BlockHash::new(hash))) + Ok(GlobalStateIdentifier::BlockHash(BlockHash::new(hash))) } else { - Ok(GlobalStateIdentifier::StateRoot(hash)) + Ok(GlobalStateIdentifier::StateRootHash(hash)) } } } diff --git a/client/lib/validation.rs b/client/lib/validation.rs index 2bbc412624..e4fb25301b 100644 --- a/client/lib/validation.rs +++ b/client/lib/validation.rs @@ -224,15 +224,15 @@ pub(crate) fn validate_query_global_state( serde_json::from_value(json_block_header_value.to_owned())?; let state_root_hash = match (state_identifier, maybe_json_block_header) { - (GlobalStateIdentifier::Block(_), None) - | (GlobalStateIdentifier::StateRoot(_), Some(_)) => { + (GlobalStateIdentifier::BlockHash(_), None) + | (GlobalStateIdentifier::StateRootHash(_), Some(_)) => { return Err(ValidateResponseError::InvalidGlobalStateResponse); } - (GlobalStateIdentifier::Block(_), Some(json_header)) => { + (GlobalStateIdentifier::BlockHash(_), Some(json_header)) => { let block_header = BlockHeader::from(json_header); *block_header.state_root_hash() } - (GlobalStateIdentifier::StateRoot(hash), None) => hash, + (GlobalStateIdentifier::StateRootHash(hash), None) => hash, }; core::validate_query_proof( diff --git a/client/src/common.rs b/client/src/common.rs index 4cd9a74212..3e1adf721d 100644 --- a/client/src/common.rs +++ b/client/src/common.rs @@ -296,83 +296,3 @@ pub(super) mod session_account { sealed_public_key::get(matches, ARG_NAME, IS_REQUIRED) } } - -/// Handles providing the arg for and retrieval of the key. -pub mod key { - use casper_node::crypto::AsymmetricKeyExt; - use casper_types::AsymmetricType; - - use super::*; - - const ARG_NAME: &str = "key"; - const ARG_SHORT: &str = "k"; - const ARG_VALUE_NAME: &str = "FORMATTED STRING or PATH"; - const ARG_HELP: &str = - "The base key for the query. This must be a properly formatted public key, account hash, \ - contract address hash, URef, transfer hash or deploy-info hash. The format for each \ - respectively is \"\", \"account-hash-\", \"hash-\", \ - \"uref--\", \"transfer-\" and \ - \"deploy-\". The public key may instead be read in from a file, in which case \ - enter the path to the file as the --key argument. The file should be one of the two public \ - key files generated via the `keygen` subcommand; \"public_key_hex\" or \"public_key.pem\""; - - pub(crate) fn arg(order: usize) -> Arg<'static, 'static> { - Arg::with_name(ARG_NAME) - .long(ARG_NAME) - .short(ARG_SHORT) - .required(true) - .value_name(ARG_VALUE_NAME) - .help(ARG_HELP) - .display_order(order) - } - - pub(crate) fn get(matches: &ArgMatches) -> Result { - let value = matches - .value_of(ARG_NAME) - .unwrap_or_else(|| panic!("should have {} arg", ARG_NAME)); - - // Try to read as a PublicKey PEM file first. - if let Ok(public_key) = PublicKey::from_file(value) { - return Ok(public_key.to_hex()); - } - - // Try to read as a hex-encoded PublicKey file next. - if let Ok(hex_public_key) = fs::read_to_string(value) { - let _ = PublicKey::from_hex(&hex_public_key).map_err(|error| { - eprintln!( - "Can't parse the contents of {} as a public key: {}", - value, error - ); - Error::FailedToParseKey - })?; - return Ok(hex_public_key); - } - - // Just return the value. - Ok(value.to_string()) - } -} - -/// Handles providing the arg for and retrieval of the key. -pub mod path { - use super::*; - - const ARG_NAME: &str = "query-path"; - const ARG_SHORT: &str = "q"; - const ARG_VALUE_NAME: &str = "PATH/FROM/KEY"; - const ARG_HELP: &str = "The path from the key of the query"; - - pub(crate) fn arg(order: usize) -> Arg<'static, 'static> { - Arg::with_name(ARG_NAME) - .long(ARG_NAME) - .short(ARG_SHORT) - .required(false) - .value_name(ARG_VALUE_NAME) - .help(ARG_HELP) - .display_order(order) - } - - pub(crate) fn get<'a>(matches: &'a ArgMatches) -> &'a str { - matches.value_of(ARG_NAME).unwrap_or_default() - } -} diff --git a/client/src/main.rs b/client/src/main.rs index 242260361a..b663ce51a8 100644 --- a/client/src/main.rs +++ b/client/src/main.rs @@ -48,6 +48,8 @@ enum DisplayOrder { GetBlockTransfers, ListDeploys, GetStateRootHash, + QueryGlobalState, + GetDictionaryItem, GetBalance, GetAccountInfo, GetEraInfo, @@ -56,8 +58,6 @@ enum DisplayOrder { GenerateCompletion, GetRpcs, AccountAddress, - GetDictionaryItem, - QueryGlobalState, } fn cli<'a, 'b>() -> App<'a, 'b> { diff --git a/client/src/query_global_state.rs b/client/src/query_global_state.rs index 96e6473414..a1cf0da3a5 100644 --- a/client/src/query_global_state.rs +++ b/client/src/query_global_state.rs @@ -1,4 +1,4 @@ -use std::str; +use std::{fs, str}; use async_trait::async_trait; use clap::{App, Arg, ArgGroup, ArgMatches, SubCommand}; @@ -67,6 +67,87 @@ mod block_hash { } } +/// Handles providing the arg for and retrieval of the key. +mod key { + use casper_node::crypto::AsymmetricKeyExt; + use casper_types::{AsymmetricType, PublicKey}; + + use super::*; + + const ARG_NAME: &str = "key"; + const ARG_SHORT: &str = "k"; + const ARG_VALUE_NAME: &str = "FORMATTED STRING or PATH"; + const ARG_HELP: &str = + "The base key for the query. This must be a properly formatted public key, account hash, \ + contract address hash, URef, transfer hash, deploy-info hash or dictionary address. The \ + format for each respectively is \"\", \"account-hash-\", \ + \"hash-\", \"uref--\", \ + \"transfer-\", \"deploy-\" and \"dictionary-\". \ + The public key may instead be read in from a file, in which case \ + enter the path to the file as the --key argument. The file should be one of the two public \ + key files generated via the `keygen` subcommand; \"public_key_hex\" or \"public_key.pem\""; + + pub(crate) fn arg(order: usize) -> Arg<'static, 'static> { + Arg::with_name(ARG_NAME) + .long(ARG_NAME) + .short(ARG_SHORT) + .required(true) + .value_name(ARG_VALUE_NAME) + .help(ARG_HELP) + .display_order(order) + } + + pub(crate) fn get(matches: &ArgMatches) -> Result { + let value = matches + .value_of(ARG_NAME) + .unwrap_or_else(|| panic!("should have {} arg", ARG_NAME)); + + // Try to read as a PublicKey PEM file first. + if let Ok(public_key) = PublicKey::from_file(value) { + return Ok(public_key.to_hex()); + } + + // Try to read as a hex-encoded PublicKey file next. + if let Ok(hex_public_key) = fs::read_to_string(value) { + let _ = PublicKey::from_hex(&hex_public_key).map_err(|error| { + eprintln!( + "Can't parse the contents of {} as a public key: {}", + value, error + ); + Error::FailedToParseKey + })?; + return Ok(hex_public_key); + } + + // Just return the value. + Ok(value.to_string()) + } +} + +/// Handles providing the arg for and retrieval of the key. +mod path { + use super::*; + + const ARG_NAME: &str = "query-path"; + const ARG_SHORT: &str = "q"; + const ARG_VALUE_NAME: &str = "PATH/FROM/KEY"; + const ARG_HELP: &str = "The path from the key of the query"; + + pub(crate) fn arg(order: usize) -> Arg<'static, 'static> { + Arg::with_name(ARG_NAME) + .long(ARG_NAME) + .short(ARG_SHORT) + .required(false) + .value_name(ARG_VALUE_NAME) + .help(ARG_HELP) + .display_order(order) + } + + pub(crate) fn get<'a>(matches: &'a ArgMatches) -> &'a str { + matches.value_of(ARG_NAME).unwrap_or_default() + } +} + fn global_state_str_params<'a>(matches: &'a ArgMatches) -> GlobalStateStrParams<'a> { if let Some(state_root_hash) = state_root_hash::get(matches) { return GlobalStateStrParams { @@ -98,8 +179,8 @@ impl<'a, 'b> ClientCommand<'a, 'b> for QueryGlobalState { DisplayOrder::NodeAddress as usize, )) .arg(common::rpc_id::arg(DisplayOrder::RpcId as usize)) - .arg(common::key::arg(DisplayOrder::Key as usize)) - .arg(common::path::arg(DisplayOrder::Path as usize)) + .arg(key::arg(DisplayOrder::Key as usize)) + .arg(path::arg(DisplayOrder::Path as usize)) .arg(block_hash::arg()) .arg(state_root_hash::arg()) .group( @@ -115,8 +196,8 @@ impl<'a, 'b> ClientCommand<'a, 'b> for QueryGlobalState { let node_address = common::node_address::get(matches); let verbosity_level = common::verbose::get(matches); let global_state_str_params = global_state_str_params(matches); - let key = common::key::get(matches)?; - let path = common::path::get(matches); + let key = key::get(matches)?; + let path = path::get(matches); casper_client::query_global_state( maybe_rpc_id, diff --git a/client/tests/integration_test.rs b/client/tests/integration_test.rs index 0a0925210f..ed8e245b25 100644 --- a/client/tests/integration_test.rs +++ b/client/tests/integration_test.rs @@ -12,7 +12,9 @@ use warp_json_rpc::Builder; use casper_node::crypto::Error as CryptoError; use hex::FromHexError; -use casper_client::{DeployStrParams, Error, PaymentStrParams, SessionStrParams}; +use casper_client::{ + DeployStrParams, Error, GlobalStateStrParams, PaymentStrParams, SessionStrParams, +}; use casper_node::rpcs::{ account::{PutDeploy, PutDeployParams}, chain::{GetStateRootHash, GetStateRootHashParams}, @@ -143,12 +145,24 @@ impl MockServerHandle { .map(|_| ()) } + #[allow(deprecated)] async fn get_item(&self, state_root_hash: &str, key: &str, path: &str) -> Result<(), Error> { casper_client::get_item("1", &self.url(), 0, state_root_hash, key, path) .await .map(|_| ()) } + async fn query_global_state( + &self, + global_state_params: GlobalStateStrParams<'_>, + key: &str, + path: &str, + ) -> Result<(), Error> { + casper_client::query_global_state("1", &self.url(), 0, global_state_params, key, path) + .await + .map(|_| ()) + } + async fn transfer( &self, amount: &str, @@ -259,6 +273,25 @@ mod session_params { } } +/// Sample data creation methods for GlobalStateStrParams +mod global_state_params { + use super::*; + + pub fn test_params_as_state_root_hash() -> GlobalStateStrParams<'static> { + GlobalStateStrParams { + is_block_hash: false, + hash_value: VALID_STATE_ROOT_HASH, + } + } + + pub fn invalid_global_state_str_params() -> GlobalStateStrParams<'static> { + GlobalStateStrParams { + is_block_hash: false, + hash_value: "invalid state root has", + } + } +} + mod get_balance { use super::*; @@ -498,6 +531,89 @@ mod get_item { } } +mod query_global_state { + use casper_client::ValidateResponseError; + use casper_node::rpcs::state::{QueryGlobalState, QueryGlobalStateParams}; + + use super::*; + + #[tokio::test(flavor = "multi_thread")] + async fn should_succeed_with_valid_global_state_params() { + let server_handle = + MockServerHandle::spawn::(QueryGlobalState::METHOD); + + // in this case, the error means that the request was sent successfully, but due to to the + // mock implementation fails to validate + + assert!(matches!( + server_handle + .query_global_state( + global_state_params::test_params_as_state_root_hash(), + VALID_PURSE_UREF, + "" + ) + .await, + Err(Error::InvalidResponse( + ValidateResponseError::ValidateResponseFailedToParse + )) + )); + } + + #[tokio::test(flavor = "multi_thread")] + async fn should_fail_with_invalid_global_state_params() { + let server_handle = + MockServerHandle::spawn::(QueryGlobalState::METHOD); + assert!(matches!( + server_handle + .query_global_state( + global_state_params::invalid_global_state_str_params(), + VALID_PURSE_UREF, + "" + ) + .await, + Err(Error::CryptoError { + context: "global_state_identifier", + error: CryptoError::FromHex(FromHexError::InvalidStringLength) + }) + )); + } + + #[tokio::test(flavor = "multi_thread")] + async fn should_fail_with_invalid_key() { + let server_handle = + MockServerHandle::spawn::(QueryGlobalState::METHOD); + assert!(matches!( + server_handle + .query_global_state( + global_state_params::test_params_as_state_root_hash(), + "invalid key", + "" + ) + .await, + Err(Error::FailedToParseKey) + )); + } + + #[tokio::test(flavor = "multi_thread")] + async fn should_fail_with_empty_key() { + let server_handle = + MockServerHandle::spawn::(QueryGlobalState::METHOD); + assert!(matches!( + server_handle + .query_global_state( + global_state_params::invalid_global_state_str_params(), + "", + "" + ) + .await, + Err(Error::CryptoError { + context: "global_state_identifier", + error: CryptoError::FromHex(FromHexError::InvalidStringLength) + }) + )); + } +} + mod get_deploy { use super::*; diff --git a/node/src/components/rpc_server/rpcs/state.rs b/node/src/components/rpc_server/rpcs/state.rs index 341ecb34be..cf30d445da 100644 --- a/node/src/components/rpc_server/rpcs/state.rs +++ b/node/src/components/rpc_server/rpcs/state.rs @@ -99,7 +99,7 @@ static GET_DICTIONARY_ITEM_RESULT: Lazy = }); static QUERY_GLOBAL_STATE_PARAMS: Lazy = Lazy::new(|| QueryGlobalStateParams { - state_identifier: GlobalStateIdentifier::Block(*Block::doc_example().hash()), + state_identifier: GlobalStateIdentifier::BlockHash(*Block::doc_example().hash()), key: "deploy-af684263911154d26fa05be9963171802801a0b6aff8f199b7391eacb8edc9e1".to_string(), path: vec![], }); @@ -848,9 +848,9 @@ impl RpcWithParamsExt for GetDictionaryItem { #[serde(deny_unknown_fields)] pub enum GlobalStateIdentifier { /// Query using a block hash. - Block(BlockHash), + BlockHash(BlockHash), /// Query using the state root hash. - StateRoot(Digest), + StateRootHash(Digest), } /// Params for "query_global_state" RPC @@ -911,7 +911,7 @@ impl RpcWithParamsExt for QueryGlobalState { ) -> BoxFuture<'static, Result, Error>> { async move { let (state_root_hash, maybe_block_header) = match params.state_identifier { - GlobalStateIdentifier::Block(block_hash) => { + GlobalStateIdentifier::BlockHash(block_hash) => { match effect_builder .get_block_header_from_storage(block_hash) .await @@ -931,7 +931,7 @@ impl RpcWithParamsExt for QueryGlobalState { } } } - GlobalStateIdentifier::StateRoot(state_root_hash) => (state_root_hash, None), + GlobalStateIdentifier::StateRootHash(state_root_hash) => (state_root_hash, None), }; let base_key = match Key::from_formatted_str(¶ms.key) From e2fbb5e8e7afac8e8a992c4f60cbcb4833df4dd3 Mon Sep 17 00:00:00 2001 From: Karan Dhareshwar Date: Tue, 10 Aug 2021 12:49:00 -0500 Subject: [PATCH 09/12] 1608: Address PR feedback pt.3 --- client/lib/ffi.rs | 2 +- client/lib/lib.rs | 40 ++++++++++++++++++-------------- client/src/query_global_state.rs | 14 +++++++---- 3 files changed, 33 insertions(+), 23 deletions(-) diff --git a/client/lib/ffi.rs b/client/lib/ffi.rs index 88e50338d5..9b789b08b9 100644 --- a/client/lib/ffi.rs +++ b/client/lib/ffi.rs @@ -551,7 +551,6 @@ pub extern "C" fn casper_get_state_root_hash( /// /// See [get_item](super::get_item) for more details. #[no_mangle] -#[allow(deprecated)] #[deprecated(note = "Users should use `casper_client::query_global_state` instead.")] pub extern "C" fn casper_get_item( maybe_rpc_id: *const c_char, @@ -571,6 +570,7 @@ pub extern "C" fn casper_get_item( let key = try_unsafe_arg!(key); let path = try_unsafe_arg!(path); runtime.block_on(async move { + #[allow(deprecated)] let result = super::get_item( maybe_rpc_id, node_address, diff --git a/client/lib/lib.rs b/client/lib/lib.rs index e8558bd52c..41fe0bfcab 100644 --- a/client/lib/lib.rs +++ b/client/lib/lib.rs @@ -385,13 +385,18 @@ pub async fn get_state_root_hash( /// or [`Key`](https://docs.rs/casper-types/latest/casper-types/enum.PublicKey.html). This will /// take one of the following forms: /// ```text -/// 01c9e33693951aaac23c49bee44ad6f863eedcd38c084a3a8f11237716a3df9c2c # PublicKey +/// 01c9e33693951aaac23c49bee44ad6f863eedcd38c084a3a8f11237716a3df9c2c # PublicKey /// account-hash-0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20 # Key::Account -/// hash-0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20 # Key::Hash -/// uref-0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20-007 # Key::URef -/// transfer-0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20 # Key::Transfer -/// deploy-0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20 # Key::DeployInfo -/// dictionary-0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20 # Key::Dictionary +/// hash-0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20 # Key::Hash +/// uref-0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20-007 # Key::URef +/// transfer-0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20 # Key::Transfer +/// deploy-0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20 # Key::DeployInfo +/// era-1 # Key::EraInfo +/// bid-0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20 # Key::Bid +/// withdraw-0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20 # Key::Withdraw +/// dictionary-0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20 # Key::Dictionary +/// The Key::SystemContractRegistry variant is unique and can only take the following value: +/// system-contract-registry-0000000000000000000000000000000000000000000000000000000000000000 /// ``` /// * `path` is comprised of components starting from the `key`, separated by `/`s. #[deprecated(note = "Users should use `casper_client::query_global_state` instead.")] @@ -528,23 +533,24 @@ pub async fn get_account_info( /// count of the field. When `verbosity_level` is greater than `1`, the request will be printed /// to `stdout` with no abbreviation of long fields. When `verbosity_level` is `0`, the request /// will not be printed to `stdout`. -/// * `state_identifier` is a str value that identifies the supplied 32-byte hex encoded hash as as -/// either a `Block` hash or a specific `state_root_hash`. -/// * `hash` is a 32-byte hex encoded hash which can either be the block hash or state root hash and -/// is identified by the `state_identifier` arg. /// * `global_state_str_params` contains global state identifier related options for this query. See /// [`GlobalStateStrParams`](struct.GlobalStateStrParams.html) for more details. /// * `key` must be a formatted [`PublicKey`](https://docs.rs/casper-node/latest/casper-node/crypto/asymmetric_key/enum.PublicKey.html) -/// or [`Key`](https://docs.rs/casper-types/latest/casper-types/enum.PublicKey.html). This will +/// or [`Key`](https://docs.rs/casper-types/latest/casper-types/enum.Key.html). This will /// take one of the following forms: /// ```text -/// 01c9e33693951aaac23c49bee44ad6f863eedcd38c084a3a8f11237716a3df9c2c # PublicKey +/// 01c9e33693951aaac23c49bee44ad6f863eedcd38c084a3a8f11237716a3df9c2c # PublicKey /// account-hash-0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20 # Key::Account -/// hash-0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20 # Key::Hash -/// uref-0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20-007 # Key::URef -/// transfer-0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20 # Key::Transfer -/// deploy-0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20 # Key::DeployInfo -/// dictionary-0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20 # Key::Dictionary +/// hash-0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20 # Key::Hash +/// uref-0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20-007 # Key::URef +/// transfer-0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20 # Key::Transfer +/// deploy-0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20 # Key::DeployInfo +/// era-1 # Key::EraInfo +/// bid-0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20 # Key::Bid +/// withdraw-0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20 # Key::Withdraw +/// dictionary-0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20 # Key::Dictionary +/// The Key::SystemContractRegistry variant is unique and can only take the following value: +/// system-contract-registry-0000000000000000000000000000000000000000000000000000000000000000 /// ``` /// * `path` is comprised of components starting from the `key`, separated by `/`s. pub async fn query_global_state( diff --git a/client/src/query_global_state.rs b/client/src/query_global_state.rs index a1cf0da3a5..480be2db64 100644 --- a/client/src/query_global_state.rs +++ b/client/src/query_global_state.rs @@ -79,11 +79,15 @@ mod key { const ARG_VALUE_NAME: &str = "FORMATTED STRING or PATH"; const ARG_HELP: &str = "The base key for the query. This must be a properly formatted public key, account hash, \ - contract address hash, URef, transfer hash, deploy-info hash or dictionary address. The \ - format for each respectively is \"\", \"account-hash-\", \ - \"hash-\", \"uref--\", \ - \"transfer-\", \"deploy-\" and \"dictionary-\". \ - The public key may instead be read in from a file, in which case \ + contract address hash, URef, transfer hash, deploy-info hash,era-info number, bid, withdraw \ + or dictionary address. The format for each respectively is \"\", \ + \"account-hash-\", \"hash-\", \ + \"uref--\", \"transfer-\", \ + \"deploy-\", \"era-\", \"bid-\",\ + \"withdraw-\" or \"dictionary-\". \ + The system contract registry key is unique and can only take the value: \ + system-contract-registry-0000000000000000000000000000000000000000000000000000000000000000. \ + \nThe public key may instead be read in from a file, in which case \ enter the path to the file as the --key argument. The file should be one of the two public \ key files generated via the `keygen` subcommand; \"public_key_hex\" or \"public_key.pem\""; From aa30b50c8e09885b7697621ceddb29381d9a9014 Mon Sep 17 00:00:00 2001 From: Karan Dhareshwar Date: Tue, 10 Aug 2021 13:14:13 -0500 Subject: [PATCH 10/12] 1608: Fix CI issue --- client/lib/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/lib/lib.rs b/client/lib/lib.rs index 41fe0bfcab..c71540dbf9 100644 --- a/client/lib/lib.rs +++ b/client/lib/lib.rs @@ -536,8 +536,8 @@ pub async fn get_account_info( /// * `global_state_str_params` contains global state identifier related options for this query. See /// [`GlobalStateStrParams`](struct.GlobalStateStrParams.html) for more details. /// * `key` must be a formatted [`PublicKey`](https://docs.rs/casper-node/latest/casper-node/crypto/asymmetric_key/enum.PublicKey.html) -/// or [`Key`](https://docs.rs/casper-types/latest/casper-types/enum.Key.html). This will -/// take one of the following forms: +/// or [`Key`](https://docs.rs/casper-types/latest/casper-types/enum.Key.html). This will take one +/// of the following forms: /// ```text /// 01c9e33693951aaac23c49bee44ad6f863eedcd38c084a3a8f11237716a3df9c2c # PublicKey /// account-hash-0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20 # Key::Account From 05006763fe4a419d364c0e0027db20b3a2ddd058 Mon Sep 17 00:00:00 2001 From: Karan Dhareshwar Date: Thu, 12 Aug 2021 09:16:18 -0500 Subject: [PATCH 11/12] 1608: Update error message --- client/lib/error.rs | 2 +- client/lib/lib.rs | 2 +- client/lib/validation.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/lib/error.rs b/client/lib/error.rs index bba05a6563..daec5ece57 100644 --- a/client/lib/error.rs +++ b/client/lib/error.rs @@ -166,7 +166,7 @@ pub enum Error { FailedToParseDictionaryIdentifier, /// Failed to identify the hash as either block hash or state root hash. - #[error("Failed to determine state identifier")] + #[error("Failed to parse state identifier")] FailedToParseStateIdentifier, /// Must call FFI's setup function prior to making FFI calls. diff --git a/client/lib/lib.rs b/client/lib/lib.rs index c71540dbf9..f218c59be6 100644 --- a/client/lib/lib.rs +++ b/client/lib/lib.rs @@ -1144,7 +1144,7 @@ impl<'a> TryInto for DictionaryItemStrParams<'a> { } } -/// The two ways of constructing a query to global state. +/// The two ways to construct a query to global state. #[derive(Default, Debug)] pub struct GlobalStateStrParams<'a> { /// Identifier to mark the hash as either a Block hash or `state_root_hash` diff --git a/client/lib/validation.rs b/client/lib/validation.rs index e4fb25301b..3b949e6976 100644 --- a/client/lib/validation.rs +++ b/client/lib/validation.rs @@ -64,7 +64,7 @@ pub enum ValidateResponseError { UnexpectedBlockHeight, /// An invalid combination of state identifier and block header response - #[error("Invalid combination of State identifier and block header in response")] + #[error("Invalid combination of state identifier and block header in response")] InvalidGlobalStateResponse, } From f8dac1e8cb9292ec68fd115f2cc1f90f188d77eb Mon Sep 17 00:00:00 2001 From: Karan Dhareshwar Date: Fri, 13 Aug 2021 08:10:17 -0500 Subject: [PATCH 12/12] 1608: Update nctl tests --- client/README.md | 8 ++++---- utils/nctl/sh/contracts-erc20/utils.sh | 4 ++-- utils/nctl/sh/contracts-kv/utils.sh | 4 ++-- utils/nctl/sh/views/view_chain_account.sh | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/client/README.md b/client/README.md index 84563a8716..4bdc91457c 100644 --- a/client/README.md +++ b/client/README.md @@ -39,7 +39,7 @@ SUBCOMMANDS: get-block-transfers Retrieves all transfers for a block from the network list-deploys Retrieves the list of all deploy hashes in a given block get-state-root-hash Retrieves a state root hash at a given block - query-state Retrieves a stored value from the network + query-global-state Retrieves a stored value from the network get-balance Retrieves a purse's balance from the network get-auction-info Retrieves the bids and validators as of the most recently added block keygen Generates account key files in the given directory @@ -342,11 +342,11 @@ cargo run --release -- get-block-transfers \ ### Query the global state -To view data stored to global state after executing a deploy, you can use `query-state`. For example, to see the value +To view data stored to global state after executing a deploy, you can use `query-global-state`. For example, to see the value stored under our new account's public key: ``` -cargo run --release -- query-state \ +cargo run --release -- query-global-state \ --node-address=http://localhost:50101 \ --state-root-hash=242666f5959e6a51b7a75c23264f3cb326eecd6bec6dbab147f5801ec23daed6 \ --key=$PUBLIC_KEY @@ -411,7 +411,7 @@ cargo run --release -- get-balance \ Note that the system mint contract is required to retrieve the balance of any given purse. If you execute a -`query-state` specifying a purse `URef` as the `--key` argument, you'll find that the actual value stored there is a +`query-global-state` specifying a purse `URef` as the `--key` argument, you'll find that the actual value stored there is a unit value `()`. This makes the `get-balance` subcommand particularly useful. --- diff --git a/utils/nctl/sh/contracts-erc20/utils.sh b/utils/nctl/sh/contracts-erc20/utils.sh index 62be2dc352..8ddc1fb9bc 100644 --- a/utils/nctl/sh/contracts-erc20/utils.sh +++ b/utils/nctl/sh/contracts-erc20/utils.sh @@ -9,7 +9,7 @@ function get_erc20_contract_hash () { local ACCOUNT_KEY=${1} - $(get_path_to_client) query-state \ + $(get_path_to_client) query-global-state \ --node-address "$(get_node_address_rpc)" \ --state-root-hash "$(get_state_root_hash)" \ --key "$ACCOUNT_KEY" \ @@ -28,7 +28,7 @@ function get_erc20_contract_key_value () local QUERY_KEY=${1} local QUERY_PATH=${2} - $(get_path_to_client) query-state \ + $(get_path_to_client) query-global-state \ --node-address "$(get_node_address_rpc)" \ --state-root-hash "$(get_state_root_hash)" \ --key "$QUERY_KEY" \ diff --git a/utils/nctl/sh/contracts-kv/utils.sh b/utils/nctl/sh/contracts-kv/utils.sh index f68bd37f75..6d3fdcbfbf 100644 --- a/utils/nctl/sh/contracts-kv/utils.sh +++ b/utils/nctl/sh/contracts-kv/utils.sh @@ -9,7 +9,7 @@ function get_kv_contract_hash () { local ACCOUNT_KEY=${1} - $(get_path_to_client) query-state \ + $(get_path_to_client) query-global-state \ --node-address "$(get_node_address_rpc)" \ --state-root-hash "$(get_state_root_hash)" \ --key "$ACCOUNT_KEY" \ @@ -28,7 +28,7 @@ function get_kv_contract_key_value () local QUERY_KEY=${1} local QUERY_PATH=${2} - $(get_path_to_client) query-state \ + $(get_path_to_client) query-global-state \ --node-address "$(get_node_address_rpc)" \ --state-root-hash "$(get_state_root_hash)" \ --key "$QUERY_KEY" \ diff --git a/utils/nctl/sh/views/view_chain_account.sh b/utils/nctl/sh/views/view_chain_account.sh index df38cded04..b4f1f0b056 100644 --- a/utils/nctl/sh/views/view_chain_account.sh +++ b/utils/nctl/sh/views/view_chain_account.sh @@ -23,7 +23,7 @@ source "$NCTL"/sh/utils/main.sh NODE_ADDRESS=$(get_node_address_rpc) STATE_ROOT_HASH=${STATE_ROOT_HASH:-$(get_state_root_hash)} -$(get_path_to_client) query-state \ +$(get_path_to_client) query-global-state \ --node-address "$NODE_ADDRESS" \ --state-root-hash "$STATE_ROOT_HASH" \ --key "$ACCOUNT_KEY" \