mod helpers;
mod authorize;
mod deploy;
mod execute;
mod finalize;
mod verify;
use crate::{
cast_ref,
ledger::{
store::{BlockStorage, BlockStore, ProgramStorage, ProgramStore},
AdditionalFee,
Transaction,
},
process,
process::{Authorization, Deployment, Execution, Process},
program::Program,
};
use console::{
account::PrivateKey,
network::prelude::*,
program::{Identifier, Plaintext, ProgramID, Record, Response, Value},
};
use core::marker::PhantomData;
use parking_lot::RwLock;
use std::sync::Arc;
#[derive(Clone)]
pub struct VM<N: Network, P: ProgramStorage<N>> {
process: Arc<RwLock<Process<console::network::Testnet3>>>,
store: ProgramStore<N, P>,
_phantom: PhantomData<N>,
}
impl<N: Network, P: ProgramStorage<N>> VM<N, P> {
#[inline]
pub fn new(store: ProgramStore<N, P>) -> Result<Self> {
Ok(Self { process: Arc::new(RwLock::new(Process::load()?)), store, _phantom: PhantomData })
}
#[inline]
pub fn from<B: BlockStorage<N>>(blocks: &BlockStore<N, B>, store: ProgramStore<N, P>) -> Result<Self> {
let transaction_store = blocks.transaction_store();
let mut process = Process::load()?;
for transaction_id in transaction_store.deployment_ids() {
match transaction_store.get_deployment(&transaction_id)? {
Some(deployment) => process.load_deployment(&deployment)?,
None => bail!("Deployment transaction '{transaction_id}' is not found in storage."),
};
}
macro_rules! logic {
($process:expr, $network:path, $aleo:path) => {{
let process = cast_ref!(process as Process<$network>);
Ok(Self { process: Arc::new(RwLock::new((*process).clone())), store, _phantom: PhantomData })
}};
}
process!(self, logic)
}
#[inline]
pub fn contains_program(&self, program_id: &ProgramID<N>) -> bool {
macro_rules! logic {
($process:expr, $network:path, $aleo:path) => {{
let task = || {
let program_id = cast_ref!(&program_id as ProgramID<$network>);
Ok($process.contains_program(program_id))
};
task()
}};
}
match process!(self, logic) {
Ok(contains_program) => contains_program,
Err(error) => {
warn!("Failed to check if program exists: {error}");
false
}
}
}
}
#[cfg(test)]
pub(crate) mod test_helpers {
use super::*;
use crate::{program::Program, ProgramMemory, RecordsFilter};
use console::{
account::{Address, ViewKey},
network::Testnet3,
program::{Identifier, Value},
};
use once_cell::sync::OnceCell;
type CurrentNetwork = Testnet3;
pub(crate) fn sample_vm() -> VM<CurrentNetwork, ProgramMemory<CurrentNetwork>> {
VM::new(ProgramStore::open().unwrap()).unwrap()
}
pub(crate) fn sample_program() -> Program<CurrentNetwork> {
static INSTANCE: OnceCell<Program<CurrentNetwork>> = OnceCell::new();
INSTANCE
.get_or_init(|| {
Program::<CurrentNetwork>::from_str(
r"
program testing.aleo;
interface message:
amount as u128;
record token:
owner as address.private;
gates as u64.private;
amount as u64.private;
function compute:
input r0 as message.private;
input r1 as message.public;
input r2 as message.private;
input r3 as token.record;
add r0.amount r1.amount into r4;
cast r3.owner r3.gates r3.amount into r5 as token.record;
output r4 as u128.public;
output r5 as token.record;",
)
.unwrap()
})
.clone()
}
pub(crate) fn sample_deployment_transaction() -> Transaction<CurrentNetwork> {
static INSTANCE: OnceCell<Transaction<CurrentNetwork>> = OnceCell::new();
INSTANCE
.get_or_init(|| {
let program = sample_program();
let caller_private_key = crate::ledger::test_helpers::sample_genesis_private_key();
let caller_view_key = ViewKey::try_from(&caller_private_key).unwrap();
let ledger = crate::ledger::test_helpers::sample_genesis_ledger();
let records = ledger
.find_records(&caller_view_key, RecordsFilter::SlowUnspent(caller_private_key))
.unwrap()
.filter(|(_, record)| !record.gates().is_zero())
.collect::<indexmap::IndexMap<_, _>>();
trace!("Unspent Records:\n{:#?}", records);
let credits = records.values().next().unwrap().clone();
let additional_fee = (credits, 10);
let rng = &mut test_crypto_rng();
let vm = sample_vm();
let transaction = Transaction::deploy(&vm, &caller_private_key, &program, additional_fee, rng).unwrap();
assert!(vm.verify(&transaction));
transaction
})
.clone()
}
pub(crate) fn sample_execution_transaction() -> Transaction<CurrentNetwork> {
static INSTANCE: OnceCell<Transaction<CurrentNetwork>> = OnceCell::new();
INSTANCE
.get_or_init(|| {
let caller_private_key = crate::ledger::test_helpers::sample_genesis_private_key();
let caller_view_key = ViewKey::try_from(&caller_private_key).unwrap();
let address = Address::try_from(&caller_private_key).unwrap();
let ledger = crate::ledger::test_helpers::sample_genesis_ledger();
let records = ledger
.find_records(&caller_view_key, RecordsFilter::SlowUnspent(caller_private_key))
.unwrap()
.filter(|(_, record)| !record.gates().is_zero())
.collect::<indexmap::IndexMap<_, _>>();
trace!("Unspent Records:\n{:#?}", records);
let record = records.values().next().unwrap().clone();
let rng = &mut test_crypto_rng();
let vm = sample_vm();
let authorization = vm
.authorize(
&caller_private_key,
&ProgramID::from_str("credits.aleo").unwrap(),
Identifier::from_str("transfer").unwrap(),
&[
Value::<CurrentNetwork>::Record(record),
Value::<CurrentNetwork>::from_str(&address.to_string()).unwrap(),
Value::<CurrentNetwork>::from_str("1u64").unwrap(),
],
rng,
)
.unwrap();
assert_eq!(authorization.len(), 1);
let transaction = Transaction::execute_authorization(&vm, authorization, rng).unwrap();
assert!(vm.verify(&transaction));
transaction
})
.clone()
}
}