aleo_rust/program/
network.rs

1// Copyright (C) 2019-2023 Aleo Systems Inc.
2// This file is part of the Aleo SDK library.
3
4// The Aleo SDK library is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// The Aleo SDK library is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with the Aleo SDK library. If not, see <https://www.gnu.org/licenses/>.
16
17use super::*;
18
19impl<N: Network> ProgramManager<N> {
20    /// Broadcast a transaction to the network
21    pub fn broadcast_transaction(&self, transaction: Transaction<N>) -> Result<String> {
22        let transaction_type = if let Transaction::Deploy(..) = &transaction { "Deployment" } else { "Execute" };
23        let api_client = self.api_client()?;
24        let result = api_client.transaction_broadcast(transaction);
25        if result.is_ok() {
26            println!("✅ {} Transaction successfully posted to {}", transaction_type, api_client.base_url());
27        } else {
28            println!("❌ {} Transaction failed to post to {}", transaction_type, api_client.base_url());
29        }
30        result
31    }
32
33    /// Get a reference to the configured API client
34    pub fn api_client(&self) -> Result<&AleoAPIClient<N>> {
35        self.api_client.as_ref().ok_or_else(|| anyhow!("No API client found"))
36    }
37
38    /// Check the on-chain version of a program to determine if it is deployed, and if so,
39    /// if it is the same as the local version
40    pub fn on_chain_program_state(&self, program: &Program<N>) -> Result<OnChainProgramState> {
41        let program_id = program.id();
42        Ok(self
43            .api_client()?
44            .get_program(program_id)
45            .map(
46                |chain_program| {
47                    if chain_program.eq(program) { OnChainProgramState::Same } else { OnChainProgramState::Different }
48                },
49            )
50            .unwrap_or(OnChainProgramState::NotDeployed))
51    }
52
53    /// Check the value of an on-chain mapping
54    pub fn get_mapping_value(
55        &self,
56        program_id: impl TryInto<ProgramID<N>>,
57        mapping_name: impl TryInto<Identifier<N>>,
58        key: impl TryInto<Plaintext<N>>,
59    ) -> Result<Value<N>> {
60        let api_client = self.api_client()?;
61        let mapping_value = api_client.get_mapping_value(program_id, mapping_name, key)?;
62        Ok(mapping_value)
63    }
64
65    /// Check the mappings available in a program
66    pub fn get_mappings(&self, program_id: impl TryInto<ProgramID<N>>) -> Result<Vec<Identifier<N>>> {
67        let api_client = self.api_client()?;
68        let mappings = api_client.get_program_mappings(program_id)?;
69        Ok(mappings)
70    }
71}
72
73#[cfg(test)]
74mod tests {
75    use super::*;
76    use crate::{
77        test_utils::{random_program, GENERIC_PROGRAM_BODY, RECIPIENT_PRIVATE_KEY},
78        AleoAPIClient,
79    };
80    use snarkvm_console::{account::PrivateKey, network::Testnet3};
81
82    use std::{ops::Add, str::FromStr};
83
84    #[test]
85    fn test_network_functionality_works_as_expected() {
86        let credits = snarkvm::synthesizer::Program::<Testnet3>::credits().unwrap();
87        let api_client = AleoAPIClient::<Testnet3>::testnet3();
88        let private_key = PrivateKey::<Testnet3>::from_str(RECIPIENT_PRIVATE_KEY).unwrap();
89        // Create a temp dir without proper programs to test that the hybrid client works even if the local resource directory doesn't exist
90        let temp_dir = std::env::temp_dir().join("no_op");
91        let _ = std::fs::create_dir(&temp_dir);
92
93        let program_manager =
94            ProgramManager::<Testnet3>::new(Some(private_key), None, Some(api_client), Some(temp_dir.clone()), false)
95                .unwrap();
96
97        // Test that API clients works
98        let api_client = program_manager.api_client().unwrap();
99        let network_credits_program = api_client.get_program("credits.aleo").unwrap();
100        assert_eq!(network_credits_program, credits);
101
102        // Test that on_chain_program_state gives a positive ID for a deployed program that matches
103        // a local program
104        let program_state = program_manager.on_chain_program_state(&credits).unwrap();
105        assert!(matches!(program_state, OnChainProgramState::Same));
106
107        // Test on_chain_program_state() identifies when a program is not deployed
108        let random_program = random_program();
109        let random_program_state = program_manager.on_chain_program_state(&random_program).unwrap();
110        assert!(matches!(random_program_state, OnChainProgramState::NotDeployed));
111
112        // Test on_chain_program_state() identifies when a program is deployed but different from
113        // the local version
114        let wrong_hello_program_string = String::from("program credits.aleo;\n").add(GENERIC_PROGRAM_BODY);
115        let wrong_hello_program = Program::<Testnet3>::from_str(&wrong_hello_program_string).unwrap();
116        let state_mismatch = program_manager.on_chain_program_state(&wrong_hello_program).unwrap();
117
118        assert!(matches!(state_mismatch, OnChainProgramState::Different));
119
120        let _ = std::fs::remove_dir_all(temp_dir);
121    }
122}