Skip to main content

stellar_registry_cli/commands/
upgrade.rs

1use clap::Parser;
2use stellar_cli::commands::contract::invoke;
3use stellar_registry_build::named_registry::PrefixedName;
4
5use crate::commands::global;
6
7#[derive(Parser, Debug, Clone)]
8pub struct Cmd {
9    /// Name of contract to upgrade.  Can use prefix of not using verified registry.
10    /// E.g. `unverified/<name>`
11    #[arg(long)]
12    pub contract_name: PrefixedName,
13
14    /// Name of published Wasm.  Can use prefix of not using verified registry.
15    /// E.g. `unverified/<name>`
16    #[arg(long)]
17    pub wasm_name: PrefixedName,
18
19    /// Version of published Wasm, if not specified, the latest version will be fetched
20    #[arg(long)]
21    pub version: Option<String>,
22
23    #[command(flatten)]
24    pub config: global::Args,
25}
26
27#[derive(thiserror::Error, Debug)]
28pub enum Error {
29    #[error(transparent)]
30    Invoke(#[from] invoke::Error),
31    #[error(transparent)]
32    Strkey(#[from] stellar_strkey::DecodeError),
33    #[error(transparent)]
34    LocatorConfig(#[from] stellar_cli::config::locator::Error),
35    #[error(transparent)]
36    Config(#[from] stellar_cli::config::Error),
37    #[error(transparent)]
38    Io(#[from] std::io::Error),
39    #[error("Upgrade failed: {0:?}")]
40    UpgradeFailed(invoke::Error),
41    #[error(transparent)]
42    Registry(#[from] stellar_registry_build::Error),
43}
44
45impl Cmd {
46    pub async fn run(&self) -> Result<(), Error> {
47        let contract_name = &self.contract_name.name;
48        let wasm_name = &self.wasm_name.name;
49
50        let mut slop = vec![
51            "upgrade_contract",
52            "--name",
53            contract_name,
54            "--wasm-name",
55            wasm_name,
56        ];
57        if let Some(version) = self.version.as_deref() {
58            slop.push("--version");
59            slop.push(version);
60        }
61        let registry = self.contract_name.registry(&self.config).await?;
62        registry
63            .as_contract()
64            .invoke_with_result(&slop, false)
65            .await
66            .map_err(Error::UpgradeFailed)?;
67        let version = if let Some(version) = self.version.as_deref() {
68            version.to_string()
69        } else {
70            registry
71                .as_contract()
72                .invoke_with_result(&["current_version", "--wasm-name", wasm_name], true)
73                .await?
74        };
75        println!("Upgraded {contract_name} to {wasm_name}@{version}",);
76        Ok(())
77    }
78}
79
80#[cfg(feature = "integration-tests")]
81#[cfg(test)]
82mod tests {
83
84    use stellar_cli::commands::{contract::invoke, global};
85
86    use stellar_scaffold_test::RegistryTest;
87
88    use crate::commands::{create_alias, upgrade};
89
90    #[tokio::test]
91    #[allow(clippy::too_many_lines)]
92    async fn simple_upgrade() {
93        // Create test environment
94        let registry = RegistryTest::new().await;
95        let v1 = registry.hello_wasm_v1();
96        let v2 = registry.hello_wasm_v2();
97
98        let _test_env = registry.clone().env;
99
100        // Path to the hello world contract WASM
101
102        // First publish the contract
103        registry
104            .registry_cli("publish")
105            .arg("--wasm")
106            .arg(v1.to_str().unwrap())
107            .arg("--binver")
108            .arg("0.0.1")
109            .arg("--wasm-name")
110            .arg("hello")
111            .assert()
112            .success();
113
114        // Then deploy it
115        registry
116            .registry_cli("deploy")
117            .arg("--contract-name")
118            .arg("hello")
119            .arg("--wasm-name")
120            .arg("hello")
121            .arg("--")
122            .arg("--admin=alice")
123            .assert()
124            .success();
125
126        registry
127            .parse_cmd::<create_alias::Cmd>(&["hello"])
128            .unwrap()
129            .run()
130            .await
131            .unwrap();
132
133        let res = registry
134            .parse_cmd::<invoke::Cmd>(&["--id=hello", "--", "hello", "--to=world"])
135            .unwrap()
136            .invoke(&global::Args::default())
137            .await
138            .unwrap()
139            .into_result()
140            .unwrap();
141        assert_eq!(res, r#""world""#);
142
143        // Publish new version
144        registry
145            .registry_cli("publish")
146            .arg("--wasm")
147            .arg(v2.to_str().unwrap())
148            .arg("--binver")
149            .arg("0.0.2")
150            .arg("--wasm-name")
151            .arg("hello")
152            .assert()
153            .success();
154        println!("Published new version of hello contract");
155        registry
156            .parse_cmd::<upgrade::Cmd>(&[
157                "--contract-name",
158                "hello",
159                "--wasm-name",
160                "hello",
161                "--version",
162                "\"0.0.2\"",
163                "--source=alice",
164            ])
165            .unwrap()
166            .run()
167            .await
168            .unwrap();
169
170        let res = registry
171            .parse_cmd::<invoke::Cmd>(&["--id=hello", "--", "hi", "--to=world"])
172            .unwrap()
173            .invoke(&global::Args::default())
174            .await
175            .unwrap()
176            .into_result()
177            .unwrap();
178        assert_eq!(res, r#""world""#);
179
180        // Upgrade the contract using the old version
181        registry
182            .parse_cmd::<upgrade::Cmd>(&[
183                "--contract-name",
184                "hello",
185                "--wasm-name",
186                "hello",
187                "--version",
188                "\"0.0.1\"",
189                "--source=alice",
190            ])
191            .unwrap()
192            .run()
193            .await
194            .unwrap();
195
196        let res = registry
197            .parse_cmd::<invoke::Cmd>(&["--id=hello", "--", "hello", "--to=world"])
198            .unwrap()
199            .invoke(&global::Args::default())
200            .await
201            .unwrap()
202            .into_result()
203            .unwrap();
204        assert_eq!(res, r#""world""#);
205
206        // Upgrade the contract without specifying version to upgrade to the latest version
207        registry
208            .parse_cmd::<upgrade::Cmd>(&["--contract-name", "hello", "--wasm-name", "hello"])
209            .unwrap()
210            .run()
211            .await
212            .unwrap();
213
214        let res = registry
215            .parse_cmd::<invoke::Cmd>(&["--id=hello", "--", "hi", "--to=world"])
216            .unwrap()
217            .invoke(&global::Args::default())
218            .await
219            .unwrap()
220            .into_result()
221            .unwrap();
222        assert_eq!(res, r#""world""#);
223    }
224
225    #[tokio::test]
226    #[allow(clippy::too_many_lines)]
227    async fn unverified() {
228        // Create test environment
229        let registry = RegistryTest::new().await;
230        let v1 = registry.hello_wasm_v1();
231        let v2 = registry.hello_wasm_v2();
232
233        let _test_env = registry.clone().env;
234
235        // Path to the hello world contract WASM
236
237        // First publish the contract
238        registry
239            .registry_cli("publish")
240            .arg("--wasm")
241            .arg(v1.to_str().unwrap())
242            .arg("--binver")
243            .arg("0.0.1")
244            .arg("--wasm-name")
245            .arg("unverified/hello")
246            .assert()
247            .success();
248
249        // Then deploy it
250        registry
251            .registry_cli("deploy")
252            .arg("--contract-name")
253            .arg("unverified/hello")
254            .arg("--wasm-name")
255            .arg("unverified/hello")
256            .arg("--")
257            .arg("--admin=alice")
258            .assert()
259            .success();
260
261        registry
262            .parse_cmd::<create_alias::Cmd>(&["unverified/hello"])
263            .unwrap()
264            .run()
265            .await
266            .unwrap();
267
268        let res = registry
269            .parse_cmd::<invoke::Cmd>(&["--id=hello", "--", "hello", "--to=world"])
270            .unwrap()
271            .invoke(&global::Args::default())
272            .await
273            .unwrap()
274            .into_result()
275            .unwrap();
276        assert_eq!(res, r#""world""#);
277
278        // Publish new version
279        registry
280            .registry_cli("publish")
281            .arg("--wasm")
282            .arg(v2.to_str().unwrap())
283            .arg("--binver")
284            .arg("0.0.2")
285            .arg("--wasm-name")
286            .arg("unverified/hello")
287            .assert()
288            .success();
289        println!("Published new version of hello contract");
290        registry
291            .parse_cmd::<upgrade::Cmd>(&[
292                "--contract-name",
293                "unverified/hello",
294                "--wasm-name",
295                "unverified/hello",
296                "--version",
297                "\"0.0.2\"",
298                "--source=alice",
299            ])
300            .unwrap()
301            .run()
302            .await
303            .unwrap();
304
305        let res = registry
306            .parse_cmd::<invoke::Cmd>(&["--id=hello", "--", "hi", "--to=world"])
307            .unwrap()
308            .invoke(&global::Args::default())
309            .await
310            .unwrap()
311            .into_result()
312            .unwrap();
313        assert_eq!(res, r#""world""#);
314
315        // Upgrade the contract using the old version
316        registry
317            .parse_cmd::<upgrade::Cmd>(&[
318                "--contract-name",
319                "unverified/hello",
320                "--wasm-name",
321                "unverified/hello",
322                "--version",
323                "\"0.0.1\"",
324                "--source=alice",
325            ])
326            .unwrap()
327            .run()
328            .await
329            .unwrap();
330
331        let res = registry
332            .parse_cmd::<invoke::Cmd>(&["--id=hello", "--", "hello", "--to=world"])
333            .unwrap()
334            .invoke(&global::Args::default())
335            .await
336            .unwrap()
337            .into_result()
338            .unwrap();
339        assert_eq!(res, r#""world""#);
340
341        // Upgrade the contract without specifying version to upgrade to the latest version
342        registry
343            .parse_cmd::<upgrade::Cmd>(&[
344                "--contract-name",
345                "unverified/hello",
346                "--wasm-name",
347                "hello",
348            ])
349            .unwrap()
350            .run()
351            .await
352            .unwrap();
353
354        let res = registry
355            .parse_cmd::<invoke::Cmd>(&["--id=hello", "--", "hi", "--to=world"])
356            .unwrap()
357            .invoke(&global::Args::default())
358            .await
359            .unwrap()
360            .into_result()
361            .unwrap();
362        assert_eq!(res, r#""world""#);
363    }
364}