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