/ ledger / store / src / block / mod.rs
mod.rs
   1  // Copyright (c) 2019-2025 Alpha-Delta Network Inc.
   2  // This file is part of the alphavm 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  pub mod confirmed_tx_type;
  17  pub use confirmed_tx_type::*;
  18  
  19  mod cache;
  20  use cache::BlockCache;
  21  
  22  use crate::{
  23      TransactionStorage,
  24      TransactionStore,
  25      TransitionStorage,
  26      TransitionStore,
  27      atomic_batch_scope,
  28      helpers::{Map, MapRead},
  29  };
  30  use alphavm_ledger_authority::Authority;
  31  use alphavm_ledger_block::{
  32      Block,
  33      ConfirmedTransaction,
  34      Header,
  35      Ratifications,
  36      Rejected,
  37      Solutions,
  38      Transaction,
  39      Transactions,
  40  };
  41  use alphavm_ledger_narwhal_batch_certificate::BatchCertificate;
  42  use alphavm_ledger_puzzle::{Solution, SolutionID};
  43  use alphavm_synthesizer_program::{FinalizeOperation, Program};
  44  use console::{
  45      network::prelude::*,
  46      program::{BlockTree, HeaderLeaf, ProgramID, StatePath},
  47      types::Field,
  48  };
  49  
  50  use alphastd_storage::StorageMode;
  51  #[cfg(feature = "rocks")]
  52  use alphastd_storage::alpha_ledger_dir;
  53  use anyhow::{Context, Result};
  54  #[cfg(feature = "locktick")]
  55  use locktick::{LockGuard, parking_lot::RwLock};
  56  #[cfg(not(feature = "locktick"))]
  57  use parking_lot::RwLock;
  58  use std::{borrow::Cow, sync::Arc};
  59  #[cfg(feature = "rocks")]
  60  use std::{fs, io::BufWriter};
  61  
  62  #[cfg(not(feature = "serial"))]
  63  use rayon::prelude::*;
  64  
  65  /// Separates the confirmed transaction into a tuple.
  66  #[allow(clippy::type_complexity)]
  67  fn to_confirmed_tuple<N: Network>(
  68      confirmed: ConfirmedTransaction<N>,
  69  ) -> Result<(ConfirmedTxType<N>, Transaction<N>, Vec<FinalizeOperation<N>>)> {
  70      match confirmed {
  71          ConfirmedTransaction::AcceptedDeploy(index, tx, finalize_operations) => {
  72              // Return the confirmed tuple.
  73              Ok((ConfirmedTxType::AcceptedDeploy(index), tx, finalize_operations))
  74          }
  75          ConfirmedTransaction::AcceptedExecute(index, tx, finalize_operations) => {
  76              // Return the confirmed tuple.
  77              Ok((ConfirmedTxType::AcceptedExecute(index), tx, finalize_operations))
  78          }
  79          ConfirmedTransaction::RejectedDeploy(index, tx, rejected, finalize_operations) => {
  80              // Return the confirmed tuple.
  81              Ok((ConfirmedTxType::RejectedDeploy(index, rejected), tx, finalize_operations))
  82          }
  83          ConfirmedTransaction::RejectedExecute(index, tx, rejected, finalize_operations) => {
  84              // Return the confirmed tuple.
  85              Ok((ConfirmedTxType::RejectedExecute(index, rejected), tx, finalize_operations))
  86          }
  87      }
  88  }
  89  
  90  fn to_confirmed_transaction<N: Network>(
  91      confirmed_type: ConfirmedTxType<N>,
  92      transaction: Transaction<N>,
  93      finalize_operations: Vec<FinalizeOperation<N>>,
  94  ) -> Result<ConfirmedTransaction<N>> {
  95      match confirmed_type {
  96          ConfirmedTxType::AcceptedDeploy(index) => {
  97              // Return the confirmed transaction.
  98              ConfirmedTransaction::accepted_deploy(index, transaction, finalize_operations)
  99          }
 100          ConfirmedTxType::AcceptedExecute(index) => {
 101              // Return the confirmed transaction.
 102              ConfirmedTransaction::accepted_execute(index, transaction, finalize_operations)
 103          }
 104          ConfirmedTxType::RejectedDeploy(index, rejected) => {
 105              // Return the confirmed transaction.
 106              ConfirmedTransaction::rejected_deploy(index, transaction, rejected, finalize_operations)
 107          }
 108          ConfirmedTxType::RejectedExecute(index, rejected) => {
 109              // Return the confirmed transaction.
 110              ConfirmedTransaction::rejected_execute(index, transaction, rejected, finalize_operations)
 111          }
 112      }
 113  }
 114  
 115  /// A trait for block storage.
 116  pub trait BlockStorage<N: Network>: 'static + Clone + Send + Sync {
 117      /// The mapping of `block height` to `state root`.
 118      type StateRootMap: for<'a> Map<'a, u32, N::StateRoot>;
 119      /// The mapping of `state root` to `block height`.
 120      type ReverseStateRootMap: for<'a> Map<'a, N::StateRoot, u32>;
 121      /// The mapping of `block height` to `block hash`.
 122      type IDMap: for<'a> Map<'a, u32, N::BlockHash>;
 123      /// The mapping of `block hash` to `block height`.
 124      type ReverseIDMap: for<'a> Map<'a, N::BlockHash, u32>;
 125      /// The mapping of `block hash` to `block header`.
 126      type HeaderMap: for<'a> Map<'a, N::BlockHash, Header<N>>;
 127      /// The mapping of `block hash` to `block authority`.
 128      type AuthorityMap: for<'a> Map<'a, N::BlockHash, Authority<N>>;
 129      /// The mapping of `certificate ID` to (`block height`, `round height`).
 130      type CertificateMap: for<'a> Map<'a, Field<N>, (u32, u64)>;
 131      /// The mapping of `block hash` to `block ratifications`.
 132      type RatificationsMap: for<'a> Map<'a, N::BlockHash, Ratifications<N>>;
 133      /// The mapping of `block hash` to `block solutions`.
 134      type SolutionsMap: for<'a> Map<'a, N::BlockHash, Solutions<N>>;
 135      /// The mapping of `solution ID` to `block height`.
 136      type SolutionIDsMap: for<'a> Map<'a, SolutionID<N>, u32>;
 137      /// The mapping of `block hash` to `[aborted solution ID]`.
 138      type AbortedSolutionIDsMap: for<'a> Map<'a, N::BlockHash, Vec<SolutionID<N>>>;
 139      /// The mapping of aborted `solution ID` to `block height`.
 140      type AbortedSolutionHeightsMap: for<'a> Map<'a, SolutionID<N>, u32>;
 141      /// The mapping of `block hash` to `[transaction ID]`.
 142      type TransactionsMap: for<'a> Map<'a, N::BlockHash, Vec<N::TransactionID>>;
 143      /// The mapping of `block hash` to `[aborted transaction ID]`.
 144      type AbortedTransactionIDsMap: for<'a> Map<'a, N::BlockHash, Vec<N::TransactionID>>;
 145      /// The mapping of rejected or aborted `transaction ID` to `block hash`.
 146      type RejectedOrAbortedTransactionIDMap: for<'a> Map<'a, N::TransactionID, N::BlockHash>;
 147      /// The mapping of `transaction ID` to `(block hash, confirmed tx type, finalize operations)`.
 148      type ConfirmedTransactionsMap: for<'a> Map<'a, N::TransactionID, (N::BlockHash, ConfirmedTxType<N>, Vec<FinalizeOperation<N>>)>;
 149      /// The rejected deployment or execution map.
 150      type RejectedDeploymentOrExecutionMap: for<'a> Map<'a, Field<N>, Rejected<N>>;
 151      /// The transaction storage.
 152      type TransactionStorage: TransactionStorage<N, TransitionStorage = Self::TransitionStorage>;
 153      /// The transition storage.
 154      type TransitionStorage: TransitionStorage<N>;
 155  
 156      /// Initializes the block storage.
 157      fn open<S: Into<StorageMode>>(storage: S) -> Result<Self>;
 158  
 159      /// Returns the state root map.
 160      fn state_root_map(&self) -> &Self::StateRootMap;
 161      /// Returns the reverse state root map.
 162      fn reverse_state_root_map(&self) -> &Self::ReverseStateRootMap;
 163      /// Returns the ID map.
 164      fn id_map(&self) -> &Self::IDMap;
 165      /// Returns the reverse ID map.
 166      fn reverse_id_map(&self) -> &Self::ReverseIDMap;
 167      /// Returns the header map.
 168      fn header_map(&self) -> &Self::HeaderMap;
 169      /// Returns the authority map.
 170      fn authority_map(&self) -> &Self::AuthorityMap;
 171      /// Returns the certificate map.
 172      fn certificate_map(&self) -> &Self::CertificateMap;
 173      /// Returns the ratifications map.
 174      fn ratifications_map(&self) -> &Self::RatificationsMap;
 175      /// Returns the solutions map.
 176      fn solutions_map(&self) -> &Self::SolutionsMap;
 177      /// Returns the solution IDs map.
 178      fn solution_ids_map(&self) -> &Self::SolutionIDsMap;
 179      /// Returns the aborted solution IDs map.
 180      fn aborted_solution_ids_map(&self) -> &Self::AbortedSolutionIDsMap;
 181      /// Returns the aborted solution heights map.
 182      fn aborted_solution_heights_map(&self) -> &Self::AbortedSolutionHeightsMap;
 183      /// Returns the accepted transactions map.
 184      fn transactions_map(&self) -> &Self::TransactionsMap;
 185      /// Returns the aborted transaction IDs map.
 186      fn aborted_transaction_ids_map(&self) -> &Self::AbortedTransactionIDsMap;
 187      /// Returns the rejected or aborted transaction ID map.
 188      fn rejected_or_aborted_transaction_id_map(&self) -> &Self::RejectedOrAbortedTransactionIDMap;
 189      /// Returns the confirmed transactions map.
 190      fn confirmed_transactions_map(&self) -> &Self::ConfirmedTransactionsMap;
 191      /// Returns the rejected deployment or execution map.
 192      fn rejected_deployment_or_execution_map(&self) -> &Self::RejectedDeploymentOrExecutionMap;
 193      /// Returns the transaction store.
 194      fn transaction_store(&self) -> &TransactionStore<N, Self::TransactionStorage>;
 195  
 196      /// Returns the transition store.
 197      fn transition_store(&self) -> &TransitionStore<N, Self::TransitionStorage> {
 198          self.transaction_store().transition_store()
 199      }
 200      /// Returns the storage mode.
 201      fn storage_mode(&self) -> &StorageMode {
 202          debug_assert!(self.transaction_store().storage_mode() == self.transition_store().storage_mode());
 203          self.transition_store().storage_mode()
 204      }
 205  
 206      /// Starts an atomic batch write operation.
 207      fn start_atomic(&self) {
 208          self.state_root_map().start_atomic();
 209          self.reverse_state_root_map().start_atomic();
 210          self.id_map().start_atomic();
 211          self.reverse_id_map().start_atomic();
 212          self.header_map().start_atomic();
 213          self.authority_map().start_atomic();
 214          self.certificate_map().start_atomic();
 215          self.ratifications_map().start_atomic();
 216          self.solutions_map().start_atomic();
 217          self.solution_ids_map().start_atomic();
 218          self.aborted_solution_ids_map().start_atomic();
 219          self.aborted_solution_heights_map().start_atomic();
 220          self.transactions_map().start_atomic();
 221          self.aborted_transaction_ids_map().start_atomic();
 222          self.rejected_or_aborted_transaction_id_map().start_atomic();
 223          self.confirmed_transactions_map().start_atomic();
 224          self.rejected_deployment_or_execution_map().start_atomic();
 225          self.transaction_store().start_atomic();
 226      }
 227  
 228      /// Checks if an atomic batch is in progress.
 229      fn is_atomic_in_progress(&self) -> bool {
 230          self.state_root_map().is_atomic_in_progress()
 231              || self.reverse_state_root_map().is_atomic_in_progress()
 232              || self.id_map().is_atomic_in_progress()
 233              || self.reverse_id_map().is_atomic_in_progress()
 234              || self.header_map().is_atomic_in_progress()
 235              || self.authority_map().is_atomic_in_progress()
 236              || self.certificate_map().is_atomic_in_progress()
 237              || self.ratifications_map().is_atomic_in_progress()
 238              || self.solutions_map().is_atomic_in_progress()
 239              || self.solution_ids_map().is_atomic_in_progress()
 240              || self.aborted_solution_ids_map().is_atomic_in_progress()
 241              || self.aborted_solution_heights_map().is_atomic_in_progress()
 242              || self.transactions_map().is_atomic_in_progress()
 243              || self.aborted_transaction_ids_map().is_atomic_in_progress()
 244              || self.rejected_or_aborted_transaction_id_map().is_atomic_in_progress()
 245              || self.confirmed_transactions_map().is_atomic_in_progress()
 246              || self.rejected_deployment_or_execution_map().is_atomic_in_progress()
 247              || self.transaction_store().is_atomic_in_progress()
 248      }
 249  
 250      /// Checkpoints the atomic batch.
 251      fn atomic_checkpoint(&self) {
 252          self.state_root_map().atomic_checkpoint();
 253          self.reverse_state_root_map().atomic_checkpoint();
 254          self.id_map().atomic_checkpoint();
 255          self.reverse_id_map().atomic_checkpoint();
 256          self.header_map().atomic_checkpoint();
 257          self.authority_map().atomic_checkpoint();
 258          self.certificate_map().atomic_checkpoint();
 259          self.ratifications_map().atomic_checkpoint();
 260          self.solutions_map().atomic_checkpoint();
 261          self.solution_ids_map().atomic_checkpoint();
 262          self.aborted_solution_ids_map().atomic_checkpoint();
 263          self.aborted_solution_heights_map().atomic_checkpoint();
 264          self.transactions_map().atomic_checkpoint();
 265          self.aborted_transaction_ids_map().atomic_checkpoint();
 266          self.rejected_or_aborted_transaction_id_map().atomic_checkpoint();
 267          self.confirmed_transactions_map().atomic_checkpoint();
 268          self.rejected_deployment_or_execution_map().atomic_checkpoint();
 269          self.transaction_store().atomic_checkpoint();
 270      }
 271  
 272      /// Clears the latest atomic batch checkpoint.
 273      fn clear_latest_checkpoint(&self) {
 274          self.state_root_map().clear_latest_checkpoint();
 275          self.reverse_state_root_map().clear_latest_checkpoint();
 276          self.id_map().clear_latest_checkpoint();
 277          self.reverse_id_map().clear_latest_checkpoint();
 278          self.header_map().clear_latest_checkpoint();
 279          self.authority_map().clear_latest_checkpoint();
 280          self.certificate_map().clear_latest_checkpoint();
 281          self.ratifications_map().clear_latest_checkpoint();
 282          self.solutions_map().clear_latest_checkpoint();
 283          self.solution_ids_map().clear_latest_checkpoint();
 284          self.aborted_solution_ids_map().clear_latest_checkpoint();
 285          self.aborted_solution_heights_map().clear_latest_checkpoint();
 286          self.transactions_map().clear_latest_checkpoint();
 287          self.aborted_transaction_ids_map().clear_latest_checkpoint();
 288          self.rejected_or_aborted_transaction_id_map().clear_latest_checkpoint();
 289          self.confirmed_transactions_map().clear_latest_checkpoint();
 290          self.rejected_deployment_or_execution_map().clear_latest_checkpoint();
 291          self.transaction_store().clear_latest_checkpoint();
 292      }
 293  
 294      /// Rewinds the atomic batch to the previous checkpoint.
 295      fn atomic_rewind(&self) {
 296          self.state_root_map().atomic_rewind();
 297          self.reverse_state_root_map().atomic_rewind();
 298          self.id_map().atomic_rewind();
 299          self.reverse_id_map().atomic_rewind();
 300          self.header_map().atomic_rewind();
 301          self.authority_map().atomic_rewind();
 302          self.certificate_map().atomic_rewind();
 303          self.ratifications_map().atomic_rewind();
 304          self.solutions_map().atomic_rewind();
 305          self.solution_ids_map().atomic_rewind();
 306          self.aborted_solution_ids_map().atomic_rewind();
 307          self.aborted_solution_heights_map().atomic_rewind();
 308          self.transactions_map().atomic_rewind();
 309          self.aborted_transaction_ids_map().atomic_rewind();
 310          self.rejected_or_aborted_transaction_id_map().atomic_rewind();
 311          self.confirmed_transactions_map().atomic_rewind();
 312          self.rejected_deployment_or_execution_map().atomic_rewind();
 313          self.transaction_store().atomic_rewind();
 314      }
 315  
 316      /// Aborts an atomic batch write operation.
 317      fn abort_atomic(&self) {
 318          self.state_root_map().abort_atomic();
 319          self.reverse_state_root_map().abort_atomic();
 320          self.id_map().abort_atomic();
 321          self.reverse_id_map().abort_atomic();
 322          self.header_map().abort_atomic();
 323          self.authority_map().abort_atomic();
 324          self.certificate_map().abort_atomic();
 325          self.ratifications_map().abort_atomic();
 326          self.solutions_map().abort_atomic();
 327          self.solution_ids_map().abort_atomic();
 328          self.aborted_solution_ids_map().abort_atomic();
 329          self.aborted_solution_heights_map().abort_atomic();
 330          self.transactions_map().abort_atomic();
 331          self.aborted_transaction_ids_map().abort_atomic();
 332          self.rejected_or_aborted_transaction_id_map().abort_atomic();
 333          self.confirmed_transactions_map().abort_atomic();
 334          self.rejected_deployment_or_execution_map().abort_atomic();
 335          self.transaction_store().abort_atomic();
 336      }
 337  
 338      /// Finishes an atomic batch write operation.
 339      fn finish_atomic(&self) -> Result<()> {
 340          self.state_root_map().finish_atomic()?;
 341          self.reverse_state_root_map().finish_atomic()?;
 342          self.id_map().finish_atomic()?;
 343          self.reverse_id_map().finish_atomic()?;
 344          self.header_map().finish_atomic()?;
 345          self.authority_map().finish_atomic()?;
 346          self.certificate_map().finish_atomic()?;
 347          self.ratifications_map().finish_atomic()?;
 348          self.solutions_map().finish_atomic()?;
 349          self.solution_ids_map().finish_atomic()?;
 350          self.aborted_solution_ids_map().finish_atomic()?;
 351          self.aborted_solution_heights_map().finish_atomic()?;
 352          self.transactions_map().finish_atomic()?;
 353          self.aborted_transaction_ids_map().finish_atomic()?;
 354          self.rejected_or_aborted_transaction_id_map().finish_atomic()?;
 355          self.confirmed_transactions_map().finish_atomic()?;
 356          self.rejected_deployment_or_execution_map().finish_atomic()?;
 357          self.transaction_store().finish_atomic()
 358      }
 359  
 360      /// Pauses atomic writes.
 361      fn pause_atomic_writes(&self) -> Result<()> {
 362          // Since this applies to the entire storage, any map can be used; this
 363          // one is just the first one in the list.
 364          self.state_root_map().pause_atomic_writes()
 365      }
 366  
 367      /// Unpauses atomic writes.
 368      fn unpause_atomic_writes<const DISCARD_BATCH: bool>(&self) -> Result<()> {
 369          // Since this applies to the entire storage, any map can be used; this
 370          // one is just the first one in the list.
 371          self.state_root_map().unpause_atomic_writes::<DISCARD_BATCH>()
 372      }
 373  
 374      /// Stores the given `(state root, block)` pair into storage.
 375      fn insert(&self, state_root: N::StateRoot, block: &Block<N>) -> Result<()> {
 376          // Prepare the confirmed transactions.
 377          let confirmed = block
 378              .transactions()
 379              .iter()
 380              .cloned()
 381              .map(|confirmed| to_confirmed_tuple(confirmed))
 382              .collect::<Result<Vec<_>, _>>()?;
 383  
 384          // Retrieve the certificate IDs to store.
 385          let certificates_to_store = match block.authority() {
 386              Authority::Beacon(_) => Vec::new(),
 387              Authority::Quorum(subdag) => {
 388                  subdag.iter().flat_map(|(round, certificates)| certificates.iter().map(|c| (c.id(), *round))).collect()
 389              }
 390          };
 391  
 392          // Prepare the rejected transaction IDs and their corresponding unconfirmed transaction IDs.
 393          let rejected_transaction_ids: Vec<_> = block
 394              .transactions()
 395              .iter()
 396              .filter(|tx| tx.is_rejected())
 397              .map(|tx| tx.to_unconfirmed_transaction_id())
 398              .collect::<Result<Vec<_>>>()?;
 399  
 400          atomic_batch_scope!(self, {
 401              // Store the (block height, state root) pair.
 402              self.state_root_map().insert(block.height(), state_root)?;
 403              // Store the (state root, block height) pair.
 404              self.reverse_state_root_map().insert(state_root, block.height())?;
 405  
 406              // Store the block hash.
 407              self.id_map().insert(block.height(), block.hash())?;
 408              // Store the block height.
 409              self.reverse_id_map().insert(block.hash(), block.height())?;
 410              // Store the block header.
 411              self.header_map().insert(block.hash(), *block.header())?;
 412  
 413              // Store the block authority.
 414              self.authority_map().insert(block.hash(), block.authority().clone())?;
 415  
 416              // Store the block certificates.
 417              for (certificate_id, round) in certificates_to_store {
 418                  self.certificate_map().insert(certificate_id, (block.height(), round))?;
 419              }
 420  
 421              // Store the block ratifications.
 422              self.ratifications_map().insert(block.hash(), block.ratifications().clone())?;
 423  
 424              // Store the block solutions.
 425              self.solutions_map().insert(block.hash(), block.solutions().clone())?;
 426  
 427              // Store the block solution IDs.
 428              for solution_id in block.solutions().solution_ids() {
 429                  self.solution_ids_map().insert(*solution_id, block.height())?;
 430              }
 431  
 432              // Store the aborted solution IDs.
 433              self.aborted_solution_ids_map().insert(block.hash(), block.aborted_solution_ids().clone())?;
 434  
 435              // Store the block aborted solution heights.
 436              for solution_id in block.aborted_solution_ids() {
 437                  self.aborted_solution_heights_map().insert(*solution_id, block.height())?;
 438              }
 439  
 440              // Store the transaction IDs.
 441              self.transactions_map().insert(block.hash(), block.transaction_ids().copied().collect())?;
 442  
 443              // Store the aborted transaction IDs.
 444              self.aborted_transaction_ids_map().insert(block.hash(), block.aborted_transaction_ids().clone())?;
 445              for aborted_transaction_id in block.aborted_transaction_ids() {
 446                  self.rejected_or_aborted_transaction_id_map().insert(*aborted_transaction_id, block.hash())?;
 447              }
 448  
 449              // Store the rejected transactions IDs.
 450              for rejected_transaction_id in rejected_transaction_ids {
 451                  self.rejected_or_aborted_transaction_id_map().insert(rejected_transaction_id, block.hash())?;
 452              }
 453  
 454              // Store the confirmed transactions.
 455              for (confirmed_type, transaction, finalize_operations) in confirmed {
 456                  // Store the block hash and confirmed transaction data.
 457                  self.confirmed_transactions_map()
 458                      .insert(transaction.id(), (block.hash(), confirmed_type.clone(), finalize_operations))?;
 459                  // Store the rejected deployment or execution.
 460                  if let ConfirmedTxType::RejectedDeploy(_, rejected) | ConfirmedTxType::RejectedExecute(_, rejected) =
 461                      confirmed_type
 462                  {
 463                      self.rejected_deployment_or_execution_map().insert(rejected.to_id()?, rejected)?;
 464                  }
 465                  // Store the transaction.
 466                  self.transaction_store().insert(&transaction)?;
 467              }
 468  
 469              Ok(())
 470          })
 471      }
 472  
 473      /// Removes the block for the given `block hash`.
 474      fn remove(&self, block_hash: &N::BlockHash) -> Result<()> {
 475          // Retrieve the block height.
 476          let Some(block_height) = self.get_block_height(block_hash)? else {
 477              bail!("Failed to remove block: missing block height for block hash '{block_hash}'")
 478          };
 479          // Retrieve the state root.
 480          let Some(state_root) = self.state_root_map().get_confirmed(&block_height)?.map(|x| *x) else {
 481              bail!("Failed to remove block: missing state root for block height '{block_height}'");
 482          };
 483          // Retrieve the transaction IDs.
 484          let Some(transaction_ids) = self.transactions_map().get_confirmed(block_hash)? else {
 485              bail!("Failed to remove block: missing transactions for block '{block_height}' ('{block_hash}')");
 486          };
 487          // Retrieve the solutions.
 488          let Some(solutions) = self.solutions_map().get_confirmed(block_hash)?.map(|x| x.into_owned()) else {
 489              bail!("Failed to remove block: missing solutions for block '{block_height}' ('{block_hash}')");
 490          };
 491  
 492          // Retrieve the aborted solution IDs.
 493          let aborted_solution_ids = self.get_block_aborted_solution_ids(block_hash)?.unwrap_or_default();
 494  
 495          // Retrieve the aborted transaction IDs.
 496          let aborted_transaction_ids = self.get_block_aborted_transaction_ids(block_hash)?.unwrap_or_default();
 497  
 498          // Retrieve the rejected transaction IDs, and the deployment or execution ID.
 499          let rejected_transaction_ids_and_deployment_or_execution_id = match self.get_block_transactions(block_hash)? {
 500              Some(transactions) => transactions
 501                  .iter()
 502                  .filter(|tx| tx.is_rejected())
 503                  .map(|tx| Ok((tx.to_unconfirmed_transaction_id()?, tx.to_rejected_id()?)))
 504                  .collect::<Result<Vec<_>>>()?,
 505              None => Vec::new(),
 506          };
 507  
 508          // Determine the certificate IDs to remove.
 509          let certificate_ids_to_remove = match self.authority_map().get_confirmed(block_hash)? {
 510              Some(authority) => match &authority {
 511                  Cow::Owned(Authority::Beacon(_)) | Cow::Borrowed(Authority::Beacon(_)) => Vec::new(),
 512                  Cow::Owned(Authority::Quorum(subdag)) | Cow::Borrowed(Authority::Quorum(subdag)) => {
 513                      subdag.values().flatten().map(|c| c.id()).collect()
 514                  }
 515              },
 516              None => bail!("Failed to remove block: missing authority for block '{block_height}' ('{block_hash}')"),
 517          };
 518  
 519          atomic_batch_scope!(self, {
 520              // Remove the (block height, state root) pair.
 521              self.state_root_map().remove(&block_height)?;
 522              // Remove the (state root, block height) pair.
 523              self.reverse_state_root_map().remove(&state_root)?;
 524  
 525              // Remove the block hash.
 526              self.id_map().remove(&block_height)?;
 527              // Remove the block height.
 528              self.reverse_id_map().remove(block_hash)?;
 529              // Remove the block header.
 530              self.header_map().remove(block_hash)?;
 531  
 532              // Remove the block authority.
 533              self.authority_map().remove(block_hash)?;
 534  
 535              // Remove the block certificates.
 536              for certificate_id in certificate_ids_to_remove.iter() {
 537                  self.certificate_map().remove(certificate_id)?;
 538              }
 539  
 540              // Remove the block ratifications.
 541              self.ratifications_map().remove(block_hash)?;
 542  
 543              // Remove the block solutions.
 544              self.solutions_map().remove(block_hash)?;
 545  
 546              // Remove the block solution IDs.
 547              for solution_id in solutions.solution_ids() {
 548                  self.solution_ids_map().remove(solution_id)?;
 549              }
 550  
 551              // Remove the aborted solution IDs.
 552              self.aborted_solution_ids_map().remove(block_hash)?;
 553  
 554              // Remove the aborted solution heights.
 555              for solution_id in aborted_solution_ids {
 556                  self.aborted_solution_heights_map().remove(&solution_id)?;
 557              }
 558  
 559              // Remove the transaction IDs.
 560              self.transactions_map().remove(block_hash)?;
 561  
 562              // Remove the aborted transaction IDs.
 563              self.aborted_transaction_ids_map().remove(block_hash)?;
 564              for aborted_transaction_id in aborted_transaction_ids {
 565                  self.rejected_or_aborted_transaction_id_map().remove(&aborted_transaction_id)?;
 566              }
 567  
 568              // Remove the rejected state.
 569              for (rejected_transaction_id, rejected_id) in rejected_transaction_ids_and_deployment_or_execution_id {
 570                  // Remove the rejected transaction ID.
 571                  self.rejected_or_aborted_transaction_id_map().remove(&rejected_transaction_id)?;
 572                  // Remove the rejected deployment or execution.
 573                  if let Some(rejected_id) = rejected_id {
 574                      self.rejected_deployment_or_execution_map().remove(&rejected_id)?;
 575                  }
 576              }
 577  
 578              // Remove the block transactions.
 579              for transaction_id in transaction_ids.iter() {
 580                  // Remove the reverse transaction ID.
 581                  self.confirmed_transactions_map().remove(transaction_id)?;
 582                  // Remove the transaction.
 583                  self.transaction_store().remove(transaction_id)?;
 584              }
 585  
 586              Ok(())
 587          })
 588      }
 589  
 590      /// Returns `true` if the given transaction ID exists.
 591      fn contains_transaction_id(&self, transaction_id: &N::TransactionID) -> Result<bool> {
 592          Ok(self.transaction_store().contains_transaction_id(transaction_id)?
 593              || self.contains_rejected_or_aborted_transaction_id(transaction_id)?)
 594      }
 595  
 596      /// Returns `true` if the given rejected transaction ID or aborted transaction ID exists.
 597      fn contains_rejected_or_aborted_transaction_id(&self, transaction_id: &N::TransactionID) -> Result<bool> {
 598          self.rejected_or_aborted_transaction_id_map().contains_key_confirmed(transaction_id)
 599      }
 600  
 601      /// Returns `true` if the given rejected deployment or execution ID.
 602      fn contains_rejected_deployment_or_execution_id(&self, rejected_id: &Field<N>) -> Result<bool> {
 603          self.rejected_deployment_or_execution_map().contains_key_confirmed(rejected_id)
 604      }
 605  
 606      /// Returns the block height that contains the given `state root`.
 607      fn find_block_height_from_state_root(&self, state_root: N::StateRoot) -> Result<Option<u32>> {
 608          Ok(self.reverse_state_root_map().get_confirmed(&state_root)?.map(|x| *x))
 609      }
 610  
 611      /// Returns the block hash that contains the given `transaction ID`.
 612      fn find_block_hash(&self, transaction_id: &N::TransactionID) -> Result<Option<N::BlockHash>> {
 613          match self.confirmed_transactions_map().get_confirmed(transaction_id)? {
 614              Some(Cow::Borrowed((block_hash, _, _))) => Ok(Some(*block_hash)),
 615              Some(Cow::Owned((block_hash, _, _))) => Ok(Some(block_hash)),
 616              None => match self.rejected_or_aborted_transaction_id_map().get_confirmed(transaction_id)? {
 617                  Some(Cow::Borrowed(block_hash)) => Ok(Some(*block_hash)),
 618                  Some(Cow::Owned(block_hash)) => Ok(Some(block_hash)),
 619                  None => Ok(None),
 620              },
 621          }
 622      }
 623  
 624      /// Returns the block height that contains the given `solution ID`.
 625      fn find_block_height_from_solution_id(&self, solution_id: &SolutionID<N>) -> Result<Option<u32>> {
 626          Ok(match self.solution_ids_map().get_confirmed(solution_id)? {
 627              some @ Some(..) => some,
 628              None => self.aborted_solution_heights_map().get_confirmed(solution_id)?,
 629          }
 630          .map(|x| *x))
 631      }
 632  
 633      /// Returns the state root that contains the given `block height`.
 634      fn get_state_root(&self, block_height: u32) -> Result<Option<N::StateRoot>> {
 635          Ok(self.state_root_map().get_confirmed(&block_height)?.map(|x| *x))
 636      }
 637  
 638      /// Returns a state path for the given `commitment`.
 639      fn get_state_path_for_commitment(&self, commitment: &Field<N>, block_tree: &BlockTree<N>) -> Result<StatePath<N>> {
 640          // Ensure the commitment exists.
 641          if !self.transition_store().contains_commitment(commitment)? {
 642              bail!("Commitment '{commitment}' does not exist");
 643          }
 644  
 645          // Find the transition that contains the commitment.
 646          let transition_id = self.transition_store().find_transition_id(commitment)?;
 647          // Find the transaction that contains the transition.
 648          let Some(transaction_id) = self.transaction_store().find_transaction_id_from_transition_id(&transition_id)?
 649          else {
 650              bail!("The transaction ID for commitment '{commitment}' is missing in storage");
 651          };
 652          // Find the block that contains the transaction.
 653          let Some(block_hash) = self.find_block_hash(&transaction_id)? else {
 654              bail!("The block hash for commitment '{commitment}' is missing in storage");
 655          };
 656  
 657          // Retrieve the transition.
 658          let Some(transition) = self.transition_store().get_transition(&transition_id)? else {
 659              bail!("The transition '{transition_id}' for commitment '{commitment}' is missing in storage");
 660          };
 661          // Retrieve the block.
 662          let Some(block) = self.get_block(&block_hash)? else {
 663              bail!("The block '{block_hash}' for commitment '{commitment}' is missing in storage");
 664          };
 665  
 666          // Construct the global state root and block path.
 667          let global_state_root = *block_tree.root();
 668          let block_path = block_tree.prove(block.height() as usize, &block.hash().to_bits_le())?;
 669  
 670          // Ensure the global state root exists in storage.
 671          if !self.reverse_state_root_map().contains_key_confirmed(&global_state_root.into())? {
 672              bail!("The global state root '{global_state_root}' for commitment '{commitment}' is missing in storage");
 673          }
 674  
 675          // Construct the transition root, transition path and transaction leaf.
 676          let transition_root = transition.to_root()?;
 677          let transition_leaf = transition.to_leaf(commitment, false)?;
 678          let transition_path = transition.to_path(&transition_leaf)?;
 679  
 680          // Construct the transactions path.
 681          let transactions = block.transactions();
 682          let Ok(transactions_path) = transactions.to_path(transaction_id) else {
 683              bail!("The transaction '{transaction_id}' for commitment '{commitment}' is not in the block");
 684          };
 685  
 686          // Construct the transaction path and transaction leaf.
 687          let Some(transaction) = transactions.get(&transaction_id) else {
 688              bail!("The transaction '{transaction_id}' for commitment '{commitment}' is not in the block");
 689          };
 690          let transaction_leaf = transaction.to_leaf(transition.id())?;
 691          let transaction_path = transaction.to_path(&transaction_leaf)?;
 692  
 693          // Construct the block header path.
 694          let block_header = block.header();
 695          let header_root = block_header.to_root()?;
 696          let header_leaf = HeaderLeaf::<N>::new(1, block_header.transactions_root());
 697          let header_path = block_header.to_path(&header_leaf)?;
 698  
 699          Ok(StatePath::from(
 700              global_state_root.into(),
 701              block_path,
 702              block.hash(),
 703              block.previous_hash(),
 704              header_root,
 705              header_path,
 706              header_leaf,
 707              transactions_path,
 708              transaction.id(),
 709              transaction_path,
 710              transaction_leaf,
 711              transition_root,
 712              *transition.tcm(),
 713              transition_path,
 714              transition_leaf,
 715          ))
 716      }
 717  
 718      /// Returns a list of state paths for the given list of `commitment`s.
 719      fn get_state_paths_for_commitments(
 720          &self,
 721          commitments: &[Field<N>],
 722          block_tree: &BlockTree<N>,
 723      ) -> Result<Vec<StatePath<N>>> {
 724          // Restrict the number of commitments requested to the maximum number of inputs in a transition.
 725          if commitments.len() > N::MAX_INPUTS {
 726              bail!("Too many commitments provided: expected at most {}, got {}", N::MAX_INPUTS, commitments.len());
 727          }
 728          commitments.iter().map(|commitment| self.get_state_path_for_commitment(commitment, block_tree)).collect()
 729      }
 730  
 731      /// Returns the previous block hash of the given `block height`.
 732      fn get_previous_block_hash(&self, height: u32) -> Result<Option<N::BlockHash>> {
 733          if height.is_zero() {
 734              Ok(Some(N::BlockHash::default()))
 735          } else {
 736              Ok(self.id_map().get_confirmed(&(height - 1))?.map(|x| *x))
 737          }
 738      }
 739  
 740      /// Returns the block hash for the given `block height`.
 741      fn get_block_hash(&self, height: u32) -> Result<Option<N::BlockHash>> {
 742          Ok(self.id_map().get_confirmed(&height)?.map(|x| *x))
 743      }
 744  
 745      /// Returns the block height for the given `block hash`.
 746      fn get_block_height(&self, block_hash: &N::BlockHash) -> Result<Option<u32>> {
 747          Ok(self.reverse_id_map().get_confirmed(block_hash)?.map(|x| *x))
 748      }
 749  
 750      /// Returns the block header for the given `block hash`.
 751      fn get_block_header(&self, block_hash: &N::BlockHash) -> Result<Option<Header<N>>> {
 752          Ok(self.header_map().get_confirmed(block_hash)?.map(|x| x.into_owned()))
 753      }
 754  
 755      /// Returns the block authority for the given `block hash`.
 756      fn get_block_authority(&self, block_hash: &N::BlockHash) -> Result<Option<Authority<N>>> {
 757          Ok(self.authority_map().get_confirmed(block_hash)?.map(|x| x.into_owned()))
 758      }
 759  
 760      /// Returns true if there is a block that contains the specified certificate.
 761      fn contains_block_for_certificate(&self, certificate_id: &Field<N>) -> Result<bool> {
 762          self.certificate_map().contains_key_confirmed(certificate_id)
 763      }
 764  
 765      /// Returns the batch certificate for the given `certificate ID`.
 766      fn get_batch_certificate(&self, certificate_id: &Field<N>) -> Result<Option<BatchCertificate<N>>> {
 767          // Retrieve the height and round for the given certificate ID.
 768          let Some((block_height, round)) = self.certificate_map().get_confirmed(certificate_id)?.map(|x| *x) else {
 769              return Ok(None);
 770          };
 771          // Retrieve the block hash.
 772          let Some(block_hash) = self.get_block_hash(block_height)? else {
 773              bail!("The block hash for block '{block_height}' is missing in block storage")
 774          };
 775          // Retrieve the authority for the given block hash.
 776          let Some(authority) = self.authority_map().get_confirmed(&block_hash)? else {
 777              bail!("The authority for '{block_hash}' is missing in block storage")
 778          };
 779          // Retrieve the certificate for the given certificate ID.
 780          match &authority {
 781              Cow::Owned(Authority::Quorum(subdag)) | Cow::Borrowed(Authority::Quorum(subdag)) => {
 782                  match subdag.get(&round) {
 783                      Some(certificates) => {
 784                          // Retrieve the certificate for the given certificate ID.
 785                          match certificates.iter().find(|certificate| &certificate.id() == certificate_id) {
 786                              Some(certificate) => Ok(Some(certificate.clone())),
 787                              None => bail!("The certificate '{certificate_id}' is missing in block storage"),
 788                          }
 789                      }
 790                      None => bail!("The certificates for round '{round}' is missing in block storage"),
 791                  }
 792              }
 793              _ => bail!(
 794                  "Cannot fetch batch certificate '{certificate_id}' - The authority for block '{block_height}' is not a subdag"
 795              ),
 796          }
 797      }
 798  
 799      /// Returns the block ratifications for the given `block hash`.
 800      fn get_block_ratifications(&self, block_hash: &N::BlockHash) -> Result<Option<Ratifications<N>>> {
 801          Ok(self.ratifications_map().get_confirmed(block_hash)?.map(|x| x.into_owned()))
 802      }
 803  
 804      /// Returns the block solutions for the given `block hash`.
 805      fn get_block_solutions(&self, block_hash: &N::BlockHash) -> Result<Solutions<N>> {
 806          let Some(solutions) = self.solutions_map().get_confirmed(block_hash)? else {
 807              bail!("Missing solutions for block ('{block_hash}')");
 808          };
 809          Ok(solutions.into_owned())
 810      }
 811  
 812      /// Returns the prover solution for the given solution ID.
 813      fn get_solution(&self, solution_id: &SolutionID<N>) -> Result<Solution<N>> {
 814          // Retrieve the block height for the solution ID.
 815          let Some(block_height) = self.find_block_height_from_solution_id(solution_id)? else {
 816              bail!("The block height for solution ID '{solution_id}' is missing in block storage")
 817          };
 818          // Retrieve the block hash.
 819          let Some(block_hash) = self.get_block_hash(block_height)? else {
 820              bail!("The block hash for block '{block_height}' is missing in block storage")
 821          };
 822          // Retrieve the solutions.
 823          let Some(solutions) = self.solutions_map().get_confirmed(&block_hash)? else {
 824              bail!("The solutions for block '{block_height}' are missing in block storage")
 825          };
 826          // Retrieve the prover solution.
 827          match solutions.deref().deref() {
 828              Some(solutions) => solutions.get(solution_id).cloned().ok_or_else(|| {
 829                  anyhow!("The prover solution for solution ID '{solution_id}' is missing in block storage")
 830              }),
 831              _ => bail!("The prover solution for solution ID '{solution_id}' is missing in block storage"),
 832          }
 833      }
 834  
 835      /// Returns the block aborted solution IDs for the given `block hash`.
 836      fn get_block_aborted_solution_ids(&self, block_hash: &N::BlockHash) -> Result<Option<Vec<SolutionID<N>>>> {
 837          Ok(self.aborted_solution_ids_map().get_confirmed(block_hash)?.map(|x| x.into_owned()))
 838      }
 839  
 840      /// Returns the block transactions for the given `block hash`.
 841      fn get_block_transactions(&self, block_hash: &N::BlockHash) -> Result<Option<Transactions<N>>> {
 842          // Retrieve the transaction IDs.
 843          let Some(transaction_ids) = self.transactions_map().get_confirmed(block_hash)? else {
 844              return Ok(None);
 845          };
 846          // Retrieve the transactions.
 847          transaction_ids
 848              .iter()
 849              .map(|transaction_id| self.get_confirmed_transaction(*transaction_id))
 850              .collect::<Result<Option<Transactions<_>>>>()
 851      }
 852  
 853      /// Returns the block aborted transaction IDs for the given `block hash`.
 854      fn get_block_aborted_transaction_ids(&self, block_hash: &N::BlockHash) -> Result<Option<Vec<N::TransactionID>>> {
 855          Ok(self.aborted_transaction_ids_map().get_confirmed(block_hash)?.map(|x| x.into_owned()))
 856      }
 857  
 858      /// Returns the transaction for the given `TransactionID`.
 859      fn get_transaction(&self, transaction_id: &N::TransactionID) -> Result<Option<Transaction<N>>> {
 860          // Check if the transaction was rejected or aborted.
 861          // Note: We can only retrieve accepted or rejected transactions. We cannot retrieve aborted transactions.
 862          let Some(block_hash) = self.rejected_or_aborted_transaction_id_map().get_confirmed(transaction_id)? else {
 863              return self.transaction_store().get_transaction(transaction_id);
 864          };
 865          let Some(transactions) = self.get_block_transactions(&block_hash)? else {
 866              bail!("Missing transactions for block '{block_hash}' in block storage")
 867          };
 868  
 869          let Some(confirmed) = transactions.find_confirmed_transaction_for_unconfirmed_transaction_id(transaction_id)
 870          else {
 871              if let Some(aborted_ids) = self.get_block_aborted_transaction_ids(&block_hash)? {
 872                  if aborted_ids.contains(transaction_id) {
 873                      bail!("Transaction '{transaction_id}' was aborted in block '{block_hash}'");
 874                  }
 875              }
 876              bail!("Missing transaction '{transaction_id}' in block storage");
 877          };
 878          Ok(Some(confirmed.transaction().clone()))
 879      }
 880  
 881      /// Returns the confirmed transaction for the given `transaction ID`.
 882      fn get_confirmed_transaction(&self, transaction_id: N::TransactionID) -> Result<Option<ConfirmedTransaction<N>>> {
 883          // Retrieve the transaction.
 884          let Some(transaction) = self.get_transaction(&transaction_id)? else {
 885              bail!("Missing transaction '{transaction_id}' in block storage");
 886          };
 887          // Retrieve the confirmed attributes.
 888          let Some((_, confirmed_type, finalize_operations)) =
 889              self.confirmed_transactions_map().get_confirmed(&transaction.id())?.map(|x| x.into_owned())
 890          else {
 891              bail!("Missing confirmed transaction '{transaction_id}' in block storage")
 892          };
 893          // Construct the confirmed transaction.
 894          to_confirmed_transaction(confirmed_type, transaction, finalize_operations).map(Some)
 895      }
 896  
 897      /// Get the unconfirmed transaction for the given `TransactionID`.
 898      ///
 899      /// For unconfirmed and accepted transactions, this will return original transaction issued by the client.
 900      /// This function also returns the original execution/deployment for a rejected transaction,
 901      /// even when the given `TransactionID` is of a fee transaction.
 902      fn get_unconfirmed_transaction(&self, transaction_id: &N::TransactionID) -> Result<Option<Transaction<N>>> {
 903          // Check if the transaction was rejected or aborted.
 904          // Note: We can only retrieve accepted or rejected transactions. We cannot retrieve aborted transactions.
 905          match self.rejected_or_aborted_transaction_id_map().get_confirmed(transaction_id)? {
 906              Some(block_hash) => match self.get_block_transactions(&block_hash)? {
 907                  Some(transactions) => {
 908                      match transactions.find_confirmed_transaction_for_unconfirmed_transaction_id(transaction_id) {
 909                          Some(confirmed) => Ok(Some(confirmed.to_unconfirmed_transaction()?)),
 910                          None => bail!("Missing transaction '{transaction_id}' in block storage"),
 911                      }
 912                  }
 913                  None => bail!("Missing transactions for block '{block_hash}' in block storage"),
 914              },
 915              None => {
 916                  let Some(txn) = self.transaction_store().get_transaction(transaction_id)? else {
 917                      return Ok(None);
 918                  };
 919  
 920                  // If the transaction is a fee transaction, return the original execution/deployment instead.
 921                  if let Transaction::Fee(_, fee) = txn {
 922                      // Look up the original transaction in its block.
 923                      let Some(block_hash) = self.find_block_hash(transaction_id)? else {
 924                          bail!("Missing fee transaction '{transaction_id}' in block storage");
 925                      };
 926  
 927                      match self.get_block_transactions(&block_hash)? {
 928                          Some(transactions) => transactions.find_unconfirmed_transaction_for_transition_id(fee.id()),
 929                          None => bail!("Missing transactions for block '{block_hash}' in block storage"),
 930                      }
 931                  } else {
 932                      Ok(Some(txn))
 933                  }
 934              }
 935          }
 936      }
 937  
 938      /// Returns the block for the given `block hash`.
 939      fn get_block(&self, block_hash: &N::BlockHash) -> Result<Option<Block<N>>> {
 940          // Retrieve the block height.
 941          let Some(height) = self.get_block_height(block_hash)? else { return Ok(None) };
 942  
 943          // Retrieve the block header.
 944          let Some(header) = self.get_block_header(block_hash)? else {
 945              bail!("Missing block header for block {height} ('{block_hash}')");
 946          };
 947          // Ensure the block height matches.
 948          if header.height() != height {
 949              bail!("Mismatching block height for block {height} ('{block_hash}')")
 950          }
 951  
 952          // Retrieve the previous block hash.
 953          let Some(previous_hash) = self.get_previous_block_hash(height)? else {
 954              bail!("Missing previous block hash for block {height} ('{block_hash}')");
 955          };
 956          // Retrieve the block authority.
 957          let Some(authority) = self.get_block_authority(block_hash)? else {
 958              bail!("Missing authority for block {height} ('{block_hash}')");
 959          };
 960          // Retrieve the block ratifications.
 961          let Some(ratifications) = self.get_block_ratifications(block_hash)? else {
 962              bail!("Missing ratifications for block {height} ('{block_hash}')");
 963          };
 964          // Retrieve the block solutions.
 965          let Ok(solutions) = self.get_block_solutions(block_hash) else {
 966              bail!("Missing solutions for block {height} ('{block_hash}')");
 967          };
 968          // Retrieve the block aborted solution IDs.
 969          let Some(aborted_solution_ids) = self.get_block_aborted_solution_ids(block_hash)? else {
 970              bail!("Missing aborted solutions IDs for block {height} ('{block_hash}')");
 971          };
 972          // Retrieve the block transactions.
 973          let Some(transactions) = self.get_block_transactions(block_hash)? else {
 974              bail!("Missing transactions for block {height} ('{block_hash}')");
 975          };
 976          // Retrieve the block aborted transaction IDs.
 977          let Some(aborted_transaction_ids) = self.get_block_aborted_transaction_ids(block_hash)? else {
 978              bail!("Missing aborted transaction IDs for block {height} ('{block_hash}')");
 979          };
 980  
 981          // Return the block.
 982          Ok(Some(Block::from_unchecked(
 983              *block_hash,
 984              previous_hash,
 985              header,
 986              authority,
 987              ratifications,
 988              solutions,
 989              aborted_solution_ids,
 990              transactions,
 991              aborted_transaction_ids,
 992          )?))
 993      }
 994  
 995      #[cfg(feature = "rocks")]
 996      fn backup_database<P: AsRef<std::path::Path>>(&self, path: P) -> Result<(), String>;
 997  
 998      fn create_block_tree(&self) -> Result<BlockTree<N>>;
 999  }
1000  
1001  /// The `BlockStore` is the user facing API that either uses `BlockMemory` or `BlockDB` as its storae backend.
1002  #[derive(Clone)]
1003  pub struct BlockStore<N: Network, B: BlockStorage<N>> {
1004      /// The block storage.
1005      storage: B,
1006      /// The block tree.
1007      tree: Arc<RwLock<BlockTree<N>>>,
1008      /// Cache of the most recent blocks.
1009      block_cache: Arc<Option<RwLock<BlockCache<N>>>>,
1010  }
1011  
1012  impl<N: Network, B: BlockStorage<N>> BlockStore<N, B> {
1013      /// Initializes the block storage and its cache.
1014      pub fn open<S: Into<StorageMode>>(storage: S) -> Result<Self> {
1015          let storage = B::open(storage)?;
1016  
1017          let tree = storage.create_block_tree()?;
1018  
1019          let mut initial_cache = Vec::new();
1020          let cache_end_height = u32::try_from(tree.number_of_leaves())?;
1021          let cache_start_height = cache_end_height.saturating_sub(BlockCache::<N>::BLOCK_CACHE_SIZE);
1022  
1023          for height in cache_start_height..cache_end_height {
1024              // Ignore genesis block.
1025              if height == 0 {
1026                  continue;
1027              }
1028  
1029              // Get the hash for the next block to add to the cache.
1030              let hash = storage.id_map().get_confirmed(&height)?.with_context(|| {
1031                  format!(
1032                      "Block {height} exists in tree but not in storage;\
1033                      perhaps you used a wrong block tree cache file?"
1034                  )
1035              })?;
1036  
1037              initial_cache.push(
1038                  storage.get_block(&hash)?.with_context(|| format!("Block {hash} exists in tree but not in storage"))?,
1039              );
1040          }
1041  
1042          let block_cache = RwLock::new(BlockCache::new(initial_cache)?);
1043          Ok(Self { storage, tree: Arc::new(RwLock::new(tree)), block_cache: Arc::new(Some(block_cache)) })
1044      }
1045  
1046      /// Stores the given block into storage.
1047      pub fn insert(&self, block: &Block<N>) -> Result<()> {
1048          // Acquire the write lock on the block tree.
1049          let mut tree = self.tree.write();
1050          // Prepare an updated Merkle tree containing the new block hash.
1051          let updated_tree = tree.prepare_append(&[block.hash().to_bits_le()])?;
1052          // Ensure the next block height is correct.
1053          if block.height() != u32::try_from(updated_tree.number_of_leaves())? - 1 {
1054              bail!("Attempted to insert a block at the incorrect height into storage")
1055          }
1056          // Insert the (state root, block height) pair.
1057          self.storage.insert((*updated_tree.root()).into(), block)?;
1058          // Update the block tree.
1059          *tree = updated_tree;
1060          // Add the block to the block cache (unless it is a genesis block).
1061          if block.height() != 0
1062              && let Some(block_cache) = &*self.block_cache
1063          {
1064              block_cache.write().insert(block.clone())?;
1065          }
1066          // Return success.
1067          Ok(())
1068      }
1069  
1070      /// Returns the size of the block cache (or `None` if the block cache is not enabled).
1071      pub fn cache_size(&self) -> Option<u32> {
1072          if self.block_cache.is_none() { None } else { Some(BlockCache::<N>::BLOCK_CACHE_SIZE) }
1073      }
1074  
1075      /// Reverts the Merkle tree to its shape before the insertion of the last 'n' blocks.
1076      pub fn remove_last_n_from_tree_only(&self, n: u32) -> Result<()> {
1077          // Ensure 'n' is non-zero.
1078          ensure!(n > 0, "Cannot remove zero blocks");
1079          // Acquire the write lock on the block tree.
1080          let mut tree = self.tree.write();
1081          // Prepare an updated Merkle tree removing the last 'n' block hashes.
1082          let updated_tree = tree.prepare_remove_last_n(usize::try_from(n)?)?;
1083          // Update the block tree.
1084          *tree = updated_tree;
1085          // Return success.
1086          Ok(())
1087      }
1088  
1089      /// Removes the last (most recent) `n` blocks from storage.
1090      pub fn remove_last_n(&self, n: u32) -> Result<()> {
1091          // Ensure 'n' is non-zero.
1092          ensure!(n > 0, "Cannot remove zero blocks");
1093  
1094          // Acquire the write lock on the block tree.
1095          let mut tree = self.tree.write();
1096  
1097          // Determine the block heights to remove.
1098          let heights = match self.max_height() {
1099              Some(end_height) => {
1100                  // Determine the start block height to remove.
1101                  let start_height = end_height
1102                      .checked_sub(n - 1)
1103                      .ok_or_else(|| anyhow!("Failed to remove last '{n}' blocks: block height underflow"))?;
1104                  // Ensure the block height matches the number of leaves in the Merkle tree.
1105                  ensure!(end_height == u32::try_from(tree.number_of_leaves())? - 1, "Block height mismatch");
1106                  // Output the block heights.
1107                  start_height..=end_height
1108              }
1109              None => bail!("Failed to remove last '{n}' blocks: no blocks in storage"),
1110          };
1111          // Fetch the block hashes to remove.
1112          let hashes = cfg_into_iter!(heights)
1113              .map(|height| match self.storage.get_block_hash(height)? {
1114                  Some(hash) => Ok(hash),
1115                  None => bail!("Failed to remove last '{n}' blocks: missing block hash for block {height}"),
1116              })
1117              .collect::<Result<Vec<_>>>()?;
1118  
1119          // Prepare an updated Merkle tree removing the last 'n' block hashes.
1120          let updated_tree = tree.prepare_remove_last_n(usize::try_from(n)?)?;
1121  
1122          atomic_batch_scope!(self, {
1123              // Remove the blocks, in descending order.
1124              for block_hash in hashes.iter().rev() {
1125                  self.storage.remove(block_hash)?;
1126              }
1127              Ok(())
1128          })?;
1129  
1130          // Update the block tree.
1131          *tree = updated_tree;
1132          // Also remove the last n blocks from the cache.
1133          if let Some(block_cache) = &*self.block_cache {
1134              block_cache.write().remove_last_n(n)?;
1135          }
1136          // Return success.
1137          Ok(())
1138      }
1139  
1140      /// Returns the transaction store.
1141      pub fn transaction_store(&self) -> &TransactionStore<N, B::TransactionStorage> {
1142          self.storage.transaction_store()
1143      }
1144  
1145      /// Returns the transition store.
1146      pub fn transition_store(&self) -> &TransitionStore<N, B::TransitionStorage> {
1147          self.storage.transaction_store().transition_store()
1148      }
1149  
1150      /// Starts an atomic batch write operation.
1151      pub fn start_atomic(&self) {
1152          self.storage.start_atomic();
1153      }
1154  
1155      /// Checks if an atomic batch is in progress.
1156      pub fn is_atomic_in_progress(&self) -> bool {
1157          self.storage.is_atomic_in_progress()
1158      }
1159  
1160      /// Checkpoints the atomic batch.
1161      pub fn atomic_checkpoint(&self) {
1162          self.storage.atomic_checkpoint();
1163      }
1164  
1165      /// Clears the latest atomic batch checkpoint.
1166      pub fn clear_latest_checkpoint(&self) {
1167          self.storage.clear_latest_checkpoint();
1168      }
1169  
1170      /// Rewinds the atomic batch to the previous checkpoint.
1171      pub fn atomic_rewind(&self) {
1172          self.storage.atomic_rewind();
1173      }
1174  
1175      /// Aborts an atomic batch write operation.
1176      pub fn abort_atomic(&self) {
1177          self.storage.abort_atomic();
1178      }
1179  
1180      /// Finishes an atomic batch write operation.
1181      pub fn finish_atomic(&self) -> Result<()> {
1182          self.storage.finish_atomic()
1183      }
1184  
1185      /// Returns the storage mode.
1186      pub fn storage_mode(&self) -> &StorageMode {
1187          self.storage.storage_mode()
1188      }
1189  
1190      /// Pauses atomic writes.
1191      pub fn pause_atomic_writes(&self) -> Result<()> {
1192          self.storage.pause_atomic_writes()
1193      }
1194  
1195      /// Unpauses atomic writes.
1196      pub fn unpause_atomic_writes<const DISCARD_BATCH: bool>(&self) -> Result<()> {
1197          self.storage.unpause_atomic_writes::<DISCARD_BATCH>()
1198      }
1199  
1200      /// Stores a database backup at the given location.
1201      #[cfg(feature = "rocks")]
1202      pub fn backup_database<P: AsRef<std::path::Path>>(&self, path: P) -> Result<(), String> {
1203          self.storage.backup_database(path)
1204      }
1205  
1206      /// Serializes and persists the current block tree.
1207      #[cfg(feature = "rocks")]
1208      pub fn cache_block_tree(&self) -> Result<()> {
1209          // Prepare the path for the target file.
1210          let mut path = alpha_ledger_dir(N::ID, self.storage.storage_mode());
1211          path.push("block_tree");
1212  
1213          // Create the target file.
1214          let file = fs::File::create(path)?;
1215          // The block tree can become quite large, so use a BufWriter in order to
1216          // not have to keep the entire serialized tree in memory, and to reduce
1217          // the number of syscalls involved with disk writes. 1MiB should provide
1218          // a good balance between the CPU cache and maximum disk throughput.
1219          let mut writer = BufWriter::with_capacity(1024 * 1024, file);
1220          bincode::serialize_into(&mut writer, &&*self.tree.read())?;
1221          writer.flush()?;
1222          // TODO(ljedrz): this operation can already take ~2.5s, so we may want
1223          // to perform chunking and parallel serialization. This may be useful
1224          // for other applications, so it should be implemented as a common
1225          // utility.
1226  
1227          Ok(())
1228      }
1229  
1230      /// Returns the root of the block tree.
1231      pub fn get_block_tree_root(&self) -> Field<N> {
1232          *self.tree.read().root()
1233      }
1234  }
1235  
1236  impl<N: Network, B: BlockStorage<N>> BlockStore<N, B> {
1237      /// Returns the block height that contains the given `state root`.
1238      pub fn find_block_height_from_state_root(&self, state_root: N::StateRoot) -> Result<Option<u32>> {
1239          self.storage.find_block_height_from_state_root(state_root)
1240      }
1241  
1242      /// Returns the block hash that contains the given `transaction ID`.
1243      pub fn find_block_hash(&self, transaction_id: &N::TransactionID) -> Result<Option<N::BlockHash>> {
1244          self.storage.find_block_hash(transaction_id)
1245      }
1246  
1247      /// Returns the block height that contains the given `solution ID`.
1248      pub fn find_block_height_from_solution_id(&self, solution_id: &SolutionID<N>) -> Result<Option<u32>> {
1249          self.storage.find_block_height_from_solution_id(solution_id)
1250      }
1251  }
1252  
1253  impl<N: Network, B: BlockStorage<N>> BlockStore<N, B> {
1254      /// Returns the read-locked `BlockCache`.
1255      ///
1256      /// This may return `None` due to lock contention, even if the cache is enabled.
1257      #[inline]
1258      #[cfg(feature = "locktick")]
1259      fn get_block_cache(&self) -> Option<LockGuard<parking_lot::RwLockReadGuard<'_, BlockCache<N>>>> {
1260          // This uses `try_read` to avoid deadlocks or prologned blocking of a thread in rayon: https://github.com/rayon-rs/rayon/issues/1205
1261          if let Some(cache) = &*self.block_cache { cache.try_read() } else { None }
1262      }
1263  
1264      #[inline]
1265      #[cfg(not(feature = "locktick"))]
1266      fn get_block_cache(&self) -> Option<parking_lot::RwLockReadGuard<'_, BlockCache<N>>> {
1267          // This uses `try_read` to avoid deadlocks or prologned blocking of a thread in rayon: https://github.com/rayon-rs/rayon/issues/1205
1268          if let Some(cache) = &*self.block_cache { cache.try_read() } else { None }
1269      }
1270  
1271      /// Returns the current state root.
1272      pub fn current_state_root(&self) -> N::StateRoot {
1273          (*self.tree.read().root()).into()
1274      }
1275  
1276      /// Returns the current block height.
1277      pub fn current_block_height(&self) -> u32 {
1278          u32::try_from(self.tree.read().number_of_leaves()).unwrap().saturating_sub(1)
1279      }
1280  
1281      /// Returns the state root that contains the given `block height`.
1282      pub fn get_state_root(&self, block_height: u32) -> Result<Option<N::StateRoot>> {
1283          self.storage.get_state_root(block_height)
1284      }
1285  
1286      /// Returns a state path for the given `commitment`.
1287      pub fn get_state_path_for_commitment(&self, commitment: &Field<N>) -> Result<StatePath<N>> {
1288          self.storage.get_state_path_for_commitment(commitment, &self.tree.read())
1289      }
1290  
1291      /// Returns a list of state paths for the given list of `commitment`s.
1292      pub fn get_state_paths_for_commitments(&self, commitments: &[Field<N>]) -> Result<Vec<StatePath<N>>> {
1293          self.storage.get_state_paths_for_commitments(commitments, &self.tree.read())
1294      }
1295  
1296      /// Returns the previous block hash of the given `block height`.
1297      pub fn get_previous_block_hash(&self, height: u32) -> Result<Option<N::BlockHash>> {
1298          if let Some(cache) = self.get_block_cache()
1299              && let Some(block) = cache.get_block(height)
1300          {
1301              return Ok(Some(block.previous_hash()));
1302          }
1303  
1304          self.storage.get_previous_block_hash(height)
1305      }
1306  
1307      /// Returns the block hash for the given `block height`.
1308      pub fn get_block_hash(&self, height: u32) -> Result<Option<N::BlockHash>> {
1309          if let Some(cache) = self.get_block_cache()
1310              && let Some(block) = cache.get_block(height)
1311          {
1312              return Ok(Some(block.hash()));
1313          }
1314  
1315          self.storage.get_block_hash(height)
1316      }
1317  
1318      /// Returns the block height for the given `block hash`.
1319      pub fn get_block_height(&self, block_hash: &N::BlockHash) -> Result<Option<u32>> {
1320          self.storage.get_block_height(block_hash)
1321      }
1322  
1323      /// Returns the block header for the given `block hash`.
1324      pub fn get_block_header(&self, block_hash: &N::BlockHash) -> Result<Option<Header<N>>> {
1325          if let Some(cache) = self.get_block_cache()
1326              && let Some(block) = cache.get_block_by_hash(block_hash)
1327          {
1328              Ok(Some(*block.header()))
1329          } else {
1330              self.storage.get_block_header(block_hash)
1331          }
1332      }
1333  
1334      /// Returns the block authority for the given `block hash`.
1335      pub fn get_block_authority(&self, block_hash: &N::BlockHash) -> Result<Option<Authority<N>>> {
1336          if let Some(cache) = self.get_block_cache()
1337              && let Some(block) = cache.get_block_by_hash(block_hash)
1338          {
1339              Ok(Some(block.authority().clone()))
1340          } else {
1341              self.storage.get_block_authority(block_hash)
1342          }
1343      }
1344  
1345      /// Returns the block ratifications for the given `block hash`.
1346      pub fn get_block_ratifications(&self, block_hash: &N::BlockHash) -> Result<Option<Ratifications<N>>> {
1347          if let Some(block_cache) = self.get_block_cache()
1348              && let Some(block) = block_cache.get_block_by_hash(block_hash)
1349          {
1350              Ok(Some(block.ratifications().clone()))
1351          } else {
1352              self.storage.get_block_ratifications(block_hash)
1353          }
1354      }
1355  
1356      /// Returns the block solutions for the given `block hash`.
1357      pub fn get_block_solutions(&self, block_hash: &N::BlockHash) -> Result<Solutions<N>> {
1358          if let Some(block_cache) = self.get_block_cache()
1359              && let Some(block) = block_cache.get_block_by_hash(block_hash)
1360          {
1361              Ok(block.solutions().clone())
1362          } else {
1363              self.storage.get_block_solutions(block_hash)
1364          }
1365      }
1366  
1367      /// Returns the prover solution for the given solution ID.
1368      pub fn get_solution(&self, solution_id: &SolutionID<N>) -> Result<Solution<N>> {
1369          self.storage.get_solution(solution_id)
1370      }
1371  
1372      /// Returns the block transactions for the given `block hash`.
1373      pub fn get_block_transactions(&self, block_hash: &N::BlockHash) -> Result<Option<Transactions<N>>> {
1374          self.storage.get_block_transactions(block_hash)
1375      }
1376  
1377      /// Returns the block aborted transaction IDs for the given `block hash`.
1378      pub fn get_block_aborted_transaction_ids(
1379          &self,
1380          block_hash: &N::BlockHash,
1381      ) -> Result<Option<Vec<N::TransactionID>>> {
1382          self.storage.get_block_aborted_transaction_ids(block_hash)
1383      }
1384  
1385      /// Returns the transaction for the given `transaction ID`.
1386      ///
1387      /// For a rejected transaction, this returns the fee transaction, not the original/unconfirmed one.
1388      pub fn get_transaction(&self, transaction_id: &N::TransactionID) -> Result<Option<Transaction<N>>> {
1389          self.storage.get_transaction(transaction_id)
1390      }
1391  
1392      /// Returns the confirmed transaction for the given `transaction ID`.
1393      pub fn get_confirmed_transaction(
1394          &self,
1395          transaction_id: &N::TransactionID,
1396      ) -> Result<Option<ConfirmedTransaction<N>>> {
1397          self.storage.get_confirmed_transaction(*transaction_id)
1398      }
1399  
1400      /// Returns the unconfirmed transaction for the given `transaction ID`.
1401      ///
1402      /// For a rejected transaction, this returns the origin transaction issued by the user, not the fee transaction.
1403      pub fn get_unconfirmed_transaction(&self, transaction_id: &N::TransactionID) -> Result<Option<Transaction<N>>> {
1404          self.storage.get_unconfirmed_transaction(transaction_id)
1405      }
1406  
1407      /// Returns the block for the given `block hash`.
1408      pub fn get_block(&self, block_hash: &N::BlockHash) -> Result<Option<Block<N>>> {
1409          if let Some(cache) = self.block_cache.as_ref()
1410              && let Some(block) = cache.read().get_block_by_hash(block_hash)
1411          {
1412              Ok(Some(block.clone()))
1413          } else {
1414              self.storage.get_block(block_hash)
1415          }
1416      }
1417  
1418      /// Returns the latest edition for the given `program ID`.
1419      pub fn get_latest_edition_for_program(&self, program_id: &ProgramID<N>) -> Result<Option<u16>> {
1420          self.storage.transaction_store().get_latest_edition_for_program(program_id)
1421      }
1422  
1423      /// Returns the latest program for the given `program ID`.
1424      pub fn get_latest_program(&self, program_id: &ProgramID<N>) -> Result<Option<Program<N>>> {
1425          self.storage.transaction_store().get_latest_program(program_id)
1426      }
1427  
1428      /// Returns the program for the given `program ID` and `edition`.
1429      pub fn get_program_for_edition(&self, program_id: &ProgramID<N>, edition: u16) -> Result<Option<Program<N>>> {
1430          self.storage.transaction_store().get_program_for_edition(program_id, edition)
1431      }
1432  
1433      /// Returns true if there is a block for the given certificate.
1434      pub fn contains_block_for_certificate(&self, certificate_id: &Field<N>) -> Result<bool> {
1435          self.storage.contains_block_for_certificate(certificate_id)
1436      }
1437  
1438      /// Returns the batch certificate for the given `certificate ID`.
1439      pub fn get_batch_certificate(&self, certificate_id: &Field<N>) -> Result<Option<BatchCertificate<N>>> {
1440          self.storage.get_batch_certificate(certificate_id)
1441      }
1442  }
1443  
1444  impl<N: Network, B: BlockStorage<N>> BlockStore<N, B> {
1445      /// Returns `true` if the given state root exists.
1446      pub fn contains_state_root(&self, state_root: &N::StateRoot) -> Result<bool> {
1447          self.storage.reverse_state_root_map().contains_key_confirmed(state_root)
1448      }
1449  
1450      /// Returns `true` if the given block height exists.
1451      pub fn contains_block_height(&self, height: u32) -> Result<bool> {
1452          self.storage.id_map().contains_key_confirmed(&height)
1453      }
1454  
1455      /// Returns `true` if the given block hash exists.
1456      pub fn contains_block_hash(&self, block_hash: &N::BlockHash) -> Result<bool> {
1457          self.storage.reverse_id_map().contains_key_confirmed(block_hash)
1458      }
1459  
1460      /// Returns `true` if the given transaction ID exists.
1461      pub fn contains_transaction_id(&self, transaction_id: &N::TransactionID) -> Result<bool> {
1462          self.storage.contains_transaction_id(transaction_id)
1463      }
1464  
1465      /// Returns `true` if the given rejected transaction ID or aborted transaction ID exists.
1466      pub fn contains_rejected_or_aborted_transaction_id(&self, transaction_id: &N::TransactionID) -> Result<bool> {
1467          self.storage.contains_rejected_or_aborted_transaction_id(transaction_id)
1468      }
1469  
1470      /// Returns `true` if the given rejected deployment or execution ID.
1471      pub fn contains_rejected_deployment_or_execution_id(&self, rejected_id: &Field<N>) -> Result<bool> {
1472          self.storage.contains_rejected_deployment_or_execution_id(rejected_id)
1473      }
1474  
1475      /// Returns `true` if the given certificate ID exists.
1476      pub fn contains_certificate(&self, certificate_id: &Field<N>) -> Result<bool> {
1477          self.storage.certificate_map().contains_key_confirmed(certificate_id)
1478      }
1479  
1480      /// Returns `true` if the given solution ID exists.
1481      pub fn contains_solution_id(&self, solution_id: &SolutionID<N>) -> Result<bool> {
1482          Ok(self.storage.solution_ids_map().contains_key_confirmed(solution_id)?
1483              || self.contains_aborted_solution_id(solution_id)?)
1484      }
1485  
1486      /// Returns `true` if the given aborted solution ID exists.
1487      fn contains_aborted_solution_id(&self, solution_id: &SolutionID<N>) -> Result<bool> {
1488          self.storage.aborted_solution_heights_map().contains_key_confirmed(solution_id)
1489      }
1490  }
1491  
1492  impl<N: Network, B: BlockStorage<N>> BlockStore<N, B> {
1493      /// Returns an iterator over the state roots, for all blocks in `self`.
1494      pub fn state_roots(&self) -> impl '_ + Iterator<Item = Cow<'_, N::StateRoot>> {
1495          self.storage.reverse_state_root_map().keys_confirmed()
1496      }
1497  
1498      /// Returns an iterator over the block heights, for all blocks in `self`.
1499      pub fn heights(&self) -> impl '_ + Iterator<Item = Cow<'_, u32>> {
1500          self.storage.id_map().keys_confirmed()
1501      }
1502  
1503      /// Returns the height of the latest block in the storage.
1504      pub fn max_height(&self) -> Option<u32> {
1505          self.storage.id_map().len_confirmed().checked_sub(1)?.try_into().ok()
1506      }
1507  
1508      /// Returns an iterator over the block hashes, for all blocks in `self`.
1509      pub fn hashes(&self) -> impl '_ + Iterator<Item = Cow<'_, N::BlockHash>> {
1510          self.storage.reverse_id_map().keys_confirmed()
1511      }
1512  
1513      /// Returns an iterator over the solution IDs, for all blocks in `self`.
1514      pub fn solution_ids(&self) -> impl '_ + Iterator<Item = Cow<'_, SolutionID<N>>> {
1515          self.storage.solution_ids_map().keys_confirmed()
1516      }
1517  }
1518  
1519  #[cfg(test)]
1520  mod tests {
1521      use super::*;
1522      use crate::helpers::memory::BlockMemory;
1523  
1524      type CurrentNetwork = console::network::MainnetV0;
1525  
1526      #[test]
1527      fn test_current_block_height_empty() {
1528          // Initialize a new block store.
1529          let block_store = BlockStore::<CurrentNetwork, BlockMemory<_>>::open(StorageMode::new_test(None)).unwrap();
1530  
1531          // Current_block_height shouldn't panic.
1532          assert_eq!(block_store.current_block_height(), 0);
1533  
1534          // Verify the equivalence of the alternative method.
1535          assert_eq!(block_store.max_height().unwrap_or_default(), 0);
1536      }
1537  
1538      #[test]
1539      fn test_insert_get_remove() {
1540          let rng = &mut TestRng::default();
1541  
1542          // Sample the block.
1543          let block = alphavm_ledger_test_helpers::sample_genesis_block(rng);
1544          let block_hash = block.hash();
1545  
1546          // Initialize a new block store.
1547          let block_store = BlockStore::<CurrentNetwork, BlockMemory<_>>::open(StorageMode::new_test(None)).unwrap();
1548  
1549          // Ensure the block does not exist.
1550          let candidate = block_store.get_block(&block_hash).unwrap();
1551          assert_eq!(None, candidate);
1552  
1553          // Insert the block.
1554          block_store.insert(&block).unwrap();
1555  
1556          // Retrieve the block.
1557          let candidate = block_store.get_block(&block_hash).unwrap();
1558          assert_eq!(Some(block), candidate);
1559  
1560          // Remove the block.
1561          block_store.remove_last_n(1).unwrap();
1562  
1563          // Ensure the block does not exist.
1564          let candidate = block_store.get_block(&block_hash).unwrap();
1565          assert_eq!(None, candidate);
1566      }
1567  
1568      #[test]
1569      fn test_find_block_hash() {
1570          let rng = &mut TestRng::default();
1571  
1572          // Sample the block.
1573          let block = alphavm_ledger_test_helpers::sample_genesis_block(rng);
1574          let block_hash = block.hash();
1575          assert!(block.transactions().num_accepted() > 0, "This test must be run with at least one transaction.");
1576  
1577          // Initialize a new block store.
1578          let block_store = BlockStore::<CurrentNetwork, BlockMemory<_>>::open(StorageMode::new_test(None)).unwrap();
1579  
1580          // Ensure the block does not exist.
1581          let candidate = block_store.get_block(&block_hash).unwrap();
1582          assert_eq!(None, candidate);
1583  
1584          for transaction_id in block.transaction_ids() {
1585              // Ensure the block hash is not found.
1586              let candidate = block_store.find_block_hash(transaction_id).unwrap();
1587              assert_eq!(None, candidate);
1588          }
1589  
1590          // Insert the block.
1591          block_store.insert(&block).unwrap();
1592  
1593          for transaction_id in block.transaction_ids() {
1594              // Find the block hash.
1595              let candidate = block_store.find_block_hash(transaction_id).unwrap();
1596              assert_eq!(Some(block_hash), candidate);
1597          }
1598  
1599          // Remove the block.
1600          block_store.remove_last_n(1).unwrap();
1601  
1602          for transaction_id in block.transaction_ids() {
1603              // Ensure the block hash is not found.
1604              let candidate = block_store.find_block_hash(transaction_id).unwrap();
1605              assert_eq!(None, candidate);
1606          }
1607      }
1608  
1609      #[test]
1610      fn test_get_transaction() {
1611          let rng = &mut TestRng::default();
1612  
1613          // Sample the block.
1614          let block = alphavm_ledger_test_helpers::sample_genesis_block(rng);
1615          assert!(block.transactions().num_accepted() > 0, "This test must be run with at least one transaction.");
1616  
1617          // Initialize a new block store.
1618          let block_store = BlockStore::<CurrentNetwork, BlockMemory<_>>::open(StorageMode::new_test(None)).unwrap();
1619          // Insert the block.
1620          block_store.insert(&block).unwrap();
1621  
1622          for confirmed in block.transactions().clone().into_iter() {
1623              // Retrieve the transaction.
1624              assert_eq!(block_store.get_transaction(&confirmed.id()).unwrap().unwrap(), confirmed.into_transaction());
1625          }
1626      }
1627  
1628      #[test]
1629      fn test_get_confirmed_transaction() {
1630          let rng = &mut TestRng::default();
1631  
1632          // Sample the block.
1633          let block = alphavm_ledger_test_helpers::sample_genesis_block(rng);
1634          assert!(block.transactions().num_accepted() > 0, "This test must be run with at least one transaction.");
1635  
1636          // Initialize a new block store.
1637          let block_store = BlockStore::<CurrentNetwork, BlockMemory<_>>::open(StorageMode::new_test(None)).unwrap();
1638          // Insert the block.
1639          block_store.insert(&block).unwrap();
1640  
1641          for confirmed in block.transactions().clone().into_iter() {
1642              // Retrieve the transaction.
1643              assert_eq!(block_store.get_confirmed_transaction(&confirmed.id()).unwrap().unwrap(), confirmed);
1644          }
1645      }
1646  
1647      #[test]
1648      fn test_get_unconfirmed_transaction() {
1649          let rng = &mut TestRng::default();
1650  
1651          // Sample the block.
1652          let block = alphavm_ledger_test_helpers::sample_genesis_block(rng);
1653          assert!(block.transactions().num_accepted() > 0, "This test must be run with at least one transaction.");
1654  
1655          // Initialize a new block store.
1656          let block_store = BlockStore::<CurrentNetwork, BlockMemory<_>>::open(StorageMode::new_test(None)).unwrap();
1657          // Insert the block.
1658          block_store.insert(&block).unwrap();
1659  
1660          for confirmed in block.transactions().clone().into_iter() {
1661              // Retrieve the transaction.
1662              assert_eq!(
1663                  block_store.get_unconfirmed_transaction(&confirmed.id()).unwrap().unwrap(),
1664                  confirmed.to_unconfirmed_transaction().unwrap()
1665              );
1666          }
1667      }
1668  
1669      /// Test that we can look up a rejected transaction using the fee transaction ID.
1670      #[test]
1671      fn test_rejected_transaction() {
1672          let rng = &mut TestRng::default();
1673  
1674          let private_key = alphavm_ledger_test_helpers::sample_genesis_private_key(rng);
1675  
1676          let block_store = BlockStore::<CurrentNetwork, BlockMemory<_>>::open(StorageMode::new_test(None)).unwrap();
1677  
1678          let fee = alphavm_ledger_test_helpers::sample_fee_public_transaction(rng);
1679          let rejected = alphavm_ledger_test_helpers::sample_rejected_execution(false, rng);
1680          let transactions =
1681              Transactions::from_iter([
1682                  ConfirmedTransaction::rejected_execute(0, fee.clone(), rejected.clone(), vec![]).unwrap()
1683              ]);
1684          let ratifications = Ratifications::try_from(vec![]).unwrap();
1685  
1686          let header = Header::genesis(&ratifications, &transactions, vec![]).unwrap();
1687          let previous_hash = <CurrentNetwork as Network>::BlockHash::default();
1688  
1689          let fee_id = fee.id();
1690          let unconfirmed_id = rejected.to_unconfirmed_id(&fee.fee_transition()).unwrap().into();
1691  
1692          // Construct the block.
1693          let block = Block::new_beacon(
1694              &private_key,
1695              previous_hash,
1696              header,
1697              ratifications,
1698              None.into(),
1699              vec![],
1700              transactions,
1701              vec![unconfirmed_id],
1702              rng,
1703          )
1704          .unwrap();
1705  
1706          block_store.insert(&block).unwrap();
1707  
1708          let txn1 = block_store.get_unconfirmed_transaction(&unconfirmed_id).unwrap().unwrap();
1709          let txn2 = block_store.get_unconfirmed_transaction(&fee_id).unwrap().unwrap();
1710  
1711          // Ensure the execute transaction is returned in both cases.
1712          assert!(matches!(txn2, Transaction::Execute(..)));
1713          assert_eq!(txn1, txn2);
1714  
1715          let txn3 = block_store.get_transaction(&unconfirmed_id).unwrap().unwrap();
1716          let txn4 = block_store.get_transaction(&fee_id).unwrap().unwrap();
1717  
1718          // For get_transaction the Fee must be returned in both cases.
1719          assert!(matches!(txn3, Transaction::Fee(..)));
1720          assert_ne!(txn1, txn3);
1721          assert_eq!(txn3, txn4);
1722      }
1723  }