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 }