aleo_agent/
deploy.rs

1//! program deployment implementation
2
3use std::str::FromStr;
4
5use anyhow::{bail, ensure, Error};
6
7use crate::agent::Agent;
8use crate::program::ProgramManager;
9
10use super::*;
11
12impl Agent {
13    /// Deploy a program to the network
14    ///
15    /// # Arguments
16    ///  * `program` - The program to deploy
17    ///  * `priority_fee` - The priority fee to pay for the deployment
18    ///  * `fee_record` - The fee record to pay for deployment costs
19    ///
20    /// # Returns
21    /// * The transaction hash of the deployment transaction
22    pub fn deploy_program(
23        &self,
24        program: &Program,
25        priority_fee: u64,
26        fee_record: Option<PlaintextRecord>,
27    ) -> anyhow::Result<String> {
28        // Check if program is already deployed on chain, cancel deployment if so
29        let program_id = program.id();
30        ensure!(
31            ProgramManager::get_program_from_chain(program_id).is_err(),
32            "❌ Program {:?} already deployed on chain, cancelling deployment",
33            program_id
34        );
35
36        // If the program has imports, check if they are deployed on chain. If not, cancel deployment
37        program.imports().keys().try_for_each(|program_id| {
38            if ProgramManager::get_program_from_chain(program_id).is_err() {
39                bail!("❌ Imported program {program_id:?} could not be found on the Aleo Network, please deploy this imported program first before continuing with deployment of {program_id:?}");
40            }
41            Ok(())
42        })?;
43
44        let private_key = self.account().private_key();
45
46        // Create the deployment transaction
47        let transaction = self.create_deploy_transaction(
48            program,
49            private_key,
50            self.base_url(),
51            priority_fee,
52            fee_record,
53        )?;
54
55        self.broadcast_transaction(&transaction)
56    }
57
58    /// Create a deployment transaction for a program without instantiating the program manager
59    fn create_deploy_transaction(
60        &self,
61        program: &Program,
62        private_key: &PrivateKey,
63        node_url: &String,
64        priority_fee: u64,
65        fee_record: Option<PlaintextRecord>,
66    ) -> anyhow::Result<Transaction> {
67        // Initialize an RNG.
68        let rng = &mut rand::thread_rng();
69        let query = Query::from(node_url);
70
71        // Initialize the VM
72        let vm = Self::initialize_vm(program)?;
73
74        // Create the deployment transaction
75        vm.deploy(
76            private_key,
77            program,
78            fee_record,
79            priority_fee,
80            Some(query),
81            rng,
82        )
83    }
84
85    fn initialize_vm(program: &Program) -> anyhow::Result<VM> {
86        // Create an ephemeral SnarkVM to store the programs
87        // Initialize an RNG and query object for the transaction
88        let store = ConsensusStore::open(None)?;
89        let vm = VM::from(store)?;
90
91        // Resolve imports
92        let credits_id = ProgramID::from_str("credits.aleo")?;
93        ProgramManager::get_import_programs_from_chain(program)?
94            .iter()
95            .try_for_each(|(_, import)| {
96                if import.id() != &credits_id {
97                    vm.process().write().add_program(import)?
98                }
99                Ok::<_, Error>(())
100            })?;
101        Ok(vm)
102    }
103}