1use super::*;
16
17impl<N: Network, C: ConsensusStorage<N>> VM<N, C> {
18 #[inline]
29 pub fn speculate<'a>(
30 &self,
31 state: FinalizeGlobalState,
32 coinbase_reward: Option<u64>,
33 candidate_ratifications: Vec<Ratify<N>>,
34 candidate_solutions: Option<&CoinbaseSolution<N>>,
35 candidate_transactions: impl ExactSizeIterator<Item = &'a Transaction<N>>,
36 ) -> Result<(Ratifications<N>, Transactions<N>, Vec<N::TransactionID>, Vec<FinalizeOperation<N>>)> {
37 let timer = timer!("VM::speculate");
38
39 let (ratifications, confirmed_transactions, aborted_transactions, ratified_finalize_operations) = self
41 .atomic_speculate(
42 state,
43 coinbase_reward,
44 candidate_ratifications,
45 candidate_solutions,
46 candidate_transactions,
47 )?;
48
49 let mut aborted_transaction_ids = Vec::with_capacity(aborted_transactions.len());
51 for (tx, error) in aborted_transactions {
52 warn!("Speculation safely aborted a transaction - {error} ({})", tx.id());
53 aborted_transaction_ids.push(tx.id());
54 }
55
56 finish!(timer, "Finished dry-run of the transactions");
57
58 Ok((
60 ratifications,
61 confirmed_transactions.into_iter().collect(),
62 aborted_transaction_ids,
63 ratified_finalize_operations,
64 ))
65 }
66
67 #[inline]
71 pub fn check_speculate(
72 &self,
73 state: FinalizeGlobalState,
74 ratifications: &Ratifications<N>,
75 solutions: Option<&CoinbaseSolution<N>>,
76 transactions: &Transactions<N>,
77 ) -> Result<Vec<FinalizeOperation<N>>> {
78 let timer = timer!("VM::check_speculate");
79
80 let candidate_ratifications = ratifications.iter().cloned().collect::<Vec<_>>();
82 let candidate_transactions =
84 transactions.iter().map(|confirmed| confirmed.to_unconfirmed_transaction()).collect::<Result<Vec<_>>>()?;
85
86 let (speculate_ratifications, confirmed_transactions, aborted_transactions, ratified_finalize_operations) =
88 self.atomic_speculate(state, None, candidate_ratifications, solutions, candidate_transactions.iter())?;
89
90 if ratifications != &speculate_ratifications {
92 bail!("The ratifications after speculation do not match the ratifications in the block");
93 }
94 if transactions != &confirmed_transactions.into_iter().collect() {
96 bail!("The transactions after speculation do not match the transactions in the block");
97 }
98 ensure!(aborted_transactions.is_empty(), "Aborted transactions found in the block (from speculation)");
102
103 finish!(timer, "Finished dry-run of the transactions");
104
105 Ok(ratified_finalize_operations)
107 }
108
109 #[inline]
113 pub fn finalize(
114 &self,
115 state: FinalizeGlobalState,
116 ratifications: &Ratifications<N>,
117 solutions: Option<&CoinbaseSolution<N>>,
118 transactions: &Transactions<N>,
119 ) -> Result<Vec<FinalizeOperation<N>>> {
120 let timer = timer!("VM::finalize");
121
122 let ratified_finalize_operations = self.atomic_finalize(state, ratifications, solutions, transactions)?;
124
125 finish!(timer, "Finished real-run of finalize");
126 Ok(ratified_finalize_operations)
127 }
128}
129
130impl<N: Network, C: ConsensusStorage<N>> VM<N, C> {
131 #[cfg(not(any(test, feature = "test")))]
133 pub const MAXIMUM_CONFIRMED_TRANSACTIONS: usize = Transactions::<N>::MAX_TRANSACTIONS;
134 #[cfg(any(test, feature = "test"))]
137 pub const MAXIMUM_CONFIRMED_TRANSACTIONS: usize = 8;
138
139 fn atomic_speculate<'a>(
150 &self,
151 state: FinalizeGlobalState,
152 coinbase_reward: Option<u64>,
153 ratifications: Vec<Ratify<N>>,
154 solutions: Option<&CoinbaseSolution<N>>,
155 transactions: impl ExactSizeIterator<Item = &'a Transaction<N>>,
156 ) -> Result<(
157 Ratifications<N>,
158 Vec<ConfirmedTransaction<N>>,
159 Vec<(Transaction<N>, String)>,
160 Vec<FinalizeOperation<N>>,
161 )> {
162 let _atomic_lock = self.atomic_lock.lock();
166
167 let timer = timer!("VM::atomic_speculate");
168
169 let num_transactions = transactions.len();
171
172 atomic_finalize!(self.finalize_store(), FinalizeMode::DryRun, {
174 if num_transactions > 2 * Transactions::<N>::MAX_TRANSACTIONS {
176 return Err(format!(
178 "Too many transactions in the block - {num_transactions} (max: {})",
179 2 * Transactions::<N>::MAX_TRANSACTIONS
180 ));
181 }
182
183 let pre_ratifications = ratifications.iter().filter(|r| match r {
185 Ratify::Genesis(_, _) => true,
186 Ratify::BlockReward(..) | Ratify::PuzzleReward(..) => false,
187 });
188 let post_ratifications = ratifications.iter().filter(|r| match r {
190 Ratify::Genesis(_, _) => false,
191 Ratify::BlockReward(..) | Ratify::PuzzleReward(..) => true,
192 });
193
194 let mut ratified_finalize_operations = Vec::new();
196
197 let store = self.finalize_store();
199
200 match Self::atomic_pre_ratify(store, state, pre_ratifications) {
203 Ok(operations) => ratified_finalize_operations.extend(operations),
205 Err(e) => return Err(format!("Failed to pre-ratify - {e}")),
207 }
208
209 let process = self.process.write();
215
216 let mut confirmed = Vec::with_capacity(num_transactions);
218 let mut aborted = Vec::new();
220 let mut deployments = IndexSet::new();
222 let mut counter = 0u32;
224 let mut input_ids: IndexSet<Field<N>> = IndexSet::new();
226
227 'outer: for transaction in transactions {
229 if confirmed.len() >= Self::MAXIMUM_CONFIRMED_TRANSACTIONS {
232 aborted.push((transaction.clone(), "Exceeds block transaction limit".to_string()));
234 continue 'outer;
236 }
237
238 for input_id in transaction.input_ids() {
240 if input_ids.contains(input_id)
242 || self.transition_store().contains_input_id(input_id).unwrap_or(true)
243 {
244 aborted.push((transaction.clone(), format!("Double-spending input {input_id}")));
246 continue 'outer;
248 }
249 }
250
251 let outcome = match transaction {
255 Transaction::Deploy(_, program_owner, deployment, fee) => {
258 let process_rejected_deployment =
260 |fee: &Fee<N>,
261 deployment: Deployment<N>|
262 -> Result<Result<ConfirmedTransaction<N>, String>> {
263 process
264 .finalize_fee(state, store, fee)
265 .and_then(|finalize| {
266 Transaction::from_fee(fee.clone()).map(|fee_tx| (fee_tx, finalize))
267 })
268 .map(|(fee_tx, finalize)| {
269 let rejected = Rejected::new_deployment(*program_owner, deployment);
270 ConfirmedTransaction::rejected_deploy(counter, fee_tx, rejected, finalize)
271 .map_err(|e| e.to_string())
272 })
273 };
274
275 match deployments.contains(deployment.program_id()) {
277 true => match process_rejected_deployment(fee, *deployment.clone()) {
279 Ok(result) => result,
280 Err(error) => {
281 #[cfg(debug_assertions)]
283 eprintln!("Failed to finalize the fee in a rejected deploy - {error}");
284 aborted.push((transaction.clone(), error.to_string()));
286 continue 'outer;
288 }
289 },
290 false => match process.finalize_deployment(state, store, deployment, fee) {
292 Ok((_, finalize)) => {
294 deployments.insert(*deployment.program_id());
296 ConfirmedTransaction::accepted_deploy(counter, transaction.clone(), finalize)
297 .map_err(|e| e.to_string())
298 }
299 Err(_error) => match process_rejected_deployment(fee, *deployment.clone()) {
301 Ok(result) => result,
302 Err(error) => {
303 #[cfg(debug_assertions)]
305 eprintln!("Failed to finalize the fee in a rejected deploy - {error}");
306 aborted.push((transaction.clone(), error.to_string()));
308 continue 'outer;
310 }
311 },
312 },
313 }
314 }
315 Transaction::Execute(_, execution, fee) => {
318 match process.finalize_execution(state, store, execution, fee.as_ref()) {
319 Ok(finalize) => {
321 ConfirmedTransaction::accepted_execute(counter, transaction.clone(), finalize)
322 .map_err(|e| e.to_string())
323 }
324 Err(_error) => match fee {
326 Some(fee) => {
328 match process.finalize_fee(state, store, fee).and_then(|finalize| {
329 Transaction::from_fee(fee.clone()).map(|fee_tx| (fee_tx, finalize))
330 }) {
331 Ok((fee_tx, finalize)) => {
332 let rejected = Rejected::new_execution(execution.clone());
334 ConfirmedTransaction::rejected_execute(counter, fee_tx, rejected, finalize)
336 .map_err(|e| e.to_string())
337 }
338 Err(error) => {
339 #[cfg(debug_assertions)]
341 eprintln!("Failed to finalize the fee in a rejected execute - {error}");
342 aborted.push((transaction.clone(), error.to_string()));
344 continue 'outer;
346 }
347 }
348 }
349 None => Err("Rejected execute transaction has no fee".to_string()),
352 },
353 }
354 }
355 Transaction::Fee(..) => Err("Cannot speculate on a fee transaction".to_string()),
358 };
359 lap!(timer, "Speculated on transaction '{}'", transaction.id());
360
361 match outcome {
362 Ok(confirmed_transaction) => {
364 input_ids.extend(confirmed_transaction.transaction().input_ids());
366 confirmed.push(confirmed_transaction);
368 counter = counter.saturating_add(1);
370 }
371 Err(error) => {
373 eprintln!("Critical bug in speculate: {error}\n\n{transaction}");
374 return Err(format!("Failed to speculate on transaction - {error}"));
376 }
377 }
378 }
379
380 if confirmed.len() + aborted.len() != num_transactions {
382 return Err("Not all transactions were processed in 'VM::atomic_speculate'".to_string());
384 }
385
386 let reward_ratifications = match coinbase_reward {
390 None => vec![],
392 Some(coinbase_reward) => {
394 let Ok(transaction_fees) =
396 confirmed.iter().map(|tx| Ok(*tx.priority_fee_amount()?)).sum::<Result<u64>>()
397 else {
398 return Err("Failed to calculate the transaction fees during speculation".to_string());
400 };
401
402 let block_reward = ledger_block::block_reward(
404 N::STARTING_SUPPLY,
405 N::BLOCK_TIME,
406 coinbase_reward,
407 transaction_fees,
408 );
409 let puzzle_reward = ledger_block::puzzle_reward(coinbase_reward);
411
412 vec![Ratify::BlockReward(block_reward), Ratify::PuzzleReward(puzzle_reward)]
414 }
415 };
416
417 let post_ratifications = reward_ratifications.iter().chain(post_ratifications);
419
420 match Self::atomic_post_ratify(store, state, post_ratifications.clone(), solutions) {
422 Ok(operations) => ratified_finalize_operations.extend(operations),
424 Err(e) => {
426 debug!(
429 "atomic speculate error, state is {:?}, post_ratifications is {:?}, solutions is {:?}",
430 state, post_ratifications, solutions
431 );
432 return Err(format!("Failed to post-ratify - {e}"))
433 },
434 }
435
436 let Ok(ratifications) =
439 Ratifications::try_from_iter(reward_ratifications.into_iter().chain(ratifications.into_iter()))
440 else {
441 return Err("Failed to construct the ratifications after speculation".to_string());
443 };
444
445 finish!(timer);
446
447 Ok((ratifications, confirmed, aborted, ratified_finalize_operations))
450 })
451 }
452
453 #[inline]
457 fn atomic_finalize(
458 &self,
459 state: FinalizeGlobalState,
460 ratifications: &Ratifications<N>,
461 solutions: Option<&CoinbaseSolution<N>>,
462 transactions: &Transactions<N>,
463 ) -> Result<Vec<FinalizeOperation<N>>> {
464 let _atomic_lock = self.atomic_lock.lock();
468
469 let timer = timer!("VM::atomic_finalize");
470
471 atomic_finalize!(self.finalize_store(), FinalizeMode::RealRun, {
473 let pre_ratifications = ratifications.iter().filter(|r| match r {
475 Ratify::Genesis(_, _) => true,
476 Ratify::BlockReward(..) | Ratify::PuzzleReward(..) => false,
477 });
478 let post_ratifications = ratifications.iter().filter(|r| match r {
480 Ratify::Genesis(_, _) => false,
481 Ratify::BlockReward(..) | Ratify::PuzzleReward(..) => true,
482 });
483
484 let mut ratified_finalize_operations = Vec::new();
486
487 let store = self.finalize_store();
489
490 match Self::atomic_pre_ratify(store, state, pre_ratifications) {
493 Ok(operations) => ratified_finalize_operations.extend(operations),
495 Err(e) => return Err(format!("Failed to pre-ratify - {e}")),
497 }
498
499 let mut process = self.process.write();
505
506 let mut stacks = Vec::new();
508
509 for (index, transaction) in transactions.iter().enumerate() {
511 let index = u32::try_from(index).map_err(|_| "Failed to convert transaction index".to_string())?;
514 if index != transaction.index() {
516 return Err(format!("Mismatch in {} transaction index", transaction.variant()));
518 }
519 let outcome: Result<(), String> = match transaction {
523 ConfirmedTransaction::AcceptedDeploy(_, transaction, finalize) => {
524 let (deployment, fee) = match transaction {
526 Transaction::Deploy(_, _, deployment, fee) => (deployment, fee),
527 _ => return Err("Expected deploy transaction".to_string()),
529 };
530 match process.finalize_deployment(state, store, deployment, fee) {
532 Ok((stack, finalize_operations)) => match finalize == &finalize_operations {
534 true => stacks.push(stack),
536 false => {
538 return Err(format!(
539 "Mismatch in finalize operations for an accepted deploy - (found: {finalize_operations:?}, expected: {finalize:?})"
540 ));
541 }
542 },
543 Err(error) => {
545 return Err(format!("Failed to finalize an accepted deploy transaction - {error}"));
546 }
547 };
548 Ok(())
549 }
550 ConfirmedTransaction::AcceptedExecute(_, transaction, finalize) => {
551 let (execution, fee) = match transaction {
553 Transaction::Execute(_, execution, fee) => (execution, fee),
554 _ => return Err("Expected execute transaction".to_string()),
556 };
557 match process.finalize_execution(state, store, execution, fee.as_ref()) {
560 Ok(finalize_operations) => {
562 if finalize != &finalize_operations {
563 return Err(format!(
565 "Mismatch in finalize operations for an accepted execute - (found: {finalize_operations:?}, expected: {finalize:?})"
566 ));
567 }
568 }
569 Err(error) => {
571 return Err(format!("Failed to finalize an accepted execute transaction - {error}"));
572 }
573 }
574 Ok(())
575 }
576 ConfirmedTransaction::RejectedDeploy(_, Transaction::Fee(_, fee), rejected, finalize) => {
577 let Some(deployment) = rejected.deployment() else {
579 return Err("Expected rejected deployment".to_string());
581 };
582 let Ok(expected_deployment_id) = deployment.to_deployment_id() else {
584 return Err("Failed to compute the deployment ID for a rejected deployment".to_string());
586 };
587 let Ok(candidate_deployment_id) = fee.deployment_or_execution_id() else {
589 return Err("Failed to retrieve the deployment ID from the fee".to_string());
591 };
592 if candidate_deployment_id != expected_deployment_id {
594 return Err("Mismatch in fee for a rejected deploy transaction".to_string());
596 }
597 match process.finalize_fee(state, store, fee) {
599 Ok(finalize_operations) => {
601 if finalize != &finalize_operations {
602 return Err(format!(
604 "Mismatch in finalize operations for a rejected deploy - (found: {finalize_operations:?}, expected: {finalize:?})"
605 ));
606 }
607 }
608 Err(_e) => {
610 return Err("Failed to finalize the fee in a rejected deploy transaction".to_string());
611 }
612 }
613 Ok(())
614 }
615 ConfirmedTransaction::RejectedExecute(_, Transaction::Fee(_, fee), rejected, finalize) => {
616 let Some(execution) = rejected.execution() else {
618 return Err("Expected rejected execution".to_string());
620 };
621 let Ok(expected_execution_id) = execution.to_execution_id() else {
623 return Err("Failed to compute the execution ID for a rejected execution".to_string());
625 };
626 let Ok(candidate_execution_id) = fee.deployment_or_execution_id() else {
628 return Err("Failed to retrieve the execution ID from the fee".to_string());
630 };
631 if candidate_execution_id != expected_execution_id {
633 return Err("Mismatch in fee for a rejected execute transaction".to_string());
635 }
636 match process.finalize_fee(state, store, fee) {
638 Ok(finalize_operations) => {
640 if finalize != &finalize_operations {
641 return Err(format!(
643 "Mismatch in finalize operations for a rejected execute - (found: {finalize_operations:?}, expected: {finalize:?})"
644 ));
645 }
646 }
647 Err(_e) => {
649 return Err("Failed to finalize the fee in a rejected execute transaction".to_string());
650 }
651 }
652 Ok(())
653 }
654 _ => return Err("Invalid confirmed transaction type".to_string()),
656 };
657 lap!(timer, "Finalizing transaction {}", transaction.id());
658
659 match outcome {
660 Ok(()) => (),
662 Err(error) => {
664 eprintln!("Critical bug in finalize: {error}\n\n{transaction}");
665 return Err(format!("Failed to finalize on transaction - {error}"));
667 }
668 }
669 }
670
671 match Self::atomic_post_ratify(store, state, post_ratifications, solutions) {
674 Ok(operations) => ratified_finalize_operations.extend(operations),
676 Err(e) => {
678 debug!(
681 "atomic finalize error, state is {:?}, solutions is {:?}",
682 state, solutions
683 );
684 return Err(format!("Failed to post-ratify - {e}"))
685 },
686 }
687
688 if !stacks.is_empty() {
692 stacks.into_iter().for_each(|stack| process.add_stack(stack))
693 }
694
695 finish!(timer); Ok(ratified_finalize_operations)
698 })
699 }
700
701 #[inline]
703 fn atomic_pre_ratify<'a>(
704 store: &FinalizeStore<N, C::FinalizeStorage>,
705 state: FinalizeGlobalState,
706 pre_ratifications: impl Iterator<Item = &'a Ratify<N>>,
707 ) -> Result<Vec<FinalizeOperation<N>>> {
708 let program_id = ProgramID::from_str("credits.aleo")?;
710 let committee_mapping = Identifier::from_str("committee")?;
712 let bonded_mapping = Identifier::from_str("bonded")?;
714 let account_mapping = Identifier::from_str("account")?;
716
717 let mut finalize_operations = Vec::new();
719
720 let mut is_genesis_ratified = false;
722
723 for ratify in pre_ratifications {
725 match ratify {
726 Ratify::Genesis(committee, public_balances) => {
727 ensure!(state.block_height() == 0, "Ratify::Genesis(..) expected a genesis block");
729 ensure!(
731 committee.starting_round() == 0,
732 "Ratify::Genesis(..) expected a genesis committee round of 0"
733 );
734 ensure!(!is_genesis_ratified, "Ratify::Genesis(..) has already been ratified");
736
737 let mut stakers = IndexMap::with_capacity(committee.members().len());
751 for (validator, (microcredits, _)) in committee.members() {
753 stakers.insert(*validator, (*validator, *microcredits));
755 }
756
757 let (next_committee_map, next_bonded_map) =
759 to_next_commitee_map_and_bonded_map(committee, &stakers);
760
761 store.committee_store().insert(state.block_height(), committee.clone())?;
763 finalize_operations.extend(&[
765 store.replace_mapping(program_id, committee_mapping, next_committee_map)?,
767 store.replace_mapping(program_id, bonded_mapping, next_bonded_map)?,
769 ]);
770
771 for (address, amount) in public_balances {
773 let key = Plaintext::from(Literal::Address(*address));
775 let value = store.get_value_speculative(program_id, account_mapping, &key)?;
777 let next_value = Value::from(Literal::U64(U64::new(match value {
779 Some(Value::Plaintext(Plaintext::Literal(Literal::U64(value), _))) => {
780 (*value).saturating_add(*amount)
781 }
782 None => *amount,
783 v => bail!("Critical bug in pre-ratify - Invalid public balance type ({v:?})"),
784 })));
785 let operation = store.update_key_value(program_id, account_mapping, key, next_value)?;
787 finalize_operations.push(operation);
788 }
789
790 is_genesis_ratified = true;
792 }
793 Ratify::BlockReward(..) | Ratify::PuzzleReward(..) => continue,
794 }
795 }
796
797 Ok(finalize_operations)
799 }
800
801 #[inline]
803 fn atomic_post_ratify<'a>(
804 store: &FinalizeStore<N, C::FinalizeStorage>,
805 state: FinalizeGlobalState,
806 post_ratifications: impl Iterator<Item = &'a Ratify<N>>,
807 solutions: Option<&CoinbaseSolution<N>>,
808 ) -> Result<Vec<FinalizeOperation<N>>> {
809 let program_id = ProgramID::from_str("credits.aleo")?;
811 let committee_mapping = Identifier::from_str("committee")?;
813 let bonded_mapping = Identifier::from_str("bonded")?;
815 let account_mapping = Identifier::from_str("account")?;
817
818 let mut finalize_operations = Vec::new();
820
821 let mut is_block_reward_ratified = false;
823 let mut is_puzzle_reward_ratified = false;
825
826 for ratify in post_ratifications {
828 match ratify {
829 Ratify::Genesis(..) => continue,
830 Ratify::BlockReward(block_reward) => {
831 ensure!(!is_block_reward_ratified, "Ratify::BlockReward(..) has already been ratified");
833
834 let current_committee_map = store.get_mapping_speculative(program_id, committee_mapping)?;
836 let current_committee = committee_map_into_committee(state.block_round(), current_committee_map)?;
838 let current_bonded_map = store.get_mapping_speculative(program_id, bonded_mapping)?;
840 let current_stakers = bonded_map_into_stakers(current_bonded_map)?;
842
843 ensure_stakers_matches(¤t_committee, ¤t_stakers)?;
845
846 let next_stakers = staking_rewards(¤t_stakers, ¤t_committee, *block_reward);
848 let next_committee = to_next_committee(¤t_committee, state.block_round(), &next_stakers)?;
850
851 let (next_committee_map, next_bonded_map) =
853 to_next_commitee_map_and_bonded_map(&next_committee, &next_stakers);
854
855 let state_height = state.block_height();
858 let state_round = state.block_round();
859 debug!(
860 "atmoic_post_ratify error, the next committee is {:?}, current state height is {:?}, current state round is {:?}",
861 next_committee,
862 state_height,
863 state_round,
864 );
865
866 store.committee_store().insert(state.block_height(), next_committee)?;
868 finalize_operations.extend(&[
870 store.replace_mapping(program_id, committee_mapping, next_committee_map)?,
872 store.replace_mapping(program_id, bonded_mapping, next_bonded_map)?,
874 ]);
875
876 is_block_reward_ratified = true;
878 }
879 Ratify::PuzzleReward(puzzle_reward) => {
880 ensure!(!is_puzzle_reward_ratified, "Ratify::PuzzleReward(..) has already been ratified");
882
883 if *puzzle_reward == 0 {
885 continue;
886 }
887 let Some(solutions) = solutions else {
889 continue;
890 };
891 let proof_targets =
893 solutions.values().map(|s| Ok((s.address(), s.to_target()?))).collect::<Result<Vec<_>>>()?;
894 let proving_rewards = proving_rewards(proof_targets, *puzzle_reward);
896 for (address, amount) in proving_rewards {
898 let key = Plaintext::from(Literal::Address(address));
900 let value = store.get_value_speculative(program_id, account_mapping, &key)?;
902 let next_value = Value::from(Literal::U64(U64::new(match value {
904 Some(Value::Plaintext(Plaintext::Literal(Literal::U64(value), _))) => {
905 (*value).saturating_add(amount)
906 }
907 None => amount,
908 v => bail!("Critical bug in post-ratify puzzle reward- Invalid amount ({v:?})"),
909 })));
910 let operation = store.update_key_value(program_id, account_mapping, key, next_value)?;
912 finalize_operations.push(operation);
913 }
914
915 is_puzzle_reward_ratified = true;
917 }
918 }
919 }
920
921 Ok(finalize_operations)
923 }
924}
925
926#[cfg(test)]
927mod tests {
928 use super::*;
929 use crate::vm::{test_helpers, test_helpers::sample_finalize_state};
930 use console::{
931 account::{Address, PrivateKey, ViewKey},
932 program::{Ciphertext, Entry, Record},
933 types::Field,
934 };
935 use ledger_block::{Block, Header, Metadata, Transaction, Transition};
936 use ledger_store::helpers::memory::ConsensusMemory;
937 use synthesizer_program::Program;
938
939 use rand::distributions::DistString;
940
941 type CurrentNetwork = test_helpers::CurrentNetwork;
942
943 fn new_program_deployment<R: Rng + CryptoRng>(
945 vm: &VM<CurrentNetwork, ConsensusMemory<CurrentNetwork>>,
946 private_key: &PrivateKey<CurrentNetwork>,
947 previous_block: &Block<CurrentNetwork>,
948 unspent_records: &mut Vec<Record<CurrentNetwork, Ciphertext<CurrentNetwork>>>,
949 rng: &mut R,
950 ) -> Result<(String, Block<CurrentNetwork>)> {
951 let program_name = format!("a{}.aleo", Alphanumeric.sample_string(rng, 8).to_lowercase());
952
953 let program = Program::<CurrentNetwork>::from_str(&format!(
954 "
955program {program_name};
956
957mapping account:
958 // The token owner.
959 key as address.public;
960 // The token amount.
961 value as u64.public;
962
963function mint_public:
964 input r0 as address.public;
965 input r1 as u64.public;
966 async mint_public r0 r1 into r2;
967 output r2 as {program_name}/mint_public.future;
968
969finalize mint_public:
970 input r0 as address.public;
971 input r1 as u64.public;
972
973 get.or_use account[r0] 0u64 into r2;
974 add r2 r1 into r3;
975 set r3 into account[r0];
976
977function transfer_public:
978 input r0 as address.public;
979 input r1 as u64.public;
980 async transfer_public self.caller r0 r1 into r2;
981 output r2 as {program_name}/transfer_public.future;
982
983finalize transfer_public:
984 input r0 as address.public;
985 input r1 as address.public;
986 input r2 as u64.public;
987
988 get.or_use account[r0] 0u64 into r3;
989 get.or_use account[r1] 0u64 into r4;
990
991 sub r3 r2 into r5;
992 add r4 r2 into r6;
993
994 set r5 into account[r0];
995 set r6 into account[r1];"
996 ))?;
997
998 let view_key = ViewKey::<CurrentNetwork>::try_from(private_key)?;
1000 let credits = Some(unspent_records.pop().unwrap().decrypt(&view_key)?);
1001
1002 let transaction = vm.deploy(private_key, &program, credits, 10, None, rng)?;
1004
1005 let next_block = sample_next_block(vm, private_key, &[transaction], previous_block, unspent_records, rng)?;
1007
1008 Ok((program_name, next_block))
1009 }
1010
1011 fn sample_next_block<R: Rng + CryptoRng>(
1013 vm: &VM<CurrentNetwork, ConsensusMemory<CurrentNetwork>>,
1014 private_key: &PrivateKey<CurrentNetwork>,
1015 transactions: &[Transaction<CurrentNetwork>],
1016 previous_block: &Block<CurrentNetwork>,
1017 unspent_records: &mut Vec<Record<CurrentNetwork, Ciphertext<CurrentNetwork>>>,
1018 rng: &mut R,
1019 ) -> Result<Block<CurrentNetwork>> {
1020 let (ratifications, transactions, aborted_transaction_ids, ratified_finalize_operations) =
1022 vm.speculate(sample_finalize_state(1), None, vec![], None, transactions.iter())?;
1023
1024 let metadata = Metadata::new(
1026 CurrentNetwork::ID,
1027 previous_block.round() + 1,
1028 previous_block.height() + 1,
1029 0,
1030 0,
1031 CurrentNetwork::GENESIS_COINBASE_TARGET,
1032 CurrentNetwork::GENESIS_PROOF_TARGET,
1033 previous_block.last_coinbase_target(),
1034 previous_block.last_coinbase_timestamp(),
1035 CurrentNetwork::GENESIS_TIMESTAMP + 1,
1036 )?;
1037
1038 let header = Header::from(
1040 vm.block_store().current_state_root(),
1041 transactions.to_transactions_root().unwrap(),
1042 transactions.to_finalize_root(ratified_finalize_operations).unwrap(),
1043 ratifications.to_ratifications_root().unwrap(),
1044 Field::zero(),
1045 Field::zero(),
1046 metadata,
1047 )?;
1048
1049 let block = Block::new_beacon(
1050 private_key,
1051 previous_block.hash(),
1052 header,
1053 ratifications,
1054 None,
1055 transactions,
1056 aborted_transaction_ids,
1057 rng,
1058 )?;
1059
1060 let new_records = block
1062 .transitions()
1063 .cloned()
1064 .flat_map(Transition::into_records)
1065 .map(|(_, record)| record)
1066 .collect::<Vec<_>>();
1067 unspent_records.extend(new_records);
1068
1069 Ok(block)
1070 }
1071
1072 fn generate_splits<R: Rng + CryptoRng>(
1074 vm: &VM<CurrentNetwork, ConsensusMemory<CurrentNetwork>>,
1075 private_key: &PrivateKey<CurrentNetwork>,
1076 previous_block: &Block<CurrentNetwork>,
1077 unspent_records: &mut Vec<Record<CurrentNetwork, Ciphertext<CurrentNetwork>>>,
1078 rng: &mut R,
1079 ) -> Result<Block<CurrentNetwork>> {
1080 let view_key = ViewKey::<CurrentNetwork>::try_from(private_key)?;
1082
1083 let mut transactions = Vec::new();
1085 while !unspent_records.is_empty() {
1086 let record = unspent_records.pop().unwrap().decrypt(&view_key)?;
1087
1088 let split_balance = match record.find(&[Identifier::from_str("microcredits")?]) {
1090 Ok(Entry::Private(Plaintext::Literal(Literal::U64(amount), _))) => *amount / 2,
1091 _ => bail!("fee record does not contain a microcredits entry"),
1092 };
1093
1094 let inputs = [
1096 Value::<CurrentNetwork>::Record(record),
1097 Value::<CurrentNetwork>::from_str(&format!("{split_balance}u64")).unwrap(),
1098 ]
1099 .into_iter();
1100
1101 let transaction = vm.execute(private_key, ("credits.aleo", "split"), inputs, None, 0, None, rng).unwrap();
1103
1104 transactions.push(transaction);
1105 }
1106
1107 sample_next_block(vm, private_key, &transactions, previous_block, unspent_records, rng)
1109 }
1110
1111 fn create_execution(
1113 vm: &VM<CurrentNetwork, ConsensusMemory<CurrentNetwork>>,
1114 caller_private_key: PrivateKey<CurrentNetwork>,
1115 program_id: &str,
1116 function_name: &str,
1117 inputs: Vec<Value<CurrentNetwork>>,
1118 unspent_records: &mut Vec<Record<CurrentNetwork, Ciphertext<CurrentNetwork>>>,
1119 rng: &mut TestRng,
1120 ) -> Transaction<CurrentNetwork> {
1121 assert!(vm.contains_program(&ProgramID::from_str(program_id).unwrap()));
1122
1123 let view_key = ViewKey::<CurrentNetwork>::try_from(caller_private_key).unwrap();
1125 let credits = Some(unspent_records.pop().unwrap().decrypt(&view_key).unwrap());
1126
1127 let transaction = vm
1129 .execute(&caller_private_key, (program_id, function_name), inputs.into_iter(), credits, 1, None, rng)
1130 .unwrap();
1131 vm.check_transaction(&transaction, None, rng).unwrap();
1133
1134 transaction
1136 }
1137
1138 fn sample_mint_public(
1140 vm: &VM<CurrentNetwork, ConsensusMemory<CurrentNetwork>>,
1141 caller_private_key: PrivateKey<CurrentNetwork>,
1142 program_id: &str,
1143 recipient: Address<CurrentNetwork>,
1144 amount: u64,
1145 unspent_records: &mut Vec<Record<CurrentNetwork, Ciphertext<CurrentNetwork>>>,
1146 rng: &mut TestRng,
1147 ) -> Transaction<CurrentNetwork> {
1148 let inputs = vec![
1149 Value::<CurrentNetwork>::from_str(&recipient.to_string()).unwrap(),
1150 Value::<CurrentNetwork>::from_str(&format!("{amount}u64")).unwrap(),
1151 ];
1152
1153 create_execution(vm, caller_private_key, program_id, "mint_public", inputs, unspent_records, rng)
1154 }
1155
1156 fn sample_transfer_public(
1158 vm: &VM<CurrentNetwork, ConsensusMemory<CurrentNetwork>>,
1159 caller_private_key: PrivateKey<CurrentNetwork>,
1160 program_id: &str,
1161 recipient: Address<CurrentNetwork>,
1162 amount: u64,
1163 unspent_records: &mut Vec<Record<CurrentNetwork, Ciphertext<CurrentNetwork>>>,
1164 rng: &mut TestRng,
1165 ) -> Transaction<CurrentNetwork> {
1166 let inputs = vec![
1167 Value::<CurrentNetwork>::from_str(&recipient.to_string()).unwrap(),
1168 Value::<CurrentNetwork>::from_str(&format!("{amount}u64")).unwrap(),
1169 ];
1170
1171 create_execution(vm, caller_private_key, program_id, "transfer_public", inputs, unspent_records, rng)
1172 }
1173
1174 fn reject(
1176 index: u32,
1177 transaction: &Transaction<CurrentNetwork>,
1178 finalize: &[FinalizeOperation<CurrentNetwork>],
1179 ) -> ConfirmedTransaction<CurrentNetwork> {
1180 match transaction {
1181 Transaction::Execute(_, execution, fee) => ConfirmedTransaction::RejectedExecute(
1182 index,
1183 Transaction::from_fee(fee.clone().unwrap()).unwrap(),
1184 Rejected::new_execution(execution.clone()),
1185 finalize.to_vec(),
1186 ),
1187 _ => panic!("only reject execution transactions"),
1188 }
1189 }
1190
1191 #[test]
1192 fn test_finalize_duplicate_deployment() {
1193 let rng = &mut TestRng::default();
1194
1195 let vm = crate::vm::test_helpers::sample_vm();
1196
1197 let deployment_transaction = crate::vm::test_helpers::sample_deployment_transaction(rng);
1199 let deployment_transaction_id = deployment_transaction.id();
1200
1201 let program_id = ProgramID::from_str("testing.aleo").unwrap();
1203
1204 let (ratifications, confirmed_transactions, aborted_transaction_ids, _) = vm
1206 .speculate(sample_finalize_state(1), None, vec![], None, [deployment_transaction.clone()].iter())
1207 .unwrap();
1208 assert_eq!(confirmed_transactions.len(), 1);
1209 assert!(aborted_transaction_ids.is_empty());
1210
1211 assert!(!vm.contains_program(&program_id));
1213
1214 assert!(vm.finalize(sample_finalize_state(1), &ratifications, None, &confirmed_transactions).is_ok());
1216
1217 assert!(vm.contains_program(&program_id));
1219
1220 assert!(vm.finalize(sample_finalize_state(1), &ratifications, None, &confirmed_transactions).is_err());
1222
1223 assert!(vm.contains_program(&program_id));
1225
1226 let (_, candidate_transactions, aborted_transaction_ids, _) =
1228 vm.atomic_speculate(sample_finalize_state(1), None, vec![], None, [deployment_transaction].iter()).unwrap();
1229 assert_eq!(candidate_transactions.len(), 1);
1230 assert!(matches!(candidate_transactions[0], ConfirmedTransaction::RejectedDeploy(..)));
1231 assert!(aborted_transaction_ids.is_empty());
1232
1233 assert_eq!(candidate_transactions[0].to_unconfirmed_transaction_id().unwrap(), deployment_transaction_id);
1235 }
1236
1237 #[test]
1238 fn test_atomic_finalize_many() {
1239 let rng = &mut TestRng::default();
1240
1241 let caller_private_key = test_helpers::sample_genesis_private_key(rng);
1243 let caller_address = Address::try_from(&caller_private_key).unwrap();
1244
1245 let recipient_private_key = PrivateKey::new(rng).unwrap();
1247 let recipient_address = Address::try_from(&recipient_private_key).unwrap();
1248
1249 let vm = test_helpers::sample_vm_with_genesis_block(rng);
1251
1252 let genesis =
1254 vm.block_store().get_block(&vm.block_store().get_block_hash(0).unwrap().unwrap()).unwrap().unwrap();
1255
1256 let mut unspent_records = genesis
1258 .transitions()
1259 .cloned()
1260 .flat_map(Transition::into_records)
1261 .map(|(_, record)| record)
1262 .collect::<Vec<_>>();
1263
1264 let (program_id, deployment_block) =
1266 new_program_deployment(&vm, &caller_private_key, &genesis, &mut unspent_records, rng).unwrap();
1267
1268 vm.add_next_block(&deployment_block).unwrap();
1270
1271 let splits_block =
1273 generate_splits(&vm, &caller_private_key, &deployment_block, &mut unspent_records, rng).unwrap();
1274
1275 vm.add_next_block(&splits_block).unwrap();
1277
1278 let initial_mint =
1280 sample_mint_public(&vm, caller_private_key, &program_id, caller_address, 20, &mut unspent_records, rng);
1281 let initial_mint_block =
1282 sample_next_block(&vm, &caller_private_key, &[initial_mint], &splits_block, &mut unspent_records, rng)
1283 .unwrap();
1284
1285 vm.add_next_block(&initial_mint_block).unwrap();
1287
1288 let mint_10 =
1290 sample_mint_public(&vm, caller_private_key, &program_id, caller_address, 10, &mut unspent_records, rng);
1291 let mint_20 =
1292 sample_mint_public(&vm, caller_private_key, &program_id, caller_address, 20, &mut unspent_records, rng);
1293 let transfer_10 = sample_transfer_public(
1294 &vm,
1295 caller_private_key,
1296 &program_id,
1297 recipient_address,
1298 10,
1299 &mut unspent_records,
1300 rng,
1301 );
1302 let transfer_20 = sample_transfer_public(
1303 &vm,
1304 caller_private_key,
1305 &program_id,
1306 recipient_address,
1307 20,
1308 &mut unspent_records,
1309 rng,
1310 );
1311 let transfer_30 = sample_transfer_public(
1312 &vm,
1313 caller_private_key,
1314 &program_id,
1315 recipient_address,
1316 30,
1317 &mut unspent_records,
1318 rng,
1319 );
1320
1321 {
1328 let transactions = [mint_10.clone(), transfer_10.clone(), transfer_20.clone()];
1329 let (_, confirmed_transactions, aborted_transaction_ids, _) =
1330 vm.atomic_speculate(sample_finalize_state(1), None, vec![], None, transactions.iter()).unwrap();
1331
1332 assert_eq!(confirmed_transactions.len(), 3);
1334 confirmed_transactions.iter().for_each(|confirmed_tx| assert!(confirmed_tx.is_accepted()));
1335 assert!(aborted_transaction_ids.is_empty());
1336
1337 assert_eq!(confirmed_transactions[0].transaction(), &mint_10);
1338 assert_eq!(confirmed_transactions[1].transaction(), &transfer_10);
1339 assert_eq!(confirmed_transactions[2].transaction(), &transfer_20);
1340 }
1341
1342 {
1348 let transactions = [transfer_20.clone(), mint_10.clone(), mint_20.clone(), transfer_30.clone()];
1349 let (_, confirmed_transactions, aborted_transaction_ids, _) =
1350 vm.atomic_speculate(sample_finalize_state(1), None, vec![], None, transactions.iter()).unwrap();
1351
1352 assert_eq!(confirmed_transactions.len(), 4);
1354 confirmed_transactions.iter().for_each(|confirmed_tx| assert!(confirmed_tx.is_accepted()));
1355 assert!(aborted_transaction_ids.is_empty());
1356
1357 assert_eq!(confirmed_transactions[0].transaction(), &transfer_20);
1359 assert_eq!(confirmed_transactions[1].transaction(), &mint_10);
1360 assert_eq!(confirmed_transactions[2].transaction(), &mint_20);
1361 assert_eq!(confirmed_transactions[3].transaction(), &transfer_30);
1362 }
1363
1364 {
1368 let transactions = [transfer_20.clone(), transfer_10.clone()];
1369 let (_, confirmed_transactions, aborted_transaction_ids, _) =
1370 vm.atomic_speculate(sample_finalize_state(1), None, vec![], None, transactions.iter()).unwrap();
1371
1372 assert_eq!(confirmed_transactions.len(), 2);
1374 assert!(aborted_transaction_ids.is_empty());
1375
1376 assert!(confirmed_transactions[0].is_accepted());
1377 assert!(confirmed_transactions[1].is_rejected());
1378
1379 assert_eq!(confirmed_transactions[0].transaction(), &transfer_20);
1380 assert_eq!(
1381 confirmed_transactions[1],
1382 reject(1, &transfer_10, confirmed_transactions[1].finalize_operations())
1383 );
1384 }
1385
1386 {
1392 let transactions = [mint_20.clone(), transfer_30.clone(), transfer_20.clone(), transfer_10.clone()];
1393 let (_, confirmed_transactions, aborted_transaction_ids, _) =
1394 vm.atomic_speculate(sample_finalize_state(1), None, vec![], None, transactions.iter()).unwrap();
1395
1396 assert_eq!(confirmed_transactions.len(), 4);
1398 assert!(aborted_transaction_ids.is_empty());
1399
1400 assert!(confirmed_transactions[0].is_accepted());
1401 assert!(confirmed_transactions[1].is_accepted());
1402 assert!(confirmed_transactions[2].is_rejected());
1403 assert!(confirmed_transactions[3].is_accepted());
1404
1405 assert_eq!(confirmed_transactions[0].transaction(), &mint_20);
1406 assert_eq!(confirmed_transactions[1].transaction(), &transfer_30);
1407 assert_eq!(
1408 confirmed_transactions[2],
1409 reject(2, &transfer_20, confirmed_transactions[2].finalize_operations())
1410 );
1411 assert_eq!(confirmed_transactions[3].transaction(), &transfer_10);
1412 }
1413 }
1414
1415 #[test]
1416 fn test_finalize_catch_halt() {
1417 let rng = &mut TestRng::default();
1418
1419 let caller_private_key = test_helpers::sample_genesis_private_key(rng);
1421 let caller_view_key = ViewKey::try_from(&caller_private_key).unwrap();
1422
1423 for finalize_logic in &[
1424 "finalize ped_hash:
1425 input r0 as u128.public;
1426 hash.ped64 r0 into r1 as field;
1427 set r1 into hashes[r0];",
1428 "finalize ped_hash:
1429 input r0 as u128.public;
1430 div r0 0u128 into r1;",
1431 ] {
1432 let vm = test_helpers::sample_vm_with_genesis_block(rng);
1434
1435 let genesis =
1437 vm.block_store().get_block(&vm.block_store().get_block_hash(0).unwrap().unwrap()).unwrap().unwrap();
1438
1439 let mut unspent_records = genesis
1441 .transitions()
1442 .cloned()
1443 .flat_map(Transition::into_records)
1444 .map(|(_, record)| record)
1445 .collect::<Vec<_>>();
1446
1447 let program_id = "testing.aleo";
1449 let program = Program::<CurrentNetwork>::from_str(&format!(
1450 "
1451program {program_id};
1452
1453mapping hashes:
1454 key as u128.public;
1455 value as field.public;
1456
1457function ped_hash:
1458 input r0 as u128.public;
1459 // hash.ped64 r0 into r1 as field; // <--- This will cause a E::halt.
1460 async ped_hash r0 into r1;
1461 output r1 as {program_id}/ped_hash.future;
1462
1463{finalize_logic}"
1464 ))
1465 .unwrap();
1466
1467 let credits = Some(unspent_records.pop().unwrap().decrypt(&caller_view_key).unwrap());
1468
1469 let deployment_transaction = vm.deploy(&caller_private_key, &program, credits, 10, None, rng).unwrap();
1471
1472 let deployment_block = sample_next_block(
1474 &vm,
1475 &caller_private_key,
1476 &[deployment_transaction],
1477 &genesis,
1478 &mut unspent_records,
1479 rng,
1480 )
1481 .unwrap();
1482
1483 vm.add_next_block(&deployment_block).unwrap();
1485
1486 let inputs = vec![Value::<CurrentNetwork>::from_str("1u128").unwrap()];
1488 let transaction =
1489 create_execution(&vm, caller_private_key, program_id, "ped_hash", inputs, &mut unspent_records, rng);
1490
1491 let (_, confirmed_transactions, aborted_transaction_ids, _) =
1493 vm.speculate(sample_finalize_state(1), None, vec![], None, [transaction.clone()].iter()).unwrap();
1494 assert!(aborted_transaction_ids.is_empty());
1495
1496 assert_eq!(confirmed_transactions.len(), 1);
1498 assert!(transaction.is_execute());
1499 if let Transaction::Execute(_, execution, fee) = transaction {
1500 let fee_transaction = Transaction::from_fee(fee.unwrap()).unwrap();
1501 let expected_confirmed_transaction = ConfirmedTransaction::RejectedExecute(
1502 0,
1503 fee_transaction,
1504 Rejected::new_execution(execution),
1505 vec![],
1506 );
1507
1508 let confirmed_transaction = confirmed_transactions.iter().next().unwrap();
1509 assert_eq!(confirmed_transaction, &expected_confirmed_transaction);
1510 }
1511 }
1512 }
1513
1514 #[test]
1515 fn test_rejected_transaction_should_not_update_storage() {
1516 let rng = &mut TestRng::default();
1517
1518 let private_key = test_helpers::sample_genesis_private_key(rng);
1520 let address = Address::try_from(&private_key).unwrap();
1521
1522 let vm = test_helpers::sample_vm_with_genesis_block(rng);
1524
1525 let genesis =
1527 vm.block_store().get_block(&vm.block_store().get_block_hash(0).unwrap().unwrap()).unwrap().unwrap();
1528
1529 let mut unspent_records = genesis
1531 .transitions()
1532 .cloned()
1533 .flat_map(Transition::into_records)
1534 .map(|(_, record)| record)
1535 .collect::<Vec<_>>();
1536
1537 let splits_block = generate_splits(&vm, &private_key, &genesis, &mut unspent_records, rng).unwrap();
1539
1540 vm.add_next_block(&splits_block).unwrap();
1542
1543 let deployment_block = {
1545 let program = Program::<CurrentNetwork>::from_str(
1546 "
1547program testing.aleo;
1548
1549mapping entries:
1550 key as address.public;
1551 value as u8.public;
1552
1553function compute:
1554 input r0 as u8.public;
1555 async compute self.caller r0 into r1;
1556 output r1 as testing.aleo/compute.future;
1557
1558finalize compute:
1559 input r0 as address.public;
1560 input r1 as u8.public;
1561 get.or_use entries[r0] r1 into r2;
1562 add r1 r2 into r3;
1563 set r3 into entries[r0];
1564 get entries[r0] into r4;
1565 add r4 r1 into r5;
1566 set r5 into entries[r0];
1567",
1568 )
1569 .unwrap();
1570
1571 let view_key = ViewKey::<CurrentNetwork>::try_from(private_key).unwrap();
1573 let credits = Some(unspent_records.pop().unwrap().decrypt(&view_key).unwrap());
1574
1575 let transaction = vm.deploy(&private_key, &program, credits, 10, None, rng).unwrap();
1577
1578 sample_next_block(&vm, &private_key, &[transaction], &splits_block, &mut unspent_records, rng).unwrap()
1580 };
1581
1582 vm.add_next_block(&deployment_block).unwrap();
1584
1585 let splits_block = generate_splits(&vm, &private_key, &deployment_block, &mut unspent_records, rng).unwrap();
1587
1588 vm.add_next_block(&splits_block).unwrap();
1590
1591 let r0 = Value::<CurrentNetwork>::from_str("100u8").unwrap();
1593 let first = create_execution(&vm, private_key, "testing.aleo", "compute", vec![r0], &mut unspent_records, rng);
1594
1595 let next_block =
1597 sample_next_block(&vm, &private_key, &[first], &splits_block, &mut unspent_records, rng).unwrap();
1598
1599 assert!(next_block.transactions().iter().next().unwrap().is_rejected());
1601
1602 vm.add_next_block(&next_block).unwrap();
1604
1605 let program_id = ProgramID::from_str("testing.aleo").unwrap();
1607 let mapping_name = Identifier::from_str("entries").unwrap();
1608 let value = vm
1609 .finalize_store()
1610 .get_value_speculative(program_id, mapping_name, &Plaintext::from(Literal::Address(address)))
1611 .unwrap();
1612 println!("{:?}", value);
1613 assert!(
1614 !vm.finalize_store()
1615 .contains_key_confirmed(program_id, mapping_name, &Plaintext::from(Literal::Address(address)))
1616 .unwrap()
1617 );
1618
1619 let r0 = Value::<CurrentNetwork>::from_str("100u8").unwrap();
1621 let first = create_execution(&vm, private_key, "testing.aleo", "compute", vec![r0], &mut unspent_records, rng);
1622
1623 let r0 = Value::<CurrentNetwork>::from_str("1u8").unwrap();
1625 let second = create_execution(&vm, private_key, "testing.aleo", "compute", vec![r0], &mut unspent_records, rng);
1626
1627 let next_block =
1629 sample_next_block(&vm, &private_key, &[first, second], &next_block, &mut unspent_records, rng).unwrap();
1630
1631 assert!(next_block.transactions().iter().next().unwrap().is_rejected());
1633
1634 vm.add_next_block(&next_block).unwrap();
1636
1637 let value = vm
1639 .finalize_store()
1640 .get_value_speculative(program_id, mapping_name, &Plaintext::from(Literal::Address(address)))
1641 .unwrap()
1642 .unwrap();
1643 let expected = Value::<CurrentNetwork>::from_str("3u8").unwrap();
1644 assert_eq!(value, expected);
1645 }
1646
1647 #[test]
1648 fn test_excess_transactions_should_be_aborted() {
1649 let rng = &mut TestRng::default();
1650
1651 let caller_private_key = test_helpers::sample_genesis_private_key(rng);
1653 let caller_address = Address::try_from(&caller_private_key).unwrap();
1654
1655 let vm = test_helpers::sample_vm_with_genesis_block(rng);
1657
1658 let genesis =
1660 vm.block_store().get_block(&vm.block_store().get_block_hash(0).unwrap().unwrap()).unwrap().unwrap();
1661
1662 let mut unspent_records = genesis
1664 .transitions()
1665 .cloned()
1666 .flat_map(Transition::into_records)
1667 .map(|(_, record)| record)
1668 .collect::<Vec<_>>();
1669
1670 let (program_id, deployment_block) =
1672 new_program_deployment(&vm, &caller_private_key, &genesis, &mut unspent_records, rng).unwrap();
1673
1674 vm.add_next_block(&deployment_block).unwrap();
1676
1677 let splits_block =
1679 generate_splits(&vm, &caller_private_key, &deployment_block, &mut unspent_records, rng).unwrap();
1680
1681 vm.add_next_block(&splits_block).unwrap();
1683
1684 let splits_block = generate_splits(&vm, &caller_private_key, &splits_block, &mut unspent_records, rng).unwrap();
1686
1687 vm.add_next_block(&splits_block).unwrap();
1689
1690 let mut transactions = Vec::new();
1692 let mut excess_transaction_ids = Vec::new();
1693
1694 for _ in 0..VM::<CurrentNetwork, ConsensusMemory<_>>::MAXIMUM_CONFIRMED_TRANSACTIONS + 1 {
1695 let transaction =
1696 sample_mint_public(&vm, caller_private_key, &program_id, caller_address, 10, &mut unspent_records, rng);
1697 if transactions.len() >= VM::<CurrentNetwork, ConsensusMemory<_>>::MAXIMUM_CONFIRMED_TRANSACTIONS {
1699 excess_transaction_ids.push(transaction.id());
1700 }
1701
1702 transactions.push(transaction);
1703 }
1704
1705 let next_block =
1707 sample_next_block(&vm, &caller_private_key, &transactions, &splits_block, &mut unspent_records, rng)
1708 .unwrap();
1709
1710 assert_eq!(next_block.aborted_transaction_ids(), &excess_transaction_ids);
1712 assert_eq!(
1713 next_block.transactions().len(),
1714 VM::<CurrentNetwork, ConsensusMemory<_>>::MAXIMUM_CONFIRMED_TRANSACTIONS
1715 );
1716 }
1717}