1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
// Copyright (C) Gear Technologies Inc.
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
//! Command `create-program`.
use crate::{app::App, utils::HexBytes};
use anyhow::{Context, Result};
use clap::{Args, Parser};
use colored::Colorize;
use gear_core::ids::CodeId;
use std::path::PathBuf;
use tokio::{
fs,
io::{self, AsyncReadExt},
};
/// Deploy a program to a Gear node.
#[derive(Clone, Debug, Parser)]
pub struct CreateProgram {
/// Program salt, as hex string.
///
/// Used to create multiple programs with the same code.
#[arg(short, long, default_value = "0x")]
salt: HexBytes,
/// Initial message payload, as hex string.
///
/// Sent to the program during initialization.
#[arg(short, long, default_value = "0x")]
init_payload: HexBytes,
/// Operation gas limit.
///
/// Defaults to the estimated gas limit
/// required for the operation.
#[arg(short, long)]
gas_limit: Option<u64>,
/// Initial program balance.
#[arg(short, long, default_value = "0")]
value: u128,
#[clap(flatten)]
code_args: CodeArgs,
}
#[derive(Debug, Clone, Args)]
#[group(required = true, multiple = false)]
struct CodeArgs {
/// ID of a previously uploaded code.
///
/// Mutually exclusive with `--path` and `--stdin`.
#[arg(short, long)]
code_id: Option<CodeId>,
/// Path to the program code.
///
/// Mutually exclusive with `--code-id` and `--stdin`.
#[arg(short, long)]
path: Option<PathBuf>,
/// Read the program code from stdin.
///
/// Mutually exclusive with `--code-id` and `--path`.
#[arg(long)]
stdin: bool,
}
enum Code {
Uploaded(CodeId),
Binary(Vec<u8>),
}
impl CodeArgs {
async fn into_code(self) -> Result<Code> {
match self {
Self {
code_id: Some(code_id),
path: None,
stdin: false,
} => Ok(Code::Uploaded(code_id)),
Self {
code_id: None,
path: Some(path),
stdin: false,
} => Ok(Code::Binary(
fs::read(path)
.await
.context("failed to read program code from file")?,
)),
Self {
code_id: None,
path: None,
stdin: true,
} => Ok(Code::Binary({
let mut buffer = Vec::new();
io::stdin()
.read_to_end(&mut buffer)
.await
.context("failed to read program code from stdin")?;
buffer
})),
_ => unreachable!(), // `CodeArgs` is only used by `clap`, which validates the input
}
}
}
impl CreateProgram {
pub async fn exec(self, app: &mut App) -> Result<()> {
let api = app.signed_api().await?;
let code = self.code_args.into_code().await?;
let gas_limit = if let Some(gas_limit) = self.gas_limit {
gas_limit
} else {
match &code {
Code::Uploaded(code_id) => {
api.calculate_create_gas(*code_id, &self.init_payload, self.value, false)
.await?
}
Code::Binary(code) => {
api.calculate_upload_gas(code, &self.init_payload, self.value, false)
.await?
}
}
.min_limit
};
let (message_id, program_id) = match code {
Code::Uploaded(code_id) => {
api.create_program_bytes(
code_id,
self.salt.as_slice(),
self.init_payload.as_slice(),
gas_limit,
self.value,
)
.await?
}
Code::Binary(code) => {
api.upload_program_bytes(
code,
self.salt.as_slice(),
self.init_payload.as_slice(),
gas_limit,
self.value,
)
.await?
}
}
.value;
println!("Successfully deployed the program");
println!();
println!("{} {}", "Initial message ID:".bold(), message_id);
println!("{} {}", "Program ID:".bold(), program_id);
Ok(())
}
}