snarkos_aot/auth/
mod.rs

1use anyhow::Result;
2use args::{AuthArgs, AuthBlob, FeeKey};
3use auth_fee::estimate_cost;
4use clap::{Args, Subcommand};
5use rand::SeedableRng;
6use rand_chacha::ChaChaRng;
7use snarkvm::synthesizer::{process::deployment_cost, Process};
8
9use crate::{Key, Network};
10
11pub mod args;
12pub mod auth_deploy;
13pub mod auth_fee;
14pub mod auth_id;
15pub mod auth_program;
16pub mod execute;
17pub mod query;
18
19pub fn rng_from_seed(seed: Option<u64>) -> ChaChaRng {
20    if let Some(seed) = seed {
21        ChaChaRng::seed_from_u64(seed)
22    } else {
23        ChaChaRng::from_entropy()
24    }
25}
26
27/// A command to help generate various different types of authorizations and
28/// execute them.
29#[derive(Debug, Subcommand)]
30pub enum AuthCommand<N: Network> {
31    Execute(execute::Execute<N>),
32    Program(AuthProgramCommand<N>),
33    Fee(auth_fee::AuthorizeFee<N>),
34    /// Given an authorization (and fee), return the transaction ID.
35    Id(AuthArgs<N>),
36    Cost(CostCommand<N>),
37    Deploy(AuthDeployCommand<N>),
38}
39
40/// Estimate the cost of a program execution or deployment.
41
42#[derive(Debug, Args)]
43pub struct CostCommand<N: Network> {
44    /// The query to use for the program.
45    #[clap(long)]
46    query: Option<String>,
47    #[clap(flatten)]
48    auth: AuthArgs<N>,
49}
50
51/// Authorize a program execution.
52#[derive(Debug, Args)]
53pub struct AuthProgramCommand<N: Network> {
54    #[clap(flatten)]
55    pub key: Key<N>,
56    #[clap(flatten)]
57    pub fee_key: FeeKey<N>,
58    /// Prevent the fee from being included in the authorization
59    #[clap(long)]
60    pub skip_fee: bool,
61    #[clap(flatten)]
62    pub fee_opts: auth_fee::AuthFeeOptions<N>,
63    #[clap(flatten)]
64    pub program_opts: auth_program::AuthProgramOptions<N>,
65    /// The seed to use for the authorization generation
66    #[clap(long)]
67    pub seed: Option<u64>,
68}
69
70/// Deploy a program to the network.
71#[derive(Debug, Args)]
72pub struct AuthDeployCommand<N: Network> {
73    #[clap(flatten)]
74    pub key: Key<N>,
75    #[clap(flatten)]
76    pub fee_key: FeeKey<N>,
77    /// Prevent the fee from being included in the authorization
78    #[clap(long)]
79    pub skip_fee: bool,
80    #[clap(flatten)]
81    pub fee_opts: auth_fee::AuthFeeOptions<N>,
82    #[clap(flatten)]
83    pub deploy_opts: auth_deploy::AuthDeployOptions<N>,
84    /// The seed to use for the authorization generation
85    #[clap(long)]
86    pub seed: Option<u64>,
87}
88
89impl<N: Network> AuthCommand<N> {
90    pub(crate) fn parse(self) -> Result<()> {
91        match self {
92            // execute command consumes authorizations and outputs a transaction
93            AuthCommand::Execute(command) => command.parse(),
94            // fee command consumes an authorization and a private key to pay a fee. outputs a fee
95            // authorization
96            AuthCommand::Fee(fee) => {
97                println!("{}", serde_json::to_string(&fee.parse()?)?);
98                Ok(())
99            }
100            // id command consumes an authorization or deployment and outputs the predicted
101            // transaction id
102            AuthCommand::Id(args) => {
103                let id = match args.pick()? {
104                    AuthBlob::Program { auth, fee_auth } => {
105                        let auth = auth.into();
106                        let fee_auth = fee_auth.map(Into::into);
107
108                        // calculate the transaction id for the program based off of the authorization and fee
109                        auth_id::auth_tx_id(&auth, fee_auth.as_ref())?
110                    }
111                    AuthBlob::Deploy {
112                        deployment,
113                        fee_auth,
114                        ..
115                        // calculate the transaction id for the deployment based off of the deployment and fee
116                    } => auth_id::deploy_tx_id(&deployment, fee_auth.map(Into::into).as_ref())?,
117                };
118                println!("{id}");
119                Ok(())
120            }
121            AuthCommand::Cost(CostCommand { query, auth }) => {
122                let cost = match auth.pick()? {
123                    AuthBlob::Program { auth, .. } => {
124                        let auth = auth.into();
125
126                        // load the programs the auth references into the process
127                        // as cost estimation measures the size of values from within the auth's
128                        // transitions
129                        let mut process = Process::load()?;
130                        if let Some(query) = query.as_deref() {
131                            let programs = query::get_programs_from_auth(&auth);
132                            query::add_many_programs_to_process(&mut process, programs, query)?;
133                        }
134
135                        estimate_cost(&process, &auth)?
136                    }
137                    AuthBlob::Deploy { deployment, .. } => deployment_cost(&deployment)?.0,
138                };
139                println!("{cost}");
140                Ok(())
141            }
142            AuthCommand::Program(AuthProgramCommand {
143                key,
144                skip_fee,
145                fee_key,
146                program_opts,
147                fee_opts,
148                seed,
149            }) => {
150                let query = program_opts.query.clone();
151
152                // authorize the program execution without a fee
153                let (auth, cost) = auth_program::AuthorizeProgram {
154                    key: key.clone(),
155                    options: program_opts,
156                    seed,
157                }
158                .parse()?;
159
160                if skip_fee {
161                    println!("{}", serde_json::to_string(&auth)?);
162                    return Ok(());
163                };
164
165                // authorize the fee using the authorization's execution ID and estimated cost
166                let fee_auth = auth_fee::AuthorizeFee {
167                    key: fee_key.as_key().unwrap_or(key),
168                    auth: None,
169                    options: fee_opts,
170                    deployment: None,
171                    query,
172                    id: Some(auth.to_execution_id()?),
173                    cost: Some(cost),
174                    seed,
175                }
176                .parse()?;
177
178                println!(
179                    "{}",
180                    serde_json::to_string(&AuthBlob::Program {
181                        auth: auth.into(),
182                        fee_auth: fee_auth.map(Into::into),
183                    })?
184                );
185                Ok(())
186            }
187            AuthCommand::Deploy(AuthDeployCommand {
188                key,
189                skip_fee,
190                fee_key,
191                deploy_opts,
192                fee_opts,
193                seed,
194            }) => {
195                // authorize the deployment without a fee
196                let AuthBlob::Deploy {
197                    deployment, owner, ..
198                } = auth_deploy::AuthorizeDeploy {
199                    key: key.clone(),
200                    options: deploy_opts,
201                    seed,
202                }
203                .parse()?
204                else {
205                    unreachable!("authorize deploy never returns a program auth")
206                };
207
208                if skip_fee {
209                    println!(
210                        "{}",
211                        serde_json::to_string(&AuthBlob::Deploy {
212                            deployment,
213                            owner,
214                            fee_auth: None
215                        })?
216                    );
217                    return Ok(());
218                };
219
220                // authorize the fee using the deployment's ID and estimated cost
221                let fee_auth = auth_fee::AuthorizeFee {
222                    key: fee_key.as_key().unwrap_or(key),
223                    auth: None,
224                    options: fee_opts,
225                    deployment: None,
226                    query: None,
227                    id: Some(deployment.to_deployment_id()?),
228                    cost: Some(deployment_cost(&deployment)?.0),
229                    seed,
230                }
231                .parse()?
232                .map(Into::into);
233
234                println!(
235                    "{}",
236                    serde_json::to_string(&AuthBlob::Deploy {
237                        deployment,
238                        fee_auth,
239                        owner,
240                    })?
241                );
242                Ok(())
243            }
244        }
245    }
246}