#![doc(
html_root_url = "https://docs.rs/casper-client/5.0.1",
html_favicon_url = "https://raw.githubusercontent.com/casper-network/casper-node/master/images/CasperLabs_Logo_Favicon_RGB_50px.png",
html_logo_url = "https://raw.githubusercontent.com/casper-network/casper-node/master/images/CasperLabs_Logo_Symbol_RGB.png",
test(attr(forbid(warnings)))
)]
#![warn(
missing_docs,
trivial_casts,
trivial_numeric_casts,
unused_qualifications
)]
#![allow(clippy::result_large_err)]
pub mod cli;
mod error;
mod json_rpc;
#[cfg(feature = "std-fs-io")]
pub mod keygen;
#[cfg(any(feature = "std-fs-io", test))]
mod output_kind;
pub mod rpcs;
pub mod types;
mod validation;
mod verbosity;
mod verification;
mod verification_types;
extern crate alloc;
#[cfg(any(feature = "std-fs-io", test))]
use std::{
env::current_dir,
fs,
io::{Cursor, Read, Write},
path::Path,
};
#[cfg(feature = "std-fs-io")]
use serde::Serialize;
#[cfg(any(feature = "std-fs-io", test))]
use casper_types::SecretKey;
#[cfg(doc)]
use casper_types::{account::Account, Block, StoredValue, Transfer};
use casper_types::{
Deploy, DeployHash, Digest, Key, PublicKey, Transaction, TransactionHash, URef,
};
#[cfg(any(feature = "std-fs-io", test))]
use base64::{engine::general_purpose::STANDARD, Engine};
pub use error::Error;
use json_rpc::JsonRpcCall;
pub use json_rpc::{JsonRpcId, SuccessResponse};
#[cfg(any(feature = "std-fs-io", test))]
pub use output_kind::OutputKind;
use rpcs::{
common::{BlockIdentifier, GlobalStateIdentifier},
results::{
GetAccountResult, GetAddressableEntityResult, GetAuctionInfoResult, GetBalanceResult,
GetBlockResult, GetBlockTransfersResult, GetChainspecResult, GetDeployResult,
GetDictionaryItemResult, GetEraInfoResult, GetEraSummaryResult, GetNodeStatusResult,
GetPeersResult, GetRewardResult, GetStateRootHashResult, GetTransactionResult,
GetValidatorChangesResult, ListRpcsResult, PutDeployResult, PutTransactionResult,
QueryBalanceDetailsResult, QueryBalanceResult, QueryGlobalStateResult,
SpeculativeExecResult, SpeculativeExecTxnResult,
},
v2_0_0::{
get_account::{AccountIdentifier, GetAccountParams, GET_ACCOUNT_METHOD},
get_auction_info::{GetAuctionInfoParams, GET_AUCTION_INFO_METHOD},
get_balance::{GetBalanceParams, GET_BALANCE_METHOD},
get_block::{GetBlockParams, GET_BLOCK_METHOD},
get_block_transfers::{GetBlockTransfersParams, GET_BLOCK_TRANSFERS_METHOD},
get_chainspec::GET_CHAINSPEC_METHOD,
get_deploy::{GetDeployParams, GET_DEPLOY_METHOD},
get_dictionary_item::{GetDictionaryItemParams, GET_DICTIONARY_ITEM_METHOD},
get_entity::{EntityIdentifier, GetAddressableEntityParams, GET_ENTITY_METHOD},
get_era_info::{GetEraInfoParams, GET_ERA_INFO_METHOD},
get_era_summary::{GetEraSummaryParams, GET_ERA_SUMMARY_METHOD},
get_node_status::GET_NODE_STATUS_METHOD,
get_peers::GET_PEERS_METHOD,
get_reward::{GetRewardParams, GET_REWARD_METHOD},
get_state_root_hash::{GetStateRootHashParams, GET_STATE_ROOT_HASH_METHOD},
get_transaction::{GetTransactionParams, GET_TRANSACTION_METHOD},
get_validator_changes::GET_VALIDATOR_CHANGES_METHOD,
list_rpcs::LIST_RPCS_METHOD,
put_deploy::{PutDeployParams, PUT_DEPLOY_METHOD},
put_transaction::{PutTransactionParams, PUT_TRANSACTION_METHOD},
query_balance::{PurseIdentifier, QueryBalanceParams, QUERY_BALANCE_METHOD},
query_balance_details::{QueryBalanceDetailsParams, QUERY_BALANCE_DETAILS_METHOD},
query_global_state::{QueryGlobalStateParams, QUERY_GLOBAL_STATE_METHOD},
speculative_exec::{SpeculativeExecParams, SPECULATIVE_EXEC_METHOD},
speculative_exec_transaction::{SpeculativeExecTxnParams, SPECULATIVE_EXEC_TXN_METHOD},
},
DictionaryItemIdentifier, EraIdentifier,
};
pub use validation::ValidateResponseError;
pub use verbosity::Verbosity;
pub use verification::{build_archive, send_verification_request};
#[cfg(any(feature = "std-fs-io", test))]
use verification_types::VerificationDetails;
pub const MAX_SERIALIZED_SIZE_OF_DEPLOY: u32 = 1_024 * 1_024;
#[deprecated(since = "3.0.0", note = "use `put_transaction` instead")]
pub async fn put_deploy(
rpc_id: JsonRpcId,
node_address: &str,
verbosity: Verbosity,
deploy: Deploy,
) -> Result<SuccessResponse<PutDeployResult>, Error> {
JsonRpcCall::new(rpc_id, node_address, verbosity)?
.send_request(PUT_DEPLOY_METHOD, Some(PutDeployParams::new(deploy)))
.await
}
pub async fn put_transaction(
rpc_id: JsonRpcId,
node_address: &str,
verbosity: Verbosity,
transaction: Transaction,
) -> Result<SuccessResponse<PutTransactionResult>, Error> {
JsonRpcCall::new(rpc_id, node_address, verbosity)?
.send_request(
PUT_TRANSACTION_METHOD,
Some(PutTransactionParams::new(transaction)),
)
.await
}
#[deprecated(since = "3.0.0", note = "use `speculative_exec_txn` instead")]
pub async fn speculative_exec(
rpc_id: JsonRpcId,
node_address: &str,
verbosity: Verbosity,
deploy: Deploy,
) -> Result<SuccessResponse<SpeculativeExecResult>, Error> {
JsonRpcCall::new(rpc_id, node_address, verbosity)?
.send_request(
SPECULATIVE_EXEC_METHOD,
Some(SpeculativeExecParams::new(deploy)),
)
.await
}
pub async fn speculative_exec_txn(
rpc_id: JsonRpcId,
node_address: &str,
verbosity: Verbosity,
transaction: Transaction,
) -> Result<SuccessResponse<SpeculativeExecTxnResult>, Error> {
JsonRpcCall::new(rpc_id, node_address, verbosity)?
.send_request(
SPECULATIVE_EXEC_TXN_METHOD,
Some(SpeculativeExecTxnParams::new(transaction)),
)
.await
}
#[deprecated(since = "3.0.0", note = "use `output_transaction` instead")]
#[cfg(any(feature = "std-fs-io", test))]
pub fn output_deploy(output: OutputKind, deploy: &Deploy) -> Result<(), Error> {
write_deploy(deploy, output.get()?)?;
output.commit()
}
#[cfg(any(feature = "std-fs-io", test))]
pub fn output_transaction(output: OutputKind, transaction: &Transaction) -> Result<(), Error> {
write_transaction(transaction, output.get()?)?;
output.commit()
}
#[deprecated(since = "3.0.0", note = "use `read_transaction_file` instead")]
#[cfg(any(feature = "std-fs-io", test))]
pub fn read_deploy_file<P: AsRef<Path>>(deploy_path: P) -> Result<Deploy, Error> {
let input = fs::read(deploy_path.as_ref()).map_err(|error| Error::IoError {
context: format!(
"unable to read deploy file at '{}'",
deploy_path.as_ref().display()
),
error,
})?;
read_deploy(Cursor::new(input))
}
#[cfg(any(feature = "std-fs-io", test))]
pub fn read_transaction_file<P: AsRef<Path>>(transaction_path: P) -> Result<Transaction, Error> {
let input = fs::read(transaction_path.as_ref()).map_err(|error| Error::IoError {
context: format!(
"unable to read transaction file at '{}'",
transaction_path.as_ref().display()
),
error,
})?;
read_transaction(Cursor::new(input))
}
#[deprecated(since = "3.0.0", note = "use `sign_transaction_file` instead")]
#[cfg(any(feature = "std-fs-io", test))]
pub fn sign_deploy_file<P: AsRef<Path>>(
input_path: P,
secret_key: &SecretKey,
output: OutputKind,
) -> Result<(), Error> {
#[allow(deprecated)]
let mut deploy = read_deploy_file(input_path)?;
deploy.sign(secret_key);
deploy.is_valid_size(MAX_SERIALIZED_SIZE_OF_DEPLOY)?;
write_deploy(&deploy, output.get()?)?;
output.commit()
}
#[cfg(any(feature = "std-fs-io", test))]
pub fn sign_transaction_file<P: AsRef<Path>>(
input_path: P,
secret_key: &SecretKey,
output: OutputKind,
) -> Result<(), Error> {
let mut transaction = read_transaction_file(input_path)?;
transaction.sign(secret_key);
write_transaction(&transaction, output.get()?)?;
output.commit()
}
pub async fn get_deploy(
rpc_id: JsonRpcId,
node_address: &str,
verbosity: Verbosity,
deploy_hash: DeployHash,
finalized_approvals: bool,
) -> Result<SuccessResponse<GetDeployResult>, Error> {
JsonRpcCall::new(rpc_id, node_address, verbosity)?
.send_request(
GET_DEPLOY_METHOD,
Some(GetDeployParams::new(deploy_hash, finalized_approvals)),
)
.await
}
pub async fn get_transaction(
rpc_id: JsonRpcId,
node_address: &str,
verbosity: Verbosity,
transaction_hash: TransactionHash,
finalized_approvals: bool,
) -> Result<SuccessResponse<GetTransactionResult>, Error> {
JsonRpcCall::new(rpc_id, node_address, verbosity)?
.send_request(
GET_TRANSACTION_METHOD,
Some(GetTransactionParams::new(
transaction_hash,
finalized_approvals,
)),
)
.await
}
pub async fn get_block(
rpc_id: JsonRpcId,
node_address: &str,
verbosity: Verbosity,
maybe_block_identifier: Option<BlockIdentifier>,
) -> Result<SuccessResponse<GetBlockResult>, Error> {
let params = maybe_block_identifier.map(GetBlockParams::new);
let success_response = JsonRpcCall::new(rpc_id, node_address, verbosity)?
.send_request(GET_BLOCK_METHOD, params)
.await?;
validation::validate_get_block_result(maybe_block_identifier, &success_response.result)?;
Ok(success_response)
}
pub async fn get_block_transfers(
rpc_id: JsonRpcId,
node_address: &str,
verbosity: Verbosity,
maybe_block_identifier: Option<BlockIdentifier>,
) -> Result<SuccessResponse<GetBlockTransfersResult>, Error> {
let params = maybe_block_identifier.map(GetBlockTransfersParams::new);
JsonRpcCall::new(rpc_id, node_address, verbosity)?
.send_request(GET_BLOCK_TRANSFERS_METHOD, params)
.await
}
pub async fn get_state_root_hash(
rpc_id: JsonRpcId,
node_address: &str,
verbosity: Verbosity,
maybe_block_identifier: Option<BlockIdentifier>,
) -> Result<SuccessResponse<GetStateRootHashResult>, Error> {
let params = maybe_block_identifier.map(GetStateRootHashParams::new);
JsonRpcCall::new(rpc_id, node_address, verbosity)?
.send_request(GET_STATE_ROOT_HASH_METHOD, params)
.await
}
pub async fn get_era_summary(
rpc_id: JsonRpcId,
node_address: &str,
verbosity: Verbosity,
maybe_block_identifier: Option<BlockIdentifier>,
) -> Result<SuccessResponse<GetEraSummaryResult>, Error> {
let params = maybe_block_identifier.map(GetEraSummaryParams::new);
JsonRpcCall::new(rpc_id, node_address, verbosity)?
.send_request(GET_ERA_SUMMARY_METHOD, params)
.await
}
pub async fn query_global_state(
rpc_id: JsonRpcId,
node_address: &str,
verbosity: Verbosity,
global_state_identifier: Option<GlobalStateIdentifier>,
key: Key,
path: Vec<String>,
) -> Result<SuccessResponse<QueryGlobalStateResult>, Error> {
let params = QueryGlobalStateParams::new(global_state_identifier, key, path);
JsonRpcCall::new(rpc_id, node_address, verbosity)?
.send_request(QUERY_GLOBAL_STATE_METHOD, Some(params))
.await
}
pub async fn query_balance(
rpc_id: JsonRpcId,
node_address: &str,
verbosity: Verbosity,
maybe_global_state_identifier: Option<GlobalStateIdentifier>,
purse_identifier: PurseIdentifier,
) -> Result<SuccessResponse<QueryBalanceResult>, Error> {
let params = QueryBalanceParams::new(maybe_global_state_identifier, purse_identifier);
JsonRpcCall::new(rpc_id, node_address, verbosity)?
.send_request(QUERY_BALANCE_METHOD, Some(params))
.await
}
pub async fn query_balance_details(
rpc_id: JsonRpcId,
node_address: &str,
verbosity: Verbosity,
maybe_global_state_identifier: Option<GlobalStateIdentifier>,
purse_identifier: PurseIdentifier,
) -> Result<SuccessResponse<QueryBalanceDetailsResult>, Error> {
let params = QueryBalanceDetailsParams::new(maybe_global_state_identifier, purse_identifier);
JsonRpcCall::new(rpc_id, node_address, verbosity)?
.send_request(QUERY_BALANCE_DETAILS_METHOD, Some(params))
.await
}
pub async fn get_dictionary_item(
rpc_id: JsonRpcId,
node_address: &str,
verbosity: Verbosity,
state_root_hash: Digest,
dictionary_item_identifier: DictionaryItemIdentifier,
) -> Result<SuccessResponse<GetDictionaryItemResult>, Error> {
let params = GetDictionaryItemParams::new(state_root_hash, dictionary_item_identifier);
JsonRpcCall::new(rpc_id, node_address, verbosity)?
.send_request(GET_DICTIONARY_ITEM_METHOD, Some(params))
.await
}
pub async fn get_balance(
rpc_id: JsonRpcId,
node_address: &str,
verbosity: Verbosity,
state_root_hash: Digest,
purse: URef,
) -> Result<SuccessResponse<GetBalanceResult>, Error> {
let params = GetBalanceParams::new(state_root_hash, purse);
JsonRpcCall::new(rpc_id, node_address, verbosity)?
.send_request(GET_BALANCE_METHOD, Some(params))
.await
}
pub async fn get_account(
rpc_id: JsonRpcId,
node_address: &str,
verbosity: Verbosity,
maybe_block_identifier: Option<BlockIdentifier>,
account_identifier: AccountIdentifier,
) -> Result<SuccessResponse<GetAccountResult>, Error> {
let params = GetAccountParams::new(account_identifier, maybe_block_identifier);
JsonRpcCall::new(rpc_id, node_address, verbosity)?
.send_request(GET_ACCOUNT_METHOD, Some(params))
.await
}
pub async fn get_entity(
rpc_id: JsonRpcId,
node_address: &str,
verbosity: Verbosity,
maybe_block_identifier: Option<BlockIdentifier>,
entity_identifier: EntityIdentifier,
) -> Result<SuccessResponse<GetAddressableEntityResult>, Error> {
let params = GetAddressableEntityParams::new(entity_identifier, maybe_block_identifier);
JsonRpcCall::new(rpc_id, node_address, verbosity)?
.send_request(GET_ENTITY_METHOD, Some(params))
.await
}
pub async fn get_reward(
rpc_id: JsonRpcId,
node_address: &str,
verbosity: Verbosity,
maybe_era_identifier: Option<EraIdentifier>,
validator: PublicKey,
delegator: Option<PublicKey>,
) -> Result<SuccessResponse<GetRewardResult>, Error> {
let params = GetRewardParams::new(maybe_era_identifier, validator, delegator);
JsonRpcCall::new(rpc_id, node_address, verbosity)?
.send_request(GET_REWARD_METHOD, Some(params))
.await
}
pub async fn get_auction_info(
rpc_id: JsonRpcId,
node_address: &str,
verbosity: Verbosity,
maybe_block_identifier: Option<BlockIdentifier>,
) -> Result<SuccessResponse<GetAuctionInfoResult>, Error> {
let params = maybe_block_identifier.map(GetAuctionInfoParams::new);
JsonRpcCall::new(rpc_id, node_address, verbosity)?
.send_request(GET_AUCTION_INFO_METHOD, params)
.await
}
pub async fn get_validator_changes(
rpc_id: JsonRpcId,
node_address: &str,
verbosity: Verbosity,
) -> Result<SuccessResponse<GetValidatorChangesResult>, Error> {
JsonRpcCall::new(rpc_id, node_address, verbosity)?
.send_request::<(), _>(GET_VALIDATOR_CHANGES_METHOD, None)
.await
}
pub async fn get_peers(
rpc_id: JsonRpcId,
node_address: &str,
verbosity: Verbosity,
) -> Result<SuccessResponse<GetPeersResult>, Error> {
JsonRpcCall::new(rpc_id, node_address, verbosity)?
.send_request::<(), _>(GET_PEERS_METHOD, None)
.await
}
pub async fn get_node_status(
rpc_id: JsonRpcId,
node_address: &str,
verbosity: Verbosity,
) -> Result<SuccessResponse<GetNodeStatusResult>, Error> {
JsonRpcCall::new(rpc_id, node_address, verbosity)?
.send_request::<(), _>(GET_NODE_STATUS_METHOD, None)
.await
}
pub async fn get_chainspec(
rpc_id: JsonRpcId,
node_address: &str,
verbosity: Verbosity,
) -> Result<SuccessResponse<GetChainspecResult>, Error> {
JsonRpcCall::new(rpc_id, node_address, verbosity)?
.send_request::<(), _>(GET_CHAINSPEC_METHOD, None)
.await
}
pub async fn list_rpcs(
rpc_id: JsonRpcId,
node_address: &str,
verbosity: Verbosity,
) -> Result<SuccessResponse<ListRpcsResult>, Error> {
JsonRpcCall::new(rpc_id, node_address, verbosity)?
.send_request::<(), _>(LIST_RPCS_METHOD, None)
.await
}
#[cfg(feature = "std-fs-io")]
pub(crate) fn json_pretty_print<T: ?Sized + Serialize>(
value: &T,
verbosity: Verbosity,
) -> Result<(), Error> {
let output = match verbosity {
Verbosity::Low => return Ok(()),
Verbosity::Medium => casper_types::json_pretty_print(value),
Verbosity::High => serde_json::to_string_pretty(value),
}
.map_err(|error| Error::FailedToEncodeToJson {
context: "in json_pretty_print",
error,
})?;
println!("{}", output);
Ok(())
}
#[cfg(any(feature = "std-fs-io", test))]
fn write_deploy<W: Write>(deploy: &Deploy, mut output: W) -> Result<(), Error> {
let content =
serde_json::to_string_pretty(deploy).map_err(|error| Error::FailedToEncodeToJson {
context: "writing deploy",
error,
})?;
output
.write_all(content.as_bytes())
.map_err(|error| Error::IoError {
context: "unable to write deploy".to_owned(),
error,
})
}
#[cfg(any(feature = "std-fs-io", test))]
fn write_transaction<W: Write>(transaction: &Transaction, mut output: W) -> Result<(), Error> {
let content =
serde_json::to_string_pretty(transaction).map_err(|error| Error::FailedToEncodeToJson {
context: "writing transaction",
error,
})?;
output
.write_all(content.as_bytes())
.map_err(|error| Error::IoError {
context: "unable to write transaction".to_owned(),
error,
})
}
#[cfg(any(feature = "std-fs-io", test))]
fn read_deploy<R: Read>(input: R) -> Result<Deploy, Error> {
let deploy: Deploy =
serde_json::from_reader(input).map_err(|error| Error::FailedToDecodeFromJson {
context: "reading deploy",
error,
})?;
deploy.is_valid_size(MAX_SERIALIZED_SIZE_OF_DEPLOY)?;
Ok(deploy)
}
#[cfg(any(feature = "std-fs-io", test))]
fn read_transaction<R: Read>(input: R) -> Result<Transaction, Error> {
let transaction: Transaction =
serde_json::from_reader(input).map_err(|error| Error::FailedToDecodeFromJson {
context: "reading transaction",
error,
})?;
Ok(transaction)
}
#[deprecated(
since = "2.0.0",
note = "prefer 'get_era_summary' as it doesn't require a switch block"
)]
pub async fn get_era_info(
rpc_id: JsonRpcId,
node_address: &str,
verbosity: Verbosity,
maybe_block_identifier: Option<BlockIdentifier>,
) -> Result<SuccessResponse<GetEraInfoResult>, Error> {
let params = maybe_block_identifier.map(GetEraInfoParams::new);
JsonRpcCall::new(rpc_id, node_address, verbosity)?
.send_request(GET_ERA_INFO_METHOD, params)
.await
}
#[cfg(any(feature = "std-fs-io", test))]
pub async fn verify_contract(
hash_str: &str,
verification_url_base_path: &str,
project_path: Option<&str>,
verbosity: Verbosity,
) -> Result<VerificationDetails, Error> {
if verbosity == Verbosity::Medium || verbosity == Verbosity::High {
println!("Hash: {hash_str}");
println!("Verification service base path: {verification_url_base_path}",);
}
let project_path = match project_path {
Some(path) => Path::new(path).to_path_buf(),
None => match current_dir() {
Ok(path) => path,
Err(error) => {
eprintln!("Cannot get current directory: {error}");
return Err(Error::ContractVerificationFailed);
}
},
};
let archive = match build_archive(&project_path) {
Ok(archive) => {
if verbosity == Verbosity::Medium || verbosity == Verbosity::High {
println!("Created project archive (size: {})", archive.len());
}
archive
}
Err(error) => {
eprintln!("Cannot create project archive: {error}");
return Err(Error::ContractVerificationFailed);
}
};
send_verification_request(
hash_str,
verification_url_base_path,
STANDARD.encode(&archive),
verbosity,
)
.await
}