From 8f94939fcb1df15d15e9e9f7670c597ab4d4a6fe Mon Sep 17 00:00:00 2001 From: Gregory Roussac Date: Mon, 18 Dec 2023 23:30:30 +0100 Subject: [PATCH] Proposition of changes into Casper Types to support a Rust SDK for 1.6 (#110) * feat std-fs-io * Compile wasm * avoid including dependencies required for bin target only when building the lib * further restrict functions and tests included when std-fs-io feature is not enabled * extend CI tests * update patch section in manifest * update deploy builder to not return invalid deploy --------- Co-authored-by: Fraser Hutchison --- .github/workflows/ci-casper-client-rs.yml | 19 ++ Cargo.toml | 42 +-- lib/cli.rs | 79 +++-- lib/cli/deploy.rs | 88 ++++-- lib/cli/deploy_str_params.rs | 3 +- lib/cli/error.rs | 2 +- lib/cli/json_args.rs | 3 +- lib/cli/parse.rs | 348 +++++++++++++++------- lib/cli/payment_str_params.rs | 26 +- lib/cli/session_str_params.rs | 26 +- lib/cli/simple_args.rs | 6 +- lib/cli/tests.rs | 61 ++-- lib/error.rs | 9 + lib/json_rpc/call.rs | 3 + lib/lib.rs | 22 +- lib/rpcs.rs | 2 + lib/rpcs/v1_4_5/get_account.rs | 2 +- lib/rpcs/v1_4_5/get_auction_info.rs | 2 +- lib/rpcs/v1_4_5/get_balance.rs | 2 +- lib/rpcs/v1_4_5/get_block.rs | 2 +- lib/rpcs/v1_4_5/get_block_transfers.rs | 2 +- lib/rpcs/v1_4_5/get_deploy.rs | 2 +- lib/rpcs/v1_4_5/get_dictionary_item.rs | 4 +- lib/rpcs/v1_4_5/get_era_info.rs | 2 +- lib/rpcs/v1_4_5/get_node_status.rs | 2 +- lib/rpcs/v1_4_5/get_peers.rs | 2 +- lib/rpcs/v1_4_5/get_state_root_hash.rs | 2 +- lib/rpcs/v1_4_5/get_validator_changes.rs | 2 +- lib/rpcs/v1_4_5/list_rpcs.rs | 2 +- lib/rpcs/v1_4_5/put_deploy.rs | 2 +- lib/rpcs/v1_4_5/query_global_state.rs | 2 +- lib/rpcs/v1_5_0.rs | 8 +- lib/rpcs/v1_5_0/get_chainspec.rs | 2 +- lib/rpcs/v1_5_0/get_deploy.rs | 2 +- lib/rpcs/v1_5_0/get_era_summary.rs | 2 +- lib/rpcs/v1_5_0/get_node_status.rs | 2 +- lib/rpcs/v1_5_0/query_balance.rs | 2 +- lib/rpcs/v1_5_0/speculative_exec.rs | 2 +- lib/rpcs/v1_6_0/get_account.rs | 2 +- lib/rpcs/v1_6_0/query_global_state.rs | 2 +- lib/types/block.rs | 6 + lib/types/deploy.rs | 58 +++- lib/types/executable_deploy_item.rs | 3 +- lib/types/execution_result.rs | 2 +- src/keygen.rs | 12 + src/main.rs | 2 +- 46 files changed, 616 insertions(+), 262 deletions(-) diff --git a/.github/workflows/ci-casper-client-rs.yml b/.github/workflows/ci-casper-client-rs.yml index b3c1330f..05bab790 100644 --- a/.github/workflows/ci-casper-client-rs.yml +++ b/.github/workflows/ci-casper-client-rs.yml @@ -27,6 +27,7 @@ jobs: toolchain: stable profile: minimal components: rustfmt, clippy + target: wasm32-unknown-unknown - name: Fmt uses: actions-rs/cargo@v1 @@ -45,6 +46,12 @@ jobs: command: clippy args: --all-targets + - name: Clippy with no features + uses: actions-rs/cargo@v1 + with: + command: clippy + args: --all-targets --no-default-features + - name: Doc uses: actions-rs/cargo@v1 with: @@ -55,3 +62,15 @@ jobs: uses: actions-rs/cargo@v1 with: command: test + + - name: Test with no features + uses: actions-rs/cargo@v1 + with: + command: test + args: --no-default-features + + - name: Build lib for Wasm with no features + uses: actions-rs/cargo@v1 + with: + command: build + args: --lib --target wasm32-unknown-unknown --no-default-features diff --git a/Cargo.toml b/Cargo.toml index 78eb37ca..c6a4cfa9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,7 @@ repository = "https://github.com/casper-ecosystem/casper-client-rs" license = "Apache-2.0" [lib] +crate-type = ["cdylib", "rlib"] name = "casper_client" path = "lib/lib.rs" @@ -18,43 +19,48 @@ path = "lib/lib.rs" name = "casper-client" path = "src/main.rs" doc = false +required-features = ["async-trait", "clap", "clap_complete", "tokio", "std-fs-io"] + +[features] +default = ["async-trait", "clap", "clap_complete", "tokio", "std-fs-io"] +std-fs-io = ["casper-types/std-fs-io"] [dependencies] -async-trait = "0.1.59" +async-trait = { version = "0.1.74", optional = true } base16 = "0.2.1" -casper-hashing = "2.0.0" -casper-types = { version = "3.0.0", features = ["std"] } -clap = { version = "4", features = ["cargo", "deprecated", "wrap_help"] } -clap_complete = "4" +casper-hashing = "3.0.0" +casper-types = { version = "4.0.1", features = ["std"] } +clap = { version = "4.4.10", optional = true, features = ["cargo", "deprecated", "wrap_help"] } +clap_complete = { version = "4.4.4", optional = true } hex-buffer-serde = "0.4.0" -humantime = "2" -itertools = "0.11.0" +humantime = "2.1.0" +itertools = "0.12.0" jsonrpc-lite = "0.6.0" num-traits = "0.2.15" -once_cell = "1" +once_cell = "1.18.0" rand = "0.8.5" -reqwest = { version = "0.11.13", features = ["json"] } -schemars = "0.8" -serde = { version = "1", default-features = false, features = ["derive"] } -serde_json = { version = "1", features = ["preserve_order"] } -thiserror = "1.0.34" -tokio = { version = "1.23.0", features = ["macros", "net", "rt-multi-thread", "sync", "time", ]} -uint = "0.9.4" +reqwest = { version = "0.11.22", features = ["json"] } +schemars = "=0.8.5" +serde = { version = "1.0.193", default-features = false, features = ["derive"] } +serde_json = { version = "1.0.108", features = ["preserve_order"] } +thiserror = "1.0.50" +tokio = { version = "1.34.0", optional = true, features = ["macros", "rt", "sync", "time"] } +uint = "0.9.5" [dev-dependencies] -tempfile = "3.7.1" +tempfile = "3.8.1" [build-dependencies] vergen = { version = "7", default-features = false, features = ["git"] } [patch.crates-io] casper-hashing = { git = "https://github.com/casper-network/casper-node", branch = "dev" } -casper-types = { git = "https://github.com/casper-network/casper-node", branch = "dev"} +casper-types = { git = "https://github.com/casper-network/casper-node", branch = "dev" } [package.metadata.deb] features = ["vendored-openssl"] revision = "0" -assets = [["./target/release/casper-client", "/usr/bin/casper-client", "755"], ] +assets = [["./target/release/casper-client", "/usr/bin/casper-client", "755"]] extended-description = """ Package for Casper Client to connect to Casper Node. diff --git a/lib/cli.rs b/lib/cli.rs index e6f1658a..19102854 100644 --- a/lib/cli.rs +++ b/lib/cli.rs @@ -20,7 +20,7 @@ //! * `maybe_block_id` - Must be a hex-encoded, 32-byte hash digest or a `u64` representing the //! [`Block`] height or empty. If empty, the latest `Block` known on the server will be used. -/// Deploy module. +/// Functions for creating Deploys. pub mod deploy; mod deploy_str_params; mod dictionary_item_str_params; @@ -33,6 +33,7 @@ mod simple_args; #[cfg(test)] mod tests; +#[cfg(feature = "std-fs-io")] use serde::Serialize; use casper_hashing::Digest; @@ -51,22 +52,25 @@ use crate::{ }, DictionaryItemIdentifier, }, + types::Deploy, SuccessResponse, }; #[cfg(doc)] -use crate::{Account, Block, Deploy, Error, StoredValue, Transfer}; +use crate::{Account, Block, Error, StoredValue, Transfer}; #[cfg(doc)] use casper_types::PublicKey; pub use deploy_str_params::DeployStrParams; pub use dictionary_item_str_params::DictionaryItemStrParams; pub use error::CliError; -use json_args::JsonArg; pub use json_args::{ - help as json_args_help, Error as JsonArgsError, ErrorDetails as JsonArgsErrorDetails, + help as json_args_help, Error as JsonArgsError, ErrorDetails as JsonArgsErrorDetails, JsonArg, +}; +pub use parse::{ + account_identifier as parse_account_identifier, purse_identifier as parse_purse_identifier, }; pub use payment_str_params::PaymentStrParams; pub use session_str_params::SessionStrParams; -pub use simple_args::help as simple_args_help; +pub use simple_args::{help as simple_args_help, insert_arg}; /// Creates a [`Deploy`] and sends it to the network for execution. /// @@ -111,26 +115,32 @@ pub async fn speculative_put_deploy( .await .map_err(CliError::from) } -/// Creates a [`Deploy`] and outputs it to a file or stdout. +/// Returns a [`Deploy`] and outputs it to a file or stdout if the `std-fs-io` feature is enabled. /// /// As a file, the `Deploy` can subsequently be signed by other parties using [`sign_deploy_file`] -/// and then sent to the network for execution using [`send_deploy_file`]. -/// -/// `maybe_output_path` specifies the output file path, or if empty, will print it to `stdout`. If -/// `force` is true, and a file exists at `maybe_output_path`, it will be overwritten. If `force` -/// is false and a file exists at `maybe_output_path`, [`Error::FileAlreadyExists`] is returned -/// and the file will not be written. +/// and then sent to the network for execution using [`send_deploy_file`]. Alternatively, the +/// returned `Deploy` can be signed via the [`Deploy::sign`] method. +/// +/// If the `std-fs-io` feature is NOT enabled, `maybe_output_path` and `force` are ignored. +/// Otherwise, `maybe_output_path` specifies the output file path, or if empty, will print it to +/// `stdout`. If `force` is true, and a file exists at `maybe_output_path`, it will be +/// overwritten. If `force` is false and a file exists at `maybe_output_path`, +/// [`Error::FileAlreadyExists`] is returned and the file will not be written. pub fn make_deploy( - maybe_output_path: &str, + #[allow(unused_variables)] maybe_output_path: &str, deploy_params: DeployStrParams<'_>, session_params: SessionStrParams<'_>, payment_params: PaymentStrParams<'_>, - force: bool, -) -> Result<(), CliError> { - let output = parse::output_kind(maybe_output_path, force); + #[allow(unused_variables)] force: bool, +) -> Result { let deploy = deploy::with_payment_and_session(deploy_params, payment_params, session_params, true)?; - crate::output_deploy(output, &deploy).map_err(CliError::from) + #[cfg(feature = "std-fs-io")] + { + let output = parse::output_kind(maybe_output_path, force); + crate::output_deploy(output, &deploy).map_err(CliError::from)?; + } + Ok(deploy) } /// Reads a previously-saved [`Deploy`] from a file, cryptographically signs it, and outputs it to a @@ -140,6 +150,7 @@ pub fn make_deploy( /// `force` is true, and a file exists at `maybe_output_path`, it will be overwritten. If `force` /// is false and a file exists at `maybe_output_path`, [`Error::FileAlreadyExists`] is returned /// and the file will not be written. +#[cfg(feature = "std-fs-io")] pub fn sign_deploy_file( input_path: &str, secret_key_path: &str, @@ -154,6 +165,7 @@ pub fn sign_deploy_file( /// Reads a previously-saved [`Deploy`] from a file and sends it to the network for execution. /// /// For details of the parameters, see [the module docs](crate::cli#common-parameters). +#[cfg(feature = "std-fs-io")] pub async fn send_deploy_file( maybe_rpc_id: &str, node_address: &str, @@ -171,6 +183,7 @@ pub async fn send_deploy_file( /// Reads a previously-saved [`Deploy`] from a file and sends it to the specified node for /// speculative execution. /// For details of the parameters, see [the module docs](crate::cli#common-parameters). +#[cfg(feature = "std-fs-io")] pub async fn speculative_send_deploy_file( maybe_block_id: &str, maybe_rpc_id: &str, @@ -264,25 +277,27 @@ pub async fn speculative_transfer( .map_err(CliError::from) } -/// Creates a transfer [`Deploy`] and outputs it to a file or stdout. +/// Returns a transfer [`Deploy`] and outputs it to a file or stdout if the `std-fs-io` feature is +/// enabled. /// /// As a file, the `Deploy` can subsequently be signed by other parties using [`sign_deploy_file`] -/// and then sent to the network for execution using [`send_deploy_file`]. -/// -/// `maybe_output_path` specifies the output file path, or if empty, will print it to `stdout`. If -/// `force` is true, and a file exists at `maybe_output_path`, it will be overwritten. If `force` -/// is false and a file exists at `maybe_output_path`, [`Error::FileAlreadyExists`] is returned -/// and the file will not be written. +/// and then sent to the network for execution using [`send_deploy_file`]. Alternatively, the +/// returned `Deploy` can be signed via the [`Deploy::sign`] method. +/// +/// If the `std-fs-io` feature is NOT enabled, `maybe_output_path` and `force` are ignored. +/// Otherwise, `maybe_output_path` specifies the output file path, or if empty, will print it to +/// `stdout`. If `force` is true, and a file exists at `maybe_output_path`, it will be +/// overwritten. If `force` is false and a file exists at `maybe_output_path`, +/// [`Error::FileAlreadyExists`] is returned and the file will not be written. pub fn make_transfer( - maybe_output_path: &str, + #[allow(unused_variables)] maybe_output_path: &str, amount: &str, target_account: &str, transfer_id: &str, deploy_params: DeployStrParams<'_>, payment_params: PaymentStrParams<'_>, - force: bool, -) -> Result<(), CliError> { - let output = parse::output_kind(maybe_output_path, force); + #[allow(unused_variables)] force: bool, +) -> Result { let deploy = deploy::new_transfer( amount, None, @@ -292,7 +307,12 @@ pub fn make_transfer( payment_params, true, )?; - crate::output_deploy(output, &deploy).map_err(CliError::from) + #[cfg(feature = "std-fs-io")] + { + let output = parse::output_kind(maybe_output_path, force); + crate::output_deploy(output, &deploy).map_err(CliError::from)?; + } + Ok(deploy) } /// Retrieves a [`Deploy`] from the network. @@ -658,6 +678,7 @@ pub async fn list_rpcs( /// When `verbosity_level` is `0`, nothing is printed. For `1`, the value is printed with long /// string fields shortened to a string indicating the character count of the field. Greater than /// `1` is the same as for `1` except without abbreviation of long fields. +#[cfg(feature = "std-fs-io")] pub fn json_pretty_print( value: &T, verbosity_level: u64, diff --git a/lib/cli/deploy.rs b/lib/cli/deploy.rs index 7d71abcd..9510a72f 100644 --- a/lib/cli/deploy.rs +++ b/lib/cli/deploy.rs @@ -1,4 +1,6 @@ -use casper_types::{account::AccountHash, AsymmetricType, PublicKey, UIntParseError, URef, U512}; +use casper_types::{ + account::AccountHash, AsymmetricType, PublicKey, SecretKey, UIntParseError, URef, U512, +}; use super::{parse, CliError, DeployStrParams, PaymentStrParams, SessionStrParams}; use crate::{ @@ -15,19 +17,6 @@ pub fn with_payment_and_session( ) -> Result { let chain_name = deploy_params.chain_name.to_string(); let session = parse::session_executable_deploy_item(session_params)?; - let maybe_secret_key = if allow_unsigned_deploy && deploy_params.secret_key.is_empty() { - None - } else if deploy_params.secret_key.is_empty() && !allow_unsigned_deploy { - return Err(CliError::InvalidArgument { - context: "with_payment_and_session (secret_key, allow_unsigned_deploy)", - error: format!( - "allow_unsigned_deploy was {}, but no secret key was provided", - allow_unsigned_deploy - ), - }); - } else { - Some(parse::secret_key_from_file(deploy_params.secret_key)?) - }; let payment = parse::payment_executable_deploy_item(payment_params)?; let timestamp = parse::timestamp(deploy_params.timestamp)?; let ttl = parse::ttl(deploy_params.ttl)?; @@ -38,6 +27,11 @@ pub fn with_payment_and_session( .with_timestamp(timestamp) .with_ttl(ttl); + let maybe_secret_key = get_maybe_secret_key( + deploy_params.secret_key, + allow_unsigned_deploy, + "with_payment_and_session", + )?; if let Some(secret_key) = &maybe_secret_key { deploy_builder = deploy_builder.with_secret_key(secret_key); } @@ -63,21 +57,7 @@ pub fn new_transfer( allow_unsigned_deploy: bool, ) -> Result { let chain_name = deploy_params.chain_name.to_string(); - let maybe_secret_key = if allow_unsigned_deploy && deploy_params.secret_key.is_empty() { - None - } else if deploy_params.secret_key.is_empty() && !allow_unsigned_deploy { - return Err(CliError::InvalidArgument { - context: "new_transfer (secret_key, allow_unsigned_deploy)", - error: format!( - "allow_unsigned_deploy was {}, but no secret key was provided", - allow_unsigned_deploy - ), - }); - } else { - Some(parse::secret_key_from_file(deploy_params.secret_key)?) - }; let payment = parse::payment_executable_deploy_item(payment_params)?; - let amount = U512::from_dec_str(amount).map_err(|err| CliError::FailedToParseUint { context: "new_transfer amount", error: UIntParseError::FromDecStr(err), @@ -111,6 +91,12 @@ pub fn new_transfer( .with_payment(payment) .with_timestamp(timestamp) .with_ttl(ttl); + + let maybe_secret_key = get_maybe_secret_key( + deploy_params.secret_key, + allow_unsigned_deploy, + "new_transfer", + )?; if let Some(secret_key) = &maybe_secret_key { deploy_builder = deploy_builder.with_secret_key(secret_key); } @@ -123,3 +109,49 @@ pub fn new_transfer( .map_err(crate::Error::from)?; Ok(deploy) } + +/// Retrieves a `SecretKey` based on the provided secret key string and configuration options. +/// +/// # Arguments +/// +/// * `secret_key` - A string representing the secret key. If empty, a `None` option is returned. +/// * `allow_unsigned_deploy` - A boolean indicating whether unsigned deploys are allowed. +/// +/// # Returns +/// +/// Returns a `Result` containing an `Option`. If a valid secret key is provided and the `sdk` feature is enabled, +/// the `Result` contains `Some(SecretKey)`. If the `sdk` feature is disabled, the `Result` contains `Some(SecretKey)` parsed from the provided file. +/// If `secret_key` is empty and `allow_unsigned_deploy` is `true`, the `Result` contains `None`. If `secret_key` is empty and `allow_unsigned_deploy` is `false`, +/// an `Err` variant with a `CliError::InvalidArgument` is returned. +/// +/// # Errors +/// +/// Returns an `Err` variant with a `CliError::Core` or `CliError::InvalidArgument` if there are issues with parsing the secret key. +fn get_maybe_secret_key( + secret_key: &str, + allow_unsigned_deploy: bool, + context: &'static str, +) -> Result, CliError> { + if !secret_key.is_empty() { + #[cfg(feature = "std-fs-io")] + { + Ok(Some(parse::secret_key_from_file(secret_key)?)) + } + #[cfg(not(feature = "std-fs-io"))] + { + let secret_key = SecretKey::from_pem(secret_key) + .map_err(|error| CliError::Core(crate::Error::CryptoError { context, error }))?; + Ok(Some(secret_key)) + } + } else if !allow_unsigned_deploy { + Err(CliError::InvalidArgument { + context, + error: format!( + "allow_unsigned_deploy was {}, but no secret key was provided", + allow_unsigned_deploy + ), + }) + } else { + Ok(None) + } +} diff --git a/lib/cli/deploy_str_params.rs b/lib/cli/deploy_str_params.rs index b81615f0..82ef245b 100644 --- a/lib/cli/deploy_str_params.rs +++ b/lib/cli/deploy_str_params.rs @@ -1,7 +1,8 @@ /// Container for `Deploy` construction options. #[derive(Default, Debug)] pub struct DeployStrParams<'a> { - /// Path to secret key file. + /// Path to secret key file if the `std-fs-io` feature is enabled (enabled by default), or a + /// PEM-encoded secret key if not. /// /// If `secret_key` is empty, the new deploy will not be signed and will need to be signed (e.g. /// via [`sign_deploy_file`](super::sign_deploy_file)) at least once in order to be made valid. diff --git a/lib/cli/error.rs b/lib/cli/error.rs index e9296938..439f4fb8 100644 --- a/lib/cli/error.rs +++ b/lib/cli/error.rs @@ -113,7 +113,7 @@ pub enum CliError { ConflictingArguments { /// Contextual description of where this error occurred including relevant paths, /// filenames, etc. - context: &'static str, + context: String, /// Arguments passed, with their values. args: Vec, }, diff --git a/lib/cli/json_args.rs b/lib/cli/json_args.rs index 50d85da9..00c88282 100644 --- a/lib/cli/json_args.rs +++ b/lib/cli/json_args.rs @@ -16,8 +16,9 @@ use casper_types::{ use crate::cli::CliError; pub use error::{Error, ErrorDetails}; +/// Represents a JSON argument with a name, type, and value. #[derive(Clone, Serialize, Deserialize, Debug)] -pub(super) struct JsonArg { +pub struct JsonArg { name: String, #[serde(rename = "type")] cl_type: CLType, diff --git a/lib/cli/parse.rs b/lib/cli/parse.rs index 32ebc13e..dca65d26 100644 --- a/lib/cli/parse.rs +++ b/lib/cli/parse.rs @@ -1,22 +1,32 @@ //! This module contains structs and helpers which are used by multiple subcommands related to //! creating deploys. -use std::{convert::TryInto, fs, io, path::Path, str::FromStr}; +use std::str::FromStr; +#[cfg(feature = "std-fs-io")] +use std::{convert::TryInto, fs, io, path::Path}; use rand::Rng; +#[cfg(feature = "std-fs-io")] use serde::{self, Deserialize}; use casper_hashing::Digest; use casper_types::{ - account::AccountHash, bytesrepr, crypto, AsymmetricType, CLValue, HashAddr, Key, NamedArg, - PublicKey, RuntimeArgs, SecretKey, UIntParseError, URef, U512, + account::AccountHash, crypto, AsymmetricType, HashAddr, Key, NamedArg, PublicKey, RuntimeArgs, + UIntParseError, URef, U512, +}; +#[cfg(feature = "std-fs-io")] +use casper_types::{ + bytesrepr::{self, Bytes}, + CLValue, SecretKey, }; use super::{simple_args, CliError, PaymentStrParams, SessionStrParams}; +#[cfg(feature = "std-fs-io")] +use crate::OutputKind; use crate::{ types::{BlockHash, DeployHash, ExecutableDeployItem, TimeDiff, Timestamp}, - AccountIdentifier, BlockIdentifier, GlobalStateIdentifier, JsonRpcId, OutputKind, - PurseIdentifier, Verbosity, + AccountIdentifier, BlockIdentifier, GlobalStateIdentifier, JsonRpcId, PurseIdentifier, + Verbosity, }; pub(super) fn rpc_id(maybe_rpc_id: &str) -> JsonRpcId { @@ -37,6 +47,7 @@ pub(super) fn verbosity(verbosity_level: u64) -> Verbosity { } } +#[cfg(feature = "std-fs-io")] pub(super) fn output_kind(maybe_output_path: &str, force: bool) -> OutputKind { if maybe_output_path.is_empty() { OutputKind::Stdout @@ -45,6 +56,7 @@ pub(super) fn output_kind(maybe_output_path: &str, force: bool) -> OutputKind { } } +#[cfg(feature = "std-fs-io")] pub(super) fn secret_key_from_file>( secret_key_path: P, ) -> Result { @@ -157,6 +169,7 @@ mod args_json { /// Handles providing the arg for and retrieval of complex session and payment args. These are read /// in from a file. +#[cfg(feature = "std-fs-io")] mod args_complex { use std::{ fmt::{self, Formatter}, @@ -314,7 +327,7 @@ fn args_from_simple_or_json_or_complex( /// - More than one parameter is non-empty. /// - Any parameter that is non-empty has requires[] requirements that are empty. macro_rules! check_exactly_one_not_empty { - ( context: $site:expr, $( ($x:expr) requires[$($y:expr),*] requires_empty[$($z:expr),*] ),+ $(,)? ) => {{ + ( context: $site:tt, $( ($x:expr) requires[$($y:expr),*] requires_empty[$($z:expr),*] ),+ $(,)? ) => {{ let field_is_empty_map = &[$( (stringify!($x), $x.is_empty()) @@ -373,7 +386,7 @@ macro_rules! check_exactly_one_not_empty { conflicting_fields.push(format!("{}={}", name, value)); conflicting_fields.sort(); return Err(CliError::ConflictingArguments{ - context: $site, + context: $site.to_string(), args: conflicting_fields, }); } @@ -388,13 +401,67 @@ macro_rules! check_exactly_one_not_empty { .collect::>(); non_empty_fields_with_values.sort(); return Err(CliError::ConflictingArguments { - context: $site, + context: $site.to_string(), args: non_empty_fields_with_values, }); } }} } +/// Checks if conflicting arguments are provided for parsing session information. +/// +/// # Arguments +/// +/// * `context` - A string indicating the context in which the arguments are checked. +/// * `simple` - A vector of strings representing simple arguments. +/// * `json` - A string representing JSON-formatted arguments. +/// * `complex` - A string representing complex arguments. +/// +/// # Returns +/// +/// Returns a `Result` with an empty `Ok(())` variant if no conflicting arguments are found. If +/// conflicting arguments are provided, an `Err` variant with a `CliError::ConflictingArguments` is +/// returned. +/// +/// # Errors +/// +/// Returns an `Err` variant with a `CliError::ConflictingArguments` if conflicting arguments are +/// provided. +fn check_no_conflicting_arg_types( + context: &str, + simple: &Vec<&str>, + json: &str, + complex: &str, +) -> Result<(), CliError> { + let count = [!simple.is_empty(), !json.is_empty(), !complex.is_empty()] + .iter() + .filter(|&&x| x) + .count(); + + if count > 1 { + return Err(CliError::ConflictingArguments { + context: format!("parsing {context} args conflict (simple json complex)",), + args: vec![simple.join(", "), json.to_owned(), complex.to_owned()], + }); + } + Ok(()) +} + +/// Parses session parameters and constructs an `ExecutableDeployItem` accordingly. +/// +/// # Arguments +/// +/// * `params` - A struct containing session-related parameters including hashes, names, paths, bytes, and arguments. +/// +/// # Returns +/// +/// Returns a `Result` containing an `ExecutableDeployItem` if the session parameters are valid. +/// +/// # Errors +/// +/// Returns an `Err` variant with a `CliError` if there are issues with parsing session parameters, +/// conflicting arguments, or invalid entry points. +/// pub(super) fn session_executable_deploy_item( params: SessionStrParams, ) -> Result { @@ -404,6 +471,7 @@ pub(super) fn session_executable_deploy_item( session_package_hash, session_package_name, session_path, + session_bytes, ref session_args_simple, session_args_json, session_args_complex, @@ -413,6 +481,8 @@ pub(super) fn session_executable_deploy_item( } = params; // This is to make sure that we're using &str consistently in the macro call below. let is_session_transfer = if session_transfer { "true" } else { "" }; + // This is to make sure that we're using &str consistently in the macro call below. + let has_session_bytes = if session_bytes.is_empty() { "" } else { "true" }; check_exactly_one_not_empty!( context: "parse_session_info", @@ -425,21 +495,27 @@ pub(super) fn session_executable_deploy_item( (session_package_name) requires[session_entry_point] requires_empty[], (session_path) - requires[] requires_empty[session_entry_point, session_version], + requires[] requires_empty[session_entry_point, session_version, has_session_bytes], + (has_session_bytes) + requires[] requires_empty[session_entry_point, session_version, session_path], (is_session_transfer) requires[] requires_empty[session_entry_point, session_version] ); - if !session_args_simple.is_empty() && !session_args_complex.is_empty() { - return Err(CliError::ConflictingArguments { - context: "parse_session_info", - args: vec!["session_args".to_owned(), "session_args_complex".to_owned()], - }); - } + + check_no_conflicting_arg_types( + "session", + session_args_simple, + session_args_json, + session_args_complex, + )?; let session_args = args_from_simple_or_json_or_complex( arg_simple::session::parse(session_args_simple)?, args_json::session::parse(session_args_json)?, + #[cfg(feature = "std-fs-io")] args_complex::session::parse(session_args_complex)?, + #[cfg(not(feature = "std-fs-io"))] + None, ); if session_transfer { if session_args.is_empty() { @@ -489,12 +565,26 @@ pub(super) fn session_executable_deploy_item( }); } - let module_bytes = fs::read(session_path).map_err(|error| crate::Error::IoError { - context: format!("unable to read session file at '{}'", session_path), - error, - })?; + let module_bytes = if !session_bytes.is_empty() { + session_bytes + } else { + #[cfg(feature = "std-fs-io")] + { + let session = fs::read(session_path).map_err(|error| crate::Error::IoError { + context: format!("unable to read session file at '{}'", session_path), + error, + })?; + Bytes::from(session) + } + #[cfg(not(feature = "std-fs-io"))] + return Err(CliError::InvalidArgument { + context: "session_executable_deploy_item", + error: "missing session bytes".to_string(), + }); + }; + Ok(ExecutableDeployItem::ModuleBytes { - module_bytes: module_bytes.into(), + module_bytes, args: session_args, }) } @@ -509,12 +599,15 @@ pub(super) fn payment_executable_deploy_item( payment_package_hash, payment_package_name, payment_path, + payment_bytes, ref payment_args_simple, payment_args_json, payment_args_complex, payment_version, payment_entry_point, } = params; + // This is to make sure that we're using &str consistently in the macro call below. + let has_payment_bytes = if payment_bytes.is_empty() { "" } else { "true" }; check_exactly_one_not_empty!( context: "parse_payment_info", (payment_amount) @@ -527,17 +620,18 @@ pub(super) fn payment_executable_deploy_item( requires[payment_entry_point] requires_empty[], (payment_package_name) requires[payment_entry_point] requires_empty[], - (payment_path) requires[] requires_empty[payment_entry_point, payment_version], + (payment_path) + requires[] requires_empty[payment_entry_point, payment_version, has_payment_bytes], + (has_payment_bytes) + requires[] requires_empty[payment_entry_point, payment_version, payment_path], ); - if !payment_args_simple.is_empty() && !payment_args_complex.is_empty() { - return Err(CliError::ConflictingArguments { - context: "parse_payment_info", - args: vec![ - "payment_args_simple".to_owned(), - "payment_args_complex".to_owned(), - ], - }); - } + + check_no_conflicting_arg_types( + "payment", + payment_args_simple, + payment_args_json, + payment_args_complex, + )?; if let Ok(payment_args) = standard_payment(payment_amount) { return Ok(ExecutableDeployItem::ModuleBytes { @@ -554,7 +648,10 @@ pub(super) fn payment_executable_deploy_item( let payment_args = args_from_simple_or_json_or_complex( arg_simple::payment::parse(payment_args_simple)?, args_json::payment::parse(payment_args_json)?, + #[cfg(feature = "std-fs-io")] args_complex::payment::parse(payment_args_complex)?, + #[cfg(not(feature = "std-fs-io"))] + None, ); if let Some(payment_name) = name(payment_name) { @@ -592,12 +689,26 @@ pub(super) fn payment_executable_deploy_item( }); } - let module_bytes = fs::read(payment_path).map_err(|error| crate::Error::IoError { - context: format!("unable to read payment file at '{}'", payment_path), - error, - })?; + let module_bytes = if !payment_bytes.is_empty() { + payment_bytes + } else { + #[cfg(feature = "std-fs-io")] + { + let payment = fs::read(payment_path).map_err(|error| crate::Error::IoError { + context: format!("unable to read payment file at '{}'", payment_path), + error, + })?; + Bytes::from(payment) + } + #[cfg(not(feature = "std-fs-io"))] + return Err(CliError::InvalidArgument { + context: "payment_executable_deploy_item", + error: "missing payment bytes".to_string(), + }); + }; + Ok(ExecutableDeployItem::ModuleBytes { - module_bytes: module_bytes.into(), + module_bytes, args: payment_args, }) } @@ -734,7 +845,7 @@ pub(super) fn global_state_identifier( } /// `purse_id` can be a formatted public key, account hash, or URef. It may not be empty. -pub(super) fn purse_identifier(purse_id: &str) -> Result { +pub fn purse_identifier(purse_id: &str) -> Result { const ACCOUNT_HASH_PREFIX: &str = "account-hash-"; const UREF_PREFIX: &str = "uref-"; @@ -775,7 +886,7 @@ pub(super) fn purse_identifier(purse_id: &str) -> Result Result { +pub fn account_identifier(account_identifier: &str) -> Result { const ACCOUNT_HASH_PREFIX: &str = "account-hash-"; if account_identifier.is_empty() { @@ -809,6 +920,8 @@ pub(super) fn account_identifier(account_identifier: &str) -> Result PATH, conflict: session_package_hash => PACKAGE_HASH, requires[], path_conflicts_with_package_hash] - test[session_path => PATH, conflict: session_package_name => PACKAGE_NAME, requires[], path_conflicts_with_package_name] - test[session_path => PATH, conflict: session_hash => HASH, requires[], path_conflicts_with_hash] - test[session_path => PATH, conflict: session_name => HASH, requires[], path_conflicts_with_name] - test[session_path => PATH, conflict: session_version => VERSION, requires[], path_conflicts_with_version] - test[session_path => PATH, conflict: session_entry_point => ENTRY_POINT, requires[], path_conflicts_with_entry_point] - test[session_path => PATH, conflict: is_session_transfer => TRANSFER, requires[], path_conflicts_with_transfer] + test[session_path => PATH, conflict: session_package_hash => PACKAGE_HASH, requires[], path_conflicts_with_package_hash] + test[session_path => PATH, conflict: session_package_name => PACKAGE_NAME, requires[], path_conflicts_with_package_name] + test[session_path => PATH, conflict: session_bytes => Bytes::from(vec![1]), requires[], path_conflicts_with_session_bytes] + test[session_path => PATH, conflict: session_hash => HASH, requires[], path_conflicts_with_hash] + test[session_path => PATH, conflict: session_name => HASH, requires[], path_conflicts_with_name] + test[session_path => PATH, conflict: session_version => VERSION, requires[], path_conflicts_with_version] + test[session_path => PATH, conflict: session_entry_point => ENTRY_POINT, requires[], path_conflicts_with_entry_point] + test[session_path => PATH, conflict: is_session_transfer => TRANSFER, requires[], path_conflicts_with_transfer] + + // bytes + test[session_bytes => Bytes::from(vec![1]), conflict: session_package_hash => PACKAGE_HASH, requires[session_entry_point => ENTRY_POINT], bytes_conflicts_with_package_hash] + test[session_bytes => Bytes::from(vec![1]), conflict: session_package_name => PACKAGE_NAME, requires[session_entry_point => ENTRY_POINT], bytes_conflicts_with_package_name] + test[session_bytes => Bytes::from(vec![1]), conflict: session_hash => HASH, requires[session_entry_point => ENTRY_POINT], bytes_conflicts_with_hash] + test[session_bytes => Bytes::from(vec![1]), conflict: session_version => VERSION, requires[session_entry_point => ENTRY_POINT], bytes_conflicts_with_version] + test[session_bytes => Bytes::from(vec![1]), conflict: is_session_transfer => TRANSFER, requires[session_entry_point => ENTRY_POINT], bytes_conflicts_with_transfer] // name test[session_name => NAME, conflict: session_package_hash => PACKAGE_HASH, requires[session_entry_point => ENTRY_POINT], name_conflicts_with_package_hash] @@ -1281,12 +1412,19 @@ mod tests { // path // amount <-> path is already checked - test[payment_path => PATH, conflict: payment_package_hash => PACKAGE_HASH, requires[], path_conflicts_with_package_hash] - test[payment_path => PATH, conflict: payment_package_name => PACKAGE_NAME, requires[], path_conflicts_with_package_name] - test[payment_path => PATH, conflict: payment_hash => HASH, requires[], path_conflicts_with_hash] - test[payment_path => PATH, conflict: payment_name => HASH, requires[], path_conflicts_with_name] - test[payment_path => PATH, conflict: payment_version => VERSION, requires[], path_conflicts_with_version] - test[payment_path => PATH, conflict: payment_entry_point => ENTRY_POINT, requires[], path_conflicts_with_entry_point] + test[payment_path => PATH, conflict: payment_package_hash => PACKAGE_HASH, requires[], path_conflicts_with_package_hash] + test[payment_path => PATH, conflict: payment_package_name => PACKAGE_NAME, requires[], path_conflicts_with_package_name] + test[payment_path => PATH, conflict: payment_bytes => Bytes::from(vec![1]), requires[], path_conflicts_with_payment_bytes] + test[payment_path => PATH, conflict: payment_hash => HASH, requires[], path_conflicts_with_hash] + test[payment_path => PATH, conflict: payment_name => HASH, requires[], path_conflicts_with_name] + test[payment_path => PATH, conflict: payment_version => VERSION, requires[], path_conflicts_with_version] + test[payment_path => PATH, conflict: payment_entry_point => ENTRY_POINT, requires[], path_conflicts_with_entry_point] + + // bytes + test[payment_bytes => Bytes::from(vec![1]), conflict: payment_package_hash => PACKAGE_HASH, requires[payment_entry_point => ENTRY_POINT], bytes_conflicts_with_package_hash] + test[payment_bytes => Bytes::from(vec![1]), conflict: payment_package_name => PACKAGE_NAME, requires[payment_entry_point => ENTRY_POINT], bytes_conflicts_with_package_name] + test[payment_bytes => Bytes::from(vec![1]), conflict: payment_hash => HASH, requires[payment_entry_point => ENTRY_POINT], bytes_conflicts_with_hash] + test[payment_bytes => Bytes::from(vec![1]), conflict: payment_version => VERSION, requires[payment_entry_point => ENTRY_POINT], bytes_conflicts_with_version] // name // amount <-> path is already checked diff --git a/lib/cli/payment_str_params.rs b/lib/cli/payment_str_params.rs index 882f4449..fcd330d1 100644 --- a/lib/cli/payment_str_params.rs +++ b/lib/cli/payment_str_params.rs @@ -1,3 +1,5 @@ +use casper_types::bytesrepr::Bytes; + #[cfg(doc)] use crate::cli; @@ -99,7 +101,7 @@ use crate::cli; /// /// **Note** while multiple payment args can be specified for a single payment code instance, only /// one of `payment_args_simple`, `payment_args_json` or `payment_args_complex` may be used. -#[derive(Default)] +#[derive(Default, Debug)] pub struct PaymentStrParams<'a> { pub(super) payment_amount: &'a str, pub(super) payment_hash: &'a str, @@ -107,6 +109,7 @@ pub struct PaymentStrParams<'a> { pub(super) payment_package_hash: &'a str, pub(super) payment_package_name: &'a str, pub(super) payment_path: &'a str, + pub(super) payment_bytes: Bytes, pub(super) payment_args_simple: Vec<&'a str>, pub(super) payment_args_json: &'a str, pub(super) payment_args_complex: &'a str, @@ -136,6 +139,27 @@ impl<'a> PaymentStrParams<'a> { } } + /// Constructs a `PaymentStrParams` using payment bytes. + /// + /// * `payment_bytes` are the bytes of the compiled Wasm payment code. + /// * See the struct docs for a description of [`payment_args_simple`](#payment_args_simple), + /// [`payment_args_json`](#payment_args_json) and + /// [`payment_args_complex`](#payment_args_complex). + pub fn with_bytes( + payment_bytes: Bytes, + payment_args_simple: Vec<&'a str>, + payment_args_json: &'a str, + payment_args_complex: &'a str, + ) -> Self { + Self { + payment_bytes, + payment_args_simple, + payment_args_json, + payment_args_complex, + ..Default::default() + } + } + /// Constructs a `PaymentStrParams` using a payment amount. /// /// `payment_amount` uses the standard-payment system contract rather than custom payment Wasm. diff --git a/lib/cli/session_str_params.rs b/lib/cli/session_str_params.rs index 0b9ad930..a496905c 100644 --- a/lib/cli/session_str_params.rs +++ b/lib/cli/session_str_params.rs @@ -1,3 +1,5 @@ +use casper_types::bytesrepr::Bytes; + /// Container for session-related arguments used while constructing a `Deploy`. /// /// ## `session_args_simple` @@ -29,13 +31,14 @@ /// /// **Note** while multiple session args can be specified for a single session code instance, only /// one of `session_args_simple`, `session_args_json` or `session_args_complex` may be used. -#[derive(Default)] +#[derive(Default, Debug)] pub struct SessionStrParams<'a> { pub(super) session_hash: &'a str, pub(super) session_name: &'a str, pub(super) session_package_hash: &'a str, pub(super) session_package_name: &'a str, pub(super) session_path: &'a str, + pub(super) session_bytes: Bytes, pub(super) session_args_simple: Vec<&'a str>, pub(super) session_args_json: &'a str, pub(super) session_args_complex: &'a str, @@ -66,6 +69,27 @@ impl<'a> SessionStrParams<'a> { } } + /// Constructs a `SessionStrParams` using session bytes. + /// + /// * `session_bytes` are the bytes of the compiled Wasm session code. + /// * See the struct docs for a description of [`session_args_simple`](#session_args_simple), + /// [`session_args_json`](#session_args_json) and + /// [`session_args_complex`](#session_args_complex). + pub fn with_bytes( + session_bytes: Bytes, + session_args_simple: Vec<&'a str>, + session_args_json: &'a str, + session_args_complex: &'a str, + ) -> Self { + Self { + session_bytes, + session_args_simple, + session_args_json, + session_args_complex, + ..Default::default() + } + } + /// Constructs a `SessionStrParams` using a stored contract's name. /// /// * `session_name` is the name of the stored contract (associated with the executing account) diff --git a/lib/cli/simple_args.rs b/lib/cli/simple_args.rs index 9ab2c4ff..49134ec7 100644 --- a/lib/cli/simple_args.rs +++ b/lib/cli/simple_args.rs @@ -344,7 +344,7 @@ fn split_arg(arg: &str) -> Result<(&str, &str, &str), CliError> { } /// Insert a value built from a single arg into `runtime_args`. -pub(super) fn insert_arg(arg: &str, runtime_args: &mut RuntimeArgs) -> Result<(), CliError> { +pub fn insert_arg(arg: &str, runtime_args: &mut RuntimeArgs) -> Result<(), CliError> { let (name, initial_type, value) = split_arg(arg)?; let (simple_type, optional_status, trimmed_value) = get_simple_type_and_optional_status(initial_type, value)?; @@ -676,7 +676,7 @@ mod tests { const ARG_GIBBERISH: &str = "asdf|1234(..)"; const ARG_UNQUOTED: &str = "name:u32=0"; // value needs single quotes to be valid const EMPTY: &str = ""; - const LARGE_2K_INPUT: &str = r#" + const LARGE_2K_INPUT: &str = r" eJy2irIizK6zT0XOklyBAY1KVUsAbyF6eJUYBmRPHqX2rONbaEieJt4Ci1eZYjBdHdEq46oMBH0LeiQO8RIJb95 SJGEp83RxakDj7trunJVvMbj2KZFnpJOyEauFa35dlaVG9Ki7hjFy4BLlDyA0Wgwk20RXFkbgKQIQVvR16RPffR WO86WqZ3gMuOh447svZRYfhbRF3NVBaWRz7SJ9Zm3w8djisvS0Y3GSnpzKnSEQirApqomfQTHTrU9ww2SMgdGuu @@ -701,7 +701,7 @@ Q8d1wPtqKgayVtgdIaMbvsnXMkRqITkf3o8Qh495pm1wkKArTGFGODXc1cCKheFUEtJWdK92DHH7OuRE PKzSUg2k18wyf9XCy1pQKv31wii3rWrWMCbxOWmhuzw1N9tqO8U97NsThRSoPAjpd05G2roia4m4CaPWTAUmVky RfiWoA7bglAh4Aoz2LN2ezFleTNJjjLw3n9bYPg5BdRL8n8wimhXDo9SW46A5YS62C08ZOVtvfn82YRaYkuKKz7 3NJ25PnQG6diMm4Lm3wi22yR7lY7oYYJjLNcaLYOI6HOvaJ -"#; +"; check_insert_invalid_arg(ARG_BAD_TYPE); check_insert_invalid_arg(ARG_GIBBERISH); diff --git a/lib/cli/tests.rs b/lib/cli/tests.rs index 4ed02094..2d3b831d 100644 --- a/lib/cli/tests.rs +++ b/lib/cli/tests.rs @@ -1,9 +1,8 @@ use casper_types::{AsymmetricType, PublicKey, SecretKey}; -use crate::{ - types::{ExecutableDeployItem, MAX_SERIALIZED_SIZE_OF_DEPLOY}, - Error, OutputKind, -}; +use crate::Error; +#[cfg(feature = "std-fs-io")] +use crate::{types::ExecutableDeployItem, OutputKind, MAX_SERIALIZED_SIZE_OF_DEPLOY}; use super::*; @@ -11,6 +10,7 @@ const SAMPLE_ACCOUNT: &str = "01722e1b3d31bef0ba832121bd2941aae6a246d0d05ac95aa1 const PKG_HASH: &str = "09dcee4b212cfd53642ab323fbef07dafafc6f945a80a00147f62910a915c4e6"; const ENTRYPOINT: &str = "entrypoint"; const VERSION: &str = "2"; +#[cfg(feature = "std-fs-io")] const SAMPLE_DEPLOY: &str = r#"{ "hash": "1053f767f1734e3b5b31253ea680778ac53f134f7c24518bf2c4cbb204852617", "header": { @@ -116,6 +116,7 @@ fn args_simple() -> Vec<&'static str> { } #[test] +#[cfg(feature = "std-fs-io")] fn should_create_deploy() { let deploy_params = deploy_params(); let payment_params = @@ -123,12 +124,12 @@ fn should_create_deploy() { let session_params = SessionStrParams::with_package_hash(PKG_HASH, VERSION, ENTRYPOINT, args_simple(), "", ""); - let mut output = Vec::new(); - - let deploy = + let _deploy = deploy::with_payment_and_session(deploy_params, payment_params, session_params, false) .unwrap(); - crate::write_deploy(&deploy, &mut output).unwrap(); + + let mut output = Vec::new(); + crate::write_deploy(&_deploy, &mut output).unwrap(); // The test output can be used to generate data for SAMPLE_DEPLOY: // let secret_key = SecretKey::generate_ed25519().unwrap(); @@ -149,6 +150,7 @@ fn should_create_deploy() { } #[test] +#[cfg(feature = "std-fs-io")] fn should_fail_to_create_large_deploy() { let deploy_params = deploy_params(); let payment_params = @@ -180,12 +182,14 @@ fn should_fail_to_create_large_deploy() { } #[test] +#[cfg(feature = "std-fs-io")] fn should_read_deploy() { let bytes = SAMPLE_DEPLOY.as_bytes(); - assert!(matches!(crate::read_deploy(bytes), Ok(_))); + assert!(crate::read_deploy(bytes).is_ok()); } #[test] +#[cfg(feature = "std-fs-io")] fn should_sign_deploy() { let bytes = SAMPLE_DEPLOY.as_bytes(); let deploy = crate::read_deploy(bytes).unwrap(); @@ -211,6 +215,7 @@ fn should_sign_deploy() { } #[test] +#[cfg(feature = "std-fs-io")] fn should_create_transfer() { use casper_types::{AsymmetricType, PublicKey}; @@ -395,13 +400,18 @@ fn should_fail_to_create_transfer_with_no_secret_key_while_not_allowing_unsigned ); assert!(transfer_deploy.is_err()); - assert!(matches!( - transfer_deploy.unwrap_err(), - CliError::InvalidArgument { - context: "new_transfer (secret_key, allow_unsigned_deploy)", - error: _ - } - )); + let actual_error = transfer_deploy.unwrap_err(); + assert!( + matches!( + actual_error, + CliError::InvalidArgument { + context: "new_transfer", + error: _ + } + ), + "{:?}", + actual_error + ); } #[test] @@ -417,11 +427,16 @@ fn should_fail_to_create_deploy_with_payment_and_session_with_no_secret_key_whil deploy::with_payment_and_session(deploy_params, payment_params, session_params, false); assert!(transfer_deploy.is_err()); - assert!(matches!( - transfer_deploy.unwrap_err(), - CliError::InvalidArgument { - context: "with_payment_and_session (secret_key, allow_unsigned_deploy)", - error: _ - } - )); + let actual_error = transfer_deploy.unwrap_err(); + assert!( + matches!( + actual_error, + CliError::InvalidArgument { + context: "with_payment_and_session", + error: _ + } + ), + "{:?}", + actual_error + ); } diff --git a/lib/error.rs b/lib/error.rs index d18ea8d8..a0970b32 100644 --- a/lib/error.rs +++ b/lib/error.rs @@ -1,3 +1,4 @@ +#[cfg(feature = "std-fs-io")] use std::{io, path::PathBuf}; use thiserror::Error; @@ -29,6 +30,12 @@ pub enum Error { #[error("deploy requires session account - use `with_account` or `with_secret_key`")] DeployMissingSessionAccount, + /// Failed to build [`Deploy`] due to missing timestamp. + /// + /// Call [`DeployBuilder::with_timestamp`] before calling [`DeployBuilder::build`]. + #[error("deploy requires timestamp - use `with_timestamp`")] + DeployMissingTimestamp, + /// Failed to build [`Deploy`] due to missing payment code. /// /// Call [`DeployBuilder::with_standard_payment`] or [`DeployBuilder::with_payment`] before @@ -125,6 +132,7 @@ pub enum Error { }, /// Failed to create new file because it already exists. + #[cfg(feature = "std-fs-io")] #[error("file at {} already exists", .0.display())] FileAlreadyExists(PathBuf), @@ -137,6 +145,7 @@ pub enum Error { UnsupportedAlgorithm(String), /// Context-adding wrapper for `std::io::Error`. + #[cfg(feature = "std-fs-io")] #[error("input/output error: {context}: {error}")] IoError { /// Contextual description of where this error occurred including relevant paths, diff --git a/lib/json_rpc/call.rs b/lib/json_rpc/call.rs index e106a910..62c5fc92 100644 --- a/lib/json_rpc/call.rs +++ b/lib/json_rpc/call.rs @@ -16,6 +16,7 @@ static CLIENT: OnceCell = OnceCell::new(); pub(crate) struct Call { rpc_id: JsonRpcId, node_address: String, + #[cfg_attr(not(feature = "std-fs-io"), allow(dead_code))] verbosity: Verbosity, } @@ -53,6 +54,7 @@ impl Call { None => JsonRpc::request(&self.rpc_id, method), }; + #[cfg(feature = "std-fs-io")] crate::json_pretty_print(&rpc_request, self.verbosity)?; let client = CLIENT.get_or_init(Client::new); @@ -85,6 +87,7 @@ impl Call { error, })?; + #[cfg(feature = "std-fs-io")] crate::json_pretty_print(&rpc_response, self.verbosity)?; let response_kind = match &rpc_response { diff --git a/lib/lib.rs b/lib/lib.rs index 6c787671..94e83cf4 100644 --- a/lib/lib.rs +++ b/lib/lib.rs @@ -26,7 +26,7 @@ html_root_url = "https://docs.rs/casper-client/2.0.0", html_favicon_url = "https://raw.githubusercontent.com/casper-network/casper-node/blob/dev/images/Casper_Logo_Favicon_48.png", html_logo_url = "https://raw.githubusercontent.com/casper-network/casper-node/blob/dev/images/Casper_Logo_Favicon.png", - test(attr(forbid(warnings))) + test(attr(deny(warnings))) )] #![warn( missing_docs, @@ -34,11 +34,14 @@ trivial_numeric_casts, unused_qualifications )] +#![cfg_attr(docsrs, feature(doc_auto_cfg))] pub mod cli; mod error; mod json_rpc; +#[cfg(feature = "std-fs-io")] pub mod keygen; +#[cfg(feature = "std-fs-io")] mod output_kind; pub mod rpcs; mod transfer_target; @@ -46,22 +49,27 @@ pub mod types; mod validation; mod verbosity; +#[cfg(feature = "std-fs-io")] use std::{ fs, io::{Cursor, Read, Write}, path::Path, }; +#[cfg(feature = "std-fs-io")] use serde::Serialize; use casper_hashing::Digest; +#[cfg(feature = "std-fs-io")] +use casper_types::SecretKey; #[cfg(doc)] use casper_types::Transfer; -use casper_types::{Key, SecretKey, URef}; +use casper_types::{Key, URef}; pub use error::Error; use json_rpc::JsonRpcCall; pub use json_rpc::{JsonRpcId, SuccessResponse}; +#[cfg(feature = "std-fs-io")] pub use output_kind::OutputKind; use rpcs::{ common::{BlockIdentifier, GlobalStateIdentifier}, @@ -96,9 +104,11 @@ use rpcs::{ DictionaryItemIdentifier, }; pub use transfer_target::TransferTarget; +#[cfg(feature = "std-fs-io")] +use types::MAX_SERIALIZED_SIZE_OF_DEPLOY; #[cfg(doc)] use types::{Account, Block, StoredValue}; -use types::{Deploy, DeployHash, MAX_SERIALIZED_SIZE_OF_DEPLOY}; +use types::{Deploy, DeployHash}; pub use validation::ValidateResponseError; pub use verbosity::Verbosity; @@ -146,12 +156,14 @@ pub async fn speculative_exec( /// /// `output` specifies the output file and corresponding overwrite behaviour, or if /// `OutputKind::Stdout`, causes the `Deploy` to be printed `stdout`. +#[cfg(feature = "std-fs-io")] pub fn output_deploy(output: OutputKind, deploy: &Deploy) -> Result<(), Error> { write_deploy(deploy, output.get()?)?; output.commit() } /// Reads a previously-saved [`Deploy`] from a file. +#[cfg(feature = "std-fs-io")] pub fn read_deploy_file>(deploy_path: P) -> Result { let input = fs::read(deploy_path.as_ref()).map_err(|error| Error::IoError { context: format!( @@ -171,6 +183,7 @@ pub fn read_deploy_file>(deploy_path: P) -> Result /// /// The same path can be specified for input and output, and if the operation fails, the original /// input file will be left unmodified. +#[cfg(feature = "std-fs-io")] pub fn sign_deploy_file>( input_path: P, secret_key: &SecretKey, @@ -476,6 +489,7 @@ pub async fn list_rpcs( /// When `verbosity` is `Low`, nothing is printed. For `Medium`, the value is printed with long /// string fields shortened to a string indicating the character count of the field. `High` /// verbosity is the same as `Medium` except without abbreviation of long fields. +#[cfg(feature = "std-fs-io")] pub(crate) fn json_pretty_print( value: &T, verbosity: Verbosity, @@ -493,6 +507,7 @@ pub(crate) fn json_pretty_print( Ok(()) } +#[cfg(feature = "std-fs-io")] fn write_deploy(deploy: &Deploy, mut output: W) -> Result<(), Error> { let content = serde_json::to_string_pretty(deploy).map_err(|error| Error::FailedToEncodeToJson { @@ -507,6 +522,7 @@ fn write_deploy(deploy: &Deploy, mut output: W) -> Result<(), Error> { }) } +#[cfg(feature = "std-fs-io")] fn read_deploy(input: R) -> Result { let deploy: Deploy = serde_json::from_reader(input).map_err(|error| Error::FailedToDecodeFromJson { diff --git a/lib/rpcs.rs b/lib/rpcs.rs index 9f701d06..b16f1387 100644 --- a/lib/rpcs.rs +++ b/lib/rpcs.rs @@ -13,3 +13,5 @@ pub use v1_5_0::{ get_dictionary_item::DictionaryItemIdentifier, query_balance::PurseIdentifier, query_global_state::GlobalStateIdentifier, }; + +pub use v1_6_0::get_account::AccountIdentifier; diff --git a/lib/rpcs/v1_4_5/get_account.rs b/lib/rpcs/v1_4_5/get_account.rs index a3b945c7..5d6b534f 100644 --- a/lib/rpcs/v1_4_5/get_account.rs +++ b/lib/rpcs/v1_4_5/get_account.rs @@ -27,7 +27,7 @@ impl GetAccountParams { } /// The `result` field of a successful JSON-RPC response to a `state_get_account_info` request. -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] #[serde(deny_unknown_fields)] pub struct GetAccountResult { /// The JSON-RPC server version. diff --git a/lib/rpcs/v1_4_5/get_auction_info.rs b/lib/rpcs/v1_4_5/get_auction_info.rs index ca1a7cfc..eb4e2b8e 100644 --- a/lib/rpcs/v1_4_5/get_auction_info.rs +++ b/lib/rpcs/v1_4_5/get_auction_info.rs @@ -19,7 +19,7 @@ impl GetAuctionInfoParams { } /// The `result` field of a successful JSON-RPC response to a `state_get_auction_info` request. -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] #[serde(deny_unknown_fields)] pub struct GetAuctionInfoResult { /// The JSON-RPC server version. diff --git a/lib/rpcs/v1_4_5/get_balance.rs b/lib/rpcs/v1_4_5/get_balance.rs index 7bcd66aa..92e6af31 100644 --- a/lib/rpcs/v1_4_5/get_balance.rs +++ b/lib/rpcs/v1_4_5/get_balance.rs @@ -22,7 +22,7 @@ impl GetBalanceParams { } /// The `result` field of a successful JSON-RPC response to a `state_get_balance` request. -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] #[serde(deny_unknown_fields)] pub struct GetBalanceResult { /// The JSON-RPC server version. diff --git a/lib/rpcs/v1_4_5/get_block.rs b/lib/rpcs/v1_4_5/get_block.rs index 8736c8a0..1aa8599a 100644 --- a/lib/rpcs/v1_4_5/get_block.rs +++ b/lib/rpcs/v1_4_5/get_block.rs @@ -19,7 +19,7 @@ impl GetBlockParams { } /// The `result` field of a successful JSON-RPC response to a `chain_get_block` request. -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] #[serde(deny_unknown_fields)] pub struct GetBlockResult { /// The JSON-RPC server version. diff --git a/lib/rpcs/v1_4_5/get_block_transfers.rs b/lib/rpcs/v1_4_5/get_block_transfers.rs index 0aeb7a49..c55b31a8 100644 --- a/lib/rpcs/v1_4_5/get_block_transfers.rs +++ b/lib/rpcs/v1_4_5/get_block_transfers.rs @@ -19,7 +19,7 @@ impl GetBlockTransfersParams { } /// The `result` field of a successful JSON-RPC response to a `chain_get_block_transfers` request. -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] #[serde(deny_unknown_fields)] pub struct GetBlockTransfersResult { /// The JSON-RPC server version. diff --git a/lib/rpcs/v1_4_5/get_deploy.rs b/lib/rpcs/v1_4_5/get_deploy.rs index 9e90e3a4..d8524ad3 100644 --- a/lib/rpcs/v1_4_5/get_deploy.rs +++ b/lib/rpcs/v1_4_5/get_deploy.rs @@ -23,7 +23,7 @@ impl GetDeployParams { } /// The `result` field of a successful JSON-RPC response to an `info_get_deploy` request. -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] #[serde(deny_unknown_fields)] pub struct GetDeployResult { /// The JSON-RPC server version. diff --git a/lib/rpcs/v1_4_5/get_dictionary_item.rs b/lib/rpcs/v1_4_5/get_dictionary_item.rs index 0999d919..47b0c357 100644 --- a/lib/rpcs/v1_4_5/get_dictionary_item.rs +++ b/lib/rpcs/v1_4_5/get_dictionary_item.rs @@ -10,7 +10,7 @@ use crate::{types::StoredValue, Error}; pub(crate) const GET_DICTIONARY_ITEM_METHOD: &str = "state_get_dictionary_item"; /// The identifier for a dictionary item. -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] #[serde(deny_unknown_fields)] pub enum DictionaryItemIdentifier { /// A dictionary item identified via an [`Account`]'s named keys. @@ -116,7 +116,7 @@ impl GetDictionaryItemParams { } /// The `result` field of a successful JSON-RPC response to a `state_get_dictionary_item` request. -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] #[serde(deny_unknown_fields)] pub struct GetDictionaryItemResult { /// The JSON-RPC server version. diff --git a/lib/rpcs/v1_4_5/get_era_info.rs b/lib/rpcs/v1_4_5/get_era_info.rs index eceb3627..99dd0377 100644 --- a/lib/rpcs/v1_4_5/get_era_info.rs +++ b/lib/rpcs/v1_4_5/get_era_info.rs @@ -40,7 +40,7 @@ pub struct EraSummary { /// The `result` field of a successful JSON-RPC response to a `chain_get_era_info_by_switch_block` /// request. -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] #[serde(deny_unknown_fields)] pub struct GetEraInfoResult { /// The JSON-RPC server version. diff --git a/lib/rpcs/v1_4_5/get_node_status.rs b/lib/rpcs/v1_4_5/get_node_status.rs index 725c903f..8f2a514e 100644 --- a/lib/rpcs/v1_4_5/get_node_status.rs +++ b/lib/rpcs/v1_4_5/get_node_status.rs @@ -46,7 +46,7 @@ pub struct NextUpgrade { } /// The `result` field of a successful JSON-RPC response to a `info_get_status` request. -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] #[serde(deny_unknown_fields)] pub struct GetNodeStatusResult { /// The JSON-RPC server version. diff --git a/lib/rpcs/v1_4_5/get_peers.rs b/lib/rpcs/v1_4_5/get_peers.rs index 8ac894db..ead0a0fd 100644 --- a/lib/rpcs/v1_4_5/get_peers.rs +++ b/lib/rpcs/v1_4_5/get_peers.rs @@ -15,7 +15,7 @@ pub struct PeerEntry { } /// The `result` field of a successful JSON-RPC response to a `info_get_peers` request. -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] #[serde(deny_unknown_fields)] pub struct GetPeersResult { /// The JSON-RPC server version. diff --git a/lib/rpcs/v1_4_5/get_state_root_hash.rs b/lib/rpcs/v1_4_5/get_state_root_hash.rs index f7fb2e93..b6b5ec16 100644 --- a/lib/rpcs/v1_4_5/get_state_root_hash.rs +++ b/lib/rpcs/v1_4_5/get_state_root_hash.rs @@ -20,7 +20,7 @@ impl GetStateRootHashParams { } /// The `result` field of a successful JSON-RPC response to a `chain_get_state_root_hash` request. -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] #[serde(deny_unknown_fields)] pub struct GetStateRootHashResult { /// The JSON-RPC server version. diff --git a/lib/rpcs/v1_4_5/get_validator_changes.rs b/lib/rpcs/v1_4_5/get_validator_changes.rs index 87609940..00e80a08 100644 --- a/lib/rpcs/v1_4_5/get_validator_changes.rs +++ b/lib/rpcs/v1_4_5/get_validator_changes.rs @@ -40,7 +40,7 @@ pub struct ValidatorChanges { } /// The `result` field of a successful JSON-RPC response to a `info_get_validator_changes` request. -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] #[serde(deny_unknown_fields)] pub struct GetValidatorChangesResult { /// The JSON-RPC server version. diff --git a/lib/rpcs/v1_4_5/list_rpcs.rs b/lib/rpcs/v1_4_5/list_rpcs.rs index df339b15..f5c33e5e 100644 --- a/lib/rpcs/v1_4_5/list_rpcs.rs +++ b/lib/rpcs/v1_4_5/list_rpcs.rs @@ -137,7 +137,7 @@ pub struct OpenRpcSchema { } /// The `result` field of a successful JSON-RPC response to a `rpc.discover` request. -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] #[serde(deny_unknown_fields)] pub struct ListRpcsResult { /// The JSON-RPC server version. diff --git a/lib/rpcs/v1_4_5/put_deploy.rs b/lib/rpcs/v1_4_5/put_deploy.rs index 4e93aaea..6cde1291 100644 --- a/lib/rpcs/v1_4_5/put_deploy.rs +++ b/lib/rpcs/v1_4_5/put_deploy.rs @@ -19,7 +19,7 @@ impl PutDeployParams { } /// The `result` field of a successful JSON-RPC response to an `account_put_deploy` request. -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] #[serde(deny_unknown_fields)] pub struct PutDeployResult { /// The JSON-RPC server version. diff --git a/lib/rpcs/v1_4_5/query_global_state.rs b/lib/rpcs/v1_4_5/query_global_state.rs index 82846d04..79415be1 100644 --- a/lib/rpcs/v1_4_5/query_global_state.rs +++ b/lib/rpcs/v1_4_5/query_global_state.rs @@ -37,7 +37,7 @@ impl QueryGlobalStateParams { } /// The `result` field of a successful JSON-RPC response to a `query_global_state` request. -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] #[serde(deny_unknown_fields)] pub struct QueryGlobalStateResult { /// The JSON-RPC server version. diff --git a/lib/rpcs/v1_5_0.rs b/lib/rpcs/v1_5_0.rs index 0f92e257..c45c386f 100644 --- a/lib/rpcs/v1_5_0.rs +++ b/lib/rpcs/v1_5_0.rs @@ -9,10 +9,6 @@ pub(crate) mod speculative_exec; // The following RPCs are all unchanged from v1.4.5, so we just re-export them. -pub(crate) mod get_account { - pub use crate::rpcs::v1_4_5::get_account::GetAccountResult; -} - pub(crate) mod get_auction_info { pub use crate::rpcs::v1_4_5::get_auction_info::GetAuctionInfoResult; pub(crate) use crate::rpcs::v1_4_5::get_auction_info::{ @@ -85,7 +81,5 @@ pub(crate) mod put_deploy { } pub(crate) mod query_global_state { - pub use crate::rpcs::v1_4_5::query_global_state::{ - GlobalStateIdentifier, QueryGlobalStateResult, - }; + pub use crate::rpcs::v1_4_5::query_global_state::GlobalStateIdentifier; } diff --git a/lib/rpcs/v1_5_0/get_chainspec.rs b/lib/rpcs/v1_5_0/get_chainspec.rs index 3c200d65..bdba4109 100644 --- a/lib/rpcs/v1_5_0/get_chainspec.rs +++ b/lib/rpcs/v1_5_0/get_chainspec.rs @@ -39,7 +39,7 @@ impl Display for ChainspecRawBytes { } /// The `result` field of a successful JSON-RPC response to a `info_get_chainspec` request. -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] #[serde(deny_unknown_fields)] pub struct GetChainspecResult { /// The JSON-RPC server version. diff --git a/lib/rpcs/v1_5_0/get_deploy.rs b/lib/rpcs/v1_5_0/get_deploy.rs index 8592d93b..a104872a 100644 --- a/lib/rpcs/v1_5_0/get_deploy.rs +++ b/lib/rpcs/v1_5_0/get_deploy.rs @@ -6,7 +6,7 @@ pub(crate) use crate::rpcs::v1_4_5::get_deploy::{GetDeployParams, GET_DEPLOY_MET use crate::types::{BlockHashAndHeight, Deploy, ExecutionResult}; /// The `result` field of a successful JSON-RPC response to an `info_get_deploy` request. -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] #[serde(deny_unknown_fields)] pub struct GetDeployResult { /// The JSON-RPC server version. diff --git a/lib/rpcs/v1_5_0/get_era_summary.rs b/lib/rpcs/v1_5_0/get_era_summary.rs index 7d7ddc04..43b11896 100644 --- a/lib/rpcs/v1_5_0/get_era_summary.rs +++ b/lib/rpcs/v1_5_0/get_era_summary.rs @@ -19,7 +19,7 @@ impl GetEraSummaryParams { } /// The `result` field of a successful JSON-RPC response to a `chain_get_era_summary` request. -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] #[serde(deny_unknown_fields)] pub struct GetEraSummaryResult { /// The JSON-RPC server version. diff --git a/lib/rpcs/v1_5_0/get_node_status.rs b/lib/rpcs/v1_5_0/get_node_status.rs index cb73ecc0..39e93b77 100644 --- a/lib/rpcs/v1_5_0/get_node_status.rs +++ b/lib/rpcs/v1_5_0/get_node_status.rs @@ -59,7 +59,7 @@ pub struct BlockSynchronizerStatus { } /// The `result` field of a successful JSON-RPC response to a `info_get_status` request. -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] #[serde(deny_unknown_fields)] pub struct GetNodeStatusResult { /// The JSON-RPC server version. diff --git a/lib/rpcs/v1_5_0/query_balance.rs b/lib/rpcs/v1_5_0/query_balance.rs index 06f12c5a..325304fe 100644 --- a/lib/rpcs/v1_5_0/query_balance.rs +++ b/lib/rpcs/v1_5_0/query_balance.rs @@ -40,7 +40,7 @@ impl QueryBalanceParams { } /// Result for "query_balance" RPC response. -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] pub struct QueryBalanceResult { /// The RPC API version. pub api_version: ProtocolVersion, diff --git a/lib/rpcs/v1_5_0/speculative_exec.rs b/lib/rpcs/v1_5_0/speculative_exec.rs index 63943b6b..24c0db65 100644 --- a/lib/rpcs/v1_5_0/speculative_exec.rs +++ b/lib/rpcs/v1_5_0/speculative_exec.rs @@ -25,7 +25,7 @@ impl SpeculativeExecParams { } /// The `result` field of a successful JSON-RPC response to a `speculative_exec` request. -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] #[serde(deny_unknown_fields)] pub struct SpeculativeExecResult { /// The JSON-RPC server version. diff --git a/lib/rpcs/v1_6_0/get_account.rs b/lib/rpcs/v1_6_0/get_account.rs index 2143d19c..63f9f652 100644 --- a/lib/rpcs/v1_6_0/get_account.rs +++ b/lib/rpcs/v1_6_0/get_account.rs @@ -38,7 +38,7 @@ impl GetAccountParams { } /// The `result` field of a successful JSON-RPC response to a `state_get_account_info` request. -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] #[serde(deny_unknown_fields)] pub struct GetAccountResult { /// The JSON-RPC server version. diff --git a/lib/rpcs/v1_6_0/query_global_state.rs b/lib/rpcs/v1_6_0/query_global_state.rs index cc7cb655..c9472974 100644 --- a/lib/rpcs/v1_6_0/query_global_state.rs +++ b/lib/rpcs/v1_6_0/query_global_state.rs @@ -38,7 +38,7 @@ impl QueryGlobalStateParams { } /// The `result` field of a successful JSON-RPC response to a `query_global_state` request. -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] #[serde(deny_unknown_fields)] pub struct QueryGlobalStateResult { /// The JSON-RPC server version. diff --git a/lib/types/block.rs b/lib/types/block.rs index c9fe4f14..f8d05c8c 100644 --- a/lib/types/block.rs +++ b/lib/types/block.rs @@ -61,6 +61,12 @@ impl Display for BlockHash { } } +impl AsRef<[u8]> for BlockHash { + fn as_ref(&self) -> &[u8] { + self.0.as_ref() + } +} + impl ToBytes for BlockHash { fn write_bytes(&self, buffer: &mut Vec) -> Result<(), bytesrepr::Error> { self.0.write_bytes(buffer) diff --git a/lib/types/deploy.rs b/lib/types/deploy.rs index 3693fd06..8e7eda57 100644 --- a/lib/types/deploy.rs +++ b/lib/types/deploy.rs @@ -45,6 +45,12 @@ impl Display for DeployHash { } } +impl AsRef<[u8]> for DeployHash { + fn as_ref(&self) -> &[u8] { + self.0.as_ref() + } +} + impl ToBytes for DeployHash { fn write_bytes(&self, buffer: &mut Vec) -> Result<(), bytesrepr::Error> { self.0.write_bytes(buffer) @@ -190,11 +196,20 @@ impl ToBytes for Approval { #[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Serialize, Deserialize, Debug)] #[serde(deny_unknown_fields)] pub struct Deploy { - hash: DeployHash, - header: DeployHeader, - payment: ExecutableDeployItem, - session: ExecutableDeployItem, - approvals: Vec, + /// The unique identifier for the deploy. + pub hash: DeployHash, + + /// The header information for the deploy. + pub header: DeployHeader, + + /// The payment logic for the deploy. + pub payment: ExecutableDeployItem, + + /// The session logic for the deploy. + pub session: ExecutableDeployItem, + + /// List of approvals for the deploy. + pub approvals: Vec, } /// Used when constructing a `Deploy`. @@ -369,7 +384,7 @@ fn serialize_body(payment: &ExecutableDeployItem, session: &ExecutableDeployItem pub struct DeployBuilder<'a> { account: Option, secret_key: Option<&'a SecretKey>, - timestamp: Timestamp, + timestamp: Option, ttl: TimeDiff, gas_price: u64, dependencies: Vec, @@ -389,11 +404,19 @@ impl<'a> DeployBuilder<'a> { /// * that payment code is provided by either calling /// [`with_standard_payment`](Self::with_standard_payment) or /// [`with_payment`](Self::with_payment) + /// * if feature `std-fs-io` is not enabled (it is enabled by default) that a timestamp is + /// provided by calling [`with_timestamp`](Self::with_timestamp) pub fn new>(chain_name: C, session: ExecutableDeployItem) -> Self { + // Timestamp::now() not available with feature sdk, use default() instead + #[cfg(feature = "std-fs-io")] + let timestamp = Some(Timestamp::now()); + #[cfg(not(feature = "std-fs-io"))] + let timestamp = None; + DeployBuilder { account: None, secret_key: None, - timestamp: Timestamp::now(), + timestamp, ttl: Deploy::DEFAULT_TTL, gas_price: Deploy::DEFAULT_GAS_PRICE, dependencies: vec![], @@ -415,6 +438,8 @@ impl<'a> DeployBuilder<'a> { /// * that payment code is provided by either calling /// [`with_standard_payment`](Self::with_standard_payment) or /// [`with_payment`](Self::with_payment) + /// * if feature `std-fs-io` is not enabled (it is enabled by default) that a timestamp is + /// provided by calling [`with_timestamp`](Self::with_timestamp) pub fn new_transfer, A: Into>( chain_name: C, amount: A, @@ -460,9 +485,11 @@ impl<'a> DeployBuilder<'a> { /// Sets the `timestamp` in the `Deploy`. /// /// If not provided, the timestamp will be set to the time when the `DeployBuilder` was - /// constructed. + /// constructed if feature `std-fs-io` is enabled (it is enabled by default). If `std-fs-io` + /// is not enabled, [`build`](Self::build) will return an error if `with_timestamp` has not + /// previously been called. pub fn with_timestamp(mut self, timestamp: Timestamp) -> Self { - self.timestamp = timestamp; + self.timestamp = Some(timestamp); self } @@ -474,9 +501,13 @@ impl<'a> DeployBuilder<'a> { self } - /// Returns the new `Deploy`, or an error if neither - /// [`with_standard_payment`](Self::with_standard_payment) nor - /// [`with_payment`](Self::with_payment) were previously called. + /// Returns the new `Deploy`. + /// + /// Returns an error if + /// * neither [`with_standard_payment`](Self::with_standard_payment) nor + /// [`with_payment`](Self::with_payment) were previously called + /// * if feature `std-fs-io` was not enabled (it is enabled by default) and + /// [`with_timestamp`](Self::with_timestamp) was not previously called pub fn build(self) -> Result { let account_and_secret_key = match (self.account, self.secret_key) { (Some(account), Some(secret_key)) => AccountAndSecretKey::Both { @@ -488,9 +519,10 @@ impl<'a> DeployBuilder<'a> { (None, None) => return Err(Error::DeployMissingSessionAccount), }; + let timestamp = self.timestamp.ok_or(Error::DeployMissingTimestamp)?; let payment = self.payment.ok_or(Error::DeployMissingPaymentCode)?; let deploy = Deploy::new( - self.timestamp, + timestamp, self.ttl, self.gas_price, self.dependencies, diff --git a/lib/types/executable_deploy_item.rs b/lib/types/executable_deploy_item.rs index b068762e..5f566d42 100644 --- a/lib/types/executable_deploy_item.rs +++ b/lib/types/executable_deploy_item.rs @@ -198,8 +198,7 @@ impl ExecutableDeployItem { } /// Returns the runtime arguments. - #[cfg(test)] - pub(crate) fn args(&self) -> &RuntimeArgs { + pub fn args(&self) -> &RuntimeArgs { match self { ExecutableDeployItem::ModuleBytes { args, .. } | ExecutableDeployItem::StoredContractByHash { args, .. } diff --git a/lib/types/execution_result.rs b/lib/types/execution_result.rs index f7073c59..98c89320 100644 --- a/lib/types/execution_result.rs +++ b/lib/types/execution_result.rs @@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize}; use super::BlockHash; /// The execution result of a single deploy. -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] #[serde(deny_unknown_fields)] pub struct ExecutionResult { /// The block hash. diff --git a/src/keygen.rs b/src/keygen.rs index 2fc13a4a..c349a8ed 100644 --- a/src/keygen.rs +++ b/src/keygen.rs @@ -101,7 +101,19 @@ impl ClientCommand for Keygen { .arg(algorithm::arg()) } + /// Asynchronously runs the keygen command based on the provided command-line arguments. + /// + /// # Arguments + /// + /// * `matches` - A reference to the `ArgMatches` containing the parsed command-line arguments. + /// + /// # Returns + /// + /// Returns a `Result` indicating the success or failure of the command execution. + /// If successful, returns `Success::Output` with a message indicating the outcome. + /// If unsuccessful, returns a `CliError` with details about the error. async fn run(matches: &ArgMatches) -> Result { + // Retrieve the output directory, key algorithm, and force flag from command-line arguments let output_dir = output_dir::get(matches); let algorithm = algorithm::get(matches); let force = common::force::get(matches); diff --git a/src/main.rs b/src/main.rs index 3584a200..d67770b9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -142,7 +142,7 @@ fn cli() -> Command { )) } -#[tokio::main] +#[tokio::main(flavor = "current_thread")] async fn main() { let arg_matches = cli().get_matches(); let (subcommand_name, matches) = arg_matches.subcommand().unwrap_or_else(|| {