pub mod deploy;
pub use deploy::*;
pub mod execute;
pub use execute::*;
pub mod join;
pub use join::*;
pub mod split;
pub use split::*;
pub mod transfer;
pub use transfer::*;
const DEFAULT_URL: &str = "https://api.explorer.aleo.org/v1";
use crate::{KeyPair, PrivateKey, ProvingKey, RecordPlaintext, VerifyingKey};
use crate::types::native::{
cost_in_microcredits,
deployment_cost,
IdentifierNative,
ProcessNative,
ProgramIDNative,
ProgramNative,
ProvingKeyNative,
QueryNative,
VerifyingKeyNative,
};
use js_sys::{Object, Reflect};
use std::str::FromStr;
use wasm_bindgen::prelude::wasm_bindgen;
#[wasm_bindgen]
#[derive(Clone)]
pub struct ProgramManager;
#[wasm_bindgen]
impl ProgramManager {
pub(crate) fn validate_amount(credits: f64, amount: &RecordPlaintext, fee: bool) -> Result<u64, String> {
let name = if fee { "Fee" } else { "Amount" };
if credits <= 0.0 {
return Err(format!("{name} must be greater than zero to deploy or execute a program"));
}
let microcredits = (credits * 1_000_000.0f64) as u64;
if amount.microcredits() < microcredits {
return Err(format!("{name} record does not have enough credits to pay the specified fee"));
}
Ok(microcredits)
}
#[wasm_bindgen(js_name = "synthesizeKeyPair")]
pub async fn synthesize_keypair(
private_key: &PrivateKey,
program: &str,
function_id: &str,
inputs: js_sys::Array,
imports: Option<Object>,
) -> Result<KeyPair, String> {
ProgramManager::execute_function_offline(
private_key,
program,
function_id,
inputs,
false,
true,
imports,
None,
None,
None,
None,
)
.await?
.get_keys()
}
pub(crate) fn contains_key(
process: &ProcessNative,
program_id: &ProgramIDNative,
function_id: &IdentifierNative,
) -> bool {
process.get_stack(program_id).map_or_else(
|_| false,
|stack| stack.contains_proving_key(function_id) && stack.contains_verifying_key(function_id),
)
}
pub(crate) fn resolve_imports(
process: &mut ProcessNative,
program: &ProgramNative,
imports: Option<Object>,
) -> Result<(), String> {
if let Some(imports) = imports {
program.imports().keys().try_for_each(|program_id| {
let program_id = program_id.to_string();
if let Some(import_string) = Reflect::get(&imports, &program_id.as_str().into())
.map_err(|_| "Program import not found in imports provided".to_string())?
.as_string()
{
if &program_id != "credits.aleo" {
crate::log(&format!("Importing program: {}", program_id));
let import = ProgramNative::from_str(&import_string).map_err(|err| err.to_string())?;
Self::resolve_imports(process, &import, Some(imports.clone()))?;
if !process.contains_program(import.id()) {
process.add_program(&import).map_err(|err| err.to_string())?;
}
}
}
Ok::<(), String>(())
})
} else {
Ok(())
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use js_sys::{Object, Reflect};
use wasm_bindgen::JsValue;
use wasm_bindgen_test::*;
pub const MULTIPLY_PROGRAM: &str = r#"// The 'multiply_test.aleo' program which is imported by the 'double_test.aleo' program.
program multiply_test.aleo;
function multiply:
input r0 as u32.public;
input r1 as u32.private;
mul r0 r1 into r2;
output r2 as u32.private;
"#;
pub const MULTIPLY_IMPORT_PROGRAM: &str = r#"// The 'double_test.aleo' program that uses a single import from another program to perform doubling.
import multiply_test.aleo;
program double_test.aleo;
function double_it:
input r0 as u32.private;
call multiply_test.aleo/multiply 2u32 r0 into r1;
output r1 as u32.private;
"#;
pub const ADDITION_PROGRAM: &str = r#"// The 'addition_test.aleo' program is imported by the 'double_test.aleo' program.
program addition_test.aleo;
function binary_add:
input r0 as u32.public;
input r1 as u32.private;
add r0 r1 into r2;
output r2 as u32.private;
"#;
pub const NESTED_IMPORT_PROGRAM: &str = r#"// The 'imported_add_mul.aleo' program uses a nested series of imports. It imports the 'double_test.aleo' program
// which then imports the 'multiply_test.aleo' program and implicitly uses that to perform the doubling.
import double_test.aleo;
import addition_test.aleo;
program imported_add_mul.aleo;
function add_and_double:
input r0 as u32.public;
input r1 as u32.private;
call addition_test.aleo/binary_add r0 r1 into r2;
call double_test.aleo/double_it r2 into r3;
output r3 as u32.private;
"#;
#[wasm_bindgen_test]
fn test_import_resolution() {
let imports = Object::new();
Reflect::set(&imports, &JsValue::from_str("multiply_test.aleo"), &JsValue::from_str(MULTIPLY_PROGRAM)).unwrap();
Reflect::set(&imports, &JsValue::from_str("addition_test.aleo"), &JsValue::from_str(ADDITION_PROGRAM)).unwrap();
Reflect::set(&imports, &JsValue::from_str("double_test.aleo"), &JsValue::from_str(MULTIPLY_IMPORT_PROGRAM))
.unwrap();
let mut process = ProcessNative::load_web().unwrap();
let program = ProgramNative::from_str(NESTED_IMPORT_PROGRAM).unwrap();
let add_program = ProgramNative::from_str(ADDITION_PROGRAM).unwrap();
let multiply_program = ProgramNative::from_str(MULTIPLY_PROGRAM).unwrap();
let double_program = ProgramNative::from_str(MULTIPLY_IMPORT_PROGRAM).unwrap();
ProgramManager::resolve_imports(&mut process, &program, Some(imports)).unwrap();
let add_import = process.get_program("addition_test.aleo").unwrap();
let multiply_import = process.get_program("multiply_test.aleo").unwrap();
let double_import = process.get_program("double_test.aleo").unwrap();
let main_program = process.get_program("imported_add_mul.aleo");
assert_eq!(add_import, &add_program);
assert_eq!(multiply_import, &multiply_program);
assert_eq!(double_import, &double_program);
assert!(main_program.is_err());
}
}