deploy.rs
1 // Copyright (c) 2019-2025 Alpha-Delta Network Inc. 2 // This file is part of the alphavm 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 alphavm_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 = alphavm_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 }