snarkvm_synthesizer_debug/vm/
finalize.rs

1// Copyright (C) 2019-2023 Aleo Systems Inc.
2// This file is part of the snarkVM library.
3
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at:
7// http://www.apache.org/licenses/LICENSE-2.0
8
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use super::*;
16
17impl<N: Network, C: ConsensusStorage<N>> VM<N, C> {
18    /// Speculates on the given list of transactions in the VM.
19    ///
20    /// Returns the confirmed transactions, aborted transaction IDs,
21    /// and finalize operations from pre-ratify and post-ratify.
22    ///
23    /// Note: This method is used to create a new block (including the genesis block).
24    ///   - If `coinbase_reward = None`, then the `ratifications` will not be modified.
25    ///   - If `coinbase_reward = Some(coinbase_reward)`, then the method will append a
26    ///     `Ratify::BlockReward(block_reward)` and `Ratify::PuzzleReward(puzzle_reward)`
27    ///     to the front of the `ratifications` list.
28    #[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        // Performs a **dry-run** over the list of ratifications, solutions, and transactions.
40        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        // Convert the aborted transactions into aborted transaction IDs.
50        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        // Return the ratifications, confirmed transactions, aborted transaction IDs, and ratified finalize operations.
59        Ok((
60            ratifications,
61            confirmed_transactions.into_iter().collect(),
62            aborted_transaction_ids,
63            ratified_finalize_operations,
64        ))
65    }
66
67    /// Checks the speculation on the given transactions in the VM.
68    ///
69    /// Returns the finalize operations from pre-ratify and post-ratify.
70    #[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        // Reconstruct the candidate ratifications to verify the speculation.
81        let candidate_ratifications = ratifications.iter().cloned().collect::<Vec<_>>();
82        // Reconstruct the unconfirmed transactions to verify the speculation.
83        let candidate_transactions =
84            transactions.iter().map(|confirmed| confirmed.to_unconfirmed_transaction()).collect::<Result<Vec<_>>>()?;
85
86        // Performs a **dry-run** over the list of ratifications, solutions, and transactions.
87        let (speculate_ratifications, confirmed_transactions, aborted_transactions, ratified_finalize_operations) =
88            self.atomic_speculate(state, None, candidate_ratifications, solutions, candidate_transactions.iter())?;
89
90        // Ensure the ratifications after speculation match.
91        if ratifications != &speculate_ratifications {
92            bail!("The ratifications after speculation do not match the ratifications in the block");
93        }
94        // Ensure the transactions after speculation match.
95        if transactions != &confirmed_transactions.into_iter().collect() {
96            bail!("The transactions after speculation do not match the transactions in the block");
97        }
98        // Ensure there are no aborted transaction IDs from this speculation.
99        // Note: There should be no aborted transactions, because we are checking a block,
100        // where any aborted transactions should be in the aborted transaction ID list, not in transactions.
101        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        // Return the ratified finalize operations.
106        Ok(ratified_finalize_operations)
107    }
108
109    /// Finalizes the given transactions into the VM.
110    ///
111    /// Returns the finalize operations from pre-ratify and post-ratify.
112    #[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        // Performs a **real-run** of finalize over the list of ratifications, solutions, and transactions.
123        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    /// The maximum number of confirmed transactions allowed in a block.
132    #[cfg(not(any(test, feature = "test")))]
133    pub const MAXIMUM_CONFIRMED_TRANSACTIONS: usize = Transactions::<N>::MAX_TRANSACTIONS;
134    /// The maximum number of confirmed transactions allowed in a block.
135    /// This is set to a deliberately low value (8) for testing purposes only.
136    #[cfg(any(test, feature = "test"))]
137    pub const MAXIMUM_CONFIRMED_TRANSACTIONS: usize = 8;
138
139    /// Performs atomic speculation over a list of transactions.
140    ///
141    /// Returns the ratifications, confirmed transactions, aborted transactions,
142    /// and finalize operations from pre-ratify and post-ratify.
143    ///
144    /// Note: This method is used by `VM::speculate` and `VM::check_speculate`.
145    ///   - If `coinbase_reward = None`, then the `ratifications` will not be modified.
146    ///   - If `coinbase_reward = Some(coinbase_reward)`, then the method will append a
147    ///     `Ratify::BlockReward(block_reward)` and `Ratify::PuzzleReward(puzzle_reward)`
148    ///     to the front of the `ratifications` list.
149    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        // Acquire the atomic lock, which is needed to ensure this function is not called concurrently
163        // with other `atomic_finalize!` macro calls, which will cause a `bail!` to be triggered erroneously.
164        // Note: This lock must be held for the entire scope of the call to `atomic_finalize!`.
165        let _atomic_lock = self.atomic_lock.lock();
166
167        let timer = timer!("VM::atomic_speculate");
168
169        // Retrieve the number of transactions.
170        let num_transactions = transactions.len();
171
172        // Perform the finalize operation on the preset finalize mode.
173        atomic_finalize!(self.finalize_store(), FinalizeMode::DryRun, {
174            // Ensure the number of transactions does not exceed the maximum.
175            if num_transactions > 2 * Transactions::<N>::MAX_TRANSACTIONS {
176                // Note: This will abort the entire atomic batch.
177                return Err(format!(
178                    "Too many transactions in the block - {num_transactions} (max: {})",
179                    2 * Transactions::<N>::MAX_TRANSACTIONS
180                ));
181            }
182
183            // Initialize an iterator for ratifications before finalize.
184            let pre_ratifications = ratifications.iter().filter(|r| match r {
185                Ratify::Genesis(_, _) => true,
186                Ratify::BlockReward(..) | Ratify::PuzzleReward(..) => false,
187            });
188            // Initialize an iterator for ratifications after finalize.
189            let post_ratifications = ratifications.iter().filter(|r| match r {
190                Ratify::Genesis(_, _) => false,
191                Ratify::BlockReward(..) | Ratify::PuzzleReward(..) => true,
192            });
193
194            // Initialize a list of finalize operations.
195            let mut ratified_finalize_operations = Vec::new();
196
197            // Retrieve the finalize store.
198            let store = self.finalize_store();
199
200            /* Perform the ratifications before finalize. */
201
202            match Self::atomic_pre_ratify(store, state, pre_ratifications) {
203                // Store the finalize operations from the post-ratify.
204                Ok(operations) => ratified_finalize_operations.extend(operations),
205                // Note: This will abort the entire atomic batch.
206                Err(e) => return Err(format!("Failed to pre-ratify - {e}")),
207            }
208
209            /* Perform the atomic finalize over the transactions. */
210
211            // Acquire the write lock on the process.
212            // Note: Due to the highly-sensitive nature of processing all `finalize` calls,
213            // we choose to acquire the write lock for the entire duration of this atomic batch.
214            let process = self.process.write();
215
216            // Initialize a list of the confirmed transactions.
217            let mut confirmed = Vec::with_capacity(num_transactions);
218            // Initialize a list of the aborted transactions.
219            let mut aborted = Vec::new();
220            // Initialize a list of the successful deployments.
221            let mut deployments = IndexSet::new();
222            // Initialize a counter for the confirmed transaction index.
223            let mut counter = 0u32;
224            // Initialize a list of spent input IDs.
225            let mut input_ids: IndexSet<Field<N>> = IndexSet::new();
226
227            // Finalize the transactions.
228            'outer: for transaction in transactions {
229                // Ensure the number of confirmed transactions does not exceed the maximum.
230                // Upon reaching the maximum number of confirmed transactions, all remaining transactions are aborted.
231                if confirmed.len() >= Self::MAXIMUM_CONFIRMED_TRANSACTIONS {
232                    // Store the aborted transaction.
233                    aborted.push((transaction.clone(), "Exceeds block transaction limit".to_string()));
234                    // Continue to the next transaction.
235                    continue 'outer;
236                }
237
238                // Ensure that the transaction is not double-spending an input.
239                for input_id in transaction.input_ids() {
240                    // If the input ID is already spent in this block or previous blocks, abort the transaction.
241                    if input_ids.contains(input_id)
242                        || self.transition_store().contains_input_id(input_id).unwrap_or(true)
243                    {
244                        // Store the aborted transaction.
245                        aborted.push((transaction.clone(), format!("Double-spending input {input_id}")));
246                        // Continue to the next transaction.
247                        continue 'outer;
248                    }
249                }
250
251                // Process the transaction in an isolated atomic batch.
252                // - If the transaction succeeds, the finalize operations are stored.
253                // - If the transaction fails, the atomic batch is aborted and no finalize operations are stored.
254                let outcome = match transaction {
255                    // The finalize operation here involves appending the 'stack',
256                    // and adding the program to the finalize tree.
257                    Transaction::Deploy(_, program_owner, deployment, fee) => {
258                        // Define the closure for processing a rejected deployment.
259                        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                        // Check if the program has already been deployed in this block.
276                        match deployments.contains(deployment.program_id()) {
277                            // If the program has already been deployed, construct the rejected deploy transaction.
278                            true => match process_rejected_deployment(fee, *deployment.clone()) {
279                                Ok(result) => result,
280                                Err(error) => {
281                                    // Note: On failure, skip this transaction, and continue speculation.
282                                    #[cfg(debug_assertions)]
283                                    eprintln!("Failed to finalize the fee in a rejected deploy - {error}");
284                                    // Store the aborted transaction.
285                                    aborted.push((transaction.clone(), error.to_string()));
286                                    // Continue to the next transaction.
287                                    continue 'outer;
288                                }
289                            },
290                            // If the program has not yet been deployed, attempt to deploy it.
291                            false => match process.finalize_deployment(state, store, deployment, fee) {
292                                // Construct the accepted deploy transaction.
293                                Ok((_, finalize)) => {
294                                    // Add the program id to the list of deployments.
295                                    deployments.insert(*deployment.program_id());
296                                    ConfirmedTransaction::accepted_deploy(counter, transaction.clone(), finalize)
297                                        .map_err(|e| e.to_string())
298                                }
299                                // Construct the rejected deploy transaction.
300                                Err(_error) => match process_rejected_deployment(fee, *deployment.clone()) {
301                                    Ok(result) => result,
302                                    Err(error) => {
303                                        // Note: On failure, skip this transaction, and continue speculation.
304                                        #[cfg(debug_assertions)]
305                                        eprintln!("Failed to finalize the fee in a rejected deploy - {error}");
306                                        // Store the aborted transaction.
307                                        aborted.push((transaction.clone(), error.to_string()));
308                                        // Continue to the next transaction.
309                                        continue 'outer;
310                                    }
311                                },
312                            },
313                        }
314                    }
315                    // The finalize operation here involves calling 'update_key_value',
316                    // and update the respective leaves of the finalize tree.
317                    Transaction::Execute(_, execution, fee) => {
318                        match process.finalize_execution(state, store, execution, fee.as_ref()) {
319                            // Construct the accepted execute transaction.
320                            Ok(finalize) => {
321                                ConfirmedTransaction::accepted_execute(counter, transaction.clone(), finalize)
322                                    .map_err(|e| e.to_string())
323                            }
324                            // Construct the rejected execute transaction.
325                            Err(_error) => match fee {
326                                // Finalize the fee, to ensure it is valid.
327                                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                                            // Construct the rejected execution.
333                                            let rejected = Rejected::new_execution(execution.clone());
334                                            // Construct the rejected execute transaction.
335                                            ConfirmedTransaction::rejected_execute(counter, fee_tx, rejected, finalize)
336                                                .map_err(|e| e.to_string())
337                                        }
338                                        Err(error) => {
339                                            // Note: On failure, skip this transaction, and continue speculation.
340                                            #[cfg(debug_assertions)]
341                                            eprintln!("Failed to finalize the fee in a rejected execute - {error}");
342                                            // Store the aborted transaction.
343                                            aborted.push((transaction.clone(), error.to_string()));
344                                            // Continue to the next transaction.
345                                            continue 'outer;
346                                        }
347                                    }
348                                }
349                                // This is a foundational bug - the caller is violating protocol rules.
350                                // Note: This will abort the entire atomic batch.
351                                None => Err("Rejected execute transaction has no fee".to_string()),
352                            },
353                        }
354                    }
355                    // There are no finalize operations here.
356                    // Note: This will abort the entire atomic batch.
357                    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                    // If the transaction succeeded, store it and continue to the next transaction.
363                    Ok(confirmed_transaction) => {
364                        // Add the input IDs to the set of spent input IDs.
365                        input_ids.extend(confirmed_transaction.transaction().input_ids());
366                        // Store the confirmed transaction.
367                        confirmed.push(confirmed_transaction);
368                        // Increment the transaction index counter.
369                        counter = counter.saturating_add(1);
370                    }
371                    // If the transaction failed, abort the entire batch.
372                    Err(error) => {
373                        eprintln!("Critical bug in speculate: {error}\n\n{transaction}");
374                        // Note: This will abort the entire atomic batch.
375                        return Err(format!("Failed to speculate on transaction - {error}"));
376                    }
377                }
378            }
379
380            // Ensure all transactions were processed.
381            if confirmed.len() + aborted.len() != num_transactions {
382                // Note: This will abort the entire atomic batch.
383                return Err("Not all transactions were processed in 'VM::atomic_speculate'".to_string());
384            }
385
386            /* Perform the ratifications after finalize. */
387
388            // Prepare the reward ratifications, if any.
389            let reward_ratifications = match coinbase_reward {
390                // If the coinbase reward is `None`, then there are no reward ratifications.
391                None => vec![],
392                // If the coinbase reward is `Some(coinbase_reward)`, then we must compute the reward ratifications.
393                Some(coinbase_reward) => {
394                    // Calculate the transaction fees.
395                    let Ok(transaction_fees) =
396                        confirmed.iter().map(|tx| Ok(*tx.priority_fee_amount()?)).sum::<Result<u64>>()
397                    else {
398                        // Note: This will abort the entire atomic batch.
399                        return Err("Failed to calculate the transaction fees during speculation".to_string());
400                    };
401
402                    // Compute the block reward.
403                    let block_reward = ledger_block::block_reward(
404                        N::STARTING_SUPPLY,
405                        N::BLOCK_TIME,
406                        coinbase_reward,
407                        transaction_fees,
408                    );
409                    // Compute the puzzle reward.
410                    let puzzle_reward = ledger_block::puzzle_reward(coinbase_reward);
411
412                    // Output the reward ratifications.
413                    vec![Ratify::BlockReward(block_reward), Ratify::PuzzleReward(puzzle_reward)]
414                }
415            };
416
417            // Update the post-ratifications iterator.
418            let post_ratifications = reward_ratifications.iter().chain(post_ratifications);
419
420            // Process the post-ratifications.
421            match Self::atomic_post_ratify(store, state, post_ratifications.clone(), solutions) {
422                // Store the finalize operations from the post-ratify.
423                Ok(operations) => ratified_finalize_operations.extend(operations),
424                // Note: This will abort the entire atomic batch.
425                Err(e) => {
426                    //author: ethan
427                    //propose: trace block syn bug
428                    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            /* Construct the ratifications after speculation. */
437
438            let Ok(ratifications) =
439                Ratifications::try_from_iter(reward_ratifications.into_iter().chain(ratifications.into_iter()))
440            else {
441                // Note: This will abort the entire atomic batch.
442                return Err("Failed to construct the ratifications after speculation".to_string());
443            };
444
445            finish!(timer);
446
447            // On return, 'atomic_finalize!' will abort the batch, and return the ratifications,
448            // confirmed & aborted transactions, and finalize operations from pre-ratify and post-ratify.
449            Ok((ratifications, confirmed, aborted, ratified_finalize_operations))
450        })
451    }
452
453    /// Performs atomic finalization over a list of transactions.
454    ///
455    /// Returns the finalize operations from pre-ratify and post-ratify.
456    #[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        // Acquire the atomic lock, which is needed to ensure this function is not called concurrently
465        // with other `atomic_finalize!` macro calls, which will cause a `bail!` to be triggered erroneously.
466        // Note: This lock must be held for the entire scope of the call to `atomic_finalize!`.
467        let _atomic_lock = self.atomic_lock.lock();
468
469        let timer = timer!("VM::atomic_finalize");
470
471        // Perform the finalize operation on the preset finalize mode.
472        atomic_finalize!(self.finalize_store(), FinalizeMode::RealRun, {
473            // Initialize an iterator for ratifications before finalize.
474            let pre_ratifications = ratifications.iter().filter(|r| match r {
475                Ratify::Genesis(_, _) => true,
476                Ratify::BlockReward(..) | Ratify::PuzzleReward(..) => false,
477            });
478            // Initialize an iterator for ratifications after finalize.
479            let post_ratifications = ratifications.iter().filter(|r| match r {
480                Ratify::Genesis(_, _) => false,
481                Ratify::BlockReward(..) | Ratify::PuzzleReward(..) => true,
482            });
483
484            // Initialize a list of finalize operations.
485            let mut ratified_finalize_operations = Vec::new();
486
487            // Retrieve the finalize store.
488            let store = self.finalize_store();
489
490            /* Perform the ratifications before finalize. */
491
492            match Self::atomic_pre_ratify(store, state, pre_ratifications) {
493                // Store the finalize operations from the post-ratify.
494                Ok(operations) => ratified_finalize_operations.extend(operations),
495                // Note: This will abort the entire atomic batch.
496                Err(e) => return Err(format!("Failed to pre-ratify - {e}")),
497            }
498
499            /* Perform the atomic finalize over the transactions. */
500
501            // Acquire the write lock on the process.
502            // Note: Due to the highly-sensitive nature of processing all `finalize` calls,
503            // we choose to acquire the write lock for the entire duration of this atomic batch.
504            let mut process = self.process.write();
505
506            // Initialize a list for the deployed stacks.
507            let mut stacks = Vec::new();
508
509            // Finalize the transactions.
510            for (index, transaction) in transactions.iter().enumerate() {
511                // Convert the transaction index to a u32.
512                // Note: On failure, this will abort the entire atomic batch.
513                let index = u32::try_from(index).map_err(|_| "Failed to convert transaction index".to_string())?;
514                // Ensure the index matches the expected index.
515                if index != transaction.index() {
516                    // Note: This will abort the entire atomic batch.
517                    return Err(format!("Mismatch in {} transaction index", transaction.variant()));
518                }
519                // Process the transaction in an isolated atomic batch.
520                // - If the transaction succeeds, the finalize operations are stored.
521                // - If the transaction fails, the atomic batch is aborted and no finalize operations are stored.
522                let outcome: Result<(), String> = match transaction {
523                    ConfirmedTransaction::AcceptedDeploy(_, transaction, finalize) => {
524                        // Extract the deployment and fee from the transaction.
525                        let (deployment, fee) = match transaction {
526                            Transaction::Deploy(_, _, deployment, fee) => (deployment, fee),
527                            // Note: This will abort the entire atomic batch.
528                            _ => return Err("Expected deploy transaction".to_string()),
529                        };
530                        // The finalize operation here involves appending the 'stack', and adding the program to the finalize tree.
531                        match process.finalize_deployment(state, store, deployment, fee) {
532                            // Ensure the finalize operations match the expected.
533                            Ok((stack, finalize_operations)) => match finalize == &finalize_operations {
534                                // Store the stack.
535                                true => stacks.push(stack),
536                                // Note: This will abort the entire atomic batch.
537                                false => {
538                                    return Err(format!(
539                                        "Mismatch in finalize operations for an accepted deploy - (found: {finalize_operations:?}, expected: {finalize:?})"
540                                    ));
541                                }
542                            },
543                            // Note: This will abort the entire atomic batch.
544                            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                        // Extract the execution and fee from the transaction.
552                        let (execution, fee) = match transaction {
553                            Transaction::Execute(_, execution, fee) => (execution, fee),
554                            // Note: This will abort the entire atomic batch.
555                            _ => return Err("Expected execute transaction".to_string()),
556                        };
557                        // The finalize operation here involves calling 'update_key_value',
558                        // and update the respective leaves of the finalize tree.
559                        match process.finalize_execution(state, store, execution, fee.as_ref()) {
560                            // Ensure the finalize operations match the expected.
561                            Ok(finalize_operations) => {
562                                if finalize != &finalize_operations {
563                                    // Note: This will abort the entire atomic batch.
564                                    return Err(format!(
565                                        "Mismatch in finalize operations for an accepted execute - (found: {finalize_operations:?}, expected: {finalize:?})"
566                                    ));
567                                }
568                            }
569                            // Note: This will abort the entire atomic batch.
570                            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                        // Extract the rejected deployment.
578                        let Some(deployment) = rejected.deployment() else {
579                            // Note: This will abort the entire atomic batch.
580                            return Err("Expected rejected deployment".to_string());
581                        };
582                        // Compute the expected deployment ID.
583                        let Ok(expected_deployment_id) = deployment.to_deployment_id() else {
584                            // Note: This will abort the entire atomic batch.
585                            return Err("Failed to compute the deployment ID for a rejected deployment".to_string());
586                        };
587                        // Retrieve the candidate deployment ID.
588                        let Ok(candidate_deployment_id) = fee.deployment_or_execution_id() else {
589                            // Note: This will abort the entire atomic batch.
590                            return Err("Failed to retrieve the deployment ID from the fee".to_string());
591                        };
592                        // Ensure this fee corresponds to the deployment.
593                        if candidate_deployment_id != expected_deployment_id {
594                            // Note: This will abort the entire atomic batch.
595                            return Err("Mismatch in fee for a rejected deploy transaction".to_string());
596                        }
597                        // Lastly, finalize the fee.
598                        match process.finalize_fee(state, store, fee) {
599                            // Ensure the finalize operations match the expected.
600                            Ok(finalize_operations) => {
601                                if finalize != &finalize_operations {
602                                    // Note: This will abort the entire atomic batch.
603                                    return Err(format!(
604                                        "Mismatch in finalize operations for a rejected deploy - (found: {finalize_operations:?}, expected: {finalize:?})"
605                                    ));
606                                }
607                            }
608                            // Note: This will abort the entire atomic batch.
609                            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                        // Extract the rejected execution.
617                        let Some(execution) = rejected.execution() else {
618                            // Note: This will abort the entire atomic batch.
619                            return Err("Expected rejected execution".to_string());
620                        };
621                        // Compute the expected execution ID.
622                        let Ok(expected_execution_id) = execution.to_execution_id() else {
623                            // Note: This will abort the entire atomic batch.
624                            return Err("Failed to compute the execution ID for a rejected execution".to_string());
625                        };
626                        // Retrieve the candidate execution ID.
627                        let Ok(candidate_execution_id) = fee.deployment_or_execution_id() else {
628                            // Note: This will abort the entire atomic batch.
629                            return Err("Failed to retrieve the execution ID from the fee".to_string());
630                        };
631                        // Ensure this fee corresponds to the execution.
632                        if candidate_execution_id != expected_execution_id {
633                            // Note: This will abort the entire atomic batch.
634                            return Err("Mismatch in fee for a rejected execute transaction".to_string());
635                        }
636                        // Lastly, finalize the fee.
637                        match process.finalize_fee(state, store, fee) {
638                            // Ensure the finalize operations match the expected.
639                            Ok(finalize_operations) => {
640                                if finalize != &finalize_operations {
641                                    // Note: This will abort the entire atomic batch.
642                                    return Err(format!(
643                                        "Mismatch in finalize operations for a rejected execute - (found: {finalize_operations:?}, expected: {finalize:?})"
644                                    ));
645                                }
646                            }
647                            // Note: This will abort the entire atomic batch.
648                            Err(_e) => {
649                                return Err("Failed to finalize the fee in a rejected execute transaction".to_string());
650                            }
651                        }
652                        Ok(())
653                    }
654                    // Note: This will abort the entire atomic batch.
655                    _ => return Err("Invalid confirmed transaction type".to_string()),
656                };
657                lap!(timer, "Finalizing transaction {}", transaction.id());
658
659                match outcome {
660                    // If the transaction succeeded to finalize, continue to the next transaction.
661                    Ok(()) => (),
662                    // If the transaction failed to finalize, abort and continue to the next transaction.
663                    Err(error) => {
664                        eprintln!("Critical bug in finalize: {error}\n\n{transaction}");
665                        // Note: This will abort the entire atomic batch.
666                        return Err(format!("Failed to finalize on transaction - {error}"));
667                    }
668                }
669            }
670
671            /* Perform the ratifications after finalize. */
672
673            match Self::atomic_post_ratify(store, state, post_ratifications, solutions) {
674                // Store the finalize operations from the post-ratify.
675                Ok(operations) => ratified_finalize_operations.extend(operations),
676                // Note: This will abort the entire atomic batch.
677                Err(e) => {
678                    //author: ethan
679                    //propose: trace block syn bug
680                    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            /* Start the commit process. */
689
690            // Commit all of the stacks to the process.
691            if !stacks.is_empty() {
692                stacks.into_iter().for_each(|stack| process.add_stack(stack))
693            }
694
695            finish!(timer); // <- Note: This timer does **not** include the time to write batch to DB.
696
697            Ok(ratified_finalize_operations)
698        })
699    }
700
701    /// Performs the pre-ratifications before finalizing transactions.
702    #[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        // Construct the program ID.
709        let program_id = ProgramID::from_str("credits.aleo")?;
710        // Construct the committee mapping name.
711        let committee_mapping = Identifier::from_str("committee")?;
712        // Construct the bonded mapping name.
713        let bonded_mapping = Identifier::from_str("bonded")?;
714        // Construct the account mapping name.
715        let account_mapping = Identifier::from_str("account")?;
716
717        // Initialize a list of finalize operations.
718        let mut finalize_operations = Vec::new();
719
720        // Initialize a flag for the genesis ratification.
721        let mut is_genesis_ratified = false;
722
723        // Iterate over the ratifications.
724        for ratify in pre_ratifications {
725            match ratify {
726                Ratify::Genesis(committee, public_balances) => {
727                    // Ensure this is the genesis block.
728                    ensure!(state.block_height() == 0, "Ratify::Genesis(..) expected a genesis block");
729                    // Ensure the genesis committee round is 0.
730                    ensure!(
731                        committee.starting_round() == 0,
732                        "Ratify::Genesis(..) expected a genesis committee round of 0"
733                    );
734                    // Ensure genesis has not been ratified yet.
735                    ensure!(!is_genesis_ratified, "Ratify::Genesis(..) has already been ratified");
736
737                    // TODO (howardwu): Consider whether to initialize the mappings here.
738                    //  Currently, this is breaking for test cases that use VM but do not insert the genesis block.
739                    // // Initialize the store for 'credits.aleo'.
740                    // let credits = Program::<N>::credits()?;
741                    // for mapping in credits.mappings().values() {
742                    //     // Ensure that all mappings are initialized.
743                    //     if !store.contains_mapping_confirmed(credits.id(), mapping.name())? {
744                    //         // Initialize the mappings for 'credits.aleo'.
745                    //         finalize_operations.push(store.initialize_mapping(*credits.id(), *mapping.name())?);
746                    //     }
747                    // }
748
749                    // Initialize the stakers.
750                    let mut stakers = IndexMap::with_capacity(committee.members().len());
751                    // Iterate over the committee members.
752                    for (validator, (microcredits, _)) in committee.members() {
753                        // Insert the validator into the stakers.
754                        stakers.insert(*validator, (*validator, *microcredits));
755                    }
756
757                    // Construct the next committee map and next bonded map.
758                    let (next_committee_map, next_bonded_map) =
759                        to_next_commitee_map_and_bonded_map(committee, &stakers);
760
761                    // Insert the next committee into storage.
762                    store.committee_store().insert(state.block_height(), committee.clone())?;
763                    // Store the finalize operations for updating the committee and bonded mapping.
764                    finalize_operations.extend(&[
765                        // Replace the committee mapping in storage.
766                        store.replace_mapping(program_id, committee_mapping, next_committee_map)?,
767                        // Replace the bonded mapping in storage.
768                        store.replace_mapping(program_id, bonded_mapping, next_bonded_map)?,
769                    ]);
770
771                    // Iterate over the public balances.
772                    for (address, amount) in public_balances {
773                        // Construct the key.
774                        let key = Plaintext::from(Literal::Address(*address));
775                        // Retrieve the current public balance.
776                        let value = store.get_value_speculative(program_id, account_mapping, &key)?;
777                        // Compute the next public balance.
778                        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                        // Update the public balance in finalize storage.
786                        let operation = store.update_key_value(program_id, account_mapping, key, next_value)?;
787                        finalize_operations.push(operation);
788                    }
789
790                    // Set the genesis ratification flag.
791                    is_genesis_ratified = true;
792                }
793                Ratify::BlockReward(..) | Ratify::PuzzleReward(..) => continue,
794            }
795        }
796
797        // Return the finalize operations.
798        Ok(finalize_operations)
799    }
800
801    /// Performs the post-ratifications after finalizing transactions.
802    #[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        // Construct the program ID.
810        let program_id = ProgramID::from_str("credits.aleo")?;
811        // Construct the committee mapping name.
812        let committee_mapping = Identifier::from_str("committee")?;
813        // Construct the bonded mapping name.
814        let bonded_mapping = Identifier::from_str("bonded")?;
815        // Construct the account mapping name.
816        let account_mapping = Identifier::from_str("account")?;
817
818        // Initialize a list of finalize operations.
819        let mut finalize_operations = Vec::new();
820
821        // Initialize a flag for the block reward ratification.
822        let mut is_block_reward_ratified = false;
823        // Initialize a flag for the puzzle reward ratification.
824        let mut is_puzzle_reward_ratified = false;
825
826        // Iterate over the ratifications.
827        for ratify in post_ratifications {
828            match ratify {
829                Ratify::Genesis(..) => continue,
830                Ratify::BlockReward(block_reward) => {
831                    // Ensure the block reward has not been ratified yet.
832                    ensure!(!is_block_reward_ratified, "Ratify::BlockReward(..) has already been ratified");
833
834                    // Retrieve the committee mapping from storage.
835                    let current_committee_map = store.get_mapping_speculative(program_id, committee_mapping)?;
836                    // Convert the committee mapping into a committee.
837                    let current_committee = committee_map_into_committee(state.block_round(), current_committee_map)?;
838                    // Retrieve the bonded mapping from storage.
839                    let current_bonded_map = store.get_mapping_speculative(program_id, bonded_mapping)?;
840                    // Convert the bonded map into stakers.
841                    let current_stakers = bonded_map_into_stakers(current_bonded_map)?;
842
843                    // Ensure the committee matches the bonded mapping.
844                    ensure_stakers_matches(&current_committee, &current_stakers)?;
845
846                    // Compute the updated stakers, using the committee and block reward.
847                    let next_stakers = staking_rewards(&current_stakers, &current_committee, *block_reward);
848                    // Compute the updated committee, using the stakers.
849                    let next_committee = to_next_committee(&current_committee, state.block_round(), &next_stakers)?;
850
851                    // Construct the next committee map and next bonded map.
852                    let (next_committee_map, next_bonded_map) =
853                        to_next_commitee_map_and_bonded_map(&next_committee, &next_stakers);
854
855                    //author: ethan
856                    //propose: trace block sync bug
857                    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                    // Insert the next committee into storage.
867                    store.committee_store().insert(state.block_height(), next_committee)?;
868                    // Store the finalize operations for updating the committee and bonded mapping.
869                    finalize_operations.extend(&[
870                        // Replace the committee mapping in storage.
871                        store.replace_mapping(program_id, committee_mapping, next_committee_map)?,
872                        // Replace the bonded mapping in storage.
873                        store.replace_mapping(program_id, bonded_mapping, next_bonded_map)?,
874                    ]);
875
876                    // Set the block reward ratification flag.
877                    is_block_reward_ratified = true;
878                }
879                Ratify::PuzzleReward(puzzle_reward) => {
880                    // Ensure the puzzle reward has not been ratified yet.
881                    ensure!(!is_puzzle_reward_ratified, "Ratify::PuzzleReward(..) has already been ratified");
882
883                    // If the puzzle reward is zero, skip.
884                    if *puzzle_reward == 0 {
885                        continue;
886                    }
887                    // Retrieve the solutions.
888                    let Some(solutions) = solutions else {
889                        continue;
890                    };
891                    // Compute the proof targets, with the corresponding addresses.
892                    let proof_targets =
893                        solutions.values().map(|s| Ok((s.address(), s.to_target()?))).collect::<Result<Vec<_>>>()?;
894                    // Calculate the proving rewards.
895                    let proving_rewards = proving_rewards(proof_targets, *puzzle_reward);
896                    // Iterate over the proving rewards.
897                    for (address, amount) in proving_rewards {
898                        // Construct the key.
899                        let key = Plaintext::from(Literal::Address(address));
900                        // Retrieve the current public balance.
901                        let value = store.get_value_speculative(program_id, account_mapping, &key)?;
902                        // Compute the next public balance.
903                        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                        // Update the public balance in finalize storage.
911                        let operation = store.update_key_value(program_id, account_mapping, key, next_value)?;
912                        finalize_operations.push(operation);
913                    }
914
915                    // Set the puzzle reward ratification flag.
916                    is_puzzle_reward_ratified = true;
917                }
918            }
919        }
920
921        // Return the finalize operations.
922        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    /// Sample a new program and deploy it to the VM. Returns the program name.
944    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        // Prepare the additional fee.
999        let view_key = ViewKey::<CurrentNetwork>::try_from(private_key)?;
1000        let credits = Some(unspent_records.pop().unwrap().decrypt(&view_key)?);
1001
1002        // Deploy.
1003        let transaction = vm.deploy(private_key, &program, credits, 10, None, rng)?;
1004
1005        // Construct the new block.
1006        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    /// Construct a new block based on the given transactions.
1012    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        // Speculate on the candidate ratifications, solutions, and transactions.
1021        let (ratifications, transactions, aborted_transaction_ids, ratified_finalize_operations) =
1022            vm.speculate(sample_finalize_state(1), None, vec![], None, transactions.iter())?;
1023
1024        // Construct the metadata associated with the block.
1025        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        // Construct the new block header.
1039        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        // Track the new records.
1061        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    /// Generate split transactions for the unspent records.
1073    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        // Prepare the additional fee.
1081        let view_key = ViewKey::<CurrentNetwork>::try_from(private_key)?;
1082
1083        // Generate split transactions.
1084        let mut transactions = Vec::new();
1085        while !unspent_records.is_empty() {
1086            let record = unspent_records.pop().unwrap().decrypt(&view_key)?;
1087
1088            // Fetch the record balance and divide it in half.
1089            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            // Prepare the inputs.
1095            let inputs = [
1096                Value::<CurrentNetwork>::Record(record),
1097                Value::<CurrentNetwork>::from_str(&format!("{split_balance}u64")).unwrap(),
1098            ]
1099            .into_iter();
1100
1101            // Execute.
1102            let transaction = vm.execute(private_key, ("credits.aleo", "split"), inputs, None, 0, None, rng).unwrap();
1103
1104            transactions.push(transaction);
1105        }
1106
1107        // Construct the new block.
1108        sample_next_block(vm, private_key, &transactions, previous_block, unspent_records, rng)
1109    }
1110
1111    /// Create an execution transaction.
1112    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        // Prepare the additional fee.
1124        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        // Execute.
1128        let transaction = vm
1129            .execute(&caller_private_key, (program_id, function_name), inputs.into_iter(), credits, 1, None, rng)
1130            .unwrap();
1131        // Verify.
1132        vm.check_transaction(&transaction, None, rng).unwrap();
1133
1134        // Return the transaction.
1135        transaction
1136    }
1137
1138    /// Sample a public mint transaction.
1139    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    /// Sample a public transfer transaction.
1157    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    /// A helper method to construct the rejected transaction format for `atomic_finalize`.
1175    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        // Fetch a deployment transaction.
1198        let deployment_transaction = crate::vm::test_helpers::sample_deployment_transaction(rng);
1199        let deployment_transaction_id = deployment_transaction.id();
1200
1201        // Construct the program name.
1202        let program_id = ProgramID::from_str("testing.aleo").unwrap();
1203
1204        // Prepare the confirmed transactions.
1205        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        // Ensure the VM does not contain this program.
1212        assert!(!vm.contains_program(&program_id));
1213
1214        // Finalize the transaction.
1215        assert!(vm.finalize(sample_finalize_state(1), &ratifications, None, &confirmed_transactions).is_ok());
1216
1217        // Ensure the VM contains this program.
1218        assert!(vm.contains_program(&program_id));
1219
1220        // Ensure the VM can't redeploy the same transaction.
1221        assert!(vm.finalize(sample_finalize_state(1), &ratifications, None, &confirmed_transactions).is_err());
1222
1223        // Ensure the VM contains this program.
1224        assert!(vm.contains_program(&program_id));
1225
1226        // Ensure the dry run of the redeployment will cause a reject transaction to be created.
1227        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        // Check that the unconfirmed transaction id of the rejected deployment is correct.
1234        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        // Sample a private key and address for the caller.
1242        let caller_private_key = test_helpers::sample_genesis_private_key(rng);
1243        let caller_address = Address::try_from(&caller_private_key).unwrap();
1244
1245        // Sample a private key and address for the recipient.
1246        let recipient_private_key = PrivateKey::new(rng).unwrap();
1247        let recipient_address = Address::try_from(&recipient_private_key).unwrap();
1248
1249        // Initialize the vm.
1250        let vm = test_helpers::sample_vm_with_genesis_block(rng);
1251
1252        // Deploy a new program.
1253        let genesis =
1254            vm.block_store().get_block(&vm.block_store().get_block_hash(0).unwrap().unwrap()).unwrap().unwrap();
1255
1256        // Get the unspent records.
1257        let mut unspent_records = genesis
1258            .transitions()
1259            .cloned()
1260            .flat_map(Transition::into_records)
1261            .map(|(_, record)| record)
1262            .collect::<Vec<_>>();
1263
1264        // Construct the deployment block.
1265        let (program_id, deployment_block) =
1266            new_program_deployment(&vm, &caller_private_key, &genesis, &mut unspent_records, rng).unwrap();
1267
1268        // Add the deployment block to the VM.
1269        vm.add_next_block(&deployment_block).unwrap();
1270
1271        // Generate more records to use for the next block.
1272        let splits_block =
1273            generate_splits(&vm, &caller_private_key, &deployment_block, &mut unspent_records, rng).unwrap();
1274
1275        // Add the splits block to the VM.
1276        vm.add_next_block(&splits_block).unwrap();
1277
1278        // Construct the initial mint.
1279        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        // Add the block to the vm.
1286        vm.add_next_block(&initial_mint_block).unwrap();
1287
1288        // Construct a mint and a transfer.
1289        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        // TODO (raychu86): Confirm that the finalize_operations here are correct.
1322
1323        // Starting Balance = 20
1324        // Mint_10 -> Balance = 20 + 10  = 30
1325        // Transfer_10 -> Balance = 30 - 10 = 20
1326        // Transfer_20 -> Balance = 20 - 20 = 0
1327        {
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 that all the transactions are accepted.
1333            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        // Starting Balance = 20
1343        // Transfer_20 -> Balance = 20 - 20 = 0
1344        // Mint_10 -> Balance = 0 + 10 = 10
1345        // Mint_20 -> Balance = 10 + 20 = 30
1346        // Transfer_30 -> Balance = 30 - 30 = 0
1347        {
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 that all the transactions are accepted.
1353            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            // Ensure that the transactions are in the correct order.
1358            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        // Starting Balance = 20
1365        // Transfer_20 -> Balance = 20 - 20 = 0
1366        // Transfer_10 -> Balance = 0 - 10 = -10 (should be rejected)
1367        {
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 that the accepted and rejected transactions are correct.
1373            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        // Starting Balance = 20
1387        // Mint_20 -> Balance = 20 + 20
1388        // Transfer_30 -> Balance = 40 - 30 = 10
1389        // Transfer_20 -> Balance = 10 - 20 = -10 (should be rejected)
1390        // Transfer_10 -> Balance = 10 - 10 = 0
1391        {
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 that the accepted and rejected transactions are correct.
1397            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        // Sample a private key, view key, and address for the caller.
1420        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            // Initialize the vm.
1433            let vm = test_helpers::sample_vm_with_genesis_block(rng);
1434
1435            // Deploy a new program.
1436            let genesis =
1437                vm.block_store().get_block(&vm.block_store().get_block_hash(0).unwrap().unwrap()).unwrap().unwrap();
1438
1439            // Get the unspent records.
1440            let mut unspent_records = genesis
1441                .transitions()
1442                .cloned()
1443                .flat_map(Transition::into_records)
1444                .map(|(_, record)| record)
1445                .collect::<Vec<_>>();
1446
1447            // Create a program that will always cause a E::halt in the finalize execution.
1448            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            // Deploy the program.
1470            let deployment_transaction = vm.deploy(&caller_private_key, &program, credits, 10, None, rng).unwrap();
1471
1472            // Construct the deployment block.
1473            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            // Add the deployment block to the VM.
1484            vm.add_next_block(&deployment_block).unwrap();
1485
1486            // Construct a transaction that will cause a E::halt in the finalize execution.
1487            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            // Speculatively execute the transaction. Ensure that this call does not panic and returns a rejected transaction.
1492            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            // Ensure that the transaction is rejected.
1497            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        // Sample a private key.
1519        let private_key = test_helpers::sample_genesis_private_key(rng);
1520        let address = Address::try_from(&private_key).unwrap();
1521
1522        // Initialize the vm.
1523        let vm = test_helpers::sample_vm_with_genesis_block(rng);
1524
1525        // Deploy a new program.
1526        let genesis =
1527            vm.block_store().get_block(&vm.block_store().get_block_hash(0).unwrap().unwrap()).unwrap().unwrap();
1528
1529        // Get the unspent records.
1530        let mut unspent_records = genesis
1531            .transitions()
1532            .cloned()
1533            .flat_map(Transition::into_records)
1534            .map(|(_, record)| record)
1535            .collect::<Vec<_>>();
1536
1537        // Generate more records to use for the next block.
1538        let splits_block = generate_splits(&vm, &private_key, &genesis, &mut unspent_records, rng).unwrap();
1539
1540        // Add the splits block to the VM.
1541        vm.add_next_block(&splits_block).unwrap();
1542
1543        // Construct the deployment block.
1544        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            // Prepare the additional fee.
1572            let view_key = ViewKey::<CurrentNetwork>::try_from(private_key).unwrap();
1573            let credits = Some(unspent_records.pop().unwrap().decrypt(&view_key).unwrap());
1574
1575            // Deploy.
1576            let transaction = vm.deploy(&private_key, &program, credits, 10, None, rng).unwrap();
1577
1578            // Construct the new block.
1579            sample_next_block(&vm, &private_key, &[transaction], &splits_block, &mut unspent_records, rng).unwrap()
1580        };
1581
1582        // Add the deployment block to the VM.
1583        vm.add_next_block(&deployment_block).unwrap();
1584
1585        // Generate more records to use for the next block.
1586        let splits_block = generate_splits(&vm, &private_key, &deployment_block, &mut unspent_records, rng).unwrap();
1587
1588        // Add the splits block to the VM.
1589        vm.add_next_block(&splits_block).unwrap();
1590
1591        // Create an execution transaction, that will be rejected.
1592        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        // Construct the next block.
1596        let next_block =
1597            sample_next_block(&vm, &private_key, &[first], &splits_block, &mut unspent_records, rng).unwrap();
1598
1599        // Check that the transaction was rejected.
1600        assert!(next_block.transactions().iter().next().unwrap().is_rejected());
1601
1602        // Add the next block to the VM.
1603        vm.add_next_block(&next_block).unwrap();
1604
1605        // Check that the storage was not updated.
1606        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        // Create an execution transaction, that will be rejected.
1620        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        // Create an execution transaction, that will be accepted.
1624        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        // Construct the next block.
1628        let next_block =
1629            sample_next_block(&vm, &private_key, &[first, second], &next_block, &mut unspent_records, rng).unwrap();
1630
1631        // Check that the first transaction was rejected.
1632        assert!(next_block.transactions().iter().next().unwrap().is_rejected());
1633
1634        // Add the next block to the VM.
1635        vm.add_next_block(&next_block).unwrap();
1636
1637        // Check that the storage was updated correctly.
1638        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        // Sample a private key.
1652        let caller_private_key = test_helpers::sample_genesis_private_key(rng);
1653        let caller_address = Address::try_from(&caller_private_key).unwrap();
1654
1655        // Initialize the vm.
1656        let vm = test_helpers::sample_vm_with_genesis_block(rng);
1657
1658        // Deploy a new program.
1659        let genesis =
1660            vm.block_store().get_block(&vm.block_store().get_block_hash(0).unwrap().unwrap()).unwrap().unwrap();
1661
1662        // Get the unspent records.
1663        let mut unspent_records = genesis
1664            .transitions()
1665            .cloned()
1666            .flat_map(Transition::into_records)
1667            .map(|(_, record)| record)
1668            .collect::<Vec<_>>();
1669
1670        // Construct the deployment block.
1671        let (program_id, deployment_block) =
1672            new_program_deployment(&vm, &caller_private_key, &genesis, &mut unspent_records, rng).unwrap();
1673
1674        // Add the deployment block to the VM.
1675        vm.add_next_block(&deployment_block).unwrap();
1676
1677        // Generate more records to use for the next block.
1678        let splits_block =
1679            generate_splits(&vm, &caller_private_key, &deployment_block, &mut unspent_records, rng).unwrap();
1680
1681        // Add the splits block to the VM.
1682        vm.add_next_block(&splits_block).unwrap();
1683
1684        // Generate more records to use for the next block.
1685        let splits_block = generate_splits(&vm, &caller_private_key, &splits_block, &mut unspent_records, rng).unwrap();
1686
1687        // Add the splits block to the VM.
1688        vm.add_next_block(&splits_block).unwrap();
1689
1690        // Generate the transactions.
1691        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            // Abort the transaction if the block is full.
1698            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        // Construct the next block.
1706        let next_block =
1707            sample_next_block(&vm, &caller_private_key, &transactions, &splits_block, &mut unspent_records, rng)
1708                .unwrap();
1709
1710        // Ensure that the excess transactions were aborted.
1711        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}