v1.9: Enforce tx metadata upload to bigtable (#23212)
* Enforce tx metadata upload with static types (#23028) * resolve conflicts * fix test
This commit is contained in:
@ -8,7 +8,7 @@ use {
|
||||
RpcAccountInfoConfig, RpcBlockSubscribeConfig, RpcBlockSubscribeFilter,
|
||||
RpcProgramAccountsConfig,
|
||||
},
|
||||
rpc_response::{RpcBlockUpdate, SlotInfo},
|
||||
rpc_response::SlotInfo,
|
||||
},
|
||||
solana_ledger::{blockstore::Blockstore, get_tmp_ledger_path},
|
||||
solana_rpc::{
|
||||
@ -34,7 +34,9 @@ use {
|
||||
},
|
||||
solana_streamer::socket::SocketAddrSpace,
|
||||
solana_test_validator::TestValidator,
|
||||
solana_transaction_status::{TransactionDetails, UiTransactionEncoding},
|
||||
solana_transaction_status::{
|
||||
ConfirmedBlockWithOptionalMetadata, TransactionDetails, UiTransactionEncoding,
|
||||
},
|
||||
std::{
|
||||
collections::HashSet,
|
||||
net::{IpAddr, SocketAddr},
|
||||
@ -212,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
|
||||
@ -225,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,
|
||||
@ -276,22 +281,12 @@ fn test_block_subscription() {
|
||||
match maybe_actual {
|
||||
Ok(actual) => {
|
||||
let complete_block = blockstore.get_complete_block(slot, false).unwrap();
|
||||
let block = complete_block.clone().configure(
|
||||
let block = ConfirmedBlockWithOptionalMetadata::from(complete_block).configure(
|
||||
UiTransactionEncoding::Json,
|
||||
TransactionDetails::Signatures,
|
||||
false,
|
||||
);
|
||||
let expected = RpcBlockUpdate {
|
||||
slot,
|
||||
block: Some(block),
|
||||
err: None,
|
||||
};
|
||||
let block = complete_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) => {
|
||||
|
@ -2067,7 +2067,7 @@ mod tests {
|
||||
transaction::{Transaction, TransactionError},
|
||||
},
|
||||
solana_streamer::{recvmmsg::recv_mmsg, socket::SocketAddrSpace},
|
||||
solana_transaction_status::TransactionWithStatusMeta,
|
||||
solana_transaction_status::TransactionWithMetadata,
|
||||
solana_vote_program::vote_transaction,
|
||||
std::{
|
||||
net::SocketAddr,
|
||||
@ -3223,39 +3223,23 @@ mod tests {
|
||||
genesis_config.rent.lamports_per_byte_year = 50;
|
||||
genesis_config.rent.exemption_threshold = 2.0;
|
||||
let bank = Arc::new(Bank::new_no_wallclock_throttle_for_tests(&genesis_config));
|
||||
let rent_exempt_minimum = bank.get_minimum_balance_for_rent_exemption(0);
|
||||
let pubkey = solana_sdk::pubkey::new_rand();
|
||||
let pubkey1 = solana_sdk::pubkey::new_rand();
|
||||
let keypair1 = Keypair::new();
|
||||
|
||||
let rent_exempt_amount = bank.get_minimum_balance_for_rent_exemption(0);
|
||||
|
||||
let success_tx = system_transaction::transfer(
|
||||
&mint_keypair,
|
||||
&pubkey,
|
||||
rent_exempt_amount,
|
||||
genesis_config.hash(),
|
||||
);
|
||||
let success_tx =
|
||||
system_transaction::transfer(&mint_keypair, &pubkey, 0, genesis_config.hash());
|
||||
let success_signature = success_tx.signatures[0];
|
||||
let entry_1 = next_entry(&genesis_config.hash(), 1, vec![success_tx.clone()]);
|
||||
let ix_error_tx = system_transaction::transfer(
|
||||
&keypair1,
|
||||
&pubkey1,
|
||||
2 * rent_exempt_amount,
|
||||
genesis_config.hash(),
|
||||
);
|
||||
let ix_error_tx =
|
||||
system_transaction::transfer(&keypair1, &pubkey1, std::u64::MAX, genesis_config.hash());
|
||||
let ix_error_signature = ix_error_tx.signatures[0];
|
||||
let entry_2 = next_entry(&entry_1.hash, 1, vec![ix_error_tx.clone()]);
|
||||
let fail_tx = system_transaction::transfer(
|
||||
&mint_keypair,
|
||||
&pubkey1,
|
||||
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]);
|
||||
bank.transfer(rent_exempt_amount, &mint_keypair, &keypair1.pubkey())
|
||||
let transactions = sanitize_transactions(vec![success_tx, ix_error_tx]);
|
||||
bank.transfer(rent_exempt_minimum, &mint_keypair, &keypair1.pubkey())
|
||||
.unwrap();
|
||||
|
||||
let ledger_path = get_tmp_ledger_path_auto_delete!();
|
||||
@ -3314,27 +3298,24 @@ mod tests {
|
||||
transaction_status_service.join().unwrap();
|
||||
|
||||
let confirmed_block = blockstore.get_rooted_block(bank.slot(), false).unwrap();
|
||||
assert_eq!(confirmed_block.transactions.len(), 3);
|
||||
|
||||
for TransactionWithStatusMeta { transaction, meta } in
|
||||
confirmed_block.transactions.into_iter()
|
||||
{
|
||||
if transaction.signatures[0] == success_signature {
|
||||
let meta = meta.unwrap();
|
||||
assert_eq!(meta.status, Ok(()));
|
||||
} else if transaction.signatures[0] == ix_error_signature {
|
||||
let meta = meta.unwrap();
|
||||
assert_eq!(
|
||||
meta.status,
|
||||
Err(TransactionError::InstructionError(
|
||||
0,
|
||||
InstructionError::Custom(1)
|
||||
))
|
||||
);
|
||||
} else {
|
||||
assert_eq!(meta, None);
|
||||
}
|
||||
}
|
||||
let actual_tx_results: Vec<_> = confirmed_block
|
||||
.transactions
|
||||
.into_iter()
|
||||
.map(|TransactionWithMetadata { transaction, meta }| {
|
||||
(transaction.signatures[0], meta.status)
|
||||
})
|
||||
.collect();
|
||||
let expected_tx_results = vec![
|
||||
(success_signature, Ok(())),
|
||||
(
|
||||
ix_error_signature,
|
||||
Err(TransactionError::InstructionError(
|
||||
0,
|
||||
InstructionError::Custom(1),
|
||||
)),
|
||||
),
|
||||
];
|
||||
assert_eq!(actual_tx_results, expected_tx_results);
|
||||
|
||||
poh_recorder
|
||||
.lock()
|
||||
|
@ -3021,7 +3021,7 @@ pub mod tests {
|
||||
transaction::TransactionError,
|
||||
},
|
||||
solana_streamer::socket::SocketAddrSpace,
|
||||
solana_transaction_status::TransactionWithStatusMeta,
|
||||
solana_transaction_status::TransactionWithMetadata,
|
||||
solana_vote_program::{
|
||||
vote_state::{VoteState, VoteStateVersions},
|
||||
vote_transaction,
|
||||
@ -3871,36 +3871,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 TransactionWithStatusMeta { transaction, meta } in
|
||||
confirmed_block.transactions.into_iter()
|
||||
{
|
||||
if transaction.signatures[0] == signatures[0] {
|
||||
let meta = meta.unwrap();
|
||||
assert_eq!(meta.status, Ok(()));
|
||||
} else if transaction.signatures[0] == signatures[1] {
|
||||
let meta = meta.unwrap();
|
||||
assert_eq!(
|
||||
meta.status,
|
||||
Err(TransactionError::InstructionError(
|
||||
0,
|
||||
InstructionError::Custom(1)
|
||||
))
|
||||
);
|
||||
} else {
|
||||
assert_eq!(meta, None);
|
||||
}
|
||||
}
|
||||
let actual_tx_results: Vec<_> = confirmed_block
|
||||
.transactions
|
||||
.into_iter()
|
||||
.map(|TransactionWithMetadata { 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();
|
||||
}
|
||||
|
@ -16,7 +16,9 @@ use {
|
||||
},
|
||||
solana_ledger::{blockstore::Blockstore, blockstore_db::AccessType},
|
||||
solana_sdk::{clock::Slot, pubkey::Pubkey, signature::Signature},
|
||||
solana_transaction_status::{ConfirmedBlock, EncodedTransaction, UiTransactionEncoding},
|
||||
solana_transaction_status::{
|
||||
ConfirmedBlockWithOptionalMetadata, EncodedTransaction, UiTransactionEncoding,
|
||||
},
|
||||
std::{
|
||||
collections::HashSet,
|
||||
path::Path,
|
||||
@ -30,7 +32,6 @@ async fn upload(
|
||||
blockstore: Blockstore,
|
||||
starting_slot: Slot,
|
||||
ending_slot: Option<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 +43,6 @@ async fn upload(
|
||||
bigtable,
|
||||
starting_slot,
|
||||
ending_slot,
|
||||
allow_missing_metadata,
|
||||
force_reupload,
|
||||
Arc::new(AtomicBool::new(false)),
|
||||
)
|
||||
@ -194,7 +194,7 @@ pub async fn transaction_history(
|
||||
) -> Result<(), Box<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, ConfirmedBlockWithOptionalMetadata)> = None;
|
||||
while limit > 0 {
|
||||
let results = bigtable
|
||||
.get_confirmed_signatures_for_address(
|
||||
@ -306,12 +306,6 @@ impl BigTableSubCommand for App<'_, '_> {
|
||||
.index(2)
|
||||
.help("Stop uploading at this slot [default: last available slot]"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("allow_missing_metadata")
|
||||
.long("allow-missing-metadata")
|
||||
.takes_value(false)
|
||||
.help("Don't panic if transaction metadata is missing"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("force_reupload")
|
||||
.long("force")
|
||||
@ -506,7 +500,6 @@ pub fn bigtable_process_command(ledger_path: &Path, matches: &ArgMatches<'_>) {
|
||||
("upload", Some(arg_matches)) => {
|
||||
let starting_slot = value_t!(arg_matches, "starting_slot", Slot).unwrap_or(0);
|
||||
let ending_slot = value_t!(arg_matches, "ending_slot", Slot).ok();
|
||||
let allow_missing_metadata = arg_matches.is_present("allow_missing_metadata");
|
||||
let force_reupload = arg_matches.is_present("force_reupload");
|
||||
let blockstore = crate::open_blockstore(
|
||||
&canonicalize_ledger_path(ledger_path),
|
||||
@ -518,7 +511,6 @@ pub fn bigtable_process_command(ledger_path: &Path, matches: &ArgMatches<'_>) {
|
||||
blockstore,
|
||||
starting_slot,
|
||||
ending_slot,
|
||||
allow_missing_metadata,
|
||||
force_reupload,
|
||||
))
|
||||
}
|
||||
|
@ -25,7 +25,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>> {
|
||||
@ -186,20 +185,7 @@ pub async fn upload_confirmed_blocks(
|
||||
num_blocks -= 1;
|
||||
None
|
||||
}
|
||||
Some(confirmed_block) => {
|
||||
if confirmed_block
|
||||
.transactions
|
||||
.iter()
|
||||
.any(|transaction| transaction.meta.is_none())
|
||||
{
|
||||
if allow_missing_metadata {
|
||||
info!("Transaction metadata missing from slot {}", slot);
|
||||
} else {
|
||||
panic!("Transaction metadata missing from slot {}", slot);
|
||||
}
|
||||
}
|
||||
Some(bigtable.upload_confirmed_block(slot, confirmed_block))
|
||||
}
|
||||
Some(confirmed_block) => Some(bigtable.upload_confirmed_block(slot, confirmed_block)),
|
||||
});
|
||||
|
||||
for result in futures::future::join_all(uploads).await {
|
||||
|
@ -72,7 +72,6 @@ impl BigTableUploadService {
|
||||
bigtable_ledger_storage.clone(),
|
||||
start_slot,
|
||||
Some(end_slot),
|
||||
true,
|
||||
false,
|
||||
exit.clone(),
|
||||
));
|
||||
|
@ -42,7 +42,7 @@ use {
|
||||
solana_storage_proto::{StoredExtendedRewards, StoredTransactionStatusMeta},
|
||||
solana_transaction_status::{
|
||||
ConfirmedBlock, ConfirmedTransaction, ConfirmedTransactionStatusWithSignature, Rewards,
|
||||
TransactionStatusMeta, TransactionWithStatusMeta,
|
||||
TransactionStatusMeta, TransactionWithMetadata,
|
||||
},
|
||||
std::{
|
||||
borrow::Cow,
|
||||
@ -1945,18 +1945,17 @@ impl Blockstore {
|
||||
&self,
|
||||
slot: Slot,
|
||||
iterator: impl Iterator<Item = VersionedTransaction>,
|
||||
) -> Result<Vec<TransactionWithStatusMeta>> {
|
||||
) -> Result<Vec<TransactionWithMetadata>> {
|
||||
iterator
|
||||
.map(|versioned_tx| {
|
||||
// TODO: add support for versioned transactions
|
||||
if let Some(transaction) = versioned_tx.into_legacy_transaction() {
|
||||
let signature = transaction.signatures[0];
|
||||
Ok(TransactionWithStatusMeta {
|
||||
Ok(TransactionWithMetadata {
|
||||
transaction,
|
||||
meta: self
|
||||
.read_transaction_status((signature, slot))
|
||||
.ok()
|
||||
.flatten(),
|
||||
.read_transaction_status((signature, slot))?
|
||||
.ok_or(BlockstoreError::MissingTransactionMetadata)?,
|
||||
})
|
||||
} else {
|
||||
Err(BlockstoreError::UnsupportedTransactionVersion)
|
||||
@ -2243,7 +2242,7 @@ impl Blockstore {
|
||||
signature: Signature,
|
||||
confirmed_unrooted_slots: &[Slot],
|
||||
) -> Result<Option<ConfirmedTransaction>> {
|
||||
if let Some((slot, status)) =
|
||||
if let Some((slot, meta)) =
|
||||
self.get_transaction_status(signature, confirmed_unrooted_slots)?
|
||||
{
|
||||
let transaction = self
|
||||
@ -2258,10 +2257,7 @@ impl Blockstore {
|
||||
let block_time = self.get_block_time(slot)?;
|
||||
Ok(Some(ConfirmedTransaction {
|
||||
slot,
|
||||
transaction: TransactionWithStatusMeta {
|
||||
transaction,
|
||||
meta: Some(status),
|
||||
},
|
||||
transaction: TransactionWithMetadata { transaction, meta },
|
||||
block_time,
|
||||
}))
|
||||
} else {
|
||||
@ -6052,7 +6048,7 @@ pub mod tests {
|
||||
.put_meta_bytes(slot - 1, &serialize(&parent_meta).unwrap())
|
||||
.unwrap();
|
||||
|
||||
let expected_transactions: Vec<TransactionWithStatusMeta> = entries
|
||||
let expected_transactions: Vec<TransactionWithMetadata> = entries
|
||||
.iter()
|
||||
.cloned()
|
||||
.filter(|entry| !entry.is_tick())
|
||||
@ -6118,9 +6114,9 @@ pub mod tests {
|
||||
.transaction_status_cf
|
||||
.put_protobuf((0, signature, slot + 2), &status)
|
||||
.unwrap();
|
||||
TransactionWithStatusMeta {
|
||||
TransactionWithMetadata {
|
||||
transaction,
|
||||
meta: Some(TransactionStatusMeta {
|
||||
meta: TransactionStatusMeta {
|
||||
status: Ok(()),
|
||||
fee: 42,
|
||||
pre_balances,
|
||||
@ -6130,21 +6126,22 @@ pub mod tests {
|
||||
pre_token_balances: Some(vec![]),
|
||||
post_token_balances: Some(vec![]),
|
||||
rewards: Some(vec![]),
|
||||
}),
|
||||
},
|
||||
}
|
||||
})
|
||||
.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
|
||||
@ -6908,7 +6905,7 @@ pub mod tests {
|
||||
blockstore.insert_shreds(shreds, None, false).unwrap();
|
||||
blockstore.set_roots(vec![slot - 1, slot].iter()).unwrap();
|
||||
|
||||
let expected_transactions: Vec<TransactionWithStatusMeta> = entries
|
||||
let expected_transactions: Vec<TransactionWithMetadata> = entries
|
||||
.iter()
|
||||
.cloned()
|
||||
.filter(|entry| !entry.is_tick())
|
||||
@ -6949,9 +6946,9 @@ pub mod tests {
|
||||
.transaction_status_cf
|
||||
.put_protobuf((0, signature, slot), &status)
|
||||
.unwrap();
|
||||
TransactionWithStatusMeta {
|
||||
TransactionWithMetadata {
|
||||
transaction,
|
||||
meta: Some(TransactionStatusMeta {
|
||||
meta: TransactionStatusMeta {
|
||||
status: Ok(()),
|
||||
fee: 42,
|
||||
pre_balances,
|
||||
@ -6961,7 +6958,7 @@ pub mod tests {
|
||||
pre_token_balances,
|
||||
post_token_balances,
|
||||
rewards,
|
||||
}),
|
||||
},
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
@ -6990,7 +6987,7 @@ pub mod tests {
|
||||
|
||||
blockstore.run_purge(0, 2, PurgeType::PrimaryIndex).unwrap();
|
||||
*blockstore.lowest_cleanup_slot.write().unwrap() = slot;
|
||||
for TransactionWithStatusMeta { transaction, .. } in expected_transactions {
|
||||
for TransactionWithMetadata { transaction, .. } in expected_transactions {
|
||||
let signature = transaction.signatures[0];
|
||||
assert_eq!(blockstore.get_rooted_transaction(signature).unwrap(), None,);
|
||||
assert_eq!(
|
||||
@ -7012,7 +7009,7 @@ pub mod tests {
|
||||
let shreds = entries_to_test_shreds(entries.clone(), slot, slot - 1, true, 0);
|
||||
blockstore.insert_shreds(shreds, None, false).unwrap();
|
||||
|
||||
let expected_transactions: Vec<TransactionWithStatusMeta> = entries
|
||||
let expected_transactions: Vec<TransactionWithMetadata> = entries
|
||||
.iter()
|
||||
.cloned()
|
||||
.filter(|entry| !entry.is_tick())
|
||||
@ -7053,9 +7050,9 @@ pub mod tests {
|
||||
.transaction_status_cf
|
||||
.put_protobuf((0, signature, slot), &status)
|
||||
.unwrap();
|
||||
TransactionWithStatusMeta {
|
||||
TransactionWithMetadata {
|
||||
transaction,
|
||||
meta: Some(TransactionStatusMeta {
|
||||
meta: TransactionStatusMeta {
|
||||
status: Ok(()),
|
||||
fee: 42,
|
||||
pre_balances,
|
||||
@ -7065,7 +7062,7 @@ pub mod tests {
|
||||
pre_token_balances,
|
||||
post_token_balances,
|
||||
rewards,
|
||||
}),
|
||||
},
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
@ -7087,7 +7084,7 @@ pub mod tests {
|
||||
|
||||
blockstore.run_purge(0, 2, PurgeType::PrimaryIndex).unwrap();
|
||||
*blockstore.lowest_cleanup_slot.write().unwrap() = slot;
|
||||
for TransactionWithStatusMeta { transaction, .. } in expected_transactions {
|
||||
for TransactionWithMetadata { transaction, .. } in expected_transactions {
|
||||
let signature = transaction.signatures[0];
|
||||
assert_eq!(
|
||||
blockstore
|
||||
@ -7809,7 +7806,6 @@ pub mod tests {
|
||||
fn test_map_transactions_to_statuses() {
|
||||
let ledger_path = get_tmp_ledger_path_auto_delete!();
|
||||
let blockstore = Blockstore::open(ledger_path.path()).unwrap();
|
||||
|
||||
let transaction_status_cf = &blockstore.transaction_status_cf;
|
||||
|
||||
let slot = 0;
|
||||
@ -7841,6 +7837,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(
|
||||
@ -7854,13 +7860,7 @@ pub mod tests {
|
||||
);
|
||||
|
||||
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);
|
||||
assert_matches!(map_result, Err(BlockstoreError::MissingTransactionMetadata));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -102,6 +102,7 @@ pub enum BlockstoreError {
|
||||
ParentEntriesUnavailable,
|
||||
SlotUnavailable,
|
||||
UnsupportedTransactionVersion,
|
||||
MissingTransactionMetadata,
|
||||
}
|
||||
pub type Result<T> = std::result::Result<T, BlockstoreError>;
|
||||
|
||||
|
@ -877,46 +877,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));
|
||||
|
@ -61,7 +61,8 @@ use solana_sdk::{
|
||||
};
|
||||
use solana_transaction_status::{
|
||||
token_balances::collect_token_balances, ConfirmedTransaction, InnerInstructions,
|
||||
TransactionStatusMeta, TransactionWithStatusMeta,
|
||||
TransactionStatusMeta,
|
||||
TransactionWithMetadata,
|
||||
};
|
||||
use std::{collections::HashMap, env, fs::File, io::Read, path::PathBuf, str::FromStr, sync::Arc};
|
||||
|
||||
@ -420,9 +421,9 @@ fn execute_transactions(
|
||||
|
||||
Ok(ConfirmedTransaction {
|
||||
slot: bank.slot(),
|
||||
transaction: TransactionWithStatusMeta {
|
||||
transaction: TransactionWithMetadata {
|
||||
transaction: tx.clone(),
|
||||
meta: Some(tx_status_meta),
|
||||
meta: tx_status_meta,
|
||||
},
|
||||
block_time: None,
|
||||
})
|
||||
@ -2466,8 +2467,8 @@ fn test_program_upgradeable_locks() {
|
||||
assert!(matches!(
|
||||
results1[0],
|
||||
Ok(ConfirmedTransaction {
|
||||
transaction: TransactionWithStatusMeta {
|
||||
meta: Some(TransactionStatusMeta { status: Ok(()), .. }),
|
||||
transaction: TransactionWithMetadata {
|
||||
meta: TransactionStatusMeta { status: Ok(()), .. },
|
||||
..
|
||||
},
|
||||
..
|
||||
@ -2478,14 +2479,14 @@ fn test_program_upgradeable_locks() {
|
||||
assert!(matches!(
|
||||
results2[0],
|
||||
Ok(ConfirmedTransaction {
|
||||
transaction: TransactionWithStatusMeta {
|
||||
meta: Some(TransactionStatusMeta {
|
||||
transaction: TransactionWithMetadata {
|
||||
meta: TransactionStatusMeta {
|
||||
status: Err(TransactionError::InstructionError(
|
||||
0,
|
||||
InstructionError::ProgramFailedToComplete
|
||||
)),
|
||||
..
|
||||
}),
|
||||
},
|
||||
..
|
||||
},
|
||||
..
|
||||
|
109
rpc/src/rpc.rs
109
rpc/src/rpc.rs
@ -76,9 +76,9 @@ use {
|
||||
solana_storage_bigtable::Error as StorageError,
|
||||
solana_streamer::socket::SocketAddrSpace,
|
||||
solana_transaction_status::{
|
||||
ConfirmedBlock, ConfirmedTransactionStatusWithSignature, EncodedConfirmedTransaction,
|
||||
Reward, RewardType, TransactionConfirmationStatus, TransactionStatus, UiConfirmedBlock,
|
||||
UiTransactionEncoding,
|
||||
ConfirmedBlockWithOptionalMetadata, ConfirmedTransactionStatusWithSignature,
|
||||
ConfirmedTransactionWithOptionalMetadata, EncodedConfirmedTransaction, Reward, RewardType,
|
||||
TransactionConfirmationStatus, TransactionStatus, UiConfirmedBlock, UiTransactionEncoding,
|
||||
},
|
||||
solana_vote_program::vote_state::{VoteState, MAX_LOCKOUT_HISTORY},
|
||||
spl_token::{
|
||||
@ -914,12 +914,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,
|
||||
@ -940,21 +936,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(())
|
||||
}
|
||||
@ -962,15 +953,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(())
|
||||
}
|
||||
@ -1013,7 +998,7 @@ 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 = |confirmed_block: ConfirmedBlock| {
|
||||
let configure_block = |confirmed_block: ConfirmedBlockWithOptionalMetadata| {
|
||||
let mut confirmed_block =
|
||||
confirmed_block.configure(encoding, transaction_details, show_rewards);
|
||||
if slot == 0 {
|
||||
@ -1031,7 +1016,10 @@ impl JsonRpcRequestProcessor {
|
||||
}
|
||||
}
|
||||
self.check_slot_cleaned_up(&result, slot)?;
|
||||
return Ok(result.ok().map(configure_block));
|
||||
return Ok(result
|
||||
.ok()
|
||||
.map(ConfirmedBlockWithOptionalMetadata::from)
|
||||
.map(configure_block));
|
||||
} else if commitment.is_confirmed() {
|
||||
// Check if block is confirmed
|
||||
let confirmed_bank = self.bank(Some(CommitmentConfig::confirmed()));
|
||||
@ -1053,7 +1041,11 @@ impl JsonRpcRequestProcessor {
|
||||
}
|
||||
}
|
||||
}
|
||||
confirmed_block.configure(encoding, transaction_details, show_rewards)
|
||||
ConfirmedBlockWithOptionalMetadata::from(confirmed_block).configure(
|
||||
encoding,
|
||||
transaction_details,
|
||||
show_rewards,
|
||||
)
|
||||
}));
|
||||
}
|
||||
}
|
||||
@ -1399,7 +1391,10 @@ impl JsonRpcRequestProcessor {
|
||||
.get(confirmed_transaction.slot)
|
||||
.map(|bank| bank.clock().unix_timestamp);
|
||||
}
|
||||
return Ok(Some(confirmed_transaction.encode(encoding)));
|
||||
return Ok(Some(
|
||||
ConfirmedTransactionWithOptionalMetadata::from(confirmed_transaction)
|
||||
.encode(encoding),
|
||||
));
|
||||
}
|
||||
if confirmed_transaction.slot
|
||||
<= self
|
||||
@ -1408,7 +1403,10 @@ impl JsonRpcRequestProcessor {
|
||||
.unwrap()
|
||||
.highest_confirmed_root()
|
||||
{
|
||||
return Ok(Some(confirmed_transaction.encode(encoding)));
|
||||
return Ok(Some(
|
||||
ConfirmedTransactionWithOptionalMetadata::from(confirmed_transaction)
|
||||
.encode(encoding),
|
||||
));
|
||||
}
|
||||
}
|
||||
None => {
|
||||
@ -4315,15 +4313,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.clone(),
|
||||
@ -4349,17 +4339,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();
|
||||
@ -6605,7 +6598,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
|
||||
@ -6650,7 +6643,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
|
||||
|
@ -39,7 +39,7 @@ use {
|
||||
timing::timestamp,
|
||||
transaction,
|
||||
},
|
||||
solana_transaction_status::ConfirmedBlock,
|
||||
solana_transaction_status::{ConfirmedBlock, ConfirmedBlockWithOptionalMetadata},
|
||||
solana_vote_program::vote_state::Vote,
|
||||
std::{
|
||||
cell::RefCell,
|
||||
@ -278,30 +278,26 @@ impl RpcNotifier {
|
||||
}
|
||||
|
||||
fn filter_block_result_txs(
|
||||
block: ConfirmedBlock,
|
||||
mut block: ConfirmedBlock,
|
||||
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 = ConfirmedBlockWithOptionalMetadata::from(block).configure(
|
||||
params.encoding,
|
||||
params.transaction_details,
|
||||
params.show_rewards,
|
||||
@ -964,12 +960,13 @@ impl RpcSubscriptions {
|
||||
}
|
||||
match blockstore.get_complete_block(s, false) {
|
||||
Ok(block) => {
|
||||
if let Some(res) = filter_block_result_txs(block, s, params)
|
||||
if let Some(block_update) =
|
||||
filter_block_result_txs(block, s, params)
|
||||
{
|
||||
notifier.notify(
|
||||
Response {
|
||||
context: RpcResponseContext { slot: s },
|
||||
value: res,
|
||||
value: block_update,
|
||||
},
|
||||
subscription,
|
||||
false,
|
||||
@ -1346,8 +1343,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);
|
||||
@ -1388,10 +1390,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(),
|
||||
@ -1404,7 +1407,11 @@ pub(crate) mod tests {
|
||||
let actual_resp = serde_json::from_str::<serde_json::Value>(&actual_resp).unwrap();
|
||||
|
||||
let block = blockstore.get_complete_block(slot, false).unwrap();
|
||||
let block = block.configure(params.encoding, params.transaction_details, false);
|
||||
let block = ConfirmedBlockWithOptionalMetadata::from(block).configure(
|
||||
params.encoding,
|
||||
params.transaction_details,
|
||||
false,
|
||||
);
|
||||
let expected_resp = RpcBlockUpdate {
|
||||
slot,
|
||||
block: Some(block),
|
||||
@ -1443,8 +1450,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);
|
||||
@ -1486,10 +1498,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(),
|
||||
@ -1509,7 +1522,11 @@ pub(crate) mod tests {
|
||||
.account_keys
|
||||
.contains(&keypair1.pubkey())
|
||||
});
|
||||
let block = block.configure(params.encoding, params.transaction_details, false);
|
||||
let block = ConfirmedBlockWithOptionalMetadata::from(block).configure(
|
||||
params.encoding,
|
||||
params.transaction_details,
|
||||
false,
|
||||
);
|
||||
let expected_resp = RpcBlockUpdate {
|
||||
slot,
|
||||
block: Some(block),
|
||||
@ -1538,8 +1555,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);
|
||||
@ -1579,10 +1601,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(),
|
||||
@ -1600,7 +1623,11 @@ pub(crate) mod tests {
|
||||
let actual_resp = serde_json::from_str::<serde_json::Value>(&actual_resp).unwrap();
|
||||
|
||||
let block = blockstore.get_complete_block(slot, false).unwrap();
|
||||
let block = block.configure(params.encoding, params.transaction_details, false);
|
||||
let block = ConfirmedBlockWithOptionalMetadata::from(block).configure(
|
||||
params.encoding,
|
||||
params.transaction_details,
|
||||
false,
|
||||
);
|
||||
let expected_resp = RpcBlockUpdate {
|
||||
slot,
|
||||
block: Some(block),
|
||||
|
@ -793,19 +793,42 @@ mod tests {
|
||||
solana_sdk::{hash::Hash, signature::Keypair, system_transaction},
|
||||
solana_storage_proto::convert::generated,
|
||||
solana_transaction_status::{
|
||||
ConfirmedBlock, TransactionStatusMeta, TransactionWithStatusMeta,
|
||||
ConfirmedBlock, ConfirmedBlockWithOptionalMetadata, TransactionStatusMeta,
|
||||
TransactionWithMetadata,
|
||||
},
|
||||
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(Into::into).collect(),
|
||||
rewards: rewards.into_iter().map(|r| r.into()).collect(),
|
||||
block_time: block_time.map(|timestamp| generated::UnixTimestamp { timestamp }),
|
||||
block_height: block_height.map(|block_height| generated::BlockHeight { block_height }),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_deserialize_protobuf_or_bincode_cell_data() {
|
||||
let from = Keypair::new();
|
||||
let recipient = solana_sdk::pubkey::new_rand();
|
||||
let transaction = system_transaction::transfer(&from, &recipient, 42, Hash::default());
|
||||
let with_meta = TransactionWithStatusMeta {
|
||||
let with_meta = TransactionWithMetadata {
|
||||
transaction,
|
||||
meta: Some(TransactionStatusMeta {
|
||||
meta: TransactionStatusMeta {
|
||||
status: Ok(()),
|
||||
fee: 1,
|
||||
pre_balances: vec![43, 0, 1],
|
||||
@ -815,9 +838,9 @@ mod tests {
|
||||
pre_token_balances: Some(vec![]),
|
||||
post_token_balances: Some(vec![]),
|
||||
rewards: Some(vec![]),
|
||||
}),
|
||||
},
|
||||
};
|
||||
let block = ConfirmedBlock {
|
||||
let expected_block = ConfirmedBlock {
|
||||
transactions: vec![with_meta],
|
||||
parent_slot: 1,
|
||||
blockhash: Hash::default().to_string(),
|
||||
@ -827,11 +850,14 @@ mod tests {
|
||||
block_height: Some(1),
|
||||
};
|
||||
let bincode_block = compress_best(
|
||||
&bincode::serialize::<StoredConfirmedBlock>(&block.clone().into()).unwrap(),
|
||||
&bincode::serialize::<StoredConfirmedBlock>(
|
||||
&ConfirmedBlockWithOptionalMetadata::from(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();
|
||||
@ -846,7 +872,10 @@ mod tests {
|
||||
)
|
||||
.unwrap();
|
||||
if let CellData::Protobuf(protobuf_block) = deserialized {
|
||||
assert_eq!(block, protobuf_block.try_into().unwrap());
|
||||
assert_eq!(
|
||||
ConfirmedBlockWithOptionalMetadata::from(expected_block.clone()),
|
||||
protobuf_block.try_into().unwrap(),
|
||||
);
|
||||
} else {
|
||||
panic!("deserialization should produce CellData::Protobuf");
|
||||
}
|
||||
@ -861,15 +890,19 @@ mod tests {
|
||||
)
|
||||
.unwrap();
|
||||
if let CellData::Bincode(bincode_block) = deserialized {
|
||||
let mut block = block;
|
||||
if let Some(meta) = &mut block.transactions[0].meta {
|
||||
let mut block = expected_block;
|
||||
{
|
||||
let mut meta = &mut block.transactions[0].meta;
|
||||
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
|
||||
meta.post_token_balances = None; // Legacy bincode implementation does not support token balances
|
||||
meta.rewards = None; // Legacy bincode implementation does not support rewards
|
||||
}
|
||||
assert_eq!(block, bincode_block.into());
|
||||
assert_eq!(
|
||||
ConfirmedBlockWithOptionalMetadata::from(block),
|
||||
ConfirmedBlockWithOptionalMetadata::from(bincode_block)
|
||||
);
|
||||
} else {
|
||||
panic!("deserialization should produce CellData::Bincode");
|
||||
}
|
||||
|
@ -13,10 +13,10 @@ use {
|
||||
},
|
||||
solana_storage_proto::convert::{generated, tx_by_addr},
|
||||
solana_transaction_status::{
|
||||
extract_and_fmt_memos, ConfirmedBlock, ConfirmedTransaction,
|
||||
ConfirmedTransactionStatusWithSignature, Reward, TransactionByAddrInfo,
|
||||
TransactionConfirmationStatus, TransactionStatus, TransactionStatusMeta,
|
||||
TransactionWithStatusMeta,
|
||||
extract_and_fmt_memos, ConfirmedBlock, ConfirmedBlockWithOptionalMetadata,
|
||||
ConfirmedTransactionStatusWithSignature, ConfirmedTransactionWithOptionalMetadata, Reward,
|
||||
TransactionByAddrInfo, TransactionConfirmationStatus, TransactionStatus,
|
||||
TransactionStatusMeta, TransactionWithMetadata, TransactionWithOptionalMetadata,
|
||||
},
|
||||
std::{
|
||||
collections::{HashMap, HashSet},
|
||||
@ -114,9 +114,10 @@ struct StoredConfirmedBlock {
|
||||
block_height: Option<u64>,
|
||||
}
|
||||
|
||||
impl From<ConfirmedBlock> for StoredConfirmedBlock {
|
||||
fn from(confirmed_block: ConfirmedBlock) -> Self {
|
||||
let ConfirmedBlock {
|
||||
#[cfg(test)]
|
||||
impl From<ConfirmedBlockWithOptionalMetadata> for StoredConfirmedBlock {
|
||||
fn from(confirmed_block: ConfirmedBlockWithOptionalMetadata) -> Self {
|
||||
let ConfirmedBlockWithOptionalMetadata {
|
||||
previous_blockhash,
|
||||
blockhash,
|
||||
parent_slot,
|
||||
@ -138,7 +139,7 @@ impl From<ConfirmedBlock> for StoredConfirmedBlock {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<StoredConfirmedBlock> for ConfirmedBlock {
|
||||
impl From<StoredConfirmedBlock> for ConfirmedBlockWithOptionalMetadata {
|
||||
fn from(confirmed_block: StoredConfirmedBlock) -> Self {
|
||||
let StoredConfirmedBlock {
|
||||
previous_blockhash,
|
||||
@ -168,8 +169,9 @@ struct StoredConfirmedBlockTransaction {
|
||||
meta: Option<StoredConfirmedBlockTransactionStatusMeta>,
|
||||
}
|
||||
|
||||
impl From<TransactionWithStatusMeta> for StoredConfirmedBlockTransaction {
|
||||
fn from(value: TransactionWithStatusMeta) -> Self {
|
||||
#[cfg(test)]
|
||||
impl From<TransactionWithOptionalMetadata> for StoredConfirmedBlockTransaction {
|
||||
fn from(value: TransactionWithOptionalMetadata) -> Self {
|
||||
Self {
|
||||
transaction: value.transaction,
|
||||
meta: value.meta.map(|meta| meta.into()),
|
||||
@ -177,7 +179,7 @@ impl From<TransactionWithStatusMeta> for StoredConfirmedBlockTransaction {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<StoredConfirmedBlockTransaction> for TransactionWithStatusMeta {
|
||||
impl From<StoredConfirmedBlockTransaction> for TransactionWithOptionalMetadata {
|
||||
fn from(value: StoredConfirmedBlockTransaction) -> Self {
|
||||
Self {
|
||||
transaction: value.transaction,
|
||||
@ -392,7 +394,10 @@ impl LedgerStorage {
|
||||
}
|
||||
|
||||
/// Fetch the confirmed block from the desired slot
|
||||
pub async fn get_confirmed_block(&self, slot: Slot) -> Result<ConfirmedBlock> {
|
||||
pub async fn get_confirmed_block(
|
||||
&self,
|
||||
slot: Slot,
|
||||
) -> Result<ConfirmedBlockWithOptionalMetadata> {
|
||||
debug!(
|
||||
"LedgerStorage::get_confirmed_block request received: {:?}",
|
||||
slot
|
||||
@ -438,7 +443,7 @@ impl LedgerStorage {
|
||||
pub async fn get_confirmed_transaction(
|
||||
&self,
|
||||
signature: &Signature,
|
||||
) -> Result<Option<ConfirmedTransaction>> {
|
||||
) -> Result<Option<ConfirmedTransactionWithOptionalMetadata>> {
|
||||
debug!(
|
||||
"LedgerStorage::get_confirmed_transaction request received: {:?}",
|
||||
signature
|
||||
@ -471,7 +476,7 @@ impl LedgerStorage {
|
||||
);
|
||||
Ok(None)
|
||||
} else {
|
||||
Ok(Some(ConfirmedTransaction {
|
||||
Ok(Some(ConfirmedTransactionWithOptionalMetadata {
|
||||
slot,
|
||||
transaction: bucket_block_transaction,
|
||||
block_time: block.block_time,
|
||||
@ -635,8 +640,8 @@ impl LedgerStorage {
|
||||
|
||||
let mut tx_cells = vec![];
|
||||
for (index, transaction_with_meta) in confirmed_block.transactions.iter().enumerate() {
|
||||
let TransactionWithStatusMeta { meta, transaction } = transaction_with_meta;
|
||||
let err = meta.as_ref().and_then(|meta| meta.status.clone().err());
|
||||
let TransactionWithMetadata { meta, transaction } = transaction_with_meta;
|
||||
let err = meta.status.clone().err();
|
||||
let index = index as u32;
|
||||
let signature = transaction.signatures[0];
|
||||
let memo = extract_and_fmt_memos(&transaction.message);
|
||||
@ -723,7 +728,7 @@ 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 TransactionWithStatusMeta { meta, transaction } = transaction_with_meta;
|
||||
let TransactionWithOptionalMetadata { meta, transaction } = transaction_with_meta;
|
||||
let signature = transaction.signatures[0];
|
||||
let index = index as u32;
|
||||
let err = meta.as_ref().and_then(|meta| meta.status.clone().err());
|
||||
|
@ -10,8 +10,9 @@ use {
|
||||
transaction::{Transaction, TransactionError},
|
||||
},
|
||||
solana_transaction_status::{
|
||||
ConfirmedBlock, InnerInstructions, Reward, RewardType, TransactionByAddrInfo,
|
||||
TransactionStatusMeta, TransactionTokenBalance, TransactionWithStatusMeta,
|
||||
ConfirmedBlock, ConfirmedBlockWithOptionalMetadata, InnerInstructions, Reward, RewardType,
|
||||
TransactionByAddrInfo, TransactionStatusMeta, TransactionTokenBalance,
|
||||
TransactionWithMetadata, TransactionWithOptionalMetadata,
|
||||
},
|
||||
std::{
|
||||
convert::{TryFrom, TryInto},
|
||||
@ -135,7 +136,7 @@ impl From<ConfirmedBlock> for generated::ConfirmedBlock {
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<generated::ConfirmedBlock> for ConfirmedBlock {
|
||||
impl TryFrom<generated::ConfirmedBlock> for ConfirmedBlockWithOptionalMetadata {
|
||||
type Error = bincode::Error;
|
||||
fn try_from(
|
||||
confirmed_block: generated::ConfirmedBlock,
|
||||
@ -157,7 +158,8 @@ impl TryFrom<generated::ConfirmedBlock> for ConfirmedBlock {
|
||||
transactions: transactions
|
||||
.into_iter()
|
||||
.map(|tx| tx.try_into())
|
||||
.collect::<std::result::Result<Vec<TransactionWithStatusMeta>, Self::Error>>()?,
|
||||
.collect::<std::result::Result<Vec<TransactionWithOptionalMetadata>, Self::Error>>(
|
||||
)?,
|
||||
rewards: rewards.into_iter().map(|r| r.into()).collect(),
|
||||
block_time: block_time.map(|generated::UnixTimestamp { timestamp }| timestamp),
|
||||
block_height: block_height.map(|generated::BlockHeight { block_height }| block_height),
|
||||
@ -165,17 +167,16 @@ impl TryFrom<generated::ConfirmedBlock> for ConfirmedBlock {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TransactionWithStatusMeta> for generated::ConfirmedTransaction {
|
||||
fn from(value: TransactionWithStatusMeta) -> Self {
|
||||
let meta = value.meta.map(|meta| meta.into());
|
||||
impl From<TransactionWithMetadata> for generated::ConfirmedTransaction {
|
||||
fn from(value: TransactionWithMetadata) -> Self {
|
||||
Self {
|
||||
transaction: Some(value.transaction.into()),
|
||||
meta,
|
||||
meta: Some(value.meta.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<generated::ConfirmedTransaction> for TransactionWithStatusMeta {
|
||||
impl TryFrom<generated::ConfirmedTransaction> for TransactionWithOptionalMetadata {
|
||||
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()?;
|
||||
|
@ -355,19 +355,43 @@ pub struct Reward {
|
||||
|
||||
pub type Rewards = Vec<Reward>;
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct ConfirmedBlock {
|
||||
pub previous_blockhash: String,
|
||||
pub blockhash: String,
|
||||
pub parent_slot: Slot,
|
||||
pub transactions: Vec<TransactionWithStatusMeta>,
|
||||
pub transactions: Vec<TransactionWithMetadata>,
|
||||
pub rewards: Rewards,
|
||||
pub block_time: Option<UnixTimestamp>,
|
||||
pub block_height: Option<u64>,
|
||||
}
|
||||
|
||||
impl ConfirmedBlock {
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct ConfirmedBlockWithOptionalMetadata {
|
||||
pub previous_blockhash: String,
|
||||
pub blockhash: String,
|
||||
pub parent_slot: Slot,
|
||||
pub transactions: Vec<TransactionWithOptionalMetadata>,
|
||||
pub rewards: Rewards,
|
||||
pub block_time: Option<UnixTimestamp>,
|
||||
pub block_height: Option<u64>,
|
||||
}
|
||||
|
||||
impl From<ConfirmedBlock> for ConfirmedBlockWithOptionalMetadata {
|
||||
fn from(block: ConfirmedBlock) -> Self {
|
||||
Self {
|
||||
previous_blockhash: block.previous_blockhash,
|
||||
blockhash: block.blockhash,
|
||||
parent_slot: block.parent_slot,
|
||||
transactions: block.transactions.into_iter().map(Into::into).collect(),
|
||||
rewards: block.rewards,
|
||||
block_time: block.block_time,
|
||||
block_height: block.block_height,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ConfirmedBlockWithOptionalMetadata {
|
||||
pub fn encode(self, encoding: UiTransactionEncoding) -> EncodedConfirmedBlock {
|
||||
EncodedConfirmedBlock {
|
||||
previous_blockhash: self.previous_blockhash,
|
||||
@ -499,16 +523,31 @@ impl Default for TransactionDetails {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct ConfirmedTransaction {
|
||||
pub slot: Slot,
|
||||
#[serde(flatten)]
|
||||
pub transaction: TransactionWithStatusMeta,
|
||||
pub transaction: TransactionWithMetadata,
|
||||
pub block_time: Option<UnixTimestamp>,
|
||||
}
|
||||
|
||||
impl ConfirmedTransaction {
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct ConfirmedTransactionWithOptionalMetadata {
|
||||
pub slot: Slot,
|
||||
pub transaction: TransactionWithOptionalMetadata,
|
||||
pub block_time: Option<UnixTimestamp>,
|
||||
}
|
||||
|
||||
impl From<ConfirmedTransaction> for ConfirmedTransactionWithOptionalMetadata {
|
||||
fn from(confirmed_tx: ConfirmedTransaction) -> Self {
|
||||
Self {
|
||||
slot: confirmed_tx.slot,
|
||||
transaction: confirmed_tx.transaction.into(),
|
||||
block_time: confirmed_tx.block_time,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ConfirmedTransactionWithOptionalMetadata {
|
||||
pub fn encode(self, encoding: UiTransactionEncoding) -> EncodedConfirmedTransaction {
|
||||
EncodedConfirmedTransaction {
|
||||
slot: self.slot,
|
||||
@ -561,14 +600,28 @@ pub struct UiParsedMessage {
|
||||
pub instructions: Vec<UiInstruction>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct TransactionWithStatusMeta {
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct TransactionWithMetadata {
|
||||
pub transaction: Transaction,
|
||||
pub meta: TransactionStatusMeta,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct TransactionWithOptionalMetadata {
|
||||
pub transaction: Transaction,
|
||||
pub meta: Option<TransactionStatusMeta>,
|
||||
}
|
||||
|
||||
impl TransactionWithStatusMeta {
|
||||
impl From<TransactionWithMetadata> for TransactionWithOptionalMetadata {
|
||||
fn from(tx_with_meta: TransactionWithMetadata) -> Self {
|
||||
Self {
|
||||
transaction: tx_with_meta.transaction,
|
||||
meta: Some(tx_with_meta.meta),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TransactionWithOptionalMetadata {
|
||||
fn encode(self, encoding: UiTransactionEncoding) -> EncodedTransactionWithStatusMeta {
|
||||
let message = self.transaction.message();
|
||||
let meta = self.meta.map(|meta| meta.encode(encoding, message));
|
||||
|
Reference in New Issue
Block a user