1mod helpers;
16pub use helpers::*;
17
18mod authorize;
19mod deploy;
20mod execute;
21mod finalize;
22mod verify;
23
24use crate::{cast_mut_ref, cast_ref, process};
25use console::{
26 account::{Address, PrivateKey},
27 network::prelude::*,
28 program::{Identifier, Literal, Locator, Plaintext, ProgramID, ProgramOwner, Record, Value},
29 types::{Field, U64},
30};
31use ledger_block::{
32 Block,
33 ConfirmedTransaction,
34 Deployment,
35 Execution,
36 Fee,
37 Header,
38 Ratifications,
39 Ratify,
40 Rejected,
41 Transaction,
42 Transactions,
43};
44use ledger_coinbase::CoinbaseSolution;
45use ledger_committee::Committee;
46use ledger_query::Query;
47use ledger_store::{
48 atomic_finalize,
49 BlockStore,
50 ConsensusStorage,
51 ConsensusStore,
52 FinalizeMode,
53 FinalizeStore,
54 TransactionStorage,
55 TransactionStore,
56 TransitionStore,
57};
58use synthesizer_process::{Authorization, Process, Trace};
59use synthesizer_program::{FinalizeGlobalState, FinalizeOperation, FinalizeStoreTrait, Program};
60
61use aleo_std::prelude::{finish, lap, timer};
62use indexmap::{IndexMap, IndexSet};
63use parking_lot::{Mutex, RwLock};
64use std::sync::Arc;
65
66#[cfg(not(feature = "serial"))]
67use rayon::prelude::*;
68
69#[derive(Clone)]
70pub struct VM<N: Network, C: ConsensusStorage<N>> {
71 process: Arc<RwLock<Process<N>>>,
73 store: ConsensusStore<N, C>,
75 atomic_lock: Arc<Mutex<()>>,
77 block_lock: Arc<Mutex<()>>,
79}
80
81impl<N: Network, C: ConsensusStorage<N>> VM<N, C> {
82 #[inline]
84 pub fn from(store: ConsensusStore<N, C>) -> Result<Self> {
85 let mut process = Process::load()?;
87
88 let credits = Program::<N>::credits()?;
90 for mapping in credits.mappings().values() {
91 if !store.finalize_store().contains_mapping_confirmed(credits.id(), mapping.name())? {
93 store.finalize_store().initialize_mapping(*credits.id(), *mapping.name())?;
95 }
96 }
97
98 fn load_deployment_and_imports<N: Network, T: TransactionStorage<N>>(
100 process: &Process<N>,
101 transaction_store: &TransactionStore<N, T>,
102 transaction_id: N::TransactionID,
103 ) -> Result<Vec<(ProgramID<N>, Deployment<N>)>> {
104 let deployment = match transaction_store.get_deployment(&transaction_id)? {
106 Some(deployment) => deployment,
107 None => bail!("Deployment transaction '{transaction_id}' is not found in storage."),
108 };
109
110 let program = deployment.program();
112 let program_id = program.id();
113
114 if process.contains_program(program_id) {
116 return Ok(vec![]);
117 }
118
119 let mut deployments = vec![];
121
122 for import_program_id in program.imports().keys() {
124 if !process.contains_program(import_program_id) {
126 let Some(transaction_id) =
128 transaction_store.deployment_store().find_transaction_id_from_program_id(import_program_id)?
129 else {
130 bail!("Transaction id for '{program_id}' is not found in storage.");
131 };
132
133 deployments.extend_from_slice(&load_deployment_and_imports(
135 process,
136 transaction_store,
137 transaction_id,
138 )?);
139 }
140 }
141
142 deployments.push((*program_id, deployment));
144
145 Ok(deployments)
146 }
147
148 let transaction_store = store.transaction_store();
150 let deployment_ids = transaction_store.deployment_transaction_ids().collect::<Vec<_>>();
152 for (i, chunk) in deployment_ids.chunks(256).enumerate() {
154 debug!(
155 "Loading deployments {}-{} (of {})...",
156 i * 256,
157 ((i + 1) * 256).min(deployment_ids.len()),
158 deployment_ids.len()
159 );
160 let deployments = cfg_iter!(chunk)
161 .map(|transaction_id| {
162 load_deployment_and_imports(&process, transaction_store, **transaction_id)
164 })
165 .collect::<Result<Vec<_>>>()?;
166
167 for (program_id, deployment) in deployments.iter().flatten() {
168 if !process.contains_program(program_id) {
170 process.load_deployment(deployment)?;
171 }
172 }
173 }
174
175 Ok(Self {
177 process: Arc::new(RwLock::new(process)),
178 store,
179 atomic_lock: Arc::new(Mutex::new(())),
180 block_lock: Arc::new(Mutex::new(())),
181 })
182 }
183
184 #[inline]
186 pub fn contains_program(&self, program_id: &ProgramID<N>) -> bool {
187 self.process.read().contains_program(program_id)
188 }
189
190 #[inline]
192 pub fn process(&self) -> Arc<RwLock<Process<N>>> {
193 self.process.clone()
194 }
195}
196
197impl<N: Network, C: ConsensusStorage<N>> VM<N, C> {
198 #[inline]
200 pub fn finalize_store(&self) -> &FinalizeStore<N, C::FinalizeStorage> {
201 self.store.finalize_store()
202 }
203
204 #[inline]
206 pub fn block_store(&self) -> &BlockStore<N, C::BlockStorage> {
207 self.store.block_store()
208 }
209
210 #[inline]
212 pub fn transaction_store(&self) -> &TransactionStore<N, C::TransactionStorage> {
213 self.store.transaction_store()
214 }
215
216 #[inline]
218 pub fn transition_store(&self) -> &TransitionStore<N, C::TransitionStorage> {
219 self.store.transition_store()
220 }
221}
222
223impl<N: Network, C: ConsensusStorage<N>> VM<N, C> {
224 pub fn genesis_beacon<R: Rng + CryptoRng>(&self, private_key: &PrivateKey<N>, rng: &mut R) -> Result<Block<N>> {
226 let private_keys = [*private_key, PrivateKey::new(rng)?, PrivateKey::new(rng)?, PrivateKey::new(rng)?];
227
228 let members = indexmap::indexmap! {
230 Address::try_from(private_keys[0])? => (ledger_committee::MIN_VALIDATOR_STAKE, true),
231 Address::try_from(private_keys[1])? => (ledger_committee::MIN_VALIDATOR_STAKE, true),
232 Address::try_from(private_keys[2])? => (ledger_committee::MIN_VALIDATOR_STAKE, true),
233 Address::try_from(private_keys[3])? => (ledger_committee::MIN_VALIDATOR_STAKE, true),
234 };
235 let committee = Committee::<N>::new_genesis(members)?;
237
238 let remaining_supply = N::STARTING_SUPPLY - (ledger_committee::MIN_VALIDATOR_STAKE * 4);
240 let public_balances = indexmap::indexmap! {
242 Address::try_from(private_keys[0])? => remaining_supply / 4,
243 Address::try_from(private_keys[1])? => remaining_supply / 4,
244 Address::try_from(private_keys[2])? => remaining_supply / 4,
245 Address::try_from(private_keys[3])? => remaining_supply / 4,
246 };
247 self.genesis_quorum(private_key, committee, public_balances, rng)
249 }
250
251 pub fn genesis_quorum<R: Rng + CryptoRng>(
253 &self,
254 private_key: &PrivateKey<N>,
255 committee: Committee<N>,
256 public_balances: IndexMap<Address<N>, u64>,
257 rng: &mut R,
258 ) -> Result<Block<N>> {
259 let total_stake = committee.total_stake();
261 let account_supply = public_balances.values().fold(0u64, |acc, x| acc.saturating_add(*x));
263 let total_supply = total_stake.checked_add(account_supply).ok_or_else(|| anyhow!("Invalid total supply"))?;
265 ensure!(total_supply == N::STARTING_SUPPLY, "Invalid total supply");
267
268 let caller = Address::try_from(private_key)?;
270 let locator = ("credits.aleo", "transfer_public_to_private");
272 let amount = ledger_committee::MIN_VALIDATOR_STAKE;
274 let inputs = [caller.to_string(), format!("{amount}_u64")];
276
277 let ratifications = vec![Ratify::Genesis(committee, public_balances)];
279 let solutions = None; let transactions = (0..Block::<N>::NUM_GENESIS_TRANSACTIONS)
283 .map(|_| self.execute(private_key, locator, inputs.iter(), None, 0, None, rng))
284 .collect::<Result<Vec<_>, _>>()?;
285
286 let state = FinalizeGlobalState::new_genesis::<N>()?;
288 let (ratifications, transactions, aborted_transaction_ids, ratified_finalize_operations) =
290 self.speculate(state, None, ratifications, solutions.as_ref(), transactions.iter())?;
291 ensure!(
292 aborted_transaction_ids.is_empty(),
293 "Failed to initialize a genesis block - found aborted transaction IDs"
294 );
295
296 let header = Header::genesis(&ratifications, &transactions, ratified_finalize_operations)?;
298 let previous_hash = N::BlockHash::default();
300
301 let block = Block::new_beacon(
303 private_key,
304 previous_hash,
305 header,
306 ratifications,
307 solutions,
308 transactions,
309 aborted_transaction_ids,
310 rng,
311 )?;
312 match block.is_genesis() {
314 true => Ok(block),
315 false => bail!("Failed to initialize a genesis block"),
316 }
317 }
318
319 #[inline]
321 pub fn add_next_block(&self, block: &Block<N>) -> Result<()> {
322 let _block_lock = self.block_lock.lock();
325
326 let state = FinalizeGlobalState::new::<N>(
328 block.round(),
329 block.height(),
330 block.cumulative_weight(),
331 block.cumulative_proof_target(),
332 block.previous_hash(),
333 )?;
334
335 self.block_store().insert(block)?;
340 match self.finalize(state, block.ratifications(), block.solutions(), block.transactions()) {
342 Ok(_ratified_finalize_operations) => Ok(()),
343 Err(finalize_error) => {
344 self.block_store().remove_last_n(1).map_err(|removal_error| {
346 error!("Failed to finalize block {} - {finalize_error}", block.height());
348 removal_error
350 })?;
351 Err(finalize_error)
353 }
354 }
355 }
356}
357
358#[cfg(test)]
359pub(crate) mod test_helpers {
360 use super::*;
361 use console::{
362 account::{Address, ViewKey},
363 network::Testnet3,
364 program::Value,
365 types::Field,
366 };
367 use ledger_block::{Block, Header, Metadata, Transition};
368 use ledger_store::helpers::memory::ConsensusMemory;
369 use synthesizer_program::Program;
370
371 use indexmap::IndexMap;
372 use once_cell::sync::OnceCell;
373 use std::borrow::Borrow;
374
375 pub(crate) type CurrentNetwork = Testnet3;
376
377 pub(crate) fn sample_finalize_state(block_height: u32) -> FinalizeGlobalState {
379 FinalizeGlobalState::from(block_height as u64, block_height, [0u8; 32])
380 }
381
382 pub(crate) fn sample_vm() -> VM<CurrentNetwork, ConsensusMemory<CurrentNetwork>> {
383 VM::from(ConsensusStore::open(None).unwrap()).unwrap()
385 }
386
387 pub(crate) fn sample_genesis_private_key(rng: &mut TestRng) -> PrivateKey<CurrentNetwork> {
388 static INSTANCE: OnceCell<PrivateKey<CurrentNetwork>> = OnceCell::new();
389 *INSTANCE.get_or_init(|| {
390 PrivateKey::<CurrentNetwork>::new(rng).unwrap()
392 })
393 }
394
395 pub(crate) fn sample_genesis_block(rng: &mut TestRng) -> Block<CurrentNetwork> {
396 static INSTANCE: OnceCell<Block<CurrentNetwork>> = OnceCell::new();
397 INSTANCE
398 .get_or_init(|| {
399 let vm = crate::vm::test_helpers::sample_vm();
401 let caller_private_key = crate::vm::test_helpers::sample_genesis_private_key(rng);
403 vm.genesis_beacon(&caller_private_key, rng).unwrap()
405 })
406 .clone()
407 }
408
409 pub(crate) fn sample_vm_with_genesis_block(
410 rng: &mut TestRng,
411 ) -> VM<CurrentNetwork, ConsensusMemory<CurrentNetwork>> {
412 let vm = crate::vm::test_helpers::sample_vm();
414 let genesis = crate::vm::test_helpers::sample_genesis_block(rng);
416 vm.add_next_block(&genesis).unwrap();
418 vm
420 }
421
422 pub(crate) fn sample_program() -> Program<CurrentNetwork> {
423 static INSTANCE: OnceCell<Program<CurrentNetwork>> = OnceCell::new();
424 INSTANCE
425 .get_or_init(|| {
426 Program::<CurrentNetwork>::from_str(
428 r"
429program testing.aleo;
430
431struct message:
432 amount as u128;
433
434mapping account:
435 key as address.public;
436 value as u64.public;
437
438record token:
439 owner as address.private;
440 amount as u64.private;
441
442function initialize:
443 input r0 as address.private;
444 input r1 as u64.private;
445 cast r0 r1 into r2 as token.record;
446 output r2 as token.record;
447
448function compute:
449 input r0 as message.private;
450 input r1 as message.public;
451 input r2 as message.private;
452 input r3 as token.record;
453 add r0.amount r1.amount into r4;
454 cast r3.owner r3.amount into r5 as token.record;
455 output r4 as u128.public;
456 output r5 as token.record;",
457 )
458 .unwrap()
459 })
460 .clone()
461 }
462
463 pub(crate) fn sample_deployment_transaction(rng: &mut TestRng) -> Transaction<CurrentNetwork> {
464 static INSTANCE: OnceCell<Transaction<CurrentNetwork>> = OnceCell::new();
465 INSTANCE
466 .get_or_init(|| {
467 let program = sample_program();
469
470 let caller_private_key = crate::vm::test_helpers::sample_genesis_private_key(rng);
472 let caller_view_key = ViewKey::try_from(&caller_private_key).unwrap();
473
474 let genesis = crate::vm::test_helpers::sample_genesis_block(rng);
476
477 let records =
479 genesis.transitions().cloned().flat_map(Transition::into_records).collect::<IndexMap<_, _>>();
480 trace!("Unspent Records:\n{:#?}", records);
481
482 let credits = Some(records.values().next().unwrap().decrypt(&caller_view_key).unwrap());
484
485 let vm = sample_vm();
487 vm.add_next_block(&genesis).unwrap();
489
490 let transaction = vm.deploy(&caller_private_key, &program, credits, 10, None, rng).unwrap();
492 vm.check_transaction(&transaction, None, rng).unwrap();
494 transaction
496 })
497 .clone()
498 }
499
500 pub(crate) fn sample_execution_transaction_without_fee(rng: &mut TestRng) -> Transaction<CurrentNetwork> {
501 static INSTANCE: OnceCell<Transaction<CurrentNetwork>> = OnceCell::new();
502 INSTANCE
503 .get_or_init(|| {
504 let caller_private_key = crate::vm::test_helpers::sample_genesis_private_key(rng);
506 let caller_view_key = ViewKey::try_from(&caller_private_key).unwrap();
507
508 let genesis = crate::vm::test_helpers::sample_genesis_block(rng);
510
511 let records =
513 genesis.transitions().cloned().flat_map(Transition::into_records).collect::<IndexMap<_, _>>();
514 trace!("Unspent Records:\n{:#?}", records);
515
516 let record = records.values().next().unwrap().decrypt(&caller_view_key).unwrap();
518
519 let vm = sample_vm();
521 vm.add_next_block(&genesis).unwrap();
523
524 let inputs =
526 [Value::<CurrentNetwork>::Record(record), Value::<CurrentNetwork>::from_str("1u64").unwrap()]
527 .into_iter();
528
529 let authorization = vm.authorize(&caller_private_key, "credits.aleo", "split", inputs, rng).unwrap();
531 assert_eq!(authorization.len(), 1);
532
533 let transaction = vm.execute_authorization(authorization, None, None, rng).unwrap();
535 vm.check_transaction(&transaction, None, rng).unwrap();
537 transaction
539 })
540 .clone()
541 }
542
543 pub(crate) fn sample_execution_transaction_with_private_fee(rng: &mut TestRng) -> Transaction<CurrentNetwork> {
544 static INSTANCE: OnceCell<Transaction<CurrentNetwork>> = OnceCell::new();
545 INSTANCE
546 .get_or_init(|| {
547 let caller_private_key = crate::vm::test_helpers::sample_genesis_private_key(rng);
549 let caller_view_key = ViewKey::try_from(&caller_private_key).unwrap();
550 let address = Address::try_from(&caller_private_key).unwrap();
551
552 let genesis = crate::vm::test_helpers::sample_genesis_block(rng);
554
555 let records =
557 genesis.transitions().cloned().flat_map(Transition::into_records).collect::<IndexMap<_, _>>();
558 trace!("Unspent Records:\n{:#?}", records);
559
560 let record = Some(records.values().next().unwrap().decrypt(&caller_view_key).unwrap());
562
563 let vm = sample_vm();
565 vm.add_next_block(&genesis).unwrap();
567
568 let inputs = [
570 Value::<CurrentNetwork>::from_str(&address.to_string()).unwrap(),
571 Value::<CurrentNetwork>::from_str("1u64").unwrap(),
572 ]
573 .into_iter();
574
575 let transaction = vm
577 .execute(&caller_private_key, ("credits.aleo", "transfer_public"), inputs, record, 0, None, rng)
578 .unwrap();
579 vm.check_transaction(&transaction, None, rng).unwrap();
581 transaction
583 })
584 .clone()
585 }
586
587 pub(crate) fn sample_execution_transaction_with_public_fee(rng: &mut TestRng) -> Transaction<CurrentNetwork> {
588 static INSTANCE: OnceCell<Transaction<CurrentNetwork>> = OnceCell::new();
589 INSTANCE
590 .get_or_init(|| {
591 let caller_private_key = crate::vm::test_helpers::sample_genesis_private_key(rng);
593 let address = Address::try_from(&caller_private_key).unwrap();
594
595 let genesis = crate::vm::test_helpers::sample_genesis_block(rng);
597
598 let vm = sample_vm();
600 vm.add_next_block(&genesis).unwrap();
602
603 let inputs = [
605 Value::<CurrentNetwork>::from_str(&address.to_string()).unwrap(),
606 Value::<CurrentNetwork>::from_str("1u64").unwrap(),
607 ]
608 .into_iter();
609
610 let transaction_without_fee = vm
612 .execute(&caller_private_key, ("credits.aleo", "transfer_public"), inputs, None, 0, None, rng)
613 .unwrap();
614 let execution = transaction_without_fee.execution().unwrap().clone();
615
616 let authorization = vm
618 .authorize_fee_public(
619 &caller_private_key,
620 10_000_000,
621 100,
622 execution.to_execution_id().unwrap(),
623 rng,
624 )
625 .unwrap();
626 let fee = vm.execute_fee_authorization(authorization, None, rng).unwrap();
628
629 let transaction = Transaction::from_execution(execution, Some(fee)).unwrap();
631 vm.check_transaction(&transaction, None, rng).unwrap();
633 transaction
635 })
636 .clone()
637 }
638
639 pub fn sample_next_block<R: Rng + CryptoRng>(
640 vm: &VM<Testnet3, ConsensusMemory<Testnet3>>,
641 private_key: &PrivateKey<Testnet3>,
642 transactions: &[Transaction<Testnet3>],
643 rng: &mut R,
644 ) -> Result<Block<Testnet3>> {
645 let block_hash =
647 vm.block_store().get_block_hash(*vm.block_store().heights().max().unwrap().borrow()).unwrap().unwrap();
648 let previous_block = vm.block_store().get_block(&block_hash).unwrap().unwrap();
649
650 let (ratifications, transactions, aborted_transaction_ids, ratified_finalize_operations) =
652 vm.speculate(sample_finalize_state(1), None, vec![], None, transactions.iter())?;
653 assert!(aborted_transaction_ids.is_empty());
654
655 let metadata = Metadata::new(
657 Testnet3::ID,
658 previous_block.round() + 1,
659 previous_block.height() + 1,
660 0,
661 0,
662 Testnet3::GENESIS_COINBASE_TARGET,
663 Testnet3::GENESIS_PROOF_TARGET,
664 previous_block.last_coinbase_target(),
665 previous_block.last_coinbase_timestamp(),
666 Testnet3::GENESIS_TIMESTAMP + 1,
667 )?;
668
669 let header = Header::from(
670 vm.block_store().current_state_root(),
671 transactions.to_transactions_root().unwrap(),
672 transactions.to_finalize_root(ratified_finalize_operations).unwrap(),
673 ratifications.to_ratifications_root().unwrap(),
674 Field::zero(),
675 Field::zero(),
676 metadata,
677 )?;
678
679 Block::new_beacon(
681 private_key,
682 previous_block.hash(),
683 header,
684 ratifications,
685 None,
686 transactions,
687 aborted_transaction_ids,
688 rng,
689 )
690 }
691
692 #[test]
693 fn test_multiple_deployments_and_multiple_executions() {
694 let rng = &mut TestRng::default();
695
696 let caller_private_key = crate::vm::test_helpers::sample_genesis_private_key(rng);
698 let caller_view_key = ViewKey::try_from(&caller_private_key).unwrap();
699
700 let genesis = crate::vm::test_helpers::sample_genesis_block(rng);
702
703 let records = genesis.transitions().cloned().flat_map(Transition::into_records).collect::<IndexMap<_, _>>();
705 trace!("Unspent Records:\n{:#?}", records);
706
707 let record = records.values().next().unwrap().decrypt(&caller_view_key).unwrap();
709
710 let vm = sample_vm();
712 vm.add_next_block(&genesis).unwrap();
714
715 let transaction = vm
717 .execute(
718 &caller_private_key,
719 ("credits.aleo", "split"),
720 [Value::Record(record), Value::from_str("1000000000u64").unwrap()].iter(), None,
722 0,
723 None,
724 rng,
725 )
726 .unwrap();
727 let records = transaction.records().collect_vec();
728 let first_record = records[0].1.clone().decrypt(&caller_view_key).unwrap();
729 let second_record = records[1].1.clone().decrypt(&caller_view_key).unwrap();
730 let block = sample_next_block(&vm, &caller_private_key, &[transaction], rng).unwrap();
731 vm.add_next_block(&block).unwrap();
732
733 let mut transactions = Vec::new();
735 let transaction = vm
736 .execute(
737 &caller_private_key,
738 ("credits.aleo", "split"),
739 [Value::Record(first_record), Value::from_str("100000000u64").unwrap()].iter(), None,
741 0,
742 None,
743 rng,
744 )
745 .unwrap();
746 let records = transaction.records().collect_vec();
747 let first_record = records[0].1.clone().decrypt(&caller_view_key).unwrap();
748 let third_record = records[1].1.clone().decrypt(&caller_view_key).unwrap();
749 transactions.push(transaction);
750 let transaction = vm
752 .execute(
753 &caller_private_key,
754 ("credits.aleo", "split"),
755 [Value::Record(second_record), Value::from_str("100000000u64").unwrap()].iter(), None,
757 0,
758 None,
759 rng,
760 )
761 .unwrap();
762 let records = transaction.records().collect_vec();
763 let second_record = records[0].1.clone().decrypt(&caller_view_key).unwrap();
764 let fourth_record = records[1].1.clone().decrypt(&caller_view_key).unwrap();
765 transactions.push(transaction);
766 let fee_block = sample_next_block(&vm, &caller_private_key, &transactions, rng).unwrap();
768 vm.add_next_block(&fee_block).unwrap();
769
770 let first_program = r"
772program test_program_1.aleo;
773mapping map_0:
774 key as field.public;
775 value as field.public;
776function init:
777 async init into r0;
778 output r0 as test_program_1.aleo/init.future;
779finalize init:
780 set 0field into map_0[0field];
781function getter:
782 async getter into r0;
783 output r0 as test_program_1.aleo/getter.future;
784finalize getter:
785 get map_0[0field] into r0;
786 ";
787 let second_program = r"
788program test_program_2.aleo;
789mapping map_0:
790 key as field.public;
791 value as field.public;
792function init:
793 async init into r0;
794 output r0 as test_program_2.aleo/init.future;
795finalize init:
796 set 0field into map_0[0field];
797function getter:
798 async getter into r0;
799 output r0 as test_program_2.aleo/getter.future;
800finalize getter:
801 get map_0[0field] into r0;
802 ";
803 let first_deployment = vm
804 .deploy(&caller_private_key, &Program::from_str(first_program).unwrap(), Some(first_record), 1, None, rng)
805 .unwrap();
806 let second_deployment = vm
807 .deploy(&caller_private_key, &Program::from_str(second_program).unwrap(), Some(second_record), 1, None, rng)
808 .unwrap();
809 let deployment_block =
810 sample_next_block(&vm, &caller_private_key, &[first_deployment, second_deployment], rng).unwrap();
811 vm.add_next_block(&deployment_block).unwrap();
812
813 let first_execution = vm
815 .execute(
816 &caller_private_key,
817 ("test_program_1.aleo", "init"),
818 Vec::<Value<Testnet3>>::new().iter(),
819 Some(third_record),
820 1,
821 None,
822 rng,
823 )
824 .unwrap();
825 let second_execution = vm
826 .execute(
827 &caller_private_key,
828 ("test_program_2.aleo", "init"),
829 Vec::<Value<Testnet3>>::new().iter(),
830 Some(fourth_record),
831 1,
832 None,
833 rng,
834 )
835 .unwrap();
836 let execution_block =
837 sample_next_block(&vm, &caller_private_key, &[first_execution, second_execution], rng).unwrap();
838 vm.add_next_block(&execution_block).unwrap();
839 }
840
841 #[test]
842 fn test_load_deployments_with_imports() {
843 let rng = &mut TestRng::fixed(123456789);
845
846 let caller_private_key = PrivateKey::<CurrentNetwork>::new(rng).unwrap();
848 let caller_view_key = ViewKey::try_from(&caller_private_key).unwrap();
849
850 let vm = crate::vm::test_helpers::sample_vm();
852 let genesis = vm.genesis_beacon(&caller_private_key, rng).unwrap();
854 vm.add_next_block(&genesis).unwrap();
856
857 let records = genesis.transitions().cloned().flat_map(Transition::into_records).collect::<Vec<(_, _)>>();
859 trace!("Unspent Records:\n{:#?}", records);
860 let record_0 = records[0].1.decrypt(&caller_view_key).unwrap();
861 let record_1 = records[1].1.decrypt(&caller_view_key).unwrap();
862 let record_2 = records[2].1.decrypt(&caller_view_key).unwrap();
863 let record_3 = records[3].1.decrypt(&caller_view_key).unwrap();
864
865 let program_1 = r"
867program first_program.aleo;
868
869function c:
870 input r0 as u8.private;
871 input r1 as u8.private;
872 add r0 r1 into r2;
873 output r2 as u8.private;
874 ";
875 let deployment_1 = vm
876 .deploy(&caller_private_key, &Program::from_str(program_1).unwrap(), Some(record_0), 0, None, rng)
877 .unwrap();
878
879 let deployment_block = sample_next_block(&vm, &caller_private_key, &[deployment_1.clone()], rng).unwrap();
881 vm.add_next_block(&deployment_block).unwrap();
882
883 let program_2 = r"
885import first_program.aleo;
886
887program second_program.aleo;
888
889function b:
890 input r0 as u8.private;
891 input r1 as u8.private;
892 call first_program.aleo/c r0 r1 into r2;
893 output r2 as u8.private;
894 ";
895 let deployment_2 = vm
896 .deploy(&caller_private_key, &Program::from_str(program_2).unwrap(), Some(record_1), 0, None, rng)
897 .unwrap();
898
899 let deployment_block = sample_next_block(&vm, &caller_private_key, &[deployment_2.clone()], rng).unwrap();
901 vm.add_next_block(&deployment_block).unwrap();
902
903 let program_3 = r"
905import second_program.aleo;
906
907program third_program.aleo;
908
909function a:
910 input r0 as u8.private;
911 input r1 as u8.private;
912 call second_program.aleo/b r0 r1 into r2;
913 output r2 as u8.private;
914 ";
915 let deployment_3 = vm
916 .deploy(&caller_private_key, &Program::from_str(program_3).unwrap(), Some(record_2), 0, None, rng)
917 .unwrap();
918
919 let program_4 = r"
921import second_program.aleo;
922import first_program.aleo;
923
924program fourth_program.aleo;
925
926function a:
927 input r0 as u8.private;
928 input r1 as u8.private;
929 call second_program.aleo/b r0 r1 into r2;
930 output r2 as u8.private;
931 ";
932 let deployment_4 = vm
933 .deploy(&caller_private_key, &Program::from_str(program_4).unwrap(), Some(record_3), 0, None, rng)
934 .unwrap();
935
936 let deployment_block =
938 sample_next_block(&vm, &caller_private_key, &[deployment_3.clone(), deployment_4.clone()], rng).unwrap();
939 vm.add_next_block(&deployment_block).unwrap();
940
941 let deployment_transaction_ids =
943 vm.transaction_store().deployment_transaction_ids().map(|id| *id).collect::<Vec<_>>();
944 assert_ne!(deployment_transaction_ids, vec![
947 deployment_1.id(),
948 deployment_2.id(),
949 deployment_3.id(),
950 deployment_4.id()
951 ]);
952
953 assert!(VM::from(vm.store.clone()).is_ok());
955 }
956
957 #[test]
958 fn test_multiple_external_calls() {
959 let rng = &mut TestRng::default();
960
961 let caller_private_key = crate::vm::test_helpers::sample_genesis_private_key(rng);
963 let caller_view_key = ViewKey::try_from(&caller_private_key).unwrap();
964 let address = Address::try_from(&caller_private_key).unwrap();
965
966 let genesis = crate::vm::test_helpers::sample_genesis_block(rng);
968
969 let records =
971 genesis.transitions().cloned().flat_map(Transition::into_records).take(3).collect::<IndexMap<_, _>>();
972 trace!("Unspent Records:\n{:#?}", records);
973 let record_0 = records.values().next().unwrap().decrypt(&caller_view_key).unwrap();
974 let record_1 = records.values().nth(1).unwrap().decrypt(&caller_view_key).unwrap();
975 let record_2 = records.values().nth(2).unwrap().decrypt(&caller_view_key).unwrap();
976
977 let vm = sample_vm();
979 vm.add_next_block(&genesis).unwrap();
981
982 let program = Program::from_str(
984 r"
985import credits.aleo;
986
987program test_multiple_external_calls.aleo;
988
989function multitransfer:
990 input r0 as credits.aleo/credits.record;
991 input r1 as address.private;
992 input r2 as u64.private;
993 call credits.aleo/transfer_private r0 r1 r2 into r3 r4;
994 call credits.aleo/transfer_private r4 r1 r2 into r5 r6;
995 output r4 as credits.aleo/credits.record;
996 output r5 as credits.aleo/credits.record;
997 output r6 as credits.aleo/credits.record;
998 ",
999 )
1000 .unwrap();
1001 let deployment = vm.deploy(&caller_private_key, &program, Some(record_0), 1, None, rng).unwrap();
1002 vm.add_next_block(&sample_next_block(&vm, &caller_private_key, &[deployment], rng).unwrap()).unwrap();
1003
1004 let inputs = [
1006 Value::<Testnet3>::Record(record_1),
1007 Value::<Testnet3>::from_str(&address.to_string()).unwrap(),
1008 Value::<Testnet3>::from_str("10u64").unwrap(),
1009 ];
1010 let execution = vm
1011 .execute(
1012 &caller_private_key,
1013 ("test_multiple_external_calls.aleo", "multitransfer"),
1014 inputs.into_iter(),
1015 Some(record_2),
1016 1,
1017 None,
1018 rng,
1019 )
1020 .unwrap();
1021 vm.add_next_block(&sample_next_block(&vm, &caller_private_key, &[execution], rng).unwrap()).unwrap();
1022 }
1023
1024 #[test]
1025 fn test_nested_deployment_with_assert() {
1026 let rng = &mut TestRng::default();
1027
1028 let private_key = sample_genesis_private_key(rng);
1030
1031 let genesis = sample_genesis_block(rng);
1033
1034 let vm = sample_vm();
1036 vm.add_next_block(&genesis).unwrap();
1038
1039 let program = Program::from_str(
1041 r"
1042program child_program.aleo;
1043
1044function check:
1045 input r0 as field.private;
1046 assert.eq r0 123456789123456789123456789123456789123456789123456789field;
1047 ",
1048 )
1049 .unwrap();
1050
1051 let deployment = vm.deploy(&private_key, &program, None, 0, None, rng).unwrap();
1052 assert!(vm.check_transaction(&deployment, None, rng).is_ok());
1053 vm.add_next_block(&sample_next_block(&vm, &private_key, &[deployment], rng).unwrap()).unwrap();
1054
1055 assert!(vm.contains_program(&ProgramID::from_str("child_program.aleo").unwrap()));
1057
1058 let program = Program::from_str(
1060 r"
1061import child_program.aleo;
1062
1063program parent_program.aleo;
1064
1065function check:
1066 input r0 as field.private;
1067 call child_program.aleo/check r0;
1068 ",
1069 )
1070 .unwrap();
1071
1072 let deployment = vm.deploy(&private_key, &program, None, 0, None, rng).unwrap();
1073 assert!(vm.check_transaction(&deployment, None, rng).is_ok());
1074 vm.add_next_block(&sample_next_block(&vm, &private_key, &[deployment], rng).unwrap()).unwrap();
1075
1076 assert!(vm.contains_program(&ProgramID::from_str("parent_program.aleo").unwrap()));
1078 }
1079
1080 #[test]
1081 #[ignore]
1082 fn test_deployment_memory_overload() {
1083 const NUM_DEPLOYMENTS: usize = 32;
1084
1085 let rng = &mut TestRng::default();
1086
1087 let private_key = sample_genesis_private_key(rng);
1089
1090 let view_key = ViewKey::try_from(&private_key).unwrap();
1092
1093 let genesis = sample_genesis_block(rng);
1095
1096 let vm = sample_vm();
1098 vm.add_next_block(&genesis).unwrap();
1100
1101 let program = Program::from_str(
1103 r"
1104program program_layer_0.aleo;
1105
1106mapping m:
1107 key as u8.public;
1108 value as u32.public;
1109
1110function do:
1111 input r0 as u32.public;
1112 async do r0 into r1;
1113 output r1 as program_layer_0.aleo/do.future;
1114
1115finalize do:
1116 input r0 as u32.public;
1117 set r0 into m[0u8];",
1118 )
1119 .unwrap();
1120
1121 let deployment = vm.deploy(&private_key, &program, None, 0, None, rng).unwrap();
1122 vm.add_next_block(&sample_next_block(&vm, &private_key, &[deployment], rng).unwrap()).unwrap();
1123
1124 for i in 1..NUM_DEPLOYMENTS {
1126 let mut program_string = String::new();
1127 for j in 0..i {
1129 program_string.push_str(&format!("import program_layer_{}.aleo;\n", j));
1130 }
1131 program_string.push_str(&format!(
1133 "program program_layer_{i}.aleo;
1134
1135mapping m:
1136 key as u8.public;
1137 value as u32.public;
1138
1139function do:
1140 input r0 as u32.public;
1141 call program_layer_{prev}.aleo/do r0 into r1;
1142 async do r0 r1 into r2;
1143 output r2 as program_layer_{i}.aleo/do.future;
1144
1145finalize do:
1146 input r0 as u32.public;
1147 input r1 as program_layer_{prev}.aleo/do.future;
1148 await r1;
1149 set r0 into m[0u8];",
1150 prev = i - 1
1151 ));
1152 let program = Program::from_str(&program_string).unwrap();
1154
1155 let deployment = vm.deploy(&private_key, &program, None, 0, None, rng).unwrap();
1157
1158 vm.add_next_block(&sample_next_block(&vm, &private_key, &[deployment], rng).unwrap()).unwrap();
1159 }
1160
1161 let records = genesis.transitions().cloned().flat_map(Transition::into_records).collect::<IndexMap<_, _>>();
1163 trace!("Unspent Records:\n{:#?}", records);
1164
1165 let record = Some(records.values().next().unwrap().decrypt(&view_key).unwrap());
1167
1168 let inputs = [Value::<CurrentNetwork>::from_str("1u32").unwrap()].into_iter();
1170
1171 let transaction =
1173 vm.execute(&private_key, ("program_layer_30.aleo", "do"), inputs, record, 0, None, rng).unwrap();
1174
1175 vm.check_transaction(&transaction, None, rng).unwrap();
1177 }
1178}