Skip to main content

casper_client/cli/
deploy.rs

1//! Functions facilitating sending of [`Deploy`]s to the network
2
3use casper_types::{
4    account::AccountHash, AsymmetricType, Deploy, PublicKey, TransferTarget, UIntParseError, URef,
5    U512,
6};
7
8use super::{
9    parse, transaction::get_maybe_secret_key, CliError, DeployStrParams, PaymentStrParams,
10    SessionStrParams,
11};
12use crate::{
13    cli::DeployBuilder,
14    rpcs::results::{PutDeployResult, SpeculativeExecResult},
15    SuccessResponse, MAX_SERIALIZED_SIZE_OF_DEPLOY,
16};
17
18const DEFAULT_GAS_PRICE: u64 = 1;
19
20/// Creates a [`Deploy`] and sends it to the network for execution.
21///
22/// For details of the parameters, see [the module docs](crate::cli#common-parameters) or the docs
23/// of the individual parameter types.
24pub async fn put_deploy(
25    maybe_rpc_id: &str,
26    node_address: &str,
27    verbosity_level: u64,
28    deploy_params: DeployStrParams<'_>,
29    session_params: SessionStrParams<'_>,
30    payment_params: PaymentStrParams<'_>,
31) -> Result<SuccessResponse<PutDeployResult>, CliError> {
32    let rpc_id = parse::rpc_id(maybe_rpc_id);
33    let verbosity = parse::verbosity(verbosity_level);
34    let deploy = with_payment_and_session(deploy_params, payment_params, session_params, false)?;
35    #[allow(deprecated)]
36    crate::put_deploy(rpc_id, node_address, verbosity, deploy)
37        .await
38        .map_err(CliError::from)
39}
40
41/// Creates a [`Deploy`] and sends it to the specified node for speculative execution.
42///
43/// For details of the parameters, see [the module docs](crate::cli#common-parameters) or the docs
44/// of the individual parameter types.
45pub async fn speculative_put_deploy(
46    maybe_rpc_id: &str,
47    node_address: &str,
48    verbosity_level: u64,
49    deploy_params: DeployStrParams<'_>,
50    session_params: SessionStrParams<'_>,
51    payment_params: PaymentStrParams<'_>,
52) -> Result<SuccessResponse<SpeculativeExecResult>, CliError> {
53    let rpc_id = parse::rpc_id(maybe_rpc_id);
54    let verbosity = parse::verbosity(verbosity_level);
55    let deploy = with_payment_and_session(deploy_params, payment_params, session_params, false)?;
56    #[allow(deprecated)]
57    crate::speculative_exec(rpc_id, node_address, verbosity, deploy)
58        .await
59        .map_err(CliError::from)
60}
61
62/// Returns a [`Deploy`] and outputs it to a file or stdout if the `std-fs-io` feature is enabled.
63///
64/// As a file, the `Deploy` can subsequently be signed by other parties using [`sign_deploy_file`]
65/// and then sent to the network for execution using [`send_deploy_file`].
66///
67/// If the `std-fs-io` feature is NOT enabled, `maybe_output_path` and `force` are ignored.
68/// Otherwise, `maybe_output_path` specifies the output file path, or if empty, will print it to
69/// `stdout`.  If `force` is true, and a file exists at `maybe_output_path`, it will be
70/// overwritten.  If `force` is false and a file exists at `maybe_output_path`,
71/// [`crate::Error::FileAlreadyExists`] is returned and the file will not be written.
72pub fn make_deploy(
73    #[allow(unused_variables)] maybe_output_path: &str,
74    deploy_params: DeployStrParams<'_>,
75    session_params: SessionStrParams<'_>,
76    payment_params: PaymentStrParams<'_>,
77    #[allow(unused_variables)] force: bool,
78) -> Result<Deploy, CliError> {
79    let deploy = with_payment_and_session(deploy_params, payment_params, session_params, true)?;
80    #[cfg(feature = "std-fs-io")]
81    {
82        let output = parse::output_kind(maybe_output_path, force);
83        #[allow(deprecated)]
84        crate::output_deploy(output, &deploy).map_err(CliError::from)?;
85    }
86    Ok(deploy)
87}
88
89/// Reads a previously-saved [`Deploy`] from a file, cryptographically signs it, and outputs it to a
90/// file or stdout.
91///
92/// `maybe_output_path` specifies the output file path, or if empty, will print it to `stdout`.  If
93/// `force` is true, and a file exists at `maybe_output_path`, it will be overwritten.  If `force`
94/// is false and a file exists at `maybe_output_path`, [`crate::Error::FileAlreadyExists`] is returned
95/// and the file will not be written.
96#[cfg(feature = "std-fs-io")]
97pub fn sign_deploy_file(
98    input_path: &str,
99    secret_key_path: &str,
100    maybe_output_path: &str,
101    force: bool,
102) -> Result<(), CliError> {
103    let secret_key = parse::secret_key_from_file(secret_key_path)?;
104    let output = parse::output_kind(maybe_output_path, force);
105    #[allow(deprecated)]
106    crate::sign_deploy_file(input_path, &secret_key, output).map_err(CliError::from)
107}
108
109/// Reads a previously-saved [`Deploy`] from a file and sends it to the network for execution.
110///
111/// For details of the parameters, see [the module docs](crate::cli#common-parameters).
112#[cfg(feature = "std-fs-io")]
113pub async fn send_deploy_file(
114    maybe_rpc_id: &str,
115    node_address: &str,
116    verbosity_level: u64,
117    input_path: &str,
118) -> Result<SuccessResponse<PutDeployResult>, CliError> {
119    let rpc_id = parse::rpc_id(maybe_rpc_id);
120    let verbosity = parse::verbosity(verbosity_level);
121    #[allow(deprecated)]
122    let deploy = crate::read_deploy_file(input_path)?;
123    #[allow(deprecated)]
124    crate::put_deploy(rpc_id, node_address, verbosity, deploy)
125        .await
126        .map_err(CliError::from)
127}
128
129/// Reads a previously-saved [`Deploy`] from a file and sends it to the specified node for
130/// speculative execution.
131/// For details of the parameters, see [the module docs](crate::cli#common-parameters).
132#[cfg(feature = "std-fs-io")]
133pub async fn speculative_send_deploy_file(
134    maybe_rpc_id: &str,
135    node_address: &str,
136    verbosity_level: u64,
137    input_path: &str,
138) -> Result<SuccessResponse<SpeculativeExecResult>, CliError> {
139    let rpc_id = parse::rpc_id(maybe_rpc_id);
140    let verbosity = parse::verbosity(verbosity_level);
141    #[allow(deprecated)]
142    let deploy = crate::read_deploy_file(input_path)?;
143    #[allow(deprecated)]
144    crate::speculative_exec(rpc_id, node_address, verbosity, deploy)
145        .await
146        .map_err(CliError::from)
147}
148
149/// Transfers funds between purses.
150///
151/// * `amount` is a string to be parsed as a `U512` specifying the amount to be transferred.
152/// * `target_account` is the [`AccountHash`], [`URef`] or [`PublicKey`] of the account to which the
153///   funds will be transferred, formatted as a hex-encoded string.  The account's main purse will
154///   receive the funds.
155/// * `transfer_id` is a string to be parsed as a `u64` representing a user-defined identifier which
156///   will be permanently associated with the transfer.
157///
158/// For details of other parameters, see [the module docs](crate::cli#common-parameters).
159#[allow(clippy::too_many_arguments)]
160pub async fn transfer(
161    maybe_rpc_id: &str,
162    node_address: &str,
163    verbosity_level: u64,
164    amount: &str,
165    target_account: &str,
166    transfer_id: &str,
167    deploy_params: DeployStrParams<'_>,
168    payment_params: PaymentStrParams<'_>,
169) -> Result<SuccessResponse<PutDeployResult>, CliError> {
170    let rpc_id = parse::rpc_id(maybe_rpc_id);
171    let verbosity = parse::verbosity(verbosity_level);
172    let deploy = new_transfer(
173        amount,
174        None,
175        target_account,
176        transfer_id,
177        deploy_params,
178        payment_params,
179        false,
180    )?;
181    #[allow(deprecated)]
182    crate::put_deploy(rpc_id, node_address, verbosity, deploy)
183        .await
184        .map_err(CliError::from)
185}
186
187/// Creates a [`Deploy`] to transfer funds between purses, and sends it to the specified node for
188/// speculative execution.
189///
190/// * `amount` is a string to be parsed as a `U512` specifying the amount to be transferred.
191/// * `target_account` is the [`AccountHash`], [`URef`] or [`PublicKey`] of the account to which the
192///   funds will be transferred, formatted as a hex-encoded string.  The account's main purse will
193///   receive the funds.
194/// * `transfer_id` is a string to be parsed as a `u64` representing a user-defined identifier which
195///   will be permanently associated with the transfer.
196///
197/// For details of other parameters, see [the module docs](crate::cli#common-parameters).
198#[allow(clippy::too_many_arguments)]
199pub async fn speculative_transfer(
200    maybe_rpc_id: &str,
201    node_address: &str,
202    verbosity_level: u64,
203    amount: &str,
204    target_account: &str,
205    transfer_id: &str,
206    deploy_params: DeployStrParams<'_>,
207    payment_params: PaymentStrParams<'_>,
208) -> Result<SuccessResponse<SpeculativeExecResult>, CliError> {
209    let rpc_id = parse::rpc_id(maybe_rpc_id);
210    let verbosity = parse::verbosity(verbosity_level);
211    let deploy = new_transfer(
212        amount,
213        None,
214        target_account,
215        transfer_id,
216        deploy_params,
217        payment_params,
218        false,
219    )?;
220    #[allow(deprecated)]
221    crate::speculative_exec(rpc_id, node_address, verbosity, deploy)
222        .await
223        .map_err(CliError::from)
224}
225
226/// Returns a transfer [`Deploy`] and outputs it to a file or stdout if the `std-fs-io` feature is
227/// enabled.
228///
229/// As a file, the `Deploy` can subsequently be signed by other parties using [`sign_deploy_file`]
230/// and then sent to the network for execution using [`send_deploy_file`].
231///
232/// If the `std-fs-io` feature is NOT enabled, `maybe_output_path` and `force` are ignored.
233/// Otherwise, `maybe_output_path` specifies the output file path, or if empty, will print it to
234/// `stdout`.  If `force` is true, and a file exists at `maybe_output_path`, it will be
235/// overwritten.  If `force` is false and a file exists at `maybe_output_path`,
236/// [`crate::Error::FileAlreadyExists`] is returned and the file will not be written.
237pub fn make_transfer(
238    #[allow(unused_variables)] maybe_output_path: &str,
239    amount: &str,
240    target_account: &str,
241    transfer_id: &str,
242    deploy_params: DeployStrParams<'_>,
243    payment_params: PaymentStrParams<'_>,
244    #[allow(unused_variables)] force: bool,
245) -> Result<Deploy, CliError> {
246    let deploy = new_transfer(
247        amount,
248        None,
249        target_account,
250        transfer_id,
251        deploy_params,
252        payment_params,
253        true,
254    )?;
255    #[cfg(feature = "std-fs-io")]
256    {
257        let output = parse::output_kind(maybe_output_path, force);
258        #[allow(deprecated)]
259        crate::output_deploy(output, &deploy).map_err(CliError::from)?;
260    }
261    Ok(deploy)
262}
263
264/// Creates new Deploy with specified payment and session data.
265pub fn with_payment_and_session(
266    deploy_params: DeployStrParams,
267    payment_params: PaymentStrParams,
268    session_params: SessionStrParams,
269    allow_unsigned_deploy: bool,
270) -> Result<Deploy, CliError> {
271    let gas_price: u64 = deploy_params
272        .gas_price_tolerance
273        .parse::<u64>()
274        .unwrap_or(DEFAULT_GAS_PRICE);
275    let chain_name = deploy_params.chain_name.to_string();
276    let session = parse::session_executable_deploy_item(session_params)?;
277    let payment = parse::payment_executable_deploy_item(payment_params)?;
278    let timestamp = parse::timestamp(deploy_params.timestamp)?;
279    let ttl = parse::ttl(deploy_params.ttl)?;
280    let maybe_session_account = parse::session_account(deploy_params.session_account)?;
281
282    let mut deploy_builder = DeployBuilder::new(chain_name, session)
283        .with_payment(payment)
284        .with_timestamp(timestamp)
285        .with_gas_price(gas_price)
286        .with_ttl(ttl);
287    let maybe_secret_key = get_maybe_secret_key(
288        deploy_params.secret_key,
289        allow_unsigned_deploy,
290        "with_payment_and_session",
291    )?;
292    if let Some(secret_key) = &maybe_secret_key {
293        deploy_builder = deploy_builder.with_secret_key(secret_key);
294    }
295    if let Some(account) = maybe_session_account {
296        deploy_builder = deploy_builder.with_account(account);
297    }
298
299    let deploy = deploy_builder.build().map_err(crate::Error::from)?;
300    deploy
301        .is_valid_size(MAX_SERIALIZED_SIZE_OF_DEPLOY)
302        .map_err(crate::Error::from)?;
303    Ok(deploy)
304}
305
306/// Creates new Transfer with specified data.
307pub fn new_transfer(
308    amount: &str,
309    source_purse: Option<URef>,
310    target_account: &str,
311    transfer_id: &str,
312    deploy_params: DeployStrParams,
313    payment_params: PaymentStrParams,
314    allow_unsigned_deploy: bool,
315) -> Result<Deploy, CliError> {
316    let chain_name = deploy_params.chain_name.to_string();
317    let payment = parse::payment_executable_deploy_item(payment_params)?;
318    let amount = U512::from_dec_str(amount).map_err(|err| CliError::FailedToParseUint {
319        context: "new_transfer amount",
320        error: UIntParseError::FromDecStr(err),
321    })?;
322
323    let target = if let Ok(public_key) = PublicKey::from_hex(target_account) {
324        TransferTarget::PublicKey(public_key)
325    } else if let Ok(account_hash) = AccountHash::from_formatted_str(target_account) {
326        TransferTarget::AccountHash(account_hash)
327    } else if let Ok(uref) = URef::from_formatted_str(target_account) {
328        TransferTarget::URef(uref)
329    } else {
330        return Err(CliError::InvalidArgument {
331            context: "new_transfer target_account",
332            error: format!(
333                "allowed types: PublicKey, AccountHash or URef, got {}",
334                target_account
335            ),
336        });
337    };
338
339    let transfer_id = parse::transfer_id(transfer_id)?;
340    let maybe_transfer_id = Some(transfer_id);
341
342    let timestamp = parse::timestamp(deploy_params.timestamp)?;
343    let ttl = parse::ttl(deploy_params.ttl)?;
344    let maybe_session_account = parse::session_account(deploy_params.session_account)?;
345    let gas_price: u64 = deploy_params
346        .gas_price_tolerance
347        .parse::<u64>()
348        .unwrap_or(DEFAULT_GAS_PRICE);
349
350    let mut deploy_builder =
351        DeployBuilder::new_transfer(chain_name, amount, source_purse, target, maybe_transfer_id)
352            .with_payment(payment)
353            .with_timestamp(timestamp)
354            .with_gas_price(gas_price)
355            .with_ttl(ttl);
356
357    let maybe_secret_key = get_maybe_secret_key(
358        deploy_params.secret_key,
359        allow_unsigned_deploy,
360        "new_transfer",
361    )?;
362    if let Some(secret_key) = &maybe_secret_key {
363        deploy_builder = deploy_builder.with_secret_key(secret_key);
364    }
365    if let Some(account) = maybe_session_account {
366        deploy_builder = deploy_builder.with_account(account);
367    }
368    let deploy = deploy_builder.build().map_err(crate::Error::from)?;
369    deploy
370        .is_valid_size(MAX_SERIALIZED_SIZE_OF_DEPLOY)
371        .map_err(crate::Error::from)?;
372    Ok(deploy)
373}