diff --git a/ledger/src/blockstore.rs b/ledger/src/blockstore.rs index 3ea9525fc..bf9a4096b 100644 --- a/ledger/src/blockstore.rs +++ b/ledger/src/blockstore.rs @@ -59,7 +59,7 @@ use { solana_transaction_status::{ ConfirmedTransactionStatusWithSignature, ConfirmedTransactionWithStatusMeta, Rewards, TransactionStatusMeta, TransactionWithStatusMeta, VersionedConfirmedBlock, - VersionedTransactionWithStatusMeta, + VersionedConfirmedBlockWithEntries, VersionedTransactionWithStatusMeta, }, std::{ borrow::Cow, @@ -2031,6 +2031,33 @@ impl Blockstore { slot: Slot, require_previous_blockhash: bool, ) -> Result { + self.get_complete_block_with_entries(slot, require_previous_blockhash, false) + .map(|result| result.block) + } + + pub fn get_rooted_block_with_entries( + &self, + slot: Slot, + require_previous_blockhash: bool, + ) -> Result { + datapoint_info!( + "blockstore-rpc-api", + ("method", "get_rooted_block_with_entries", String) + ); + let _lock = self.check_lowest_cleanup_slot(slot)?; + + if self.is_root(slot) { + return self.get_complete_block_with_entries(slot, require_previous_blockhash, true); + } + Err(BlockstoreError::SlotNotRooted) + } + + fn get_complete_block_with_entries( + &self, + slot: Slot, + require_previous_blockhash: bool, + populate_entries: bool, + ) -> Result { let Some(slot_meta) = self.meta_cf.get(slot)? else { info!("SlotMeta not found for slot {}", slot); return Err(BlockstoreError::SlotUnavailable); @@ -2042,9 +2069,26 @@ impl Blockstore { .last() .map(|entry| entry.hash) .unwrap_or_else(|| panic!("Rooted slot {slot:?} must have blockhash")); + let mut starting_transaction_index = 0; + let mut entries = if populate_entries { + Vec::with_capacity(slot_entries.len()) + } else { + Vec::new() + }; let slot_transaction_iterator = slot_entries .into_iter() - .flat_map(|entry| entry.transactions) + .flat_map(|entry| { + if populate_entries { + entries.push(solana_transaction_status::EntrySummary { + num_hashes: entry.num_hashes, + hash: entry.hash, + num_transactions: entry.transactions.len() as u64, + starting_transaction_index, + }); + starting_transaction_index += entry.transactions.len(); + } + entry.transactions + }) .map(|transaction| { if let Err(err) = transaction.sanitize() { warn!( @@ -2096,7 +2140,7 @@ impl Blockstore { block_time, block_height, }; - return Ok(block); + return Ok(VersionedConfirmedBlockWithEntries { block, entries }); } } Err(BlockstoreError::SlotUnavailable) diff --git a/transaction-status/src/lib.rs b/transaction-status/src/lib.rs index 84654a564..fac20d985 100644 --- a/transaction-status/src/lib.rs +++ b/transaction-status/src/lib.rs @@ -12,6 +12,7 @@ use { solana_sdk::{ clock::{Slot, UnixTimestamp}, commitment_config::CommitmentConfig, + hash::Hash, instruction::CompiledInstruction, message::{ v0::{self, LoadedAddresses, LoadedMessage, MessageAddressTableLookup}, @@ -793,6 +794,23 @@ pub struct UiConfirmedBlock { pub block_height: Option, } +// Confirmed block with type guarantees that transaction metadata is always +// present, as well as a list of the entry data needed to cryptographically +// verify the block. Used for uploading to BigTable. +pub struct VersionedConfirmedBlockWithEntries { + pub block: VersionedConfirmedBlock, + pub entries: Vec, +} + +// Data needed to reconstruct an Entry, given an ordered list of transactions in +// a block. Used for uploading to BigTable. +pub struct EntrySummary { + pub num_hashes: u64, + pub hash: Hash, + pub num_transactions: u64, + pub starting_transaction_index: usize, +} + #[derive(Clone, Debug, PartialEq)] #[allow(clippy::large_enum_variant)] pub enum TransactionWithStatusMeta {