diff --git a/client-test/tests/client.rs b/client-test/tests/client.rs index 1b86664839e26d..f29ea043a3cf79 100644 --- a/client-test/tests/client.rs +++ b/client-test/tests/client.rs @@ -10,7 +10,7 @@ use { RpcAccountInfoConfig, RpcBlockSubscribeConfig, RpcBlockSubscribeFilter, RpcProgramAccountsConfig, }, - rpc_response::{RpcBlockUpdate, SlotInfo}, + rpc_response::SlotInfo, }, solana_ledger::{blockstore::Blockstore, get_tmp_ledger_path}, solana_rpc::{ @@ -36,7 +36,7 @@ use { }, solana_streamer::socket::SocketAddrSpace, solana_test_validator::TestValidator, - solana_transaction_status::{TransactionDetails, UiTransactionEncoding}, + solana_transaction_status::{ConfirmedBlock, TransactionDetails, UiTransactionEncoding}, std::{ collections::HashSet, net::{IpAddr, SocketAddr}, @@ -214,6 +214,7 @@ fn test_block_subscription() { .. } = create_genesis_config(10_000); let bank = Bank::new_for_tests(&genesis_config); + let rent_exempt_amount = bank.get_minimum_balance_for_rent_exemption(0); let bank_forks = Arc::new(RwLock::new(BankForks::new(bank))); // setup Blockstore @@ -227,6 +228,8 @@ fn test_block_subscription() { let keypair2 = Keypair::new(); let keypair3 = Keypair::new(); let max_complete_transaction_status_slot = Arc::new(AtomicU64::new(blockstore.max_root())); + bank.transfer(rent_exempt_amount, &alice, &keypair2.pubkey()) + .unwrap(); let _confirmed_block_signatures = create_test_transactions_and_populate_blockstore( vec![&alice, &keypair1, &keypair2, &keypair3], 0, @@ -278,23 +281,15 @@ fn test_block_subscription() { match maybe_actual { Ok(actual) => { let versioned_block = blockstore.get_complete_block(slot, false).unwrap(); - let legacy_block = versioned_block.into_legacy_block().unwrap(); - let block = legacy_block.clone().configure( - UiTransactionEncoding::Json, - TransactionDetails::Signatures, - false, - ); - let expected = RpcBlockUpdate { - slot, - block: Some(block), - err: None, - }; + let legacy_block = ConfirmedBlock::from(versioned_block) + .into_legacy_block() + .unwrap(); let block = legacy_block.configure( UiTransactionEncoding::Json, TransactionDetails::Signatures, false, ); - assert_eq!(actual.value.slot, expected.slot); + assert_eq!(actual.value.slot, slot); assert!(block.eq(&actual.value.block.unwrap())); } Err(e) => { diff --git a/core/src/banking_stage.rs b/core/src/banking_stage.rs index 887f99248b4959..944fd67bdb9371 100644 --- a/core/src/banking_stage.rs +++ b/core/src/banking_stage.rs @@ -2955,16 +2955,9 @@ mod tests { ); 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, - rent_exempt_amount, - 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 = sanitize_transactions(vec![success_tx, ix_error_tx, fail_tx]); + let transactions = sanitize_transactions(vec![success_tx, ix_error_tx]); bank.transfer(rent_exempt_amount, &mint_keypair, &keypair1.pubkey()) .unwrap(); @@ -3024,27 +3017,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 VersionedTransactionWithStatusMeta { 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(|VersionedTransactionWithStatusMeta { 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() @@ -3191,7 +3181,7 @@ mod tests { let recorded_meta = confirmed_block.transactions.pop().unwrap().meta; assert_eq!( recorded_meta, - Some(TransactionStatusMeta { + TransactionStatusMeta { status: Ok(()), pre_balances: vec![1, 0, 0], post_balances: vec![1, 0, 0], @@ -3200,7 +3190,7 @@ mod tests { rewards: Some(vec![]), loaded_addresses: sanitized_tx.get_loaded_addresses(), ..TransactionStatusMeta::default() - }) + } ); poh_recorder .lock() diff --git a/core/src/replay_stage.rs b/core/src/replay_stage.rs index 0608770a730af5..a5836a7724ffda 100644 --- a/core/src/replay_stage.rs +++ b/core/src/replay_stage.rs @@ -3984,36 +3984,35 @@ pub 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); - - for VersionedTransactionWithStatusMeta { 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 actual_tx_results: Vec<_> = confirmed_block + .transactions + .into_iter() + .map(|VersionedTransactionWithStatusMeta { transaction, meta }| { + (transaction.signatures[0], meta.status) + }) + .collect(); + 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 a64f572c698a28..77144cac938dc3 100644 --- a/ledger-tool/src/bigtable.rs +++ b/ledger-tool/src/bigtable.rs @@ -16,7 +16,7 @@ use { }, solana_ledger::{blockstore::Blockstore, blockstore_db::AccessType}, solana_sdk::{clock::Slot, pubkey::Pubkey, signature::Signature}, - solana_transaction_status::{ConfirmedBlock, Encodable, UiTransactionEncoding}, + solana_transaction_status::{Encodable, LegacyConfirmedBlock, UiTransactionEncoding}, std::{ collections::HashSet, path::Path, @@ -30,7 +30,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 +41,6 @@ async fn upload( bigtable, starting_slot, ending_slot, - allow_missing_metadata, force_reupload, Arc::new(AtomicBool::new(false)), ) @@ -73,13 +71,13 @@ async fn block(slot: Slot, output_format: OutputFormat) -> Result<(), Box { - let confirmed_transaction = versioned_confirmed_tx + Ok(Some(confirmed_tx)) => { + let legacy_confirmed_tx = confirmed_tx .into_legacy_confirmed_transaction() .ok_or_else(|| "Failed to read versioned transaction in block".to_string())?; transaction = Some(CliTransaction { - transaction: confirmed_transaction - .transaction + transaction: legacy_confirmed_tx + .tx_with_meta .transaction .encode(UiTransactionEncoding::Json), - meta: confirmed_transaction.transaction.meta.map(|m| m.into()), - block_time: confirmed_transaction.block_time, - slot: Some(confirmed_transaction.slot), - decoded_transaction: confirmed_transaction.transaction.transaction, + meta: legacy_confirmed_tx.tx_with_meta.meta.map(|m| m.into()), + block_time: legacy_confirmed_tx.block_time, + slot: Some(legacy_confirmed_tx.slot), + decoded_transaction: legacy_confirmed_tx.tx_with_meta.transaction, prefix: " ".to_string(), sigverify_status: vec![], }); @@ -201,7 +199,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, LegacyConfirmedBlock)> = None; while limit > 0 { let results = bigtable .get_confirmed_signatures_for_address( @@ -267,8 +265,8 @@ pub async fn transaction_history( println!(" Unable to get confirmed transaction details: {}", err); break; } - Ok(versioned_block) => { - let block = versioned_block.into_legacy_block().ok_or_else(|| { + Ok(confirmed_block) => { + let block = confirmed_block.into_legacy_block().ok_or_else(|| { "Failed to read versioned transaction in block".to_string() })?; loaded_block = Some((result.slot, block)); @@ -316,12 +314,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") @@ -516,7 +508,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), @@ -528,7 +519,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 f7ae973b8656a7..8e4f0350f03f70 100644 --- a/ledger/src/bigtable_upload.rs +++ b/ledger/src/bigtable_upload.rs @@ -26,7 +26,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> { @@ -187,20 +186,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 9dba8e483cd7da..bf9eb6085b7015 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 b36e8028496bf0..50ca4914ec999c 100644 --- a/ledger/src/blockstore.rs +++ b/ledger/src/blockstore.rs @@ -42,8 +42,8 @@ use { }, solana_storage_proto::{StoredExtendedRewards, StoredTransactionStatusMeta}, solana_transaction_status::{ - ConfirmedTransactionStatusWithSignature, Rewards, TransactionStatusMeta, - VersionedConfirmedBlock, VersionedConfirmedTransactionWithStatusMeta, + ConfirmedTransactionStatusWithSignature, ConfirmedTransactionWithStatusMeta, Rewards, + TransactionStatusMeta, TransactionWithStatusMeta, VersionedConfirmedBlock, VersionedTransactionWithStatusMeta, }, std::{ @@ -2043,9 +2043,8 @@ impl Blockstore { Ok(VersionedTransactionWithStatusMeta { transaction, meta: self - .read_transaction_status((signature, slot)) - .ok() - .flatten(), + .read_transaction_status((signature, slot))? + .ok_or(BlockstoreError::MissingTransactionMetadata)?, }) }) .collect() @@ -2294,7 +2293,7 @@ impl Blockstore { pub fn get_rooted_transaction( &self, signature: Signature, - ) -> Result> { + ) -> Result> { datapoint_info!( "blockstore-rpc-api", ("method", "get_rooted_transaction", String) @@ -2307,7 +2306,7 @@ impl Blockstore { &self, signature: Signature, highest_confirmed_slot: Slot, - ) -> Result> { + ) -> Result> { datapoint_info!( "blockstore-rpc-api", ("method", "get_complete_transaction", String) @@ -2324,8 +2323,8 @@ impl Blockstore { &self, signature: Signature, confirmed_unrooted_slots: &[Slot], - ) -> Result> { - if let Some((slot, status)) = + ) -> Result> { + if let Some((slot, meta)) = self.get_transaction_status(signature, confirmed_unrooted_slots)? { let transaction = self @@ -2333,12 +2332,11 @@ impl Blockstore { .ok_or(BlockstoreError::TransactionStatusSlotMismatch)?; // Should not happen let block_time = self.get_block_time(slot)?; - Ok(Some(VersionedConfirmedTransactionWithStatusMeta { + Ok(Some(ConfirmedTransactionWithStatusMeta { slot, - tx_with_meta: VersionedTransactionWithStatusMeta { - transaction, - meta: Some(status), - }, + tx_with_meta: TransactionWithStatusMeta::Complete( + VersionedTransactionWithStatusMeta { transaction, meta }, + ), block_time, })) } else { @@ -6327,7 +6325,7 @@ pub mod tests { .unwrap(); VersionedTransactionWithStatusMeta { transaction, - meta: Some(TransactionStatusMeta { + meta: TransactionStatusMeta { status: Ok(()), fee: 42, pre_balances, @@ -6338,21 +6336,22 @@ pub mod tests { post_token_balances: Some(vec![]), rewards: Some(vec![]), loaded_addresses: LoadedAddresses::default(), - }), + }, } }) .collect(); // Even if marked as root, a slot that is empty of entries should return an error - let confirmed_block_err = blockstore.get_rooted_block(slot - 1, true).unwrap_err(); - assert_matches!(confirmed_block_err, BlockstoreError::SlotUnavailable); + assert_matches!( + blockstore.get_rooted_block(slot - 1, true), + Err(BlockstoreError::SlotUnavailable) + ); // The previous_blockhash of `expected_block` is default because its parent slot is a root, // but empty of entries (eg. snapshot root slots). This now returns an error. - let confirmed_block_err = blockstore.get_rooted_block(slot, true).unwrap_err(); assert_matches!( - confirmed_block_err, - BlockstoreError::ParentEntriesUnavailable + blockstore.get_rooted_block(slot, true), + Err(BlockstoreError::ParentEntriesUnavailable) ); // Test if require_previous_blockhash is false @@ -7168,7 +7167,7 @@ pub mod tests { .unwrap(); VersionedTransactionWithStatusMeta { transaction, - meta: Some(TransactionStatusMeta { + meta: TransactionStatusMeta { status: Ok(()), fee: 42, pre_balances, @@ -7179,7 +7178,7 @@ pub mod tests { post_token_balances, rewards, loaded_addresses: LoadedAddresses::default(), - }), + }, } }) .collect(); @@ -7188,9 +7187,9 @@ pub mod tests { let signature = tx_with_meta.transaction.signatures[0]; assert_eq!( blockstore.get_rooted_transaction(signature).unwrap(), - Some(VersionedConfirmedTransactionWithStatusMeta { + Some(ConfirmedTransactionWithStatusMeta { slot, - tx_with_meta: tx_with_meta.clone(), + tx_with_meta: TransactionWithStatusMeta::Complete(tx_with_meta.clone()), block_time: None }) ); @@ -7198,9 +7197,9 @@ pub mod tests { blockstore .get_complete_transaction(signature, slot + 1) .unwrap(), - Some(VersionedConfirmedTransactionWithStatusMeta { + Some(ConfirmedTransactionWithStatusMeta { slot, - tx_with_meta, + tx_with_meta: TransactionWithStatusMeta::Complete(tx_with_meta), block_time: None }) ); @@ -7270,7 +7269,7 @@ pub mod tests { .unwrap(); VersionedTransactionWithStatusMeta { transaction, - meta: Some(TransactionStatusMeta { + meta: TransactionStatusMeta { status: Ok(()), fee: 42, pre_balances, @@ -7281,7 +7280,7 @@ pub mod tests { post_token_balances, rewards, loaded_addresses: LoadedAddresses::default(), - }), + }, } }) .collect(); @@ -7292,9 +7291,9 @@ pub mod tests { blockstore .get_complete_transaction(signature, slot) .unwrap(), - Some(VersionedConfirmedTransactionWithStatusMeta { + Some(ConfirmedTransactionWithStatusMeta { slot, - tx_with_meta, + tx_with_meta: TransactionWithStatusMeta::Complete(tx_with_meta), block_time: None }) ); @@ -8052,6 +8051,16 @@ pub mod tests { .unwrap(); transactions.push(transaction.into()); } + + 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( @@ -8064,14 +8073,9 @@ pub mod tests { .into(), ); - let map_result = blockstore.map_transactions_to_statuses(slot, transactions.into_iter()); - assert!(map_result.is_ok()); - let map = map_result.unwrap(); - 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.clone().into_iter()); + assert_matches!(map_result, Err(BlockstoreError::MissingTransactionMetadata)); } #[test] diff --git a/ledger/src/blockstore_db.rs b/ledger/src/blockstore_db.rs index a0c34b1f9ca8c8..353f54a4db8396 100644 --- a/ledger/src/blockstore_db.rs +++ b/ledger/src/blockstore_db.rs @@ -102,6 +102,7 @@ pub enum BlockstoreError { ParentEntriesUnavailable, SlotUnavailable, UnsupportedTransactionVersion, + MissingTransactionMetadata, } pub type Result = std::result::Result; diff --git a/local-cluster/tests/local_cluster_slow.rs b/local-cluster/tests/local_cluster_slow.rs index 663551b632bc5a..2f2ae101879ab4 100644 --- a/local-cluster/tests/local_cluster_slow.rs +++ b/local-cluster/tests/local_cluster_slow.rs @@ -878,46 +878,27 @@ fn find_latest_replayed_slot_from_ledger( latest_slot = new_latest_slot; info!("Checking latest_slot {}", latest_slot); // Wait for the slot to be fully received by the validator - let entries; loop { info!("Waiting for slot {} to be full", latest_slot); if blockstore.is_full(latest_slot) { - entries = blockstore.get_slot_entries(latest_slot, 0).unwrap(); - assert!(!entries.is_empty()); break; } else { sleep(Duration::from_millis(50)); blockstore = open_blockstore(ledger_path); } } - // Check the slot has been replayed - let non_tick_entry = entries.into_iter().find(|e| !e.transactions.is_empty()); - if let Some(non_tick_entry) = non_tick_entry { - // Wait for the slot to be replayed - loop { - info!("Waiting for slot {} to be replayed", latest_slot); - if !blockstore - .map_transactions_to_statuses( - latest_slot, - non_tick_entry.transactions.clone().into_iter(), - ) - .unwrap() - .is_empty() - { - return ( - latest_slot, - AncestorIterator::new(latest_slot, &blockstore).collect(), - ); - } else { - sleep(Duration::from_millis(50)); - blockstore = open_blockstore(ledger_path); - } + // Wait for the slot to be replayed + loop { + info!("Waiting for slot {} to be replayed", latest_slot); + if blockstore.get_bank_hash(latest_slot).is_some() { + return ( + latest_slot, + AncestorIterator::new(latest_slot, &blockstore).collect(), + ); + } else { + sleep(Duration::from_millis(50)); + blockstore = open_blockstore(ledger_path); } - } else { - info!( - "No transactions in slot {}, can't tell if it was replayed", - latest_slot - ); } } sleep(Duration::from_millis(50)); diff --git a/programs/bpf/tests/programs.rs b/programs/bpf/tests/programs.rs index 717b14f50bf305..e3a55407d6bb45 100644 --- a/programs/bpf/tests/programs.rs +++ b/programs/bpf/tests/programs.rs @@ -54,11 +54,11 @@ use solana_sdk::{ system_instruction::{self, MAX_PERMITTED_DATA_LENGTH}, system_program, sysvar, sysvar::{clock, rent}, - transaction::{SanitizedTransaction, Transaction, TransactionError}, + transaction::{SanitizedTransaction, Transaction, TransactionError, VersionedTransaction}, }; use solana_transaction_status::{ token_balances::collect_token_balances, ConfirmedTransactionWithStatusMeta, InnerInstructions, - TransactionStatusMeta, TransactionWithStatusMeta, + TransactionStatusMeta, TransactionWithStatusMeta, VersionedTransactionWithStatusMeta, }; use std::{collections::HashMap, env, fs::File, io::Read, path::PathBuf, str::FromStr, sync::Arc}; @@ -429,10 +429,12 @@ fn execute_transactions( Ok(ConfirmedTransactionWithStatusMeta { slot: bank.slot(), - transaction: TransactionWithStatusMeta { - transaction: tx.clone(), - meta: Some(tx_status_meta), - }, + tx_with_meta: TransactionWithStatusMeta::Complete( + VersionedTransactionWithStatusMeta { + transaction: VersionedTransaction::from(tx.clone()), + meta: tx_status_meta, + }, + ), block_time: None, }) } @@ -2476,10 +2478,10 @@ fn test_program_upgradeable_locks() { assert!(matches!( results1[0], Ok(ConfirmedTransactionWithStatusMeta { - transaction: TransactionWithStatusMeta { - meta: Some(TransactionStatusMeta { status: Ok(()), .. }), + tx_with_meta: TransactionWithStatusMeta::Complete(VersionedTransactionWithStatusMeta { + meta: TransactionStatusMeta { status: Ok(()), .. }, .. - }, + }), .. }) )); @@ -2488,16 +2490,16 @@ fn test_program_upgradeable_locks() { assert!(matches!( results2[0], Ok(ConfirmedTransactionWithStatusMeta { - transaction: TransactionWithStatusMeta { - meta: Some(TransactionStatusMeta { + tx_with_meta: TransactionWithStatusMeta::Complete(VersionedTransactionWithStatusMeta { + meta: TransactionStatusMeta { status: Err(TransactionError::InstructionError( 0, InstructionError::ProgramFailedToComplete )), .. - }), + }, .. - }, + }), .. }) )); diff --git a/rpc/src/rpc.rs b/rpc/src/rpc.rs index 9c334b79f82cc1..1e23f3a400f318 100644 --- a/rpc/src/rpc.rs +++ b/rpc/src/rpc.rs @@ -80,10 +80,10 @@ use { solana_storage_bigtable::Error as StorageError, solana_streamer::socket::SocketAddrSpace, solana_transaction_status::{ - ConfirmedTransactionStatusWithSignature, Encodable, - EncodedConfirmedTransactionWithStatusMeta, Reward, RewardType, - TransactionConfirmationStatus, TransactionStatus, UiConfirmedBlock, UiTransactionEncoding, - VersionedConfirmedBlock, + ConfirmedBlock, ConfirmedTransactionStatusWithSignature, + ConfirmedTransactionWithStatusMeta, Encodable, EncodedConfirmedTransactionWithStatusMeta, + Reward, RewardType, TransactionConfirmationStatus, TransactionStatus, UiConfirmedBlock, + UiTransactionEncoding, }, solana_vote_program::vote_state::{VoteState, MAX_LOCKOUT_HISTORY}, spl_token::{ @@ -918,12 +918,8 @@ impl JsonRpcRequestProcessor { &self, result: &std::result::Result, slot: Slot, - ) -> Result<()> - where - T: std::fmt::Debug, - { - if result.is_err() { - let err = result.as_ref().unwrap_err(); + ) -> Result<()> { + if let Err(err) = result { debug!( "check_blockstore_root, slot: {:?}, max root: {:?}, err: {:?}", slot, @@ -944,21 +940,16 @@ impl JsonRpcRequestProcessor { &self, result: &std::result::Result, slot: Slot, - ) -> Result<()> - where - T: std::fmt::Debug, - { - if result.is_err() { - if let BlockstoreError::SlotCleanedUp = result.as_ref().unwrap_err() { - return Err(RpcCustomError::BlockCleanedUp { - slot, - first_available_block: self - .blockstore - .get_first_available_block() - .unwrap_or_default(), - } - .into()); + ) -> Result<()> { + if let Err(BlockstoreError::SlotCleanedUp) = result { + return Err(RpcCustomError::BlockCleanedUp { + slot, + first_available_block: self + .blockstore + .get_first_available_block() + .unwrap_or_default(), } + .into()); } Ok(()) } @@ -966,15 +957,9 @@ impl JsonRpcRequestProcessor { fn check_bigtable_result( &self, result: &std::result::Result, - ) -> Result<()> - where - T: std::fmt::Debug, - { - if result.is_err() { - let err = result.as_ref().unwrap_err(); - if let solana_storage_bigtable::Error::BlockNotFound(slot) = err { - return Err(RpcCustomError::LongTermStorageSlotSkipped { slot: *slot }.into()); - } + ) -> Result<()> { + if let Err(solana_storage_bigtable::Error::BlockNotFound(slot)) = result { + return Err(RpcCustomError::LongTermStorageSlotSkipped { slot: *slot }.into()); } Ok(()) } @@ -1017,12 +1002,12 @@ impl JsonRpcRequestProcessor { self.check_status_is_complete(slot)?; let result = self.blockstore.get_rooted_block(slot, true); self.check_blockstore_root(&result, slot)?; - let configure_block = |versioned_block: VersionedConfirmedBlock| { - let confirmed_block = versioned_block + let configure_block = |confirmed_block: ConfirmedBlock| { + let legacy_block = confirmed_block .into_legacy_block() .ok_or(RpcCustomError::UnsupportedTransactionVersion)?; let mut confirmed_block = - confirmed_block.configure(encoding, transaction_details, show_rewards); + legacy_block.configure(encoding, transaction_details, show_rewards); if slot == 0 { confirmed_block.block_time = Some(self.genesis_creation_time()); confirmed_block.block_height = Some(0); @@ -1038,7 +1023,11 @@ impl JsonRpcRequestProcessor { } } self.check_slot_cleaned_up(&result, slot)?; - return result.ok().map(configure_block).transpose(); + return result + .ok() + .map(ConfirmedBlock::from) + .map(configure_block) + .transpose(); } else if commitment.is_confirmed() { // Check if block is confirmed let confirmed_bank = self.bank(Some(CommitmentConfig::confirmed())); @@ -1047,30 +1036,28 @@ impl JsonRpcRequestProcessor { let result = self.blockstore.get_complete_block(slot, true); return result .ok() - .map(|versioned_block| { - let mut confirmed_block = versioned_block + .map(ConfirmedBlock::from) + .map(|confirmed_block| -> Result { + let mut legacy_block = confirmed_block .into_legacy_block() .ok_or(RpcCustomError::UnsupportedTransactionVersion)?; - if confirmed_block.block_time.is_none() - || confirmed_block.block_height.is_none() + + if legacy_block.block_time.is_none() + || legacy_block.block_height.is_none() { let r_bank_forks = self.bank_forks.read().unwrap(); let bank = r_bank_forks.get(slot).cloned(); if let Some(bank) = bank { - if confirmed_block.block_time.is_none() { - confirmed_block.block_time = - Some(bank.clock().unix_timestamp); + if legacy_block.block_time.is_none() { + legacy_block.block_time = Some(bank.clock().unix_timestamp); } - if confirmed_block.block_height.is_none() { - confirmed_block.block_height = Some(bank.block_height()); + if legacy_block.block_height.is_none() { + legacy_block.block_height = Some(bank.block_height()); } } } - Ok(confirmed_block.configure( - encoding, - transaction_details, - show_rewards, - )) + + Ok(legacy_block.configure(encoding, transaction_details, show_rewards)) }) .transpose(); } @@ -1397,7 +1384,7 @@ impl JsonRpcRequestProcessor { if self.config.enable_rpc_transaction_history { let confirmed_bank = self.bank(Some(CommitmentConfig::confirmed())); - let versioned_confirmed_tx = if commitment.is_confirmed() { + let confirmed_transaction = if commitment.is_confirmed() { let highest_confirmed_slot = confirmed_bank.slot(); self.blockstore .get_complete_transaction(signature, highest_confirmed_slot) @@ -1405,12 +1392,16 @@ impl JsonRpcRequestProcessor { self.blockstore.get_rooted_transaction(signature) }; - match versioned_confirmed_tx.unwrap_or(None) { - Some(versioned_confirmed_tx) => { - let mut confirmed_transaction = versioned_confirmed_tx - .into_legacy_confirmed_transaction() + let encode_transaction = + |confirmed_tx_with_meta: ConfirmedTransactionWithStatusMeta| -> Result { + let legacy_tx_with_meta = confirmed_tx_with_meta.into_legacy_confirmed_transaction() .ok_or(RpcCustomError::UnsupportedTransactionVersion)?; + Ok(legacy_tx_with_meta.encode(encoding)) + }; + + match confirmed_transaction.unwrap_or(None) { + Some(mut confirmed_transaction) => { if commitment.is_confirmed() && confirmed_bank // should be redundant .status_cache_ancestors() @@ -1422,8 +1413,9 @@ impl JsonRpcRequestProcessor { .get(confirmed_transaction.slot) .map(|bank| bank.clock().unix_timestamp); } - return Ok(Some(confirmed_transaction.encode(encoding))); + return Ok(Some(encode_transaction(confirmed_transaction)?)); } + if confirmed_transaction.slot <= self .block_commitment_cache @@ -1431,7 +1423,7 @@ impl JsonRpcRequestProcessor { .unwrap() .highest_confirmed_root() { - return Ok(Some(confirmed_transaction.encode(encoding))); + return Ok(Some(encode_transaction(confirmed_transaction)?)); } } None => { @@ -1440,13 +1432,7 @@ impl JsonRpcRequestProcessor { .get_confirmed_transaction(&signature) .await .unwrap_or(None) - .map(|versioned_confirmed_tx| { - let confirmed_tx = versioned_confirmed_tx - .into_legacy_confirmed_transaction() - .ok_or(RpcCustomError::UnsupportedTransactionVersion)?; - - Ok(confirmed_tx.encode(encoding)) - }) + .map(encode_transaction) .transpose(); } } @@ -4328,15 +4314,7 @@ pub fn create_test_transactions_and_populate_blockstore( ); let ix_error_signature = ix_error_tx.signatures[0]; let entry_2 = solana_entry::entry::next_entry(&entry_1.hash, 1, vec![ix_error_tx]); - // Failed transaction - let fail_tx = solana_sdk::system_transaction::transfer( - mint_keypair, - &keypair2.pubkey(), - rent_exempt_amount, - Hash::default(), - ); - let entry_3 = solana_entry::entry::next_entry(&entry_2.hash, 1, vec![fail_tx]); - let entries = vec![entry_1, entry_2, entry_3]; + let entries = vec![entry_1, entry_2]; let shreds = solana_ledger::blockstore::entries_to_test_shreds(&entries, slot, previous_slot, true, 0); @@ -4357,17 +4335,20 @@ pub fn create_test_transactions_and_populate_blockstore( // Check that process_entries successfully writes can_commit transactions statuses, and // that they are matched properly by get_rooted_block - let _result = solana_ledger::blockstore_processor::process_entries_for_tests( - &bank, - entries, - true, - Some( - &solana_ledger::blockstore_processor::TransactionStatusSender { - sender: transaction_status_sender, - enable_cpi_and_log_storage: false, - }, + assert_eq!( + solana_ledger::blockstore_processor::process_entries_for_tests( + &bank, + entries, + true, + Some( + &solana_ledger::blockstore_processor::TransactionStatusSender { + sender: transaction_status_sender, + enable_cpi_and_log_storage: false, + }, + ), + Some(&replay_vote_sender), ), - Some(&replay_vote_sender), + Ok(()) ); transaction_status_service.join().unwrap(); @@ -6613,7 +6594,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 @@ -6658,7 +6639,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/rpc/src/rpc_subscriptions.rs b/rpc/src/rpc_subscriptions.rs index f81b3392fc09aa..374b5f123cae20 100644 --- a/rpc/src/rpc_subscriptions.rs +++ b/rpc/src/rpc_subscriptions.rs @@ -40,7 +40,7 @@ use { timing::timestamp, transaction, }, - solana_transaction_status::ConfirmedBlock, + solana_transaction_status::{ConfirmedBlock, LegacyConfirmedBlock}, std::{ cell::RefCell, collections::{HashMap, VecDeque}, @@ -278,30 +278,26 @@ impl RpcNotifier { } fn filter_block_result_txs( - block: ConfirmedBlock, + mut block: LegacyConfirmedBlock, last_modified_slot: Slot, params: &BlockSubscriptionParams, ) -> Option { - let transactions = match params.kind { + block.transactions = match params.kind { BlockSubscriptionKind::All => block.transactions, BlockSubscriptionKind::MentionsAccountOrProgram(pk) => block .transactions .into_iter() - .filter(|tx| tx.transaction.message.account_keys.contains(&pk)) + .filter(|tx_with_meta| tx_with_meta.transaction.message.account_keys.contains(&pk)) .collect(), }; - if transactions.is_empty() { + if block.transactions.is_empty() { if let BlockSubscriptionKind::MentionsAccountOrProgram(_) = params.kind { return None; } } - let block = ConfirmedBlock { - transactions, - ..block - } - .configure( + let block = block.configure( params.encoding, params.transaction_details, params.show_rewards, @@ -962,26 +958,29 @@ impl RpcSubscriptions { break; } - let block_result = blockstore + let block_update_result = blockstore .get_complete_block(s, false) .map_err(|e| { error!("get_complete_block error: {}", e); RpcBlockUpdateError::BlockStoreError }) .and_then(|versioned_block| { - versioned_block.into_legacy_block().ok_or( - RpcBlockUpdateError::UnsupportedTransactionVersion, - ) + ConfirmedBlock::from(versioned_block) + .into_legacy_block() + .ok_or( + RpcBlockUpdateError::UnsupportedTransactionVersion, + ) }); - match block_result { - Ok(block) => { - if let Some(res) = filter_block_result_txs(block, s, params) + match block_update_result { + Ok(block_update) => { + if let Some(block_update) = + filter_block_result_txs(block_update, s, params) { notifier.notify( Response { context: RpcResponseContext { slot: s }, - value: res, + value: block_update, }, subscription, false, @@ -1357,8 +1356,13 @@ pub(crate) mod tests { #[serial] fn test_check_confirmed_block_subscribe() { let exit = Arc::new(AtomicBool::new(false)); - let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(10_000); + let GenesisConfigInfo { + genesis_config, + mint_keypair, + .. + } = create_genesis_config(10_000); let bank = Bank::new_for_tests(&genesis_config); + let rent_exempt_amount = bank.get_minimum_balance_for_rent_exemption(0); let bank_forks = Arc::new(RwLock::new(BankForks::new(bank))); let optimistically_confirmed_bank = OptimisticallyConfirmedBank::locked_from_bank_forks_root(&bank_forks); @@ -1399,10 +1403,11 @@ pub(crate) mod tests { let keypair1 = Keypair::new(); let keypair2 = Keypair::new(); let keypair3 = Keypair::new(); - let keypair4 = Keypair::new(); let max_complete_transaction_status_slot = Arc::new(AtomicU64::new(blockstore.max_root())); + bank.transfer(rent_exempt_amount, &mint_keypair, &keypair2.pubkey()) + .unwrap(); let _confirmed_block_signatures = create_test_transactions_and_populate_blockstore( - vec![&keypair1, &keypair2, &keypair3, &keypair4], + vec![&mint_keypair, &keypair1, &keypair2, &keypair3], 0, bank, blockstore.clone(), @@ -1414,8 +1419,9 @@ pub(crate) mod tests { let actual_resp = receiver.recv(); let actual_resp = serde_json::from_str::(&actual_resp).unwrap(); - let versioned_block = blockstore.get_complete_block(slot, false).unwrap(); - let legacy_block = versioned_block.into_legacy_block().unwrap(); + let confirmed_block = + ConfirmedBlock::from(blockstore.get_complete_block(slot, false).unwrap()); + let legacy_block = confirmed_block.into_legacy_block().unwrap(); let block = legacy_block.configure(params.encoding, params.transaction_details, false); let expected_resp = RpcBlockUpdate { slot, @@ -1455,8 +1461,13 @@ pub(crate) mod tests { #[serial] fn test_check_confirmed_block_subscribe_with_mentions() { let exit = Arc::new(AtomicBool::new(false)); - let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(10_000); + let GenesisConfigInfo { + genesis_config, + mint_keypair, + .. + } = create_genesis_config(10_000); let bank = Bank::new_for_tests(&genesis_config); + let rent_exempt_amount = bank.get_minimum_balance_for_rent_exemption(0); let bank_forks = Arc::new(RwLock::new(BankForks::new(bank))); let optimistically_confirmed_bank = OptimisticallyConfirmedBank::locked_from_bank_forks_root(&bank_forks); @@ -1498,10 +1509,11 @@ pub(crate) mod tests { let bank = bank_forks.read().unwrap().working_bank(); let keypair2 = Keypair::new(); let keypair3 = Keypair::new(); - let keypair4 = Keypair::new(); let max_complete_transaction_status_slot = Arc::new(AtomicU64::new(blockstore.max_root())); + bank.transfer(rent_exempt_amount, &mint_keypair, &keypair2.pubkey()) + .unwrap(); let _confirmed_block_signatures = create_test_transactions_and_populate_blockstore( - vec![&keypair1, &keypair2, &keypair3, &keypair4], + vec![&mint_keypair, &keypair1, &keypair2, &keypair3], 0, bank, blockstore.clone(), @@ -1514,8 +1526,9 @@ pub(crate) mod tests { let actual_resp = serde_json::from_str::(&actual_resp).unwrap(); // make sure it filtered out the other keypairs - let versioned_block = blockstore.get_complete_block(slot, false).unwrap(); - let mut legacy_block = versioned_block.into_legacy_block().unwrap(); + let confirmed_block = + ConfirmedBlock::from(blockstore.get_complete_block(slot, false).unwrap()); + let mut legacy_block = confirmed_block.into_legacy_block().unwrap(); legacy_block.transactions.retain(|tx_with_meta| { tx_with_meta .transaction @@ -1552,8 +1565,13 @@ pub(crate) mod tests { #[serial] fn test_check_finalized_block_subscribe() { let exit = Arc::new(AtomicBool::new(false)); - let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(10_000); + let GenesisConfigInfo { + genesis_config, + mint_keypair, + .. + } = create_genesis_config(10_000); let bank = Bank::new_for_tests(&genesis_config); + let rent_exempt_amount = bank.get_minimum_balance_for_rent_exemption(0); let bank_forks = Arc::new(RwLock::new(BankForks::new(bank))); let optimistically_confirmed_bank = OptimisticallyConfirmedBank::locked_from_bank_forks_root(&bank_forks); @@ -1593,10 +1611,11 @@ pub(crate) mod tests { let keypair1 = Keypair::new(); let keypair2 = Keypair::new(); let keypair3 = Keypair::new(); - let keypair4 = Keypair::new(); let max_complete_transaction_status_slot = Arc::new(AtomicU64::new(blockstore.max_root())); + bank.transfer(rent_exempt_amount, &mint_keypair, &keypair2.pubkey()) + .unwrap(); let _confirmed_block_signatures = create_test_transactions_and_populate_blockstore( - vec![&keypair1, &keypair2, &keypair3, &keypair4], + vec![&mint_keypair, &keypair1, &keypair2, &keypair3], 0, bank, blockstore.clone(), @@ -1613,8 +1632,9 @@ pub(crate) mod tests { let actual_resp = receiver.recv(); let actual_resp = serde_json::from_str::(&actual_resp).unwrap(); - let versioned_block = blockstore.get_complete_block(slot, false).unwrap(); - let legacy_block = versioned_block.into_legacy_block().unwrap(); + let confirmed_block = + ConfirmedBlock::from(blockstore.get_complete_block(slot, false).unwrap()); + let legacy_block = confirmed_block.into_legacy_block().unwrap(); let block = legacy_block.configure(params.encoding, params.transaction_details, false); let expected_resp = RpcBlockUpdate { slot, diff --git a/storage-bigtable/src/bigtable.rs b/storage-bigtable/src/bigtable.rs index e38f27c062bf86..e95825736e0c0a 100644 --- a/storage-bigtable/src/bigtable.rs +++ b/storage-bigtable/src/bigtable.rs @@ -792,23 +792,46 @@ mod tests { prost::Message, solana_sdk::{ hash::Hash, message::v0::LoadedAddresses, signature::Keypair, system_transaction, + transaction::VersionedTransaction, }, solana_storage_proto::convert::generated, solana_transaction_status::{ ConfirmedBlock, TransactionStatusMeta, TransactionWithStatusMeta, - VersionedConfirmedBlock, + VersionedTransactionWithStatusMeta, }, std::convert::TryInto, }; + fn confirmed_block_into_protobuf(confirmed_block: ConfirmedBlock) -> generated::ConfirmedBlock { + let ConfirmedBlock { + 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 { - transaction, - meta: Some(TransactionStatusMeta { + let with_meta = TransactionWithStatusMeta::Complete(VersionedTransactionWithStatusMeta { + transaction: VersionedTransaction::from(transaction), + meta: TransactionStatusMeta { status: Ok(()), fee: 1, pre_balances: vec![43, 0, 1], @@ -819,9 +842,9 @@ mod tests { post_token_balances: Some(vec![]), rewards: Some(vec![]), loaded_addresses: LoadedAddresses::default(), - }), - }; - let block = ConfirmedBlock { + }, + }); + let expected_block = ConfirmedBlock { transactions: vec![with_meta], parent_slot: 1, blockhash: Hash::default().to_string(), @@ -831,11 +854,11 @@ mod tests { block_height: Some(1), }; let bincode_block = compress_best( - &bincode::serialize::(&block.clone().into()).unwrap(), + &bincode::serialize::(&expected_block.clone().into()).unwrap(), ) .unwrap(); - let protobuf_block = generated::ConfirmedBlock::from(block.clone()); + let protobuf_block = confirmed_block_into_protobuf(expected_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(); @@ -849,7 +872,6 @@ mod tests { "".to_string(), ) .unwrap(); - let expected_block: VersionedConfirmedBlock = block.into(); if let CellData::Protobuf(protobuf_block) = deserialized { assert_eq!(expected_block, protobuf_block.try_into().unwrap()); } else { @@ -867,7 +889,11 @@ mod tests { .unwrap(); if let CellData::Bincode(bincode_block) = deserialized { let mut block = expected_block; - if let Some(meta) = &mut block.transactions[0].meta { + if let TransactionWithStatusMeta::Complete(VersionedTransactionWithStatusMeta { + meta, + .. + }) = &mut block.transactions[0] + { meta.inner_instructions = None; // Legacy bincode implementation does not support inner_instructions meta.log_messages = None; // Legacy bincode implementation does not support log_messages meta.pre_token_balances = None; // Legacy bincode implementation does not support token balances diff --git a/storage-bigtable/src/lib.rs b/storage-bigtable/src/lib.rs index c882cfd9a71e8c..08b0f997d4a721 100644 --- a/storage-bigtable/src/lib.rs +++ b/storage-bigtable/src/lib.rs @@ -14,10 +14,10 @@ use { }, solana_storage_proto::convert::{generated, tx_by_addr}, solana_transaction_status::{ - extract_and_fmt_memos, ConfirmedBlock, ConfirmedTransactionStatusWithSignature, Reward, - TransactionByAddrInfo, TransactionConfirmationStatus, TransactionStatus, - TransactionStatusMeta, TransactionWithStatusMeta, VersionedConfirmedBlock, - VersionedConfirmedTransactionWithStatusMeta, VersionedTransactionWithStatusMeta, + extract_and_fmt_memos, ConfirmedBlock, ConfirmedTransactionStatusWithSignature, + ConfirmedTransactionWithStatusMeta, Reward, TransactionByAddrInfo, + TransactionConfirmationStatus, TransactionStatus, TransactionStatusMeta, + TransactionWithStatusMeta, VersionedConfirmedBlock, VersionedTransactionWithStatusMeta, }, std::{ collections::{HashMap, HashSet}, @@ -115,6 +115,7 @@ struct StoredConfirmedBlock { block_height: Option, } +#[cfg(test)] impl From for StoredConfirmedBlock { fn from(confirmed_block: ConfirmedBlock) -> Self { let ConfirmedBlock { @@ -139,7 +140,7 @@ impl From for StoredConfirmedBlock { } } -impl From for VersionedConfirmedBlock { +impl From for ConfirmedBlock { fn from(confirmed_block: StoredConfirmedBlock) -> Self { let StoredConfirmedBlock { previous_blockhash, @@ -169,20 +170,38 @@ struct StoredConfirmedBlockTransaction { meta: Option, } +#[cfg(test)] impl From for StoredConfirmedBlockTransaction { fn from(value: TransactionWithStatusMeta) -> Self { - Self { - transaction: value.transaction.into(), - meta: value.meta.map(|meta| meta.into()), + match value { + TransactionWithStatusMeta::MissingMetadata(transaction) => Self { + transaction: VersionedTransaction::from(transaction), + meta: None, + }, + TransactionWithStatusMeta::Complete(VersionedTransactionWithStatusMeta { + transaction, + meta, + }) => Self { + transaction, + meta: Some(meta.into()), + }, } } } -impl From for VersionedTransactionWithStatusMeta { - fn from(value: StoredConfirmedBlockTransaction) -> Self { - Self { - transaction: value.transaction, - meta: value.meta.map(|meta| meta.into()), +impl From for TransactionWithStatusMeta { + fn from(tx_with_meta: StoredConfirmedBlockTransaction) -> Self { + let StoredConfirmedBlockTransaction { transaction, meta } = tx_with_meta; + match meta { + None => Self::MissingMetadata( + transaction + .into_legacy_transaction() + .expect("versioned transactions always have meta"), + ), + Some(meta) => Self::Complete(VersionedTransactionWithStatusMeta { + transaction, + meta: meta.into(), + }), } } } @@ -394,7 +413,7 @@ 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 @@ -440,7 +459,7 @@ impl LedgerStorage { pub async fn get_confirmed_transaction( &self, signature: &Signature, - ) -> Result> { + ) -> Result> { debug!( "LedgerStorage::get_confirmed_transaction request received: {:?}", signature @@ -465,17 +484,17 @@ impl LedgerStorage { warn!("Transaction info for {} is corrupt", signature); Ok(None) } - Some(bucket_block_transaction) => { - if bucket_block_transaction.transaction.signatures[0] != *signature { + Some(tx_with_meta) => { + if tx_with_meta.transaction_signature() != signature { warn!( "Transaction info or confirmed block for {} is corrupt", signature ); Ok(None) } else { - Ok(Some(VersionedConfirmedTransactionWithStatusMeta { + Ok(Some(ConfirmedTransactionWithStatusMeta { slot, - tx_with_meta: bucket_block_transaction, + tx_with_meta, block_time: block.block_time, })) } @@ -638,7 +657,7 @@ impl LedgerStorage { let mut tx_cells = vec![]; for (index, transaction_with_meta) in confirmed_block.transactions.iter().enumerate() { let VersionedTransactionWithStatusMeta { meta, transaction } = transaction_with_meta; - let err = meta.as_ref().and_then(|meta| meta.status.clone().err()); + let err = meta.status.clone().err(); let index = index as u32; let signature = transaction.signatures[0]; let memo = extract_and_fmt_memos(transaction_with_meta); @@ -725,21 +744,41 @@ 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 VersionedTransactionWithStatusMeta { transaction, meta } = 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()); + match transaction_with_meta { + TransactionWithStatusMeta::MissingMetadata(transaction) => { + let signature = transaction.signatures[0]; + let index = index as u32; + let err = None; + + for address in transaction.message.account_keys.iter() { + if !is_sysvar_id(address) { + addresses.insert(address); + } + } - for address in transaction_with_meta.account_keys().iter() { - if !is_sysvar_id(address) { - addresses.insert(address); + expected_tx_infos.insert( + signature.to_string(), + UploadedTransaction { slot, index, err }, + ); } - } + TransactionWithStatusMeta::Complete(tx_with_meta) => { + let VersionedTransactionWithStatusMeta { transaction, meta } = tx_with_meta; + let signature = transaction.signatures[0]; + let index = index as u32; + let err = meta.status.clone().err(); + + for address in tx_with_meta.account_keys().iter() { + if !is_sysvar_id(address) { + addresses.insert(address); + } + } - expected_tx_infos.insert( - signature.to_string(), - UploadedTransaction { slot, index, err }, - ); + expected_tx_infos.insert( + signature.to_string(), + UploadedTransaction { slot, index, err }, + ); + } + } } let address_slot_rows: Vec<_> = addresses diff --git a/storage-proto/src/convert.rs b/storage-proto/src/convert.rs index f3ad0395d46287..98c8e57bbe87de 100644 --- a/storage-proto/src/convert.rs +++ b/storage-proto/src/convert.rs @@ -140,31 +140,7 @@ impl From for generated::ConfirmedBlock { } } -impl From for generated::ConfirmedBlock { - fn from(confirmed_block: ConfirmedBlock) -> Self { - let ConfirmedBlock { - previous_blockhash, - blockhash, - parent_slot, - transactions, - rewards, - block_time, - block_height, - } = confirmed_block; - - Self { - 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 }), - } - } -} - -impl TryFrom for VersionedConfirmedBlock { +impl TryFrom for ConfirmedBlock { type Error = bincode::Error; fn try_from( confirmed_block: generated::ConfirmedBlock, @@ -195,32 +171,38 @@ impl TryFrom for VersionedConfirmedBlock { } impl From for generated::ConfirmedTransaction { - fn from(value: TransactionWithStatusMeta) -> Self { - let meta = value.meta.map(|meta| meta.into()); - Self { - transaction: Some(value.transaction.into()), - meta, + fn from(tx_with_meta: TransactionWithStatusMeta) -> Self { + match tx_with_meta { + TransactionWithStatusMeta::MissingMetadata(transaction) => Self { + transaction: Some(generated::Transaction::from(transaction)), + meta: None, + }, + TransactionWithStatusMeta::Complete(tx_with_meta) => Self::from(tx_with_meta), } } } impl From for generated::ConfirmedTransaction { fn from(value: VersionedTransactionWithStatusMeta) -> Self { - let meta = value.meta.map(|meta| meta.into()); Self { transaction: Some(value.transaction.into()), - meta, + meta: Some(value.meta.into()), } } } -impl TryFrom for VersionedTransactionWithStatusMeta { +impl TryFrom for TransactionWithStatusMeta { type Error = bincode::Error; fn try_from(value: generated::ConfirmedTransaction) -> std::result::Result { let meta = value.meta.map(|meta| meta.try_into()).transpose()?; - Ok(Self { - transaction: value.transaction.expect("transaction is required").into(), - meta, + let transaction = value.transaction.expect("transaction is required").into(); + Ok(match meta { + Some(meta) => Self::Complete(VersionedTransactionWithStatusMeta { transaction, meta }), + None => Self::MissingMetadata( + transaction + .into_legacy_transaction() + .expect("meta is required for versioned transactions"), + ), }) } } diff --git a/transaction-status/src/lib.rs b/transaction-status/src/lib.rs index 386f1e932ad582..52a0e6e3f3dcc1 100644 --- a/transaction-status/src/lib.rs +++ b/transaction-status/src/lib.rs @@ -388,7 +388,7 @@ pub struct Reward { pub type Rewards = Vec; -#[derive(Clone, Debug, Default, PartialEq)] +#[derive(Clone, Debug, PartialEq)] pub struct ConfirmedBlock { pub previous_blockhash: String, pub blockhash: String, @@ -399,7 +399,9 @@ pub struct ConfirmedBlock { pub block_height: Option, } -#[derive(Clone, Debug, Default, PartialEq)] +// Confirmed block with type guarantees that transaction metadata +// is always present. Used for uploading to BigTable. +#[derive(Clone, Debug, PartialEq)] pub struct VersionedConfirmedBlock { pub previous_blockhash: String, pub blockhash: String, @@ -410,11 +412,23 @@ pub struct VersionedConfirmedBlock { pub block_height: Option, } -impl VersionedConfirmedBlock { +// Confirmed block which only supports legacy transactions. Used +// until migration to versioned transactions is completed. +pub struct LegacyConfirmedBlock { + 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 ConfirmedBlock { /// Downgrades a versioned block into a legacy block type /// if it only contains legacy transactions - pub fn into_legacy_block(self) -> Option { - Some(ConfirmedBlock { + pub fn into_legacy_block(self) -> Option { + Some(LegacyConfirmedBlock { previous_blockhash: self.previous_blockhash, blockhash: self.blockhash, parent_slot: self.parent_slot, @@ -430,13 +444,17 @@ impl VersionedConfirmedBlock { } } -impl From for VersionedConfirmedBlock { - fn from(block: ConfirmedBlock) -> Self { - VersionedConfirmedBlock { +impl From for ConfirmedBlock { + fn from(block: VersionedConfirmedBlock) -> Self { + Self { previous_blockhash: block.previous_blockhash, blockhash: block.blockhash, parent_slot: block.parent_slot, - transactions: block.transactions.into_iter().map(|tx| tx.into()).collect(), + transactions: block + .transactions + .into_iter() + .map(TransactionWithStatusMeta::Complete) + .collect(), rewards: block.rewards, block_time: block.block_time, block_height: block.block_height, @@ -444,7 +462,7 @@ impl From for VersionedConfirmedBlock { } } -impl Encodable for ConfirmedBlock { +impl Encodable for LegacyConfirmedBlock { type Encoded = EncodedConfirmedBlock; fn encode(self, encoding: UiTransactionEncoding) -> Self::Encoded { Self::Encoded { @@ -463,7 +481,7 @@ impl Encodable for ConfirmedBlock { } } -impl ConfirmedBlock { +impl LegacyConfirmedBlock { pub fn configure( self, encoding: UiTransactionEncoding, @@ -565,44 +583,68 @@ impl From for UiConfirmedBlock { } } +#[derive(Clone, Debug, PartialEq)] +#[allow(clippy::large_enum_variant)] +pub enum TransactionWithStatusMeta { + // Very old transactions may be missing metadata + MissingMetadata(Transaction), + // Versioned stored transaction always have metadata + Complete(VersionedTransactionWithStatusMeta), +} + #[derive(Clone, Debug, PartialEq)] pub struct VersionedTransactionWithStatusMeta { pub transaction: VersionedTransaction, + pub meta: TransactionStatusMeta, +} + +pub struct LegacyTransactionWithStatusMeta { + pub transaction: Transaction, pub meta: Option, } +impl TransactionWithStatusMeta { + pub fn transaction_signature(&self) -> &Signature { + match self { + Self::MissingMetadata(transaction) => &transaction.signatures[0], + Self::Complete(VersionedTransactionWithStatusMeta { transaction, .. }) => { + &transaction.signatures[0] + } + } + } + + pub fn into_legacy_transaction_with_meta(self) -> Option { + match self { + TransactionWithStatusMeta::MissingMetadata(transaction) => { + Some(LegacyTransactionWithStatusMeta { + transaction, + meta: None, + }) + } + TransactionWithStatusMeta::Complete(tx_with_meta) => { + tx_with_meta.into_legacy_transaction_with_meta() + } + } + } +} + impl VersionedTransactionWithStatusMeta { pub fn account_keys(&self) -> AccountKeys { AccountKeys::new( self.transaction.message.static_account_keys(), - self.meta.as_ref().map(|meta| &meta.loaded_addresses), + Some(&self.meta.loaded_addresses), ) } - pub fn into_legacy_transaction_with_meta(self) -> Option { - Some(TransactionWithStatusMeta { + pub fn into_legacy_transaction_with_meta(self) -> Option { + Some(LegacyTransactionWithStatusMeta { transaction: self.transaction.into_legacy_transaction()?, - meta: self.meta, + meta: Some(self.meta), }) } } -impl From for VersionedTransactionWithStatusMeta { - fn from(tx_with_meta: TransactionWithStatusMeta) -> Self { - Self { - transaction: tx_with_meta.transaction.into(), - meta: tx_with_meta.meta, - } - } -} - -#[derive(Clone, Debug, PartialEq)] -pub struct TransactionWithStatusMeta { - pub transaction: Transaction, - pub meta: Option, -} - -impl Encodable for TransactionWithStatusMeta { +impl Encodable for LegacyTransactionWithStatusMeta { type Encoded = EncodedTransactionWithStatusMeta; fn encode(self, encoding: UiTransactionEncoding) -> Self::Encoded { Self::Encoded { @@ -627,37 +669,42 @@ pub struct EncodedTransactionWithStatusMeta { #[derive(Debug, Clone, PartialEq)] pub struct ConfirmedTransactionWithStatusMeta { pub slot: Slot, - pub transaction: TransactionWithStatusMeta, + pub tx_with_meta: TransactionWithStatusMeta, + pub block_time: Option, +} + +#[derive(Debug, Clone, PartialEq)] +pub struct VersionedConfirmedTransactionWithStatusMeta { + pub slot: Slot, + pub tx_with_meta: VersionedTransactionWithStatusMeta, + pub block_time: Option, +} + +pub struct LegacyConfirmedTransactionWithStatusMeta { + pub slot: Slot, + pub tx_with_meta: LegacyTransactionWithStatusMeta, pub block_time: Option, } -impl Encodable for ConfirmedTransactionWithStatusMeta { +impl Encodable for LegacyConfirmedTransactionWithStatusMeta { type Encoded = EncodedConfirmedTransactionWithStatusMeta; fn encode(self, encoding: UiTransactionEncoding) -> Self::Encoded { Self::Encoded { slot: self.slot, - transaction: self.transaction.encode(encoding), + transaction: self.tx_with_meta.encode(encoding), block_time: self.block_time, } } } -#[derive(Debug, Clone, PartialEq)] -pub struct VersionedConfirmedTransactionWithStatusMeta { - pub slot: Slot, - pub tx_with_meta: VersionedTransactionWithStatusMeta, - pub block_time: Option, -} - -impl VersionedConfirmedTransactionWithStatusMeta { +impl ConfirmedTransactionWithStatusMeta { /// Downgrades a versioned confirmed transaction into a legacy /// confirmed transaction if it contains a legacy transaction. - pub fn into_legacy_confirmed_transaction(self) -> Option { - Some(ConfirmedTransactionWithStatusMeta { - transaction: TransactionWithStatusMeta { - transaction: self.tx_with_meta.transaction.into_legacy_transaction()?, - meta: self.tx_with_meta.meta, - }, + pub fn into_legacy_confirmed_transaction( + self, + ) -> Option { + Some(LegacyConfirmedTransactionWithStatusMeta { + tx_with_meta: self.tx_with_meta.into_legacy_transaction_with_meta()?, block_time: self.block_time, slot: self.slot, })