1use std::fs;
5#[cfg(feature = "std-fs-io")]
6use std::path::Path;
7use std::str::FromStr;
8
9use rand::Rng;
10
11#[cfg(feature = "std-fs-io")]
12use casper_types::SecretKey;
13use casper_types::{
14 account::AccountHash, bytesrepr::Bytes, crypto, AsymmetricType, BlockHash, DeployHash, Digest,
15 EntityAddr, ExecutableDeployItem, HashAddr, Key, NamedArg, PricingMode, PublicKey, RuntimeArgs,
16 TimeDiff, Timestamp, TransactionArgs, TransactionHash, TransactionV1Hash, TransferTarget,
17 UIntParseError, URef, U512,
18};
19
20use super::{simple_args, CliError, PaymentStrParams, SessionStrParams};
21#[cfg(feature = "std-fs-io")]
22use crate::OutputKind;
23use crate::{
24 rpcs::EraIdentifier, AccountIdentifier, BlockIdentifier, EntityIdentifier,
25 GlobalStateIdentifier, JsonRpcId, PurseIdentifier, Verbosity,
26};
27
28pub(super) fn rpc_id(maybe_rpc_id: &str) -> JsonRpcId {
29 if maybe_rpc_id.is_empty() {
30 JsonRpcId::from(rand::thread_rng().gen::<i64>())
31 } else if let Ok(i64_id) = maybe_rpc_id.parse::<i64>() {
32 JsonRpcId::from(i64_id)
33 } else {
34 JsonRpcId::from(maybe_rpc_id.to_string())
35 }
36}
37
38pub(super) fn verbosity(verbosity_level: u64) -> Verbosity {
39 match verbosity_level {
40 0 => Verbosity::Low,
41 1 => Verbosity::Medium,
42 _ => Verbosity::High,
43 }
44}
45
46#[cfg(feature = "std-fs-io")]
47pub(super) fn output_kind(maybe_output_path: &str, force: bool) -> OutputKind {
48 if maybe_output_path.is_empty() {
49 OutputKind::Stdout
50 } else {
51 OutputKind::file(Path::new(maybe_output_path), force)
52 }
53}
54
55#[cfg(feature = "std-fs-io")]
56pub(super) fn secret_key_from_file<P: AsRef<Path>>(
57 secret_key_path: P,
58) -> Result<SecretKey, CliError> {
59 SecretKey::from_file(secret_key_path).map_err(|error| {
60 CliError::Core(crate::Error::CryptoError {
61 context: "secret key",
62 error,
63 })
64 })
65}
66
67pub(super) fn timestamp(value: &str) -> Result<Timestamp, CliError> {
68 #[cfg(any(feature = "std-fs-io", test))]
69 let timestamp = Timestamp::now();
70 #[cfg(not(any(feature = "std-fs-io", test)))]
71 let timestamp = Timestamp::zero();
72 if value.is_empty() {
73 return Ok(timestamp);
74 }
75 Timestamp::from_str(value).map_err(|error| CliError::FailedToParseTimestamp {
76 context: "timestamp",
77 error,
78 })
79}
80
81pub(super) fn ttl(value: &str) -> Result<TimeDiff, CliError> {
82 TimeDiff::from_str(value).map_err(|error| CliError::FailedToParseTimeDiff {
83 context: "ttl",
84 error,
85 })
86}
87
88pub(super) fn session_account(value: &str) -> Result<Option<PublicKey>, CliError> {
89 if value.is_empty() {
90 return Ok(None);
91 }
92
93 let public_key = PublicKey::from_hex(value).map_err(|error| crate::Error::CryptoError {
94 context: "session account",
95 error: crypto::ErrorExt::from(error),
96 })?;
97 Ok(Some(public_key))
98}
99
100pub(crate) mod arg_simple {
102 use super::*;
103
104 pub(crate) mod session {
105 use super::*;
106
107 pub fn parse(values: &[&str]) -> Result<Option<RuntimeArgs>, CliError> {
109 Ok(if values.is_empty() {
110 None
111 } else {
112 Some(get(values)?)
113 })
114 }
115 }
116
117 pub(crate) mod payment {
118 use super::*;
119
120 pub fn parse(values: &[&str]) -> Result<Option<RuntimeArgs>, CliError> {
121 Ok(if values.is_empty() {
122 None
123 } else {
124 Some(get(values)?)
125 })
126 }
127 }
128
129 fn get(values: &[&str]) -> Result<RuntimeArgs, CliError> {
130 let mut runtime_args = RuntimeArgs::new();
131 for arg in values {
132 simple_args::insert_arg(arg, &mut runtime_args)?;
133 }
134 Ok(runtime_args)
135 }
136}
137
138pub(crate) mod args_json {
139 use super::*;
140 use crate::cli::JsonArg;
141
142 pub mod session {
143 use super::*;
144
145 pub fn parse(json_str: &str) -> Result<Option<RuntimeArgs>, CliError> {
147 get(json_str)
148 }
149 }
150
151 pub mod payment {
152 use super::*;
153
154 pub fn parse(json_str: &str) -> Result<Option<RuntimeArgs>, CliError> {
155 get(json_str)
156 }
157 }
158
159 fn get(json_str: &str) -> Result<Option<RuntimeArgs>, CliError> {
160 if json_str.is_empty() {
161 return Ok(None);
162 }
163 let json_args: Vec<JsonArg> = serde_json::from_str(json_str)?;
164 let mut named_args = Vec::with_capacity(json_args.len());
165 for json_arg in json_args {
166 named_args.push(NamedArg::try_from(json_arg)?);
167 }
168 Ok(Some(RuntimeArgs::from(named_args)))
169 }
170}
171
172const STANDARD_PAYMENT_ARG_NAME: &str = "amount";
173fn standard_payment(value: &str) -> Result<RuntimeArgs, CliError> {
174 if value.is_empty() {
175 return Err(CliError::InvalidCLValue(value.to_string()));
176 }
177 let arg = U512::from_dec_str(value).map_err(|err| CliError::FailedToParseUint {
178 context: "amount",
179 error: UIntParseError::FromDecStr(err),
180 })?;
181 let mut runtime_args = RuntimeArgs::new();
182 runtime_args.insert(STANDARD_PAYMENT_ARG_NAME, arg)?;
183 Ok(runtime_args)
184}
185
186fn check_no_conflicting_arg_types(
205 context: &str,
206 simple: &[&str],
207 json: &str,
208) -> Result<(), CliError> {
209 let count = [!simple.is_empty(), !json.is_empty()]
210 .iter()
211 .filter(|&&x| x)
212 .count();
213
214 if count > 1 {
215 return Err(CliError::ConflictingArguments {
216 context: format!("{context} args conflict (simple json)",),
217 args: vec![simple.join(", "), json.to_owned()],
218 });
219 }
220 Ok(())
221}
222
223pub fn args_from_simple_or_json(
247 simple: Option<RuntimeArgs>,
248 json: Option<RuntimeArgs>,
249 chunked: Option<Vec<u8>>,
250) -> TransactionArgs {
251 match chunked {
253 Some(chunked) => TransactionArgs::Bytesrepr(chunked.into()),
254 None => {
255 let named_args = match (simple, json) {
256 (Some(args), None) | (None, Some(args)) => args,
257 (None, None) => RuntimeArgs::new(),
258 _ => unreachable!("should not have more than one of simple, json args"),
259 };
260 TransactionArgs::Named(named_args)
261 }
262 }
263}
264
265macro_rules! check_exactly_one_not_empty {
275 ( context: $site:tt, $( ($x:expr) requires[$($y:expr),*] requires_empty[$($z:expr),*] ),+ $(,)? ) => {{
276
277 let field_is_empty_map = &[$(
278 (stringify!($x), $x.is_empty())
279 ),+];
280
281 let required_arguments = field_is_empty_map
282 .iter()
283 .filter(|(_, is_empty)| !*is_empty)
284 .map(|(field, _)| field.to_string())
285 .collect::<Vec<_>>();
286
287 if required_arguments.is_empty() {
288 let required_param_names = vec![$((stringify!($x))),+];
289 return Err(CliError::InvalidArgument {
290 context: $site,
291 error: format!("Missing a required arg - exactly one of the following must be provided: {:?}", required_param_names),
292 });
293 }
294 if required_arguments.len() == 1 {
295 let name = &required_arguments[0];
296 let field_requirements = &[$(
297 (
298 stringify!($x),
299 $x,
300 vec![$((stringify!($y), $y)),*],
301 vec![$((stringify!($z), $z)),*],
302 )
303 ),+];
304
305 let (_, value, requirements, required_empty) = field_requirements
307 .iter()
308 .find(|(field, _, _, _)| *field == name).expect("should exist");
309 let required_arguments = requirements
310 .iter()
311 .filter(|(_, value)| !value.is_empty())
312 .collect::<Vec<_>>();
313
314 if requirements.len() != required_arguments.len() {
315 let required_param_names = requirements
316 .iter()
317 .map(|(requirement_name, _)| requirement_name)
318 .collect::<Vec<_>>();
319 return Err(CliError::InvalidArgument {
320 context: $site,
321 error: format!("Field {} also requires following fields to be provided: {:?}", name, required_param_names),
322 });
323 }
324
325 let mut conflicting_fields = required_empty
326 .iter()
327 .filter(|(_, value)| !value.is_empty())
328 .map(|(field, value)| format!("{}={}", field, value)).collect::<Vec<_>>();
329
330 if !conflicting_fields.is_empty() {
331 conflicting_fields.push(format!("{}={}", name, value));
332 conflicting_fields.sort();
333 return Err(CliError::ConflictingArguments{
334 context: $site.to_string(),
335 args: conflicting_fields,
336 });
337 }
338 } else {
339 let mut non_empty_fields_with_values = [$((stringify!($x), $x)),+]
342 .iter()
343 .filter_map(|(field_name, field_value)| if !field_value.is_empty() {
344 Some(format!("{}={}", field_name, field_value))
345 } else {
346 None
347 })
348 .collect::<Vec<String>>();
349 non_empty_fields_with_values.sort();
350 return Err(CliError::ConflictingArguments {
351 context: $site.to_string(),
352 args: non_empty_fields_with_values,
353 });
354 }
355 }}
356}
357
358pub(super) fn session_executable_deploy_item(
359 params: SessionStrParams,
360) -> Result<ExecutableDeployItem, CliError> {
361 let SessionStrParams {
362 session_hash,
363 session_name,
364 session_package_hash,
365 session_package_name,
366 session_path,
367 session_bytes,
368 ref session_args_simple,
369 session_args_json,
370 session_version,
371 session_entry_point,
372 is_session_transfer: session_transfer,
373 session_chunked_args,
374 } = params;
375 let is_session_transfer = if session_transfer { "true" } else { "" };
377 let has_session_bytes = if session_bytes.is_empty() { "" } else { "true" };
379
380 check_exactly_one_not_empty!(
381 context: "parse_session_info",
382 (session_hash)
383 requires[session_entry_point] requires_empty[session_version],
384 (session_name)
385 requires[session_entry_point] requires_empty[session_version],
386 (session_package_hash)
387 requires[session_entry_point] requires_empty[],
388 (session_package_name)
389 requires[session_entry_point] requires_empty[],
390 (session_path)
391 requires[] requires_empty[session_entry_point, session_version, has_session_bytes],
392 (has_session_bytes)
393 requires[] requires_empty[session_entry_point, session_version, session_path],
394 (is_session_transfer)
395 requires[] requires_empty[session_entry_point, session_version]
396 );
397
398 check_no_conflicting_arg_types("parse_session_info", session_args_simple, session_args_json)?;
399
400 let session_args = args_from_simple_or_json(
401 arg_simple::session::parse(session_args_simple)?,
402 args_json::session::parse(session_args_json)?,
403 session_chunked_args.map(ToOwned::to_owned),
404 );
405
406 if session_transfer {
407 let session_args = session_args.as_named().unwrap().clone();
408 if session_args.is_empty() {
409 return Err(CliError::InvalidArgument {
410 context: "is_session_transfer",
411 error: "requires --session-arg to be present".to_string(),
412 });
413 }
414 return Ok(ExecutableDeployItem::Transfer { args: session_args });
415 }
416 let invalid_entry_point = || CliError::InvalidArgument {
417 context: "session_entry_point",
418 error: session_entry_point.to_string(),
419 };
420 if let Some(session_name) = name(session_name) {
421 let session_args = session_args.as_named().unwrap().clone();
422
423 return Ok(ExecutableDeployItem::StoredContractByName {
424 name: session_name,
425 entry_point: entry_point(session_entry_point).ok_or_else(invalid_entry_point)?,
426 args: session_args,
427 });
428 }
429
430 if let Some(session_hash) = contract_hash(session_hash)? {
431 let session_args = session_args.as_named().unwrap().clone();
432 return Ok(ExecutableDeployItem::StoredContractByHash {
433 hash: session_hash.into(),
434 entry_point: entry_point(session_entry_point).ok_or_else(invalid_entry_point)?,
435 args: session_args,
436 });
437 }
438
439 let version = version(session_version)?;
440 if let Some(package_name) = name(session_package_name) {
441 let session_args = session_args.as_named().unwrap().clone();
442 return Ok(ExecutableDeployItem::StoredVersionedContractByName {
443 name: package_name,
444 version, entry_point: entry_point(session_entry_point).ok_or_else(invalid_entry_point)?,
446 args: session_args,
447 });
448 }
449
450 if let Some(package_hash) = contract_hash(session_package_hash)? {
451 let session_args = session_args.as_named().unwrap().clone();
452 return Ok(ExecutableDeployItem::StoredVersionedContractByHash {
453 hash: package_hash.into(),
454 version, entry_point: entry_point(session_entry_point).ok_or_else(invalid_entry_point)?,
456 args: session_args,
457 });
458 }
459
460 let module_bytes = if !session_bytes.is_empty() {
461 session_bytes
462 } else {
463 #[cfg(feature = "std-fs-io")]
464 {
465 transaction_module_bytes(session_path)?
466 }
467 #[cfg(not(feature = "std-fs-io"))]
468 return Err(CliError::InvalidArgument {
469 context: "session_executable_deploy_item",
470 error: "missing session bytes".to_string(),
471 });
472 };
473
474 let args = session_args
475 .as_named()
476 .ok_or(CliError::UnexpectedTransactionArgsVariant)?;
477
478 Ok(ExecutableDeployItem::ModuleBytes {
479 module_bytes,
480 args: args.clone(),
481 })
482}
483
484pub fn transaction_module_bytes(session_path: &str) -> Result<Bytes, CliError> {
486 let module_bytes = fs::read(session_path).map_err(|error| crate::Error::IoError {
487 context: format!("unable to read session file at '{}'", session_path),
488 error,
489 })?;
490 Ok(Bytes::from(module_bytes))
491}
492
493pub fn transfer_target(target_str: &str) -> Result<TransferTarget, CliError> {
495 if let Ok(public_key) = PublicKey::from_hex(target_str) {
496 return Ok(TransferTarget::PublicKey(public_key));
497 }
498 #[cfg(feature = "std-fs-io")]
499 {
500 if let Ok(public_key) = PublicKey::from_file(target_str) {
501 return Ok(TransferTarget::PublicKey(public_key));
502 }
503 }
504 if let Ok(account_hash) = AccountHash::from_formatted_str(target_str) {
505 return Ok(TransferTarget::AccountHash(account_hash));
506 }
507 if let Ok(uref) = URef::from_formatted_str(target_str) {
508 return Ok(TransferTarget::URef(uref));
509 }
510 Err(CliError::FailedToParseTransferTarget)
511}
512
513pub fn uref(uref_str: &str) -> Result<URef, CliError> {
515 match URef::from_formatted_str(uref_str) {
516 Ok(uref) => Ok(uref),
517 Err(err) => Err(CliError::FailedToParseURef {
518 context: "Failed to parse URef for transaction",
519 error: err,
520 }),
521 }
522}
523
524pub(super) fn payment_executable_deploy_item(
525 params: PaymentStrParams,
526) -> Result<ExecutableDeployItem, CliError> {
527 let PaymentStrParams {
528 payment_amount,
529 payment_hash,
530 payment_name,
531 payment_package_hash,
532 payment_package_name,
533 payment_path,
534 payment_bytes,
535 ref payment_args_simple,
536 payment_args_json,
537 payment_version,
538 payment_entry_point,
539 } = params;
540 let has_payment_bytes = if payment_bytes.is_empty() { "" } else { "true" };
542 check_exactly_one_not_empty!(
543 context: "parse_payment_info",
544 (payment_amount)
545 requires[] requires_empty[payment_entry_point, payment_version],
546 (payment_hash)
547 requires[payment_entry_point] requires_empty[payment_version],
548 (payment_name)
549 requires[payment_entry_point] requires_empty[payment_version],
550 (payment_package_hash)
551 requires[payment_entry_point] requires_empty[],
552 (payment_package_name)
553 requires[payment_entry_point] requires_empty[],
554 (payment_path) requires[] requires_empty[payment_entry_point, payment_version, has_payment_bytes],
555 (has_payment_bytes)
556 requires[] requires_empty[payment_entry_point, payment_version, payment_path],
557 );
558
559 check_no_conflicting_arg_types("parse_payment_info", payment_args_simple, payment_args_json)?;
560
561 let payment_args = args_from_simple_or_json(
562 arg_simple::payment::parse(payment_args_simple)?,
563 args_json::payment::parse(payment_args_json)?,
564 None,
565 );
566
567 if let Ok(payment_args) = standard_payment(payment_amount) {
568 return Ok(ExecutableDeployItem::ModuleBytes {
569 module_bytes: vec![].into(),
570 args: payment_args,
571 });
572 }
573
574 let invalid_entry_point = || CliError::InvalidArgument {
575 context: "payment_entry_point",
576 error: payment_entry_point.to_string(),
577 };
578
579 let payment_args = payment_args
580 .as_named()
581 .cloned()
582 .ok_or(CliError::UnexpectedTransactionArgsVariant)?;
583
584 if let Some(payment_name) = name(payment_name) {
585 return Ok(ExecutableDeployItem::StoredContractByName {
586 name: payment_name,
587 entry_point: entry_point(payment_entry_point).ok_or_else(invalid_entry_point)?,
588 args: payment_args,
589 });
590 }
591
592 if let Some(payment_hash) = contract_hash(payment_hash)? {
593 return Ok(ExecutableDeployItem::StoredContractByHash {
594 hash: payment_hash.into(),
595 entry_point: entry_point(payment_entry_point).ok_or_else(invalid_entry_point)?,
596 args: payment_args,
597 });
598 }
599
600 let version = version(payment_version)?;
601 if let Some(package_name) = name(payment_package_name) {
602 return Ok(ExecutableDeployItem::StoredVersionedContractByName {
603 name: package_name,
604 version, entry_point: entry_point(payment_entry_point).ok_or_else(invalid_entry_point)?,
606 args: payment_args,
607 });
608 }
609
610 if let Some(package_hash) = contract_hash(payment_package_hash)? {
611 return Ok(ExecutableDeployItem::StoredVersionedContractByHash {
612 hash: package_hash.into(),
613 version, entry_point: entry_point(payment_entry_point).ok_or_else(invalid_entry_point)?,
615 args: payment_args,
616 });
617 }
618
619 let module_bytes = fs::read(payment_path).map_err(|error| crate::Error::IoError {
620 context: format!("unable to read payment file at '{}'", payment_path),
621 error,
622 })?;
623 Ok(ExecutableDeployItem::ModuleBytes {
624 module_bytes: module_bytes.into(),
625 args: payment_args,
626 })
627}
628
629fn contract_hash(value: &str) -> Result<Option<HashAddr>, CliError> {
630 if value.is_empty() {
631 return Ok(None);
632 }
633
634 match Digest::from_hex(value) {
635 Ok(digest) => Ok(Some(digest.value())),
636 Err(error) => match Key::from_formatted_str(value) {
637 Ok(Key::Hash(hash)) | Ok(Key::SmartContract(hash)) => Ok(Some(hash)),
638 _ => Err(CliError::FailedToParseDigest {
639 context: "contract hash",
640 error,
641 }),
642 },
643 }
644}
645
646fn name(value: &str) -> Option<String> {
647 if value.is_empty() {
648 return None;
649 }
650 Some(value.to_string())
651}
652
653fn entry_point(value: &str) -> Option<String> {
654 if value.is_empty() {
655 return None;
656 }
657 Some(value.to_string())
658}
659
660fn version(value: &str) -> Result<Option<u32>, CliError> {
661 if value.is_empty() {
662 return Ok(None);
663 }
664 let parsed = value
665 .parse::<u32>()
666 .map_err(|error| CliError::FailedToParseInt {
667 context: "version",
668 error,
669 })?;
670 Ok(Some(parsed))
671}
672
673pub(super) fn transfer_id(value: &str) -> Result<u64, CliError> {
674 value.parse().map_err(|error| CliError::FailedToParseInt {
675 context: "transfer_id",
676 error,
677 })
678}
679
680pub(super) fn block_identifier(
681 maybe_block_identifier: &str,
682) -> Result<Option<BlockIdentifier>, CliError> {
683 if maybe_block_identifier.is_empty() {
684 return Ok(None);
685 }
686
687 if maybe_block_identifier.len() == (Digest::LENGTH * 2) {
688 let hash = Digest::from_hex(maybe_block_identifier).map_err(|error| {
689 CliError::FailedToParseDigest {
690 context: "block_identifier",
691 error,
692 }
693 })?;
694 Ok(Some(BlockIdentifier::Hash(BlockHash::new(hash))))
695 } else {
696 let height =
697 maybe_block_identifier
698 .parse()
699 .map_err(|error| CliError::FailedToParseInt {
700 context: "block_identifier",
701 error,
702 })?;
703 Ok(Some(BlockIdentifier::Height(height)))
704 }
705}
706
707pub(super) fn deploy_hash(deploy_hash: &str) -> Result<DeployHash, CliError> {
708 let hash = Digest::from_hex(deploy_hash).map_err(|error| CliError::FailedToParseDigest {
709 context: "deploy hash",
710 error,
711 })?;
712 Ok(DeployHash::new(hash))
713}
714
715pub(super) fn key_for_query(key: &str) -> Result<Key, CliError> {
716 match Key::from_formatted_str(key) {
717 Ok(key) => Ok(key),
718 Err(error) => {
719 if let Ok(public_key) = PublicKey::from_hex(key) {
720 Ok(Key::Account(public_key.to_account_hash()))
721 } else {
722 Err(CliError::FailedToParseKey {
723 context: "key for query",
724 error,
725 })
726 }
727 }
728 }
729}
730
731pub(super) fn global_state_identifier(
733 maybe_block_id: &str,
734 maybe_state_root_hash: &str,
735) -> Result<Option<GlobalStateIdentifier>, CliError> {
736 if maybe_state_root_hash.is_empty() && maybe_block_id.is_empty() {
737 return Ok(None);
738 }
739
740 match block_identifier(maybe_block_id)? {
741 Some(BlockIdentifier::Hash(hash)) => {
742 return Ok(Some(GlobalStateIdentifier::BlockHash(hash)))
743 }
744 Some(BlockIdentifier::Height(height)) => {
745 return Ok(Some(GlobalStateIdentifier::BlockHeight(height)))
746 }
747 None => (),
748 }
749
750 if maybe_state_root_hash.is_empty() {
751 return Ok(None);
752 }
753
754 let state_root_hash =
755 Digest::from_hex(maybe_state_root_hash).map_err(|error| CliError::FailedToParseDigest {
756 context: "state root hash in global_state_identifier",
757 error,
758 })?;
759 Ok(Some(GlobalStateIdentifier::StateRootHash(state_root_hash)))
760}
761
762pub fn purse_identifier(purse_id: &str) -> Result<PurseIdentifier, CliError> {
764 const ACCOUNT_HASH_PREFIX: &str = "account-hash-";
765 const UREF_PREFIX: &str = "uref-";
766 const ENTITY_PREFIX: &str = "entity-";
767
768 if purse_id.is_empty() {
769 return Err(CliError::InvalidArgument {
770 context: "purse_identifier",
771 error: "cannot be empty string".to_string(),
772 });
773 }
774
775 if purse_id.starts_with(ACCOUNT_HASH_PREFIX) {
776 let account_hash = AccountHash::from_formatted_str(purse_id).map_err(|error| {
777 CliError::FailedToParseAccountHash {
778 context: "purse_identifier",
779 error,
780 }
781 })?;
782 return Ok(PurseIdentifier::MainPurseUnderAccountHash(account_hash));
783 }
784
785 if purse_id.starts_with(ENTITY_PREFIX) {
786 let entity_addr = EntityAddr::from_formatted_str(purse_id).map_err(|error| {
787 CliError::FailedToParseAddressableEntityHash {
788 context: "purse_identifier",
789 error,
790 }
791 })?;
792 return Ok(PurseIdentifier::MainPurseUnderEntityAddr(entity_addr));
793 }
794
795 if purse_id.starts_with(UREF_PREFIX) {
796 let uref =
797 URef::from_formatted_str(purse_id).map_err(|error| CliError::FailedToParseURef {
798 context: "purse_identifier",
799 error,
800 })?;
801 return Ok(PurseIdentifier::PurseUref(uref));
802 }
803
804 let public_key =
805 PublicKey::from_hex(purse_id).map_err(|error| CliError::FailedToParsePublicKey {
806 context: "purse_identifier".to_string(),
807 error,
808 })?;
809 Ok(PurseIdentifier::MainPurseUnderPublicKey(public_key))
810}
811
812pub fn account_identifier(account_identifier: &str) -> Result<AccountIdentifier, CliError> {
816 const ACCOUNT_HASH_PREFIX: &str = "account-hash-";
817
818 if account_identifier.is_empty() {
819 return Err(CliError::InvalidArgument {
820 context: "account_identifier",
821 error: "cannot be empty string".to_string(),
822 });
823 }
824
825 if account_identifier.starts_with(ACCOUNT_HASH_PREFIX) {
826 let account_hash =
827 AccountHash::from_formatted_str(account_identifier).map_err(|error| {
828 CliError::FailedToParseAccountHash {
829 context: "account_identifier",
830 error,
831 }
832 })?;
833 return Ok(AccountIdentifier::AccountHash(account_hash));
834 }
835
836 let public_key = PublicKey::from_hex(account_identifier).map_err(|error| {
837 CliError::FailedToParsePublicKey {
838 context: "account_identifier".to_string(),
839 error,
840 }
841 })?;
842 Ok(AccountIdentifier::PublicKey(public_key))
843}
844
845pub fn entity_identifier(entity_identifier: &str) -> Result<EntityIdentifier, CliError> {
849 const ENTITY_PREFIX: &str = "entity-";
850 const ACCOUNT_HASH_PREFIX: &str = "account-hash-";
851
852 if entity_identifier.is_empty() {
853 return Err(CliError::InvalidArgument {
854 context: "entity_identifier",
855 error: "cannot be empty string".to_string(),
856 });
857 }
858
859 if entity_identifier.starts_with(ACCOUNT_HASH_PREFIX) {
860 let account_hash = AccountHash::from_formatted_str(entity_identifier).map_err(|error| {
861 CliError::FailedToParseAccountHash {
862 context: "entity_identifier",
863 error,
864 }
865 })?;
866 return Ok(EntityIdentifier::AccountHash(account_hash));
867 }
868 if entity_identifier.starts_with(ENTITY_PREFIX) {
869 let entity_addr = EntityAddr::from_formatted_str(entity_identifier).map_err(|error| {
870 CliError::FailedToParseAddressableEntityHash {
871 context: "entity_identifier",
872 error,
873 }
874 })?;
875 return Ok(EntityIdentifier::EntityAddr(entity_addr));
876 }
877
878 let public_key = PublicKey::from_hex(entity_identifier).map_err(|error| {
879 CliError::FailedToParsePublicKey {
880 context: "entity_identifier".to_string(),
881 error,
882 }
883 })?;
884 Ok(EntityIdentifier::PublicKey(public_key))
885}
886
887pub(super) fn era_identifier(era_identifier: &str) -> Result<Option<EraIdentifier>, CliError> {
889 if era_identifier.is_empty() {
890 return Ok(None);
891 }
892 let era_id = era_identifier
893 .parse()
894 .map_err(|error| CliError::FailedToParseInt {
895 context: "era_identifier",
896 error,
897 })?;
898 Ok(Some(EraIdentifier::Era(era_id)))
899}
900
901pub(super) fn public_key(public_key: &str) -> Result<Option<PublicKey>, CliError> {
903 if public_key.is_empty() {
904 return Ok(None);
905 }
906 let key =
907 PublicKey::from_hex(public_key).map_err(|error| CliError::FailedToParsePublicKey {
908 context: "public_key".to_owned(),
909 error,
910 })?;
911 Ok(Some(key))
912}
913
914pub(super) fn pricing_mode(
915 pricing_mode_identifier_str: &str,
916 payment_amount_str: &str,
917 gas_price_tolerance_str: &str,
918 additional_computation_factor_str: &str,
919 standard_payment_str: &str,
920 maybe_receipt: Option<Digest>,
921) -> Result<PricingMode, CliError> {
922 match pricing_mode_identifier_str.to_lowercase().as_str() {
923 "classic" => {
924 if gas_price_tolerance_str.is_empty() {
925 return Err(CliError::InvalidArgument {
926 context: "gas_price_tolerance",
927 error: "Gas price tolerance is required".to_string(),
928 });
929 }
930 if payment_amount_str.is_empty() {
931 return Err(CliError::InvalidArgument {
932 context: "payment_amount",
933 error: "Payment amount is required".to_string(),
934 });
935 }
936 if standard_payment_str.is_empty() {
937 return Err(CliError::InvalidArgument {
938 context: "standard_payment",
939 error: "Standard payment flag is required".to_string(),
940 });
941 }
942 let gas_price_tolerance = gas_price_tolerance_str.parse::<u8>().map_err(|error| {
943 CliError::FailedToParseInt {
944 context: "gas_price_tolerance",
945 error,
946 }
947 })?;
948 let payment_amount =
949 payment_amount_str
950 .parse::<u64>()
951 .map_err(|error| CliError::FailedToParseInt {
952 context: "payment_amount",
953 error,
954 })?;
955 let standard_payment = standard_payment_str.parse::<bool>().map_err(|error| {
956 CliError::FailedToParseBool {
957 context: "standard_payment",
958 error,
959 }
960 })?;
961 Ok(PricingMode::PaymentLimited {
962 payment_amount,
963 gas_price_tolerance,
964 standard_payment,
965 })
966 }
967 "fixed" => {
968 if gas_price_tolerance_str.is_empty() {
969 return Err(CliError::InvalidArgument {
970 context: "gas_price_tolerance",
971 error: "Gas price tolerance is required".to_string(),
972 });
973 }
974 let gas_price_tolerance = gas_price_tolerance_str.parse::<u8>().map_err(|error| {
975 CliError::FailedToParseInt {
976 context: "gas_price_tolerance",
977 error,
978 }
979 })?;
980
981 let additional_computation_factor = if additional_computation_factor_str.is_empty() {
983 u8::default()
984 } else {
985 additional_computation_factor_str
986 .parse::<u8>()
987 .map_err(|error| CliError::FailedToParseInt {
988 context: "additional_computation_factor",
989 error,
990 })?
991 };
992 Ok(PricingMode::Fixed {
993 gas_price_tolerance,
994 additional_computation_factor,
995 })
996 }
997 "reserved" => {
998 if maybe_receipt.is_none() {
999 return Err(CliError::InvalidArgument {
1000 context: "receipt",
1001 error: "Receipt is required for reserved pricing mode".to_string(),
1002 });
1003 }
1004 Ok(PricingMode::Prepaid {
1005 receipt: maybe_receipt.unwrap_or_default(),
1006 })
1007 }
1008 _ => Err(CliError::InvalidArgument {
1009 context: "pricing_mode",
1010 error: "Invalid pricing mode identifier".to_string(),
1011 }),
1012 }
1013}
1014
1015pub(super) fn transaction_hash(transaction_hash: &str) -> Result<TransactionHash, CliError> {
1016 let digest =
1017 Digest::from_hex(transaction_hash).map_err(|error| CliError::FailedToParseDigest {
1018 context: "failed to parse digest from string for transaction hash",
1019 error,
1020 })?;
1021 Ok(TransactionHash::from(TransactionV1Hash::from(digest)))
1022}
1023
1024#[cfg(test)]
1025mod tests {
1026 use std::convert::TryFrom;
1027
1028 use super::*;
1029
1030 const HASH: &str = "09dcee4b212cfd53642ab323fbef07dafafc6f945a80a00147f62910a915c4e6";
1031 const NAME: &str = "name";
1032 const PACKAGE_HASH: &str = "09dcee4b212cfd53642ab323fbef07dafafc6f945a80a00147f62910a915c4e6";
1033 const PACKAGE_NAME: &str = "package_name";
1034 const PATH: &str = "./session.wasm";
1035 const ENTRY_POINT: &str = "entrypoint";
1036 const VERSION: &str = "3";
1037 const TRANSFER: bool = true;
1038
1039 impl<'a> TryFrom<SessionStrParams<'a>> for ExecutableDeployItem {
1040 type Error = CliError;
1041
1042 fn try_from(params: SessionStrParams<'a>) -> Result<ExecutableDeployItem, Self::Error> {
1043 session_executable_deploy_item(params)
1044 }
1045 }
1046
1047 impl<'a> TryFrom<PaymentStrParams<'a>> for ExecutableDeployItem {
1048 type Error = CliError;
1049
1050 fn try_from(params: PaymentStrParams<'a>) -> Result<ExecutableDeployItem, Self::Error> {
1051 payment_executable_deploy_item(params)
1052 }
1053 }
1054
1055 #[test]
1056 fn should_fail_to_parse_conflicting_arg_types() {
1057 let test_context = "parse_session_info args conflict (simple json)".to_string();
1058 let actual_error = session_executable_deploy_item(SessionStrParams {
1059 session_hash: "",
1060 session_name: "name",
1061 session_package_hash: "",
1062 session_package_name: "",
1063 session_path: "",
1064 session_bytes: Bytes::new(),
1065 session_args_simple: vec!["something:u32='0'"],
1066 session_args_json: "{\"name\":\"entry_point_name\",\"type\":\"Bool\",\"value\":false}",
1067 session_version: "",
1068 session_entry_point: "entrypoint",
1069 is_session_transfer: false,
1070 session_chunked_args: None,
1071 })
1072 .unwrap_err();
1073
1074 assert!(
1075 matches!(actual_error, CliError::ConflictingArguments { ref context, .. } if *context == test_context),
1076 "{:?}",
1077 actual_error
1078 );
1079
1080 let test_context = "parse_payment_info args conflict (simple json)";
1081 let actual_error = payment_executable_deploy_item(PaymentStrParams {
1082 payment_amount: "",
1083 payment_hash: "name",
1084 payment_name: "",
1085 payment_package_hash: "",
1086 payment_package_name: "",
1087 payment_path: "",
1088 payment_bytes: Bytes::new(),
1089 payment_args_simple: vec!["something:u32='0'"],
1090 payment_args_json: "{\"name\":\"entry_point_name\",\"type\":\"Bool\",\"value\":false}",
1091 payment_version: "",
1092 payment_entry_point: "entrypoint",
1093 })
1094 .unwrap_err();
1095 assert!(
1096 matches!(
1097 actual_error,
1098 CliError::ConflictingArguments { ref context, .. } if context == test_context
1099 ),
1100 "{:?}",
1101 actual_error
1102 );
1103 }
1104
1105 #[test]
1106 fn should_fail_to_parse_conflicting_session_parameters() {
1107 let test_context = String::from("parse_session_info");
1108 assert!(matches!(
1109 session_executable_deploy_item(SessionStrParams {
1110 session_hash: HASH,
1111 session_name: NAME,
1112 session_package_hash: PACKAGE_HASH,
1113 session_package_name: PACKAGE_NAME,
1114 session_path: PATH,
1115 session_bytes: Bytes::new(),
1116 session_args_simple: vec![],
1117 session_args_json: "",
1118 session_version: "",
1119 session_entry_point: "",
1120 is_session_transfer: false,
1121 session_chunked_args: None,
1122 }),
1123 Err(CliError::ConflictingArguments { context, .. }) if context == test_context
1124 ));
1125 }
1126
1127 #[test]
1128 fn should_fail_to_parse_conflicting_payment_parameters() {
1129 let test_context = String::from("parse_payment_info");
1130 assert!(matches!(
1131 payment_executable_deploy_item(PaymentStrParams {
1132 payment_amount: "12345",
1133 payment_hash: HASH,
1134 payment_name: NAME,
1135 payment_package_hash: PACKAGE_HASH,
1136 payment_package_name: PACKAGE_NAME,
1137 payment_path: PATH,
1138 payment_bytes: Bytes::new(),
1139 payment_args_simple: vec![],
1140 payment_args_json: "",
1141 payment_version: "",
1142 payment_entry_point: "",
1143 }),
1144 Err(CliError::ConflictingArguments { context, .. }) if context == test_context
1145 ));
1146 }
1147
1148 mod missing_args {
1149 use super::*;
1150
1151 #[test]
1152 fn session_name_should_fail_to_parse_missing_entry_point() {
1153 let result = session_executable_deploy_item(SessionStrParams {
1154 session_name: NAME,
1155 ..Default::default()
1156 });
1157
1158 assert!(matches!(
1159 result,
1160 Err(CliError::InvalidArgument {
1161 context: "parse_session_info",
1162 ..
1163 })
1164 ));
1165 }
1166
1167 #[test]
1168 fn session_hash_should_fail_to_parse_missing_entry_point() {
1169 let result = session_executable_deploy_item(SessionStrParams {
1170 session_hash: HASH,
1171 ..Default::default()
1172 });
1173
1174 assert!(matches!(
1175 result,
1176 Err(CliError::InvalidArgument {
1177 context: "parse_session_info",
1178 ..
1179 })
1180 ));
1181 }
1182
1183 #[test]
1184 fn session_package_hash_should_fail_to_parse_missing_entry_point() {
1185 let result = session_executable_deploy_item(SessionStrParams {
1186 session_package_hash: PACKAGE_HASH,
1187 ..Default::default()
1188 });
1189
1190 assert!(matches!(
1191 result,
1192 Err(CliError::InvalidArgument {
1193 context: "parse_session_info",
1194 ..
1195 })
1196 ));
1197 }
1198
1199 #[test]
1200 fn session_package_name_should_fail_to_parse_missing_entry_point() {
1201 let result = session_executable_deploy_item(SessionStrParams {
1202 session_package_name: PACKAGE_NAME,
1203 ..Default::default()
1204 });
1205
1206 assert!(matches!(
1207 result,
1208 Err(CliError::InvalidArgument {
1209 context: "parse_session_info",
1210 ..
1211 })
1212 ));
1213 }
1214
1215 #[test]
1216 fn payment_name_should_fail_to_parse_missing_entry_point() {
1217 let result = payment_executable_deploy_item(PaymentStrParams {
1218 payment_name: NAME,
1219 ..Default::default()
1220 });
1221
1222 assert!(matches!(
1223 result,
1224 Err(CliError::InvalidArgument {
1225 context: "parse_payment_info",
1226 ..
1227 })
1228 ));
1229 }
1230
1231 #[test]
1232 fn payment_hash_should_fail_to_parse_missing_entry_point() {
1233 let result = payment_executable_deploy_item(PaymentStrParams {
1234 payment_hash: HASH,
1235 ..Default::default()
1236 });
1237
1238 assert!(matches!(
1239 result,
1240 Err(CliError::InvalidArgument {
1241 context: "parse_payment_info",
1242 ..
1243 })
1244 ));
1245 }
1246
1247 #[test]
1248 fn payment_package_hash_should_fail_to_parse_missing_entry_point() {
1249 let result = payment_executable_deploy_item(PaymentStrParams {
1250 payment_package_hash: PACKAGE_HASH,
1251 ..Default::default()
1252 });
1253
1254 assert!(matches!(
1255 result,
1256 Err(CliError::InvalidArgument {
1257 context: "parse_payment_info",
1258 ..
1259 })
1260 ));
1261 }
1262
1263 #[test]
1264 fn payment_package_name_should_fail_to_parse_missing_entry_point() {
1265 let result = payment_executable_deploy_item(PaymentStrParams {
1266 payment_package_name: PACKAGE_NAME,
1267 ..Default::default()
1268 });
1269
1270 assert!(matches!(
1271 result,
1272 Err(CliError::InvalidArgument {
1273 context: "parse_payment_info",
1274 ..
1275 })
1276 ));
1277 }
1278 }
1279
1280 mod conflicting_args {
1281 use super::*;
1282
1283 macro_rules! impl_test_matrix {
1336 (
1337 type: $t:ident,
1339 context: $context:expr,
1341
1342 $module:ident [$(
1344 test[
1346 $arg:tt => $arg_value:expr,
1348 conflict: $con:tt => $con_value:expr,
1350 requires[$($req:tt => $req_value:expr),*],
1352 $test_fn_name:ident
1354 ]
1355 )+]
1356 ) => {
1357 #[cfg(test)]
1358 mod $module {
1359 use super::*;
1360
1361 $(
1362 #[test]
1363 fn $test_fn_name() {
1364 let info: Result<ExecutableDeployItem, _> = $t {
1365 $arg: $arg_value,
1366 $con: $con_value,
1367 $($req: $req_value,),*
1368 ..Default::default()
1369 }
1370 .try_into();
1371 let mut conflicting = vec![
1372 format!("{}={}", stringify!($arg), $arg_value),
1373 format!("{}={}", stringify!($con), $con_value),
1374 ];
1375 conflicting.sort();
1376 let _context_string = $context.to_string();
1377 assert!(matches!(
1378 info,
1379 Err(CliError::ConflictingArguments {
1380 context: _context_string,
1381 ..
1382 }
1383 ))
1384 );
1385 }
1386 )+
1387 }
1388 };
1389 }
1390
1391 impl_test_matrix![
1395 type: SessionStrParams,
1396 context: "parse_session_info",
1397 session_str_params[
1398
1399 test[session_path => PATH, conflict: session_package_hash => PACKAGE_HASH, requires[], path_conflicts_with_package_hash]
1401 test[session_path => PATH, conflict: session_package_name => PACKAGE_NAME, requires[], path_conflicts_with_package_name]
1402 test[session_path => PATH, conflict: session_hash => HASH, requires[], path_conflicts_with_hash]
1403 test[session_path => PATH, conflict: session_name => HASH, requires[], path_conflicts_with_name]
1404 test[session_path => PATH, conflict: session_version => VERSION, requires[], path_conflicts_with_version]
1405 test[session_path => PATH, conflict: session_entry_point => ENTRY_POINT, requires[], path_conflicts_with_entry_point]
1406 test[session_path => PATH, conflict: is_session_transfer => TRANSFER, requires[], path_conflicts_with_transfer]
1407
1408 test[session_name => NAME, conflict: session_package_hash => PACKAGE_HASH, requires[session_entry_point => ENTRY_POINT], name_conflicts_with_package_hash]
1410 test[session_name => NAME, conflict: session_package_name => PACKAGE_NAME, requires[session_entry_point => ENTRY_POINT], name_conflicts_with_package_name]
1411 test[session_name => NAME, conflict: session_hash => HASH, requires[session_entry_point => ENTRY_POINT], name_conflicts_with_hash]
1412 test[session_name => NAME, conflict: session_version => VERSION, requires[session_entry_point => ENTRY_POINT], name_conflicts_with_version]
1413 test[session_name => NAME, conflict: is_session_transfer => TRANSFER, requires[session_entry_point => ENTRY_POINT], name_conflicts_with_transfer]
1414
1415 test[session_hash => HASH, conflict: session_package_hash => PACKAGE_HASH, requires[session_entry_point => ENTRY_POINT], hash_conflicts_with_package_hash]
1417 test[session_hash => HASH, conflict: session_package_name => PACKAGE_NAME, requires[session_entry_point => ENTRY_POINT], hash_conflicts_with_package_name]
1418 test[session_hash => HASH, conflict: session_version => VERSION, requires[session_entry_point => ENTRY_POINT], hash_conflicts_with_version]
1419 test[session_hash => HASH, conflict: is_session_transfer => TRANSFER, requires[session_entry_point => ENTRY_POINT], hash_conflicts_with_transfer]
1420 test[session_package_name => PACKAGE_NAME, conflict: session_package_hash => PACKAGE_HASH, requires[session_entry_point => ENTRY_POINT], package_name_conflicts_with_package_hash]
1426 test[session_package_name => VERSION, conflict: is_session_transfer => TRANSFER, requires[session_entry_point => ENTRY_POINT], package_name_conflicts_with_transfer]
1427 test[session_package_hash => PACKAGE_HASH, conflict: is_session_transfer => TRANSFER, requires[session_entry_point => ENTRY_POINT], package_hash_conflicts_with_transfer]
1434 ]
1440 ];
1441
1442 impl_test_matrix![
1443 type: PaymentStrParams,
1444 context: "parse_payment_info",
1445 payment_str_params[
1446
1447 test[payment_amount => PATH, conflict: payment_package_hash => PACKAGE_HASH, requires[], amount_conflicts_with_package_hash]
1449 test[payment_amount => PATH, conflict: payment_package_name => PACKAGE_NAME, requires[], amount_conflicts_with_package_name]
1450 test[payment_amount => PATH, conflict: payment_hash => HASH, requires[], amount_conflicts_with_hash]
1451 test[payment_amount => PATH, conflict: payment_name => HASH, requires[], amount_conflicts_with_name]
1452 test[payment_amount => PATH, conflict: payment_version => VERSION, requires[], amount_conflicts_with_version]
1453 test[payment_amount => PATH, conflict: payment_entry_point => ENTRY_POINT, requires[], amount_conflicts_with_entry_point]
1454
1455 test[payment_path => PATH, conflict: payment_package_hash => PACKAGE_HASH, requires[], path_conflicts_with_package_hash]
1458 test[payment_path => PATH, conflict: payment_package_name => PACKAGE_NAME, requires[], path_conflicts_with_package_name]
1459 test[payment_path => PATH, conflict: payment_hash => HASH, requires[], path_conflicts_with_hash]
1460 test[payment_path => PATH, conflict: payment_name => HASH, requires[], path_conflicts_with_name]
1461 test[payment_path => PATH, conflict: payment_version => VERSION, requires[], path_conflicts_with_version]
1462 test[payment_path => PATH, conflict: payment_entry_point => ENTRY_POINT, requires[], path_conflicts_with_entry_point]
1463
1464 test[payment_name => NAME, conflict: payment_package_hash => PACKAGE_HASH, requires[payment_entry_point => ENTRY_POINT], name_conflicts_with_package_hash]
1467 test[payment_name => NAME, conflict: payment_package_name => PACKAGE_NAME, requires[payment_entry_point => ENTRY_POINT], name_conflicts_with_package_name]
1468 test[payment_name => NAME, conflict: payment_hash => HASH, requires[payment_entry_point => ENTRY_POINT], name_conflicts_with_hash]
1469 test[payment_name => NAME, conflict: payment_version => VERSION, requires[payment_entry_point => ENTRY_POINT], name_conflicts_with_version]
1470
1471 test[payment_hash => HASH, conflict: payment_package_hash => PACKAGE_HASH, requires[payment_entry_point => ENTRY_POINT], hash_conflicts_with_package_hash]
1474 test[payment_hash => HASH, conflict: payment_package_name => PACKAGE_NAME, requires[payment_entry_point => ENTRY_POINT], hash_conflicts_with_package_name]
1475 test[payment_hash => HASH, conflict: payment_version => VERSION, requires[payment_entry_point => ENTRY_POINT], hash_conflicts_with_version]
1476 test[payment_package_name => PACKAGE_NAME, conflict: payment_package_hash => PACKAGE_HASH, requires[payment_entry_point => ENTRY_POINT], package_name_conflicts_with_package_hash]
1482 ]
1494 ];
1495 }
1496
1497 mod param_tests {
1498 use super::*;
1499
1500 const HASH: &str = "09dcee4b212cfd53642ab323fbef07dafafc6f945a80a00147f62910a915c4e6";
1501 const NAME: &str = "name";
1502 const PKG_NAME: &str = "pkg_name";
1503 const PKG_HASH: &str = "09dcee4b212cfd53642ab323fbef07dafafc6f945a80a00147f62910a915c4e6";
1504 const ENTRYPOINT: &str = "entrypoint";
1505 const VERSION: &str = "4";
1506
1507 fn args_simple() -> Vec<&'static str> {
1508 vec!["name_01:bool='false'", "name_02:u32='42'"]
1509 }
1510
1511 mod session_params {
1513 use std::collections::BTreeMap;
1514
1515 use casper_types::CLValue;
1516
1517 use super::*;
1518
1519 #[test]
1520 pub fn with_hash() {
1521 let params: Result<ExecutableDeployItem, CliError> =
1522 SessionStrParams::with_hash(HASH, ENTRYPOINT, args_simple(), "").try_into();
1523 match params {
1524 Ok(item @ ExecutableDeployItem::StoredContractByHash { .. }) => {
1525 let actual: BTreeMap<String, CLValue> = item.args().clone().into();
1526 let mut expected = BTreeMap::new();
1527 expected.insert("name_01".to_owned(), CLValue::from_t(false).unwrap());
1528 expected.insert("name_02".to_owned(), CLValue::from_t(42u32).unwrap());
1529 assert_eq!(actual, expected);
1530 }
1531 other => panic!("incorrect type parsed {:?}", other),
1532 }
1533 }
1534
1535 #[test]
1536 pub fn with_name() {
1537 let params: Result<ExecutableDeployItem, CliError> =
1538 SessionStrParams::with_name(NAME, ENTRYPOINT, args_simple(), "").try_into();
1539 match params {
1540 Ok(item @ ExecutableDeployItem::StoredContractByName { .. }) => {
1541 let actual: BTreeMap<String, CLValue> = item.args().clone().into();
1542 let mut expected = BTreeMap::new();
1543 expected.insert("name_01".to_owned(), CLValue::from_t(false).unwrap());
1544 expected.insert("name_02".to_owned(), CLValue::from_t(42u32).unwrap());
1545 assert_eq!(actual, expected);
1546 }
1547 other => panic!("incorrect type parsed {:?}", other),
1548 }
1549 }
1550
1551 #[test]
1552 pub fn with_package_name() {
1553 let params: Result<ExecutableDeployItem, CliError> =
1554 SessionStrParams::with_package_name(
1555 PKG_NAME,
1556 VERSION,
1557 ENTRYPOINT,
1558 args_simple(),
1559 "",
1560 )
1561 .try_into();
1562 match params {
1563 Ok(item @ ExecutableDeployItem::StoredVersionedContractByName { .. }) => {
1564 let actual: BTreeMap<String, CLValue> = item.args().clone().into();
1565 let mut expected = BTreeMap::new();
1566 expected.insert("name_01".to_owned(), CLValue::from_t(false).unwrap());
1567 expected.insert("name_02".to_owned(), CLValue::from_t(42u32).unwrap());
1568 assert_eq!(actual, expected);
1569 }
1570 other => panic!("incorrect type parsed {:?}", other),
1571 }
1572 }
1573
1574 #[test]
1575 pub fn with_package_hash() {
1576 let params: Result<ExecutableDeployItem, CliError> =
1577 SessionStrParams::with_package_hash(
1578 PKG_HASH,
1579 VERSION,
1580 ENTRYPOINT,
1581 args_simple(),
1582 "",
1583 )
1584 .try_into();
1585 match params {
1586 Ok(item @ ExecutableDeployItem::StoredVersionedContractByHash { .. }) => {
1587 let actual: BTreeMap<String, CLValue> = item.args().clone().into();
1588 let mut expected = BTreeMap::new();
1589 expected.insert("name_01".to_owned(), CLValue::from_t(false).unwrap());
1590 expected.insert("name_02".to_owned(), CLValue::from_t(42u32).unwrap());
1591 assert_eq!(actual, expected);
1592 }
1593 other => panic!("incorrect type parsed {:?}", other),
1594 }
1595 }
1596 }
1597 mod payment_params {
1599 use std::collections::BTreeMap;
1600
1601 use casper_types::{CLValue, U512};
1602
1603 use super::*;
1604
1605 #[test]
1606 pub fn with_amount() {
1607 let params: Result<ExecutableDeployItem, CliError> =
1608 PaymentStrParams::with_amount("100").try_into();
1609 match params {
1610 Ok(item @ ExecutableDeployItem::ModuleBytes { .. }) => {
1611 let amount = CLValue::from_t(U512::from(100)).unwrap();
1612 assert_eq!(item.args().get("amount"), Some(&amount));
1613 }
1614 other => panic!("incorrect type parsed {:?}", other),
1615 }
1616 }
1617
1618 #[test]
1619 pub fn with_hash() {
1620 let params: Result<ExecutableDeployItem, CliError> =
1621 PaymentStrParams::with_hash(HASH, ENTRYPOINT, args_simple(), "").try_into();
1622 match params {
1623 Ok(item @ ExecutableDeployItem::StoredContractByHash { .. }) => {
1624 let actual: BTreeMap<String, CLValue> = item.args().clone().into();
1625 let mut expected = BTreeMap::new();
1626 expected.insert("name_01".to_owned(), CLValue::from_t(false).unwrap());
1627 expected.insert("name_02".to_owned(), CLValue::from_t(42u32).unwrap());
1628 assert_eq!(actual, expected);
1629 }
1630 other => panic!("incorrect type parsed {:?}", other),
1631 }
1632 }
1633
1634 #[test]
1635 pub fn with_name() {
1636 let params: Result<ExecutableDeployItem, CliError> =
1637 PaymentStrParams::with_name(NAME, ENTRYPOINT, args_simple(), "").try_into();
1638 match params {
1639 Ok(item @ ExecutableDeployItem::StoredContractByName { .. }) => {
1640 let actual: BTreeMap<String, CLValue> = item.args().clone().into();
1641 let mut expected = BTreeMap::new();
1642 expected.insert("name_01".to_owned(), CLValue::from_t(false).unwrap());
1643 expected.insert("name_02".to_owned(), CLValue::from_t(42u32).unwrap());
1644 assert_eq!(actual, expected);
1645 }
1646 other => panic!("incorrect type parsed {:?}", other),
1647 }
1648 }
1649
1650 #[test]
1651 pub fn with_package_name() {
1652 let params: Result<ExecutableDeployItem, CliError> =
1653 PaymentStrParams::with_package_name(
1654 PKG_NAME,
1655 VERSION,
1656 ENTRYPOINT,
1657 args_simple(),
1658 "",
1659 )
1660 .try_into();
1661 match params {
1662 Ok(item @ ExecutableDeployItem::StoredVersionedContractByName { .. }) => {
1663 let actual: BTreeMap<String, CLValue> = item.args().clone().into();
1664 let mut expected = BTreeMap::new();
1665 expected.insert("name_01".to_owned(), CLValue::from_t(false).unwrap());
1666 expected.insert("name_02".to_owned(), CLValue::from_t(42u32).unwrap());
1667 assert_eq!(actual, expected);
1668 }
1669 other => panic!("incorrect type parsed {:?}", other),
1670 }
1671 }
1672
1673 #[test]
1674 pub fn with_package_hash() {
1675 let params: Result<ExecutableDeployItem, CliError> =
1676 PaymentStrParams::with_package_hash(
1677 PKG_HASH,
1678 VERSION,
1679 ENTRYPOINT,
1680 args_simple(),
1681 "",
1682 )
1683 .try_into();
1684 match params {
1685 Ok(item @ ExecutableDeployItem::StoredVersionedContractByHash { .. }) => {
1686 let actual: BTreeMap<String, CLValue> = item.args().clone().into();
1687 let mut expected = BTreeMap::new();
1688 expected.insert("name_01".to_owned(), CLValue::from_t(false).unwrap());
1689 expected.insert("name_02".to_owned(), CLValue::from_t(42u32).unwrap());
1690 assert_eq!(actual, expected);
1691 }
1692 other => panic!("incorrect type parsed {:?}", other),
1693 }
1694 }
1695 }
1696 }
1697
1698 mod account_identifier {
1699 use super::*;
1700
1701 #[test]
1702 pub fn should_parse_valid_account_hash() {
1703 let account_hash =
1704 "account-hash-c029c14904b870e64c1d443d428c606740e82f341bea0f8542ca6494cef1383e";
1705 let parsed = account_identifier(account_hash).unwrap();
1706 let expected = AccountHash::from_formatted_str(account_hash).unwrap();
1707 assert_eq!(parsed, AccountIdentifier::AccountHash(expected));
1708 }
1709
1710 #[test]
1711 pub fn should_parse_valid_public_key() {
1712 let public_key = "01567f0f205e83291312cd82988d66143d376cee7de904dd2605d3f4bbb69b3c80";
1713 let parsed = account_identifier(public_key).unwrap();
1714 let expected = PublicKey::from_hex(public_key).unwrap();
1715 assert_eq!(parsed, AccountIdentifier::PublicKey(expected));
1716 }
1717
1718 #[test]
1719 pub fn should_fail_to_parse_invalid_account_hash() {
1720 let account_hash =
1722 "account-hash-c029c14904b870e1d443d428c606740e82f341bea0f8542ca6494cef1383e";
1723 let parsed = account_identifier(account_hash);
1724 assert!(parsed.is_err());
1725 }
1726
1727 #[test]
1728 pub fn should_fail_to_parse_invalid_public_key() {
1729 let public_key = "01567f0f205e83291312cd82988d66143d376cee7de904dd26054bbb69b3c80";
1731 let parsed = account_identifier(public_key);
1732 assert!(parsed.is_err());
1733 }
1734 }
1735
1736 mod entity_identifier {
1737 use super::*;
1738
1739 #[test]
1740 pub fn should_parse_valid_contract_entity_addr() {
1741 let entity_addr =
1742 "entity-contract-c029c14904b870e64c1d443d428c606740e82f341bea0f8542ca6494cef1383e";
1743 let parsed = entity_identifier(entity_addr).unwrap();
1744 assert_eq!(
1745 parsed,
1746 EntityIdentifier::EntityAddr(
1747 EntityAddr::from_formatted_str(entity_addr).expect("should parse EntityAddr")
1748 )
1749 );
1750 }
1751
1752 #[test]
1753 pub fn should_parse_valid_account_entity_addr() {
1754 let entity_addr =
1755 "entity-account-c029c14904b870e64c1d443d428c606740e82f341bea0f8542ca6494cef1383e";
1756 let parsed = entity_identifier(entity_addr).unwrap();
1757 assert_eq!(
1758 parsed,
1759 EntityIdentifier::EntityAddr(
1760 EntityAddr::from_formatted_str(entity_addr).expect("should parse EntityAddr")
1761 )
1762 );
1763 }
1764
1765 #[test]
1766 pub fn should_parse_valid_public_key() {
1767 let public_key = "01567f0f205e83291312cd82988d66143d376cee7de904dd2605d3f4bbb69b3c80";
1768 let parsed = entity_identifier(public_key).unwrap();
1769 let expected = PublicKey::from_hex(public_key).unwrap();
1770 assert_eq!(parsed, EntityIdentifier::PublicKey(expected));
1771 }
1772
1773 #[test]
1774 pub fn should_fail_to_parse_invalid_entity_hash() {
1775 let entity_hash =
1777 "contract-addressable-entity-c029c14904b870e64c1d443d428c606740e82f341bea0f8542ca6494cef138";
1778 let parsed = entity_identifier(entity_hash);
1779 assert!(parsed.is_err());
1780 }
1781
1782 #[test]
1783 pub fn should_fail_to_parse_invalid_public_key() {
1784 let public_key = "01567f0f205e83291312cd82988d66143d376cee7de904dd26054bbb69b3c80";
1786 let parsed = entity_identifier(public_key);
1787 assert!(parsed.is_err());
1788 }
1789 }
1790
1791 mod era_identifier {
1792 use casper_types::EraId;
1793
1794 use super::*;
1795
1796 #[test]
1797 pub fn should_parse_valid_era_id() {
1798 let era_id = "123";
1799 let parsed = era_identifier(era_id).unwrap();
1800 assert!(
1801 matches!(parsed, Some(EraIdentifier::Era(id)) if id == EraId::new(123)),
1802 "{:?}",
1803 parsed
1804 );
1805 }
1806
1807 #[test]
1808 pub fn should_fail_to_parse_invalid_era_id() {
1809 let era_id = "invalid";
1810 let parsed = era_identifier(era_id);
1811 assert!(parsed.is_err());
1812 }
1813 }
1814
1815 mod public_key {
1816 use super::*;
1817
1818 #[test]
1819 pub fn should_parse_valid_public_key() {
1820 let str = "01567f0f205e83291312cd82988d66143d376cee7de904dd2605d3f4bbb69b3c80";
1821 let parsed = public_key(str).unwrap();
1822 let expected = PublicKey::from_hex(str).unwrap();
1823 assert_eq!(parsed, Some(expected));
1824 }
1825
1826 #[test]
1827 pub fn should_fail_to_parse_invalid_public_key() {
1828 let str = "01567f0f205e83291312cd82988d66143d376cee7de904dd26054bbb69b3c80";
1830 let parsed = public_key(str);
1831 assert!(parsed.is_err());
1832 }
1833 }
1834
1835 mod pricing_mode {
1836 use super::*;
1837
1838 const VALID_HASH: &str = "09dcee4b212cfd53642ab323fbef07dafafc6f945a80a00147f62910a915c4e6";
1839 #[test]
1840 fn should_parse_fixed_pricing_mode_identifier() {
1841 let pricing_mode_str = "fixed";
1842 let payment_amount = "";
1843 let gas_price_tolerance = "10";
1844 let additional_computation_factor = "1";
1845 let standard_payment = "";
1846 let parsed = pricing_mode(
1847 pricing_mode_str,
1848 payment_amount,
1849 gas_price_tolerance,
1850 additional_computation_factor,
1851 standard_payment,
1852 None,
1853 )
1854 .unwrap();
1855 assert_eq!(
1856 parsed,
1857 PricingMode::Fixed {
1858 additional_computation_factor: 1,
1859 gas_price_tolerance: 10,
1860 }
1861 );
1862 }
1863
1864 #[test]
1865 fn should_parse_fixed_pricing_mode_identifier_without_additional_computation_factor() {
1866 let pricing_mode_str = "fixed";
1867 let payment_amount = "";
1868 let gas_price_tolerance = "10";
1869 let additional_computation_factor = "";
1870 let standard_payment = "";
1871 let parsed = pricing_mode(
1872 pricing_mode_str,
1873 payment_amount,
1874 gas_price_tolerance,
1875 additional_computation_factor,
1876 standard_payment,
1877 None,
1878 )
1879 .unwrap();
1880 assert_eq!(
1881 parsed,
1882 PricingMode::Fixed {
1883 additional_computation_factor: 0,
1884 gas_price_tolerance: 10,
1885 }
1886 );
1887 }
1888
1889 #[test]
1890 fn should_parse_reserved_pricing_mode() {
1891 let pricing_mode_str = "reserved";
1892 let payment_amount = "";
1893 let gas_price_tolerance = "";
1894 let additional_computation_factor = "0";
1895 let standard_payment = "";
1896 let parsed = pricing_mode(
1897 pricing_mode_str,
1898 payment_amount,
1899 gas_price_tolerance,
1900 additional_computation_factor,
1901 standard_payment,
1902 Some(Digest::from_hex(VALID_HASH).unwrap()),
1903 )
1904 .unwrap();
1905 assert_eq!(
1906 parsed,
1907 PricingMode::Prepaid {
1908 receipt: Digest::from_hex(VALID_HASH).unwrap(),
1909 }
1910 );
1911 }
1912 #[test]
1913 fn should_parse_classic_pricing_mode() {
1914 let pricing_mode_str = "classic";
1915 let payment_amount = "10";
1916 let standard_payment = "true";
1917 let gas_price_tolerance = "10";
1918 let additional_computation_factor = "0";
1919 let parsed = pricing_mode(
1920 pricing_mode_str,
1921 payment_amount,
1922 gas_price_tolerance,
1923 additional_computation_factor,
1924 standard_payment,
1925 None,
1926 )
1927 .unwrap();
1928 assert_eq!(
1929 parsed,
1930 PricingMode::PaymentLimited {
1931 payment_amount: 10,
1932 gas_price_tolerance: 10,
1933 standard_payment: true,
1934 }
1935 );
1936 }
1937
1938 #[test]
1939 fn should_fail_to_parse_invalid_pricing_mode() {
1940 let pricing_mode_str = "invalid";
1941 let payment_amount = "10";
1942 let standard_payment = "true";
1943 let gas_price_tolerance = "10";
1944 let additional_computation_factor = "0";
1945 let parsed = pricing_mode(
1946 pricing_mode_str,
1947 payment_amount,
1948 gas_price_tolerance,
1949 additional_computation_factor,
1950 standard_payment,
1951 None,
1952 );
1953 assert!(parsed.is_err());
1954 assert!(matches!(parsed, Err(CliError::InvalidArgument { .. })));
1955 }
1956
1957 #[test]
1958 fn should_fail_to_parse_invalid_additional_computation_factor() {
1959 let pricing_mode_str = "fixed";
1960 let payment_amount = "10";
1961 let standard_payment = "true";
1962 let gas_price_tolerance = "10";
1963 let additional_computation_factor = "invalid";
1964 let parsed = pricing_mode(
1965 pricing_mode_str,
1966 payment_amount,
1967 gas_price_tolerance,
1968 additional_computation_factor,
1969 standard_payment,
1970 None,
1971 );
1972 assert!(parsed.is_err());
1973 assert!(matches!(parsed, Err(CliError::FailedToParseInt { .. })));
1974 }
1975
1976 #[test]
1977 fn should_fail_to_parse_classic_without_amount() {
1978 let pricing_mode_str = "classic";
1979 let payment_amount = "";
1980 let standard_payment = "true";
1981 let gas_price_tolerance = "10";
1982 let additional_computation_factor = "0";
1983 let parsed = pricing_mode(
1984 pricing_mode_str,
1985 payment_amount,
1986 gas_price_tolerance,
1987 additional_computation_factor,
1988 standard_payment,
1989 None,
1990 );
1991 assert!(parsed.is_err());
1992 assert!(matches!(parsed, Err(CliError::InvalidArgument { .. })));
1993 }
1994 }
1995 mod transaction_hash {
1996 use super::*;
1997 const VALID_HASH: &str = "09dcee4b212cfd53642ab323fbef07dafafc6f945a80a00147f62910a915c4e6";
1998 const INVALID_HASH: &str =
1999 "09dcee4b212cfd53642ab323fbef07dafafc6f945a80a00147f62910a915c4e";
2000 #[test]
2001 fn should_parse_transaction_hash() {
2002 let parsed = transaction_hash(VALID_HASH);
2003 assert!(parsed.is_ok());
2004 assert_eq!(
2005 parsed.unwrap(),
2006 TransactionHash::from(TransactionV1Hash::from(
2007 Digest::from_hex(VALID_HASH).unwrap()
2008 ))
2009 );
2010 }
2011 #[test]
2012 fn should_fail_to_parse_incorrect_hash() {
2013 let parsed = transaction_hash(INVALID_HASH);
2014 assert!(parsed.is_err());
2015 assert!(matches!(
2016 parsed,
2017 Err(CliError::FailedToParseDigest {
2018 context: "failed to parse digest from string for transaction hash",
2019 ..
2020 })
2021 ));
2022 }
2023 }
2024}