casper_client/
lib.rs

1//! # Casper client library
2//!
3//! The crate provides functions for interacting with a Casper network.
4//!
5//! Most of the functions involve sending a JSON-RPC request to a specified node on the chosen
6//! network, and providing the RPC response.
7//!
8//! # Common Parameters
9//!
10//! Many of the functions have similar parameters.  Descriptions for these common ones follow:
11//!
12//! * <code>rpc_id: <a href="enum.JsonRpcId.html">JsonRpcId</a></code> - The JSON-RPC identifier,
13//!   applied to the request and returned in the response.
14//! * <code>node_address: &<a href="https://doc.rust-lang.org/std/primitive.str.html">str</a></code> -
15//!   The hostname or IP and port of the server, e.g. `http://127.0.0.1:7777`.
16//! * <code>verbosity: <a href="enum.Verbosity.html">Verbosity</a></code> - When `Low`, nothing is
17//!   printed to stdout.  For `Medium`, the request and response are printed to `stdout` with long
18//!   string fields (e.g. hex-formatted raw Wasm bytes) shortened to a string indicating the char
19//!   count of the field.  `High` verbosity is the same as `Medium` except without abbreviation of
20//!   long fields.
21//! * <code>maybe_block_identifier: <a href="https://doc.rust-lang.org/core/option/enum.Option.html">Option</a><<a href="rpcs/common/enum.BlockIdentifier.html">BlockIdentifier</a>></code> -
22//!   The identifier of the [`Block`] to use, either block height or block hash.  If `None`, the
23//!   latest `Block` known on the server will be used.
24
25#![doc(
26    html_root_url = "https://docs.rs/casper-client/2.0.0",
27    html_favicon_url = "https://raw.githubusercontent.com/CasperLabs/casper-node/master/images/CasperLabs_Logo_Favicon_RGB_50px.png",
28    html_logo_url = "https://raw.githubusercontent.com/CasperLabs/casper-node/master/images/CasperLabs_Logo_Symbol_RGB.png",
29    test(attr(forbid(warnings)))
30)]
31#![warn(
32    missing_docs,
33    trivial_casts,
34    trivial_numeric_casts,
35    unused_qualifications
36)]
37
38pub mod cli;
39mod error;
40mod json_rpc;
41pub mod keygen;
42mod output_kind;
43pub mod rpcs;
44mod transfer_target;
45pub mod types;
46mod validation;
47mod verbosity;
48
49use std::{
50    fs,
51    io::{Cursor, Read, Write},
52    path::Path,
53};
54
55use serde::Serialize;
56
57use casper_hashing::Digest;
58#[cfg(doc)]
59use casper_types::Transfer;
60use casper_types::{Key, PublicKey, SecretKey, URef};
61
62pub use error::Error;
63use json_rpc::JsonRpcCall;
64pub use json_rpc::{JsonRpcId, SuccessResponse};
65pub use output_kind::OutputKind;
66use rpcs::{
67    common::{BlockIdentifier, GlobalStateIdentifier},
68    results::{
69        GetAccountResult, GetAuctionInfoResult, GetBalanceResult, GetBlockResult,
70        GetBlockTransfersResult, GetChainspecResult, GetDeployResult, GetDictionaryItemResult,
71        GetEraInfoResult, GetEraSummaryResult, GetNodeStatusResult, GetPeersResult,
72        GetStateRootHashResult, GetValidatorChangesResult, ListRpcsResult, PutDeployResult,
73        QueryBalanceResult, QueryGlobalStateResult, SpeculativeExecResult,
74    },
75    v1_5_0::{
76        get_account::{GetAccountParams, GET_ACCOUNT_METHOD},
77        get_auction_info::{GetAuctionInfoParams, GET_AUCTION_INFO_METHOD},
78        get_balance::{GetBalanceParams, GET_BALANCE_METHOD},
79        get_block::{GetBlockParams, GET_BLOCK_METHOD},
80        get_block_transfers::{GetBlockTransfersParams, GET_BLOCK_TRANSFERS_METHOD},
81        get_chainspec::GET_CHAINSPEC_METHOD,
82        get_deploy::{GetDeployParams, GET_DEPLOY_METHOD},
83        get_dictionary_item::{GetDictionaryItemParams, GET_DICTIONARY_ITEM_METHOD},
84        get_era_info::{GetEraInfoParams, GET_ERA_INFO_METHOD},
85        get_era_summary::{GetEraSummaryParams, GET_ERA_SUMMARY_METHOD},
86        get_node_status::GET_NODE_STATUS_METHOD,
87        get_peers::GET_PEERS_METHOD,
88        get_state_root_hash::{GetStateRootHashParams, GET_STATE_ROOT_HASH_METHOD},
89        get_validator_changes::GET_VALIDATOR_CHANGES_METHOD,
90        list_rpcs::LIST_RPCS_METHOD,
91        put_deploy::{PutDeployParams, PUT_DEPLOY_METHOD},
92        query_balance::{PurseIdentifier, QueryBalanceParams, QUERY_BALANCE_METHOD},
93        query_global_state::{QueryGlobalStateParams, QUERY_GLOBAL_STATE_METHOD},
94        speculative_exec::{SpeculativeExecParams, SPECULATIVE_EXEC_METHOD},
95    },
96    DictionaryItemIdentifier,
97};
98pub use transfer_target::TransferTarget;
99#[cfg(doc)]
100use types::{Account, Block, StoredValue};
101use types::{Deploy, DeployHash, MAX_SERIALIZED_SIZE_OF_DEPLOY};
102pub use validation::ValidateResponseError;
103pub use verbosity::Verbosity;
104
105/// Puts a [`Deploy`] to the network for execution.
106///
107/// Sends a JSON-RPC `account_put_deploy` request to the specified node.
108///
109/// For details of the parameters, see [the module docs](crate#common-parameters).
110pub async fn put_deploy(
111    rpc_id: JsonRpcId,
112    node_address: &str,
113    verbosity: Verbosity,
114    deploy: Deploy,
115) -> Result<SuccessResponse<PutDeployResult>, Error> {
116    JsonRpcCall::new(rpc_id, node_address, verbosity)
117        .send_request(PUT_DEPLOY_METHOD, Some(PutDeployParams::new(deploy)))
118        .await
119}
120
121/// Puts a [`Deploy`] to a single node for speculative execution on that node only.
122///
123/// Sends a JSON-RPC `speculative_exec` request to the specified node.
124///
125/// For details of the parameters, see [the module docs](crate#common-parameters).
126pub async fn speculative_exec(
127    rpc_id: JsonRpcId,
128    node_address: &str,
129    block_identifier: Option<BlockIdentifier>,
130    verbosity: Verbosity,
131    deploy: Deploy,
132) -> Result<SuccessResponse<SpeculativeExecResult>, Error> {
133    JsonRpcCall::new(rpc_id, node_address, verbosity)
134        .send_request(
135            SPECULATIVE_EXEC_METHOD,
136            Some(SpeculativeExecParams::new(block_identifier, deploy)),
137        )
138        .await
139}
140
141/// Outputs a [`Deploy`] to a file or stdout.
142///
143/// As a file, the `Deploy` can subsequently be signed by other parties using [`sign_deploy_file`]
144/// and then read and sent to the network for execution using [`read_deploy_file`] and
145/// [`put_deploy`] respectively.
146///
147/// `output` specifies the output file and corresponding overwrite behaviour, or if
148/// `OutputKind::Stdout`, causes the `Deploy` to be printed `stdout`.
149pub fn output_deploy(output: OutputKind, deploy: &Deploy) -> Result<(), Error> {
150    write_deploy(deploy, output.get()?)?;
151    output.commit()
152}
153
154/// Reads a previously-saved [`Deploy`] from a file.
155pub fn read_deploy_file<P: AsRef<Path>>(deploy_path: P) -> Result<Deploy, Error> {
156    let input = fs::read(deploy_path.as_ref()).map_err(|error| Error::IoError {
157        context: format!(
158            "unable to read deploy file at '{}'",
159            deploy_path.as_ref().display()
160        ),
161        error,
162    })?;
163    read_deploy(Cursor::new(input))
164}
165
166/// Reads a previously-saved [`Deploy`] from a file, cryptographically signs it, and outputs it
167/// to a file or stdout.
168///
169/// `output` specifies the output file and corresponding overwrite behaviour, or if
170/// `OutputKind::Stdout`, causes the `Deploy` to be printed `stdout`.
171///
172/// The same path can be specified for input and output, and if the operation fails, the original
173/// input file will be left unmodified.
174pub fn sign_deploy_file<P: AsRef<Path>>(
175    input_path: P,
176    secret_key: &SecretKey,
177    output: OutputKind,
178) -> Result<(), Error> {
179    let mut deploy = read_deploy_file(input_path)?;
180
181    deploy.sign(secret_key);
182    deploy.is_valid_size(MAX_SERIALIZED_SIZE_OF_DEPLOY)?;
183
184    write_deploy(&deploy, output.get()?)?;
185    output.commit()
186}
187
188/// Retrieves a [`Deploy`] and its metadata (i.e. execution results) from the network.
189///
190/// Sends a JSON-RPC `info_get_deploy` request to the specified node.
191///
192/// `finalized_approvals` defines whether to return the `Deploy` with its approvals as finalized by
193/// consensus of the validators on the network, or as originally received by the specified node.
194///
195/// For details of the other parameters, see [the module docs](crate#common-parameters).
196pub async fn get_deploy(
197    rpc_id: JsonRpcId,
198    node_address: &str,
199    verbosity: Verbosity,
200    deploy_hash: DeployHash,
201    finalized_approvals: bool,
202) -> Result<SuccessResponse<GetDeployResult>, Error> {
203    JsonRpcCall::new(rpc_id, node_address, verbosity)
204        .send_request(
205            GET_DEPLOY_METHOD,
206            Some(GetDeployParams::new(deploy_hash, finalized_approvals)),
207        )
208        .await
209}
210
211/// Retrieves a [`Block`] from the network.
212///
213/// Sends a JSON-RPC `chain_get_block` request to the specified node.
214///
215/// For details of the parameters, see [the module docs](crate#common-parameters).
216pub async fn get_block(
217    rpc_id: JsonRpcId,
218    node_address: &str,
219    verbosity: Verbosity,
220    maybe_block_identifier: Option<BlockIdentifier>,
221) -> Result<SuccessResponse<GetBlockResult>, Error> {
222    let params = maybe_block_identifier.map(GetBlockParams::new);
223    let success_response = JsonRpcCall::new(rpc_id, node_address, verbosity)
224        .send_request(GET_BLOCK_METHOD, params)
225        .await?;
226    validation::validate_get_block_result(maybe_block_identifier, &success_response.result)?;
227    Ok(success_response)
228}
229
230/// Retrieves all [`Transfer`] items for a given [`Block`].
231///
232/// Sends a JSON-RPC `chain_get_block_transfers` request to the specified node.
233///
234/// For details of the parameters, see [the module docs](crate#common-parameters).
235pub async fn get_block_transfers(
236    rpc_id: JsonRpcId,
237    node_address: &str,
238    verbosity: Verbosity,
239    maybe_block_identifier: Option<BlockIdentifier>,
240) -> Result<SuccessResponse<GetBlockTransfersResult>, Error> {
241    let params = maybe_block_identifier.map(GetBlockTransfersParams::new);
242    JsonRpcCall::new(rpc_id, node_address, verbosity)
243        .send_request(GET_BLOCK_TRANSFERS_METHOD, params)
244        .await
245}
246
247/// Retrieves a state root hash at a given [`Block`].
248///
249/// Sends a JSON-RPC `chain_get_state_root_hash` request to the specified node.
250///
251/// For details of the parameters, see [the module docs](crate#common-parameters).
252pub async fn get_state_root_hash(
253    rpc_id: JsonRpcId,
254    node_address: &str,
255    verbosity: Verbosity,
256    maybe_block_identifier: Option<BlockIdentifier>,
257) -> Result<SuccessResponse<GetStateRootHashResult>, Error> {
258    let params = maybe_block_identifier.map(GetStateRootHashParams::new);
259    JsonRpcCall::new(rpc_id, node_address, verbosity)
260        .send_request(GET_STATE_ROOT_HASH_METHOD, params)
261        .await
262}
263
264/// Retrieves era information from the network at a given [`Block`].
265///
266/// Sends a JSON-RPC `chain_get_era_summary` request to the specified node.
267///
268/// For details of the parameters, see [the module docs](crate#common-parameters).
269pub async fn get_era_summary(
270    rpc_id: JsonRpcId,
271    node_address: &str,
272    verbosity: Verbosity,
273    maybe_block_identifier: Option<BlockIdentifier>,
274) -> Result<SuccessResponse<GetEraSummaryResult>, Error> {
275    let params = maybe_block_identifier.map(GetEraSummaryParams::new);
276    JsonRpcCall::new(rpc_id, node_address, verbosity)
277        .send_request(GET_ERA_SUMMARY_METHOD, params)
278        .await
279}
280
281/// Retrieves a [`StoredValue`] from global state at a given [`Block`] or state root hash.
282///
283/// Sends a JSON-RPC `query_global_state` request to the specified node.
284///
285/// `key` specifies the key under which the value is stored in global state.
286///
287/// `path` defines the further path (if any) from `key` to navigate to during the query.  This is
288/// only applicable in the case where the value under `key` is an account or contract.  In this
289/// case, the first `path` element represents a name in the account/contract's named keys.  If that
290/// second `Key` also points to an account or contract, then a second path element can be added to
291/// continue the query into that account/contract's named keys.  This can continue up to the
292/// server's configured maximum query depth (5 by default).
293///
294/// For details of the other parameters, see [the module docs](crate#common-parameters).
295pub async fn query_global_state(
296    rpc_id: JsonRpcId,
297    node_address: &str,
298    verbosity: Verbosity,
299    global_state_identifier: GlobalStateIdentifier,
300    key: Key,
301    path: Vec<String>,
302) -> Result<SuccessResponse<QueryGlobalStateResult>, Error> {
303    let params = QueryGlobalStateParams::new(global_state_identifier, key, path);
304    JsonRpcCall::new(rpc_id, node_address, verbosity)
305        .send_request(QUERY_GLOBAL_STATE_METHOD, Some(params))
306        .await
307}
308
309/// Retrieves a purse's balance from global state at a given [`Block`] or state root hash.
310///
311/// Sends a JSON-RPC `query_balance` request to the specified node.
312///
313/// For details of the parameters, see [the module docs](crate#common-parameters).
314pub async fn query_balance(
315    rpc_id: JsonRpcId,
316    node_address: &str,
317    verbosity: Verbosity,
318    maybe_global_state_identifier: Option<GlobalStateIdentifier>,
319    purse_identifier: PurseIdentifier,
320) -> Result<SuccessResponse<QueryBalanceResult>, Error> {
321    let params = QueryBalanceParams::new(maybe_global_state_identifier, purse_identifier);
322    JsonRpcCall::new(rpc_id, node_address, verbosity)
323        .send_request(QUERY_BALANCE_METHOD, Some(params))
324        .await
325}
326
327/// Retrieves a [`StoredValue`] from a dictionary at a given state root hash.
328///
329/// Sends a JSON-RPC `state_get_dictionary_item` request to the specified node.
330///
331/// For details of the parameters, see [the module docs](crate#common-parameters).
332pub async fn get_dictionary_item(
333    rpc_id: JsonRpcId,
334    node_address: &str,
335    verbosity: Verbosity,
336    state_root_hash: Digest,
337    dictionary_item_identifier: DictionaryItemIdentifier,
338) -> Result<SuccessResponse<GetDictionaryItemResult>, Error> {
339    let params = GetDictionaryItemParams::new(state_root_hash, dictionary_item_identifier);
340    JsonRpcCall::new(rpc_id, node_address, verbosity)
341        .send_request(GET_DICTIONARY_ITEM_METHOD, Some(params))
342        .await
343}
344
345/// Retrieves a purse's balance at a given state root hash.
346///
347/// Sends a JSON-RPC `state_get_balance` request to the specified node.
348///
349/// For details of the parameters, see [the module docs](crate#common-parameters).
350pub async fn get_balance(
351    rpc_id: JsonRpcId,
352    node_address: &str,
353    verbosity: Verbosity,
354    state_root_hash: Digest,
355    purse: URef,
356) -> Result<SuccessResponse<GetBalanceResult>, Error> {
357    let params = GetBalanceParams::new(state_root_hash, purse);
358    JsonRpcCall::new(rpc_id, node_address, verbosity)
359        .send_request(GET_BALANCE_METHOD, Some(params))
360        .await
361}
362
363/// Retrieves an [`Account`] at a given [`Block`].
364///
365/// Sends a JSON-RPC `state_get_account_info` request to the specified node.
366///
367/// For details of the parameters, see [the module docs](crate#common-parameters).
368pub async fn get_account(
369    rpc_id: JsonRpcId,
370    node_address: &str,
371    verbosity: Verbosity,
372    maybe_block_identifier: Option<BlockIdentifier>,
373    account_identifier: PublicKey,
374) -> Result<SuccessResponse<GetAccountResult>, Error> {
375    let params = GetAccountParams::new(account_identifier, maybe_block_identifier);
376    JsonRpcCall::new(rpc_id, node_address, verbosity)
377        .send_request(GET_ACCOUNT_METHOD, Some(params))
378        .await
379}
380
381/// Retrieves the bids and validators at a given [`Block`].
382///
383/// Sends a JSON-RPC `state_get_auction_info` request to the specified node.
384///
385/// For details of the parameters, see [the module docs](crate#common-parameters).
386pub async fn get_auction_info(
387    rpc_id: JsonRpcId,
388    node_address: &str,
389    verbosity: Verbosity,
390    maybe_block_identifier: Option<BlockIdentifier>,
391) -> Result<SuccessResponse<GetAuctionInfoResult>, Error> {
392    let params = maybe_block_identifier.map(GetAuctionInfoParams::new);
393    JsonRpcCall::new(rpc_id, node_address, verbosity)
394        .send_request(GET_AUCTION_INFO_METHOD, params)
395        .await
396}
397
398/// Retrieves the status changes of the active validators on the network.
399///
400/// Sends a JSON-RPC `info_get_validator_changes` request to the specified node.
401///
402/// For details of the parameters, see [the module docs](crate#common-parameters).
403pub async fn get_validator_changes(
404    rpc_id: JsonRpcId,
405    node_address: &str,
406    verbosity: Verbosity,
407) -> Result<SuccessResponse<GetValidatorChangesResult>, Error> {
408    JsonRpcCall::new(rpc_id, node_address, verbosity)
409        .send_request::<(), _>(GET_VALIDATOR_CHANGES_METHOD, None)
410        .await
411}
412
413/// Retrieves the IDs and addresses of the specified node's peers.
414///
415/// Sends a JSON-RPC `info_get_peers` request to the specified node.
416///
417/// For details of the parameters, see [the module docs](crate#common-parameters).
418pub async fn get_peers(
419    rpc_id: JsonRpcId,
420    node_address: &str,
421    verbosity: Verbosity,
422) -> Result<SuccessResponse<GetPeersResult>, Error> {
423    JsonRpcCall::new(rpc_id, node_address, verbosity)
424        .send_request::<(), _>(GET_PEERS_METHOD, None)
425        .await
426}
427
428/// Retrieves the status of the specified node.
429///
430/// Sends a JSON-RPC `info_get_status` request to the specified node.
431///
432/// For details of the parameters, see [the module docs](crate#common-parameters).
433pub async fn get_node_status(
434    rpc_id: JsonRpcId,
435    node_address: &str,
436    verbosity: Verbosity,
437) -> Result<SuccessResponse<GetNodeStatusResult>, Error> {
438    JsonRpcCall::new(rpc_id, node_address, verbosity)
439        .send_request::<(), _>(GET_NODE_STATUS_METHOD, None)
440        .await
441}
442
443/// Retrieves the Chainspec of the network.
444///
445/// Sends a JSON-RPC `info_get_chainspec` request to the specified node.
446///
447/// For details of the parameters, see [the module docs](crate#common-parameters).
448pub async fn get_chainspec(
449    rpc_id: JsonRpcId,
450    node_address: &str,
451    verbosity: Verbosity,
452) -> Result<SuccessResponse<GetChainspecResult>, Error> {
453    JsonRpcCall::new(rpc_id, node_address, verbosity)
454        .send_request::<(), _>(GET_CHAINSPEC_METHOD, None)
455        .await
456}
457
458/// Retrieves the interface description (the schema including examples in OpenRPC format) of the
459/// JSON-RPC server's API.
460///
461/// Sends a JSON-RPC `rpc.discover` request to the specified node.
462///
463/// For details of the parameters, see [the module docs](crate#common-parameters).
464pub async fn list_rpcs(
465    rpc_id: JsonRpcId,
466    node_address: &str,
467    verbosity: Verbosity,
468) -> Result<SuccessResponse<ListRpcsResult>, Error> {
469    JsonRpcCall::new(rpc_id, node_address, verbosity)
470        .send_request::<(), _>(LIST_RPCS_METHOD, None)
471        .await
472}
473
474/// JSON-encode and pretty-print the given value to stdout at the given verbosity level.
475///
476/// When `verbosity` is `Low`, nothing is printed.  For `Medium`, the value is printed with long
477/// string fields shortened to a string indicating the character count of the field.  `High`
478/// verbosity is the same as `Medium` except without abbreviation of long fields.
479pub(crate) fn json_pretty_print<T: ?Sized + Serialize>(
480    value: &T,
481    verbosity: Verbosity,
482) -> Result<(), Error> {
483    let output = match verbosity {
484        Verbosity::Low => return Ok(()),
485        Verbosity::Medium => casper_types::json_pretty_print(value),
486        Verbosity::High => serde_json::to_string_pretty(value),
487    }
488    .map_err(|error| Error::FailedToEncodeToJson {
489        context: "in json_pretty_print",
490        error,
491    })?;
492    println!("{}", output);
493    Ok(())
494}
495
496fn write_deploy<W: Write>(deploy: &Deploy, mut output: W) -> Result<(), Error> {
497    let content =
498        serde_json::to_string_pretty(deploy).map_err(|error| Error::FailedToEncodeToJson {
499            context: "writing deploy",
500            error,
501        })?;
502    output
503        .write_all(content.as_bytes())
504        .map_err(|error| Error::IoError {
505            context: "unable to write deploy".to_owned(),
506            error,
507        })
508}
509
510fn read_deploy<R: Read>(input: R) -> Result<Deploy, Error> {
511    let deploy: Deploy =
512        serde_json::from_reader(input).map_err(|error| Error::FailedToDecodeFromJson {
513            context: "reading deploy",
514            error,
515        })?;
516    deploy.is_valid_size(MAX_SERIALIZED_SIZE_OF_DEPLOY)?;
517    Ok(deploy)
518}
519
520/// Retrieves era information from the network at a given switch [`Block`].
521///
522/// Sends a JSON-RPC `chain_get_era_info_by_switch_block` request to the specified node.
523///
524/// For details of the parameters, see [the module docs](crate#common-parameters).  Note that if the
525/// specified block is not a switch block then the response will have no era info.
526#[deprecated(
527    since = "2.0.0",
528    note = "prefer 'get_era_summary' as it doesn't require a switch block"
529)]
530pub async fn get_era_info(
531    rpc_id: JsonRpcId,
532    node_address: &str,
533    verbosity: Verbosity,
534    maybe_block_identifier: Option<BlockIdentifier>,
535) -> Result<SuccessResponse<GetEraInfoResult>, Error> {
536    let params = maybe_block_identifier.map(GetEraInfoParams::new);
537    JsonRpcCall::new(rpc_id, node_address, verbosity)
538        .send_request(GET_ERA_INFO_METHOD, params)
539        .await
540}