snarkvm_debug/package/
deploy.rs

1// Copyright (C) 2019-2023 Aleo Systems Inc.
2// This file is part of the snarkVM library.
3
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at:
7// http://www.apache.org/licenses/LICENSE-2.0
8
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use crate::ledger::block::Deployment;
16use snarkvm_console::prelude::DeserializeExt;
17
18use super::*;
19
20pub struct DeployRequest<N: Network> {
21    deployment: Deployment<N>,
22    program_id: ProgramID<N>,
23}
24
25impl<N: Network> DeployRequest<N> {
26    /// Sends the request to the given endpoint.
27    pub fn new(deployment: Deployment<N>, program_id: ProgramID<N>) -> Self {
28        Self { deployment, program_id }
29    }
30
31    /// Sends the request to the given endpoint.
32    pub fn send(&self, endpoint: &str) -> Result<DeployResponse<N>> {
33        Ok(ureq::post(endpoint).send_json(self)?.into_json()?)
34    }
35
36    /// Returns the program.
37    pub const fn deployment(&self) -> &Deployment<N> {
38        &self.deployment
39    }
40
41    /// Returns the imports.
42    pub const fn program_id(&self) -> &ProgramID<N> {
43        &self.program_id
44    }
45}
46
47impl<N: Network> Serialize for DeployRequest<N> {
48    /// Serializes the deploy request into string or bytes.
49    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
50        let mut request = serializer.serialize_struct("DeployRequest", 2)?;
51        // Serialize the deployment.
52        request.serialize_field("deployment", &self.deployment)?;
53        // Serialize the program ID.
54        request.serialize_field("program_id", &self.program_id)?;
55        request.end()
56    }
57}
58
59impl<'de, N: Network> Deserialize<'de> for DeployRequest<N> {
60    /// Deserializes the deploy request from a string or bytes.
61    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
62        // Parse the request from a string into a value.
63        let mut request = serde_json::Value::deserialize(deserializer)?;
64        // Recover the leaf.
65        Ok(Self::new(
66            // Retrieve the program.
67            DeserializeExt::take_from_value::<D>(&mut request, "deployment")?,
68            // Retrieve the program ID.
69            DeserializeExt::take_from_value::<D>(&mut request, "program_id")?,
70        ))
71    }
72}
73
74pub struct DeployResponse<N: Network> {
75    deployment: Deployment<N>,
76}
77
78impl<N: Network> DeployResponse<N> {
79    /// Initializes a new deploy response.
80    pub const fn new(deployment: Deployment<N>) -> Self {
81        Self { deployment }
82    }
83
84    /// Returns the program ID.
85    pub const fn deployment(&self) -> &Deployment<N> {
86        &self.deployment
87    }
88}
89
90impl<N: Network> Serialize for DeployResponse<N> {
91    /// Serializes the deploy response into string or bytes.
92    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
93        let mut response = serializer.serialize_struct("DeployResponse", 1)?;
94        response.serialize_field("deployment", &self.deployment)?;
95        response.end()
96    }
97}
98
99impl<'de, N: Network> Deserialize<'de> for DeployResponse<N> {
100    /// Deserializes the deploy response from a string or bytes.
101    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
102        // Parse the response from a string into a value.
103        let mut response = serde_json::Value::deserialize(deserializer)?;
104        // Recover the leaf.
105        Ok(Self::new(
106            // Retrieve the program ID.
107            DeserializeExt::take_from_value::<D>(&mut response, "deployment")?,
108        ))
109    }
110}
111
112impl<N: Network> Package<N> {
113    pub fn deploy<A: crate::circuit::Aleo<Network = N, BaseField = N::Field>>(
114        &self,
115        endpoint: Option<String>,
116    ) -> Result<Deployment<N>> {
117        // Retrieve the main program.
118        let program = self.program();
119        // Retrieve the main program ID.
120        let program_id = program.id();
121
122        #[cfg(feature = "aleo-cli")]
123        println!("⏳ Deploying '{}'...\n", program_id.to_string().bold());
124
125        // Construct the process.
126        let mut process = Process::<N>::load()?;
127
128        // Add program imports to the process.
129        let imports_directory = self.imports_directory();
130        program.imports().keys().try_for_each(|program_id| {
131            // TODO (howardwu): Add the following checks:
132            //  1) the imported program ID exists *on-chain* (for the given network)
133            //  2) the AVM bytecode of the imported program matches the AVM bytecode of the program *on-chain*
134            //  3) consensus performs the exact same checks (in `verify_deployment`)
135
136            // Open the Aleo program file.
137            let import_program_file = AleoFile::open(&imports_directory, program_id, false)?;
138            // Add the import program.
139            process.add_program(import_program_file.program())?;
140            Ok::<_, Error>(())
141        })?;
142
143        // Initialize the RNG.
144        let rng = &mut rand::thread_rng();
145        // Compute the deployment.
146        let deployment = process.deploy::<A, _>(program, rng).unwrap();
147
148        match endpoint {
149            Some(ref endpoint) => {
150                // Construct the deploy request.
151                let request = DeployRequest::new(deployment, *program_id);
152                // Send the deploy request.
153                let response = request.send(endpoint)?;
154                // Ensure the program ID matches.
155                ensure!(
156                    response.deployment.program_id() == program_id,
157                    "Program ID mismatch: {} != {program_id}",
158                    response.deployment.program_id()
159                );
160                Ok(response.deployment)
161            }
162            None => Ok(deployment),
163        }
164    }
165}
166
167#[cfg(test)]
168mod tests {
169    use super::*;
170
171    type CurrentNetwork = snarkvm_console::network::Testnet3;
172    type CurrentAleo = snarkvm_circuit::network::AleoV0;
173
174    #[test]
175    fn test_deploy() {
176        // Samples a new package at a temporary directory.
177        let (directory, package) = crate::package::test_helpers::sample_token_package();
178
179        // Deploy the package.
180        let deployment = package.deploy::<CurrentAleo>(None).unwrap();
181
182        // Ensure the deployment edition matches.
183        assert_eq!(<CurrentNetwork as Network>::EDITION, deployment.edition());
184        // Ensure the deployment program ID matches.
185        assert_eq!(package.program().id(), deployment.program_id());
186        // Ensure the deployment program matches.
187        assert_eq!(package.program(), deployment.program());
188
189        // Proactively remove the temporary directory (to conserve space).
190        std::fs::remove_dir_all(directory).unwrap();
191    }
192
193    #[test]
194    fn test_deploy_with_import() {
195        // Samples a new package at a temporary directory.
196        let (directory, package) = crate::package::test_helpers::sample_wallet_package();
197
198        // Deploy the package.
199        let deployment = package.deploy::<CurrentAleo>(None).unwrap();
200
201        // Ensure the deployment edition matches.
202        assert_eq!(<CurrentNetwork as Network>::EDITION, deployment.edition());
203        // Ensure the deployment program ID matches.
204        assert_eq!(package.program().id(), deployment.program_id());
205        // Ensure the deployment program matches.
206        assert_eq!(package.program(), deployment.program());
207
208        // Proactively remove the temporary directory (to conserve space).
209        std::fs::remove_dir_all(directory).unwrap();
210    }
211}