snarkos_cli/commands/developer/
deploy.rs1use super::Developer;
17use crate::commands::StoreFormat;
18use snarkvm::{
19 circuit::{Aleo, AleoCanaryV0, AleoTestnetV0, AleoV0},
20 console::{
21 network::{CanaryV0, MainnetV0, Network, TestnetV0},
22 program::ProgramOwner,
23 },
24 ledger::store::helpers::memory::BlockMemory,
25 prelude::{
26 PrivateKey,
27 ProgramID,
28 VM,
29 block::Transaction,
30 deployment_cost,
31 query::Query,
32 store::{ConsensusStore, helpers::memory::ConsensusMemory},
33 },
34};
35
36use aleo_std::StorageMode;
37use anyhow::{Result, bail};
38use clap::Parser;
39use colored::Colorize;
40use snarkvm::{
41 ledger::query::QueryTrait,
42 prelude::{Address, ConsensusVersion},
43};
44use std::{path::PathBuf, str::FromStr};
45use zeroize::Zeroize;
46
47#[derive(Debug, Parser)]
49pub struct Deploy {
50 program_id: String,
52 #[clap(default_value = "0", long = "network")]
54 pub network: u16,
55 #[clap(long)]
57 path: Option<String>,
58 #[clap(short, long)]
60 private_key: String,
61 #[clap(short, long)]
63 query: String,
64 #[clap(long)]
66 priority_fee: u64,
67 #[clap(short, long)]
69 record: Option<String>,
70 #[clap(short, long, conflicts_with = "dry_run")]
72 broadcast: Option<String>,
73 #[clap(short, long, conflicts_with = "broadcast")]
75 dry_run: bool,
76 #[clap(long)]
78 store: Option<String>,
79 #[clap(long, value_enum, default_value_t = StoreFormat::Bytes)]
82 store_format: StoreFormat,
83 #[clap(long = "storage_path")]
86 storage_path: Option<PathBuf>,
87}
88
89impl Drop for Deploy {
90 fn drop(&mut self) {
92 self.private_key.zeroize();
93 }
94}
95
96impl Deploy {
97 pub fn parse(self) -> Result<String> {
99 if !self.dry_run && self.broadcast.is_none() && self.store.is_none() {
101 bail!("❌ Please specify one of the following actions: --broadcast, --dry-run, --store");
102 }
103
104 match self.network {
106 MainnetV0::ID => self.construct_deployment::<MainnetV0, AleoV0>(),
107 TestnetV0::ID => self.construct_deployment::<TestnetV0, AleoTestnetV0>(),
108 CanaryV0::ID => self.construct_deployment::<CanaryV0, AleoCanaryV0>(),
109 unknown_id => bail!("Unknown network ID ({unknown_id})"),
110 }
111 }
112
113 fn construct_deployment<N: Network, A: Aleo<Network = N, BaseField = N::Field>>(&self) -> Result<String> {
115 let query = Query::<N, BlockMemory<N>>::from(&self.query);
117
118 let private_key = PrivateKey::from_str(&self.private_key)?;
120
121 let program_id = ProgramID::from_str(&self.program_id)?;
123
124 let package = Developer::parse_package(program_id, &self.path)?;
126
127 println!("📦 Creating deployment transaction for '{}'...\n", &program_id.to_string().bold());
128
129 let mut deployment = package.deploy::<A>(None)?;
131
132 let consensus_version = N::CONSENSUS_VERSION(query.current_block_height()?)?;
134
135 if consensus_version < ConsensusVersion::V9 {
138 deployment.set_program_checksum_raw(None);
139 deployment.set_program_owner_raw(None);
140 } else {
141 deployment.set_program_checksum_raw(Some(package.program().to_checksum()));
142 deployment.set_program_owner_raw(Some(Address::try_from(&private_key)?));
143 };
144
145 let deployment_id = deployment.to_deployment_id()?;
147
148 let transaction = {
150 let rng = &mut rand::thread_rng();
152
153 let storage_mode = match &self.storage_path {
155 Some(path) => StorageMode::Custom(path.clone()),
156 None => StorageMode::Production,
157 };
158 let store = ConsensusStore::<N, ConsensusMemory<N>>::open(storage_mode)?;
159
160 let vm = VM::from(store)?;
162
163 let (minimum_deployment_cost, (_, _, _, _)) = deployment_cost(&vm.process().read(), &deployment)?;
165
166 let fee = match &self.record {
168 Some(record) => {
169 let fee_record = Developer::parse_record(&private_key, record)?;
170 let fee_authorization = vm.authorize_fee_private(
171 &private_key,
172 fee_record,
173 minimum_deployment_cost,
174 self.priority_fee,
175 deployment_id,
176 rng,
177 )?;
178 vm.execute_fee_authorization(fee_authorization, Some(&query), rng)?
179 }
180 None => {
181 let fee_authorization = vm.authorize_fee_public(
182 &private_key,
183 minimum_deployment_cost,
184 self.priority_fee,
185 deployment_id,
186 rng,
187 )?;
188 vm.execute_fee_authorization(fee_authorization, Some(&query), rng)?
189 }
190 };
191 let owner = ProgramOwner::new(&private_key, deployment_id, rng)?;
193
194 Transaction::from_deployment(owner, deployment, fee)?
196 };
197 println!("✅ Created deployment transaction for '{}'", program_id.to_string().bold());
198
199 Developer::handle_transaction(
201 &self.broadcast,
202 self.dry_run,
203 &self.store,
204 self.store_format,
205 transaction,
206 program_id.to_string(),
207 )
208 }
209}
210
211#[cfg(test)]
212mod tests {
213 use super::*;
214 use crate::commands::{CLI, Command};
215
216 #[test]
217 fn clap_snarkos_deploy() {
218 let arg_vec = vec![
219 "snarkos",
220 "developer",
221 "deploy",
222 "--private-key",
223 "PRIVATE_KEY",
224 "--query",
225 "QUERY",
226 "--priority-fee",
227 "77",
228 "--record",
229 "RECORD",
230 "hello.aleo",
231 ];
232 let cli = CLI::parse_from(arg_vec);
233
234 if let Command::Developer(Developer::Deploy(deploy)) = cli.command {
235 assert_eq!(deploy.network, 0);
236 assert_eq!(deploy.program_id, "hello.aleo");
237 assert_eq!(deploy.private_key, "PRIVATE_KEY");
238 assert_eq!(deploy.query, "QUERY");
239 assert_eq!(deploy.priority_fee, 77);
240 assert_eq!(deploy.record, Some("RECORD".to_string()));
241 } else {
242 panic!("Unexpected result of clap parsing!");
243 }
244 }
245}