Enforce tx metadata upload with static types (#23028)

This commit is contained in:
Justin Starry
2022-02-10 13:28:18 +08:00
committed by GitHub
parent 2ca7ae8e30
commit d5dec989b9
16 changed files with 482 additions and 440 deletions

View File

@@ -10,7 +10,7 @@ use {
RpcAccountInfoConfig, RpcBlockSubscribeConfig, RpcBlockSubscribeFilter, RpcAccountInfoConfig, RpcBlockSubscribeConfig, RpcBlockSubscribeFilter,
RpcProgramAccountsConfig, RpcProgramAccountsConfig,
}, },
rpc_response::{RpcBlockUpdate, SlotInfo}, rpc_response::SlotInfo,
}, },
solana_ledger::{blockstore::Blockstore, get_tmp_ledger_path}, solana_ledger::{blockstore::Blockstore, get_tmp_ledger_path},
solana_rpc::{ solana_rpc::{
@@ -36,7 +36,7 @@ use {
}, },
solana_streamer::socket::SocketAddrSpace, solana_streamer::socket::SocketAddrSpace,
solana_test_validator::TestValidator, solana_test_validator::TestValidator,
solana_transaction_status::{TransactionDetails, UiTransactionEncoding}, solana_transaction_status::{ConfirmedBlock, TransactionDetails, UiTransactionEncoding},
std::{ std::{
collections::HashSet, collections::HashSet,
net::{IpAddr, SocketAddr}, net::{IpAddr, SocketAddr},
@@ -214,6 +214,7 @@ fn test_block_subscription() {
.. ..
} = create_genesis_config(10_000); } = create_genesis_config(10_000);
let bank = Bank::new_for_tests(&genesis_config); 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 bank_forks = Arc::new(RwLock::new(BankForks::new(bank)));
// setup Blockstore // setup Blockstore
@@ -227,6 +228,8 @@ fn test_block_subscription() {
let keypair2 = Keypair::new(); let keypair2 = Keypair::new();
let keypair3 = Keypair::new(); let keypair3 = Keypair::new();
let max_complete_transaction_status_slot = Arc::new(AtomicU64::new(blockstore.max_root())); 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( let _confirmed_block_signatures = create_test_transactions_and_populate_blockstore(
vec![&alice, &keypair1, &keypair2, &keypair3], vec![&alice, &keypair1, &keypair2, &keypair3],
0, 0,
@@ -278,23 +281,15 @@ fn test_block_subscription() {
match maybe_actual { match maybe_actual {
Ok(actual) => { Ok(actual) => {
let versioned_block = blockstore.get_complete_block(slot, false).unwrap(); let versioned_block = blockstore.get_complete_block(slot, false).unwrap();
let legacy_block = versioned_block.into_legacy_block().unwrap(); let legacy_block = ConfirmedBlock::from(versioned_block)
let block = legacy_block.clone().configure( .into_legacy_block()
UiTransactionEncoding::Json, .unwrap();
TransactionDetails::Signatures,
false,
);
let expected = RpcBlockUpdate {
slot,
block: Some(block),
err: None,
};
let block = legacy_block.configure( let block = legacy_block.configure(
UiTransactionEncoding::Json, UiTransactionEncoding::Json,
TransactionDetails::Signatures, TransactionDetails::Signatures,
false, false,
); );
assert_eq!(actual.value.slot, expected.slot); assert_eq!(actual.value.slot, slot);
assert!(block.eq(&actual.value.block.unwrap())); assert!(block.eq(&actual.value.block.unwrap()));
} }
Err(e) => { Err(e) => {

View File

@@ -2955,16 +2955,9 @@ mod tests {
); );
let ix_error_signature = ix_error_tx.signatures[0]; let ix_error_signature = ix_error_tx.signatures[0];
let entry_2 = next_entry(&entry_1.hash, 1, vec![ix_error_tx.clone()]); let entry_2 = next_entry(&entry_1.hash, 1, vec![ix_error_tx.clone()]);
let fail_tx = system_transaction::transfer( let entries = vec![entry_1, entry_2];
&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 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()) bank.transfer(rent_exempt_amount, &mint_keypair, &keypair1.pubkey())
.unwrap(); .unwrap();
@@ -3024,27 +3017,24 @@ mod tests {
transaction_status_service.join().unwrap(); transaction_status_service.join().unwrap();
let confirmed_block = blockstore.get_rooted_block(bank.slot(), false).unwrap(); let confirmed_block = blockstore.get_rooted_block(bank.slot(), false).unwrap();
assert_eq!(confirmed_block.transactions.len(), 3); let actual_tx_results: Vec<_> = confirmed_block
.transactions
for VersionedTransactionWithStatusMeta { transaction, meta } in .into_iter()
confirmed_block.transactions.into_iter() .map(|VersionedTransactionWithStatusMeta { transaction, meta }| {
{ (transaction.signatures[0], meta.status)
if transaction.signatures[0] == success_signature { })
let meta = meta.unwrap(); .collect();
assert_eq!(meta.status, Ok(())); let expected_tx_results = vec![
} else if transaction.signatures[0] == ix_error_signature { (success_signature, Ok(())),
let meta = meta.unwrap(); (
assert_eq!( ix_error_signature,
meta.status, Err(TransactionError::InstructionError(
Err(TransactionError::InstructionError( 0,
0, InstructionError::Custom(1),
InstructionError::Custom(1) )),
)) ),
); ];
} else { assert_eq!(actual_tx_results, expected_tx_results);
assert_eq!(meta, None);
}
}
poh_recorder poh_recorder
.lock() .lock()
@@ -3191,7 +3181,7 @@ mod tests {
let recorded_meta = confirmed_block.transactions.pop().unwrap().meta; let recorded_meta = confirmed_block.transactions.pop().unwrap().meta;
assert_eq!( assert_eq!(
recorded_meta, recorded_meta,
Some(TransactionStatusMeta { TransactionStatusMeta {
status: Ok(()), status: Ok(()),
pre_balances: vec![1, 0, 0], pre_balances: vec![1, 0, 0],
post_balances: vec![1, 0, 0], post_balances: vec![1, 0, 0],
@@ -3200,7 +3190,7 @@ mod tests {
rewards: Some(vec![]), rewards: Some(vec![]),
loaded_addresses: sanitized_tx.get_loaded_addresses(), loaded_addresses: sanitized_tx.get_loaded_addresses(),
..TransactionStatusMeta::default() ..TransactionStatusMeta::default()
}) }
); );
poh_recorder poh_recorder
.lock() .lock()

View File

@@ -3984,36 +3984,35 @@ pub mod tests {
let bank1 = Arc::new(Bank::new_from_parent(&bank0, &Pubkey::default(), 1)); let bank1 = Arc::new(Bank::new_from_parent(&bank0, &Pubkey::default(), 1));
let slot = bank1.slot(); 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], vec![&mint_keypair, &keypair1, &keypair2, &keypair3],
bank0.slot(), bank0.slot(),
bank1, bank1,
blockstore.clone(), blockstore.clone(),
Arc::new(AtomicU64::default()), Arc::new(AtomicU64::default()),
); )
.into_iter();
let confirmed_block = blockstore.get_rooted_block(slot, false).unwrap(); 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
for VersionedTransactionWithStatusMeta { transaction, meta } in .into_iter()
confirmed_block.transactions.into_iter() .map(|VersionedTransactionWithStatusMeta { transaction, meta }| {
{ (transaction.signatures[0], meta.status)
if transaction.signatures[0] == signatures[0] { })
let meta = meta.unwrap(); .collect();
assert_eq!(meta.status, Ok(())); let expected_tx_results = vec![
} else if transaction.signatures[0] == signatures[1] { (test_signatures_iter.next().unwrap(), Ok(())),
let meta = meta.unwrap(); (
assert_eq!( test_signatures_iter.next().unwrap(),
meta.status, Err(TransactionError::InstructionError(
Err(TransactionError::InstructionError( 0,
0, InstructionError::Custom(1),
InstructionError::Custom(1) )),
)) ),
); ];
} else { assert_eq!(actual_tx_results, expected_tx_results);
assert_eq!(meta, None); assert!(test_signatures_iter.next().is_none());
}
}
} }
Blockstore::destroy(&ledger_path).unwrap(); Blockstore::destroy(&ledger_path).unwrap();
} }

View File

@@ -16,7 +16,7 @@ use {
}, },
solana_ledger::{blockstore::Blockstore, blockstore_db::AccessType}, solana_ledger::{blockstore::Blockstore, blockstore_db::AccessType},
solana_sdk::{clock::Slot, pubkey::Pubkey, signature::Signature}, solana_sdk::{clock::Slot, pubkey::Pubkey, signature::Signature},
solana_transaction_status::{ConfirmedBlock, Encodable, UiTransactionEncoding}, solana_transaction_status::{Encodable, LegacyConfirmedBlock, UiTransactionEncoding},
std::{ std::{
collections::HashSet, collections::HashSet,
path::Path, path::Path,
@@ -30,7 +30,6 @@ async fn upload(
blockstore: Blockstore, blockstore: Blockstore,
starting_slot: Slot, starting_slot: Slot,
ending_slot: Option<Slot>, ending_slot: Option<Slot>,
allow_missing_metadata: bool,
force_reupload: bool, force_reupload: bool,
) -> Result<(), Box<dyn std::error::Error>> { ) -> Result<(), Box<dyn std::error::Error>> {
let bigtable = solana_storage_bigtable::LedgerStorage::new(false, None, None) let bigtable = solana_storage_bigtable::LedgerStorage::new(false, None, None)
@@ -42,7 +41,6 @@ async fn upload(
bigtable, bigtable,
starting_slot, starting_slot,
ending_slot, ending_slot,
allow_missing_metadata,
force_reupload, force_reupload,
Arc::new(AtomicBool::new(false)), Arc::new(AtomicBool::new(false)),
) )
@@ -73,13 +71,13 @@ async fn block(slot: Slot, output_format: OutputFormat) -> Result<(), Box<dyn st
.await .await
.map_err(|err| format!("Failed to connect to storage: {:?}", err))?; .map_err(|err| format!("Failed to connect to storage: {:?}", err))?;
let versioned_block = bigtable.get_confirmed_block(slot).await?; let confirmed_block = bigtable.get_confirmed_block(slot).await?;
let block = versioned_block let legacy_block = confirmed_block
.into_legacy_block() .into_legacy_block()
.ok_or_else(|| "Failed to read versioned transaction in block".to_string())?; .ok_or_else(|| "Failed to read versioned transaction in block".to_string())?;
let cli_block = CliBlock { let cli_block = CliBlock {
encoded_confirmed_block: block.encode(UiTransactionEncoding::Base64), encoded_confirmed_block: legacy_block.encode(UiTransactionEncoding::Base64),
slot, slot,
}; };
println!("{}", output_format.formatted_string(&cli_block)); println!("{}", output_format.formatted_string(&cli_block));
@@ -156,20 +154,20 @@ async fn confirm(
let mut get_transaction_error = None; let mut get_transaction_error = None;
if verbose { if verbose {
match bigtable.get_confirmed_transaction(signature).await { match bigtable.get_confirmed_transaction(signature).await {
Ok(Some(versioned_confirmed_tx)) => { Ok(Some(confirmed_tx)) => {
let confirmed_transaction = versioned_confirmed_tx let legacy_confirmed_tx = confirmed_tx
.into_legacy_confirmed_transaction() .into_legacy_confirmed_transaction()
.ok_or_else(|| "Failed to read versioned transaction in block".to_string())?; .ok_or_else(|| "Failed to read versioned transaction in block".to_string())?;
transaction = Some(CliTransaction { transaction = Some(CliTransaction {
transaction: confirmed_transaction transaction: legacy_confirmed_tx
.transaction .tx_with_meta
.transaction .transaction
.encode(UiTransactionEncoding::Json), .encode(UiTransactionEncoding::Json),
meta: confirmed_transaction.transaction.meta.map(|m| m.into()), meta: legacy_confirmed_tx.tx_with_meta.meta.map(|m| m.into()),
block_time: confirmed_transaction.block_time, block_time: legacy_confirmed_tx.block_time,
slot: Some(confirmed_transaction.slot), slot: Some(legacy_confirmed_tx.slot),
decoded_transaction: confirmed_transaction.transaction.transaction, decoded_transaction: legacy_confirmed_tx.tx_with_meta.transaction,
prefix: " ".to_string(), prefix: " ".to_string(),
sigverify_status: vec![], sigverify_status: vec![],
}); });
@@ -201,7 +199,7 @@ pub async fn transaction_history(
) -> Result<(), Box<dyn std::error::Error>> { ) -> Result<(), Box<dyn std::error::Error>> {
let bigtable = solana_storage_bigtable::LedgerStorage::new(true, None, None).await?; 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 { while limit > 0 {
let results = bigtable let results = bigtable
.get_confirmed_signatures_for_address( .get_confirmed_signatures_for_address(
@@ -267,8 +265,8 @@ pub async fn transaction_history(
println!(" Unable to get confirmed transaction details: {}", err); println!(" Unable to get confirmed transaction details: {}", err);
break; break;
} }
Ok(versioned_block) => { Ok(confirmed_block) => {
let block = versioned_block.into_legacy_block().ok_or_else(|| { let block = confirmed_block.into_legacy_block().ok_or_else(|| {
"Failed to read versioned transaction in block".to_string() "Failed to read versioned transaction in block".to_string()
})?; })?;
loaded_block = Some((result.slot, block)); loaded_block = Some((result.slot, block));
@@ -316,12 +314,6 @@ impl BigTableSubCommand for App<'_, '_> {
.index(2) .index(2)
.help("Stop uploading at this slot [default: last available slot]"), .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(
Arg::with_name("force_reupload") Arg::with_name("force_reupload")
.long("force") .long("force")
@@ -516,7 +508,6 @@ pub fn bigtable_process_command(ledger_path: &Path, matches: &ArgMatches<'_>) {
("upload", Some(arg_matches)) => { ("upload", Some(arg_matches)) => {
let starting_slot = value_t!(arg_matches, "starting_slot", Slot).unwrap_or(0); 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 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 force_reupload = arg_matches.is_present("force_reupload");
let blockstore = crate::open_blockstore( let blockstore = crate::open_blockstore(
&canonicalize_ledger_path(ledger_path), &canonicalize_ledger_path(ledger_path),
@@ -528,7 +519,6 @@ pub fn bigtable_process_command(ledger_path: &Path, matches: &ArgMatches<'_>) {
blockstore, blockstore,
starting_slot, starting_slot,
ending_slot, ending_slot,
allow_missing_metadata,
force_reupload, force_reupload,
)) ))
} }

View File

@@ -26,7 +26,6 @@ pub async fn upload_confirmed_blocks(
bigtable: solana_storage_bigtable::LedgerStorage, bigtable: solana_storage_bigtable::LedgerStorage,
starting_slot: Slot, starting_slot: Slot,
ending_slot: Option<Slot>, ending_slot: Option<Slot>,
allow_missing_metadata: bool,
force_reupload: bool, force_reupload: bool,
exit: Arc<AtomicBool>, exit: Arc<AtomicBool>,
) -> Result<(), Box<dyn std::error::Error>> { ) -> Result<(), Box<dyn std::error::Error>> {
@@ -187,20 +186,7 @@ pub async fn upload_confirmed_blocks(
num_blocks -= 1; num_blocks -= 1;
None None
} }
Some(confirmed_block) => { Some(confirmed_block) => Some(bigtable.upload_confirmed_block(slot, 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))
}
}); });
for result in futures::future::join_all(uploads).await { for result in futures::future::join_all(uploads).await {

View File

@@ -72,7 +72,6 @@ impl BigTableUploadService {
bigtable_ledger_storage.clone(), bigtable_ledger_storage.clone(),
start_slot, start_slot,
Some(end_slot), Some(end_slot),
true,
false, false,
exit.clone(), exit.clone(),
)); ));

View File

@@ -42,8 +42,8 @@ use {
}, },
solana_storage_proto::{StoredExtendedRewards, StoredTransactionStatusMeta}, solana_storage_proto::{StoredExtendedRewards, StoredTransactionStatusMeta},
solana_transaction_status::{ solana_transaction_status::{
ConfirmedTransactionStatusWithSignature, Rewards, TransactionStatusMeta, ConfirmedTransactionStatusWithSignature, ConfirmedTransactionWithStatusMeta, Rewards,
VersionedConfirmedBlock, VersionedConfirmedTransactionWithStatusMeta, TransactionStatusMeta, TransactionWithStatusMeta, VersionedConfirmedBlock,
VersionedTransactionWithStatusMeta, VersionedTransactionWithStatusMeta,
}, },
std::{ std::{
@@ -2043,9 +2043,8 @@ impl Blockstore {
Ok(VersionedTransactionWithStatusMeta { Ok(VersionedTransactionWithStatusMeta {
transaction, transaction,
meta: self meta: self
.read_transaction_status((signature, slot)) .read_transaction_status((signature, slot))?
.ok() .ok_or(BlockstoreError::MissingTransactionMetadata)?,
.flatten(),
}) })
}) })
.collect() .collect()
@@ -2294,7 +2293,7 @@ impl Blockstore {
pub fn get_rooted_transaction( pub fn get_rooted_transaction(
&self, &self,
signature: Signature, signature: Signature,
) -> Result<Option<VersionedConfirmedTransactionWithStatusMeta>> { ) -> Result<Option<ConfirmedTransactionWithStatusMeta>> {
datapoint_info!( datapoint_info!(
"blockstore-rpc-api", "blockstore-rpc-api",
("method", "get_rooted_transaction", String) ("method", "get_rooted_transaction", String)
@@ -2307,7 +2306,7 @@ impl Blockstore {
&self, &self,
signature: Signature, signature: Signature,
highest_confirmed_slot: Slot, highest_confirmed_slot: Slot,
) -> Result<Option<VersionedConfirmedTransactionWithStatusMeta>> { ) -> Result<Option<ConfirmedTransactionWithStatusMeta>> {
datapoint_info!( datapoint_info!(
"blockstore-rpc-api", "blockstore-rpc-api",
("method", "get_complete_transaction", String) ("method", "get_complete_transaction", String)
@@ -2324,8 +2323,8 @@ impl Blockstore {
&self, &self,
signature: Signature, signature: Signature,
confirmed_unrooted_slots: &[Slot], confirmed_unrooted_slots: &[Slot],
) -> Result<Option<VersionedConfirmedTransactionWithStatusMeta>> { ) -> Result<Option<ConfirmedTransactionWithStatusMeta>> {
if let Some((slot, status)) = if let Some((slot, meta)) =
self.get_transaction_status(signature, confirmed_unrooted_slots)? self.get_transaction_status(signature, confirmed_unrooted_slots)?
{ {
let transaction = self let transaction = self
@@ -2333,12 +2332,11 @@ impl Blockstore {
.ok_or(BlockstoreError::TransactionStatusSlotMismatch)?; // Should not happen .ok_or(BlockstoreError::TransactionStatusSlotMismatch)?; // Should not happen
let block_time = self.get_block_time(slot)?; let block_time = self.get_block_time(slot)?;
Ok(Some(VersionedConfirmedTransactionWithStatusMeta { Ok(Some(ConfirmedTransactionWithStatusMeta {
slot, slot,
tx_with_meta: VersionedTransactionWithStatusMeta { tx_with_meta: TransactionWithStatusMeta::Complete(
transaction, VersionedTransactionWithStatusMeta { transaction, meta },
meta: Some(status), ),
},
block_time, block_time,
})) }))
} else { } else {
@@ -6327,7 +6325,7 @@ pub mod tests {
.unwrap(); .unwrap();
VersionedTransactionWithStatusMeta { VersionedTransactionWithStatusMeta {
transaction, transaction,
meta: Some(TransactionStatusMeta { meta: TransactionStatusMeta {
status: Ok(()), status: Ok(()),
fee: 42, fee: 42,
pre_balances, pre_balances,
@@ -6338,21 +6336,22 @@ pub mod tests {
post_token_balances: Some(vec![]), post_token_balances: Some(vec![]),
rewards: Some(vec![]), rewards: Some(vec![]),
loaded_addresses: LoadedAddresses::default(), loaded_addresses: LoadedAddresses::default(),
}), },
} }
}) })
.collect(); .collect();
// Even if marked as root, a slot that is empty of entries should return an error // 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!(
assert_matches!(confirmed_block_err, BlockstoreError::SlotUnavailable); 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, // 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. // 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!( assert_matches!(
confirmed_block_err, blockstore.get_rooted_block(slot, true),
BlockstoreError::ParentEntriesUnavailable Err(BlockstoreError::ParentEntriesUnavailable)
); );
// Test if require_previous_blockhash is false // Test if require_previous_blockhash is false
@@ -7168,7 +7167,7 @@ pub mod tests {
.unwrap(); .unwrap();
VersionedTransactionWithStatusMeta { VersionedTransactionWithStatusMeta {
transaction, transaction,
meta: Some(TransactionStatusMeta { meta: TransactionStatusMeta {
status: Ok(()), status: Ok(()),
fee: 42, fee: 42,
pre_balances, pre_balances,
@@ -7179,7 +7178,7 @@ pub mod tests {
post_token_balances, post_token_balances,
rewards, rewards,
loaded_addresses: LoadedAddresses::default(), loaded_addresses: LoadedAddresses::default(),
}), },
} }
}) })
.collect(); .collect();
@@ -7188,9 +7187,9 @@ pub mod tests {
let signature = tx_with_meta.transaction.signatures[0]; let signature = tx_with_meta.transaction.signatures[0];
assert_eq!( assert_eq!(
blockstore.get_rooted_transaction(signature).unwrap(), blockstore.get_rooted_transaction(signature).unwrap(),
Some(VersionedConfirmedTransactionWithStatusMeta { Some(ConfirmedTransactionWithStatusMeta {
slot, slot,
tx_with_meta: tx_with_meta.clone(), tx_with_meta: TransactionWithStatusMeta::Complete(tx_with_meta.clone()),
block_time: None block_time: None
}) })
); );
@@ -7198,9 +7197,9 @@ pub mod tests {
blockstore blockstore
.get_complete_transaction(signature, slot + 1) .get_complete_transaction(signature, slot + 1)
.unwrap(), .unwrap(),
Some(VersionedConfirmedTransactionWithStatusMeta { Some(ConfirmedTransactionWithStatusMeta {
slot, slot,
tx_with_meta, tx_with_meta: TransactionWithStatusMeta::Complete(tx_with_meta),
block_time: None block_time: None
}) })
); );
@@ -7270,7 +7269,7 @@ pub mod tests {
.unwrap(); .unwrap();
VersionedTransactionWithStatusMeta { VersionedTransactionWithStatusMeta {
transaction, transaction,
meta: Some(TransactionStatusMeta { meta: TransactionStatusMeta {
status: Ok(()), status: Ok(()),
fee: 42, fee: 42,
pre_balances, pre_balances,
@@ -7281,7 +7280,7 @@ pub mod tests {
post_token_balances, post_token_balances,
rewards, rewards,
loaded_addresses: LoadedAddresses::default(), loaded_addresses: LoadedAddresses::default(),
}), },
} }
}) })
.collect(); .collect();
@@ -7292,9 +7291,9 @@ pub mod tests {
blockstore blockstore
.get_complete_transaction(signature, slot) .get_complete_transaction(signature, slot)
.unwrap(), .unwrap(),
Some(VersionedConfirmedTransactionWithStatusMeta { Some(ConfirmedTransactionWithStatusMeta {
slot, slot,
tx_with_meta, tx_with_meta: TransactionWithStatusMeta::Complete(tx_with_meta),
block_time: None block_time: None
}) })
); );
@@ -8052,6 +8051,16 @@ pub mod tests {
.unwrap(); .unwrap();
transactions.push(transaction.into()); 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 // Push transaction that will not have matching status, as a test case
transactions.push( transactions.push(
Transaction::new_with_compiled_instructions( Transaction::new_with_compiled_instructions(
@@ -8064,14 +8073,9 @@ pub mod tests {
.into(), .into(),
); );
let map_result = blockstore.map_transactions_to_statuses(slot, transactions.into_iter()); let map_result =
assert!(map_result.is_ok()); blockstore.map_transactions_to_statuses(slot, transactions.clone().into_iter());
let map = map_result.unwrap(); assert_matches!(map_result, Err(BlockstoreError::MissingTransactionMetadata));
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);
} }
#[test] #[test]

View File

@@ -102,6 +102,7 @@ pub enum BlockstoreError {
ParentEntriesUnavailable, ParentEntriesUnavailable,
SlotUnavailable, SlotUnavailable,
UnsupportedTransactionVersion, UnsupportedTransactionVersion,
MissingTransactionMetadata,
} }
pub type Result<T> = std::result::Result<T, BlockstoreError>; pub type Result<T> = std::result::Result<T, BlockstoreError>;

View File

@@ -878,46 +878,27 @@ fn find_latest_replayed_slot_from_ledger(
latest_slot = new_latest_slot; latest_slot = new_latest_slot;
info!("Checking latest_slot {}", latest_slot); info!("Checking latest_slot {}", latest_slot);
// Wait for the slot to be fully received by the validator // Wait for the slot to be fully received by the validator
let entries;
loop { loop {
info!("Waiting for slot {} to be full", latest_slot); info!("Waiting for slot {} to be full", latest_slot);
if blockstore.is_full(latest_slot) { if blockstore.is_full(latest_slot) {
entries = blockstore.get_slot_entries(latest_slot, 0).unwrap();
assert!(!entries.is_empty());
break; break;
} else { } else {
sleep(Duration::from_millis(50)); sleep(Duration::from_millis(50));
blockstore = open_blockstore(ledger_path); blockstore = open_blockstore(ledger_path);
} }
} }
// Check the slot has been replayed // Wait for the slot to be replayed
let non_tick_entry = entries.into_iter().find(|e| !e.transactions.is_empty()); loop {
if let Some(non_tick_entry) = non_tick_entry { info!("Waiting for slot {} to be replayed", latest_slot);
// Wait for the slot to be replayed if blockstore.get_bank_hash(latest_slot).is_some() {
loop { return (
info!("Waiting for slot {} to be replayed", latest_slot); latest_slot,
if !blockstore AncestorIterator::new(latest_slot, &blockstore).collect(),
.map_transactions_to_statuses( );
latest_slot, } else {
non_tick_entry.transactions.clone().into_iter(), sleep(Duration::from_millis(50));
) blockstore = open_blockstore(ledger_path);
.unwrap()
.is_empty()
{
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)); sleep(Duration::from_millis(50));

View File

@@ -54,11 +54,11 @@ use solana_sdk::{
system_instruction::{self, MAX_PERMITTED_DATA_LENGTH}, system_instruction::{self, MAX_PERMITTED_DATA_LENGTH},
system_program, sysvar, system_program, sysvar,
sysvar::{clock, rent}, sysvar::{clock, rent},
transaction::{SanitizedTransaction, Transaction, TransactionError}, transaction::{SanitizedTransaction, Transaction, TransactionError, VersionedTransaction},
}; };
use solana_transaction_status::{ use solana_transaction_status::{
token_balances::collect_token_balances, ConfirmedTransactionWithStatusMeta, InnerInstructions, 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}; use std::{collections::HashMap, env, fs::File, io::Read, path::PathBuf, str::FromStr, sync::Arc};
@@ -429,10 +429,12 @@ fn execute_transactions(
Ok(ConfirmedTransactionWithStatusMeta { Ok(ConfirmedTransactionWithStatusMeta {
slot: bank.slot(), slot: bank.slot(),
transaction: TransactionWithStatusMeta { tx_with_meta: TransactionWithStatusMeta::Complete(
transaction: tx.clone(), VersionedTransactionWithStatusMeta {
meta: Some(tx_status_meta), transaction: VersionedTransaction::from(tx.clone()),
}, meta: tx_status_meta,
},
),
block_time: None, block_time: None,
}) })
} }
@@ -2476,10 +2478,10 @@ fn test_program_upgradeable_locks() {
assert!(matches!( assert!(matches!(
results1[0], results1[0],
Ok(ConfirmedTransactionWithStatusMeta { Ok(ConfirmedTransactionWithStatusMeta {
transaction: TransactionWithStatusMeta { tx_with_meta: TransactionWithStatusMeta::Complete(VersionedTransactionWithStatusMeta {
meta: Some(TransactionStatusMeta { status: Ok(()), .. }), meta: TransactionStatusMeta { status: Ok(()), .. },
.. ..
}, }),
.. ..
}) })
)); ));
@@ -2488,16 +2490,16 @@ fn test_program_upgradeable_locks() {
assert!(matches!( assert!(matches!(
results2[0], results2[0],
Ok(ConfirmedTransactionWithStatusMeta { Ok(ConfirmedTransactionWithStatusMeta {
transaction: TransactionWithStatusMeta { tx_with_meta: TransactionWithStatusMeta::Complete(VersionedTransactionWithStatusMeta {
meta: Some(TransactionStatusMeta { meta: TransactionStatusMeta {
status: Err(TransactionError::InstructionError( status: Err(TransactionError::InstructionError(
0, 0,
InstructionError::ProgramFailedToComplete InstructionError::ProgramFailedToComplete
)), )),
.. ..
}), },
.. ..
}, }),
.. ..
}) })
)); ));

View File

@@ -80,10 +80,10 @@ use {
solana_storage_bigtable::Error as StorageError, solana_storage_bigtable::Error as StorageError,
solana_streamer::socket::SocketAddrSpace, solana_streamer::socket::SocketAddrSpace,
solana_transaction_status::{ solana_transaction_status::{
ConfirmedTransactionStatusWithSignature, Encodable, ConfirmedBlock, ConfirmedTransactionStatusWithSignature,
EncodedConfirmedTransactionWithStatusMeta, Reward, RewardType, ConfirmedTransactionWithStatusMeta, Encodable, EncodedConfirmedTransactionWithStatusMeta,
TransactionConfirmationStatus, TransactionStatus, UiConfirmedBlock, UiTransactionEncoding, Reward, RewardType, TransactionConfirmationStatus, TransactionStatus, UiConfirmedBlock,
VersionedConfirmedBlock, UiTransactionEncoding,
}, },
solana_vote_program::vote_state::{VoteState, MAX_LOCKOUT_HISTORY}, solana_vote_program::vote_state::{VoteState, MAX_LOCKOUT_HISTORY},
spl_token::{ spl_token::{
@@ -918,12 +918,8 @@ impl JsonRpcRequestProcessor {
&self, &self,
result: &std::result::Result<T, BlockstoreError>, result: &std::result::Result<T, BlockstoreError>,
slot: Slot, slot: Slot,
) -> Result<()> ) -> Result<()> {
where if let Err(err) = result {
T: std::fmt::Debug,
{
if result.is_err() {
let err = result.as_ref().unwrap_err();
debug!( debug!(
"check_blockstore_root, slot: {:?}, max root: {:?}, err: {:?}", "check_blockstore_root, slot: {:?}, max root: {:?}, err: {:?}",
slot, slot,
@@ -944,21 +940,16 @@ impl JsonRpcRequestProcessor {
&self, &self,
result: &std::result::Result<T, BlockstoreError>, result: &std::result::Result<T, BlockstoreError>,
slot: Slot, slot: Slot,
) -> Result<()> ) -> Result<()> {
where if let Err(BlockstoreError::SlotCleanedUp) = result {
T: std::fmt::Debug, return Err(RpcCustomError::BlockCleanedUp {
{ slot,
if result.is_err() { first_available_block: self
if let BlockstoreError::SlotCleanedUp = result.as_ref().unwrap_err() { .blockstore
return Err(RpcCustomError::BlockCleanedUp { .get_first_available_block()
slot, .unwrap_or_default(),
first_available_block: self
.blockstore
.get_first_available_block()
.unwrap_or_default(),
}
.into());
} }
.into());
} }
Ok(()) Ok(())
} }
@@ -966,15 +957,9 @@ impl JsonRpcRequestProcessor {
fn check_bigtable_result<T>( fn check_bigtable_result<T>(
&self, &self,
result: &std::result::Result<T, solana_storage_bigtable::Error>, result: &std::result::Result<T, solana_storage_bigtable::Error>,
) -> Result<()> ) -> Result<()> {
where if let Err(solana_storage_bigtable::Error::BlockNotFound(slot)) = result {
T: std::fmt::Debug, return Err(RpcCustomError::LongTermStorageSlotSkipped { slot: *slot }.into());
{
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());
}
} }
Ok(()) Ok(())
} }
@@ -1017,12 +1002,12 @@ impl JsonRpcRequestProcessor {
self.check_status_is_complete(slot)?; self.check_status_is_complete(slot)?;
let result = self.blockstore.get_rooted_block(slot, true); let result = self.blockstore.get_rooted_block(slot, true);
self.check_blockstore_root(&result, slot)?; self.check_blockstore_root(&result, slot)?;
let configure_block = |versioned_block: VersionedConfirmedBlock| { let configure_block = |confirmed_block: ConfirmedBlock| {
let confirmed_block = versioned_block let legacy_block = confirmed_block
.into_legacy_block() .into_legacy_block()
.ok_or(RpcCustomError::UnsupportedTransactionVersion)?; .ok_or(RpcCustomError::UnsupportedTransactionVersion)?;
let mut confirmed_block = let mut confirmed_block =
confirmed_block.configure(encoding, transaction_details, show_rewards); legacy_block.configure(encoding, transaction_details, show_rewards);
if slot == 0 { if slot == 0 {
confirmed_block.block_time = Some(self.genesis_creation_time()); confirmed_block.block_time = Some(self.genesis_creation_time());
confirmed_block.block_height = Some(0); confirmed_block.block_height = Some(0);
@@ -1038,7 +1023,11 @@ impl JsonRpcRequestProcessor {
} }
} }
self.check_slot_cleaned_up(&result, slot)?; 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() { } else if commitment.is_confirmed() {
// Check if block is confirmed // Check if block is confirmed
let confirmed_bank = self.bank(Some(CommitmentConfig::confirmed())); let confirmed_bank = self.bank(Some(CommitmentConfig::confirmed()));
@@ -1047,30 +1036,28 @@ impl JsonRpcRequestProcessor {
let result = self.blockstore.get_complete_block(slot, true); let result = self.blockstore.get_complete_block(slot, true);
return result return result
.ok() .ok()
.map(|versioned_block| { .map(ConfirmedBlock::from)
let mut confirmed_block = versioned_block .map(|confirmed_block| -> Result<UiConfirmedBlock> {
let mut legacy_block = confirmed_block
.into_legacy_block() .into_legacy_block()
.ok_or(RpcCustomError::UnsupportedTransactionVersion)?; .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 r_bank_forks = self.bank_forks.read().unwrap();
let bank = r_bank_forks.get(slot).cloned(); let bank = r_bank_forks.get(slot).cloned();
if let Some(bank) = bank { if let Some(bank) = bank {
if confirmed_block.block_time.is_none() { if legacy_block.block_time.is_none() {
confirmed_block.block_time = legacy_block.block_time = Some(bank.clock().unix_timestamp);
Some(bank.clock().unix_timestamp);
} }
if confirmed_block.block_height.is_none() { if legacy_block.block_height.is_none() {
confirmed_block.block_height = Some(bank.block_height()); legacy_block.block_height = Some(bank.block_height());
} }
} }
} }
Ok(confirmed_block.configure(
encoding, Ok(legacy_block.configure(encoding, transaction_details, show_rewards))
transaction_details,
show_rewards,
))
}) })
.transpose(); .transpose();
} }
@@ -1397,7 +1384,7 @@ impl JsonRpcRequestProcessor {
if self.config.enable_rpc_transaction_history { if self.config.enable_rpc_transaction_history {
let confirmed_bank = self.bank(Some(CommitmentConfig::confirmed())); 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(); let highest_confirmed_slot = confirmed_bank.slot();
self.blockstore self.blockstore
.get_complete_transaction(signature, highest_confirmed_slot) .get_complete_transaction(signature, highest_confirmed_slot)
@@ -1405,12 +1392,16 @@ impl JsonRpcRequestProcessor {
self.blockstore.get_rooted_transaction(signature) self.blockstore.get_rooted_transaction(signature)
}; };
match versioned_confirmed_tx.unwrap_or(None) { let encode_transaction =
Some(versioned_confirmed_tx) => { |confirmed_tx_with_meta: ConfirmedTransactionWithStatusMeta| -> Result<EncodedConfirmedTransactionWithStatusMeta> {
let mut confirmed_transaction = versioned_confirmed_tx let legacy_tx_with_meta = confirmed_tx_with_meta.into_legacy_confirmed_transaction()
.into_legacy_confirmed_transaction()
.ok_or(RpcCustomError::UnsupportedTransactionVersion)?; .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() if commitment.is_confirmed()
&& confirmed_bank // should be redundant && confirmed_bank // should be redundant
.status_cache_ancestors() .status_cache_ancestors()
@@ -1422,8 +1413,9 @@ impl JsonRpcRequestProcessor {
.get(confirmed_transaction.slot) .get(confirmed_transaction.slot)
.map(|bank| bank.clock().unix_timestamp); .map(|bank| bank.clock().unix_timestamp);
} }
return Ok(Some(confirmed_transaction.encode(encoding))); return Ok(Some(encode_transaction(confirmed_transaction)?));
} }
if confirmed_transaction.slot if confirmed_transaction.slot
<= self <= self
.block_commitment_cache .block_commitment_cache
@@ -1431,7 +1423,7 @@ impl JsonRpcRequestProcessor {
.unwrap() .unwrap()
.highest_confirmed_root() .highest_confirmed_root()
{ {
return Ok(Some(confirmed_transaction.encode(encoding))); return Ok(Some(encode_transaction(confirmed_transaction)?));
} }
} }
None => { None => {
@@ -1440,13 +1432,7 @@ impl JsonRpcRequestProcessor {
.get_confirmed_transaction(&signature) .get_confirmed_transaction(&signature)
.await .await
.unwrap_or(None) .unwrap_or(None)
.map(|versioned_confirmed_tx| { .map(encode_transaction)
let confirmed_tx = versioned_confirmed_tx
.into_legacy_confirmed_transaction()
.ok_or(RpcCustomError::UnsupportedTransactionVersion)?;
Ok(confirmed_tx.encode(encoding))
})
.transpose(); .transpose();
} }
} }
@@ -4328,15 +4314,7 @@ pub fn create_test_transactions_and_populate_blockstore(
); );
let ix_error_signature = ix_error_tx.signatures[0]; 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]); let entry_2 = solana_entry::entry::next_entry(&entry_1.hash, 1, vec![ix_error_tx]);
// Failed transaction let entries = vec![entry_1, entry_2];
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 shreds = let shreds =
solana_ledger::blockstore::entries_to_test_shreds(&entries, slot, previous_slot, true, 0); 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 // Check that process_entries successfully writes can_commit transactions statuses, and
// that they are matched properly by get_rooted_block // that they are matched properly by get_rooted_block
let _result = solana_ledger::blockstore_processor::process_entries_for_tests( assert_eq!(
&bank, solana_ledger::blockstore_processor::process_entries_for_tests(
entries, &bank,
true, entries,
Some( true,
&solana_ledger::blockstore_processor::TransactionStatusSender { Some(
sender: transaction_status_sender, &solana_ledger::blockstore_processor::TransactionStatusSender {
enable_cpi_and_log_storage: false, sender: transaction_status_sender,
}, enable_cpi_and_log_storage: false,
},
),
Some(&replay_vote_sender),
), ),
Some(&replay_vote_sender), Ok(())
); );
transaction_status_service.join().unwrap(); transaction_status_service.join().unwrap();
@@ -6613,7 +6594,7 @@ pub mod tests {
let confirmed_block: Option<EncodedConfirmedBlock> = let confirmed_block: Option<EncodedConfirmedBlock> =
serde_json::from_value(result["result"].clone()).unwrap(); serde_json::from_value(result["result"].clone()).unwrap();
let confirmed_block = confirmed_block.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![]); assert_eq!(confirmed_block.rewards, vec![]);
for EncodedTransactionWithStatusMeta { transaction, meta } in for EncodedTransactionWithStatusMeta { transaction, meta } in
@@ -6658,7 +6639,7 @@ pub mod tests {
let confirmed_block: Option<EncodedConfirmedBlock> = let confirmed_block: Option<EncodedConfirmedBlock> =
serde_json::from_value(result["result"].clone()).unwrap(); serde_json::from_value(result["result"].clone()).unwrap();
let confirmed_block = confirmed_block.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![]); assert_eq!(confirmed_block.rewards, vec![]);
for EncodedTransactionWithStatusMeta { transaction, meta } in for EncodedTransactionWithStatusMeta { transaction, meta } in

View File

@@ -40,7 +40,7 @@ use {
timing::timestamp, timing::timestamp,
transaction, transaction,
}, },
solana_transaction_status::ConfirmedBlock, solana_transaction_status::{ConfirmedBlock, LegacyConfirmedBlock},
std::{ std::{
cell::RefCell, cell::RefCell,
collections::{HashMap, VecDeque}, collections::{HashMap, VecDeque},
@@ -278,30 +278,26 @@ impl RpcNotifier {
} }
fn filter_block_result_txs( fn filter_block_result_txs(
block: ConfirmedBlock, mut block: LegacyConfirmedBlock,
last_modified_slot: Slot, last_modified_slot: Slot,
params: &BlockSubscriptionParams, params: &BlockSubscriptionParams,
) -> Option<RpcBlockUpdate> { ) -> Option<RpcBlockUpdate> {
let transactions = match params.kind { block.transactions = match params.kind {
BlockSubscriptionKind::All => block.transactions, BlockSubscriptionKind::All => block.transactions,
BlockSubscriptionKind::MentionsAccountOrProgram(pk) => block BlockSubscriptionKind::MentionsAccountOrProgram(pk) => block
.transactions .transactions
.into_iter() .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(), .collect(),
}; };
if transactions.is_empty() { if block.transactions.is_empty() {
if let BlockSubscriptionKind::MentionsAccountOrProgram(_) = params.kind { if let BlockSubscriptionKind::MentionsAccountOrProgram(_) = params.kind {
return None; return None;
} }
} }
let block = ConfirmedBlock { let block = block.configure(
transactions,
..block
}
.configure(
params.encoding, params.encoding,
params.transaction_details, params.transaction_details,
params.show_rewards, params.show_rewards,
@@ -962,26 +958,29 @@ impl RpcSubscriptions {
break; break;
} }
let block_result = blockstore let block_update_result = blockstore
.get_complete_block(s, false) .get_complete_block(s, false)
.map_err(|e| { .map_err(|e| {
error!("get_complete_block error: {}", e); error!("get_complete_block error: {}", e);
RpcBlockUpdateError::BlockStoreError RpcBlockUpdateError::BlockStoreError
}) })
.and_then(|versioned_block| { .and_then(|versioned_block| {
versioned_block.into_legacy_block().ok_or( ConfirmedBlock::from(versioned_block)
RpcBlockUpdateError::UnsupportedTransactionVersion, .into_legacy_block()
) .ok_or(
RpcBlockUpdateError::UnsupportedTransactionVersion,
)
}); });
match block_result { match block_update_result {
Ok(block) => { Ok(block_update) => {
if let Some(res) = filter_block_result_txs(block, s, params) if let Some(block_update) =
filter_block_result_txs(block_update, s, params)
{ {
notifier.notify( notifier.notify(
Response { Response {
context: RpcResponseContext { slot: s }, context: RpcResponseContext { slot: s },
value: res, value: block_update,
}, },
subscription, subscription,
false, false,
@@ -1357,8 +1356,13 @@ pub(crate) mod tests {
#[serial] #[serial]
fn test_check_confirmed_block_subscribe() { fn test_check_confirmed_block_subscribe() {
let exit = Arc::new(AtomicBool::new(false)); 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 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 bank_forks = Arc::new(RwLock::new(BankForks::new(bank)));
let optimistically_confirmed_bank = let optimistically_confirmed_bank =
OptimisticallyConfirmedBank::locked_from_bank_forks_root(&bank_forks); OptimisticallyConfirmedBank::locked_from_bank_forks_root(&bank_forks);
@@ -1399,10 +1403,11 @@ pub(crate) mod tests {
let keypair1 = Keypair::new(); let keypair1 = Keypair::new();
let keypair2 = Keypair::new(); let keypair2 = Keypair::new();
let keypair3 = Keypair::new(); let keypair3 = Keypair::new();
let keypair4 = Keypair::new();
let max_complete_transaction_status_slot = Arc::new(AtomicU64::new(blockstore.max_root())); 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( let _confirmed_block_signatures = create_test_transactions_and_populate_blockstore(
vec![&keypair1, &keypair2, &keypair3, &keypair4], vec![&mint_keypair, &keypair1, &keypair2, &keypair3],
0, 0,
bank, bank,
blockstore.clone(), blockstore.clone(),
@@ -1414,8 +1419,9 @@ pub(crate) mod tests {
let actual_resp = receiver.recv(); let actual_resp = receiver.recv();
let actual_resp = serde_json::from_str::<serde_json::Value>(&actual_resp).unwrap(); let actual_resp = serde_json::from_str::<serde_json::Value>(&actual_resp).unwrap();
let versioned_block = blockstore.get_complete_block(slot, false).unwrap(); let confirmed_block =
let legacy_block = versioned_block.into_legacy_block().unwrap(); 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 block = legacy_block.configure(params.encoding, params.transaction_details, false);
let expected_resp = RpcBlockUpdate { let expected_resp = RpcBlockUpdate {
slot, slot,
@@ -1455,8 +1461,13 @@ pub(crate) mod tests {
#[serial] #[serial]
fn test_check_confirmed_block_subscribe_with_mentions() { fn test_check_confirmed_block_subscribe_with_mentions() {
let exit = Arc::new(AtomicBool::new(false)); 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 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 bank_forks = Arc::new(RwLock::new(BankForks::new(bank)));
let optimistically_confirmed_bank = let optimistically_confirmed_bank =
OptimisticallyConfirmedBank::locked_from_bank_forks_root(&bank_forks); 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 bank = bank_forks.read().unwrap().working_bank();
let keypair2 = Keypair::new(); let keypair2 = Keypair::new();
let keypair3 = Keypair::new(); let keypair3 = Keypair::new();
let keypair4 = Keypair::new();
let max_complete_transaction_status_slot = Arc::new(AtomicU64::new(blockstore.max_root())); 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( let _confirmed_block_signatures = create_test_transactions_and_populate_blockstore(
vec![&keypair1, &keypair2, &keypair3, &keypair4], vec![&mint_keypair, &keypair1, &keypair2, &keypair3],
0, 0,
bank, bank,
blockstore.clone(), blockstore.clone(),
@@ -1514,8 +1526,9 @@ pub(crate) mod tests {
let actual_resp = serde_json::from_str::<serde_json::Value>(&actual_resp).unwrap(); let actual_resp = serde_json::from_str::<serde_json::Value>(&actual_resp).unwrap();
// make sure it filtered out the other keypairs // make sure it filtered out the other keypairs
let versioned_block = blockstore.get_complete_block(slot, false).unwrap(); let confirmed_block =
let mut legacy_block = versioned_block.into_legacy_block().unwrap(); 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| { legacy_block.transactions.retain(|tx_with_meta| {
tx_with_meta tx_with_meta
.transaction .transaction
@@ -1552,8 +1565,13 @@ pub(crate) mod tests {
#[serial] #[serial]
fn test_check_finalized_block_subscribe() { fn test_check_finalized_block_subscribe() {
let exit = Arc::new(AtomicBool::new(false)); 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 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 bank_forks = Arc::new(RwLock::new(BankForks::new(bank)));
let optimistically_confirmed_bank = let optimistically_confirmed_bank =
OptimisticallyConfirmedBank::locked_from_bank_forks_root(&bank_forks); OptimisticallyConfirmedBank::locked_from_bank_forks_root(&bank_forks);
@@ -1593,10 +1611,11 @@ pub(crate) mod tests {
let keypair1 = Keypair::new(); let keypair1 = Keypair::new();
let keypair2 = Keypair::new(); let keypair2 = Keypair::new();
let keypair3 = Keypair::new(); let keypair3 = Keypair::new();
let keypair4 = Keypair::new();
let max_complete_transaction_status_slot = Arc::new(AtomicU64::new(blockstore.max_root())); 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( let _confirmed_block_signatures = create_test_transactions_and_populate_blockstore(
vec![&keypair1, &keypair2, &keypair3, &keypair4], vec![&mint_keypair, &keypair1, &keypair2, &keypair3],
0, 0,
bank, bank,
blockstore.clone(), blockstore.clone(),
@@ -1613,8 +1632,9 @@ pub(crate) mod tests {
let actual_resp = receiver.recv(); let actual_resp = receiver.recv();
let actual_resp = serde_json::from_str::<serde_json::Value>(&actual_resp).unwrap(); let actual_resp = serde_json::from_str::<serde_json::Value>(&actual_resp).unwrap();
let versioned_block = blockstore.get_complete_block(slot, false).unwrap(); let confirmed_block =
let legacy_block = versioned_block.into_legacy_block().unwrap(); 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 block = legacy_block.configure(params.encoding, params.transaction_details, false);
let expected_resp = RpcBlockUpdate { let expected_resp = RpcBlockUpdate {
slot, slot,

View File

@@ -792,23 +792,46 @@ mod tests {
prost::Message, prost::Message,
solana_sdk::{ solana_sdk::{
hash::Hash, message::v0::LoadedAddresses, signature::Keypair, system_transaction, hash::Hash, message::v0::LoadedAddresses, signature::Keypair, system_transaction,
transaction::VersionedTransaction,
}, },
solana_storage_proto::convert::generated, solana_storage_proto::convert::generated,
solana_transaction_status::{ solana_transaction_status::{
ConfirmedBlock, TransactionStatusMeta, TransactionWithStatusMeta, ConfirmedBlock, TransactionStatusMeta, TransactionWithStatusMeta,
VersionedConfirmedBlock, VersionedTransactionWithStatusMeta,
}, },
std::convert::TryInto, 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] #[test]
fn test_deserialize_protobuf_or_bincode_cell_data() { fn test_deserialize_protobuf_or_bincode_cell_data() {
let from = Keypair::new(); let from = Keypair::new();
let recipient = solana_sdk::pubkey::new_rand(); let recipient = solana_sdk::pubkey::new_rand();
let transaction = system_transaction::transfer(&from, &recipient, 42, Hash::default()); let transaction = system_transaction::transfer(&from, &recipient, 42, Hash::default());
let with_meta = TransactionWithStatusMeta { let with_meta = TransactionWithStatusMeta::Complete(VersionedTransactionWithStatusMeta {
transaction, transaction: VersionedTransaction::from(transaction),
meta: Some(TransactionStatusMeta { meta: TransactionStatusMeta {
status: Ok(()), status: Ok(()),
fee: 1, fee: 1,
pre_balances: vec![43, 0, 1], pre_balances: vec![43, 0, 1],
@@ -819,9 +842,9 @@ mod tests {
post_token_balances: Some(vec![]), post_token_balances: Some(vec![]),
rewards: Some(vec![]), rewards: Some(vec![]),
loaded_addresses: LoadedAddresses::default(), loaded_addresses: LoadedAddresses::default(),
}), },
}; });
let block = ConfirmedBlock { let expected_block = ConfirmedBlock {
transactions: vec![with_meta], transactions: vec![with_meta],
parent_slot: 1, parent_slot: 1,
blockhash: Hash::default().to_string(), blockhash: Hash::default().to_string(),
@@ -831,11 +854,11 @@ mod tests {
block_height: Some(1), block_height: Some(1),
}; };
let bincode_block = compress_best( let bincode_block = compress_best(
&bincode::serialize::<StoredConfirmedBlock>(&block.clone().into()).unwrap(), &bincode::serialize::<StoredConfirmedBlock>(&expected_block.clone().into()).unwrap(),
) )
.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()); let mut buf = Vec::with_capacity(protobuf_block.encoded_len());
protobuf_block.encode(&mut buf).unwrap(); protobuf_block.encode(&mut buf).unwrap();
let protobuf_block = compress_best(&buf).unwrap(); let protobuf_block = compress_best(&buf).unwrap();
@@ -849,7 +872,6 @@ mod tests {
"".to_string(), "".to_string(),
) )
.unwrap(); .unwrap();
let expected_block: VersionedConfirmedBlock = block.into();
if let CellData::Protobuf(protobuf_block) = deserialized { if let CellData::Protobuf(protobuf_block) = deserialized {
assert_eq!(expected_block, protobuf_block.try_into().unwrap()); assert_eq!(expected_block, protobuf_block.try_into().unwrap());
} else { } else {
@@ -867,7 +889,11 @@ mod tests {
.unwrap(); .unwrap();
if let CellData::Bincode(bincode_block) = deserialized { if let CellData::Bincode(bincode_block) = deserialized {
let mut block = expected_block; 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.inner_instructions = None; // Legacy bincode implementation does not support inner_instructions
meta.log_messages = None; // Legacy bincode implementation does not support log_messages 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 meta.pre_token_balances = None; // Legacy bincode implementation does not support token balances

View File

@@ -14,10 +14,10 @@ use {
}, },
solana_storage_proto::convert::{generated, tx_by_addr}, solana_storage_proto::convert::{generated, tx_by_addr},
solana_transaction_status::{ solana_transaction_status::{
extract_and_fmt_memos, ConfirmedBlock, ConfirmedTransactionStatusWithSignature, Reward, extract_and_fmt_memos, ConfirmedBlock, ConfirmedTransactionStatusWithSignature,
TransactionByAddrInfo, TransactionConfirmationStatus, TransactionStatus, ConfirmedTransactionWithStatusMeta, Reward, TransactionByAddrInfo,
TransactionStatusMeta, TransactionWithStatusMeta, VersionedConfirmedBlock, TransactionConfirmationStatus, TransactionStatus, TransactionStatusMeta,
VersionedConfirmedTransactionWithStatusMeta, VersionedTransactionWithStatusMeta, TransactionWithStatusMeta, VersionedConfirmedBlock, VersionedTransactionWithStatusMeta,
}, },
std::{ std::{
collections::{HashMap, HashSet}, collections::{HashMap, HashSet},
@@ -115,6 +115,7 @@ struct StoredConfirmedBlock {
block_height: Option<u64>, block_height: Option<u64>,
} }
#[cfg(test)]
impl From<ConfirmedBlock> for StoredConfirmedBlock { impl From<ConfirmedBlock> for StoredConfirmedBlock {
fn from(confirmed_block: ConfirmedBlock) -> Self { fn from(confirmed_block: ConfirmedBlock) -> Self {
let ConfirmedBlock { let ConfirmedBlock {
@@ -139,7 +140,7 @@ impl From<ConfirmedBlock> for StoredConfirmedBlock {
} }
} }
impl From<StoredConfirmedBlock> for VersionedConfirmedBlock { impl From<StoredConfirmedBlock> for ConfirmedBlock {
fn from(confirmed_block: StoredConfirmedBlock) -> Self { fn from(confirmed_block: StoredConfirmedBlock) -> Self {
let StoredConfirmedBlock { let StoredConfirmedBlock {
previous_blockhash, previous_blockhash,
@@ -169,20 +170,38 @@ struct StoredConfirmedBlockTransaction {
meta: Option<StoredConfirmedBlockTransactionStatusMeta>, meta: Option<StoredConfirmedBlockTransactionStatusMeta>,
} }
#[cfg(test)]
impl From<TransactionWithStatusMeta> for StoredConfirmedBlockTransaction { impl From<TransactionWithStatusMeta> for StoredConfirmedBlockTransaction {
fn from(value: TransactionWithStatusMeta) -> Self { fn from(value: TransactionWithStatusMeta) -> Self {
Self { match value {
transaction: value.transaction.into(), TransactionWithStatusMeta::MissingMetadata(transaction) => Self {
meta: value.meta.map(|meta| meta.into()), transaction: VersionedTransaction::from(transaction),
meta: None,
},
TransactionWithStatusMeta::Complete(VersionedTransactionWithStatusMeta {
transaction,
meta,
}) => Self {
transaction,
meta: Some(meta.into()),
},
} }
} }
} }
impl From<StoredConfirmedBlockTransaction> for VersionedTransactionWithStatusMeta { impl From<StoredConfirmedBlockTransaction> for TransactionWithStatusMeta {
fn from(value: StoredConfirmedBlockTransaction) -> Self { fn from(tx_with_meta: StoredConfirmedBlockTransaction) -> Self {
Self { let StoredConfirmedBlockTransaction { transaction, meta } = tx_with_meta;
transaction: value.transaction, match meta {
meta: value.meta.map(|meta| meta.into()), 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 /// Fetch the confirmed block from the desired slot
pub async fn get_confirmed_block(&self, slot: Slot) -> Result<VersionedConfirmedBlock> { pub async fn get_confirmed_block(&self, slot: Slot) -> Result<ConfirmedBlock> {
debug!( debug!(
"LedgerStorage::get_confirmed_block request received: {:?}", "LedgerStorage::get_confirmed_block request received: {:?}",
slot slot
@@ -440,7 +459,7 @@ impl LedgerStorage {
pub async fn get_confirmed_transaction( pub async fn get_confirmed_transaction(
&self, &self,
signature: &Signature, signature: &Signature,
) -> Result<Option<VersionedConfirmedTransactionWithStatusMeta>> { ) -> Result<Option<ConfirmedTransactionWithStatusMeta>> {
debug!( debug!(
"LedgerStorage::get_confirmed_transaction request received: {:?}", "LedgerStorage::get_confirmed_transaction request received: {:?}",
signature signature
@@ -465,17 +484,17 @@ impl LedgerStorage {
warn!("Transaction info for {} is corrupt", signature); warn!("Transaction info for {} is corrupt", signature);
Ok(None) Ok(None)
} }
Some(bucket_block_transaction) => { Some(tx_with_meta) => {
if bucket_block_transaction.transaction.signatures[0] != *signature { if tx_with_meta.transaction_signature() != signature {
warn!( warn!(
"Transaction info or confirmed block for {} is corrupt", "Transaction info or confirmed block for {} is corrupt",
signature signature
); );
Ok(None) Ok(None)
} else { } else {
Ok(Some(VersionedConfirmedTransactionWithStatusMeta { Ok(Some(ConfirmedTransactionWithStatusMeta {
slot, slot,
tx_with_meta: bucket_block_transaction, tx_with_meta,
block_time: block.block_time, block_time: block.block_time,
})) }))
} }
@@ -638,7 +657,7 @@ impl LedgerStorage {
let mut tx_cells = vec![]; let mut tx_cells = vec![];
for (index, transaction_with_meta) in confirmed_block.transactions.iter().enumerate() { for (index, transaction_with_meta) in confirmed_block.transactions.iter().enumerate() {
let VersionedTransactionWithStatusMeta { meta, transaction } = transaction_with_meta; 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 index = index as u32;
let signature = transaction.signatures[0]; let signature = transaction.signatures[0];
let memo = extract_and_fmt_memos(transaction_with_meta); let memo = extract_and_fmt_memos(transaction_with_meta);
@@ -725,21 +744,41 @@ impl LedgerStorage {
let mut expected_tx_infos: HashMap<String, UploadedTransaction> = HashMap::new(); let mut expected_tx_infos: HashMap<String, UploadedTransaction> = HashMap::new();
let confirmed_block = self.get_confirmed_block(slot).await?; let confirmed_block = self.get_confirmed_block(slot).await?;
for (index, transaction_with_meta) in confirmed_block.transactions.iter().enumerate() { for (index, transaction_with_meta) in confirmed_block.transactions.iter().enumerate() {
let VersionedTransactionWithStatusMeta { transaction, meta } = transaction_with_meta; match transaction_with_meta {
let signature = transaction.signatures[0]; TransactionWithStatusMeta::MissingMetadata(transaction) => {
let index = index as u32; let signature = transaction.signatures[0];
let err = meta.as_ref().and_then(|meta| meta.status.clone().err()); let index = index as u32;
let err = None;
for address in transaction_with_meta.account_keys().iter() { for address in transaction.message.account_keys.iter() {
if !is_sysvar_id(address) { if !is_sysvar_id(address) {
addresses.insert(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 let address_slot_rows: Vec<_> = addresses

View File

@@ -140,31 +140,7 @@ impl From<VersionedConfirmedBlock> for generated::ConfirmedBlock {
} }
} }
impl From<ConfirmedBlock> for generated::ConfirmedBlock { impl TryFrom<generated::ConfirmedBlock> for 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<generated::ConfirmedBlock> for VersionedConfirmedBlock {
type Error = bincode::Error; type Error = bincode::Error;
fn try_from( fn try_from(
confirmed_block: generated::ConfirmedBlock, confirmed_block: generated::ConfirmedBlock,
@@ -195,32 +171,38 @@ impl TryFrom<generated::ConfirmedBlock> for VersionedConfirmedBlock {
} }
impl From<TransactionWithStatusMeta> for generated::ConfirmedTransaction { impl From<TransactionWithStatusMeta> for generated::ConfirmedTransaction {
fn from(value: TransactionWithStatusMeta) -> Self { fn from(tx_with_meta: TransactionWithStatusMeta) -> Self {
let meta = value.meta.map(|meta| meta.into()); match tx_with_meta {
Self { TransactionWithStatusMeta::MissingMetadata(transaction) => Self {
transaction: Some(value.transaction.into()), transaction: Some(generated::Transaction::from(transaction)),
meta, meta: None,
},
TransactionWithStatusMeta::Complete(tx_with_meta) => Self::from(tx_with_meta),
} }
} }
} }
impl From<VersionedTransactionWithStatusMeta> for generated::ConfirmedTransaction { impl From<VersionedTransactionWithStatusMeta> for generated::ConfirmedTransaction {
fn from(value: VersionedTransactionWithStatusMeta) -> Self { fn from(value: VersionedTransactionWithStatusMeta) -> Self {
let meta = value.meta.map(|meta| meta.into());
Self { Self {
transaction: Some(value.transaction.into()), transaction: Some(value.transaction.into()),
meta, meta: Some(value.meta.into()),
} }
} }
} }
impl TryFrom<generated::ConfirmedTransaction> for VersionedTransactionWithStatusMeta { impl TryFrom<generated::ConfirmedTransaction> for TransactionWithStatusMeta {
type Error = bincode::Error; type Error = bincode::Error;
fn try_from(value: generated::ConfirmedTransaction) -> std::result::Result<Self, Self::Error> { fn try_from(value: generated::ConfirmedTransaction) -> std::result::Result<Self, Self::Error> {
let meta = value.meta.map(|meta| meta.try_into()).transpose()?; let meta = value.meta.map(|meta| meta.try_into()).transpose()?;
Ok(Self { let transaction = value.transaction.expect("transaction is required").into();
transaction: value.transaction.expect("transaction is required").into(), Ok(match meta {
meta, Some(meta) => Self::Complete(VersionedTransactionWithStatusMeta { transaction, meta }),
None => Self::MissingMetadata(
transaction
.into_legacy_transaction()
.expect("meta is required for versioned transactions"),
),
}) })
} }
} }

View File

@@ -388,7 +388,7 @@ pub struct Reward {
pub type Rewards = Vec<Reward>; pub type Rewards = Vec<Reward>;
#[derive(Clone, Debug, Default, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub struct ConfirmedBlock { pub struct ConfirmedBlock {
pub previous_blockhash: String, pub previous_blockhash: String,
pub blockhash: String, pub blockhash: String,
@@ -399,7 +399,9 @@ pub struct ConfirmedBlock {
pub block_height: Option<u64>, pub block_height: Option<u64>,
} }
#[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 struct VersionedConfirmedBlock {
pub previous_blockhash: String, pub previous_blockhash: String,
pub blockhash: String, pub blockhash: String,
@@ -410,11 +412,23 @@ pub struct VersionedConfirmedBlock {
pub block_height: Option<u64>, pub block_height: Option<u64>,
} }
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<LegacyTransactionWithStatusMeta>,
pub rewards: Rewards,
pub block_time: Option<UnixTimestamp>,
pub block_height: Option<u64>,
}
impl ConfirmedBlock {
/// Downgrades a versioned block into a legacy block type /// Downgrades a versioned block into a legacy block type
/// if it only contains legacy transactions /// if it only contains legacy transactions
pub fn into_legacy_block(self) -> Option<ConfirmedBlock> { pub fn into_legacy_block(self) -> Option<LegacyConfirmedBlock> {
Some(ConfirmedBlock { Some(LegacyConfirmedBlock {
previous_blockhash: self.previous_blockhash, previous_blockhash: self.previous_blockhash,
blockhash: self.blockhash, blockhash: self.blockhash,
parent_slot: self.parent_slot, parent_slot: self.parent_slot,
@@ -430,13 +444,17 @@ impl VersionedConfirmedBlock {
} }
} }
impl From<ConfirmedBlock> for VersionedConfirmedBlock { impl From<VersionedConfirmedBlock> for ConfirmedBlock {
fn from(block: ConfirmedBlock) -> Self { fn from(block: VersionedConfirmedBlock) -> Self {
VersionedConfirmedBlock { Self {
previous_blockhash: block.previous_blockhash, previous_blockhash: block.previous_blockhash,
blockhash: block.blockhash, blockhash: block.blockhash,
parent_slot: block.parent_slot, 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, rewards: block.rewards,
block_time: block.block_time, block_time: block.block_time,
block_height: block.block_height, block_height: block.block_height,
@@ -444,7 +462,7 @@ impl From<ConfirmedBlock> for VersionedConfirmedBlock {
} }
} }
impl Encodable for ConfirmedBlock { impl Encodable for LegacyConfirmedBlock {
type Encoded = EncodedConfirmedBlock; type Encoded = EncodedConfirmedBlock;
fn encode(self, encoding: UiTransactionEncoding) -> Self::Encoded { fn encode(self, encoding: UiTransactionEncoding) -> Self::Encoded {
Self::Encoded { Self::Encoded {
@@ -463,7 +481,7 @@ impl Encodable for ConfirmedBlock {
} }
} }
impl ConfirmedBlock { impl LegacyConfirmedBlock {
pub fn configure( pub fn configure(
self, self,
encoding: UiTransactionEncoding, encoding: UiTransactionEncoding,
@@ -565,44 +583,68 @@ impl From<EncodedConfirmedBlock> 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)] #[derive(Clone, Debug, PartialEq)]
pub struct VersionedTransactionWithStatusMeta { pub struct VersionedTransactionWithStatusMeta {
pub transaction: VersionedTransaction, pub transaction: VersionedTransaction,
pub meta: TransactionStatusMeta,
}
pub struct LegacyTransactionWithStatusMeta {
pub transaction: Transaction,
pub meta: Option<TransactionStatusMeta>, pub meta: Option<TransactionStatusMeta>,
} }
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<LegacyTransactionWithStatusMeta> {
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 { impl VersionedTransactionWithStatusMeta {
pub fn account_keys(&self) -> AccountKeys { pub fn account_keys(&self) -> AccountKeys {
AccountKeys::new( AccountKeys::new(
self.transaction.message.static_account_keys(), 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<TransactionWithStatusMeta> { pub fn into_legacy_transaction_with_meta(self) -> Option<LegacyTransactionWithStatusMeta> {
Some(TransactionWithStatusMeta { Some(LegacyTransactionWithStatusMeta {
transaction: self.transaction.into_legacy_transaction()?, transaction: self.transaction.into_legacy_transaction()?,
meta: self.meta, meta: Some(self.meta),
}) })
} }
} }
impl From<TransactionWithStatusMeta> for VersionedTransactionWithStatusMeta { impl Encodable for LegacyTransactionWithStatusMeta {
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<TransactionStatusMeta>,
}
impl Encodable for TransactionWithStatusMeta {
type Encoded = EncodedTransactionWithStatusMeta; type Encoded = EncodedTransactionWithStatusMeta;
fn encode(self, encoding: UiTransactionEncoding) -> Self::Encoded { fn encode(self, encoding: UiTransactionEncoding) -> Self::Encoded {
Self::Encoded { Self::Encoded {
@@ -627,21 +669,10 @@ pub struct EncodedTransactionWithStatusMeta {
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct ConfirmedTransactionWithStatusMeta { pub struct ConfirmedTransactionWithStatusMeta {
pub slot: Slot, pub slot: Slot,
pub transaction: TransactionWithStatusMeta, pub tx_with_meta: TransactionWithStatusMeta,
pub block_time: Option<UnixTimestamp>, pub block_time: Option<UnixTimestamp>,
} }
impl Encodable for ConfirmedTransactionWithStatusMeta {
type Encoded = EncodedConfirmedTransactionWithStatusMeta;
fn encode(self, encoding: UiTransactionEncoding) -> Self::Encoded {
Self::Encoded {
slot: self.slot,
transaction: self.transaction.encode(encoding),
block_time: self.block_time,
}
}
}
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct VersionedConfirmedTransactionWithStatusMeta { pub struct VersionedConfirmedTransactionWithStatusMeta {
pub slot: Slot, pub slot: Slot,
@@ -649,15 +680,31 @@ pub struct VersionedConfirmedTransactionWithStatusMeta {
pub block_time: Option<UnixTimestamp>, pub block_time: Option<UnixTimestamp>,
} }
impl VersionedConfirmedTransactionWithStatusMeta { pub struct LegacyConfirmedTransactionWithStatusMeta {
pub slot: Slot,
pub tx_with_meta: LegacyTransactionWithStatusMeta,
pub block_time: Option<UnixTimestamp>,
}
impl Encodable for LegacyConfirmedTransactionWithStatusMeta {
type Encoded = EncodedConfirmedTransactionWithStatusMeta;
fn encode(self, encoding: UiTransactionEncoding) -> Self::Encoded {
Self::Encoded {
slot: self.slot,
transaction: self.tx_with_meta.encode(encoding),
block_time: self.block_time,
}
}
}
impl ConfirmedTransactionWithStatusMeta {
/// Downgrades a versioned confirmed transaction into a legacy /// Downgrades a versioned confirmed transaction into a legacy
/// confirmed transaction if it contains a legacy transaction. /// confirmed transaction if it contains a legacy transaction.
pub fn into_legacy_confirmed_transaction(self) -> Option<ConfirmedTransactionWithStatusMeta> { pub fn into_legacy_confirmed_transaction(
Some(ConfirmedTransactionWithStatusMeta { self,
transaction: TransactionWithStatusMeta { ) -> Option<LegacyConfirmedTransactionWithStatusMeta> {
transaction: self.tx_with_meta.transaction.into_legacy_transaction()?, Some(LegacyConfirmedTransactionWithStatusMeta {
meta: self.tx_with_meta.meta, tx_with_meta: self.tx_with_meta.into_legacy_transaction_with_meta()?,
},
block_time: self.block_time, block_time: self.block_time,
slot: self.slot, slot: self.slot,
}) })