/ synthesizer / src / vm / finalize.rs
finalize.rs
   1  // Copyright (c) 2019-2025 Alpha-Delta Network Inc.
   2  // This file is part of the deltavm 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  
   8  // http://www.apache.org/licenses/LICENSE-2.0
   9  
  10  // Unless required by applicable law or agreed to in writing, software
  11  // distributed under the License is distributed on an "AS IS" BASIS,
  12  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13  // See the License for the specific language governing permissions and
  14  // limitations under the License.
  15  
  16  use super::*;
  17  
  18  use deltavm_ledger_committee::{MAX_DELEGATORS, MIN_DELEGATOR_STAKE, MIN_VALIDATOR_SELF_STAKE};
  19  use deltavm_utilities::{cfg_sort_by_cached_key, defer, dev_eprintln};
  20  
  21  impl<N: Network, C: ConsensusStorage<N>> VM<N, C> {
  22      /// Speculates on the given list of transactions in the VM.
  23      /// This function aborts all transactions that are not are well-formed or unique.
  24      ///
  25      ///
  26      /// Returns the confirmed transactions, aborted transaction IDs,
  27      /// and finalize operations from pre-ratify and post-ratify.
  28      ///
  29      /// # Note
  30      /// This method is used to create a new block (including the genesis block).
  31      ///   - If `coinbase_reward = None`, then the `ratifications` will not be modified.
  32      ///   - If `coinbase_reward = Some(coinbase_reward)`, then the method will append a
  33      ///     `Ratify::BlockReward(block_reward)` and `Ratify::PuzzleReward(puzzle_reward)`
  34      ///     to the front of the `ratifications` list.
  35      ///
  36      /// # Panics
  37      /// This function panics if called from an async context.
  38      #[inline]
  39      #[allow(clippy::too_many_arguments)]
  40      pub fn speculate<'a, R: Rng + CryptoRng>(
  41          &self,
  42          state: FinalizeGlobalState,
  43          time_since_last_block: i64, // TODO (raychu86): Consider moving this value into `FinalizeGlobalState`.
  44          coinbase_reward: Option<u64>,
  45          candidate_ratifications: Vec<Ratify<N>>,
  46          candidate_solutions: &Solutions<N>,
  47          candidate_transactions: impl ExactSizeIterator<Item = &'a Transaction<N>>,
  48          rng: &mut R,
  49      ) -> Result<(Ratifications<N>, Transactions<N>, Vec<N::TransactionID>, Vec<FinalizeOperation<N>>)> {
  50          let timer = timer!("VM::speculate");
  51  
  52          // Collect the candidate transactions into a vector.
  53          let candidate_transactions: Vec<_> = candidate_transactions.collect::<Vec<_>>();
  54          let candidate_transaction_ids: Vec<_> = candidate_transactions.iter().map(|tx| tx.id()).collect();
  55  
  56          // Determine if the vm is currently processing the genesis block.
  57          let is_genesis =
  58              self.block_store().find_block_height_from_state_root(self.block_store().current_state_root())?.is_none();
  59          // If the transactions are not part of the genesis block, ensure each transaction is well-formed and unique. Abort any transactions that are not.
  60          let (verified_transactions, verification_aborted_transactions) = match is_genesis {
  61              // If the current state root does not exist in the block store, then the genesis block has not been introduced yet.
  62              true => (candidate_transactions, vec![]),
  63              // Verify transactions for all non-genesis cases.
  64              false => self.prepare_for_speculate(&candidate_transactions, rng)?,
  65          };
  66  
  67          // Performs a **dry-run** over the list of ratifications, solutions, and transactions.
  68          let (ratifications, confirmed_transactions, speculation_aborted_transactions, ratified_finalize_operations) =
  69              self.atomic_speculate(
  70                  state,
  71                  time_since_last_block,
  72                  coinbase_reward,
  73                  candidate_ratifications,
  74                  candidate_solutions.clone(),
  75                  verified_transactions.into_iter().cloned().collect(),
  76              )?;
  77  
  78          // Get the aborted transaction ids.
  79          let verification_aborted_transaction_ids = verification_aborted_transactions.iter().map(|(tx, e)| (tx.id(), e));
  80          let speculation_aborted_transaction_ids = speculation_aborted_transactions.iter().map(|(tx, e)| (tx.id(), e));
  81          let unordered_aborted_transaction_ids: IndexMap<N::TransactionID, &String> =
  82              verification_aborted_transaction_ids.chain(speculation_aborted_transaction_ids).collect();
  83  
  84          // Filter and order the aborted transaction ids according to candidate_transactions
  85          let aborted_transaction_ids: Vec<_> = candidate_transaction_ids
  86              .into_iter()
  87              .filter_map(|tx_id| {
  88                  unordered_aborted_transaction_ids.get(&tx_id).map(|error| {
  89                      warn!("Speculation safely aborted a transaction - {error} ({tx_id})");
  90                      tx_id
  91                  })
  92              })
  93              .collect();
  94  
  95          finish!(timer, "Finished dry-run of the transactions");
  96  
  97          // Return the ratifications, confirmed transactions, aborted transaction IDs, and ratified finalize operations.
  98          Ok((
  99              ratifications,
 100              confirmed_transactions.into_iter().collect(),
 101              aborted_transaction_ids,
 102              ratified_finalize_operations,
 103          ))
 104      }
 105  
 106      /// Checks the speculation on the given transactions in the VM.
 107      /// This function also ensure that the given transactions are well-formed and unique.
 108      ///
 109      /// Returns the finalize operations from pre-ratify and post-ratify.
 110      ///
 111      /// # Panics
 112      /// This function panics if called from an async context.
 113      #[inline]
 114      pub fn check_speculate<R: Rng + CryptoRng>(
 115          &self,
 116          state: FinalizeGlobalState,
 117          time_since_last_block: i64,
 118          ratifications: &Ratifications<N>,
 119          solutions: &Solutions<N>,
 120          transactions: &Transactions<N>,
 121          rng: &mut R,
 122      ) -> Result<Vec<FinalizeOperation<N>>> {
 123          let timer = timer!("VM::check_speculate");
 124  
 125          // Retrieve the transactions and their rejected IDs.
 126          let transactions_and_rejected_ids = cfg_iter!(transactions)
 127              .map(|transaction| transaction.to_rejected_id().map(|rejected_id| (transaction.deref(), rejected_id)))
 128              .collect::<Result<Vec<_>>>()?;
 129          // Ensure each transaction is well-formed and unique.
 130          // NOTE: We perform the transaction checks here prior to `atomic_speculate` because we must
 131          // ensure that the `Fee` transactions are valid. We can't unify the transaction checks in `atomic_speculate`
 132          // because we run speculation on the unconfirmed variant of the transactions.
 133          self.check_transactions(&transactions_and_rejected_ids, rng)?;
 134  
 135          // Reconstruct the candidate ratifications to verify the speculation.
 136          let candidate_ratifications = ratifications.iter().cloned().collect::<Vec<_>>();
 137          // Reconstruct the unconfirmed transactions to verify the speculation.
 138          let candidate_transactions =
 139              transactions.iter().map(|confirmed| confirmed.to_unconfirmed_transaction()).collect::<Result<Vec<_>>>()?;
 140  
 141          // Performs a **dry-run** over the list of ratifications, solutions, and transactions.
 142          let (speculate_ratifications, confirmed_transactions, aborted_transactions, ratified_finalize_operations) =
 143              self.atomic_speculate(
 144                  state,
 145                  time_since_last_block,
 146                  None,
 147                  candidate_ratifications,
 148                  solutions.clone(),
 149                  candidate_transactions,
 150              )?;
 151  
 152          // Ensure the ratifications after speculation match.
 153          if ratifications != &speculate_ratifications {
 154              bail!("The ratifications after speculation do not match the ratifications in the block");
 155          }
 156          // Ensure the transactions after speculation match.
 157          if transactions != &confirmed_transactions.into_iter().collect() {
 158              bail!("The transactions after speculation do not match the transactions in the block");
 159          }
 160          // Ensure there are no aborted transaction IDs from this speculation.
 161          // Note: There should be no aborted transactions, because we are checking a block,
 162          // where any aborted transactions should be in the aborted transaction ID list, not in transactions.
 163          ensure!(aborted_transactions.is_empty(), "Aborted transactions found in the block (from speculation)");
 164  
 165          finish!(timer, "Finished dry-run of the transactions");
 166  
 167          // Return the ratified finalize operations.
 168          Ok(ratified_finalize_operations)
 169      }
 170  
 171      /// Finalizes the given transactions into the VM.
 172      ///
 173      /// Returns the finalize operations from pre-ratify and post-ratify.
 174      #[inline]
 175      pub fn finalize(
 176          &self,
 177          state: FinalizeGlobalState,
 178          ratifications: &Ratifications<N>,
 179          solutions: &Solutions<N>,
 180          transactions: &Transactions<N>,
 181      ) -> Result<Vec<FinalizeOperation<N>>> {
 182          let timer = timer!("VM::finalize");
 183  
 184          // Performs a **real-run** of finalize over the list of ratifications, solutions, and transactions.
 185          let ratified_finalize_operations = self.atomic_finalize(state, ratifications, solutions, transactions)?;
 186  
 187          finish!(timer, "Finished real-run of finalize");
 188          Ok(ratified_finalize_operations)
 189      }
 190  }
 191  
 192  impl<N: Network, C: ConsensusStorage<N>> VM<N, C> {
 193      /// The maximum number of confirmed transactions allowed in a block.
 194      #[cfg(not(any(test, feature = "test")))]
 195      pub const MAXIMUM_CONFIRMED_TRANSACTIONS: usize = Transactions::<N>::MAX_TRANSACTIONS;
 196      /// The maximum number of confirmed transactions allowed in a block.
 197      /// This is deliberately set to a low value (8) for testing purposes only.
 198      #[cfg(any(test, feature = "test"))]
 199      pub const MAXIMUM_CONFIRMED_TRANSACTIONS: usize = 8;
 200  
 201      /// Performs atomic speculation over a list of transactions.
 202      ///
 203      /// Returns the ratifications, confirmed transactions, aborted transactions,
 204      /// and finalize operations from pre-ratify and post-ratify.
 205      ///
 206      /// # Note
 207      /// This method is used by `VM::speculate` and `VM::check_speculate`.
 208      ///   - If `coinbase_reward = None`, then the `ratifications` will not be modified.
 209      ///   - If `coinbase_reward = Some(coinbase_reward)`, then the method will append a
 210      ///     `Ratify::BlockReward(block_reward)` and `Ratify::PuzzleReward(puzzle_reward)`
 211      ///     to the front of the `ratifications` list.
 212      ///
 213      /// # Panics
 214      /// This function panics if called from an async context.
 215      fn atomic_speculate(
 216          &self,
 217          state: FinalizeGlobalState,
 218          time_since_last_block: i64,
 219          coinbase_reward: Option<u64>,
 220          ratifications: Vec<Ratify<N>>,
 221          solutions: Solutions<N>,
 222          transactions: Vec<Transaction<N>>,
 223      ) -> Result<(
 224          Ratifications<N>,
 225          Vec<ConfirmedTransaction<N>>,
 226          Vec<(Transaction<N>, String)>,
 227          Vec<FinalizeOperation<N>>,
 228      )> {
 229          let sequential_op = SequentialOperation::AtomicSpeculate(
 230              state,
 231              time_since_last_block,
 232              coinbase_reward,
 233              ratifications,
 234              solutions,
 235              transactions,
 236          );
 237          let Some(SequentialOperationResult::AtomicSpeculate(ret)) = self.run_sequential_operation(sequential_op) else {
 238              bail!("Already shutting down");
 239          };
 240  
 241          ret
 242      }
 243  
 244      /// Internal function called when invoking [`Self::atomic_speculate`].
 245      ///
 246      /// # Note
 247      /// This function must only be called from the sequential operation thread.
 248      ///
 249      /// # Panics
 250      /// This function panics if not called from the sequential operation thread.
 251      pub(crate) fn atomic_speculate_inner(
 252          &self,
 253          state: FinalizeGlobalState,
 254          time_since_last_block: i64,
 255          coinbase_reward: Option<u64>,
 256          ratifications: Vec<Ratify<N>>,
 257          solutions: Solutions<N>,
 258          transactions: Vec<Transaction<N>>,
 259      ) -> Result<(
 260          Ratifications<N>,
 261          Vec<ConfirmedTransaction<N>>,
 262          Vec<(Transaction<N>, String)>,
 263          Vec<FinalizeOperation<N>>,
 264      )> {
 265          self.ensure_sequential_processing();
 266  
 267          let timer = timer!("VM::atomic_speculate");
 268  
 269          // Retrieve the number of solutions.
 270          let num_solutions = solutions.len();
 271          // Retrieve the number of transactions.
 272          let num_transactions = transactions.len();
 273          // Determine the maximum number of aborted solutions allowed in a block.
 274          let max_aborted_solutions = Solutions::<N>::max_aborted_solutions()?;
 275          // Determine the maximum number of aborted transactions allowed in a block.
 276          let max_aborted_transactions = Transactions::<N>::max_aborted_transactions()?;
 277  
 278          // Perform the finalize operation on the preset finalize mode.
 279          atomic_finalize!(self.finalize_store(), FinalizeMode::DryRun, {
 280              // Ensure the number of solutions does not exceed the maximum.
 281              if num_solutions > max_aborted_solutions {
 282                  // Note: This will abort the entire atomic batch.
 283                  return Err(format!("Too many solutions in the block - {num_solutions}",));
 284              }
 285  
 286              // Ensure the number of transactions does not exceed the maximum.
 287              if num_transactions > max_aborted_transactions {
 288                  // Note: This will abort the entire atomic batch.
 289                  return Err(format!(
 290                      "Too many transactions in the block - {num_transactions} (max: {max_aborted_transactions})",
 291                  ));
 292              }
 293  
 294              // Initialize an iterator for ratifications before finalize.
 295              let pre_ratifications = ratifications.iter().filter(|r| match r {
 296                  Ratify::Genesis(_, _, _) => true,
 297                  Ratify::BlockReward(..) | Ratify::PuzzleReward(..) => false,
 298              });
 299              // Initialize an iterator for ratifications after finalize.
 300              let post_ratifications = ratifications.iter().filter(|r| match r {
 301                  Ratify::Genesis(_, _, _) => false,
 302                  Ratify::BlockReward(..) | Ratify::PuzzleReward(..) => true,
 303              });
 304  
 305              // Initialize a list of finalize operations.
 306              let mut ratified_finalize_operations = Vec::new();
 307  
 308              // Retrieve the finalize store.
 309              let store = self.finalize_store();
 310  
 311              /* Perform the ratifications before finalize. */
 312  
 313              match Self::atomic_pre_ratify(store, state, pre_ratifications) {
 314                  // Store the finalize operations from the post-ratify.
 315                  Ok(operations) => ratified_finalize_operations.extend(operations),
 316                  // Note: This will abort the entire atomic batch.
 317                  Err(e) => return Err(format!("Failed to pre-ratify - {e}")),
 318              }
 319  
 320              /* Perform the atomic finalize over the transactions. */
 321  
 322              // Acquire the write lock on the process.
 323              // Note: Due to the highly-sensitive nature of processing all `finalize` calls,
 324              // we choose to acquire the write lock for the entire duration of this atomic batch.
 325              let process = self.process.write();
 326  
 327              // Revert any unstaged stacks, when the function returns.
 328              // Note. This function does not call `commit_stacks` so the staged stacks will always be reverted
 329              //  regardless of whether the function succeeds or fails.
 330              defer! {
 331                  process.revert_stacks();
 332              }
 333  
 334              // Initialize a list of the confirmed transactions.
 335              let mut confirmed = Vec::with_capacity(num_transactions);
 336              // Initialize a list of the aborted transactions.
 337              let mut aborted = Vec::new();
 338              // Initialize a counter for the confirmed transaction index.
 339              let mut counter = 0u32;
 340              // Initialize a list of created transition IDs.
 341              let mut transition_ids: IndexSet<N::TransitionID> = IndexSet::new();
 342              // Initialize a list of spent input IDs.
 343              let mut input_ids: IndexSet<Field<N>> = IndexSet::new();
 344              // Initialize a list of created output IDs.
 345              let mut output_ids: IndexSet<Field<N>> = IndexSet::new();
 346              // Initialize the list of created transition public keys.
 347              let mut tpks: IndexSet<Group<N>> = IndexSet::new();
 348              // Initialize the list of deployment payers.
 349              let mut deployment_payers: IndexSet<Address<N>> = IndexSet::new();
 350              // Initialize a list of the successful deployments.
 351              let mut deployments = IndexSet::new();
 352  
 353              // Finalize the transactions.
 354              'outer: for transaction in transactions {
 355                  // Ensure the number of confirmed transactions does not exceed the maximum.
 356                  // Upon reaching the maximum number of confirmed transactions, all remaining transactions are aborted.
 357                  if confirmed.len() >= Self::MAXIMUM_CONFIRMED_TRANSACTIONS {
 358                      // Store the aborted transaction.
 359                      aborted.push((transaction.clone(), "Exceeds block transaction limit".to_string()));
 360                      // Continue to the next transaction.
 361                      continue 'outer;
 362                  }
 363  
 364                  // Determine if the transaction should be aborted.
 365                  if let Some(reason) = self.should_abort_transaction(
 366                      &transaction,
 367                      &transition_ids,
 368                      &input_ids,
 369                      &output_ids,
 370                      &tpks,
 371                      &deployment_payers,
 372                      &deployments,
 373                  ) {
 374                      // Store the aborted transaction.
 375                      aborted.push((transaction.clone(), reason));
 376                      // Continue to the next transaction.
 377                      continue 'outer;
 378                  }
 379  
 380                  // Process the transaction in an isolated atomic batch.
 381                  // - If the transaction succeeds, the finalize operations are stored.
 382                  // - If the transaction fails, the atomic batch is aborted and no finalize operations are stored.
 383                  let outcome = match &transaction {
 384                      // The finalize operation here involves appending the 'stack',
 385                      // and adding the program to the finalize tree.
 386                      Transaction::Deploy(_, _, program_owner, deployment, fee) => {
 387                          // Define the closure for processing a rejected deployment.
 388                          let process_rejected_deployment =
 389                              |fee: &Fee<N>,
 390                               deployment: Deployment<N>|
 391                               -> Result<Result<ConfirmedTransaction<N>, String>> {
 392                                  process
 393                                      .finalize_fee(state, store, fee)
 394                                      .and_then(|finalize| {
 395                                          Transaction::from_fee(fee.clone()).map(|fee_tx| (fee_tx, finalize))
 396                                      })
 397                                      .map(|(fee_tx, finalize)| {
 398                                          let rejected = Rejected::new_deployment(*program_owner, deployment);
 399                                          ConfirmedTransaction::rejected_deploy(counter, fee_tx, rejected, finalize)
 400                                              .map_err(|e| e.to_string())
 401                                      })
 402                              };
 403  
 404                          // Check if the program has already been deployed in this block.
 405                          match deployments.contains(deployment.program_id()) {
 406                              // If the program has already been deployed, construct the rejected deploy transaction.
 407                              true => match process_rejected_deployment(fee, *deployment.clone()) {
 408                                  Ok(result) => result,
 409                                  Err(error) => {
 410                                      // Note: On failure, skip this transaction, and continue speculation.
 411                                      dev_eprintln!("Failed to finalize the fee in a rejected deploy - {error}");
 412                                      // Store the aborted transaction.
 413                                      aborted.push((transaction.clone(), error.to_string()));
 414                                      // Continue to the next transaction.
 415                                      continue 'outer;
 416                                  }
 417                              },
 418                              // If the program has not yet been deployed, attempt to deploy it.
 419                              false => match process.finalize_deployment(state, store, deployment, fee) {
 420                                  // Construct the accepted deploy transaction.
 421                                  Ok((stack, finalize)) => {
 422                                      // Add the stack to the process with the option to be reverted.
 423                                      process.stage_stack(stack);
 424                                      ConfirmedTransaction::accepted_deploy(counter, transaction.clone(), finalize)
 425                                          .map_err(|e| e.to_string())
 426                                  }
 427                                  // Construct the rejected deploy transaction.
 428                                  Err(error) => {
 429                                      trace!("Failed to finalize deploy tx {} - {error}", transaction.id());
 430                                      match process_rejected_deployment(fee, *deployment.clone()) {
 431                                          Ok(result) => result,
 432                                          Err(error) => {
 433                                              // Note: On failure, skip this transaction, and continue speculation.
 434                                              dev_eprintln!("Failed to finalize the fee in a rejected deploy - {error}");
 435                                              // Store the aborted transaction.
 436                                              aborted.push((transaction.clone(), error.to_string()));
 437                                              // Continue to the next transaction.
 438                                              continue 'outer;
 439                                          }
 440                                      }
 441                                  }
 442                              },
 443                          }
 444                      }
 445                      // The finalize operation here involves calling 'update_key_value',
 446                      // and update the respective leaves of the finalize tree.
 447                      Transaction::Execute(_, _, execution, fee) => {
 448                          // Determine if the transaction is safe for execution, and proceed to execute it.
 449                          match Self::prepare_for_execution(state, store, execution)
 450                              .and_then(|_| process.finalize_execution(state, store, execution, fee.as_ref()))
 451                          {
 452                              // Construct the accepted execute transaction.
 453                              Ok(finalize) => {
 454                                  ConfirmedTransaction::accepted_execute(counter, transaction.clone(), finalize)
 455                                      .map_err(|e| e.to_string())
 456                              }
 457                              // Construct the rejected execute transaction.
 458                              Err(error) => {
 459                                  trace!("Failed to finalize execute tx {} - {error}", transaction.id());
 460                                  match fee {
 461                                      // Finalize the fee, to ensure it is valid.
 462                                      Some(fee) => {
 463                                          match process.finalize_fee(state, store, fee).and_then(|finalize| {
 464                                              Transaction::from_fee(fee.clone()).map(|fee_tx| (fee_tx, finalize))
 465                                          }) {
 466                                              Ok((fee_tx, finalize)) => {
 467                                                  // Construct the rejected execution.
 468                                                  let rejected = Rejected::new_execution(*execution.clone());
 469                                                  // Construct the rejected execute transaction.
 470                                                  ConfirmedTransaction::rejected_execute(
 471                                                      counter, fee_tx, rejected, finalize,
 472                                                  )
 473                                                  .map_err(|e| e.to_string())
 474                                              }
 475                                              Err(error) => {
 476                                                  // Note: On failure, skip this transaction, and continue speculation.
 477                                                  dev_eprintln!(
 478                                                      "Failed to finalize the fee in a rejected execute - {error}"
 479                                                  );
 480                                                  // Store the aborted transaction.
 481                                                  aborted.push((transaction.clone(), error.to_string()));
 482                                                  // Continue to the next transaction.
 483                                                  continue 'outer;
 484                                              }
 485                                          }
 486                                      }
 487  
 488                                      // This is a foundational bug - the caller is violating protocol rules.
 489                                      // It is possible that a `credits.delta/split` transaction has no fee. However, it
 490                                      // is a simple transition without finalize operations and should not fail here.
 491                                      // If a `credits.delta/upgrade` transaction has no fee and fails, we simply abort it.
 492                                      // Note: This will abort the entire atomic batch.
 493                                      None => {
 494                                          // Abort the upgrade transaction.
 495                                          if transaction.contains_upgrade() && execution.len() == 1 {
 496                                              aborted.push((
 497                                                  transaction.clone(),
 498                                                  "Failed to finalize a `credits.delta/upgrade` call with no fee"
 499                                                      .to_string(),
 500                                              ));
 501                                              // Continue to the next transaction.
 502                                              continue 'outer;
 503                                          }
 504                                          Err("Rejected execute transaction has no fee".to_string())
 505                                      }
 506                                  }
 507                              }
 508                          }
 509                      }
 510                      // There are no finalize operations here.
 511                      // Note: This will abort the entire atomic batch.
 512                      Transaction::Fee(..) => Err("Cannot speculate on a fee transaction".to_string()),
 513                  };
 514                  lap!(timer, "Speculated on transaction '{}'", transaction.id());
 515  
 516                  match outcome {
 517                      // If the transaction succeeded, store it and continue to the next transaction.
 518                      Ok(confirmed_transaction) => {
 519                          // Add the transition IDs to the set of produced transition IDs.
 520                          transition_ids.extend(confirmed_transaction.transaction().transition_ids());
 521                          // Add the input IDs to the set of spent input IDs.
 522                          input_ids.extend(confirmed_transaction.transaction().input_ids());
 523                          // Add the output IDs to the set of produced output IDs.
 524                          output_ids.extend(confirmed_transaction.transaction().output_ids());
 525                          // Add the transition public keys to the set of produced transition public keys.
 526                          tpks.extend(confirmed_transaction.transaction().transition_public_keys());
 527                          // Add the program owner to the set of deployment payers and the program ID to the set of deployments.
 528                          if let Transaction::Deploy(_, _, _, deployment, fee) = confirmed_transaction.transaction() {
 529                              fee.payer().map(|payer| deployment_payers.insert(payer));
 530                              deployments.insert(*deployment.program_id());
 531                          }
 532                          // Store the confirmed transaction.
 533                          confirmed.push(confirmed_transaction);
 534                          // Increment the transaction index counter.
 535                          counter = counter.saturating_add(1);
 536                      }
 537                      // If the transaction failed, abort the entire batch.
 538                      Err(error) => {
 539                          error!("Critical bug in speculate: {error}\n\n{transaction}");
 540                          dev_eprintln!("Critical bug in speculate: {error}\n\n{transaction}");
 541                          // Note: This will abort the entire atomic batch.
 542                          return Err(format!("Failed to speculate on transaction - {error}"));
 543                      }
 544                  }
 545              }
 546  
 547              // Ensure all transactions were processed.
 548              if confirmed.len() + aborted.len() != num_transactions {
 549                  // Note: This will abort the entire atomic batch.
 550                  return Err("Not all transactions were processed in 'VM::atomic_speculate'".to_string());
 551              }
 552  
 553              /* Perform the ratifications after finalize. */
 554  
 555              // Prepare the reward ratifications, if any.
 556              let reward_ratifications = match coinbase_reward {
 557                  // If the coinbase reward is `None`, then there are no reward ratifications.
 558                  None => vec![],
 559                  // If the coinbase reward is `Some(coinbase_reward)`, then we must compute the reward ratifications.
 560                  Some(coinbase_reward) => {
 561                      // Calculate the transaction fees.
 562                      let Ok(transaction_fees) =
 563                          confirmed.iter().map(|tx| Ok(*tx.priority_fee_amount()?)).sum::<Result<u64>>()
 564                      else {
 565                          // Note: This will abort the entire atomic batch.
 566                          return Err("Failed to calculate the transaction fees during speculation".to_string());
 567                      };
 568  
 569                      // Compute the block reward.
 570                      let block_reward = deltavm_ledger_block::block_reward::<N>(
 571                          state.block_height(),
 572                          N::STARTING_SUPPLY,
 573                          N::BLOCK_TIME,
 574                          time_since_last_block,
 575                          coinbase_reward,
 576                          transaction_fees,
 577                      )
 578                      .map_err(|e| format!("Failed to compute the block reward - {e}"))?;
 579                      // Compute the puzzle reward.
 580                      let puzzle_reward = deltavm_ledger_block::puzzle_reward(coinbase_reward);
 581  
 582                      // Output the reward ratifications.
 583                      vec![Ratify::BlockReward(block_reward), Ratify::PuzzleReward(puzzle_reward)]
 584                  }
 585              };
 586  
 587              // Update the post-ratifications iterator.
 588              let post_ratifications = reward_ratifications.iter().chain(post_ratifications);
 589  
 590              // Process the post-ratifications.
 591              match Self::atomic_post_ratify::<false>(&self.puzzle, store, state, post_ratifications, &solutions) {
 592                  // Store the finalize operations from the post-ratify.
 593                  Ok(operations) => ratified_finalize_operations.extend(operations),
 594                  // Note: This will abort the entire atomic batch.
 595                  Err(e) => return Err(format!("Failed to post-ratify - {e}")),
 596              }
 597  
 598              /* Construct the ratifications after speculation. */
 599  
 600              let Ok(ratifications) =
 601                  Ratifications::try_from_iter(reward_ratifications.into_iter().chain(ratifications.into_iter()))
 602              else {
 603                  // Note: This will abort the entire atomic batch.
 604                  return Err("Failed to construct the ratifications after speculation".to_string());
 605              };
 606  
 607              finish!(timer);
 608  
 609              // On return, 'atomic_finalize!' will abort the batch, and return the ratifications,
 610              // confirmed & aborted transactions, and finalize operations from pre-ratify and post-ratify.
 611              Ok((ratifications, confirmed, aborted, ratified_finalize_operations))
 612          })
 613      }
 614  
 615      /// Performs atomic finalization over a list of transactions.
 616      ///
 617      /// Returns the finalize operations from pre-ratify and post-ratify.
 618      #[inline]
 619      fn atomic_finalize(
 620          &self,
 621          state: FinalizeGlobalState,
 622          ratifications: &Ratifications<N>,
 623          solutions: &Solutions<N>,
 624          transactions: &Transactions<N>,
 625      ) -> Result<Vec<FinalizeOperation<N>>> {
 626          // The tests may run this method ad-hoc, outside of the context of add_next_block.
 627          #[cfg(not(test))]
 628          self.ensure_sequential_processing();
 629  
 630          let timer = timer!("VM::atomic_finalize");
 631  
 632          // Perform the finalize operation on the preset finalize mode.
 633          atomic_finalize!(self.finalize_store(), FinalizeMode::RealRun, {
 634              // Initialize an iterator for ratifications before finalize.
 635              let pre_ratifications = ratifications.iter().filter(|r| match r {
 636                  Ratify::Genesis(_, _, _) => true,
 637                  Ratify::BlockReward(..) | Ratify::PuzzleReward(..) => false,
 638              });
 639              // Initialize an iterator for ratifications after finalize.
 640              let post_ratifications = ratifications.iter().filter(|r| match r {
 641                  Ratify::Genesis(_, _, _) => false,
 642                  Ratify::BlockReward(..) | Ratify::PuzzleReward(..) => true,
 643              });
 644  
 645              // Initialize a list of finalize operations.
 646              let mut ratified_finalize_operations = Vec::new();
 647  
 648              // Retrieve the finalize store.
 649              let store = self.finalize_store();
 650  
 651              /* Perform the ratifications before finalize. */
 652  
 653              match Self::atomic_pre_ratify(store, state, pre_ratifications) {
 654                  // Store the finalize operations from the post-ratify.
 655                  Ok(operations) => ratified_finalize_operations.extend(operations),
 656                  // Note: This will abort the entire atomic batch.
 657                  Err(e) => return Err(format!("Failed to pre-ratify - {e}")),
 658              }
 659  
 660              /* Perform the atomic finalize over the transactions. */
 661  
 662              // Acquire the write lock on the process.
 663              // Note: Due to the highly-sensitive nature of processing all `finalize` calls,
 664              // we choose to acquire the write lock for the entire duration of this atomic batch.
 665              let process = self.process.write();
 666  
 667              // Revert any unstaged stacks, when the function returns.
 668              // Note. `commit_stacks` is called at the bottom of this function after successful finalization.
 669              //  The staged stacks are only reverted if the function returns an error.
 670              defer! {
 671                  process.revert_stacks();
 672              }
 673  
 674              // Finalize the transactions.
 675              for (index, transaction) in transactions.iter().enumerate() {
 676                  // Convert the transaction index to a u32.
 677                  // Note: On failure, this will abort the entire atomic batch.
 678                  let index = u32::try_from(index).map_err(|_| "Failed to convert transaction index".to_string())?;
 679                  // Ensure the index matches the expected index.
 680                  if index != transaction.index() {
 681                      // Note: This will abort the entire atomic batch.
 682                      return Err(format!("Mismatch in {} transaction index", transaction.variant()));
 683                  }
 684                  // Process the transaction in an isolated atomic batch.
 685                  // - If the transaction succeeds, the finalize operations are stored.
 686                  // - If the transaction fails, the atomic batch is aborted and no finalize operations are stored.
 687                  let outcome: Result<(), String> = match transaction {
 688                      ConfirmedTransaction::AcceptedDeploy(_, transaction, finalize) => {
 689                          // Extract the deployment and fee from the transaction.
 690                          let (deployment, fee) = match transaction {
 691                              Transaction::Deploy(_, _, _, deployment, fee) => (deployment, fee),
 692                              // Note: This will abort the entire atomic batch.
 693                              _ => return Err("Expected deploy transaction".to_string()),
 694                          };
 695                          // The finalize operation here involves appending the 'stack', and adding the program to the finalize tree.
 696                          match process.finalize_deployment(state, store, deployment, fee) {
 697                              // Ensure the finalize operations match the expected.
 698                              Ok((stack, finalize_operations)) => match finalize == &finalize_operations {
 699                                  // Add the stack to the process with the option to be reverted.
 700                                  true => process.stage_stack(stack),
 701                                  // Note: This will abort the entire atomic batch.
 702                                  false => {
 703                                      return Err(format!(
 704                                          "Mismatch in finalize operations for an accepted deploy - (found: {finalize_operations:?}, expected: {finalize:?})"
 705                                      ));
 706                                  }
 707                              },
 708                              // Note: This will abort the entire atomic batch.
 709                              Err(error) => {
 710                                  return Err(format!("Failed to finalize an accepted deploy transaction - {error}"));
 711                              }
 712                          };
 713                          Ok(())
 714                      }
 715                      ConfirmedTransaction::AcceptedExecute(_, transaction, finalize) => {
 716                          // Extract the execution and fee from the transaction.
 717                          let (execution, fee) = match transaction {
 718                              Transaction::Execute(_, _, execution, fee) => (execution, fee),
 719                              // Note: This will abort the entire atomic batch.
 720                              _ => return Err("Expected execute transaction".to_string()),
 721                          };
 722                          // The finalize operation here involves calling 'update_key_value',
 723                          // and update the respective leaves of the finalize tree.
 724                          match process.finalize_execution(state, store, execution, fee.as_ref()) {
 725                              // Ensure the finalize operations match the expected.
 726                              Ok(finalize_operations) => {
 727                                  if finalize != &finalize_operations {
 728                                      // Note: This will abort the entire atomic batch.
 729                                      return Err(format!(
 730                                          "Mismatch in finalize operations for an accepted execute - (found: {finalize_operations:?}, expected: {finalize:?})"
 731                                      ));
 732                                  }
 733                              }
 734                              // Note: This will abort the entire atomic batch.
 735                              Err(error) => {
 736                                  return Err(format!("Failed to finalize an accepted execute transaction - {error}"));
 737                              }
 738                          }
 739                          Ok(())
 740                      }
 741                      ConfirmedTransaction::RejectedDeploy(_, Transaction::Fee(_, fee), rejected, finalize) => {
 742                          // Extract the rejected deployment.
 743                          let Some(deployment) = rejected.deployment() else {
 744                              // Note: This will abort the entire atomic batch.
 745                              return Err("Expected rejected deployment".to_string());
 746                          };
 747                          // Compute the expected deployment ID.
 748                          let Ok(expected_deployment_id) = deployment.to_deployment_id() else {
 749                              // Note: This will abort the entire atomic batch.
 750                              return Err("Failed to compute the deployment ID for a rejected deployment".to_string());
 751                          };
 752                          // Retrieve the candidate deployment ID.
 753                          let Ok(candidate_deployment_id) = fee.deployment_or_execution_id() else {
 754                              // Note: This will abort the entire atomic batch.
 755                              return Err("Failed to retrieve the deployment ID from the fee".to_string());
 756                          };
 757                          // Ensure this fee corresponds to the deployment.
 758                          if candidate_deployment_id != expected_deployment_id {
 759                              // Note: This will abort the entire atomic batch.
 760                              return Err("Mismatch in fee for a rejected deploy transaction".to_string());
 761                          }
 762                          // Lastly, finalize the fee.
 763                          match process.finalize_fee(state, store, fee) {
 764                              // Ensure the finalize operations match the expected.
 765                              Ok(finalize_operations) => {
 766                                  if finalize != &finalize_operations {
 767                                      // Note: This will abort the entire atomic batch.
 768                                      return Err(format!(
 769                                          "Mismatch in finalize operations for a rejected deploy - (found: {finalize_operations:?}, expected: {finalize:?})"
 770                                      ));
 771                                  }
 772                              }
 773                              // Note: This will abort the entire atomic batch.
 774                              Err(_e) => {
 775                                  return Err("Failed to finalize the fee in a rejected deploy transaction".to_string());
 776                              }
 777                          }
 778                          Ok(())
 779                      }
 780                      ConfirmedTransaction::RejectedExecute(_, Transaction::Fee(_, fee), rejected, finalize) => {
 781                          // Extract the rejected execution.
 782                          let Some(execution) = rejected.execution() else {
 783                              // Note: This will abort the entire atomic batch.
 784                              return Err("Expected rejected execution".to_string());
 785                          };
 786                          // Compute the expected execution ID.
 787                          let Ok(expected_execution_id) = execution.to_execution_id() else {
 788                              // Note: This will abort the entire atomic batch.
 789                              return Err("Failed to compute the execution ID for a rejected execution".to_string());
 790                          };
 791                          // Retrieve the candidate execution ID.
 792                          let Ok(candidate_execution_id) = fee.deployment_or_execution_id() else {
 793                              // Note: This will abort the entire atomic batch.
 794                              return Err("Failed to retrieve the execution ID from the fee".to_string());
 795                          };
 796                          // Ensure this fee corresponds to the execution.
 797                          if candidate_execution_id != expected_execution_id {
 798                              // Note: This will abort the entire atomic batch.
 799                              return Err("Mismatch in fee for a rejected execute transaction".to_string());
 800                          }
 801                          // Lastly, finalize the fee.
 802                          match process.finalize_fee(state, store, fee) {
 803                              // Ensure the finalize operations match the expected.
 804                              Ok(finalize_operations) => {
 805                                  if finalize != &finalize_operations {
 806                                      // Note: This will abort the entire atomic batch.
 807                                      return Err(format!(
 808                                          "Mismatch in finalize operations for a rejected execute - (found: {finalize_operations:?}, expected: {finalize:?})"
 809                                      ));
 810                                  }
 811                              }
 812                              // Note: This will abort the entire atomic batch.
 813                              Err(_e) => {
 814                                  return Err("Failed to finalize the fee in a rejected execute transaction".to_string());
 815                              }
 816                          }
 817                          Ok(())
 818                      }
 819                      // Note: This will abort the entire atomic batch.
 820                      _ => return Err("Invalid confirmed transaction type".to_string()),
 821                  };
 822                  lap!(timer, "Finalizing transaction {}", transaction.id());
 823  
 824                  match outcome {
 825                      // If the transaction succeeded to finalize, continue to the next transaction.
 826                      Ok(()) => (),
 827                      // If the transaction failed to finalize, abort and continue to the next transaction.
 828                      Err(error) => {
 829                          error!("Critical bug in finalize: {error}\n\n{transaction}");
 830                          dev_eprintln!("Critical bug in finalize: {error}\n\n{transaction}");
 831                          // Note: This will abort the entire atomic batch.
 832                          return Err(format!("Failed to finalize on transaction - {error}"));
 833                      }
 834                  }
 835              }
 836  
 837              /* Perform the ratifications after finalize. */
 838  
 839              match Self::atomic_post_ratify::<true>(&self.puzzle, store, state, post_ratifications, solutions) {
 840                  // Store the finalize operations from the post-ratify.
 841                  Ok(operations) => ratified_finalize_operations.extend(operations),
 842                  // Note: This will abort the entire atomic batch.
 843                  Err(e) => return Err(format!("Failed to post-ratify - {e}")),
 844              }
 845  
 846              /* Start the commit process. */
 847  
 848              // Commit all the stacks to the process.
 849              process.commit_stacks();
 850  
 851              finish!(timer); // <- Note: This timer does **not** include the time to write batch to DB.
 852  
 853              Ok(ratified_finalize_operations)
 854          })
 855      }
 856  
 857      /// Returns `Some(reason)` if the transaction is aborted. Otherwise, returns `None`.
 858      ///
 859      /// The transaction will be aborted if any of the following conditions are met:
 860      /// - The transaction is producing a duplicate transition
 861      /// - The transaction is double-spending an input
 862      /// - The transaction is producing a duplicate output
 863      /// - The transaction is producing a duplicate transition public key
 864      /// - The transaction is another deployment in the block from the same public fee payer.
 865      /// - The transaction contains a transition that has been deployed or upgraded in this block.
 866      ///
 867      /// - Note: If a transaction is a deployment for a program following its deployment or redeployment in this block,
 868      ///   it is not aborted. Instead, it will be rejected and its fee will be consumed.
 869      #[allow(clippy::too_many_arguments)]
 870      fn should_abort_transaction(
 871          &self,
 872          transaction: &Transaction<N>,
 873          transition_ids: &IndexSet<N::TransitionID>,
 874          input_ids: &IndexSet<Field<N>>,
 875          output_ids: &IndexSet<Field<N>>,
 876          tpks: &IndexSet<Group<N>>,
 877          deployment_payers: &IndexSet<Address<N>>,
 878          deployments: &IndexSet<ProgramID<N>>,
 879      ) -> Option<String> {
 880          // Ensure that:
 881          //  - the transaction is not producing a duplicate transition.
 882          //  - the programs in the component transitions haven't been deployed or upgraded in this block.
 883          for transition in transaction.transitions() {
 884              // Get the transition ID.
 885              let transition_id = transition.id();
 886              // If the transition ID is already produced in this block or previous blocks, abort the transaction.
 887              if transition_ids.contains(transition_id)
 888                  || self.transition_store().contains_transition_id(transition_id).unwrap_or(true)
 889              {
 890                  return Some(format!("Duplicate transition {transition_id}"));
 891              }
 892              // If the transition's program is being deployed or redeployed in this block, abort the transaction.
 893              if deployments.contains(transition.program_id()) {
 894                  return Some(format!(
 895                      "Program {} is being deployed or redeployed in this block",
 896                      transition.program_id()
 897                  ));
 898              }
 899          }
 900  
 901          // Ensure that the transaction is not double-spending an input.
 902          for input_id in transaction.input_ids() {
 903              // If the input ID is already spent in this block or previous blocks, abort the transaction.
 904              if input_ids.contains(input_id) || self.transition_store().contains_input_id(input_id).unwrap_or(true) {
 905                  return Some(format!("Double-spending input {input_id}"));
 906              }
 907          }
 908  
 909          // Ensure that the transaction is not producing a duplicate output.
 910          for output_id in transaction.output_ids() {
 911              // If the output ID is already produced in this block or previous blocks, abort the transaction.
 912              if output_ids.contains(output_id) || self.transition_store().contains_output_id(output_id).unwrap_or(true) {
 913                  return Some(format!("Duplicate output {output_id}"));
 914              }
 915          }
 916  
 917          // Ensure that the transaction is not producing a duplicate transition public key.
 918          // Note that the tpk and tcm are corresponding, so a uniqueness check for just the tpk is sufficient.
 919          for tpk in transaction.transition_public_keys() {
 920              // If the transition public key is already produced in this block or previous blocks, abort the transaction.
 921              if tpks.contains(tpk) || self.transition_store().contains_tpk(tpk).unwrap_or(true) {
 922                  return Some(format!("Duplicate transition public key {tpk}"));
 923              }
 924          }
 925  
 926          // If the transaction is a deployment, ensure that it is not another deployment in the block from the same public fee payer.
 927          if let Transaction::Deploy(_, _, _, _, fee) = transaction {
 928              // If any public deployment payer has already deployed in this block, abort the transaction.
 929              if let Some(payer) = fee.payer() {
 930                  if deployment_payers.contains(&payer) {
 931                      return Some(format!("Another deployment in the block from the same public fee payer {payer}"));
 932                  }
 933              }
 934          }
 935  
 936          // Return `None` because the transaction is well-formed.
 937          None
 938      }
 939  
 940      /// Performs precondition checks on the transactions prior to speculation.
 941      ///
 942      /// This method is used to check the following conditions:
 943      /// - If a transaction is a fee transaction or if it is invalid,
 944      ///   then the transaction will be aborted.
 945      pub(crate) fn prepare_for_speculate<'a, R: CryptoRng + Rng>(
 946          &self,
 947          transactions: &[&'a Transaction<N>],
 948          rng: &mut R,
 949      ) -> Result<(Vec<&'a Transaction<N>>, Vec<(&'a Transaction<N>, String)>)> {
 950          // Construct the list of transactions that need to verified.
 951          let mut transactions_to_verify = Vec::with_capacity(transactions.len());
 952          // Construct the list of valid and invalid transactions.
 953          let mut valid_transactions = Vec::with_capacity(transactions.len());
 954          let mut aborted_transactions = Vec::with_capacity(transactions.len());
 955  
 956          // Initialize a list of created transition IDs.
 957          let mut transition_ids: IndexSet<N::TransitionID> = Default::default();
 958          // Initialize a list of spent input IDs.
 959          let mut input_ids: IndexSet<Field<N>> = Default::default();
 960          // Initialize a list of created output IDs.
 961          let mut output_ids: IndexSet<Field<N>> = Default::default();
 962          // Initialize the list of created transition public keys.
 963          let mut tpks: IndexSet<Group<N>> = Default::default();
 964          // Initialize the list of deployment payers.
 965          let mut deployment_payers: IndexSet<Address<N>> = Default::default();
 966          // Initialize a list of the successful deployments.
 967          let mut deployments = IndexSet::new();
 968  
 969          // Abort the transactions that are have duplicates or are invalid. This will prevent the VM from performing
 970          // verification on transactions that would have been aborted in `VM::atomic_speculate`.
 971          for transaction in transactions.iter() {
 972              // Abort the transaction early if it is a fee transaction.
 973              if transaction.is_fee() {
 974                  aborted_transactions.push((*transaction, "Fee transactions are not allowed in speculate".to_string()));
 975                  continue;
 976              }
 977  
 978              // Determine if the transaction should be aborted.
 979              match self.should_abort_transaction(
 980                  transaction,
 981                  &transition_ids,
 982                  &input_ids,
 983                  &output_ids,
 984                  &tpks,
 985                  &deployment_payers,
 986                  &deployments,
 987              ) {
 988                  // Store the aborted transaction.
 989                  Some(reason) => aborted_transactions.push((*transaction, reason.to_string())),
 990                  // Track the transaction state.
 991                  None => {
 992                      // Add the transition IDs to the set of produced transition IDs.
 993                      transition_ids.extend(transaction.transition_ids());
 994                      // Add the input IDs to the set of spent input IDs.
 995                      input_ids.extend(transaction.input_ids());
 996                      // Add the output IDs to the set of produced output IDs.
 997                      output_ids.extend(transaction.output_ids());
 998                      // Add the transition public keys to the set of produced transition public keys.
 999                      tpks.extend(transaction.transition_public_keys());
1000                      // Add the program owner to the set of deployment payers and the program ID to the set of deployments.
1001                      if let Transaction::Deploy(_, _, _, deployment, fee) = transaction {
1002                          fee.payer().map(|payer| deployment_payers.insert(payer));
1003                          deployments.insert(*deployment.program_id());
1004                      }
1005  
1006                      // Add the transaction to the list of transactions to verify.
1007                      transactions_to_verify.push(transaction);
1008                  }
1009              };
1010          }
1011  
1012          // Separate the transactions into deploys and executions.
1013          let (deployments, executions): (Vec<&Transaction<N>>, Vec<&Transaction<N>>) =
1014              transactions_to_verify.into_iter().partition(|tx| tx.is_deploy());
1015          // Chunk the deploys and executions into groups for parallel verification.
1016          let deployments_for_verification = deployments.chunks(Self::MAX_PARALLEL_DEPLOY_VERIFICATIONS);
1017          let executions_for_verification = executions.chunks(Self::MAX_PARALLEL_EXECUTE_VERIFICATIONS);
1018  
1019          // Verify the transactions in batches and separate the valid and invalid transactions.
1020          for transactions in deployments_for_verification.chain(executions_for_verification) {
1021              let rngs = (0..transactions.len()).map(|_| StdRng::from_seed(rng.r#gen())).collect::<Vec<_>>();
1022              // Verify the transactions and collect the error message if there is one.
1023              let (valid, invalid): (Vec<_>, Vec<_>) =
1024                  cfg_into_iter!(transactions).zip(rngs).partition_map(|(transaction, mut rng)| {
1025                      // Verify the transaction.
1026                      match self.check_transaction(transaction, None, &mut rng) {
1027                          // If the transaction is valid, add it to the list of valid transactions.
1028                          Ok(_) => Either::Left(*transaction),
1029                          // If the transaction is invalid, add it to the list of aborted transactions.
1030                          Err(e) => Either::Right((*transaction, e.to_string())),
1031                      }
1032                  });
1033  
1034              // Collect the valid and aborted transactions.
1035              valid_transactions.extend(valid);
1036              aborted_transactions.extend(invalid);
1037          }
1038  
1039          // Sort the valid and aborted transactions based on their position in the original list.
1040          let position: IndexSet<_> = transactions.iter().map(|tx| tx.id()).collect();
1041          cfg_sort_by_cached_key!(valid_transactions, |tx| position.get_index_of(&tx.id()));
1042          cfg_sort_by_cached_key!(aborted_transactions, |tx| position.get_index_of(&tx.0.id()));
1043  
1044          // Return the valid and invalid transactions.
1045          Ok((valid_transactions, aborted_transactions))
1046      }
1047  
1048      /// Performs precondition checks on the transaction prior to execution.
1049      ///
1050      /// This method is used to check the following conditions:
1051      /// - If the transaction contains a `credits.delta/bond_public` transition,
1052      ///   then the outcome should not exceed the maximum committee size.
1053      #[inline]
1054      fn prepare_for_execution(
1055          state: FinalizeGlobalState,
1056          store: &FinalizeStore<N, C::FinalizeStorage>,
1057          execution: &Execution<N>,
1058      ) -> Result<()> {
1059          // Construct the program ID.
1060          let program_id = ProgramID::from_str("credits.delta")?;
1061          // Construct the committee mapping name.
1062          let committee_mapping = Identifier::from_str("committee")?;
1063  
1064          // Check if the execution has any `bond_validator` transitions, and collect
1065          // the unique validator addresses if so.
1066          // Note: This does not dedup for existing and new validator addresses.
1067          let bond_validator_addresses: HashSet<_> = execution
1068              .transitions()
1069              .filter_map(|transition| match transition.is_bond_validator() {
1070                  // Get the first argument of the transition output if it is a `Future` with a `Plaintext` argument.
1071                  true => match transition.outputs().first() {
1072                      Some(Output::Future(_, Some(future))) => future.arguments().first().and_then(|arg| match arg {
1073                          Argument::Plaintext(Plaintext::Literal(Literal::Address(address), _)) => Some(*address),
1074                          _ => None,
1075                      }),
1076                      _ => None,
1077                  },
1078                  false => None,
1079              })
1080              .collect();
1081  
1082          // Check if we need to reject the execution if the number of new validators exceeds the maximum committee size.
1083          match bond_validator_addresses.is_empty() {
1084              false => {
1085                  // Retrieve the committee members from storage.
1086                  let committee_members = store
1087                      .get_mapping_speculative(program_id, committee_mapping)?
1088                      .into_iter()
1089                      .map(|(key, _)| match key {
1090                          // Extract the address from the key.
1091                          Plaintext::Literal(Literal::Address(address), _) => Ok(address),
1092                          _ => Err(anyhow!("Invalid committee key (missing address) - {key}")),
1093                      })
1094                      .collect::<Result<HashSet<_>>>()?;
1095                  // Get the number of new validators being bonded to.
1096                  let num_new_validators =
1097                      bond_validator_addresses.into_iter().filter(|address| !committee_members.contains(address)).count();
1098                  // Compute the next committee size.
1099                  let next_committee_size = committee_members.len().saturating_add(num_new_validators);
1100                  // Determine the maximum committee size to use.
1101                  let max_committee_size = consensus_config_value!(N, MAX_CERTIFICATES, state.block_height())
1102                      .ok_or(anyhow!("Failed to retrieve the maximum committee size"))?;
1103                  // Check that the number of new validators being bonded does not exceed the maximum number of validators.
1104                  match next_committee_size > max_committee_size as usize {
1105                      true => Err(anyhow!("Call to 'credits.delta/bond_public' exceeds the committee size")),
1106                      false => Ok(()),
1107                  }
1108              }
1109              true => Ok(()),
1110          }
1111      }
1112  
1113      /// Performs the pre-ratifications before finalizing transactions.
1114      #[inline]
1115      fn atomic_pre_ratify<'a>(
1116          store: &FinalizeStore<N, C::FinalizeStorage>,
1117          state: FinalizeGlobalState,
1118          pre_ratifications: impl Iterator<Item = &'a Ratify<N>>,
1119      ) -> Result<Vec<FinalizeOperation<N>>> {
1120          // Construct the program ID.
1121          let program_id = ProgramID::from_str("credits.delta")?;
1122          // Construct the committee mapping name.
1123          let committee_mapping = Identifier::from_str("committee")?;
1124          // Construct the delegated mapping name.
1125          let delegated_mapping: Identifier<N> = Identifier::from_str("delegated")?;
1126          // Construct the bonded mapping name.
1127          let bonded_mapping = Identifier::from_str("bonded")?;
1128          // Construct the account mapping name.
1129          let account_mapping = Identifier::from_str("account")?;
1130          // Construct the metadata mapping name.
1131          let metadata_mapping = Identifier::from_str("metadata")?;
1132          // Construct the withdraw mapping name.
1133          let withdraw_mapping = Identifier::from_str("withdraw")?;
1134  
1135          // Initialize a list of finalize operations.
1136          let mut finalize_operations = Vec::new();
1137  
1138          // Initialize a flag for the genesis ratification.
1139          let mut is_genesis_ratified = false;
1140  
1141          // Iterate over the ratifications.
1142          for ratify in pre_ratifications {
1143              match ratify {
1144                  Ratify::Genesis(committee, public_balances, bonded_balances) => {
1145                      // Ensure this is the genesis block.
1146                      ensure!(state.block_height() == 0, "Ratify::Genesis(..) expected a genesis block");
1147                      // Ensure the genesis committee round is 0.
1148                      ensure!(
1149                          committee.starting_round() == 0,
1150                          "Ratify::Genesis(..) expected a genesis committee round of 0"
1151                      );
1152                      // Ensure that the number of members in the committee does not exceed the maximum.
1153                      let max_committee_size = consensus_config_value!(N, MAX_CERTIFICATES, state.block_height())
1154                          .ok_or(anyhow!("Ratify::Genesis(..) failed to retrieve the maximum committee size"))?;
1155                      ensure!(
1156                          committee.members().len() <= max_committee_size as usize,
1157                          "Ratify::Genesis(..) exceeds the maximum number of committee members"
1158                      );
1159                      // Ensure that the number of delegators does not exceed the maximum.
1160                      ensure!(
1161                          bonded_balances.len().saturating_sub(committee.members().len()) <= MAX_DELEGATORS as usize,
1162                          "Ratify::Genesis(..) exceeds the maximum number of delegators"
1163                      );
1164                      // Ensure genesis has not been ratified yet.
1165                      ensure!(!is_genesis_ratified, "Ratify::Genesis(..) has already been ratified");
1166  
1167                      // TODO (howardwu): Consider whether to initialize the mappings here.
1168                      //  Currently, this is breaking for test cases that use VM but do not insert the genesis block.
1169                      // // Initialize the store for 'credits.delta'.
1170                      // let credits = Program::<N>::credits()?;
1171                      // for mapping in credits.mappings().values() {
1172                      //     // Ensure that all mappings are initialized.
1173                      //     if !store.contains_mapping_confirmed(credits.id(), mapping.name())? {
1174                      //         // Initialize the mappings for 'credits.delta'.
1175                      //         finalize_operations.push(store.initialize_mapping(*credits.id(), *mapping.name())?);
1176                      //     }
1177                      // }
1178  
1179                      // Calculate the stake per validator using `bonded_balances`.
1180                      //
1181                      // Note: There is no need to check the `delegated` mapping in the genesis block,
1182                      // because the design of `bonded_balances` by definition does not support
1183                      // delegating to a non-bonded validator. Thus, the assumption is that the
1184                      // `delegated` mapping will be correct by construction.
1185                      let mut stake_per_validator = IndexMap::with_capacity(committee.members().len());
1186                      for (address, (validator_address, _, amount)) in bonded_balances.iter() {
1187                          // Check that the amount meets the minimum requirement, depending on whether the address is a validator.
1188                          if *address == *validator_address {
1189                              ensure!(
1190                                  *amount >= MIN_VALIDATOR_SELF_STAKE,
1191                                  "Ratify::Genesis(..) the validator {address} must stake at least {MIN_VALIDATOR_SELF_STAKE}",
1192                              );
1193                          } else {
1194                              ensure!(
1195                                  *amount >= MIN_DELEGATOR_STAKE,
1196                                  "Ratify::Genesis(..) the delegator {address} must stake at least {MIN_DELEGATOR_STAKE}",
1197                              );
1198                              // If the corresponding validator is not a committee member yet, then continue.
1199                              if !committee.is_committee_member(*validator_address) {
1200                                  continue;
1201                              }
1202                              // If the address is a delegator, check that the corresponding validator is open.
1203                              ensure!(
1204                                  committee.is_committee_member_open(*validator_address),
1205                                  "Ratify::Genesis(..) the delegator {address} is delegating to a closed validator {validator_address}",
1206                              );
1207                          }
1208                          // Accumulate the staked amount per validator.
1209                          let total = stake_per_validator.entry(validator_address).or_insert(0u64);
1210                          *total = total.saturating_add(*amount);
1211                      }
1212                      // Ensure the stake per validator matches the committee.
1213                      ensure!(
1214                          stake_per_validator.len() == committee.members().len(),
1215                          "Ratify::Genesis(..) the number of validators in the committee does not match the number of validators in the bonded balances",
1216                      );
1217  
1218                      // Check that `committee` is consistent with `stake_per_validator`.
1219                      for (validator_address, amount) in &stake_per_validator {
1220                          // Retrieve the expected validator stake from the committee.
1221                          let Some((expected_amount, _, _)) = committee.members().get(*validator_address) else {
1222                              bail!(
1223                                  "Ratify::Genesis(..) found a validator in the bonded balances that is not in the committee"
1224                              )
1225                          };
1226                          // Ensure the staked amount matches the committee.
1227                          ensure!(
1228                              *expected_amount == *amount,
1229                              "Ratify::Genesis(..) inconsistent staked amount for validator {validator_address}",
1230                          );
1231                      }
1232                      // Ensure that the total stake matches the sum of the staked amounts.
1233                      ensure!(
1234                          committee.total_stake() == stake_per_validator.values().sum::<u64>(),
1235                          "Ratify::Genesis(..) incorrect total total stake for the committee"
1236                      );
1237  
1238                      // Split the bonded balances into stakers and withdrawal addresses.
1239                      let (next_stakers, withdrawal_addresses) = bonded_balances.iter().fold(
1240                          (
1241                              IndexMap::with_capacity(bonded_balances.len()),
1242                              IndexMap::with_capacity(bonded_balances.len()),
1243                          ),
1244                          |(mut stakers, mut withdrawal_addresses), (staker, (validator, withdrawal_address, amount))| {
1245                              stakers.insert(*staker, (*validator, *amount));
1246                              withdrawal_addresses.insert(*staker, *withdrawal_address);
1247                              (stakers, withdrawal_addresses)
1248                          },
1249                      );
1250  
1251                      let next_delegated = to_next_delegated(&next_stakers);
1252  
1253                      // Construct the next committee map and next bonded map.
1254                      let (next_committee_map, next_bonded_map, next_delegated_map) =
1255                          to_next_committee_bonded_delegated_map(committee, &next_stakers, &next_delegated);
1256  
1257                      // Construct the next withdraw map.
1258                      let next_withdraw_map = to_next_withdraw_map(&withdrawal_addresses);
1259  
1260                      // Insert the next committee into storage.
1261                      store.committee_store().insert(state.block_height(), *(committee.clone()))?;
1262                      // Store the finalize operations for updating the committee and bonded mapping.
1263                      finalize_operations.extend(&[
1264                          // Replace the committee mapping in storage.
1265                          store.replace_mapping(program_id, committee_mapping, next_committee_map)?,
1266                          // Replace the delegated mapping in storage.
1267                          store.replace_mapping(program_id, delegated_mapping, next_delegated_map)?,
1268                          // Replace the bonded mapping in storage.
1269                          store.replace_mapping(program_id, bonded_mapping, next_bonded_map)?,
1270                          // Replace the withdraw mapping in storage.
1271                          store.replace_mapping(program_id, withdraw_mapping, next_withdraw_map)?,
1272                      ]);
1273  
1274                      // Update the number of validators.
1275                      finalize_operations.extend(&[
1276                          // Update the number of validators in the metadata mapping.
1277                          store.update_key_value(
1278                              program_id,
1279                              metadata_mapping,
1280                              Plaintext::from_str("dx1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3qtczd")?,
1281                              Value::from_str(&format!("{}u32", committee.num_members()))?,
1282                          )?,
1283                      ]);
1284  
1285                      // Update the number of delegators.
1286                      finalize_operations.extend(&[
1287                          // Update the number of delegators in the metadata mapping.
1288                          store.update_key_value(
1289                              program_id,
1290                              metadata_mapping,
1291                              Plaintext::from_str("dx1qgqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqavzal6")?,
1292                              Value::from_str(&format!(
1293                                  "{}u32",
1294                                  bonded_balances.len().saturating_sub(committee.num_members())
1295                              ))?,
1296                          )?,
1297                      ]);
1298  
1299                      // Map the public balances into the appropriate format.
1300                      let public_balances = public_balances
1301                          .iter()
1302                          .map(|(address, amount)| {
1303                              (Plaintext::from(Literal::Address(*address)), Value::from(Literal::U64(U64::new(*amount))))
1304                          })
1305                          .collect::<Vec<_>>();
1306  
1307                      // Update the public balances.
1308                      finalize_operations.extend(&[
1309                          // Update the public balances in storage.
1310                          store.replace_mapping(program_id, account_mapping, public_balances)?,
1311                      ]);
1312  
1313                      // Set the genesis ratification flag.
1314                      is_genesis_ratified = true;
1315                  }
1316                  Ratify::BlockReward(..) | Ratify::PuzzleReward(..) => continue,
1317              }
1318          }
1319  
1320          // Return the finalize operations.
1321          Ok(finalize_operations)
1322      }
1323  
1324      /// Performs the post-ratifications after finalizing transactions.
1325      #[inline]
1326      fn atomic_post_ratify<'a, const IS_FINALIZE: bool>(
1327          puzzle: &Puzzle<N>,
1328          store: &FinalizeStore<N, C::FinalizeStorage>,
1329          state: FinalizeGlobalState,
1330          post_ratifications: impl Iterator<Item = &'a Ratify<N>>,
1331          solutions: &Solutions<N>,
1332      ) -> Result<Vec<FinalizeOperation<N>>> {
1333          // Construct the program ID.
1334          let program_id = ProgramID::from_str("credits.delta")?;
1335          // Construct the committee mapping name.
1336          let committee_mapping = Identifier::from_str("committee")?;
1337          // Construct the delegated mapping name.
1338          let delegated_mapping = Identifier::from_str("delegated")?;
1339          // Construct the bonded mapping name.
1340          let bonded_mapping = Identifier::from_str("bonded")?;
1341          // Construct the account mapping name.
1342          let account_mapping = Identifier::from_str("account")?;
1343  
1344          // Initialize a list of finalize operations.
1345          let mut finalize_operations = Vec::new();
1346  
1347          // Initialize a flag for the block reward ratification.
1348          let mut is_block_reward_ratified = false;
1349          // Initialize a flag for the puzzle reward ratification.
1350          let mut is_puzzle_reward_ratified = false;
1351  
1352          // Iterate over the ratifications.
1353          for ratify in post_ratifications {
1354              match ratify {
1355                  Ratify::Genesis(..) => continue,
1356                  Ratify::BlockReward(block_reward) => {
1357                      // Ensure the block reward has not been ratified yet.
1358                      ensure!(!is_block_reward_ratified, "Ratify::BlockReward(..) has already been ratified");
1359  
1360                      // Retrieve the committee mapping from storage.
1361                      let current_committee_map = store.get_mapping_speculative(program_id, committee_mapping)?;
1362                      // Retrieve the delegator mapping from storage.
1363                      let current_delegator_map = store.get_mapping_speculative(program_id, delegated_mapping)?;
1364                      // Convert the committee mapping into a committee.
1365                      let current_committee = committee_and_delegated_maps_into_committee(
1366                          state.block_round(),
1367                          current_committee_map,
1368                          current_delegator_map,
1369                      )?;
1370                      // Retrieve the bonded mapping from storage.
1371                      let current_bonded_map = store.get_mapping_speculative(program_id, bonded_mapping)?;
1372                      // Convert the bonded map into stakers.
1373                      let current_stakers = bonded_map_into_stakers(current_bonded_map)?;
1374  
1375                      // Ensure the committee matches the bonded mapping.
1376                      ensure_stakers_matches(&current_committee, &current_stakers)?;
1377  
1378                      // Compute the updated stakers, using the committee and block reward.
1379                      let next_stakers = staking_rewards(&current_stakers, &current_committee, *block_reward);
1380  
1381                      // Compute the updated delegated amounts, using the next_stakers updated amounts.
1382                      let next_delegated = to_next_delegated(&next_stakers);
1383  
1384                      // Compute the updated committee, using the delegatees.
1385                      let next_committee = to_next_committee(&current_committee, state.block_round(), &next_delegated)?;
1386  
1387                      // Construct the next committee map, the next bonded map, and the next delegated map.
1388                      let (next_committee_map, next_bonded_map, next_delegated_map) =
1389                          to_next_committee_bonded_delegated_map(&next_committee, &next_stakers, &next_delegated);
1390  
1391                      // Insert the next committee into storage.
1392                      store.committee_store().insert(state.block_height(), next_committee)?;
1393  
1394                      #[cfg(all(feature = "history", feature = "rocks"))]
1395                      {
1396                          // When finalizing in `FinalizeMode::RealRun`, store the delegated and bonded mappings in history.
1397                          if IS_FINALIZE {
1398                              // Load a `History` object.
1399                              let history = History::new(N::ID, store.storage_mode());
1400  
1401                              // Write the delegated mapping as JSON.
1402                              history.store_mapping(state.block_height(), MappingName::Delegated, &next_delegated_map)?;
1403  
1404                              // Write the bonded mapping as JSON.
1405                              history.store_mapping(state.block_height(), MappingName::Bonded, &next_bonded_map)?;
1406  
1407                              // Write the metadata mapping as JSON.
1408                              let metadata_mapping = Identifier::from_str("metadata")?;
1409                              let metadata_map = store.get_mapping_speculative(program_id, metadata_mapping)?;
1410                              history.store_mapping(state.block_height(), MappingName::Metadata, &metadata_map)?;
1411  
1412                              // Write the unbonding mapping as JSON.
1413                              let unbonding_mapping = Identifier::from_str("unbonding")?;
1414                              let unbonding_map = store.get_mapping_speculative(program_id, unbonding_mapping)?;
1415                              history.store_mapping(state.block_height(), MappingName::Unbonding, &unbonding_map)?;
1416  
1417                              // Write the withdraw mapping as JSON.
1418                              let withdraw_mapping = Identifier::from_str("withdraw")?;
1419                              let withdraw_map = store.get_mapping_speculative(program_id, withdraw_mapping)?;
1420                              history.store_mapping(state.block_height(), MappingName::Withdraw, &withdraw_map)?;
1421                          }
1422                      }
1423  
1424                      // Store the finalize operations for updating the committee and bonded mapping.
1425                      finalize_operations.extend(&[
1426                          // Replace the committee mapping in storage.
1427                          store.replace_mapping(program_id, committee_mapping, next_committee_map)?,
1428                          // Replace the delegated mapping in storage.
1429                          store.replace_mapping(program_id, delegated_mapping, next_delegated_map)?,
1430                          // Replace the bonded mapping in storage.
1431                          store.replace_mapping(program_id, bonded_mapping, next_bonded_map)?,
1432                      ]);
1433  
1434                      // Set the block reward ratification flag.
1435                      is_block_reward_ratified = true;
1436                  }
1437                  Ratify::PuzzleReward(puzzle_reward) => {
1438                      // Ensure the puzzle reward has not been ratified yet.
1439                      ensure!(!is_puzzle_reward_ratified, "Ratify::PuzzleReward(..) has already been ratified");
1440  
1441                      // If the puzzle reward is zero, skip.
1442                      if *puzzle_reward == 0 {
1443                          continue;
1444                      }
1445                      // Retrieve the solutions.
1446                      let Some(solutions) = solutions.deref() else {
1447                          continue;
1448                      };
1449                      // Compute the proof targets, with the corresponding addresses.
1450                      let proof_targets = solutions
1451                          .values()
1452                          .map(|s| Ok((s.address(), puzzle.get_proof_target(s)?)))
1453                          .collect::<Result<Vec<_>>>()?;
1454                      // Calculate the proving rewards.
1455                      let proving_rewards = proving_rewards(proof_targets, *puzzle_reward);
1456                      // Iterate over the proving rewards.
1457                      for (address, amount) in proving_rewards {
1458                          // Construct the key.
1459                          let key = Plaintext::from(Literal::Address(address));
1460                          // Retrieve the current public balance.
1461                          let value = store.get_value_speculative(program_id, account_mapping, &key)?;
1462                          // Compute the next public balance.
1463                          let next_value = Value::from(Literal::U64(U64::new(match value {
1464                              Some(Value::Plaintext(Plaintext::Literal(Literal::U64(value), _))) => {
1465                                  (*value).saturating_add(amount)
1466                              }
1467                              None => amount,
1468                              v => bail!("Critical bug in post-ratify puzzle reward- Invalid amount ({v:?})"),
1469                          })));
1470                          // Update the public balance in finalize storage.
1471                          let operation = store.update_key_value(program_id, account_mapping, key, next_value)?;
1472                          finalize_operations.push(operation);
1473                      }
1474  
1475                      // Set the puzzle reward ratification flag.
1476                      is_puzzle_reward_ratified = true;
1477                  }
1478              }
1479          }
1480  
1481          // Return the finalize operations.
1482          Ok(finalize_operations)
1483      }
1484  }
1485  
1486  #[cfg(test)]
1487  mod tests {
1488      use super::*;
1489      use crate::vm::{
1490          test_helpers,
1491          test_helpers::{sample_finalize_state, sample_vm},
1492      };
1493      use deltavm_ledger_block::{Block, Header, Metadata, Transaction, Transition};
1494      use deltavm_ledger_committee::{MAX_DELEGATORS, MIN_VALIDATOR_STAKE};
1495      use deltavm_synthesizer_program::Program;
1496      use console::{
1497          account::{Address, PrivateKey, ViewKey},
1498          program::{Ciphertext, Entry, Record},
1499          types::Field,
1500      };
1501  
1502      use rand::distributions::DistString;
1503  
1504      type CurrentNetwork = test_helpers::CurrentNetwork;
1505      #[cfg(not(feature = "rocks"))]
1506      type LedgerType = deltavm_ledger_store::helpers::memory::ConsensusMemory<CurrentNetwork>;
1507      #[cfg(feature = "rocks")]
1508      type LedgerType = deltavm_ledger_store::helpers::rocksdb::ConsensusDB<CurrentNetwork>;
1509  
1510      /// Sample a new program and deploy it to the VM. Returns the program name.
1511      fn new_program_deployment<R: Rng + CryptoRng>(
1512          vm: &VM<CurrentNetwork, LedgerType>,
1513          private_key: &PrivateKey<CurrentNetwork>,
1514          previous_block: &Block<CurrentNetwork>,
1515          unspent_records: &mut Vec<Record<CurrentNetwork, Ciphertext<CurrentNetwork>>>,
1516          rng: &mut R,
1517      ) -> Result<(String, Block<CurrentNetwork>)> {
1518          let program_name = format!("a{}.delta", Alphanumeric.sample_string(rng, 8).to_lowercase());
1519  
1520          let program = Program::<CurrentNetwork>::from_str(&format!(
1521              "
1522  program {program_name};
1523  
1524  mapping account:
1525      // The token owner.
1526      key as address.public;
1527      // The token amount.
1528      value as u64.public;
1529  
1530  function mint_public:
1531      input r0 as address.public;
1532      input r1 as u64.public;
1533      async mint_public r0 r1 into r2;
1534      output r2 as {program_name}/mint_public.future;
1535  
1536  finalize mint_public:
1537      input r0 as address.public;
1538      input r1 as u64.public;
1539  
1540      get.or_use account[r0] 0u64 into r2;
1541      add r2 r1 into r3;
1542      set r3 into account[r0];
1543  
1544  function transfer_public:
1545      input r0 as address.public;
1546      input r1 as u64.public;
1547      async transfer_public self.caller r0 r1 into r2;
1548      output r2 as {program_name}/transfer_public.future;
1549  
1550  finalize transfer_public:
1551      input r0 as address.public;
1552      input r1 as address.public;
1553      input r2 as u64.public;
1554  
1555      get.or_use account[r0] 0u64 into r3;
1556      get.or_use account[r1] 0u64 into r4;
1557  
1558      sub r3 r2 into r5;
1559      add r4 r2 into r6;
1560  
1561      set r5 into account[r0];
1562      set r6 into account[r1];"
1563          ))?;
1564  
1565          // Prepare the additional fee.
1566          let view_key = ViewKey::<CurrentNetwork>::try_from(private_key)?;
1567          let credits = Some(unspent_records.pop().unwrap().decrypt(&view_key)?);
1568  
1569          // Deploy.
1570          let transaction = vm.deploy(private_key, &program, credits, 10, None, rng)?;
1571  
1572          // Construct the new block.
1573          let next_block = sample_next_block(vm, private_key, &[transaction], previous_block, unspent_records, rng)?;
1574  
1575          Ok((program_name, next_block))
1576      }
1577  
1578      /// Construct a new block based on the given transactions.
1579      fn sample_next_block<R: Rng + CryptoRng>(
1580          vm: &VM<CurrentNetwork, LedgerType>,
1581          private_key: &PrivateKey<CurrentNetwork>,
1582          transactions: &[Transaction<CurrentNetwork>],
1583          previous_block: &Block<CurrentNetwork>,
1584          unspent_records: &mut Vec<Record<CurrentNetwork, Ciphertext<CurrentNetwork>>>,
1585          rng: &mut R,
1586      ) -> Result<Block<CurrentNetwork>> {
1587          // Create the finalize state for the next block height.
1588          let next_block_height = previous_block.height() + 1;
1589          let time_since_last_block = MainnetV0::BLOCK_TIME as i64;
1590          let next_block_timestamp = previous_block.timestamp().saturating_add(time_since_last_block);
1591          let next_timestamp = (next_block_height
1592              >= MainnetV0::CONSENSUS_HEIGHT(ConsensusVersion::V12).unwrap_or_default())
1593          .then_some(next_block_timestamp);
1594          let finalize_state =
1595              FinalizeGlobalState::from(next_block_height as u64, next_block_height, next_timestamp, [0u8; 32]);
1596  
1597          // Speculate on the candidate ratifications, solutions, and transactions.
1598          let (ratifications, transactions, aborted_transaction_ids, ratified_finalize_operations) =
1599              vm.speculate(finalize_state, time_since_last_block, None, vec![], &None.into(), transactions.iter(), rng)?;
1600  
1601          // Construct the metadata associated with the block.
1602          let metadata = Metadata::new(
1603              CurrentNetwork::ID,
1604              previous_block.round() + 1,
1605              previous_block.height() + 1,
1606              0,
1607              0,
1608              CurrentNetwork::GENESIS_COINBASE_TARGET,
1609              CurrentNetwork::GENESIS_PROOF_TARGET,
1610              previous_block.last_coinbase_target(),
1611              previous_block.last_coinbase_timestamp(),
1612              next_block_timestamp,
1613          )?;
1614  
1615          // Construct the new block header.
1616          let header = Header::from(
1617              vm.block_store().current_state_root(),
1618              transactions.to_transactions_root().unwrap(),
1619              transactions.to_finalize_root(ratified_finalize_operations).unwrap(),
1620              ratifications.to_ratifications_root().unwrap(),
1621              Field::zero(),
1622              Field::zero(),
1623              metadata,
1624          )?;
1625  
1626          let block = Block::new_beacon(
1627              private_key,
1628              previous_block.hash(),
1629              header,
1630              ratifications,
1631              None.into(),
1632              vec![],
1633              transactions,
1634              aborted_transaction_ids,
1635              rng,
1636          )?;
1637  
1638          // Track the new records.
1639          let new_records = block
1640              .transitions()
1641              .cloned()
1642              .flat_map(Transition::into_records)
1643              .map(|(_, record)| record)
1644              .collect::<Vec<_>>();
1645          unspent_records.extend(new_records);
1646  
1647          Ok(block)
1648      }
1649  
1650      /// Generate split transactions for the unspent records.
1651      fn generate_splits<R: Rng + CryptoRng>(
1652          vm: &VM<CurrentNetwork, LedgerType>,
1653          private_key: &PrivateKey<CurrentNetwork>,
1654          previous_block: &Block<CurrentNetwork>,
1655          unspent_records: &mut Vec<Record<CurrentNetwork, Ciphertext<CurrentNetwork>>>,
1656          rng: &mut R,
1657      ) -> Result<Block<CurrentNetwork>> {
1658          // Prepare the additional fee.
1659          let view_key = ViewKey::<CurrentNetwork>::try_from(private_key)?;
1660  
1661          // Generate split transactions.
1662          let mut transactions = Vec::new();
1663          while !unspent_records.is_empty() {
1664              let record = unspent_records.pop().unwrap().decrypt(&view_key)?;
1665  
1666              // Fetch the record balance and divide it in half.
1667              let split_balance = match record.find(&[Identifier::from_str("microcredits")?]) {
1668                  Ok(Entry::Private(Plaintext::Literal(Literal::U64(amount), _))) => *amount / 2,
1669                  _ => bail!("fee record does not contain a microcredits entry"),
1670              };
1671  
1672              // Prepare the inputs.
1673              let inputs = [
1674                  Value::<CurrentNetwork>::Record(record),
1675                  Value::<CurrentNetwork>::from_str(&format!("{split_balance}u64")).unwrap(),
1676              ]
1677              .into_iter();
1678  
1679              // Execute.
1680              let transaction = vm.execute(private_key, ("credits.delta", "split"), inputs, None, 0, None, rng).unwrap();
1681  
1682              transactions.push(transaction);
1683          }
1684  
1685          // Construct the new block.
1686          sample_next_block(vm, private_key, &transactions, previous_block, unspent_records, rng)
1687      }
1688  
1689      /// Create an execution transaction.
1690      fn create_execution(
1691          vm: &VM<CurrentNetwork, LedgerType>,
1692          caller_private_key: PrivateKey<CurrentNetwork>,
1693          program_id: &str,
1694          function_name: &str,
1695          inputs: Vec<Value<CurrentNetwork>>,
1696          unspent_records: &mut Vec<Record<CurrentNetwork, Ciphertext<CurrentNetwork>>>,
1697          rng: &mut TestRng,
1698      ) -> Transaction<CurrentNetwork> {
1699          assert!(vm.contains_program(&ProgramID::from_str(program_id).unwrap()));
1700  
1701          // Prepare the additional fee.
1702          let view_key = ViewKey::<CurrentNetwork>::try_from(caller_private_key).unwrap();
1703          let unspent_record = unspent_records.pop().unwrap();
1704          let credits = Some(unspent_record.decrypt(&view_key).unwrap());
1705  
1706          // Execute.
1707          let transaction = vm
1708              .execute(&caller_private_key, (program_id, function_name), inputs.into_iter(), credits, 1, None, rng)
1709              .unwrap();
1710          // Verify.
1711          vm.check_transaction(&transaction, None, rng).unwrap();
1712  
1713          // Return the transaction.
1714          transaction
1715      }
1716  
1717      /// Sample a public mint transaction.
1718      fn sample_mint_public(
1719          vm: &VM<CurrentNetwork, LedgerType>,
1720          caller_private_key: PrivateKey<CurrentNetwork>,
1721          program_id: &str,
1722          recipient: Address<CurrentNetwork>,
1723          amount: u64,
1724          unspent_records: &mut Vec<Record<CurrentNetwork, Ciphertext<CurrentNetwork>>>,
1725          rng: &mut TestRng,
1726      ) -> Transaction<CurrentNetwork> {
1727          let inputs = vec![
1728              Value::<CurrentNetwork>::from_str(&recipient.to_string()).unwrap(),
1729              Value::<CurrentNetwork>::from_str(&format!("{amount}u64")).unwrap(),
1730          ];
1731  
1732          create_execution(vm, caller_private_key, program_id, "mint_public", inputs, unspent_records, rng)
1733      }
1734  
1735      /// Sample a public transfer transaction.
1736      fn sample_transfer_public(
1737          vm: &VM<CurrentNetwork, LedgerType>,
1738          caller_private_key: PrivateKey<CurrentNetwork>,
1739          program_id: &str,
1740          recipient: Address<CurrentNetwork>,
1741          amount: u64,
1742          unspent_records: &mut Vec<Record<CurrentNetwork, Ciphertext<CurrentNetwork>>>,
1743          rng: &mut TestRng,
1744      ) -> Transaction<CurrentNetwork> {
1745          let inputs = vec![
1746              Value::<CurrentNetwork>::from_str(&recipient.to_string()).unwrap(),
1747              Value::<CurrentNetwork>::from_str(&format!("{amount}u64")).unwrap(),
1748          ];
1749  
1750          create_execution(vm, caller_private_key, program_id, "transfer_public", inputs, unspent_records, rng)
1751      }
1752  
1753      /// A helper method to construct the rejected transaction format for `atomic_finalize`.
1754      fn reject(
1755          index: u32,
1756          transaction: &Transaction<CurrentNetwork>,
1757          finalize: &[FinalizeOperation<CurrentNetwork>],
1758      ) -> ConfirmedTransaction<CurrentNetwork> {
1759          match transaction {
1760              Transaction::Execute(_, _, execution, fee) => ConfirmedTransaction::RejectedExecute(
1761                  index,
1762                  Transaction::from_fee(fee.clone().unwrap()).unwrap(),
1763                  Rejected::new_execution(*execution.clone()),
1764                  finalize.to_vec(),
1765              ),
1766              _ => panic!("only reject execution transactions"),
1767          }
1768      }
1769  
1770      /// Samples the validators.
1771      fn sample_validators<N: Network>(
1772          num_validators: usize,
1773          rng: &mut TestRng,
1774      ) -> IndexMap<PrivateKey<N>, (u64, bool, u8)> {
1775          (0..num_validators)
1776              .map(|_| {
1777                  let private_key = PrivateKey::new(rng).unwrap();
1778                  let amount = MIN_VALIDATOR_STAKE;
1779                  let is_open = true;
1780                  let commission: u8 = 0;
1781                  (private_key, (amount, is_open, commission))
1782              })
1783              .collect::<IndexMap<_, _>>()
1784      }
1785  
1786      /// Returns a `committee_map` and the `allocated_amount` given the validators and delegators.
1787      fn sample_committee_map_and_allocated_amount<N: Network>(
1788          validators: &IndexMap<PrivateKey<N>, (u64, bool, u8)>,
1789          delegators: &IndexMap<PrivateKey<N>, (Address<N>, u64)>,
1790      ) -> (IndexMap<Address<N>, (u64, bool, u8)>, u64) {
1791          // Reset the tracked amount.
1792          let mut allocated_amount = 0;
1793  
1794          // Construct the **correct** committee.
1795          let mut committee_map = IndexMap::new();
1796          for (private_key, (amount, is_open, commission)) in validators {
1797              let address = Address::try_from(private_key).unwrap();
1798              committee_map.insert(address, (*amount, *is_open, *commission));
1799              allocated_amount += amount;
1800          }
1801          for (delegator, (validator, amount)) in delegators {
1802              if let indexmap::map::Entry::Occupied(mut entry) = committee_map.entry(*validator) {
1803                  let (current_amount, is_open, commission) = entry.get();
1804                  // Ensure the validator is open.
1805                  assert!(*is_open, "delegator {delegator} is delegating {amount} microcredits to a closed validator");
1806                  // Update the committee map.
1807                  entry.insert((current_amount + amount, *is_open, *commission));
1808              } else {
1809                  unreachable!("delegator {delegator} is delegating to a closed validator")
1810              }
1811              // Accumulate the allocated amount.
1812              allocated_amount += amount;
1813          }
1814  
1815          (committee_map, allocated_amount)
1816      }
1817  
1818      /// Returns the `bonded_balances` given the validators and delegators.
1819      /// Note that the withdrawal address is the same as the staker address.
1820      fn sample_bonded_balances<N: Network>(
1821          validators: &IndexMap<PrivateKey<N>, (u64, bool, u8)>,
1822          delegators: &IndexMap<PrivateKey<N>, (Address<N>, u64)>,
1823      ) -> IndexMap<Address<N>, (Address<N>, Address<N>, u64)> {
1824          let mut bonded_balances = IndexMap::with_capacity(validators.len() + delegators.len());
1825          for (private_key, (amount, _, _)) in validators {
1826              let address = Address::try_from(private_key).unwrap();
1827              bonded_balances.insert(address, (address, address, *amount));
1828          }
1829          for (private_key, (validator, amount)) in delegators {
1830              let address = Address::try_from(private_key).unwrap();
1831              bonded_balances.insert(address, (*validator, address, *amount));
1832          }
1833          bonded_balances
1834      }
1835  
1836      /// Returns the `public_balances` given the addresses and total amount.
1837      /// Note that the balances are evenly distributed among the addresses.
1838      fn sample_public_balances<N: Network>(addresses: &[Address<N>], total_amount: u64) -> IndexMap<Address<N>, u64> {
1839          // Check that the addresses are not empty.
1840          assert!(!addresses.is_empty(), "must provide at least one address");
1841          // Distribute the total amount evenly among the addresses.
1842          let amount_per_address = total_amount / addresses.len() as u64;
1843          let mut public_balances: IndexMap<_, _> =
1844              addresses.iter().map(|address| (*address, amount_per_address)).collect();
1845          // Distribute the remainder to the first address.
1846          let remaining = total_amount % addresses.len() as u64;
1847          if remaining > 0 {
1848              *public_balances.get_mut(&addresses[0]).unwrap() += remaining;
1849          }
1850          // Return the public balances.
1851          public_balances
1852      }
1853  
1854      #[test]
1855      fn test_finalize_duplicate_deployment() {
1856          let rng = &mut TestRng::default();
1857  
1858          let vm = crate::vm::test_helpers::sample_vm();
1859  
1860          // Fetch a deployment transaction.
1861          let deployment_transaction = crate::vm::test_helpers::sample_deployment_transaction(rng);
1862          let deployment_transaction_id = deployment_transaction.id();
1863  
1864          // Construct the program name.
1865          let program_id = ProgramID::from_str("testing.delta").unwrap();
1866  
1867          // Prepare the confirmed transactions.
1868          let (ratifications, confirmed_transactions, aborted_transaction_ids, _) = vm
1869              .speculate(
1870                  sample_finalize_state(1),
1871                  CurrentNetwork::BLOCK_TIME as i64,
1872                  None,
1873                  vec![],
1874                  &None.into(),
1875                  [deployment_transaction.clone()].iter(),
1876                  rng,
1877              )
1878              .unwrap();
1879          assert_eq!(confirmed_transactions.len(), 1);
1880          assert!(aborted_transaction_ids.is_empty());
1881  
1882          // Ensure the VM does not contain this program.
1883          assert!(!vm.contains_program(&program_id));
1884  
1885          // Finalize the transaction.
1886          assert!(vm.finalize(sample_finalize_state(1), &ratifications, &None.into(), &confirmed_transactions).is_ok());
1887  
1888          // Ensure the VM contains this program.
1889          assert!(vm.contains_program(&program_id));
1890  
1891          // Ensure the VM can't redeploy the same transaction.
1892          assert!(vm.finalize(sample_finalize_state(1), &ratifications, &None.into(), &confirmed_transactions).is_err());
1893  
1894          // Ensure the VM contains this program.
1895          assert!(vm.contains_program(&program_id));
1896  
1897          // Ensure the dry run of the redeployment will cause a reject transaction to be created.
1898          let (_, candidate_transactions, aborted_transaction_ids, _) = vm
1899              .atomic_speculate(
1900                  sample_finalize_state(1),
1901                  CurrentNetwork::BLOCK_TIME as i64,
1902                  None,
1903                  vec![],
1904                  None.into(),
1905                  vec![deployment_transaction],
1906              )
1907              .unwrap();
1908          assert_eq!(candidate_transactions.len(), 1);
1909          assert!(matches!(candidate_transactions[0], ConfirmedTransaction::RejectedDeploy(..)));
1910          assert!(aborted_transaction_ids.is_empty());
1911  
1912          // Check that the unconfirmed transaction ID of the rejected deployment is correct.
1913          assert_eq!(candidate_transactions[0].to_unconfirmed_transaction_id().unwrap(), deployment_transaction_id);
1914      }
1915  
1916      #[test]
1917      fn test_bond_validator_above_maximum_fails() {
1918          // Initialize an RNG.
1919          let rng = &mut TestRng::default();
1920  
1921          // Initialize the VM.
1922          let vm = sample_vm();
1923  
1924          // Initialize the validators with the maximum number of validators.
1925          let validators = sample_validators::<CurrentNetwork>(
1926              consensus_config_value!(CurrentNetwork, MAX_CERTIFICATES, 0).unwrap() as usize,
1927              rng,
1928          );
1929  
1930          // Initialize a new address.
1931          let new_validator_private_key = PrivateKey::<CurrentNetwork>::new(rng).unwrap();
1932          let new_validator_address = Address::try_from(&new_validator_private_key).unwrap();
1933  
1934          // Construct the committee.
1935          // Track the allocated amount.
1936          let (committee_map, allocated_amount) =
1937              sample_committee_map_and_allocated_amount(&validators, &IndexMap::new());
1938  
1939          // Collect all of the addresses in a single place
1940          let validator_addresses =
1941              validators.keys().map(|private_key| Address::try_from(private_key).unwrap()).collect::<Vec<_>>();
1942  
1943          // Construct the public balances, allocating the remaining supply.
1944          let new_validator_balance = MIN_VALIDATOR_STAKE + 100_000_000;
1945          let mut public_balances = sample_public_balances(
1946              &validator_addresses,
1947              <CurrentNetwork as Network>::STARTING_SUPPLY - allocated_amount - new_validator_balance,
1948          );
1949          // Set the public balance of the new validator to the minimum validator stake.
1950          public_balances.insert(new_validator_address, new_validator_balance);
1951  
1952          // Construct the bonded balances.
1953          let bonded_balances = sample_bonded_balances(&validators, &IndexMap::new());
1954  
1955          // Construct the genesis block, which should pass.
1956          let block = vm
1957              .genesis_quorum(
1958                  validators.keys().next().unwrap(),
1959                  Committee::new_genesis(committee_map).unwrap(),
1960                  public_balances,
1961                  bonded_balances,
1962                  rng,
1963              )
1964              .unwrap();
1965  
1966          // Add the block.
1967          vm.add_next_block(&block).unwrap();
1968  
1969          // Attempt to bond a new validator above the maximum number of validators.
1970          let inputs = vec![
1971              Value::<CurrentNetwork>::from_str(&validator_addresses.first().unwrap().to_string()).unwrap(), // Withdrawal address
1972              Value::<CurrentNetwork>::from_str(&format!("{MIN_VALIDATOR_STAKE}u64")).unwrap(),              // Amount
1973              Value::<CurrentNetwork>::from_str("42u8").unwrap(),                                            // Commission
1974          ];
1975  
1976          // Execute.
1977          let bond_validator_transaction = vm
1978              .execute(
1979                  &new_validator_private_key,
1980                  ("credits.delta", "bond_validator"),
1981                  inputs.into_iter(),
1982                  None,
1983                  1,
1984                  None,
1985                  rng,
1986              )
1987              .unwrap();
1988  
1989          // Verify.
1990          vm.check_transaction(&bond_validator_transaction, None, rng).unwrap();
1991  
1992          // Speculate on the transactions.
1993          let transactions = vec![bond_validator_transaction.clone()];
1994          let (_, confirmed_transactions, _, _) = vm
1995              .atomic_speculate(
1996                  sample_finalize_state(1),
1997                  CurrentNetwork::BLOCK_TIME as i64,
1998                  None,
1999                  vec![],
2000                  None.into(),
2001                  transactions,
2002              )
2003              .unwrap();
2004  
2005          // Assert that the transaction is rejected.
2006          assert_eq!(confirmed_transactions.len(), 1);
2007          assert_eq!(
2008              confirmed_transactions[0],
2009              reject(0, &bond_validator_transaction, confirmed_transactions[0].finalize_operations())
2010          );
2011      }
2012  
2013      #[cfg(feature = "test")]
2014      #[test]
2015      fn test_genesis_num_validators_does_not_exceed_maximum_before_v3() {
2016          // This test will fail if the consensus v3 height is 0
2017          assert_ne!(0, CurrentNetwork::CONSENSUS_HEIGHT(ConsensusVersion::V3).unwrap());
2018  
2019          // Initialize an RNG.
2020          let rng = &mut TestRng::default();
2021  
2022          // TODO: Fix this test by adding additional constraints to `Committee::new_genesis`
2023          // Initialize the validators with the maximum number of validators before consensus v3.
2024          let validators = sample_validators::<CurrentNetwork>(
2025              consensus_config_value!(CurrentNetwork, MAX_CERTIFICATES, 0).unwrap() as usize + 5,
2026              rng,
2027          );
2028  
2029          // Construct the committee.
2030          // Track the allocated amount.
2031          let (committee_map, _allocated_amount) =
2032              sample_committee_map_and_allocated_amount(&validators, &IndexMap::new());
2033  
2034          assert!(Committee::new_genesis(committee_map).is_err());
2035      }
2036  
2037      #[cfg(not(feature = "test"))]
2038      #[test]
2039      #[allow(clippy::assertions_on_constants)]
2040      fn test_migration_v3_maximum_validator_increase() {
2041          // This test will fail if the consensus v3 height is 0
2042          assert_ne!(0, CurrentNetwork::CONSENSUS_HEIGHT(ConsensusVersion::V3).unwrap());
2043  
2044          // Initialize an RNG.
2045          let rng = &mut TestRng::default();
2046  
2047          // Initialize the VM.
2048          let vm = sample_vm();
2049  
2050          // Initialize the validators with the maximum number of validators before consensus v3.
2051          let validators = sample_validators::<CurrentNetwork>(
2052              consensus_config_value!(CurrentNetwork, MAX_CERTIFICATES, 0).unwrap() as usize,
2053              rng,
2054          );
2055  
2056          // Initialize a new address.
2057          let new_validator_private_key = PrivateKey::<CurrentNetwork>::new(rng).unwrap();
2058          let new_validator_address = Address::try_from(&new_validator_private_key).unwrap();
2059  
2060          // Construct the committee.
2061          // Track the allocated amount.
2062          let (committee_map, allocated_amount) =
2063              sample_committee_map_and_allocated_amount(&validators, &IndexMap::new());
2064  
2065          // Collect all of the addresses in a single place
2066          let validator_addresses =
2067              validators.keys().map(|private_key| Address::try_from(private_key).unwrap()).collect::<Vec<_>>();
2068  
2069          // Construct the public balances, allocating the remaining supply.
2070          let new_validator_balance = MIN_VALIDATOR_STAKE + 100_000_000;
2071          let mut public_balances = sample_public_balances(
2072              &validator_addresses,
2073              <CurrentNetwork as Network>::STARTING_SUPPLY - allocated_amount - new_validator_balance,
2074          );
2075          // Set the public balance of the new validator to the minimum validator stake.
2076          public_balances.insert(new_validator_address, new_validator_balance);
2077  
2078          // Construct the bonded balances.
2079          let bonded_balances = sample_bonded_balances(&validators, &IndexMap::new());
2080  
2081          // Construct the genesis block, which should pass.
2082          let block = vm
2083              .genesis_quorum(
2084                  validators.keys().next().unwrap(),
2085                  Committee::new_genesis(committee_map).unwrap(),
2086                  public_balances,
2087                  bonded_balances,
2088                  rng,
2089              )
2090              .unwrap();
2091  
2092          // Add the block.
2093          vm.add_next_block(&block).unwrap();
2094  
2095          // Attempt to bond a new validator above the maximum number of validators.
2096          let inputs = vec![
2097              Value::<CurrentNetwork>::from_str(&validator_addresses.first().unwrap().to_string()).unwrap(), // Withdrawal address
2098              Value::<CurrentNetwork>::from_str(&format!("{MIN_VALIDATOR_STAKE}u64")).unwrap(),              // Amount
2099              Value::<CurrentNetwork>::from_str("42u8").unwrap(),                                            // Commission
2100          ];
2101  
2102          // Execute.
2103          let bond_validator_transaction = vm
2104              .execute(
2105                  &new_validator_private_key,
2106                  ("credits.delta", "bond_validator"),
2107                  inputs.clone().into_iter(),
2108                  None,
2109                  1,
2110                  None,
2111                  rng,
2112              )
2113              .unwrap();
2114  
2115          // Verify.
2116          vm.check_transaction(&bond_validator_transaction, None, rng).unwrap();
2117  
2118          // Speculate on the transactions.
2119          let transactions = vec![bond_validator_transaction.clone()];
2120          let (_, confirmed_transactions, _, _) = vm
2121              .atomic_speculate(
2122                  sample_finalize_state(1),
2123                  CurrentNetwork::BLOCK_TIME as i64,
2124                  None,
2125                  vec![],
2126                  None.into(),
2127                  transactions,
2128              )
2129              .unwrap();
2130  
2131          // Assert that the transaction is rejected.
2132          assert_eq!(confirmed_transactions.len(), 1);
2133          assert_eq!(
2134              confirmed_transactions[0],
2135              reject(0, &bond_validator_transaction, confirmed_transactions[0].finalize_operations())
2136          );
2137  
2138          // Speculate on the transactions.
2139          let transactions = vec![bond_validator_transaction.clone()];
2140          let (_, confirmed_transactions, aborted_transaction_ids, _) = vm
2141              .atomic_speculate(
2142                  sample_finalize_state(CurrentNetwork::CONSENSUS_HEIGHT(ConsensusVersion::V3).unwrap()),
2143                  CurrentNetwork::BLOCK_TIME as i64,
2144                  None,
2145                  vec![],
2146                  None.into(),
2147                  transactions,
2148              )
2149              .unwrap();
2150  
2151          // Assert that the transaction is accepted.
2152          assert_eq!(confirmed_transactions.len(), 1);
2153          assert!(confirmed_transactions[0].is_accepted());
2154          assert!(aborted_transaction_ids.is_empty());
2155  
2156          assert_eq!(confirmed_transactions[0].transaction(), &bond_validator_transaction);
2157      }
2158  
2159      #[test]
2160      fn test_atomic_finalize_many() {
2161          let rng = &mut TestRng::default();
2162  
2163          // Sample a private key and address for the caller.
2164          let caller_private_key = test_helpers::sample_genesis_private_key(rng);
2165          let caller_address = Address::try_from(&caller_private_key).unwrap();
2166  
2167          // Sample a private key and address for the recipient.
2168          let recipient_private_key = PrivateKey::new(rng).unwrap();
2169          let recipient_address = Address::try_from(&recipient_private_key).unwrap();
2170  
2171          // Initialize the vm.
2172          let vm = test_helpers::sample_vm_with_genesis_block(rng);
2173  
2174          // Deploy a new program.
2175          let genesis =
2176              vm.block_store().get_block(&vm.block_store().get_block_hash(0).unwrap().unwrap()).unwrap().unwrap();
2177  
2178          // Get the unspent records.
2179          let mut unspent_records = genesis
2180              .transitions()
2181              .cloned()
2182              .flat_map(Transition::into_records)
2183              .map(|(_, record)| record)
2184              .collect::<Vec<_>>();
2185  
2186          // Construct the deployment block.
2187          let (program_id, deployment_block) =
2188              new_program_deployment(&vm, &caller_private_key, &genesis, &mut unspent_records, rng).unwrap();
2189  
2190          // Add the deployment block to the VM.
2191          vm.add_next_block(&deployment_block).unwrap();
2192  
2193          // Generate more records to use for the next block.
2194          let splits_block =
2195              generate_splits(&vm, &caller_private_key, &deployment_block, &mut unspent_records, rng).unwrap();
2196  
2197          // Add the splits block to the VM.
2198          vm.add_next_block(&splits_block).unwrap();
2199  
2200          // Construct the initial mint.
2201          let initial_mint =
2202              sample_mint_public(&vm, caller_private_key, &program_id, caller_address, 20, &mut unspent_records, rng);
2203          let initial_mint_block =
2204              sample_next_block(&vm, &caller_private_key, &[initial_mint], &splits_block, &mut unspent_records, rng)
2205                  .unwrap();
2206  
2207          // Add the block to the vm.
2208          vm.add_next_block(&initial_mint_block).unwrap();
2209  
2210          // Construct a mint and a transfer.
2211          let mint_10 =
2212              sample_mint_public(&vm, caller_private_key, &program_id, caller_address, 10, &mut unspent_records, rng);
2213          let mint_20 =
2214              sample_mint_public(&vm, caller_private_key, &program_id, caller_address, 20, &mut unspent_records, rng);
2215          let transfer_10 = sample_transfer_public(
2216              &vm,
2217              caller_private_key,
2218              &program_id,
2219              recipient_address,
2220              10,
2221              &mut unspent_records,
2222              rng,
2223          );
2224          let transfer_20 = sample_transfer_public(
2225              &vm,
2226              caller_private_key,
2227              &program_id,
2228              recipient_address,
2229              20,
2230              &mut unspent_records,
2231              rng,
2232          );
2233          let transfer_30 = sample_transfer_public(
2234              &vm,
2235              caller_private_key,
2236              &program_id,
2237              recipient_address,
2238              30,
2239              &mut unspent_records,
2240              rng,
2241          );
2242  
2243          // TODO (raychu86): Confirm that the finalize_operations here are correct.
2244  
2245          // Starting Balance = 20
2246          // Mint_10 -> Balance = 20 + 10  = 30
2247          // Transfer_10 -> Balance = 30 - 10 = 20
2248          // Transfer_20 -> Balance = 20 - 20 = 0
2249          {
2250              let transactions = vec![mint_10.clone(), transfer_10.clone(), transfer_20.clone()];
2251              let (_, confirmed_transactions, aborted_transaction_ids, _) = vm
2252                  .atomic_speculate(
2253                      sample_finalize_state(1),
2254                      CurrentNetwork::BLOCK_TIME as i64,
2255                      None,
2256                      vec![],
2257                      None.into(),
2258                      transactions,
2259                  )
2260                  .unwrap();
2261  
2262              // Assert that all the transactions are accepted.
2263              assert_eq!(confirmed_transactions.len(), 3);
2264              confirmed_transactions.iter().for_each(|confirmed_tx| assert!(confirmed_tx.is_accepted()));
2265              assert!(aborted_transaction_ids.is_empty());
2266  
2267              assert_eq!(confirmed_transactions[0].transaction(), &mint_10);
2268              assert_eq!(confirmed_transactions[1].transaction(), &transfer_10);
2269              assert_eq!(confirmed_transactions[2].transaction(), &transfer_20);
2270          }
2271  
2272          // Starting Balance = 20
2273          // Transfer_20 -> Balance = 20 - 20 = 0
2274          // Mint_10 -> Balance = 0 + 10 = 10
2275          // Mint_20 -> Balance = 10 + 20 = 30
2276          // Transfer_30 -> Balance = 30 - 30 = 0
2277          {
2278              let transactions = vec![transfer_20.clone(), mint_10.clone(), mint_20.clone(), transfer_30.clone()];
2279              let (_, confirmed_transactions, aborted_transaction_ids, _) = vm
2280                  .atomic_speculate(
2281                      sample_finalize_state(1),
2282                      CurrentNetwork::BLOCK_TIME as i64,
2283                      None,
2284                      vec![],
2285                      None.into(),
2286                      transactions,
2287                  )
2288                  .unwrap();
2289  
2290              // Assert that all the transactions are accepted.
2291              assert_eq!(confirmed_transactions.len(), 4);
2292              confirmed_transactions.iter().for_each(|confirmed_tx| assert!(confirmed_tx.is_accepted()));
2293              assert!(aborted_transaction_ids.is_empty());
2294  
2295              // Ensure that the transactions are in the correct order.
2296              assert_eq!(confirmed_transactions[0].transaction(), &transfer_20);
2297              assert_eq!(confirmed_transactions[1].transaction(), &mint_10);
2298              assert_eq!(confirmed_transactions[2].transaction(), &mint_20);
2299              assert_eq!(confirmed_transactions[3].transaction(), &transfer_30);
2300          }
2301  
2302          // Starting Balance = 20
2303          // Transfer_20 -> Balance = 20 - 20 = 0
2304          // Transfer_10 -> Balance = 0 - 10 = -10 (should be rejected)
2305          {
2306              let transactions = vec![transfer_20.clone(), transfer_10.clone()];
2307              let (_, confirmed_transactions, aborted_transaction_ids, _) = vm
2308                  .atomic_speculate(
2309                      sample_finalize_state(1),
2310                      CurrentNetwork::BLOCK_TIME as i64,
2311                      None,
2312                      vec![],
2313                      None.into(),
2314                      transactions,
2315                  )
2316                  .unwrap();
2317  
2318              // Assert that the accepted and rejected transactions are correct.
2319              assert_eq!(confirmed_transactions.len(), 2);
2320              assert!(aborted_transaction_ids.is_empty());
2321  
2322              assert!(confirmed_transactions[0].is_accepted());
2323              assert!(confirmed_transactions[1].is_rejected());
2324  
2325              assert_eq!(confirmed_transactions[0].transaction(), &transfer_20);
2326              assert_eq!(
2327                  confirmed_transactions[1],
2328                  reject(1, &transfer_10, confirmed_transactions[1].finalize_operations())
2329              );
2330          }
2331  
2332          // Starting Balance = 20
2333          // Mint_20 -> Balance = 20 + 20
2334          // Transfer_30 -> Balance = 40 - 30 = 10
2335          // Transfer_20 -> Balance = 10 - 20 = -10 (should be rejected)
2336          // Transfer_10 -> Balance = 10 - 10 = 0
2337          {
2338              let transactions = vec![mint_20.clone(), transfer_30.clone(), transfer_20.clone(), transfer_10.clone()];
2339              let (_, confirmed_transactions, aborted_transaction_ids, _) = vm
2340                  .atomic_speculate(
2341                      sample_finalize_state(1),
2342                      CurrentNetwork::BLOCK_TIME as i64,
2343                      None,
2344                      vec![],
2345                      None.into(),
2346                      transactions,
2347                  )
2348                  .unwrap();
2349  
2350              // Assert that the accepted and rejected transactions are correct.
2351              assert_eq!(confirmed_transactions.len(), 4);
2352              assert!(aborted_transaction_ids.is_empty());
2353  
2354              assert!(confirmed_transactions[0].is_accepted());
2355              assert!(confirmed_transactions[1].is_accepted());
2356              assert!(confirmed_transactions[2].is_rejected());
2357              assert!(confirmed_transactions[3].is_accepted());
2358  
2359              assert_eq!(confirmed_transactions[0].transaction(), &mint_20);
2360              assert_eq!(confirmed_transactions[1].transaction(), &transfer_30);
2361              assert_eq!(
2362                  confirmed_transactions[2],
2363                  reject(2, &transfer_20, confirmed_transactions[2].finalize_operations())
2364              );
2365              assert_eq!(confirmed_transactions[3].transaction(), &transfer_10);
2366          }
2367      }
2368  
2369      #[test]
2370      fn test_finalize_catch_halt() {
2371          let rng = &mut TestRng::default();
2372  
2373          // Sample a private key, view key, and address for the caller.
2374          let caller_private_key = test_helpers::sample_genesis_private_key(rng);
2375          let caller_view_key = ViewKey::try_from(&caller_private_key).unwrap();
2376  
2377          for finalize_logic in &[
2378              "finalize ped_hash:
2379      input r0 as u128.public;
2380      hash.ped64 r0 into r1 as field;
2381      set r1 into hashes[r0];",
2382              "finalize ped_hash:
2383      input r0 as u128.public;
2384      div r0 0u128 into r1;",
2385          ] {
2386              // Initialize the vm.
2387              let vm = test_helpers::sample_vm_with_genesis_block(rng);
2388  
2389              // Deploy a new program.
2390              let genesis =
2391                  vm.block_store().get_block(&vm.block_store().get_block_hash(0).unwrap().unwrap()).unwrap().unwrap();
2392  
2393              // Get the unspent records.
2394              let mut unspent_records = genesis
2395                  .transitions()
2396                  .cloned()
2397                  .flat_map(Transition::into_records)
2398                  .map(|(_, record)| record)
2399                  .collect::<Vec<_>>();
2400  
2401              // Create a program that will always cause a E::halt in the finalize execution.
2402              let program_id = "testing.delta";
2403              let program = Program::<CurrentNetwork>::from_str(&format!(
2404                  "
2405  program {program_id};
2406  
2407  mapping hashes:
2408      key as u128.public;
2409      value as field.public;
2410  
2411  function ped_hash:
2412      input r0 as u128.public;
2413      // hash.ped64 r0 into r1 as field; // <--- This will cause a E::halt.
2414      async ped_hash r0 into r1;
2415      output r1 as {program_id}/ped_hash.future;
2416  
2417  {finalize_logic}"
2418              ))
2419              .unwrap();
2420  
2421              let credits = Some(unspent_records.pop().unwrap().decrypt(&caller_view_key).unwrap());
2422  
2423              // Deploy the program.
2424              let deployment_transaction = vm.deploy(&caller_private_key, &program, credits, 10, None, rng).unwrap();
2425  
2426              // Construct the deployment block.
2427              let deployment_block = sample_next_block(
2428                  &vm,
2429                  &caller_private_key,
2430                  &[deployment_transaction],
2431                  &genesis,
2432                  &mut unspent_records,
2433                  rng,
2434              )
2435              .unwrap();
2436  
2437              // Add the deployment block to the VM.
2438              vm.add_next_block(&deployment_block).unwrap();
2439  
2440              // Construct a transaction that will cause a E::halt in the finalize execution.
2441              let inputs = vec![Value::<CurrentNetwork>::from_str("1u128").unwrap()];
2442              let transaction =
2443                  create_execution(&vm, caller_private_key, program_id, "ped_hash", inputs, &mut unspent_records, rng);
2444  
2445              // Speculatively execute the transaction. Ensure that this call does not panic and returns a rejected transaction.
2446              let (_, confirmed_transactions, aborted_transaction_ids, _) = vm
2447                  .speculate(
2448                      sample_finalize_state(1),
2449                      CurrentNetwork::BLOCK_TIME as i64,
2450                      None,
2451                      vec![],
2452                      &None.into(),
2453                      [transaction.clone()].iter(),
2454                      rng,
2455                  )
2456                  .unwrap();
2457              assert!(aborted_transaction_ids.is_empty());
2458  
2459              // Ensure that the transaction is rejected.
2460              assert_eq!(confirmed_transactions.len(), 1);
2461              assert!(transaction.is_execute());
2462              if let Transaction::Execute(_, _, execution, fee) = transaction {
2463                  let fee_transaction = Transaction::from_fee(fee.unwrap()).unwrap();
2464                  let expected_confirmed_transaction = ConfirmedTransaction::RejectedExecute(
2465                      0,
2466                      fee_transaction,
2467                      Rejected::new_execution(*execution),
2468                      vec![],
2469                  );
2470  
2471                  let confirmed_transaction = confirmed_transactions.iter().next().unwrap();
2472                  assert_eq!(confirmed_transaction, &expected_confirmed_transaction);
2473              }
2474          }
2475      }
2476  
2477      #[test]
2478      fn test_rejected_transaction_should_not_update_storage() {
2479          let rng = &mut TestRng::default();
2480  
2481          // Sample a private key.
2482          let private_key = test_helpers::sample_genesis_private_key(rng);
2483          let address = Address::try_from(&private_key).unwrap();
2484  
2485          // Initialize the vm.
2486          let vm = test_helpers::sample_vm_with_genesis_block(rng);
2487  
2488          // Deploy a new program.
2489          let genesis =
2490              vm.block_store().get_block(&vm.block_store().get_block_hash(0).unwrap().unwrap()).unwrap().unwrap();
2491  
2492          // Get the unspent records.
2493          let mut unspent_records = genesis
2494              .transitions()
2495              .cloned()
2496              .flat_map(Transition::into_records)
2497              .map(|(_, record)| record)
2498              .collect::<Vec<_>>();
2499  
2500          // Generate more records to use for the next block.
2501          let splits_block = generate_splits(&vm, &private_key, &genesis, &mut unspent_records, rng).unwrap();
2502  
2503          // Add the splits block to the VM.
2504          vm.add_next_block(&splits_block).unwrap();
2505  
2506          // Construct the deployment block.
2507          let deployment_block = {
2508              let program = Program::<CurrentNetwork>::from_str(
2509                  "
2510  program testing.delta;
2511  
2512  mapping entries:
2513      key as address.public;
2514      value as u8.public;
2515  
2516  function compute:
2517      input r0 as u8.public;
2518      async compute self.caller r0 into r1;
2519      output r1 as testing.delta/compute.future;
2520  
2521  finalize compute:
2522      input r0 as address.public;
2523      input r1 as u8.public;
2524      get.or_use entries[r0] r1 into r2;
2525      add r1 r2 into r3;
2526      set r3 into entries[r0];
2527      get entries[r0] into r4;
2528      add r4 r1 into r5;
2529      set r5 into entries[r0];
2530  ",
2531              )
2532              .unwrap();
2533  
2534              // Prepare the additional fee.
2535              let view_key = ViewKey::<CurrentNetwork>::try_from(private_key).unwrap();
2536              let credits = Some(unspent_records.pop().unwrap().decrypt(&view_key).unwrap());
2537  
2538              // Deploy.
2539              let transaction = vm.deploy(&private_key, &program, credits, 10, None, rng).unwrap();
2540  
2541              // Construct the new block.
2542              sample_next_block(&vm, &private_key, &[transaction], &splits_block, &mut unspent_records, rng).unwrap()
2543          };
2544  
2545          // Add the deployment block to the VM.
2546          vm.add_next_block(&deployment_block).unwrap();
2547  
2548          // Generate more records to use for the next block.
2549          let splits_block = generate_splits(&vm, &private_key, &deployment_block, &mut unspent_records, rng).unwrap();
2550  
2551          // Add the splits block to the VM.
2552          vm.add_next_block(&splits_block).unwrap();
2553  
2554          // Create an execution transaction, that will be rejected.
2555          let r0 = Value::<CurrentNetwork>::from_str("100u8").unwrap();
2556          let first = create_execution(&vm, private_key, "testing.delta", "compute", vec![r0], &mut unspent_records, rng);
2557  
2558          // Construct the next block.
2559          let next_block =
2560              sample_next_block(&vm, &private_key, &[first], &splits_block, &mut unspent_records, rng).unwrap();
2561  
2562          // Check that the transaction was rejected.
2563          assert!(next_block.transactions().iter().next().unwrap().is_rejected());
2564  
2565          // Add the next block to the VM.
2566          vm.add_next_block(&next_block).unwrap();
2567  
2568          // Check that the storage was not updated.
2569          let program_id = ProgramID::from_str("testing.delta").unwrap();
2570          let mapping_name = Identifier::from_str("entries").unwrap();
2571          assert!(
2572              !vm.finalize_store()
2573                  .contains_key_confirmed(program_id, mapping_name, &Plaintext::from(Literal::Address(address)))
2574                  .unwrap()
2575          );
2576  
2577          // Create an execution transaction, that will be rejected.
2578          let r0 = Value::<CurrentNetwork>::from_str("100u8").unwrap();
2579          let first = create_execution(&vm, private_key, "testing.delta", "compute", vec![r0], &mut unspent_records, rng);
2580  
2581          // Create an execution transaction, that will be accepted.
2582          let r0 = Value::<CurrentNetwork>::from_str("1u8").unwrap();
2583          let second =
2584              create_execution(&vm, private_key, "testing.delta", "compute", vec![r0], &mut unspent_records, rng);
2585  
2586          // Construct the next block.
2587          let next_block =
2588              sample_next_block(&vm, &private_key, &[first, second], &next_block, &mut unspent_records, rng).unwrap();
2589  
2590          // Check that the first transaction was rejected.
2591          assert!(next_block.transactions().iter().next().unwrap().is_rejected());
2592  
2593          // Add the next block to the VM.
2594          vm.add_next_block(&next_block).unwrap();
2595  
2596          // Check that the storage was updated correctly.
2597          let value = vm
2598              .finalize_store()
2599              .get_value_speculative(program_id, mapping_name, &Plaintext::from(Literal::Address(address)))
2600              .unwrap()
2601              .unwrap();
2602          let expected = Value::<CurrentNetwork>::from_str("3u8").unwrap();
2603          assert_eq!(value, expected);
2604      }
2605  
2606      #[test]
2607      fn test_excess_transactions_should_be_aborted() {
2608          let rng = &mut TestRng::default();
2609  
2610          // Sample a private key.
2611          let caller_private_key = test_helpers::sample_genesis_private_key(rng);
2612          let caller_address = Address::try_from(&caller_private_key).unwrap();
2613  
2614          // Initialize the vm.
2615          let vm = test_helpers::sample_vm_with_genesis_block(rng);
2616  
2617          // Deploy a new program.
2618          let genesis =
2619              vm.block_store().get_block(&vm.block_store().get_block_hash(0).unwrap().unwrap()).unwrap().unwrap();
2620  
2621          // Get the unspent records.
2622          let mut unspent_records = genesis
2623              .transitions()
2624              .cloned()
2625              .flat_map(Transition::into_records)
2626              .map(|(_, record)| record)
2627              .collect::<Vec<_>>();
2628  
2629          // Construct the deployment block.
2630          let (program_id, deployment_block) =
2631              new_program_deployment(&vm, &caller_private_key, &genesis, &mut unspent_records, rng).unwrap();
2632  
2633          // Add the deployment block to the VM.
2634          vm.add_next_block(&deployment_block).unwrap();
2635  
2636          // Generate more records to use for the next block.
2637          let splits_block =
2638              generate_splits(&vm, &caller_private_key, &deployment_block, &mut unspent_records, rng).unwrap();
2639  
2640          // Add the splits block to the VM.
2641          vm.add_next_block(&splits_block).unwrap();
2642  
2643          // Generate more records to use for the next block.
2644          let splits_block = generate_splits(&vm, &caller_private_key, &splits_block, &mut unspent_records, rng).unwrap();
2645  
2646          // Add the splits block to the VM.
2647          vm.add_next_block(&splits_block).unwrap();
2648  
2649          // Generate the transactions.
2650          let mut transactions = Vec::new();
2651          let mut excess_transaction_ids = Vec::new();
2652  
2653          for _ in 0..VM::<CurrentNetwork, LedgerType>::MAXIMUM_CONFIRMED_TRANSACTIONS + 1 {
2654              let transaction =
2655                  sample_mint_public(&vm, caller_private_key, &program_id, caller_address, 10, &mut unspent_records, rng);
2656              // Abort the transaction if the block is full.
2657              if transactions.len() >= VM::<CurrentNetwork, LedgerType>::MAXIMUM_CONFIRMED_TRANSACTIONS {
2658                  excess_transaction_ids.push(transaction.id());
2659              }
2660  
2661              transactions.push(transaction);
2662          }
2663  
2664          // Construct the next block.
2665          let next_block =
2666              sample_next_block(&vm, &caller_private_key, &transactions, &splits_block, &mut unspent_records, rng)
2667                  .unwrap();
2668  
2669          // Ensure that the excess transactions were aborted.
2670          assert_eq!(next_block.aborted_transaction_ids(), &excess_transaction_ids);
2671          assert_eq!(next_block.transactions().len(), VM::<CurrentNetwork, LedgerType>::MAXIMUM_CONFIRMED_TRANSACTIONS);
2672      }
2673  
2674      #[test]
2675      fn test_ratify_genesis_greater_than_max_committee_size() {
2676          // Initialize an RNG.
2677          let rng = &mut TestRng::default();
2678  
2679          // Initialize the VM.
2680          let vm = sample_vm();
2681  
2682          // Construct the validators, greater than the maximum committee size.
2683          let validators = sample_validators::<CurrentNetwork>(
2684              Committee::<CurrentNetwork>::max_committee_size().unwrap() as usize + 1,
2685              rng,
2686          );
2687  
2688          // Construct the committee.
2689          let mut committee_map = IndexMap::new();
2690          for (private_key, (amount, _, _)) in &validators {
2691              let address = Address::try_from(private_key).unwrap();
2692              committee_map.insert(address, (*amount, true, 0));
2693          }
2694  
2695          // Attempt to construct a `Committee` with more than the maximum committee size.
2696          let result = Committee::new_genesis(committee_map);
2697          assert!(result.is_err());
2698  
2699          // Reset the validators.
2700          // Note: We use a smaller committee size to ensure that there is enough supply to allocate to the validators and genesis block transactions.
2701          let validators = sample_validators::<CurrentNetwork>(
2702              consensus_config_value!(CurrentNetwork, MAX_CERTIFICATES, 0).unwrap() as usize,
2703              rng,
2704          );
2705  
2706          // Construct the committee.
2707          // Track the allocated amount.
2708          let (committee_map, allocated_amount) =
2709              sample_committee_map_and_allocated_amount(&validators, &IndexMap::new());
2710  
2711          // Construct the public balances, allocating the remaining supply.
2712          let public_balances = sample_public_balances(
2713              &validators.keys().map(|private_key| Address::try_from(private_key).unwrap()).collect::<Vec<_>>(),
2714              <CurrentNetwork as Network>::STARTING_SUPPLY - allocated_amount,
2715          );
2716  
2717          // Construct the bonded balances.
2718          let bonded_balances = sample_bonded_balances(&validators, &IndexMap::new());
2719  
2720          // Construct the genesis block, which should pass.
2721          let block = vm
2722              .genesis_quorum(
2723                  validators.keys().next().unwrap(),
2724                  Committee::new_genesis(committee_map).unwrap(),
2725                  public_balances,
2726                  bonded_balances,
2727                  rng,
2728              )
2729              .unwrap();
2730  
2731          // Add the block.
2732          vm.add_next_block(&block).unwrap();
2733      }
2734  
2735      // Note that the maximum delegator size is large enough that the ratification ID cannot be computed.
2736      #[test]
2737      fn test_ratify_genesis_greater_than_max_delegator_size() {
2738          // Initialize an RNG.
2739          let rng = &mut TestRng::default();
2740  
2741          // Initialize the VM.
2742          let vm = sample_vm();
2743  
2744          // Construct the validators.
2745          // Note: We use a smaller committee size to ensure that there is enough supply to allocate to the validators and genesis block transactions.
2746          let validators = sample_validators::<CurrentNetwork>(
2747              consensus_config_value!(CurrentNetwork, MAX_CERTIFICATES, 0).unwrap() as usize / 4,
2748              rng,
2749          );
2750  
2751          // Construct the delegators, greater than the maximum delegator size.
2752          let delegators = (0..MAX_DELEGATORS + 1)
2753              .map(|_| {
2754                  let private_key = PrivateKey::<CurrentNetwork>::new(rng).unwrap();
2755                  let validator = Address::try_from(validators.keys().next().unwrap()).unwrap();
2756                  let amount = MIN_DELEGATOR_STAKE;
2757                  (private_key, (validator, amount))
2758              })
2759              .collect::<IndexMap<_, _>>();
2760  
2761          // Construct the committee.
2762          // Track the allocated amount.
2763          let (committee_map, allocated_amount) = sample_committee_map_and_allocated_amount(&validators, &delegators);
2764  
2765          // Construct the public balances, allocating the remaining supply to the validators and zero to the delegators.
2766          let mut public_balances = sample_public_balances(
2767              &validators.keys().map(|private_key| Address::try_from(private_key).unwrap()).collect::<Vec<_>>(),
2768              <CurrentNetwork as Network>::STARTING_SUPPLY - allocated_amount,
2769          );
2770          public_balances.extend(sample_public_balances(
2771              &delegators.keys().map(|private_key| Address::try_from(private_key).unwrap()).collect::<Vec<_>>(),
2772              0,
2773          ));
2774  
2775          // Construct the bonded balances.
2776          let bonded_balances = sample_bonded_balances(&validators, &delegators);
2777  
2778          // Construct the genesis block, which should fail.
2779          let result = vm.genesis_quorum(
2780              validators.keys().next().unwrap(),
2781              Committee::new_genesis(committee_map).unwrap(),
2782              public_balances,
2783              bonded_balances,
2784              rng,
2785          );
2786          assert!(result.is_err());
2787      }
2788  
2789      #[test]
2790      fn test_ratify_genesis_is_correct() {
2791          const NUM_VALIDATORS: usize = 5;
2792          const NUM_DELEGATORS: usize = 8;
2793  
2794          // Sample an RNG.
2795          let rng = &mut TestRng::default();
2796  
2797          println!("Initializing VMs.");
2798  
2799          // Initialize the VM.
2800          let vm = sample_vm();
2801  
2802          println!("Constructing validator and delegator sets.");
2803  
2804          // Sample the validators.
2805          let validators = sample_validators(NUM_VALIDATORS, rng);
2806  
2807          // Sample the delegators, cycling through the validators.
2808          let delegators: IndexMap<_, _> = (0..NUM_DELEGATORS)
2809              .map(|i| {
2810                  let private_key = PrivateKey::new(rng).unwrap();
2811                  let validator = Address::try_from(validators.keys().nth(i % NUM_VALIDATORS).unwrap()).unwrap();
2812                  let amount = MIN_DELEGATOR_STAKE;
2813                  (private_key, (validator, amount))
2814              })
2815              .collect();
2816  
2817          // Sample a genesis block without any delegators.
2818          // Specifically, the genesis block will contain a `Ratification` with:
2819          //   - the committee state, containing only the validator amounts.
2820          //   - the public balances for the delegators, with 10_000_000u64 microcredits each (plus 843_880u64 microcredits for fees).
2821          //   - the public balances for the validators dividing up the remaining starting supply.
2822          //   - the bonded balances, only containing the validators.
2823  
2824          println!("Initializing the VM.");
2825  
2826          // Construct the committee.
2827          // Track the allocated amount.
2828          let (committee_map, allocated_amount) = sample_committee_map_and_allocated_amount(&validators, &delegators);
2829          let committee = Committee::new_genesis(committee_map).unwrap();
2830  
2831          // Construct the public balances, allocating the remaining supply to the validators and zero to the delegators.
2832          let mut public_balances = sample_public_balances(
2833              &validators.keys().map(|private_key| Address::try_from(private_key).unwrap()).collect::<Vec<_>>(),
2834              <CurrentNetwork as Network>::STARTING_SUPPLY - allocated_amount,
2835          );
2836          public_balances.extend(sample_public_balances(
2837              &delegators.keys().map(|private_key| Address::try_from(private_key).unwrap()).collect::<Vec<_>>(),
2838              0,
2839          ));
2840  
2841          // Construct the bonded balances.
2842          let bonded_balances = sample_bonded_balances(&validators, &delegators);
2843  
2844          println!("Generating the genesis block.");
2845  
2846          let genesis = vm
2847              .genesis_quorum(
2848                  validators.keys().next().unwrap(),
2849                  committee.clone(),
2850                  public_balances.clone(),
2851                  bonded_balances.clone(),
2852                  rng,
2853              )
2854              .unwrap();
2855  
2856          println!("Adding the genesis block to the VM.");
2857  
2858          // Add the genesis block to the VM.
2859          vm.add_next_block(&genesis).unwrap();
2860  
2861          // Check that the state of the `credits.delta` program is correct.
2862          let program_id = ProgramID::from_str("credits.delta").unwrap();
2863          let committee_mapping_name = Identifier::from_str("committee").unwrap();
2864          let account_mapping_name = Identifier::from_str("account").unwrap();
2865          let bonded_mapping_name = Identifier::from_str("bonded").unwrap();
2866          let metadata_mapping_name = Identifier::from_str("metadata").unwrap();
2867          let unbonding_mapping_name = Identifier::from_str("unbonding").unwrap();
2868          let withdraw_mapping_name = Identifier::from_str("withdraw").unwrap();
2869  
2870          // Get and check the committee mapping.
2871          let actual_committee = vm.finalize_store().get_mapping_confirmed(program_id, committee_mapping_name).unwrap();
2872          let expected_committee = committee
2873              .members()
2874              .iter()
2875              .map(|(address, (_, is_open, commission))| {
2876                  (
2877                      Plaintext::from_str(&address.to_string()).unwrap(),
2878                      Value::from_str(&format!("{{ is_open: {is_open}, commission: {commission}u8 }}")).unwrap(),
2879                  )
2880              })
2881              .collect_vec();
2882          // Note that `actual_committee` and `expected_committee` are vectors and not necessarily in the same order.
2883          // By checking that the lengths of the vector are equal and that all entries in `actual_committee` are in `expected_committee`,
2884          // we can ensure that the two vectors contain the same data.
2885          assert_eq!(actual_committee.len(), expected_committee.len());
2886          for entry in actual_committee.iter() {
2887              assert!(expected_committee.contains(entry));
2888          }
2889  
2890          // Get and check the account mapping.
2891          let actual_account = vm.finalize_store().get_mapping_confirmed(program_id, account_mapping_name).unwrap();
2892          let expected_account = public_balances
2893              .iter()
2894              .map(|(address, amount)| {
2895                  (Plaintext::from_str(&address.to_string()).unwrap(), Value::from_str(&format!("{amount}u64")).unwrap())
2896              })
2897              .collect_vec();
2898          // Note that `actual_account` and `expected_account` are vectors and not necessarily in the same order.
2899          // By checking that the lengths of the vector are equal and that all entries in `actual_account` are in `expected_account`,
2900          // we can ensure that the two vectors contain the same data.
2901          assert_eq!(actual_account.len(), expected_account.len());
2902          // Check that all entries except for the first validator are the same.
2903          for entry in actual_account.iter() {
2904              let first_validator = Address::try_from(validators.keys().next().unwrap()).unwrap();
2905              // Note that the first validator is used to execute additional transactions in `VM::genesis_quorum`.
2906              // Therefore, the balance of the first validator will be different from the expected balance.
2907              if entry.0 == Plaintext::from_str(&first_validator.to_string()).unwrap() {
2908                  assert_eq!(entry.1, Value::from_str("144991999894112u64").unwrap());
2909              } else {
2910                  assert!(expected_account.contains(entry));
2911              }
2912          }
2913  
2914          // Get and check the bonded mapping.
2915          let actual_bonded = vm.finalize_store().get_mapping_confirmed(program_id, bonded_mapping_name).unwrap();
2916          let expected_bonded = bonded_balances
2917              .iter()
2918              .map(|(address, (validator, _, amount))| {
2919                  (
2920                      Plaintext::from_str(&address.to_string()).unwrap(),
2921                      Value::from_str(&format!("{{ validator: {validator}, microcredits: {amount}u64 }}")).unwrap(),
2922                  )
2923              })
2924              .collect_vec();
2925          // Note that `actual_bonded` and `expected_bonded` are vectors and not necessarily in the same order.
2926          // By checking that the lengths of the vector are equal and that all entries in `actual_bonded` are in `expected_bonded`,
2927          // we can ensure that the two vectors contain the same data.
2928          assert_eq!(actual_bonded.len(), expected_bonded.len());
2929          for entry in actual_bonded.iter() {
2930              assert!(expected_bonded.contains(entry));
2931          }
2932  
2933          // Get and check the withdraw mapping.
2934          let actual_withdraw = vm.finalize_store().get_mapping_confirmed(program_id, withdraw_mapping_name).unwrap();
2935          let expected_withdraw = bonded_balances
2936              .iter()
2937              .map(|(address, (_, withdrawal_address, _))| {
2938                  (
2939                      Plaintext::from_str(&address.to_string()).unwrap(),
2940                      Value::from_str(&withdrawal_address.to_string()).unwrap(),
2941                  )
2942              })
2943              .collect_vec();
2944          // Note that `actual_withdraw` and `expected_withdraw` are vectors and not necessarily in the same order.
2945          // By checking that the lengths of the vector are equal and that all entries in `actual_withdraw` are in `expected_withdraw`,
2946          // we can ensure that the two vectors contain the same data.
2947          assert_eq!(actual_withdraw.len(), expected_withdraw.len());
2948          for entry in actual_withdraw.iter() {
2949              assert!(expected_withdraw.contains(entry));
2950          }
2951  
2952          // Get and check the entry in metadata mapping corresponding to the number of validators.
2953          let num_validators = vm
2954              .finalize_store()
2955              .get_value_confirmed(
2956                  program_id,
2957                  metadata_mapping_name,
2958                  &Plaintext::from_str("dx1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3qtczd").unwrap(),
2959              )
2960              .unwrap()
2961              .unwrap();
2962          assert_eq!(num_validators, Value::from_str(&format!("{NUM_VALIDATORS}u32")).unwrap());
2963  
2964          // Get and check the entry in metadata mapping corresponding to the number of delegators.
2965          let num_delegators = vm
2966              .finalize_store()
2967              .get_value_confirmed(
2968                  program_id,
2969                  metadata_mapping_name,
2970                  &Plaintext::from_str("dx1qgqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqavzal6").unwrap(),
2971              )
2972              .unwrap()
2973              .unwrap();
2974          assert_eq!(num_delegators, Value::from_str(&format!("{NUM_DELEGATORS}u32")).unwrap());
2975  
2976          // Get and check the unbonding mapping.
2977          let actual_unbonding = vm.finalize_store().get_mapping_confirmed(program_id, unbonding_mapping_name).unwrap();
2978          assert!(actual_unbonding.is_empty());
2979      }
2980  
2981      #[test]
2982      fn test_ratify_genesis_is_consistent() {
2983          const NUM_VALIDATORS: usize = 5;
2984          const NUM_DELEGATORS: usize = 8;
2985  
2986          // Sample an RNG.
2987          let rng = &mut TestRng::default();
2988  
2989          println!("Initializing VMs.");
2990  
2991          // Initialize two VMs.
2992          let vm_1 = sample_vm();
2993          let vm_2 = sample_vm();
2994  
2995          println!("Constructing validator and delegator sets.");
2996  
2997          // Sample the validators.
2998          let validators = sample_validators(NUM_VALIDATORS, rng);
2999  
3000          // Sample the delegators, cycling through the validators.
3001          let delegators: IndexMap<_, _> = (0..NUM_DELEGATORS)
3002              .map(|i| {
3003                  let private_key = PrivateKey::new(rng).unwrap();
3004                  let validator = Address::try_from(validators.keys().nth(i % NUM_VALIDATORS).unwrap()).unwrap();
3005                  let amount = MIN_DELEGATOR_STAKE;
3006                  (private_key, (validator, amount))
3007              })
3008              .collect();
3009  
3010          // For the first VM, sample a genesis block without any delegators.
3011          // Specifically, the genesis block will contain a `Ratification` with:
3012          //   - the committee state, containing only the validator amounts.
3013          //   - the public balances for the delegators, with 10_000_000u64 microcredits each (plus 843_880u64 microcredits for fees).
3014          //   - the public balances for the validators dividing up the remaining starting supply.
3015          //   - the bonded balances, only containing the validators.
3016  
3017          println!("Initializing the first VM.");
3018  
3019          // Construct the committee.
3020          // Track the allocated amount.
3021          let (committee_map, mut allocated_amount) =
3022              sample_committee_map_and_allocated_amount(&validators, &IndexMap::new());
3023          let committee = Committee::new_genesis(committee_map).unwrap();
3024  
3025          // Construct the public balances.
3026          let mut public_balances = IndexMap::new();
3027          for (private_key, (_validator, _amount)) in &delegators {
3028              let address = Address::try_from(private_key).unwrap();
3029              let amount = MIN_DELEGATOR_STAKE * 2;
3030              public_balances.insert(address, amount);
3031              allocated_amount += amount;
3032          }
3033          public_balances.extend(sample_public_balances(
3034              &validators.keys().map(|private_key| Address::try_from(private_key).unwrap()).collect::<Vec<_>>(),
3035              <CurrentNetwork as Network>::STARTING_SUPPLY - allocated_amount,
3036          ));
3037  
3038          // Construct the bonded balances.
3039          let bonded_balances = sample_bonded_balances(&validators, &IndexMap::new());
3040  
3041          println!("[VM1] Generating the genesis block.");
3042  
3043          let genesis_1 = vm_1
3044              .genesis_quorum(validators.keys().next().unwrap(), committee, public_balances, bonded_balances, rng)
3045              .unwrap();
3046  
3047          println!("[VM1] Adding the genesis block to the VM.");
3048  
3049          // Add the genesis block to the VM.
3050          vm_1.add_next_block(&genesis_1).unwrap();
3051  
3052          println!("[VM1] Generating bond transactions for each of the delegators.");
3053  
3054          // Generate bond transactions for each of the delegators.
3055          let mut transactions = Vec::new();
3056          for (private_key, (validator, amount)) in &delegators {
3057              let transaction = vm_1
3058                  .execute(
3059                      private_key,
3060                      ("credits.delta", "bond_public"),
3061                      vec![
3062                          Value::<CurrentNetwork>::from_str(&validator.to_string()).unwrap(),
3063                          Value::<CurrentNetwork>::from_str(&Address::try_from(private_key).unwrap().to_string())
3064                              .unwrap(),
3065                          Value::<CurrentNetwork>::from_str(&format!("{amount}u64")).unwrap(),
3066                      ]
3067                      .into_iter(),
3068                      None,
3069                      0,
3070                      None,
3071                      rng,
3072                  )
3073                  .unwrap();
3074              transactions.push(transaction);
3075          }
3076  
3077          println!("[VM1] Generating the next block.");
3078          let next_block =
3079              sample_next_block(&vm_1, validators.keys().next().unwrap(), &transactions, &genesis_1, &mut vec![], rng)
3080                  .unwrap();
3081  
3082          println!("[VM1] Adding the next block to the VM.");
3083          vm_1.add_next_block(&next_block).unwrap();
3084  
3085          // For the second VM, sample a genesis block with the same validators and delegators.
3086          // Specifically, the genesis block will contain a `Ratification` with:
3087          //   - the committee state, containing the total staked amount per validator.
3088          //   - the public balances for the delegators, with 0 microcredits each.
3089          //   - the public balances for the validators dividing up the remaining starting supply.
3090          //   - the bonded balances, containing the validators and delegators.
3091  
3092          println!("Initializing the second VM.");
3093  
3094          // Construct the committee.
3095          // Track the allocated amount.
3096          let (committee_map, allocated_amount) = sample_committee_map_and_allocated_amount(&validators, &delegators);
3097          let committee = Committee::new_genesis(committee_map).unwrap();
3098  
3099          // Construct the public balances, allocating the remaining supply to the validators and zero to the delegators.
3100          let mut public_balances = sample_public_balances(
3101              &validators.keys().map(|private_key| Address::try_from(private_key).unwrap()).collect::<Vec<_>>(),
3102              <CurrentNetwork as Network>::STARTING_SUPPLY - allocated_amount,
3103          );
3104          public_balances.extend(sample_public_balances(
3105              &delegators.keys().map(|private_key| Address::try_from(private_key).unwrap()).collect::<Vec<_>>(),
3106              0,
3107          ));
3108  
3109          // Construct the bonded balances.
3110          let bonded_balances = sample_bonded_balances(&validators, &delegators);
3111  
3112          println!("[VM2] Generating the genesis block.");
3113  
3114          // Construct the genesis block.
3115          let genesis_2 = vm_2
3116              .genesis_quorum(validators.keys().next().unwrap(), committee, public_balances, bonded_balances, rng)
3117              .unwrap();
3118  
3119          println!("[VM2] Adding the genesis block to the VM.");
3120  
3121          // Add the genesis block to the VM.
3122          vm_2.add_next_block(&genesis_2).unwrap();
3123  
3124          println!("Checking that all mappings in `credits.delta` are equal across the two VMs.");
3125  
3126          // Check that all mappings in `credits.delta` are equal across the two VMs.
3127          let program_id = ProgramID::from_str("credits.delta").unwrap();
3128          let committee_mapping_name = Identifier::from_str("committee").unwrap();
3129          let bonded_mapping_name = Identifier::from_str("bonded").unwrap();
3130          let unbonding_mapping_name = Identifier::from_str("unbonding").unwrap();
3131          let account_mapping_name = Identifier::from_str("account").unwrap();
3132          let metadata_mapping_name = Identifier::from_str("metadata").unwrap();
3133          let withdraw_mapping_name = Identifier::from_str("withdraw").unwrap();
3134  
3135          let committee_1 = vm_1.finalize_store().get_mapping_confirmed(program_id, committee_mapping_name).unwrap();
3136          let committee_2 = vm_2.finalize_store().get_mapping_confirmed(program_id, committee_mapping_name).unwrap();
3137          assert_eq!(committee_1, committee_2);
3138  
3139          let bonded_1 = vm_1.finalize_store().get_mapping_confirmed(program_id, bonded_mapping_name).unwrap();
3140          let bonded_2 = vm_2.finalize_store().get_mapping_confirmed(program_id, bonded_mapping_name).unwrap();
3141          assert_eq!(bonded_1, bonded_2);
3142  
3143          let unbonding_1 = vm_1.finalize_store().get_mapping_confirmed(program_id, unbonding_mapping_name).unwrap();
3144          let unbonding_2 = vm_2.finalize_store().get_mapping_confirmed(program_id, unbonding_mapping_name).unwrap();
3145          assert_eq!(unbonding_1, unbonding_2);
3146  
3147          // Check that the account mapping across both VMs have the same keys.
3148          let account_1 = vm_1
3149              .finalize_store()
3150              .get_mapping_confirmed(program_id, account_mapping_name)
3151              .unwrap()
3152              .into_iter()
3153              .map(|(k, _)| k.to_string())
3154              .collect::<std::collections::HashSet<_>>();
3155          let account_2 = vm_2
3156              .finalize_store()
3157              .get_mapping_confirmed(program_id, account_mapping_name)
3158              .unwrap()
3159              .into_iter()
3160              .map(|(k, _)| k.to_string())
3161              .collect::<std::collections::HashSet<_>>();
3162          assert_eq!(account_1, account_2);
3163  
3164          // Check that the metadata mapping across both VMs are equal.
3165          let metadata_1 = vm_1.finalize_store().get_mapping_confirmed(program_id, metadata_mapping_name).unwrap();
3166          let metadata_2 = vm_2.finalize_store().get_mapping_confirmed(program_id, metadata_mapping_name).unwrap();
3167          assert_eq!(metadata_1, metadata_2);
3168  
3169          // Check that the withdraw mapping across both VMs are equal.
3170          let withdraw_1 = vm_1.finalize_store().get_mapping_confirmed(program_id, withdraw_mapping_name).unwrap();
3171          let withdraw_2 = vm_2.finalize_store().get_mapping_confirmed(program_id, withdraw_mapping_name).unwrap();
3172          assert_eq!(withdraw_1, withdraw_2);
3173      }
3174  
3175      #[test]
3176      fn test_ratify_genesis_with_insufficient_validator_balance() {
3177          // Sample an RNG.
3178          let rng = &mut TestRng::default();
3179  
3180          // Initialize the VM.
3181          let vm = sample_vm();
3182  
3183          // Attempt to construct a genesis quorum, with a validator with an insufficient amount.
3184          let mut validators = (0..3)
3185              .map(|_| {
3186                  let private_key = PrivateKey::<CurrentNetwork>::new(rng).unwrap();
3187                  let address = Address::try_from(&private_key).unwrap();
3188                  let amount = MIN_VALIDATOR_STAKE;
3189                  let is_open = true;
3190                  let commission = 0u8;
3191                  (address, (amount, is_open, commission))
3192              })
3193              .collect::<IndexMap<_, _>>();
3194          validators
3195              .insert(Address::try_from(PrivateKey::new(rng).unwrap()).unwrap(), (MIN_VALIDATOR_STAKE - 1, true, 0));
3196  
3197          // Construct the committee.
3198          let result = Committee::new_genesis(validators);
3199          assert!(result.is_err());
3200  
3201          // Track the allocated amount.
3202          let mut allocated_amount = 0;
3203  
3204          // Reset the validators.
3205          let validators = sample_validators(4, rng);
3206  
3207          // Construct the committee.
3208          let committee = Committee::new_genesis(
3209              validators
3210                  .iter()
3211                  .map(|(private_key, (amount, _, _))| {
3212                      let address = Address::try_from(private_key).unwrap();
3213                      allocated_amount += *amount;
3214                      (address, (*amount, true, 0u8))
3215                  })
3216                  .collect(),
3217          )
3218          .unwrap();
3219  
3220          // Construct the public balances, allocating the remaining supply to rest of the validators.
3221          let public_balances = sample_public_balances(
3222              &validators.keys().map(|private_key| Address::try_from(private_key).unwrap()).collect::<Vec<_>>(),
3223              <CurrentNetwork as Network>::STARTING_SUPPLY - allocated_amount,
3224          );
3225  
3226          // Construct the bonded balances.
3227          let bonded_balances = sample_bonded_balances(&validators, &IndexMap::new());
3228  
3229          // Construct the genesis block, which should pass.
3230          let block = vm
3231              .genesis_quorum(validators.keys().next().unwrap(), committee, public_balances, bonded_balances, rng)
3232              .unwrap();
3233  
3234          // Add the block.
3235          vm.add_next_block(&block).unwrap();
3236      }
3237  
3238      #[test]
3239      fn test_ratify_genesis_with_insufficient_delegator_balance() {
3240          // Sample an RNG.
3241          let rng = &mut TestRng::default();
3242  
3243          // Initialize the VM.
3244          let vm = sample_vm();
3245  
3246          // Track the allocated amount.
3247          let mut allocated_amount = 0;
3248  
3249          // Sample the validators.
3250          let validators = sample_validators(4, rng);
3251  
3252          // Attempt to construct a genesis quorum, with a delegator with an insufficient amount.
3253          let mut delegators = IndexMap::new();
3254          delegators.insert(
3255              PrivateKey::new(rng).unwrap(),
3256              (Address::try_from(validators.keys().next().unwrap()).unwrap(), MIN_DELEGATOR_STAKE - 1),
3257          );
3258  
3259          // Construct the committee.
3260          let mut committee_map = IndexMap::new();
3261          for (private_key, (amount, _, _)) in &validators {
3262              let address = Address::try_from(private_key).unwrap();
3263              let amount = if address == Address::try_from(validators.keys().next().unwrap()).unwrap() {
3264                  *amount + MIN_DELEGATOR_STAKE - 1
3265              } else {
3266                  *amount
3267              };
3268              committee_map.insert(address, (amount, true, 0u8));
3269              allocated_amount += amount;
3270          }
3271          let committee = Committee::new_genesis(committee_map).unwrap();
3272  
3273          // Construct the public balances, allocating the remaining supply to rest of the validators.
3274          let mut public_balances = sample_public_balances(
3275              &validators.keys().map(|private_key| Address::try_from(private_key).unwrap()).collect::<Vec<_>>(),
3276              <CurrentNetwork as Network>::STARTING_SUPPLY - allocated_amount,
3277          );
3278          public_balances
3279              .extend(sample_public_balances(&[Address::try_from(delegators.keys().next().unwrap()).unwrap()], 0));
3280  
3281          // Construct the bonded balances.
3282          let bonded_balances = sample_bonded_balances(&validators, &delegators);
3283  
3284          // Construct the genesis block, which should fail.
3285          let result =
3286              vm.genesis_quorum(validators.keys().next().unwrap(), committee, public_balances, bonded_balances, rng);
3287          assert!(result.is_err());
3288  
3289          // Reset the delegators.
3290          let mut delegators = IndexMap::new();
3291          delegators.insert(
3292              PrivateKey::new(rng).unwrap(),
3293              (Address::try_from(validators.keys().next().unwrap()).unwrap(), MIN_DELEGATOR_STAKE),
3294          );
3295  
3296          // Track the allocated amount.
3297          let mut allocated_amount = 0;
3298  
3299          // Construct the committee.
3300          let mut committee_map = IndexMap::new();
3301          for (private_key, (amount, _, _)) in &validators {
3302              let address = Address::try_from(private_key).unwrap();
3303              let amount = if address == Address::try_from(validators.keys().next().unwrap()).unwrap() {
3304                  *amount + MIN_DELEGATOR_STAKE
3305              } else {
3306                  *amount
3307              };
3308              committee_map.insert(address, (amount, true, 0u8));
3309              allocated_amount += amount;
3310          }
3311          let committee = Committee::new_genesis(committee_map).unwrap();
3312  
3313          // Construct the public balances, allocating the remaining supply to rest of the validators.
3314          let mut public_balances = sample_public_balances(
3315              &validators.keys().map(|private_key| Address::try_from(private_key).unwrap()).collect::<Vec<_>>(),
3316              <CurrentNetwork as Network>::STARTING_SUPPLY - allocated_amount,
3317          );
3318          public_balances
3319              .extend(sample_public_balances(&[Address::try_from(delegators.keys().next().unwrap()).unwrap()], 0));
3320  
3321          // Construct the bonded balances.
3322          let bonded_balances = sample_bonded_balances(&validators, &delegators);
3323  
3324          // Construct the genesis block, which should pass.
3325          let block = vm
3326              .genesis_quorum(validators.keys().next().unwrap(), committee, public_balances, bonded_balances, rng)
3327              .unwrap();
3328  
3329          // Add the block.
3330          vm.add_next_block(&block).unwrap();
3331      }
3332  
3333      #[test]
3334      fn test_ratify_genesis_with_incorrect_committee_amounts() {
3335          // Sample an RNG.
3336          let rng = &mut TestRng::default();
3337  
3338          // Initialize the VM.
3339          let vm = sample_vm();
3340  
3341          // Initialize the validators.
3342          let validators = sample_validators(4, rng);
3343  
3344          // Initialize the delegators.
3345          let delegators = (0..4)
3346              .map(|_| {
3347                  let private_key = PrivateKey::new(rng).unwrap();
3348                  let validator = Address::try_from(validators.keys().next().unwrap()).unwrap();
3349                  let amount = MIN_DELEGATOR_STAKE;
3350                  (private_key, (validator, amount))
3351              })
3352              .collect::<IndexMap<_, _>>();
3353  
3354          // Construct the **incorrect** committee.
3355          // Track the allocated amount.
3356          // Note: this committee is missing the additional stake from the delegators.
3357          let (committee_map, allocated_amount) =
3358              sample_committee_map_and_allocated_amount(&validators, &IndexMap::new());
3359          let committee = Committee::new_genesis(committee_map).unwrap();
3360  
3361          // Construct the public balances, allocating the remaining supply to rest of the validators.
3362          let mut public_balances = sample_public_balances(
3363              &validators.keys().map(|private_key| Address::try_from(private_key).unwrap()).collect::<Vec<_>>(),
3364              <CurrentNetwork as Network>::STARTING_SUPPLY - allocated_amount,
3365          );
3366          public_balances.extend(sample_public_balances(
3367              &delegators.keys().map(|private_key| Address::try_from(private_key).unwrap()).collect::<Vec<_>>(),
3368              0,
3369          ));
3370  
3371          // Construct the bonded balances.
3372          let bonded_balances = sample_bonded_balances(&validators, &delegators);
3373  
3374          // Construct the genesis block, which should fail.
3375          let result = vm.genesis_quorum(
3376              validators.keys().next().unwrap(),
3377              committee,
3378              public_balances,
3379              bonded_balances.clone(),
3380              rng,
3381          );
3382          assert!(result.is_err());
3383  
3384          // Construct the **correct** committee.
3385          // Reset the tracked amount.
3386          let (committee_map, allocated_amount) = sample_committee_map_and_allocated_amount(&validators, &delegators);
3387          let committee = Committee::new_genesis(committee_map).unwrap();
3388  
3389          // Construct the public balances, allocating the remaining supply to rest of the validators.
3390          let mut public_balances = sample_public_balances(
3391              &validators.keys().map(|private_key| Address::try_from(private_key).unwrap()).collect::<Vec<_>>(),
3392              <CurrentNetwork as Network>::STARTING_SUPPLY - allocated_amount,
3393          );
3394          public_balances.extend(sample_public_balances(
3395              &delegators.keys().map(|private_key| Address::try_from(private_key).unwrap()).collect::<Vec<_>>(),
3396              0,
3397          ));
3398  
3399          // Construct the genesis block, which should pass.
3400          let block = vm
3401              .genesis_quorum(validators.keys().next().unwrap(), committee, public_balances, bonded_balances, rng)
3402              .unwrap();
3403  
3404          // Add the block.
3405          vm.add_next_block(&block).unwrap();
3406      }
3407  
3408      #[test]
3409      fn test_ratify_genesis_with_closed_validator() {
3410          // Sample an RNG.
3411          let rng = &mut TestRng::default();
3412  
3413          // Initialize the VM.
3414          let vm = sample_vm();
3415  
3416          // Initialize the validators, with one closed.
3417          let validators = (0..4)
3418              .map(|i| {
3419                  let private_key = PrivateKey::new(rng).unwrap();
3420                  let amount = MIN_VALIDATOR_STAKE;
3421                  let is_open = i != 0;
3422                  let commission = 0;
3423                  (private_key, (amount, is_open, commission))
3424              })
3425              .collect::<IndexMap<_, _>>();
3426  
3427          // Initialize a potential delegator.
3428          let delegator_key = PrivateKey::new(rng).unwrap();
3429          let delegator_address = Address::try_from(delegator_key).unwrap();
3430  
3431          // Construct the committee.
3432          // Track the allocated amount.
3433          let (committee_map, allocated_amount) =
3434              sample_committee_map_and_allocated_amount(&validators, &IndexMap::new());
3435  
3436          // Construct the public balances, allocating half to the first validator and the remaining to the delegator.
3437          let public_balances = sample_public_balances(
3438              &[Address::try_from(validators.keys().next().unwrap()).unwrap(), delegator_address],
3439              <CurrentNetwork as Network>::STARTING_SUPPLY - allocated_amount,
3440          );
3441  
3442          // Construct the bonded balances.
3443          let bonded_balances = sample_bonded_balances(&validators, &IndexMap::new());
3444  
3445          // Construct the genesis block, which should pass.
3446          let block = vm
3447              .genesis_quorum(
3448                  validators.keys().next().unwrap(),
3449                  Committee::new_genesis(committee_map).unwrap(),
3450                  public_balances,
3451                  bonded_balances,
3452                  rng,
3453              )
3454              .unwrap();
3455  
3456          // Add the block.
3457          vm.add_next_block(&block).unwrap();
3458  
3459          // Attempt to bond the potential delegator to the closed validator.
3460          let transaction = vm
3461              .execute(
3462                  &delegator_key,
3463                  ("credits.delta", "bond_public"),
3464                  vec![
3465                      Value::<CurrentNetwork>::from_str(
3466                          &Address::try_from(validators.keys().next().unwrap()).unwrap().to_string(),
3467                      )
3468                      .unwrap(),
3469                      Value::<CurrentNetwork>::from_str(&Address::try_from(delegator_key).unwrap().to_string()).unwrap(),
3470                      Value::<CurrentNetwork>::from_str(&format!("{MIN_DELEGATOR_STAKE}u64")).unwrap(),
3471                  ]
3472                  .into_iter(),
3473                  None,
3474                  0,
3475                  None,
3476                  rng,
3477              )
3478              .unwrap();
3479  
3480          // Generate the next block.
3481          let next_block =
3482              sample_next_block(&vm, validators.keys().next().unwrap(), &vec![transaction], &block, &mut vec![], rng)
3483                  .unwrap();
3484  
3485          // Add the next block.
3486          vm.add_next_block(&next_block).unwrap();
3487  
3488          // Check that the delegator is not in the `bonded` mapping.
3489          let bonded_mapping = vm
3490              .finalize_store()
3491              .get_mapping_confirmed(
3492                  ProgramID::from_str("credits.delta").unwrap(),
3493                  Identifier::from_str("bonded").unwrap(),
3494              )
3495              .unwrap();
3496          assert_eq!(bonded_mapping.len(), validators.len());
3497  
3498          // Attempt to bond the potential delegator to the open validator.
3499          let transaction = vm
3500              .execute(
3501                  &delegator_key,
3502                  ("credits.delta", "bond_public"),
3503                  vec![
3504                      Value::<CurrentNetwork>::from_str(
3505                          &Address::try_from(validators.keys().nth(1).unwrap()).unwrap().to_string(),
3506                      )
3507                      .unwrap(),
3508                      Value::<CurrentNetwork>::from_str(&Address::try_from(delegator_key).unwrap().to_string()).unwrap(),
3509                      Value::<CurrentNetwork>::from_str(&format!("{MIN_DELEGATOR_STAKE}u64")).unwrap(),
3510                  ]
3511                  .into_iter(),
3512                  None,
3513                  0,
3514                  None,
3515                  rng,
3516              )
3517              .unwrap();
3518  
3519          // Generate the next block.
3520          let next_block = sample_next_block(
3521              &vm,
3522              validators.keys().next().unwrap(),
3523              &vec![transaction],
3524              &next_block,
3525              &mut vec![],
3526              rng,
3527          )
3528          .unwrap();
3529  
3530          // Add the next block.
3531          vm.add_next_block(&next_block).unwrap();
3532  
3533          // Check that the delegator is in the `bonded` mapping.
3534          let bonded_mapping = vm
3535              .finalize_store()
3536              .get_mapping_confirmed(
3537                  ProgramID::from_str("credits.delta").unwrap(),
3538                  Identifier::from_str("bonded").unwrap(),
3539              )
3540              .unwrap();
3541          assert_eq!(bonded_mapping.len(), validators.len() + 1);
3542      }
3543  
3544      #[test]
3545      fn test_ratify_genesis_withdrawal_address() {
3546          const NUM_VALIDATORS: usize = 5;
3547          const NUM_DELEGATORS: usize = 8;
3548  
3549          // Sample an RNG.
3550          let rng = &mut TestRng::default();
3551  
3552          // Initialize the VM.
3553          let vm = sample_vm();
3554  
3555          // Sample the validators.
3556          let validators = sample_validators(NUM_VALIDATORS, rng);
3557  
3558          // Sample the delegators, cycling through the validators.
3559          let delegators: IndexMap<_, _> = (0..NUM_DELEGATORS)
3560              .map(|i| {
3561                  let private_key = PrivateKey::new(rng).unwrap();
3562                  let validator = Address::try_from(validators.keys().nth(i % NUM_VALIDATORS).unwrap()).unwrap();
3563                  let amount = MIN_DELEGATOR_STAKE;
3564                  (private_key, (validator, amount))
3565              })
3566              .collect();
3567  
3568          // Construct the committee.
3569          // Track the allocated amount.
3570          let (committee_map, allocated_amount) = sample_committee_map_and_allocated_amount(&validators, &delegators);
3571          let committee = Committee::new_genesis(committee_map).unwrap();
3572  
3573          // Construct the public balances, allocating the remaining supply to the validators and zero to the delegators.
3574          let mut public_balances = sample_public_balances(
3575              &validators.keys().map(|private_key| Address::try_from(private_key).unwrap()).collect::<Vec<_>>(),
3576              <CurrentNetwork as Network>::STARTING_SUPPLY - allocated_amount,
3577          );
3578          public_balances.extend(sample_public_balances(
3579              &delegators.keys().map(|private_key| Address::try_from(private_key).unwrap()).collect::<Vec<_>>(),
3580              0,
3581          ));
3582  
3583          // Construct the bonded balances.
3584          let mut bonded_balances = sample_bonded_balances(&validators, &delegators);
3585  
3586          // Randomly sample and update the withdrawal addresses in the bonded balances.
3587          for (_, (_, withdrawal_address, _)) in bonded_balances.iter_mut() {
3588              *withdrawal_address = Address::rand(rng);
3589          }
3590  
3591          // Construct the genesis block.
3592          let genesis = vm
3593              .genesis_quorum(
3594                  validators.keys().next().unwrap(),
3595                  committee.clone(),
3596                  public_balances.clone(),
3597                  bonded_balances.clone(),
3598                  rng,
3599              )
3600              .unwrap();
3601  
3602          // Add the genesis block to the VM.
3603          vm.add_next_block(&genesis).unwrap();
3604  
3605          // Check that the state of the `credits.delta` program is correct.
3606          let program_id = ProgramID::from_str("credits.delta").unwrap();
3607          let withdraw_mapping_name = Identifier::from_str("withdraw").unwrap();
3608  
3609          // Get and check the withdraw mapping.
3610          let actual_withdraw = vm.finalize_store().get_mapping_confirmed(program_id, withdraw_mapping_name).unwrap();
3611          let expected_withdraw = bonded_balances
3612              .iter()
3613              .map(|(address, (_, withdrawal_address, _))| {
3614                  (
3615                      Plaintext::from_str(&address.to_string()).unwrap(),
3616                      Value::from_str(&withdrawal_address.to_string()).unwrap(),
3617                  )
3618              })
3619              .collect_vec();
3620          // Note that `actual_withdraw` and `expected_withdraw` are vectors and not necessarily in the same order.
3621          // By checking that the lengths of the vector are equal and that all entries in `actual_withdraw` are in `expected_withdraw`,
3622          // we can ensure that the two vectors contain the same data.
3623          assert_eq!(actual_withdraw.len(), expected_withdraw.len());
3624          for entry in actual_withdraw.iter() {
3625              assert!(expected_withdraw.contains(entry));
3626          }
3627      }
3628  }