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
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 482 additions and 440 deletions

View File

@ -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) => {

View File

@ -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()

View File

@ -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();
}

View File

@ -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<Slot>,
allow_missing_metadata: bool,
force_reupload: bool,
) -> Result<(), Box<dyn std::error::Error>> {
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<dyn st
.await
.map_err(|err| format!("Failed to connect to storage: {:?}", err))?;
let versioned_block = bigtable.get_confirmed_block(slot).await?;
let block = versioned_block
let confirmed_block = bigtable.get_confirmed_block(slot).await?;
let legacy_block = confirmed_block
.into_legacy_block()
.ok_or_else(|| "Failed to read versioned transaction in block".to_string())?;
let cli_block = CliBlock {
encoded_confirmed_block: block.encode(UiTransactionEncoding::Base64),
encoded_confirmed_block: legacy_block.encode(UiTransactionEncoding::Base64),
slot,
};
println!("{}", output_format.formatted_string(&cli_block));
@ -156,20 +154,20 @@ async fn confirm(
let mut get_transaction_error = None;
if verbose {
match bigtable.get_confirmed_transaction(signature).await {
Ok(Some(versioned_confirmed_tx)) => {
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<dyn std::error::Error>> {
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,
))
}

View File

@ -26,7 +26,6 @@ pub async fn upload_confirmed_blocks(
bigtable: solana_storage_bigtable::LedgerStorage,
starting_slot: Slot,
ending_slot: Option<Slot>,
allow_missing_metadata: bool,
force_reupload: bool,
exit: Arc<AtomicBool>,
) -> Result<(), Box<dyn std::error::Error>> {
@ -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 {

View File

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

View File

@ -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<Option<VersionedConfirmedTransactionWithStatusMeta>> {
) -> Result<Option<ConfirmedTransactionWithStatusMeta>> {
datapoint_info!(
"blockstore-rpc-api",
("method", "get_rooted_transaction", String)
@ -2307,7 +2306,7 @@ impl Blockstore {
&self,
signature: Signature,
highest_confirmed_slot: Slot,
) -> Result<Option<VersionedConfirmedTransactionWithStatusMeta>> {
) -> Result<Option<ConfirmedTransactionWithStatusMeta>> {
datapoint_info!(
"blockstore-rpc-api",
("method", "get_complete_transaction", String)
@ -2324,8 +2323,8 @@ impl Blockstore {
&self,
signature: Signature,
confirmed_unrooted_slots: &[Slot],
) -> Result<Option<VersionedConfirmedTransactionWithStatusMeta>> {
if let Some((slot, status)) =
) -> Result<Option<ConfirmedTransactionWithStatusMeta>> {
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]

View File

@ -102,6 +102,7 @@ pub enum BlockstoreError {
ParentEntriesUnavailable,
SlotUnavailable,
UnsupportedTransactionVersion,
MissingTransactionMetadata,
}
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;
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));

View File

@ -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
)),
..
}),
},
..
},
}),
..
})
));

View File

@ -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<T, BlockstoreError>,
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<T, BlockstoreError>,
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<T>(
&self,
result: &std::result::Result<T, solana_storage_bigtable::Error>,
) -> 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<UiConfirmedBlock> {
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<EncodedConfirmedTransactionWithStatusMeta> {
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<EncodedConfirmedBlock> =
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<EncodedConfirmedBlock> =
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

View File

@ -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<RpcBlockUpdate> {
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::<serde_json::Value>(&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::<serde_json::Value>(&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::<serde_json::Value>(&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,

View File

@ -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::<StoredConfirmedBlock>(&block.clone().into()).unwrap(),
&bincode::serialize::<StoredConfirmedBlock>(&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

View File

@ -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<u64>,
}
#[cfg(test)]
impl From<ConfirmedBlock> for StoredConfirmedBlock {
fn from(confirmed_block: ConfirmedBlock) -> Self {
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 {
let StoredConfirmedBlock {
previous_blockhash,
@ -169,20 +170,38 @@ struct StoredConfirmedBlockTransaction {
meta: Option<StoredConfirmedBlockTransactionStatusMeta>,
}
#[cfg(test)]
impl From<TransactionWithStatusMeta> 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<StoredConfirmedBlockTransaction> for VersionedTransactionWithStatusMeta {
fn from(value: StoredConfirmedBlockTransaction) -> Self {
Self {
transaction: value.transaction,
meta: value.meta.map(|meta| meta.into()),
impl From<StoredConfirmedBlockTransaction> 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<VersionedConfirmedBlock> {
pub async fn get_confirmed_block(&self, slot: Slot) -> Result<ConfirmedBlock> {
debug!(
"LedgerStorage::get_confirmed_block request received: {:?}",
slot
@ -440,7 +459,7 @@ impl LedgerStorage {
pub async fn get_confirmed_transaction(
&self,
signature: &Signature,
) -> Result<Option<VersionedConfirmedTransactionWithStatusMeta>> {
) -> Result<Option<ConfirmedTransactionWithStatusMeta>> {
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<String, UploadedTransaction> = 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_with_meta.account_keys().iter() {
if !is_sysvar_id(address) {
addresses.insert(address);
for address in transaction.message.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

View File

@ -140,31 +140,7 @@ impl From<VersionedConfirmedBlock> for generated::ConfirmedBlock {
}
}
impl From<ConfirmedBlock> 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<generated::ConfirmedBlock> for VersionedConfirmedBlock {
impl TryFrom<generated::ConfirmedBlock> for ConfirmedBlock {
type Error = bincode::Error;
fn try_from(
confirmed_block: generated::ConfirmedBlock,
@ -195,32 +171,38 @@ impl TryFrom<generated::ConfirmedBlock> for VersionedConfirmedBlock {
}
impl From<TransactionWithStatusMeta> 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<VersionedTransactionWithStatusMeta> 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<generated::ConfirmedTransaction> for VersionedTransactionWithStatusMeta {
impl TryFrom<generated::ConfirmedTransaction> for TransactionWithStatusMeta {
type Error = bincode::Error;
fn try_from(value: generated::ConfirmedTransaction) -> std::result::Result<Self, Self::Error> {
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"),
),
})
}
}

View File

@ -388,7 +388,7 @@ pub struct Reward {
pub type Rewards = Vec<Reward>;
#[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<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 previous_blockhash: String,
pub blockhash: String,
@ -410,11 +412,23 @@ pub struct VersionedConfirmedBlock {
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
/// if it only contains legacy transactions
pub fn into_legacy_block(self) -> Option<ConfirmedBlock> {
Some(ConfirmedBlock {
pub fn into_legacy_block(self) -> Option<LegacyConfirmedBlock> {
Some(LegacyConfirmedBlock {
previous_blockhash: self.previous_blockhash,
blockhash: self.blockhash,
parent_slot: self.parent_slot,
@ -430,13 +444,17 @@ impl VersionedConfirmedBlock {
}
}
impl From<ConfirmedBlock> for VersionedConfirmedBlock {
fn from(block: ConfirmedBlock) -> Self {
VersionedConfirmedBlock {
impl From<VersionedConfirmedBlock> 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<ConfirmedBlock> 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<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)]
pub struct VersionedTransactionWithStatusMeta {
pub transaction: VersionedTransaction,
pub meta: TransactionStatusMeta,
}
pub struct LegacyTransactionWithStatusMeta {
pub transaction: Transaction,
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 {
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<TransactionWithStatusMeta> {
Some(TransactionWithStatusMeta {
pub fn into_legacy_transaction_with_meta(self) -> Option<LegacyTransactionWithStatusMeta> {
Some(LegacyTransactionWithStatusMeta {
transaction: self.transaction.into_legacy_transaction()?,
meta: self.meta,
meta: Some(self.meta),
})
}
}
impl From<TransactionWithStatusMeta> 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<TransactionStatusMeta>,
}
impl Encodable for TransactionWithStatusMeta {
impl Encodable for LegacyTransactionWithStatusMeta {
type Encoded = EncodedTransactionWithStatusMeta;
fn encode(self, encoding: UiTransactionEncoding) -> Self::Encoded {
Self::Encoded {
@ -627,21 +669,10 @@ 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<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)]
pub struct VersionedConfirmedTransactionWithStatusMeta {
pub slot: Slot,
@ -649,15 +680,31 @@ pub struct VersionedConfirmedTransactionWithStatusMeta {
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
/// confirmed transaction if it contains a legacy transaction.
pub fn into_legacy_confirmed_transaction(self) -> Option<ConfirmedTransactionWithStatusMeta> {
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<LegacyConfirmedTransactionWithStatusMeta> {
Some(LegacyConfirmedTransactionWithStatusMeta {
tx_with_meta: self.tx_with_meta.into_legacy_transaction_with_meta()?,
block_time: self.block_time,
slot: self.slot,
})