use super::{CurrentNetwork, Developer, Program};
use snarkvm::{
prelude::{ConsensusStore, Identifier, Locator, Plaintext, PrivateKey, ProgramID, Query, Record, Value, VM},
synthesizer::store::helpers::memory::ConsensusMemory,
};
use anyhow::{bail, Result};
use clap::Parser;
use colored::Colorize;
use std::str::FromStr;
#[derive(Debug, Parser)]
pub struct Execute {
program_id: ProgramID<CurrentNetwork>,
function: Identifier<CurrentNetwork>,
inputs: Vec<Value<CurrentNetwork>>,
#[clap(short, long)]
private_key: String,
#[clap(short, long)]
query: String,
#[clap(short, long)]
fee: Option<u64>,
#[clap(short, long)]
record: Option<String>,
#[clap(short, long, conflicts_with = "broadcast")]
display: bool,
#[clap(short, long, conflicts_with = "display")]
broadcast: Option<String>,
#[clap(long)]
store: Option<String>,
}
impl Execute {
#[allow(clippy::format_in_format_args)]
pub fn parse(self) -> Result<String> {
let query = Query::from(&self.query);
let private_key = PrivateKey::from_str(&self.private_key)?;
let response = ureq::get(&format!("{}/testnet3/program/{}", self.query, self.program_id)).call();
let program: Program<CurrentNetwork> = match response {
Ok(response) => response.into_json()?,
Err(err) => match err {
ureq::Error::Status(_status, response) => {
bail!(response.into_string().unwrap_or("Response too large!".to_owned()))
}
err => bail!(err),
},
};
println!("📦 Creating execution transaction for '{}'...\n", &self.program_id.to_string().bold());
let execution = {
let rng = &mut rand::thread_rng();
let store = ConsensusStore::<CurrentNetwork, ConsensusMemory<CurrentNetwork>>::open(None)?;
let vm = VM::from(store)?;
let credits = ProgramID::<CurrentNetwork>::try_from("credits.aleo")?;
if program.id() != &credits {
let deployment = vm.deploy_raw(&program, rng)?;
vm.process().write().load_deployment(&deployment)?;
}
let fee = match self.record {
Some(record) => Some((
Record::<CurrentNetwork, Plaintext<CurrentNetwork>>::from_str(&record)?,
self.fee.unwrap_or(0),
)),
None => {
if program.id() != &credits && self.function != Identifier::from_str("split")? {
bail!("❌ A record must be provided to pay for the transaction fee.");
}
None
}
};
vm.execute(&private_key, (self.program_id, self.function), self.inputs.iter(), fee, Some(query), rng)?
};
let locator = Locator::<CurrentNetwork>::from_str(&format!("{}/{}", self.program_id, self.function))?;
println!("✅ Created execution transaction for '{}'", locator.to_string().bold());
Developer::handle_transaction(self.broadcast, self.display, self.store, execution, locator.to_string())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::commands::{Command, CLI};
#[test]
fn clap_snarkos_execute() {
let arg_vec = vec![
"snarkos",
"developer",
"execute",
"--private-key",
"PRIVATE_KEY",
"--query",
"QUERY",
"--fee",
"77",
"--record",
"RECORD",
"hello.aleo",
"hello",
"1u32",
"2u32",
];
let cli = CLI::parse_from(arg_vec);
if let Command::Developer(Developer::Execute(execute)) = cli.command {
assert_eq!(execute.private_key, "PRIVATE_KEY");
assert_eq!(execute.query, "QUERY");
assert_eq!(execute.fee, Some(77));
assert_eq!(execute.record, Some("RECORD".into()));
assert_eq!(execute.program_id, "hello.aleo".try_into().unwrap());
assert_eq!(execute.function, "hello".try_into().unwrap());
assert_eq!(execute.inputs, vec!["1u32".try_into().unwrap(), "2u32".try_into().unwrap()]);
} else {
panic!("Unexpected result of clap parsing!");
}
}
}