diff --git a/core/src/banking_stage.rs b/core/src/banking_stage.rs index eefc3e06dd..2d49de60a8 100644 --- a/core/src/banking_stage.rs +++ b/core/src/banking_stage.rs @@ -1925,7 +1925,7 @@ mod tests { transaction::TransactionError, }, solana_streamer::{recvmmsg::recv_mmsg, socket::SocketAddrSpace}, - solana_transaction_status::TransactionWithStatusMeta, + solana_transaction_status::TransactionWithMetadata, solana_vote_program::vote_transaction, std::{ net::SocketAddr, @@ -3080,20 +3080,17 @@ mod tests { let keypair1 = Keypair::new(); let success_tx = - system_transaction::transfer(&mint_keypair, &pubkey, 1, genesis_config.hash()); + system_transaction::transfer(&mint_keypair, &pubkey, 0, genesis_config.hash()); let success_signature = success_tx.signatures[0]; let entry_1 = next_entry(&genesis_config.hash(), 1, vec![success_tx.clone()]); let ix_error_tx = - system_transaction::transfer(&keypair1, &pubkey1, 10, genesis_config.hash()); + system_transaction::transfer(&keypair1, &pubkey1, std::u64::MAX, genesis_config.hash()); let ix_error_signature = ix_error_tx.signatures[0]; let entry_2 = next_entry(&entry_1.hash, 1, vec![ix_error_tx.clone()]); - let fail_tx = - system_transaction::transfer(&mint_keypair, &pubkey1, 1, genesis_config.hash()); - let entry_3 = next_entry(&entry_2.hash, 1, vec![fail_tx.clone()]); - let entries = vec![entry_1, entry_2, entry_3]; + let entries = vec![entry_1, entry_2]; - let transactions = vec![success_tx.into(), ix_error_tx.into(), fail_tx.into()]; - bank.transfer(4, &mint_keypair, &keypair1.pubkey()).unwrap(); + let transactions = vec![success_tx.into(), ix_error_tx.into()]; + bank.transfer(1, &mint_keypair, &keypair1.pubkey()).unwrap(); let start = Arc::new(Instant::now()); let working_bank = WorkingBank { @@ -3155,27 +3152,24 @@ mod tests { transaction_status_service.join().unwrap(); let confirmed_block = blockstore.get_rooted_block(bank.slot(), false).unwrap(); - assert_eq!(confirmed_block.transactions.len(), 3); - - for TransactionWithStatusMeta { transaction, meta } in - confirmed_block.transactions.into_iter() - { - if transaction.signatures[0] == success_signature { - let meta = meta.unwrap(); - assert_eq!(meta.status, Ok(())); - } else if transaction.signatures[0] == ix_error_signature { - let meta = meta.unwrap(); - assert_eq!( - meta.status, - Err(TransactionError::InstructionError( - 0, - InstructionError::Custom(1) - )) - ); - } else { - assert_eq!(meta, None); - } - } + let actual_tx_results: Vec<_> = confirmed_block + .transactions + .into_iter() + .map(|TransactionWithMetadata { transaction, meta }| { + (transaction.signatures[0], meta.status) + }) + .collect(); + let expected_tx_results = vec![ + (success_signature, Ok(())), + ( + ix_error_signature, + Err(TransactionError::InstructionError( + 0, + InstructionError::Custom(1), + )), + ), + ]; + assert_eq!(actual_tx_results, expected_tx_results); poh_recorder .lock() diff --git a/core/src/replay_stage.rs b/core/src/replay_stage.rs index f034beed45..5d5c83db5f 100644 --- a/core/src/replay_stage.rs +++ b/core/src/replay_stage.rs @@ -2670,7 +2670,7 @@ mod tests { transaction::TransactionError, }, solana_streamer::socket::SocketAddrSpace, - solana_transaction_status::TransactionWithStatusMeta, + solana_transaction_status::TransactionWithMetadata, solana_vote_program::{ vote_state::{VoteState, VoteStateVersions}, vote_transaction, @@ -3472,36 +3472,37 @@ mod tests { let bank1 = Arc::new(Bank::new_from_parent(&bank0, &Pubkey::default(), 1)); let slot = bank1.slot(); - let signatures = create_test_transactions_and_populate_blockstore( + let mut test_signatures_iter = create_test_transactions_and_populate_blockstore( vec![&mint_keypair, &keypair1, &keypair2, &keypair3], bank0.slot(), bank1, blockstore.clone(), Arc::new(AtomicU64::default()), - ); + ) + .into_iter(); let confirmed_block = blockstore.get_rooted_block(slot, false).unwrap(); - assert_eq!(confirmed_block.transactions.len(), 3); + let actual_tx_results: Vec<_> = confirmed_block + .transactions + .into_iter() + .map(|TransactionWithMetadata { transaction, meta }| { + (transaction.signatures[0], meta.status) + }) + .collect(); - for TransactionWithStatusMeta { transaction, meta } in - confirmed_block.transactions.into_iter() - { - if transaction.signatures[0] == signatures[0] { - let meta = meta.unwrap(); - assert_eq!(meta.status, Ok(())); - } else if transaction.signatures[0] == signatures[1] { - let meta = meta.unwrap(); - assert_eq!( - meta.status, - Err(TransactionError::InstructionError( - 0, - InstructionError::Custom(1) - )) - ); - } else { - assert_eq!(meta, None); - } - } + let expected_tx_results = vec![ + (test_signatures_iter.next().unwrap(), Ok(())), + ( + test_signatures_iter.next().unwrap(), + Err(TransactionError::InstructionError( + 0, + InstructionError::Custom(1), + )), + ), + ]; + + assert_eq!(actual_tx_results, expected_tx_results); + assert!(test_signatures_iter.next().is_none()); } Blockstore::destroy(&ledger_path).unwrap(); } diff --git a/ledger-tool/src/bigtable.rs b/ledger-tool/src/bigtable.rs index 1f01fe3135..9feb25d0ae 100644 --- a/ledger-tool/src/bigtable.rs +++ b/ledger-tool/src/bigtable.rs @@ -16,7 +16,9 @@ use { }, solana_ledger::{blockstore::Blockstore, blockstore_db::AccessType}, solana_sdk::{clock::Slot, pubkey::Pubkey, signature::Signature}, - solana_transaction_status::{ConfirmedBlock, EncodedTransaction, UiTransactionEncoding}, + solana_transaction_status::{ + ConfirmedBlockWithOptionalMetadata, EncodedTransaction, UiTransactionEncoding, + }, std::{ collections::HashSet, path::Path, @@ -30,7 +32,6 @@ async fn upload( blockstore: Blockstore, starting_slot: Slot, ending_slot: Option, - allow_missing_metadata: bool, force_reupload: bool, ) -> Result<(), Box> { let bigtable = solana_storage_bigtable::LedgerStorage::new(false, None, None) @@ -42,7 +43,6 @@ async fn upload( bigtable, starting_slot, ending_slot, - allow_missing_metadata, force_reupload, Arc::new(AtomicBool::new(false)), ) @@ -194,7 +194,7 @@ pub async fn transaction_history( ) -> Result<(), Box> { let bigtable = solana_storage_bigtable::LedgerStorage::new(true, None, None).await?; - let mut loaded_block: Option<(Slot, ConfirmedBlock)> = None; + let mut loaded_block: Option<(Slot, ConfirmedBlockWithOptionalMetadata)> = None; while limit > 0 { let results = bigtable .get_confirmed_signatures_for_address( @@ -306,12 +306,6 @@ impl BigTableSubCommand for App<'_, '_> { .index(2) .help("Stop uploading at this slot [default: last available slot]"), ) - .arg( - Arg::with_name("allow_missing_metadata") - .long("allow-missing-metadata") - .takes_value(false) - .help("Don't panic if transaction metadata is missing"), - ) .arg( Arg::with_name("force_reupload") .long("force") @@ -506,7 +500,6 @@ pub fn bigtable_process_command(ledger_path: &Path, matches: &ArgMatches<'_>) { ("upload", Some(arg_matches)) => { let starting_slot = value_t!(arg_matches, "starting_slot", Slot).unwrap_or(0); let ending_slot = value_t!(arg_matches, "ending_slot", Slot).ok(); - let allow_missing_metadata = arg_matches.is_present("allow_missing_metadata"); let force_reupload = arg_matches.is_present("force_reupload"); let blockstore = crate::open_blockstore( &canonicalize_ledger_path(ledger_path), @@ -518,7 +511,6 @@ pub fn bigtable_process_command(ledger_path: &Path, matches: &ArgMatches<'_>) { blockstore, starting_slot, ending_slot, - allow_missing_metadata, force_reupload, )) } diff --git a/ledger/src/bigtable_upload.rs b/ledger/src/bigtable_upload.rs index 860a6348bf..fe170da142 100644 --- a/ledger/src/bigtable_upload.rs +++ b/ledger/src/bigtable_upload.rs @@ -25,7 +25,6 @@ pub async fn upload_confirmed_blocks( bigtable: solana_storage_bigtable::LedgerStorage, starting_slot: Slot, ending_slot: Option, - allow_missing_metadata: bool, force_reupload: bool, exit: Arc, ) -> Result<(), Box> { @@ -186,20 +185,7 @@ pub async fn upload_confirmed_blocks( num_blocks -= 1; None } - Some(confirmed_block) => { - if confirmed_block - .transactions - .iter() - .any(|transaction| transaction.meta.is_none()) - { - if allow_missing_metadata { - info!("Transaction metadata missing from slot {}", slot); - } else { - panic!("Transaction metadata missing from slot {}", slot); - } - } - Some(bigtable.upload_confirmed_block(slot, confirmed_block)) - } + Some(confirmed_block) => Some(bigtable.upload_confirmed_block(slot, confirmed_block)), }); for result in futures::future::join_all(uploads).await { diff --git a/ledger/src/bigtable_upload_service.rs b/ledger/src/bigtable_upload_service.rs index 9dba8e483c..bf9eb6085b 100644 --- a/ledger/src/bigtable_upload_service.rs +++ b/ledger/src/bigtable_upload_service.rs @@ -72,7 +72,6 @@ impl BigTableUploadService { bigtable_ledger_storage.clone(), start_slot, Some(end_slot), - true, false, exit.clone(), )); diff --git a/ledger/src/blockstore.rs b/ledger/src/blockstore.rs index 97a3a52164..ce420bc8e4 100644 --- a/ledger/src/blockstore.rs +++ b/ledger/src/blockstore.rs @@ -42,7 +42,7 @@ use { solana_storage_proto::{StoredExtendedRewards, StoredTransactionStatusMeta}, solana_transaction_status::{ ConfirmedBlock, ConfirmedTransaction, ConfirmedTransactionStatusWithSignature, Rewards, - TransactionStatusMeta, TransactionWithStatusMeta, + TransactionStatusMeta, TransactionWithMetadata, TransactionWithOptionalMetadata, }, std::{ borrow::Cow, @@ -1950,7 +1950,7 @@ impl Blockstore { // from shreds received. parent_slot: slot_meta.parent_slot.unwrap(), transactions: self - .map_transactions_to_statuses(slot, slot_transaction_iterator), + .map_transactions_to_statuses(slot, slot_transaction_iterator)?, rewards, block_time, block_height, @@ -1965,17 +1965,16 @@ impl Blockstore { &self, slot: Slot, iterator: impl Iterator + 'a, - ) -> Vec { + ) -> Result> { iterator .map(|transaction| { let signature = transaction.signatures[0]; - TransactionWithStatusMeta { + Ok(TransactionWithMetadata { transaction, meta: self - .read_transaction_status((signature, slot)) - .ok() - .flatten(), - } + .read_transaction_status((signature, slot))? + .ok_or(BlockstoreError::MissingTransactionMetadata)?, + }) }) .collect() } @@ -2256,7 +2255,7 @@ impl Blockstore { let block_time = self.get_block_time(slot)?; Ok(Some(ConfirmedTransaction { slot, - transaction: TransactionWithStatusMeta { + transaction: TransactionWithOptionalMetadata { transaction, meta: Some(status), }, @@ -6082,7 +6081,7 @@ pub mod tests { .put_meta_bytes(slot - 1, &serialize(&parent_meta).unwrap()) .unwrap(); - let expected_transactions: Vec = entries + let expected_transactions: Vec = entries .iter() .cloned() .filter(|entry| !entry.is_tick()) @@ -6143,9 +6142,9 @@ pub mod tests { .transaction_status_cf .put_protobuf((0, signature, slot + 2), &status) .unwrap(); - TransactionWithStatusMeta { + TransactionWithMetadata { transaction, - meta: Some(TransactionStatusMeta { + meta: TransactionStatusMeta { status: Ok(()), fee: 42, pre_balances, @@ -6155,7 +6154,7 @@ pub mod tests { pre_token_balances: Some(vec![]), post_token_balances: Some(vec![]), rewards: Some(vec![]), - }), + }, } }) .collect(); @@ -6952,7 +6951,7 @@ pub mod tests { blockstore.insert_shreds(shreds, None, false).unwrap(); blockstore.set_roots(vec![slot - 1, slot].iter()).unwrap(); - let expected_transactions: Vec = entries + let expected_transactions: Vec = entries .iter() .cloned() .filter(|entry| !entry.is_tick()) @@ -6989,7 +6988,7 @@ pub mod tests { .transaction_status_cf .put_protobuf((0, signature, slot), &status) .unwrap(); - TransactionWithStatusMeta { + TransactionWithOptionalMetadata { transaction, meta: Some(TransactionStatusMeta { status: Ok(()), @@ -7030,7 +7029,7 @@ pub mod tests { blockstore.run_purge(0, 2, PurgeType::PrimaryIndex).unwrap(); *blockstore.lowest_cleanup_slot.write().unwrap() = slot; - for TransactionWithStatusMeta { transaction, .. } in expected_transactions { + for TransactionWithOptionalMetadata { transaction, .. } in expected_transactions { let signature = transaction.signatures[0]; assert_eq!(blockstore.get_rooted_transaction(signature).unwrap(), None,); assert_eq!( @@ -7051,7 +7050,7 @@ pub mod tests { let blockstore = Blockstore::open(&ledger_path).unwrap(); blockstore.insert_shreds(shreds, None, false).unwrap(); - let expected_transactions: Vec = entries + let expected_transactions: Vec = entries .iter() .cloned() .filter(|entry| !entry.is_tick()) @@ -7088,7 +7087,7 @@ pub mod tests { .transaction_status_cf .put_protobuf((0, signature, slot), &status) .unwrap(); - TransactionWithStatusMeta { + TransactionWithOptionalMetadata { transaction, meta: Some(TransactionStatusMeta { status: Ok(()), @@ -7122,7 +7121,7 @@ pub mod tests { blockstore.run_purge(0, 2, PurgeType::PrimaryIndex).unwrap(); *blockstore.lowest_cleanup_slot.write().unwrap() = slot; - for TransactionWithStatusMeta { transaction, .. } in expected_transactions { + for TransactionWithOptionalMetadata { transaction, .. } in expected_transactions { let signature = transaction.signatures[0]; assert_eq!( blockstore @@ -7877,6 +7876,16 @@ pub mod tests { .unwrap(); transactions.push(transaction); } + + let map_result = + blockstore.map_transactions_to_statuses(slot, transactions.clone().into_iter()); + assert!(map_result.is_ok()); + let map = map_result.unwrap(); + assert_eq!(map.len(), 4); + for (x, m) in map.iter().enumerate() { + assert_eq!(m.meta.fee, x as u64); + } + // Push transaction that will not have matching status, as a test case transactions.push(Transaction::new_with_compiled_instructions( &[&Keypair::new()], @@ -7886,12 +7895,9 @@ pub mod tests { vec![CompiledInstruction::new(1, &(), vec![0])], )); - let map = blockstore.map_transactions_to_statuses(slot, transactions.into_iter()); - assert_eq!(map.len(), 5); - for (x, m) in map.iter().take(4).enumerate() { - assert_eq!(m.meta.as_ref().unwrap().fee, x as u64); - } - assert_eq!(map[4].meta, None); + let map_result = + blockstore.map_transactions_to_statuses(slot, transactions.into_iter()); + assert_matches!(map_result, Err(BlockstoreError::MissingTransactionMetadata)); } Blockstore::destroy(&blockstore_path).expect("Expected successful database destruction"); } diff --git a/ledger/src/blockstore_db.rs b/ledger/src/blockstore_db.rs index 9cd0abcfee..d6e864abea 100644 --- a/ledger/src/blockstore_db.rs +++ b/ledger/src/blockstore_db.rs @@ -101,6 +101,7 @@ pub enum BlockstoreError { ProtobufDecodeError(#[from] prost::DecodeError), ParentEntriesUnavailable, SlotUnavailable, + MissingTransactionMetadata, } pub type Result = std::result::Result; diff --git a/local-cluster/tests/common.rs b/local-cluster/tests/common.rs index 18d84da1bf..067c683761 100644 --- a/local-cluster/tests/common.rs +++ b/local-cluster/tests/common.rs @@ -250,6 +250,10 @@ pub fn run_cluster_partition( ..ValidatorConfig::default_for_test() }; + // Enabled so that transaction statuses are written to blockstore which enables + // tests to detect when a block has been replayed. + validator_config.rpc_config.enable_rpc_transaction_history = true; + // Returns: // 1) The keys for the validators // 2) The amount of time it would take to iterate through one full iteration of the given diff --git a/local-cluster/tests/local_cluster_slow.rs b/local-cluster/tests/local_cluster_slow.rs index 96c1c970b9..5229fdaa71 100644 --- a/local-cluster/tests/local_cluster_slow.rs +++ b/local-cluster/tests/local_cluster_slow.rs @@ -725,13 +725,19 @@ fn find_latest_replayed_slot_from_ledger( // Wait for the slot to be replayed loop { info!("Waiting for slot {} to be replayed", latest_slot); - if !blockstore + let replayed_transactions = blockstore .map_transactions_to_statuses( latest_slot, non_tick_entry.transactions.clone().into_iter(), ) - .is_empty() - { + .unwrap_or_else(|_| { + info!( + "Transaction statuses for slot {} haven't been written yet", + latest_slot + ); + Vec::new() + }); + if !replayed_transactions.is_empty() { return ( latest_slot, AncestorIterator::new(latest_slot, &blockstore).collect(), diff --git a/programs/bpf/tests/programs.rs b/programs/bpf/tests/programs.rs index 75631f4766..323fdc904d 100644 --- a/programs/bpf/tests/programs.rs +++ b/programs/bpf/tests/programs.rs @@ -50,7 +50,7 @@ use solana_sdk::{ }; use solana_transaction_status::{ token_balances::collect_token_balances, ConfirmedTransaction, InnerInstructions, - TransactionStatusMeta, TransactionWithStatusMeta, UiTransactionEncoding, + TransactionStatusMeta, TransactionWithOptionalMetadata, UiTransactionEncoding, }; use std::{ cell::RefCell, collections::HashMap, env, fs::File, io::Read, path::PathBuf, str::FromStr, @@ -403,7 +403,7 @@ fn execute_transactions(bank: &Bank, txs: &[Transaction]) -> Vec = serde_json::from_value(result["result"].clone()).unwrap(); let confirmed_block = confirmed_block.unwrap(); - assert_eq!(confirmed_block.transactions.len(), 3); + assert_eq!(confirmed_block.transactions.len(), 2); assert_eq!(confirmed_block.rewards, vec![]); for EncodedTransactionWithStatusMeta { transaction, meta } in @@ -6309,7 +6311,7 @@ pub mod tests { let confirmed_block: Option = serde_json::from_value(result["result"].clone()).unwrap(); let confirmed_block = confirmed_block.unwrap(); - assert_eq!(confirmed_block.transactions.len(), 3); + assert_eq!(confirmed_block.transactions.len(), 2); assert_eq!(confirmed_block.rewards, vec![]); for EncodedTransactionWithStatusMeta { transaction, meta } in diff --git a/storage-bigtable/src/bigtable.rs b/storage-bigtable/src/bigtable.rs index 9b541be49c..5ce2f15b54 100644 --- a/storage-bigtable/src/bigtable.rs +++ b/storage-bigtable/src/bigtable.rs @@ -793,17 +793,42 @@ mod tests { solana_sdk::{hash::Hash, signature::Keypair, system_transaction}, solana_storage_proto::convert::generated, solana_transaction_status::{ - ConfirmedBlock, TransactionStatusMeta, TransactionWithStatusMeta, + ConfirmedBlockWithOptionalMetadata, TransactionStatusMeta, + TransactionWithOptionalMetadata, }, std::convert::TryInto, }; + fn convert_from_block_with_optional_meta( + confirmed_block: ConfirmedBlockWithOptionalMetadata, + ) -> generated::ConfirmedBlock { + let ConfirmedBlockWithOptionalMetadata { + previous_blockhash, + blockhash, + parent_slot, + transactions, + rewards, + block_time, + block_height, + } = confirmed_block; + + generated::ConfirmedBlock { + previous_blockhash, + blockhash, + parent_slot, + transactions: transactions.into_iter().map(|tx| tx.into()).collect(), + rewards: rewards.into_iter().map(|r| r.into()).collect(), + block_time: block_time.map(|timestamp| generated::UnixTimestamp { timestamp }), + block_height: block_height.map(|block_height| generated::BlockHeight { block_height }), + } + } + #[test] fn test_deserialize_protobuf_or_bincode_cell_data() { let from = Keypair::new(); let recipient = solana_sdk::pubkey::new_rand(); let transaction = system_transaction::transfer(&from, &recipient, 42, Hash::default()); - let with_meta = TransactionWithStatusMeta { + let with_meta = TransactionWithOptionalMetadata { transaction, meta: Some(TransactionStatusMeta { status: Ok(()), @@ -817,7 +842,7 @@ mod tests { rewards: Some(vec![]), }), }; - let block = ConfirmedBlock { + let block = ConfirmedBlockWithOptionalMetadata { transactions: vec![with_meta], parent_slot: 1, blockhash: Hash::default().to_string(), @@ -831,7 +856,7 @@ mod tests { ) .unwrap(); - let protobuf_block = generated::ConfirmedBlock::from(block.clone()); + let protobuf_block = convert_from_block_with_optional_meta(block.clone()); let mut buf = Vec::with_capacity(protobuf_block.encoded_len()); protobuf_block.encode(&mut buf).unwrap(); let protobuf_block = compress_best(&buf).unwrap(); diff --git a/storage-bigtable/src/lib.rs b/storage-bigtable/src/lib.rs index 1c8a91f57e..f470aa8650 100644 --- a/storage-bigtable/src/lib.rs +++ b/storage-bigtable/src/lib.rs @@ -13,10 +13,10 @@ use { }, solana_storage_proto::convert::{generated, tx_by_addr}, solana_transaction_status::{ - extract_and_fmt_memos, ConfirmedBlock, ConfirmedTransaction, - ConfirmedTransactionStatusWithSignature, Reward, TransactionByAddrInfo, - TransactionConfirmationStatus, TransactionStatus, TransactionStatusMeta, - TransactionWithStatusMeta, + extract_and_fmt_memos, ConfirmedBlock, ConfirmedBlockWithOptionalMetadata, + ConfirmedTransaction, ConfirmedTransactionStatusWithSignature, Reward, + TransactionByAddrInfo, TransactionConfirmationStatus, TransactionStatus, + TransactionStatusMeta, TransactionWithMetadata, TransactionWithOptionalMetadata, }, std::{ collections::{HashMap, HashSet}, @@ -114,9 +114,9 @@ struct StoredConfirmedBlock { block_height: Option, } -impl From for StoredConfirmedBlock { - fn from(confirmed_block: ConfirmedBlock) -> Self { - let ConfirmedBlock { +impl From for StoredConfirmedBlock { + fn from(confirmed_block: ConfirmedBlockWithOptionalMetadata) -> Self { + let ConfirmedBlockWithOptionalMetadata { previous_blockhash, blockhash, parent_slot, @@ -138,7 +138,7 @@ impl From for StoredConfirmedBlock { } } -impl From for ConfirmedBlock { +impl From for ConfirmedBlockWithOptionalMetadata { fn from(confirmed_block: StoredConfirmedBlock) -> Self { let StoredConfirmedBlock { previous_blockhash, @@ -168,8 +168,8 @@ struct StoredConfirmedBlockTransaction { meta: Option, } -impl From for StoredConfirmedBlockTransaction { - fn from(value: TransactionWithStatusMeta) -> Self { +impl From for StoredConfirmedBlockTransaction { + fn from(value: TransactionWithOptionalMetadata) -> Self { Self { transaction: value.transaction, meta: value.meta.map(|meta| meta.into()), @@ -177,7 +177,7 @@ impl From for StoredConfirmedBlockTransaction { } } -impl From for TransactionWithStatusMeta { +impl From for TransactionWithOptionalMetadata { fn from(value: StoredConfirmedBlockTransaction) -> Self { Self { transaction: value.transaction, @@ -392,7 +392,10 @@ impl LedgerStorage { } /// Fetch the confirmed block from the desired slot - pub async fn get_confirmed_block(&self, slot: Slot) -> Result { + pub async fn get_confirmed_block( + &self, + slot: Slot, + ) -> Result { debug!( "LedgerStorage::get_confirmed_block request received: {:?}", slot @@ -635,8 +638,8 @@ impl LedgerStorage { let mut tx_cells = vec![]; for (index, transaction_with_meta) in confirmed_block.transactions.iter().enumerate() { - let TransactionWithStatusMeta { meta, transaction } = transaction_with_meta; - let err = meta.as_ref().and_then(|meta| meta.status.clone().err()); + let TransactionWithMetadata { meta, transaction } = transaction_with_meta; + let err = meta.status.clone().err(); let index = index as u32; let signature = transaction.signatures[0]; let memo = extract_and_fmt_memos(&transaction.message); @@ -723,7 +726,7 @@ impl LedgerStorage { let mut expected_tx_infos: HashMap = HashMap::new(); let confirmed_block = self.get_confirmed_block(slot).await?; for (index, transaction_with_meta) in confirmed_block.transactions.iter().enumerate() { - let TransactionWithStatusMeta { meta, transaction } = transaction_with_meta; + let TransactionWithOptionalMetadata { meta, transaction } = transaction_with_meta; let signature = transaction.signatures[0]; let index = index as u32; let err = meta.as_ref().and_then(|meta| meta.status.clone().err()); diff --git a/storage-proto/src/convert.rs b/storage-proto/src/convert.rs index 0a8a3f97e2..8b9226eefe 100644 --- a/storage-proto/src/convert.rs +++ b/storage-proto/src/convert.rs @@ -10,8 +10,9 @@ use { transaction::{Transaction, TransactionError}, }, solana_transaction_status::{ - ConfirmedBlock, InnerInstructions, Reward, RewardType, TransactionByAddrInfo, - TransactionStatusMeta, TransactionTokenBalance, TransactionWithStatusMeta, + ConfirmedBlock, ConfirmedBlockWithOptionalMetadata, InnerInstructions, Reward, RewardType, + TransactionByAddrInfo, TransactionStatusMeta, TransactionTokenBalance, + TransactionWithMetadata, TransactionWithOptionalMetadata, }, std::{ convert::{TryFrom, TryInto}, @@ -135,7 +136,7 @@ impl From for generated::ConfirmedBlock { } } -impl TryFrom for ConfirmedBlock { +impl TryFrom for ConfirmedBlockWithOptionalMetadata { type Error = bincode::Error; fn try_from( confirmed_block: generated::ConfirmedBlock, @@ -157,7 +158,8 @@ impl TryFrom for ConfirmedBlock { transactions: transactions .into_iter() .map(|tx| tx.try_into()) - .collect::, Self::Error>>()?, + .collect::, Self::Error>>( + )?, rewards: rewards.into_iter().map(|r| r.into()).collect(), block_time: block_time.map(|generated::UnixTimestamp { timestamp }| timestamp), block_height: block_height.map(|generated::BlockHeight { block_height }| block_height), @@ -165,17 +167,25 @@ impl TryFrom for ConfirmedBlock { } } -impl From for generated::ConfirmedTransaction { - fn from(value: TransactionWithStatusMeta) -> Self { - let meta = value.meta.map(|meta| meta.into()); +impl From for generated::ConfirmedTransaction { + fn from(value: TransactionWithOptionalMetadata) -> Self { Self { transaction: Some(value.transaction.into()), - meta, + meta: value.meta.map(|meta| meta.into()), } } } -impl TryFrom for TransactionWithStatusMeta { +impl From for generated::ConfirmedTransaction { + fn from(value: TransactionWithMetadata) -> Self { + Self { + transaction: Some(value.transaction.into()), + meta: Some(value.meta.into()), + } + } +} + +impl TryFrom for TransactionWithOptionalMetadata { type Error = bincode::Error; fn try_from(value: generated::ConfirmedTransaction) -> std::result::Result { let meta = value.meta.map(|meta| meta.try_into()).transpose()?; diff --git a/transaction-status/src/lib.rs b/transaction-status/src/lib.rs index 9cea1d9ee0..56b2136511 100644 --- a/transaction-status/src/lib.rs +++ b/transaction-status/src/lib.rs @@ -355,19 +355,52 @@ pub struct Reward { pub type Rewards = Vec; -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] +#[derive(Clone, Debug, PartialEq)] pub struct ConfirmedBlock { pub previous_blockhash: String, pub blockhash: String, pub parent_slot: Slot, - pub transactions: Vec, + pub transactions: Vec, pub rewards: Rewards, pub block_time: Option, pub block_height: Option, } -impl ConfirmedBlock { +#[derive(Clone, Debug, PartialEq)] +pub struct ConfirmedBlockWithOptionalMetadata { + pub previous_blockhash: String, + pub blockhash: String, + pub parent_slot: Slot, + pub transactions: Vec, + pub rewards: Rewards, + pub block_time: Option, + pub block_height: Option, +} + +impl From for ConfirmedBlockWithOptionalMetadata { + fn from(block: ConfirmedBlock) -> Self { + Self { + previous_blockhash: block.previous_blockhash, + blockhash: block.blockhash, + parent_slot: block.parent_slot, + transactions: block + .transactions + .into_iter() + .map(|TransactionWithMetadata { transaction, meta }| { + TransactionWithOptionalMetadata { + transaction, + meta: Some(meta), + } + }) + .collect(), + rewards: block.rewards, + block_time: block.block_time, + block_height: block.block_height, + } + } +} + +impl ConfirmedBlockWithOptionalMetadata { pub fn encode(self, encoding: UiTransactionEncoding) -> EncodedConfirmedBlock { EncodedConfirmedBlock { previous_blockhash: self.previous_blockhash, @@ -499,12 +532,10 @@ impl Default for TransactionDetails { } } -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] +#[derive(Debug, Clone, PartialEq)] pub struct ConfirmedTransaction { pub slot: Slot, - #[serde(flatten)] - pub transaction: TransactionWithStatusMeta, + pub transaction: TransactionWithOptionalMetadata, pub block_time: Option, } @@ -561,14 +592,19 @@ pub struct UiParsedMessage { pub instructions: Vec, } -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct TransactionWithStatusMeta { +#[derive(Clone, Debug, PartialEq)] +pub struct TransactionWithOptionalMetadata { pub transaction: Transaction, pub meta: Option, } -impl TransactionWithStatusMeta { +#[derive(Clone, Debug, PartialEq)] +pub struct TransactionWithMetadata { + pub transaction: Transaction, + pub meta: TransactionStatusMeta, +} + +impl TransactionWithOptionalMetadata { fn encode(self, encoding: UiTransactionEncoding) -> EncodedTransactionWithStatusMeta { let message = self.transaction.message(); let meta = self.meta.map(|meta| meta.encode(encoding, message));