snarkos_aot/auth/
args.rs

1use std::{path::PathBuf, str::FromStr};
2
3use anyhow::anyhow;
4use clap::{Args, Parser};
5use clap_stdin::MaybeStdin;
6use serde::{Deserialize, Serialize};
7use snarkvm::{
8    ledger::{Deployment, Transition},
9    prelude::{PrivateKey, ProgramOwner, Request},
10    synthesizer::Authorization,
11    utilities::DeserializeExt,
12};
13
14use crate::{Key, Network};
15
16/// The authorization arguments.
17#[derive(Clone, Debug, Parser)]
18pub struct AuthArgs<N: Network> {
19    /// Authorization for an execution of some kind.
20    #[clap(short, long)]
21    pub auth: Option<ProxyAuthorization<N>>,
22    /// The optional fee authorization for said execution.
23    #[clap(short, long)]
24    pub fee_auth: Option<ProxyAuthorization<N>>,
25    /// The owner of the program if deploying.
26    #[clap(short, long)]
27    pub owner: Option<ProgramOwner<N>>,
28    /// The deployment of the program if deploying.
29    #[clap(short, long)]
30    pub deployment: Option<Deployment<N>>,
31    /// Authorization flags as json
32    ///
33    /// `{"auth": Program Auth, "fee_auth": Fee Auth }`
34    ///
35    /// `{"deployment": Deployment, "owner": Prog Owner, "fee_auth": Fee Auth }`
36    json: Option<MaybeStdin<AuthBlob<N>>>,
37}
38
39impl<N: Network> AuthArgs<N> {
40    pub fn pick(self) -> anyhow::Result<AuthBlob<N>> {
41        self.json
42            .map(MaybeStdin::into_inner)
43            .or_else(|| match (self.auth, self.owner, self.deployment) {
44                (Some(auth), None, None) => Some(AuthBlob::Program {
45                    auth,
46                    fee_auth: self.fee_auth,
47                }),
48                (None, Some(owner), Some(deployment)) => Some(AuthBlob::Deploy {
49                    owner,
50                    deployment,
51                    fee_auth: self.fee_auth,
52                }),
53                _ => None,
54            })
55            .ok_or(anyhow!("No authorization provided"))
56    }
57}
58
59/*
60    | json: { auth, fee_auth }
61    | json: { deployment, owner, fee_auth }
62
63    | --auth --fee_auth
64    | --deployment --owner --fee_auth
65*/
66
67#[derive(Clone, Debug, Serialize)]
68#[serde(untagged)]
69pub enum AuthBlob<N: Network> {
70    Program {
71        /// The authorization for the program.
72        auth: ProxyAuthorization<N>,
73        /// The optional fee authorization for the program.
74        fee_auth: Option<ProxyAuthorization<N>>,
75    },
76    Deploy {
77        /// The owner of the program.
78        owner: ProgramOwner<N>,
79        /// The deployment of the program.
80        deployment: Deployment<N>,
81        /// The optional fee authorization for the deployment.
82        #[serde(skip_serializing_if = "Option::is_none")]
83        fee_auth: Option<ProxyAuthorization<N>>,
84    },
85}
86
87impl<'de, N: Network> Deserialize<'de> for AuthBlob<N> {
88    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
89    where
90        D: serde::Deserializer<'de>,
91    {
92        let mut value = serde_json::Value::deserialize(deserializer)?;
93        let fee_auth = value
94            .get("fee_auth")
95            .is_some()
96            .then(|| DeserializeExt::take_from_value::<D>(&mut value, "fee_auth"))
97            .transpose()?;
98
99        if value.get("auth").is_some() {
100            Ok(Self::Program {
101                fee_auth,
102                auth: DeserializeExt::take_from_value::<D>(&mut value, "auth")?,
103            })
104        } else {
105            Ok(Self::Deploy {
106                fee_auth,
107                owner: DeserializeExt::take_from_value::<D>(&mut value, "owner")?,
108                deployment: DeserializeExt::take_from_value::<D>(&mut value, "deployment")?,
109            })
110        }
111    }
112}
113
114impl<N: Network> FromStr for AuthBlob<N> {
115    type Err = serde_json::Error;
116
117    fn from_str(s: &str) -> Result<Self, Self::Err> {
118        serde_json::from_str(s)
119    }
120}
121
122/// This type exists because aleo's Authorization::try_from((Vec<Request>,
123/// Vec<Transition>)) has a bug that prevents deserialization from working on
124/// programs with multiple transitions
125///
126/// This is a wrapper that converts to and from authorizations
127#[derive(Clone, Debug, Serialize)]
128pub struct ProxyAuthorization<N: Network> {
129    pub requests: Vec<Request<N>>,
130    pub transitions: Vec<Transition<N>>,
131}
132
133impl<'de, N: Network> Deserialize<'de> for ProxyAuthorization<N> {
134    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
135    where
136        D: serde::Deserializer<'de>,
137    {
138        let mut value = serde_json::Value::deserialize(deserializer)?;
139        Ok(Self {
140            requests: DeserializeExt::take_from_value::<D>(&mut value, "requests")?,
141            transitions: DeserializeExt::take_from_value::<D>(&mut value, "transitions")?,
142        })
143    }
144}
145
146impl<N: Network> FromStr for ProxyAuthorization<N> {
147    type Err = serde_json::Error;
148
149    fn from_str(s: &str) -> Result<Self, Self::Err> {
150        serde_json::from_str(s)
151    }
152}
153
154impl<N: Network> From<ProxyAuthorization<N>> for Authorization<N> {
155    fn from(auth: ProxyAuthorization<N>) -> Self {
156        let new_auth = Authorization::try_from((vec![], vec![])).unwrap();
157        for req in auth.requests {
158            new_auth.push(req);
159        }
160        for transition in auth.transitions {
161            let _ = new_auth.insert_transition(transition);
162        }
163
164        new_auth
165    }
166}
167
168impl<N: Network> From<Authorization<N>> for ProxyAuthorization<N> {
169    fn from(auth: Authorization<N>) -> Self {
170        let mut requests = vec![];
171        let mut transitions = vec![];
172
173        for req in auth.to_vec_deque() {
174            requests.push(req.clone());
175        }
176        for transition in auth.transitions().values() {
177            transitions.push(transition.clone());
178        }
179
180        Self {
181            requests,
182            transitions,
183        }
184    }
185}
186
187/// A private key for the fee account.
188/// Either a private key or a file containing the private key.
189#[derive(Debug, Args, Clone)]
190#[group(multiple = false)]
191pub struct FeeKey<N: Network> {
192    /// Specify the account private key of the node
193    #[clap(long = "fee-private-key")]
194    pub fee_private_key: Option<PrivateKey<N>>,
195    /// Specify the account private key of the node
196    #[clap(long = "fee-private-key-file")]
197    pub fee_private_key_file: Option<PathBuf>,
198}
199
200impl<N: Network> FeeKey<N> {
201    pub fn get(self) -> Option<PrivateKey<N>> {
202        match (self.fee_private_key, self.fee_private_key_file) {
203            (Some(key), None) => Some(key),
204            (None, Some(file)) => {
205                let raw = std::fs::read_to_string(file).ok()?.trim().to_string();
206                PrivateKey::from_str(&raw).ok()
207            }
208            _ => None,
209        }
210    }
211
212    pub fn as_key(self) -> Option<Key<N>> {
213        Some(Key {
214            // this might seem redundant, but it `None` instead of `Some({ private_key: None, ...
215            // })`
216            private_key: Some(self.get()?),
217            private_key_file: None,
218        })
219    }
220}