use crate::{AleoAPIClient, ProgramManager, RecordFinder, TransferType};
use snarkvm::file::Manifest;
use snarkvm_console::{
account::{PrivateKey, ViewKey},
network::Testnet3,
program::{Plaintext, Record},
};
use anyhow::Result;
use snarkvm::synthesizer::Program;
use std::{fs, fs::File, io::Write, ops::Add, panic::catch_unwind, path::PathBuf, str::FromStr, thread::sleep};
pub const RECIPIENT_PRIVATE_KEY: &str = "APrivateKey1zkp3dQx4WASWYQVWKkq14v3RoQDfY2kbLssUj7iifi1VUQ6";
pub const RECIPIENT_ADDRESS: &str = "aleo184vuwr5u7u0ha5f5k44067dd2uaqewxx6pe5ltha5pv99wvhfqxqv339h4";
pub const BEACON_PRIVATE_KEY: &str = "APrivateKey1zkp8CZNn3yeCseEtxuVPbDCwSyhGW6yZKUYKfgXmcpoGPWH";
pub const IMPORT_PROGRAM: &str = "
import credits.aleo;
program aleo_test.aleo;
function test:
input r0 as u32.public;
input r1 as u32.private;
add r0 r1 into r2;
output r2 as u32.private;
";
pub const FINALIZE_TEST_PROGRAM: &str = "program finalize_test.aleo;
mapping monotonic_counter:
// Counter key
key id as u32.public;
// Counter value
value counter as u32.public;
function increase_counter:
// Counter index
input r0 as u32.public;
// Value to increment by
input r1 as u32.public;
finalize r0 r1;
finalize increase_counter:
// Counter index
input r0 as u32.public;
// Value to increment by
input r1 as u32.public;
// Get or initialize counter key
get.or_use monotonic_counter[r0] 0u32 into r2;
// Add r1 to into the existing counter value
add r1 r2 into r3;
// Set r3 into account[r0];
set r3 into monotonic_counter[r0];
";
pub const CREDITS_IMPORT_TEST_PROGRAM: &str = "import credits.aleo;
program credits_import_test.aleo;
function test:
input r0 as u32.public;
input r1 as u32.private;
add r0 r1 into r2;
output r2 as u32.private;
";
pub const HELLO_PROGRAM: &str = "program hello.aleo;
function hello:
input r0 as u32.public;
input r1 as u32.private;
add r0 r1 into r2;
output r2 as u32.private;
";
pub const HELLO_PROGRAM_2: &str = "program hello.aleo;
function hello:
input r0 as u32.public;
input r1 as u32.private;
mul r0 r1 into r2;
output r2 as u32.private;
";
pub const GENERIC_PROGRAM_BODY: &str = "
function fabulous:
input r0 as u32.public;
input r1 as u32.private;
add r0 r1 into r2;
output r2 as u32.private;
";
pub const MULTIPLY_PROGRAM: &str =
"// 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 =
"// 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 RECORD_2000000001_MICROCREDITS: &str = r"{
owner: aleo1j7qxyunfldj2lp8hsvy7mw5k8zaqgjfyr72x2gh3x4ewgae8v5gscf5jh3.private,
microcredits: 2000000001u64.private,
_nonce: 440655410641037118713377218645355605135385337348439127168929531052605977026group.public
}";
pub const RECORD_5_MICROCREDITS: &str = r"{
owner: aleo1j7qxyunfldj2lp8hsvy7mw5k8zaqgjfyr72x2gh3x4ewgae8v5gscf5jh3.private,
microcredits: 5u64.private,
_nonce: 3700202890700295811197086261814785945731964545546334348117582517467189701159group.public
}";
pub fn random_program_id(len: usize) -> String {
use rand::Rng;
const CHARSET: &[u8] = b"abcdefghijklmnopqrstuvwxyz";
let mut rng = rand::thread_rng();
let program: String = (0..len)
.map(|_| {
let idx = rng.gen_range(0..CHARSET.len());
CHARSET[idx] as char
})
.collect();
program.add(".aleo")
}
pub fn random_program() -> Program<Testnet3> {
let random_program = String::from("program ").add(&random_program_id(15)).add(";").add(GENERIC_PROGRAM_BODY);
Program::<Testnet3>::from_str(&random_program).unwrap()
}
pub fn setup_directory(directory_name: &str, main_program: &str, imports: Vec<(&str, &str)>) -> Result<PathBuf> {
let directory = std::env::temp_dir().join(directory_name);
catch_unwind(|| {
let _ = &directory.exists().then(|| fs::remove_dir_all(&directory).unwrap());
fs::create_dir(&directory).unwrap();
let imports_directory = directory.join("imports");
fs::create_dir(directory.join("imports")).unwrap();
let program = Program::<Testnet3>::from_str(main_program).unwrap();
let program_id = program.id();
Manifest::create(&directory, program_id).unwrap();
let mut main = File::create(directory.join("main.aleo")).unwrap();
main.write_all(main_program.as_bytes()).unwrap();
imports.into_iter().for_each(|(name, program)| {
let mut file = File::create(imports_directory.join(name)).unwrap();
file.write_all(program.as_bytes()).unwrap();
});
})
.map_err(|_| anyhow::anyhow!("Failed to create test directory"))?;
Ok(directory)
}
pub fn teardown_directory(directory: &PathBuf) {
if directory.exists() {
fs::remove_dir_all(directory).unwrap();
}
}
pub fn transfer_to_test_account(
amount: u64,
num_transactions: usize,
recipient_private_key: PrivateKey<Testnet3>,
port: &str,
) -> Result<Vec<Record<Testnet3, Plaintext<Testnet3>>>> {
let api_client = AleoAPIClient::<Testnet3>::local_testnet3(port);
let beacon_private_key = PrivateKey::<Testnet3>::from_str(BEACON_PRIVATE_KEY)?;
let recipient_view_key = ViewKey::<Testnet3>::try_from(&recipient_private_key)?;
let recipient_address = recipient_view_key.to_address();
let record_finder = RecordFinder::<Testnet3>::new(api_client.clone());
let program_manager =
ProgramManager::<Testnet3>::new(Some(beacon_private_key), None, Some(api_client), None, false).unwrap();
let fee = 500_000;
let mut transfer_successes = 0;
let mut retries = 0;
loop {
let input_record = record_finder.find_amount_and_fee_records(amount, fee, &beacon_private_key);
if input_record.is_err() {
println!("No records found, retrying");
retries += 1;
sleep(std::time::Duration::from_secs(3));
continue;
}
let (input_record, fee_record) = input_record.unwrap();
let result = program_manager.transfer(
amount,
500_000,
recipient_address,
TransferType::Private,
None,
Some(input_record),
Some(fee_record),
);
if result.is_ok() {
println!("Transfer succeeded");
transfer_successes += 1;
} else {
println!("Transfer failed with error {:?}, retrying", result);
retries += 1;
}
if transfer_successes > num_transactions {
println!("{} transfers succeeded exiting", transfer_successes);
break;
}
if retries > 15 {
println!("exceeded 15 retries, exiting with found records");
break;
}
sleep(std::time::Duration::from_secs(3));
}
let client = program_manager.api_client()?;
let latest_height = client.latest_height()?;
let records = client.get_unspent_records(&recipient_private_key, 0..latest_height, None, None)?;
Ok(records.into_iter().map(|(_cm, record)| record).collect())
}