#![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 query = query.clone().unwrap_or(Query::VM(self.block_store().clone()));
let block_height = query.current_block_height()?;
let (minimum_execution_cost, (_, _)) = match block_height < N::CONSENSUS_V2_HEIGHT {
true => execution_cost_v1(&self.process().read(), &execution)?,
false => execution_cost_v2(&self.process().read(), &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, Some(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::MainnetV0,
program::{Ciphertext, Value},
types::Field,
};
use ledger_block::Transition;
use ledger_store::helpers::memory::ConsensusMemory;
use synthesizer_process::{ConsensusFeeVersion, cost_per_command, execution_cost_v2};
use synthesizer_program::StackProgram;
use indexmap::IndexMap;
type CurrentNetwork = MainnetV0;
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_bond_validator_transaction_size() {
let rng = &mut TestRng::default();
let validator_private_key = crate::vm::test_helpers::sample_genesis_private_key(rng);
let withdrawal_private_key = PrivateKey::<CurrentNetwork>::new(rng).unwrap();
let withdrawal_address = Address::try_from(&withdrawal_private_key).unwrap();
let (vm, _) = prepare_vm(rng).unwrap();
let inputs = [
Value::<CurrentNetwork>::from_str(&withdrawal_address.to_string()).unwrap(),
Value::<CurrentNetwork>::from_str("1_000_000u64").unwrap(),
Value::<CurrentNetwork>::from_str("5u8").unwrap(),
]
.into_iter();
let transaction =
vm.execute(&validator_private_key, ("credits.aleo", "bond_validator"), inputs, None, 0, None, rng).unwrap();
assert_eq!(transaction.transitions().count(), 2);
assert!(transaction.transitions().take(1).next().unwrap().is_bond_validator());
let transaction_size_in_bytes = transaction.to_bytes_le().unwrap().len();
assert_eq!(2914, 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!(1463, execution_size_in_bytes, "Update me if serialization has changed");
}
}
#[test]
fn test_bond_public_transaction_size() {
let rng = &mut TestRng::default();
let validator_private_key = crate::vm::test_helpers::sample_genesis_private_key(rng);
let validator_address = Address::try_from(&validator_private_key).unwrap();
let delegator_private_key = PrivateKey::<CurrentNetwork>::new(rng).unwrap();
let delegator_address = Address::try_from(&delegator_private_key).unwrap();
let (vm, _) = prepare_vm(rng).unwrap();
let inputs = [
Value::<CurrentNetwork>::from_str(&validator_address.to_string()).unwrap(),
Value::<CurrentNetwork>::from_str(&delegator_address.to_string()).unwrap(),
Value::<CurrentNetwork>::from_str("1_000_000u64").unwrap(),
]
.into_iter();
let transaction =
vm.execute(&delegator_private_key, ("credits.aleo", "bond_public"), inputs, None, 0, None, rng).unwrap();
assert_eq!(transaction.transitions().count(), 2);
assert!(transaction.transitions().take(1).next().unwrap().is_bond_public());
let transaction_size_in_bytes = transaction.to_bytes_le().unwrap().len();
assert_eq!(2970, 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!(1519, execution_size_in_bytes, "Update me if serialization has changed");
}
}
#[cfg(feature = "test")]
#[test]
fn test_execute_cost_fee_migration() {
let rng = &mut TestRng::default();
let credits_program = "credits.aleo";
let function_name = "transfer_public";
let caller_private_key: PrivateKey<MainnetV0> = PrivateKey::new(rng).unwrap();
let recipient_private_key: PrivateKey<MainnetV0> = PrivateKey::new(rng).unwrap();
let recipient_address = Address::try_from(&recipient_private_key).unwrap();
let transfer_public_amount = 1_000_000u64;
let inputs = &[recipient_address.to_string(), format!("{transfer_public_amount}_u64")];
let (vm, _) = prepare_vm(rng).unwrap();
let authorization = vm.authorize(&caller_private_key, credits_program, function_name, inputs, rng).unwrap();
let execution = vm.execute_authorization_raw(authorization, None, rng).unwrap();
let (cost, _) = execution_cost_v2(&vm.process().read(), &execution).unwrap();
let (old_cost, _) = execution_cost_v1(&vm.process().read(), &execution).unwrap();
assert_eq!(34_060, cost);
assert_eq!(51_060, old_cost);
assert_eq!(old_cost - cost, 8_500 * 2);
}
#[cfg(feature = "test")]
#[test]
fn test_fee_migration_occurs_at_correct_block_height() {
assert_ne!(0, CurrentNetwork::CONSENSUS_V2_HEIGHT);
let rng = &mut TestRng::default();
let private_key = crate::vm::test_helpers::sample_genesis_private_key(rng);
let address = Address::try_from(&private_key).unwrap();
let (vm, _) = prepare_vm(rng).unwrap();
let inputs = [
Value::<CurrentNetwork>::from_str(&address.to_string()).unwrap(),
Value::<CurrentNetwork>::from_str("1_000_000u64").unwrap(),
]
.into_iter();
let transaction =
vm.execute(&private_key, ("credits.aleo", "transfer_public"), inputs.clone(), None, 0, None, rng).unwrap();
assert_eq!(51_060, *transaction.base_fee_amount().unwrap());
let transactions: [Transaction<CurrentNetwork>; 0] = [];
for _ in 0..CurrentNetwork::CONSENSUS_V2_HEIGHT {
let next_block = crate::vm::test_helpers::sample_next_block(&vm, &private_key, &transactions, rng).unwrap();
vm.add_next_block(&next_block).unwrap();
}
let transaction =
vm.execute(&private_key, ("credits.aleo", "transfer_public"), inputs.clone(), None, 0, None, rng).unwrap();
assert_eq!(34_060, *transaction.base_fee_amount().unwrap());
}
#[cfg(feature = "test")]
#[test]
fn test_fee_migration_correctly_calculates_nested() {
assert_ne!(0, CurrentNetwork::CONSENSUS_V2_HEIGHT);
let rng = &mut TestRng::default();
let private_key = crate::vm::test_helpers::sample_genesis_private_key(rng);
let (vm, _) = prepare_vm(rng).unwrap();
let program = Program::from_str(
r"
import credits.aleo;
program nested_call.aleo;
mapping data:
key as field.public;
value as field.public;
function test:
input r0 as u64.public;
call credits.aleo/transfer_public_as_signer nested_call.aleo r0 into r1;
async test r1 into r2;
output r2 as nested_call.aleo/test.future;
finalize test:
input r0 as credits.aleo/transfer_public_as_signer.future;
await r0;
get data[0field] into r1;",
)
.unwrap();
let transaction = vm.deploy(&private_key, &program, None, 0, None, rng).unwrap();
let next_block = crate::test_helpers::sample_next_block(&vm, &private_key, &[transaction], rng).unwrap();
vm.add_next_block(&next_block).unwrap();
let inputs = [Value::<CurrentNetwork>::from_str("1_000_000u64").unwrap()].into_iter();
let transaction =
vm.execute(&private_key, ("nested_call.aleo", "test"), inputs.clone(), None, 0, None, rng).unwrap();
assert_eq!(62_776, *transaction.base_fee_amount().unwrap());
let transactions: [Transaction<CurrentNetwork>; 0] = [];
for _ in 1..CurrentNetwork::CONSENSUS_V2_HEIGHT {
let next_block = crate::vm::test_helpers::sample_next_block(&vm, &private_key, &transactions, rng).unwrap();
vm.add_next_block(&next_block).unwrap();
}
let transaction =
vm.execute(&private_key, ("nested_call.aleo", "test"), inputs.clone(), None, 0, None, rng).unwrap();
assert_eq!(37_276, *transaction.base_fee_amount().unwrap());
}
#[test]
fn test_credits_bond_public_cost() {
let rng = &mut TestRng::default();
let credits_program = "credits.aleo";
let function_name = "bond_public";
let caller_private_key: PrivateKey<MainnetV0> = PrivateKey::new(rng).unwrap();
let validator_private_key: PrivateKey<MainnetV0> = PrivateKey::new(rng).unwrap();
let validator_address = Address::try_from(&validator_private_key).unwrap();
let withdrawal_address = Address::try_from(&caller_private_key).unwrap();
let bond_public_amount = 1_000_000u64;
let inputs =
&[validator_address.to_string(), withdrawal_address.to_string(), format!("{bond_public_amount}_u64")];
let (vm, _) = prepare_vm(rng).unwrap();
let authorization = vm.authorize(&caller_private_key, credits_program, function_name, inputs, rng).unwrap();
let execution = vm.execute_authorization_raw(authorization, None, rng).unwrap();
let (cost, _) = execution_cost_v1(&vm.process().read(), &execution).unwrap();
println!("Cost: {}", cost);
}
#[test]
fn test_unbond_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", "unbond_public"), inputs, None, 0, None, rng).unwrap();
assert_eq!(transaction.transitions().count(), 2);
assert!(transaction.transitions().take(1).next().unwrap().is_unbond_public());
let transaction_size_in_bytes = transaction.to_bytes_le().unwrap().len();
assert_eq!(2867, 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!(1416, execution_size_in_bytes, "Update me if serialization has changed");
}
}
#[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!(3693, 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!(2242, 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!(2871, 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!(1420, execution_size_in_bytes, "Update me if serialization has changed");
}
}
#[test]
fn test_transfer_public_as_signer_transaction_size() {
let rng = &mut TestRng::default();
let signer = crate::vm::test_helpers::sample_genesis_private_key(rng);
let address = Address::try_from(&signer).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(&signer, ("credits.aleo", "transfer_public_as_signer"), inputs, None, 0, None, rng).unwrap();
let transaction_size_in_bytes = transaction.to_bytes_le().unwrap().len();
assert_eq!(2891, 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!(1440, 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!(3538, 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!(2087, 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();
assert_eq!(transaction.transitions().count(), 1);
assert!(transaction.transitions().next().unwrap().is_split());
let transaction_size_in_bytes = transaction.to_bytes_le().unwrap().len();
assert_eq!(2166, 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!(2131, 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"),
};
assert!(fee.is_fee_private());
let fee_size_in_bytes = fee.to_bytes_le().unwrap().len();
assert_eq!(2043, 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"),
};
assert!(fee.is_fee_public());
let fee_size_in_bytes = fee.to_bytes_le().unwrap().len();
assert_eq!(1416, fee_size_in_bytes, "Update me if serialization has changed");
}
#[test]
fn test_wide_nested_execution_cost() {
let rng = &mut TestRng::default();
let caller_private_key = crate::vm::test_helpers::sample_genesis_private_key(rng);
let (vm, _) = prepare_vm(rng).unwrap();
let child_program = Program::from_str(
r"
program child.aleo;
mapping data:
key as field.public;
value as field.public;
function test:
input r0 as field.public;
input r1 as field.public;
async test r0 r1 into r2;
output r2 as child.aleo/test.future;
finalize test:
input r0 as field.public;
input r1 as field.public;
hash.bhp256 r0 into r2 as field;
hash.bhp256 r1 into r3 as field;
set r2 into data[r3];",
)
.unwrap();
let transaction = vm.deploy(&caller_private_key, &child_program, None, 0, None, rng).unwrap();
let next_block = crate::test_helpers::sample_next_block(&vm, &caller_private_key, &[transaction], rng).unwrap();
vm.add_next_block(&next_block).unwrap();
let parent_program = Program::from_str(
r"
import child.aleo;
program parent.aleo;
function test:
call child.aleo/test 0field 1field into r0;
call child.aleo/test 2field 3field into r1;
call child.aleo/test 4field 5field into r2;
call child.aleo/test 6field 7field into r3;
call child.aleo/test 8field 9field into r4;
call child.aleo/test 10field 11field into r5;
call child.aleo/test 12field 13field into r6;
call child.aleo/test 14field 15field into r7;
call child.aleo/test 16field 17field into r8;
call child.aleo/test 18field 19field into r9;
call child.aleo/test 20field 21field into r10;
call child.aleo/test 22field 23field into r11;
call child.aleo/test 24field 25field into r12;
call child.aleo/test 26field 27field into r13;
call child.aleo/test 28field 29field into r14;
call child.aleo/test 30field 31field into r15;
async test r0 r1 r2 r3 r4 r5 r6 r7 r8 r9 r10 r11 r12 r13 r14 r15 into r16;
output r16 as parent.aleo/test.future;
finalize test:
input r0 as child.aleo/test.future;
input r1 as child.aleo/test.future;
input r2 as child.aleo/test.future;
input r3 as child.aleo/test.future;
input r4 as child.aleo/test.future;
input r5 as child.aleo/test.future;
input r6 as child.aleo/test.future;
input r7 as child.aleo/test.future;
input r8 as child.aleo/test.future;
input r9 as child.aleo/test.future;
input r10 as child.aleo/test.future;
input r11 as child.aleo/test.future;
input r12 as child.aleo/test.future;
input r13 as child.aleo/test.future;
input r14 as child.aleo/test.future;
input r15 as child.aleo/test.future;
await r0;
await r1;
await r2;
await r3;
await r4;
await r5;
await r6;
await r7;
await r8;
await r9;
await r10;
await r11;
await r12;
await r13;
await r14;
await r15;",
)
.unwrap();
let transaction = vm.deploy(&caller_private_key, &parent_program, None, 0, None, rng).unwrap();
let next_block = crate::test_helpers::sample_next_block(&vm, &caller_private_key, &[transaction], rng).unwrap();
vm.add_next_block(&next_block).unwrap();
let Transaction::Execute(_, execution, _) = vm
.execute(&caller_private_key, ("parent.aleo", "test"), Vec::<Value<_>>::new().iter(), None, 0, None, rng)
.unwrap()
else {
unreachable!("VM::execute always produces an `Execution`")
};
assert_eq!(execution.transitions().len(), <CurrentNetwork as Network>::MAX_INPUTS + 1);
let (_, (_, finalize_cost)) = execution_cost_v2(&vm.process().read(), &execution).unwrap();
let mut expected_cost = 0;
for transition in execution.transitions() {
let program_id = transition.program_id();
let function_name = transition.function_name();
let stack = vm.process().read().get_stack(program_id).unwrap().clone();
let cost = match stack.get_function(function_name).unwrap().finalize_logic() {
None => 0,
Some(finalize_logic) => {
finalize_logic
.commands()
.iter()
.map(|command| cost_per_command(&stack, finalize_logic, command, ConsensusFeeVersion::V2))
.try_fold(0u64, |acc, res| {
res.and_then(|x| acc.checked_add(x).ok_or(anyhow!("Finalize cost overflowed")))
})
.unwrap()
}
};
expected_cost += cost;
}
assert_eq!(finalize_cost, expected_cost);
}
#[test]
#[ignore = "memory-intensive"]
fn test_deep_nested_execution_cost() {
let rng = &mut TestRng::default();
let caller_private_key = crate::vm::test_helpers::sample_genesis_private_key(rng);
let (vm, _) = prepare_vm(rng).unwrap();
let base_program = Program::from_str(
r"
program test_1.aleo;
mapping data:
key as field.public;
value as field.public;
function test:
input r0 as field.public;
input r1 as field.public;
async test r0 r1 into r2;
output r2 as test_1.aleo/test.future;
finalize test:
input r0 as field.public;
input r1 as field.public;
hash.bhp256 r0 into r2 as field;
hash.bhp256 r1 into r3 as field;
set r2 into data[r3];",
)
.unwrap();
let transaction = vm.deploy(&caller_private_key, &base_program, None, 0, None, rng).unwrap();
let next_block = crate::test_helpers::sample_next_block(&vm, &caller_private_key, &[transaction], rng).unwrap();
vm.add_next_block(&next_block).unwrap();
for i in 2..=Transaction::<CurrentNetwork>::MAX_TRANSITIONS - 1 {
let program = Program::from_str(&format!(
r"
{imports}
program test_{curr}.aleo;
mapping data:
key as field.public;
value as field.public;
function test:
input r0 as field.public;
input r1 as field.public;
call test_{prev}.aleo/test r0 r1 into r2;
async test r0 r1 r2 into r3;
output r3 as test_{curr}.aleo/test.future;
finalize test:
input r0 as field.public;
input r1 as field.public;
input r2 as test_{prev}.aleo/test.future;
await r2;
hash.bhp256 r0 into r3 as field;
hash.bhp256 r1 into r4 as field;
set r3 into data[r4];",
imports = (1..i).map(|j| format!("import test_{j}.aleo;")).join("\n"),
prev = i - 1,
curr = i,
))
.unwrap();
let transaction = vm.deploy(&caller_private_key, &program, None, 0, None, rng).unwrap();
let next_block =
crate::test_helpers::sample_next_block(&vm, &caller_private_key, &[transaction], rng).unwrap();
vm.add_next_block(&next_block).unwrap();
}
let Transaction::Execute(_, execution, _) = vm
.execute(
&caller_private_key,
(format!("test_{}.aleo", Transaction::<CurrentNetwork>::MAX_TRANSITIONS - 1), "test"),
vec![Value::from_str("0field").unwrap(), Value::from_str("1field").unwrap()].iter(),
None,
0,
None,
rng,
)
.unwrap()
else {
unreachable!("VM::execute always produces an `Execution`")
};
assert_eq!(execution.transitions().len(), Transaction::<CurrentNetwork>::MAX_TRANSITIONS - 1);
let (_, (_, finalize_cost)) = execution_cost_v2(&vm.process().read(), &execution).unwrap();
let mut expected_cost = 0;
for transition in execution.transitions() {
let program_id = transition.program_id();
let function_name = transition.function_name();
let stack = vm.process().read().get_stack(program_id).unwrap().clone();
let cost = match stack.get_function(function_name).unwrap().finalize_logic() {
None => 0,
Some(finalize_logic) => {
finalize_logic
.commands()
.iter()
.map(|command| cost_per_command(&stack, finalize_logic, command, ConsensusFeeVersion::V2))
.try_fold(0u64, |acc, res| {
res.and_then(|x| acc.checked_add(x).ok_or(anyhow!("Finalize cost overflowed")))
})
.unwrap()
}
};
expected_cost += cost;
}
assert_eq!(finalize_cost, expected_cost);
}
}