leo_lang/cli/commands/common/
util.rs1use snarkvm::prelude::{ConsensusVersion, Network, Program};
18
19const PROGRAM_SIZE_WARNING_THRESHOLD: usize = 90;
21
22pub fn format_program_size(size: usize, max_size: usize) -> (f64, f64, Option<String>) {
27 let size_kb = size as f64 / 1024.0;
28 let max_kb = max_size as f64 / 1024.0;
29 let percentage = (size as f64 / max_size as f64) * 100.0;
30
31 let warning = if size > max_size * PROGRAM_SIZE_WARNING_THRESHOLD / 100 {
32 Some(format!("approaching the size limit ({percentage:.1}% of {max_kb:.2} KB)"))
33 } else {
34 None
35 };
36
37 (size_kb, max_kb, warning)
38}
39
40pub const LOCAL_PROGRAM_DEFAULT_EDITION: leo_package::Edition = 1;
46
47pub fn print_program_source(id: &str, edition: Option<leo_package::Edition>) {
49 match (id, edition) {
50 ("credits.aleo", _) => println!(" - {id} (already included)"),
51 (_, Some(e)) => println!(" - {id} (edition: {e})"),
52 (_, None) => println!(" - {id} (local)"),
53 }
54}
55
56pub fn check_edition_constructor_requirements<N: Network>(
69 programs: &[(Program<N>, leo_package::Edition)],
70 consensus_version: ConsensusVersion,
71 action: &str,
72) -> Result<(), leo_errors::Backtraced> {
73 if consensus_version < ConsensusVersion::V8 {
75 return Ok(());
76 }
77
78 for (program, edition) in programs {
79 if *edition == 0 && !program.contains_constructor() {
81 let id = program.id();
82 if id.to_string() != "credits.aleo" {
84 return Err(crate::errors::custom(format!(
85 "Cannot {action} with dependency '{id}' (edition 0)\n\n\
86 Programs at edition 0 without a constructor cannot be executed under \
87 consensus version V8 or later (current: V{}).\n\n\
88 The program '{id}' must be upgraded on-chain before it can be used.",
89 consensus_version as u8
90 )));
91 }
92 }
93 }
94
95 Ok(())
96}
97
98pub fn load_extra_programs_into_vm<N: Network>(
103 entries: &[String],
104 vm: &snarkvm::prelude::VM<N, snarkvm::prelude::store::helpers::memory::ConsensusMemory<N>>,
105 context: &crate::cli::context::Context,
106 network: leo_ast::NetworkName,
107 endpoint: Option<&str>,
108 network_retries: u32,
109) -> leo_errors::Result<()> {
110 use snarkvm::prelude::ProgramID;
111 use std::{path::Path, str::FromStr};
112
113 let mut extras: Vec<(Program<N>, leo_package::Edition)> = Vec::new();
114
115 for entry in entries {
116 let path = Path::new(entry);
117 if path.is_file() {
118 println!("📂 Loading local program from {entry}...");
119 let bytecode = std::fs::read_to_string(path)
120 .map_err(|e| crate::errors::custom(format!("Failed to read program file '{entry}': {e}")))?;
121 let program = Program::<N>::from_str(&bytecode)
122 .map_err(|e| crate::errors::custom(format!("Failed to parse program from '{entry}': {e}")))?;
123 extras.push((program, LOCAL_PROGRAM_DEFAULT_EDITION));
124 } else if path.exists() {
125 return Err(crate::errors::custom(format!("'{entry}' exists but is not a file.")).into());
126 } else {
127 let endpoint = endpoint.ok_or_else(|| {
128 crate::errors::custom(format!(
129 "'{entry}' is not a local file; fetching from the network requires --endpoint to be set."
130 ))
131 })?;
132 let name = if entry.ends_with(".aleo") { entry.clone() } else { format!("{entry}.aleo") };
133 println!("⬇️ Fetching remote program {name} and its dependencies from {endpoint}...");
134 let program_id = ProgramID::<N>::from_str(&name)
135 .map_err(|e| crate::errors::custom(format!("Failed to parse program ID '{name}': {e}")))?;
136 let fetched = super::query::load_latest_programs_from_network(
137 context,
138 program_id,
139 network,
140 endpoint,
141 network_retries,
142 )?;
143 extras.extend(fetched.into_iter().map(|(p, ed)| (p, ed.unwrap_or(LOCAL_PROGRAM_DEFAULT_EDITION))));
144 }
145 }
146
147 vm.process().lock().add_programs_with_editions(&extras)?;
148
149 Ok(())
150}
151
152#[cfg(test)]
153mod tests {
154 use super::*;
155 use snarkvm::prelude::TestnetV0;
156 use std::str::FromStr;
157
158 #[test]
159 fn test_edition_constructor_error_message() {
160 let program = Program::<TestnetV0>::from_str(
162 "program old_program.aleo;\nfunction main:\n input r0 as u32.public;\n output r0 as u32.public;\n",
163 )
164 .unwrap();
165
166 let result = check_edition_constructor_requirements(&[(program, 0)], ConsensusVersion::V9, "deploy");
167 assert!(result.is_err());
168 }
169}