#![allow(clippy::too_many_arguments)]
use super::*;
impl<N: Network, C: ConsensusStorage<N>> VM<N, C> {
pub fn execute<R: Rng + CryptoRng>(
&self,
private_key: &PrivateKey<N>,
(program_id, function_name): (impl TryInto<ProgramID<N>>, impl TryInto<Identifier<N>>),
inputs: impl ExactSizeIterator<Item = impl TryInto<Value<N>>>,
fee_record: Option<Record<N, Plaintext<N>>>,
priority_fee_in_microcredits: u64,
query: Option<Query<N, C::BlockStorage>>,
rng: &mut R,
) -> Result<Transaction<N>> {
let authorization = self.authorize(private_key, program_id, function_name, inputs, rng)?;
let is_fee_required = !authorization.is_split();
let is_priority_fee_declared = priority_fee_in_microcredits > 0;
let execution = self.execute_authorization_raw(authorization, query.clone(), rng)?;
let fee = match is_fee_required || is_priority_fee_declared {
true => {
let (minimum_execution_cost, (_, _)) = execution_cost(self, &execution)?;
let execution_id = execution.to_execution_id()?;
let authorization = match fee_record {
Some(record) => self.authorize_fee_private(
private_key,
record,
minimum_execution_cost,
priority_fee_in_microcredits,
execution_id,
rng,
)?,
None => self.authorize_fee_public(
private_key,
minimum_execution_cost,
priority_fee_in_microcredits,
execution_id,
rng,
)?,
};
Some(self.execute_fee_authorization_raw(authorization, query, rng)?)
}
false => None,
};
Transaction::from_execution(execution, fee)
}
pub fn execute_authorization<R: Rng + CryptoRng>(
&self,
execute_authorization: Authorization<N>,
fee_authorization: Option<Authorization<N>>,
query: Option<Query<N, C::BlockStorage>>,
rng: &mut R,
) -> Result<Transaction<N>> {
let execution = self.execute_authorization_raw(execute_authorization, query.clone(), rng)?;
let fee = match fee_authorization {
Some(authorization) => Some(self.execute_fee_authorization_raw(authorization, query, rng)?),
None => None,
};
Transaction::from_execution(execution, fee)
}
pub fn execute_fee_authorization<R: Rng + CryptoRng>(
&self,
authorization: Authorization<N>,
query: Option<Query<N, C::BlockStorage>>,
rng: &mut R,
) -> Result<Fee<N>> {
debug_assert!(authorization.is_fee_private() || authorization.is_fee_public(), "Expected a fee authorization");
self.execute_fee_authorization_raw(authorization, query, rng)
}
}
impl<N: Network, C: ConsensusStorage<N>> VM<N, C> {
#[inline]
fn execute_authorization_raw<R: Rng + CryptoRng>(
&self,
authorization: Authorization<N>,
query: Option<Query<N, C::BlockStorage>>,
rng: &mut R,
) -> Result<Execution<N>> {
let timer = timer!("VM::execute_authorization_raw");
let locator = {
let request = authorization.peek_next()?;
Locator::new(*request.program_id(), *request.function_name()).to_string()
};
let query = match query {
Some(query) => query,
None => Query::VM(self.block_store().clone()),
};
lap!(timer, "Prepare the query");
macro_rules! logic {
($process:expr, $network:path, $aleo:path) => {{
let authorization = cast_ref!(authorization as Authorization<$network>);
let (_, mut trace) = $process.execute::<$aleo, _>(authorization.clone(), rng)?;
lap!(timer, "Execute the call");
cast_mut_ref!(trace as Trace<N>).prepare(query)?;
lap!(timer, "Prepare the assignments");
let execution = trace.prove_execution::<$aleo, _>(&locator, rng)?;
lap!(timer, "Compute the proof");
Ok(cast_ref!(execution as Execution<N>).clone())
}};
}
let result = process!(self, logic);
finish!(timer, "Execute the authorization");
result
}
#[inline]
fn execute_fee_authorization_raw<R: Rng + CryptoRng>(
&self,
authorization: Authorization<N>,
query: Option<Query<N, C::BlockStorage>>,
rng: &mut R,
) -> Result<Fee<N>> {
let timer = timer!("VM::execute_fee_authorization_raw");
let query = match query {
Some(query) => query,
None => Query::VM(self.block_store().clone()),
};
lap!(timer, "Prepare the query");
macro_rules! logic {
($process:expr, $network:path, $aleo:path) => {{
let authorization = cast_ref!(authorization as Authorization<$network>);
let (_, mut trace) = $process.execute::<$aleo, _>(authorization.clone(), rng)?;
lap!(timer, "Execute the call");
cast_mut_ref!(trace as Trace<N>).prepare(query)?;
lap!(timer, "Prepare the assignments");
let fee = trace.prove_fee::<$aleo, _>(rng)?;
lap!(timer, "Compute the proof");
Ok(cast_ref!(fee as Fee<N>).clone())
}};
}
let result = process!(self, logic);
finish!(timer, "Execute the authorization");
result
}
}
#[cfg(test)]
mod tests {
use super::*;
use console::{
account::{Address, ViewKey},
network::Testnet3,
program::{Ciphertext, Value},
types::Field,
};
use ledger_block::Transition;
use ledger_store::helpers::memory::ConsensusMemory;
use indexmap::IndexMap;
type CurrentNetwork = Testnet3;
fn prepare_vm(
rng: &mut TestRng,
) -> Result<(
VM<CurrentNetwork, ConsensusMemory<CurrentNetwork>>,
IndexMap<Field<CurrentNetwork>, Record<CurrentNetwork, Ciphertext<CurrentNetwork>>>,
)> {
let genesis = crate::vm::test_helpers::sample_genesis_block(rng);
let records = genesis.transitions().cloned().flat_map(Transition::into_records).collect::<IndexMap<_, _>>();
let genesis = crate::vm::test_helpers::sample_genesis_block(rng);
let vm = crate::vm::test_helpers::sample_vm();
vm.add_next_block(&genesis).unwrap();
Ok((vm, records))
}
#[test]
fn test_transfer_private_transaction_size() {
let rng = &mut TestRng::default();
let caller_private_key = crate::vm::test_helpers::sample_genesis_private_key(rng);
let caller_view_key = ViewKey::try_from(&caller_private_key).unwrap();
let address = Address::try_from(&caller_private_key).unwrap();
let (vm, records) = prepare_vm(rng).unwrap();
let record = records.values().next().unwrap().decrypt(&caller_view_key).unwrap();
let inputs = [
Value::<CurrentNetwork>::Record(record),
Value::<CurrentNetwork>::from_str(&address.to_string()).unwrap(),
Value::<CurrentNetwork>::from_str("1u64").unwrap(),
]
.into_iter();
let transaction =
vm.execute(&caller_private_key, ("credits.aleo", "transfer_private"), inputs, None, 0, None, rng).unwrap();
let transaction_size_in_bytes = transaction.to_bytes_le().unwrap().len();
assert_eq!(3629, transaction_size_in_bytes, "Update me if serialization has changed");
assert!(matches!(transaction, Transaction::Execute(_, _, _)));
if let Transaction::Execute(_, execution, _) = &transaction {
let execution_size_in_bytes = execution.to_bytes_le().unwrap().len();
assert_eq!(2210, execution_size_in_bytes, "Update me if serialization has changed");
}
}
#[test]
fn test_transfer_public_transaction_size() {
let rng = &mut TestRng::default();
let caller_private_key = crate::vm::test_helpers::sample_genesis_private_key(rng);
let address = Address::try_from(&caller_private_key).unwrap();
let (vm, _) = prepare_vm(rng).unwrap();
let inputs = [
Value::<CurrentNetwork>::from_str(&address.to_string()).unwrap(),
Value::<CurrentNetwork>::from_str("1u64").unwrap(),
]
.into_iter();
let transaction =
vm.execute(&caller_private_key, ("credits.aleo", "transfer_public"), inputs, None, 0, None, rng).unwrap();
let transaction_size_in_bytes = transaction.to_bytes_le().unwrap().len();
assert_eq!(2807, transaction_size_in_bytes, "Update me if serialization has changed");
assert!(matches!(transaction, Transaction::Execute(_, _, _)));
if let Transaction::Execute(_, execution, _) = &transaction {
let execution_size_in_bytes = execution.to_bytes_le().unwrap().len();
assert_eq!(1388, execution_size_in_bytes, "Update me if serialization has changed");
}
}
#[test]
fn test_join_transaction_size() {
let rng = &mut TestRng::default();
let caller_private_key = crate::vm::test_helpers::sample_genesis_private_key(rng);
let caller_view_key = ViewKey::try_from(&caller_private_key).unwrap();
let (vm, records) = prepare_vm(rng).unwrap();
let mut records = records.values();
let record_1 = records.next().unwrap().decrypt(&caller_view_key).unwrap();
let record_2 = records.next().unwrap().decrypt(&caller_view_key).unwrap();
let inputs = [Value::<CurrentNetwork>::Record(record_1), Value::<CurrentNetwork>::Record(record_2)].into_iter();
let transaction =
vm.execute(&caller_private_key, ("credits.aleo", "join"), inputs, None, 0, None, rng).unwrap();
let transaction_size_in_bytes = transaction.to_bytes_le().unwrap().len();
assert_eq!(3474, transaction_size_in_bytes, "Update me if serialization has changed");
assert!(matches!(transaction, Transaction::Execute(_, _, _)));
if let Transaction::Execute(_, execution, _) = &transaction {
let execution_size_in_bytes = execution.to_bytes_le().unwrap().len();
assert_eq!(2055, execution_size_in_bytes, "Update me if serialization has changed");
}
}
#[test]
fn test_split_transaction_size() {
let rng = &mut TestRng::default();
let caller_private_key = crate::vm::test_helpers::sample_genesis_private_key(rng);
let caller_view_key = ViewKey::try_from(&caller_private_key).unwrap();
let (vm, records) = prepare_vm(rng).unwrap();
let record = records.values().next().unwrap().decrypt(&caller_view_key).unwrap();
let inputs =
[Value::<CurrentNetwork>::Record(record), Value::<CurrentNetwork>::from_str("1u64").unwrap()].into_iter();
let transaction =
vm.execute(&caller_private_key, ("credits.aleo", "split"), inputs, None, 0, None, rng).unwrap();
let transaction_size_in_bytes = transaction.to_bytes_le().unwrap().len();
assert_eq!(2134, transaction_size_in_bytes, "Update me if serialization has changed");
assert!(matches!(transaction, Transaction::Execute(_, _, _)));
if let Transaction::Execute(_, execution, _) = &transaction {
let execution_size_in_bytes = execution.to_bytes_le().unwrap().len();
assert_eq!(2099, execution_size_in_bytes, "Update me if serialization has changed");
}
}
#[test]
fn test_fee_private_transition_size() {
let rng = &mut TestRng::default();
let transaction = ledger_test_helpers::sample_fee_private_transaction(rng);
let fee = match transaction {
Transaction::Fee(_, fee) => fee,
_ => panic!("Expected a fee transaction"),
};
let fee_size_in_bytes = fee.to_bytes_le().unwrap().len();
assert_eq!(2011, fee_size_in_bytes, "Update me if serialization has changed");
}
#[test]
fn test_fee_public_transition_size() {
let rng = &mut TestRng::default();
let transaction = ledger_test_helpers::sample_fee_public_transaction(rng);
let fee = match transaction {
Transaction::Fee(_, fee) => fee,
_ => panic!("Expected a fee transaction"),
};
let fee_size_in_bytes = fee.to_bytes_le().unwrap().len();
assert_eq!(1384, fee_size_in_bytes, "Update me if serialization has changed");
}
}