1#![doc(
26 html_root_url = "https://docs.rs/casper-client/3.1.0",
27 html_favicon_url = "https://raw.githubusercontent.com/casper-network/casper-node/master/images/CasperLabs_Logo_Favicon_RGB_50px.png",
28 html_logo_url = "https://raw.githubusercontent.com/casper-network/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;
41#[cfg(feature = "std-fs-io")]
42pub mod keygen;
43#[cfg(any(feature = "std-fs-io", test))]
44mod output_kind;
45pub mod rpcs;
46pub mod types;
47mod validation;
48mod verbosity;
49mod verification;
50mod verification_types;
51
52extern crate alloc;
53
54#[cfg(any(feature = "std-fs-io", test))]
55use std::{
56 env::current_dir,
57 fs,
58 io::{Cursor, Read, Write},
59 path::Path,
60};
61
62#[cfg(feature = "std-fs-io")]
63use serde::Serialize;
64
65#[cfg(any(feature = "std-fs-io", test))]
66use casper_types::SecretKey;
67#[cfg(doc)]
68use casper_types::{account::Account, Block, StoredValue, Transfer};
69use casper_types::{
70 Deploy, DeployHash, Digest, Key, PublicKey, Transaction, TransactionHash, URef,
71};
72
73#[cfg(any(feature = "std-fs-io", test))]
74use base64::{engine::general_purpose::STANDARD, Engine};
75pub use error::Error;
76use json_rpc::JsonRpcCall;
77pub use json_rpc::{JsonRpcId, SuccessResponse};
78#[cfg(any(feature = "std-fs-io", test))]
79pub use output_kind::OutputKind;
80use rpcs::{
81 common::{BlockIdentifier, GlobalStateIdentifier},
82 results::{
83 GetAccountResult, GetAddressableEntityResult, GetAuctionInfoResult, GetBalanceResult,
84 GetBlockResult, GetBlockTransfersResult, GetChainspecResult, GetDeployResult,
85 GetDictionaryItemResult, GetEraInfoResult, GetEraSummaryResult, GetNodeStatusResult,
86 GetPeersResult, GetRewardResult, GetStateRootHashResult, GetTransactionResult,
87 GetValidatorChangesResult, ListRpcsResult, PutDeployResult, PutTransactionResult,
88 QueryBalanceDetailsResult, QueryBalanceResult, QueryGlobalStateResult,
89 SpeculativeExecResult, SpeculativeExecTxnResult,
90 },
91 v2_0_0::{
92 get_account::{AccountIdentifier, GetAccountParams, GET_ACCOUNT_METHOD},
93 get_auction_info::{GetAuctionInfoParams, GET_AUCTION_INFO_METHOD},
94 get_balance::{GetBalanceParams, GET_BALANCE_METHOD},
95 get_block::{GetBlockParams, GET_BLOCK_METHOD},
96 get_block_transfers::{GetBlockTransfersParams, GET_BLOCK_TRANSFERS_METHOD},
97 get_chainspec::GET_CHAINSPEC_METHOD,
98 get_deploy::{GetDeployParams, GET_DEPLOY_METHOD},
99 get_dictionary_item::{GetDictionaryItemParams, GET_DICTIONARY_ITEM_METHOD},
100 get_entity::{EntityIdentifier, GetAddressableEntityParams, GET_ENTITY_METHOD},
101 get_era_info::{GetEraInfoParams, GET_ERA_INFO_METHOD},
102 get_era_summary::{GetEraSummaryParams, GET_ERA_SUMMARY_METHOD},
103 get_node_status::GET_NODE_STATUS_METHOD,
104 get_peers::GET_PEERS_METHOD,
105 get_reward::{GetRewardParams, GET_REWARD_METHOD},
106 get_state_root_hash::{GetStateRootHashParams, GET_STATE_ROOT_HASH_METHOD},
107 get_transaction::{GetTransactionParams, GET_TRANSACTION_METHOD},
108 get_validator_changes::GET_VALIDATOR_CHANGES_METHOD,
109 list_rpcs::LIST_RPCS_METHOD,
110 put_deploy::{PutDeployParams, PUT_DEPLOY_METHOD},
111 put_transaction::{PutTransactionParams, PUT_TRANSACTION_METHOD},
112 query_balance::{PurseIdentifier, QueryBalanceParams, QUERY_BALANCE_METHOD},
113 query_balance_details::{QueryBalanceDetailsParams, QUERY_BALANCE_DETAILS_METHOD},
114 query_global_state::{QueryGlobalStateParams, QUERY_GLOBAL_STATE_METHOD},
115 speculative_exec::{SpeculativeExecParams, SPECULATIVE_EXEC_METHOD},
116 speculative_exec_transaction::{SpeculativeExecTxnParams, SPECULATIVE_EXEC_TXN_METHOD},
117 },
118 DictionaryItemIdentifier, EraIdentifier,
119};
120pub use validation::ValidateResponseError;
121pub use verbosity::Verbosity;
122pub use verification::{build_archive, send_verification_request};
123#[cfg(any(feature = "std-fs-io", test))]
124use verification_types::VerificationDetails;
125
126pub const MAX_SERIALIZED_SIZE_OF_DEPLOY: u32 = 1_024 * 1_024;
131
132#[deprecated(since = "3.0.0", note = "use `put_transaction` instead")]
138pub async fn put_deploy(
139 rpc_id: JsonRpcId,
140 node_address: &str,
141 verbosity: Verbosity,
142 deploy: Deploy,
143) -> Result<SuccessResponse<PutDeployResult>, Error> {
144 JsonRpcCall::new(rpc_id, node_address, verbosity)?
145 .send_request(PUT_DEPLOY_METHOD, Some(PutDeployParams::new(deploy)))
146 .await
147}
148
149pub async fn put_transaction(
155 rpc_id: JsonRpcId,
156 node_address: &str,
157 verbosity: Verbosity,
158 transaction: Transaction,
159) -> Result<SuccessResponse<PutTransactionResult>, Error> {
160 JsonRpcCall::new(rpc_id, node_address, verbosity)?
161 .send_request(
162 PUT_TRANSACTION_METHOD,
163 Some(PutTransactionParams::new(transaction)),
164 )
165 .await
166}
167
168#[deprecated(since = "3.0.0", note = "use `speculative_exec_txn` instead")]
174pub async fn speculative_exec(
175 rpc_id: JsonRpcId,
176 node_address: &str,
177 verbosity: Verbosity,
178 deploy: Deploy,
179) -> Result<SuccessResponse<SpeculativeExecResult>, Error> {
180 JsonRpcCall::new(rpc_id, node_address, verbosity)?
181 .send_request(
182 SPECULATIVE_EXEC_METHOD,
183 Some(SpeculativeExecParams::new(deploy)),
184 )
185 .await
186}
187
188pub async fn speculative_exec_txn(
194 rpc_id: JsonRpcId,
195 node_address: &str,
196 verbosity: Verbosity,
197 transaction: Transaction,
198) -> Result<SuccessResponse<SpeculativeExecTxnResult>, Error> {
199 JsonRpcCall::new(rpc_id, node_address, verbosity)?
200 .send_request(
201 SPECULATIVE_EXEC_TXN_METHOD,
202 Some(SpeculativeExecTxnParams::new(transaction)),
203 )
204 .await
205}
206
207#[deprecated(since = "3.0.0", note = "use `output_transaction` instead")]
216#[cfg(any(feature = "std-fs-io", test))]
217pub fn output_deploy(output: OutputKind, deploy: &Deploy) -> Result<(), Error> {
218 write_deploy(deploy, output.get()?)?;
219 output.commit()
220}
221
222#[cfg(any(feature = "std-fs-io", test))]
231pub fn output_transaction(output: OutputKind, transaction: &Transaction) -> Result<(), Error> {
232 write_transaction(transaction, output.get()?)?;
233 output.commit()
234}
235
236#[deprecated(since = "3.0.0", note = "use `read_transaction_file` instead")]
238#[cfg(any(feature = "std-fs-io", test))]
239pub fn read_deploy_file<P: AsRef<Path>>(deploy_path: P) -> Result<Deploy, Error> {
240 let input = fs::read(deploy_path.as_ref()).map_err(|error| Error::IoError {
241 context: format!(
242 "unable to read deploy file at '{}'",
243 deploy_path.as_ref().display()
244 ),
245 error,
246 })?;
247 read_deploy(Cursor::new(input))
248}
249
250#[cfg(any(feature = "std-fs-io", test))]
252pub fn read_transaction_file<P: AsRef<Path>>(transaction_path: P) -> Result<Transaction, Error> {
253 let input = fs::read(transaction_path.as_ref()).map_err(|error| Error::IoError {
254 context: format!(
255 "unable to read transaction file at '{}'",
256 transaction_path.as_ref().display()
257 ),
258 error,
259 })?;
260 read_transaction(Cursor::new(input))
261}
262
263#[deprecated(since = "3.0.0", note = "use `sign_transaction_file` instead")]
272#[cfg(any(feature = "std-fs-io", test))]
273pub fn sign_deploy_file<P: AsRef<Path>>(
274 input_path: P,
275 secret_key: &SecretKey,
276 output: OutputKind,
277) -> Result<(), Error> {
278 #[allow(deprecated)]
279 let mut deploy = read_deploy_file(input_path)?;
280
281 deploy.sign(secret_key);
282 deploy.is_valid_size(MAX_SERIALIZED_SIZE_OF_DEPLOY)?;
283
284 write_deploy(&deploy, output.get()?)?;
285 output.commit()
286}
287
288#[cfg(any(feature = "std-fs-io", test))]
293pub fn sign_transaction_file<P: AsRef<Path>>(
294 input_path: P,
295 secret_key: &SecretKey,
296 output: OutputKind,
297) -> Result<(), Error> {
298 let mut transaction = read_transaction_file(input_path)?;
299
300 transaction.sign(secret_key);
301
302 write_transaction(&transaction, output.get()?)?;
303 output.commit()
304}
305
306pub async fn get_deploy(
315 rpc_id: JsonRpcId,
316 node_address: &str,
317 verbosity: Verbosity,
318 deploy_hash: DeployHash,
319 finalized_approvals: bool,
320) -> Result<SuccessResponse<GetDeployResult>, Error> {
321 JsonRpcCall::new(rpc_id, node_address, verbosity)?
322 .send_request(
323 GET_DEPLOY_METHOD,
324 Some(GetDeployParams::new(deploy_hash, finalized_approvals)),
325 )
326 .await
327}
328
329pub async fn get_transaction(
338 rpc_id: JsonRpcId,
339 node_address: &str,
340 verbosity: Verbosity,
341 transaction_hash: TransactionHash,
342 finalized_approvals: bool,
343) -> Result<SuccessResponse<GetTransactionResult>, Error> {
344 JsonRpcCall::new(rpc_id, node_address, verbosity)?
345 .send_request(
346 GET_TRANSACTION_METHOD,
347 Some(GetTransactionParams::new(
348 transaction_hash,
349 finalized_approvals,
350 )),
351 )
352 .await
353}
354
355pub async fn get_block(
361 rpc_id: JsonRpcId,
362 node_address: &str,
363 verbosity: Verbosity,
364 maybe_block_identifier: Option<BlockIdentifier>,
365) -> Result<SuccessResponse<GetBlockResult>, Error> {
366 let params = maybe_block_identifier.map(GetBlockParams::new);
367 let success_response = JsonRpcCall::new(rpc_id, node_address, verbosity)?
368 .send_request(GET_BLOCK_METHOD, params)
369 .await?;
370 validation::validate_get_block_result(maybe_block_identifier, &success_response.result)?;
371 Ok(success_response)
372}
373
374pub async fn get_block_transfers(
380 rpc_id: JsonRpcId,
381 node_address: &str,
382 verbosity: Verbosity,
383 maybe_block_identifier: Option<BlockIdentifier>,
384) -> Result<SuccessResponse<GetBlockTransfersResult>, Error> {
385 let params = maybe_block_identifier.map(GetBlockTransfersParams::new);
386 JsonRpcCall::new(rpc_id, node_address, verbosity)?
387 .send_request(GET_BLOCK_TRANSFERS_METHOD, params)
388 .await
389}
390
391pub async fn get_state_root_hash(
397 rpc_id: JsonRpcId,
398 node_address: &str,
399 verbosity: Verbosity,
400 maybe_block_identifier: Option<BlockIdentifier>,
401) -> Result<SuccessResponse<GetStateRootHashResult>, Error> {
402 let params = maybe_block_identifier.map(GetStateRootHashParams::new);
403 JsonRpcCall::new(rpc_id, node_address, verbosity)?
404 .send_request(GET_STATE_ROOT_HASH_METHOD, params)
405 .await
406}
407
408pub async fn get_era_summary(
414 rpc_id: JsonRpcId,
415 node_address: &str,
416 verbosity: Verbosity,
417 maybe_block_identifier: Option<BlockIdentifier>,
418) -> Result<SuccessResponse<GetEraSummaryResult>, Error> {
419 let params = maybe_block_identifier.map(GetEraSummaryParams::new);
420 JsonRpcCall::new(rpc_id, node_address, verbosity)?
421 .send_request(GET_ERA_SUMMARY_METHOD, params)
422 .await
423}
424
425pub async fn query_global_state(
440 rpc_id: JsonRpcId,
441 node_address: &str,
442 verbosity: Verbosity,
443 global_state_identifier: GlobalStateIdentifier,
444 key: Key,
445 path: Vec<String>,
446) -> Result<SuccessResponse<QueryGlobalStateResult>, Error> {
447 let params = QueryGlobalStateParams::new(global_state_identifier, key, path);
448 JsonRpcCall::new(rpc_id, node_address, verbosity)?
449 .send_request(QUERY_GLOBAL_STATE_METHOD, Some(params))
450 .await
451}
452
453pub async fn query_balance(
459 rpc_id: JsonRpcId,
460 node_address: &str,
461 verbosity: Verbosity,
462 maybe_global_state_identifier: Option<GlobalStateIdentifier>,
463 purse_identifier: PurseIdentifier,
464) -> Result<SuccessResponse<QueryBalanceResult>, Error> {
465 let params = QueryBalanceParams::new(maybe_global_state_identifier, purse_identifier);
466 JsonRpcCall::new(rpc_id, node_address, verbosity)?
467 .send_request(QUERY_BALANCE_METHOD, Some(params))
468 .await
469}
470
471pub async fn query_balance_details(
477 rpc_id: JsonRpcId,
478 node_address: &str,
479 verbosity: Verbosity,
480 maybe_global_state_identifier: Option<GlobalStateIdentifier>,
481 purse_identifier: PurseIdentifier,
482) -> Result<SuccessResponse<QueryBalanceDetailsResult>, Error> {
483 let params = QueryBalanceDetailsParams::new(maybe_global_state_identifier, purse_identifier);
484 JsonRpcCall::new(rpc_id, node_address, verbosity)?
485 .send_request(QUERY_BALANCE_DETAILS_METHOD, Some(params))
486 .await
487}
488
489pub async fn get_dictionary_item(
495 rpc_id: JsonRpcId,
496 node_address: &str,
497 verbosity: Verbosity,
498 state_root_hash: Digest,
499 dictionary_item_identifier: DictionaryItemIdentifier,
500) -> Result<SuccessResponse<GetDictionaryItemResult>, Error> {
501 let params = GetDictionaryItemParams::new(state_root_hash, dictionary_item_identifier);
502 JsonRpcCall::new(rpc_id, node_address, verbosity)?
503 .send_request(GET_DICTIONARY_ITEM_METHOD, Some(params))
504 .await
505}
506
507pub async fn get_balance(
513 rpc_id: JsonRpcId,
514 node_address: &str,
515 verbosity: Verbosity,
516 state_root_hash: Digest,
517 purse: URef,
518) -> Result<SuccessResponse<GetBalanceResult>, Error> {
519 let params = GetBalanceParams::new(state_root_hash, purse);
520 JsonRpcCall::new(rpc_id, node_address, verbosity)?
521 .send_request(GET_BALANCE_METHOD, Some(params))
522 .await
523}
524
525pub async fn get_account(
531 rpc_id: JsonRpcId,
532 node_address: &str,
533 verbosity: Verbosity,
534 maybe_block_identifier: Option<BlockIdentifier>,
535 account_identifier: AccountIdentifier,
536) -> Result<SuccessResponse<GetAccountResult>, Error> {
537 let params = GetAccountParams::new(account_identifier, maybe_block_identifier);
538 JsonRpcCall::new(rpc_id, node_address, verbosity)?
539 .send_request(GET_ACCOUNT_METHOD, Some(params))
540 .await
541}
542
543pub async fn get_entity(
549 rpc_id: JsonRpcId,
550 node_address: &str,
551 verbosity: Verbosity,
552 maybe_block_identifier: Option<BlockIdentifier>,
553 entity_identifier: EntityIdentifier,
554) -> Result<SuccessResponse<GetAddressableEntityResult>, Error> {
555 let params = GetAddressableEntityParams::new(entity_identifier, maybe_block_identifier);
556 JsonRpcCall::new(rpc_id, node_address, verbosity)?
557 .send_request(GET_ENTITY_METHOD, Some(params))
558 .await
559}
560
561pub async fn get_reward(
567 rpc_id: JsonRpcId,
568 node_address: &str,
569 verbosity: Verbosity,
570 maybe_era_identifier: Option<EraIdentifier>,
571 validator: PublicKey,
572 delegator: Option<PublicKey>,
573) -> Result<SuccessResponse<GetRewardResult>, Error> {
574 let params = GetRewardParams::new(maybe_era_identifier, validator, delegator);
575 JsonRpcCall::new(rpc_id, node_address, verbosity)?
576 .send_request(GET_REWARD_METHOD, Some(params))
577 .await
578}
579
580pub async fn get_auction_info(
586 rpc_id: JsonRpcId,
587 node_address: &str,
588 verbosity: Verbosity,
589 maybe_block_identifier: Option<BlockIdentifier>,
590) -> Result<SuccessResponse<GetAuctionInfoResult>, Error> {
591 let params = maybe_block_identifier.map(GetAuctionInfoParams::new);
592 JsonRpcCall::new(rpc_id, node_address, verbosity)?
593 .send_request(GET_AUCTION_INFO_METHOD, params)
594 .await
595}
596
597pub async fn get_validator_changes(
603 rpc_id: JsonRpcId,
604 node_address: &str,
605 verbosity: Verbosity,
606) -> Result<SuccessResponse<GetValidatorChangesResult>, Error> {
607 JsonRpcCall::new(rpc_id, node_address, verbosity)?
608 .send_request::<(), _>(GET_VALIDATOR_CHANGES_METHOD, None)
609 .await
610}
611
612pub async fn get_peers(
618 rpc_id: JsonRpcId,
619 node_address: &str,
620 verbosity: Verbosity,
621) -> Result<SuccessResponse<GetPeersResult>, Error> {
622 JsonRpcCall::new(rpc_id, node_address, verbosity)?
623 .send_request::<(), _>(GET_PEERS_METHOD, None)
624 .await
625}
626
627pub async fn get_node_status(
633 rpc_id: JsonRpcId,
634 node_address: &str,
635 verbosity: Verbosity,
636) -> Result<SuccessResponse<GetNodeStatusResult>, Error> {
637 JsonRpcCall::new(rpc_id, node_address, verbosity)?
638 .send_request::<(), _>(GET_NODE_STATUS_METHOD, None)
639 .await
640}
641
642pub async fn get_chainspec(
648 rpc_id: JsonRpcId,
649 node_address: &str,
650 verbosity: Verbosity,
651) -> Result<SuccessResponse<GetChainspecResult>, Error> {
652 JsonRpcCall::new(rpc_id, node_address, verbosity)?
653 .send_request::<(), _>(GET_CHAINSPEC_METHOD, None)
654 .await
655}
656
657pub async fn list_rpcs(
664 rpc_id: JsonRpcId,
665 node_address: &str,
666 verbosity: Verbosity,
667) -> Result<SuccessResponse<ListRpcsResult>, Error> {
668 JsonRpcCall::new(rpc_id, node_address, verbosity)?
669 .send_request::<(), _>(LIST_RPCS_METHOD, None)
670 .await
671}
672
673#[cfg(feature = "std-fs-io")]
679pub(crate) fn json_pretty_print<T: ?Sized + Serialize>(
680 value: &T,
681 verbosity: Verbosity,
682) -> Result<(), Error> {
683 let output = match verbosity {
684 Verbosity::Low => return Ok(()),
685 Verbosity::Medium => casper_types::json_pretty_print(value),
686 Verbosity::High => serde_json::to_string_pretty(value),
687 }
688 .map_err(|error| Error::FailedToEncodeToJson {
689 context: "in json_pretty_print",
690 error,
691 })?;
692 println!("{}", output);
693 Ok(())
694}
695
696#[cfg(any(feature = "std-fs-io", test))]
697fn write_deploy<W: Write>(deploy: &Deploy, mut output: W) -> Result<(), Error> {
698 let content =
699 serde_json::to_string_pretty(deploy).map_err(|error| Error::FailedToEncodeToJson {
700 context: "writing deploy",
701 error,
702 })?;
703 output
704 .write_all(content.as_bytes())
705 .map_err(|error| Error::IoError {
706 context: "unable to write deploy".to_owned(),
707 error,
708 })
709}
710
711#[cfg(any(feature = "std-fs-io", test))]
712fn write_transaction<W: Write>(transaction: &Transaction, mut output: W) -> Result<(), Error> {
713 let content =
714 serde_json::to_string_pretty(transaction).map_err(|error| Error::FailedToEncodeToJson {
715 context: "writing transaction",
716 error,
717 })?;
718 output
719 .write_all(content.as_bytes())
720 .map_err(|error| Error::IoError {
721 context: "unable to write transaction".to_owned(),
722 error,
723 })
724}
725
726#[cfg(any(feature = "std-fs-io", test))]
727fn read_deploy<R: Read>(input: R) -> Result<Deploy, Error> {
728 let deploy: Deploy =
729 serde_json::from_reader(input).map_err(|error| Error::FailedToDecodeFromJson {
730 context: "reading deploy",
731 error,
732 })?;
733 deploy.is_valid_size(MAX_SERIALIZED_SIZE_OF_DEPLOY)?;
734 Ok(deploy)
735}
736
737#[cfg(any(feature = "std-fs-io", test))]
738fn read_transaction<R: Read>(input: R) -> Result<Transaction, Error> {
739 let transaction: Transaction =
740 serde_json::from_reader(input).map_err(|error| Error::FailedToDecodeFromJson {
741 context: "reading transaction",
742 error,
743 })?;
744 Ok(transaction)
745}
746
747#[deprecated(
754 since = "2.0.0",
755 note = "prefer 'get_era_summary' as it doesn't require a switch block"
756)]
757pub async fn get_era_info(
758 rpc_id: JsonRpcId,
759 node_address: &str,
760 verbosity: Verbosity,
761 maybe_block_identifier: Option<BlockIdentifier>,
762) -> Result<SuccessResponse<GetEraInfoResult>, Error> {
763 let params = maybe_block_identifier.map(GetEraInfoParams::new);
764 JsonRpcCall::new(rpc_id, node_address, verbosity)?
765 .send_request(GET_ERA_INFO_METHOD, params)
766 .await
767}
768
769#[cfg(any(feature = "std-fs-io", test))]
771pub async fn verify_contract(
772 hash_str: &str,
773 verification_url_base_path: &str,
774 project_path: Option<&str>,
775 verbosity: Verbosity,
776) -> Result<VerificationDetails, Error> {
777 if verbosity == Verbosity::Medium || verbosity == Verbosity::High {
778 println!("Hash: {hash_str}");
779 println!("Verification service base path: {verification_url_base_path}",);
780 }
781
782 let project_path = match project_path {
783 Some(path) => Path::new(path).to_path_buf(),
784 None => match current_dir() {
785 Ok(path) => path,
786 Err(error) => {
787 eprintln!("Cannot get current directory: {error}");
788 return Err(Error::ContractVerificationFailed);
789 }
790 },
791 };
792
793 let archive = match build_archive(&project_path) {
794 Ok(archive) => {
795 if verbosity == Verbosity::Medium || verbosity == Verbosity::High {
796 println!("Created project archive (size: {})", archive.len());
797 }
798 archive
799 }
800 Err(error) => {
801 eprintln!("Cannot create project archive: {error}");
802 return Err(Error::ContractVerificationFailed);
803 }
804 };
805
806 send_verification_request(
807 hash_str,
808 verification_url_base_path,
809 STANDARD.encode(&archive),
810 verbosity,
811 )
812 .await
813}