Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add "query_global_state" query RPC to node #1645

Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
36 changes: 31 additions & 5 deletions client/lib/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
darthsiroftardis marked this conversation as resolved.
Show resolved Hide resolved
pub use cl_type::help;
pub use deploy::ListDeploysResult;
use deploy::{DeployExt, DeployParams};
Expand Down Expand Up @@ -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<JsonRpc> {
RpcCall::new(maybe_rpc_id, node_address, verbosity_level).query_global_state(
state_identifier,
hash,
global_state_str_params,
key,
path,
)
Expand Down Expand Up @@ -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.
darthsiroftardis marked this conversation as resolved.
Show resolved Hide resolved
pub hash_value: &'a str,
}

impl<'a> TryInto<GlobalStateIdentifier> for GlobalStateStrParams<'a> {
type Error = Error;

fn try_into(self) -> Result<GlobalStateIdentifier> {
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
Expand Down
44 changes: 5 additions & 39 deletions client/lib/rpc.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::{convert::TryFrom, fs::File};
use std::fs::File;

use futures::executor;
use jsonrpc_lite::{Id, JsonRpc, Params};
Expand Down Expand Up @@ -32,37 +32,16 @@ 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 {
/// Transfer to another account.
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<Self, Self::Error> {
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 {
Expand Down Expand Up @@ -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<JsonRpc> {
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) {
Expand Down Expand Up @@ -340,18 +318,6 @@ impl RpcCall {
}
}

fn state_identifier(state_identifier: &str, hash: &str) -> Result<GlobalStateIdentifier> {
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<JsonRpc> {
let url = format!("{}/{}", self.node_address, RPC_API_PATH);
let rpc_req = JsonRpc::request_with_params(self.rpc_id, method, params);
Expand Down
90 changes: 54 additions & 36 deletions client/src/global_state_query.rs
Original file line number Diff line number Diff line change
@@ -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")
darthsiroftardis marked this conversation as resolved.
Show resolved Hide resolved
}

impl<'a, 'b> ClientCommand<'a, 'b> for QueryGlobalState {
Expand All @@ -82,27 +96,31 @@ 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<Success, Error> {
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);

casper_client::global_state_query(
maybe_rpc_id,
node_address,
verbosity_level,
state_identifier,
hash,
global_state_str_params,
&key,
path,
)
Expand Down