use super::*;
impl<N: Network> ProgramManager<N> {
pub fn execute_program(
&mut self,
program_id: impl TryInto<ProgramID<N>>,
function: impl TryInto<Identifier<N>>,
inputs: impl ExactSizeIterator<Item = impl TryInto<Value<N>>>,
fee: u64,
fee_record: Record<N, Plaintext<N>>,
password: Option<&str>,
) -> Result<String> {
ensure!(fee > 0, "Fee must be greater than 0");
ensure!(
self.api_client.is_some(),
"❌ Network client not set. A network client must be set before execution in order to send an execution transaction to the Aleo network"
);
let program_id = program_id.try_into().map_err(|_| anyhow!("Invalid program ID"))?;
let function_id = function.try_into().map_err(|_| anyhow!("Invalid function name"))?;
let function_name = function_id.to_string();
let program = self
.api_client()?
.get_program(program_id)
.map_err(|_| anyhow!("Program {program_id:?} does not exist on the Aleo Network. Try deploying the program first before executing."))?;
let private_key = self.get_private_key(password)?;
println!("Building transaction..");
let query = self.api_client.as_ref().unwrap().base_url();
let transaction = Self::create_execute_transaction(
&private_key,
fee,
inputs,
fee_record,
&program,
function_id,
query.to_string(),
)?;
println!("Attempting to broadcast execution transaction for {program_id:?}");
let execution = self.broadcast_transaction(transaction);
if execution.is_ok() {
println!("✅ Execution of function {function_name:?} from program {program_id:?}' broadcast successfully");
} else {
println!("❌ Execution of function {function_name:?} from program {program_id:?} failed to broadcast");
}
execution
}
pub fn create_execute_transaction(
private_key: &PrivateKey<N>,
fee: u64,
inputs: impl ExactSizeIterator<Item = impl TryInto<Value<N>>>,
fee_record: Record<N, Plaintext<N>>,
program: &Program<N>,
function: impl TryInto<Identifier<N>>,
query: String,
) -> Result<Transaction<N>> {
let rng = &mut rand::thread_rng();
let query = Query::from(query);
let function_name = function.try_into().map_err(|_| anyhow!("Invalid function name"))?;
let program_id = program.id();
println!("Checking function {function_name:?} exists in {program_id:?}");
ensure!(
program.contains_function(&function_name),
"Program {program_id:?} does not contain function {function_name:?}, aborting execution"
);
let store = ConsensusStore::<N, ConsensusMemory<N>>::open(None)?;
let vm = VM::<N, ConsensusMemory<N>>::from(store)?;
if &program.id().to_string() != "credits.aleo" {
let deployment = vm.deploy(program, rng)?;
vm.process().write().finalize_deployment(vm.program_store(), &deployment)?;
};
Transaction::execute(
&vm,
private_key,
(program_id, function_name),
inputs,
Some((fee_record, fee)),
Some(query),
rng,
)
}
}
#[cfg(test)]
#[cfg(not(feature = "wasm"))]
mod tests {
use super::*;
use crate::{random_program_id, AleoAPIClient, RECORD_5_MICROCREDITS};
use snarkvm_console::network::Testnet3;
use std::str::FromStr;
#[test]
#[ignore]
fn test_execution() {
let private_key = PrivateKey::<Testnet3>::from_str(RECIPIENT_PRIVATE_KEY).unwrap();
let encrypted_private_key =
crate::Encryptor::encrypt_private_key_with_secret(&private_key, "password").unwrap();
let api_client = AleoAPIClient::<Testnet3>::local_testnet3("3030");
let record_finder = RecordFinder::new(api_client.clone());
let mut program_manager =
ProgramManager::<Testnet3>::new(Some(private_key), None, Some(api_client.clone()), None).unwrap();
for _ in 0..5 {
let fee_record = record_finder.find_one_record(&private_key, 500_000).unwrap();
let execution = program_manager.execute_program(
"credits_import_test.aleo",
"test",
["1312u32", "62131112u32"].into_iter(),
500_000,
fee_record,
None,
);
if execution.is_ok() {
break;
}
}
let mut program_manager =
ProgramManager::<Testnet3>::new(None, Some(encrypted_private_key), Some(api_client), None).unwrap();
for _ in 0..5 {
let fee_record = record_finder.find_one_record(&private_key, 500_000).unwrap();
let execution = program_manager.execute_program(
"credits_import_test.aleo",
"test",
["1337u32", "42u32"].into_iter(),
500000,
fee_record,
Some("password"),
);
if execution.is_ok() {
break;
}
}
}
#[test]
fn test_execution_failure_modes() {
let rng = &mut rand::thread_rng();
let recipient_private_key = PrivateKey::<Testnet3>::new(rng).unwrap();
let api_client = AleoAPIClient::<Testnet3>::testnet3();
let record_5_microcredits = Record::<Testnet3, Plaintext<Testnet3>>::from_str(RECORD_5_MICROCREDITS).unwrap();
let record_2000000001_microcredits =
Record::<Testnet3, Plaintext<Testnet3>>::from_str(RECORD_2000000001_MICROCREDITS).unwrap();
let mut program_manager =
ProgramManager::<Testnet3>::new(Some(recipient_private_key), None, Some(api_client), None).unwrap();
let execution = program_manager.execute_program(
"hello.aleo",
"main",
["5u32", "6u32"].into_iter(),
500000,
record_5_microcredits,
None,
);
assert!(execution.is_err());
let execution = program_manager.execute_program(
"hello.aleo",
"main",
["5u32", "6u32"].into_iter(),
200,
record_2000000001_microcredits.clone(),
None,
);
assert!(execution.is_err());
let randomized_program_id = random_program_id(16);
let execution = program_manager.execute_program(
&randomized_program_id,
"main",
["5u32", "6u32"].into_iter(),
500000,
record_2000000001_microcredits.clone(),
None,
);
assert!(execution.is_err());
let execution = program_manager.execute_program(
"hello.aleo",
"random_function",
["5u32", "6u32"].into_iter(),
500000,
record_2000000001_microcredits.clone(),
None,
);
assert!(execution.is_err());
let execution = program_manager.execute_program(
"hello.aleo",
"random_function",
["5u32", "6u32"].into_iter(),
500000,
record_2000000001_microcredits,
None,
);
assert!(execution.is_err());
}
}