Store address table lookups in blockstore and bigtable (#22402)

This commit is contained in:
Justin Starry 2022-01-14 15:24:41 +08:00 committed by GitHub
parent 4c577d7f8c
commit f804ccdece
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 836 additions and 199 deletions

1
Cargo.lock generated
View File

@ -4848,6 +4848,7 @@ dependencies = [
"serde_json", "serde_json",
"serial_test", "serial_test",
"solana-accountsdb-plugin-manager", "solana-accountsdb-plugin-manager",
"solana-address-lookup-table-program",
"solana-client", "solana-client",
"solana-entry", "solana-entry",
"solana-frozen-abi 1.10.0", "solana-frozen-abi 1.10.0",

View File

@ -1034,6 +1034,10 @@ pub(crate) mod tests {
commission: Some(11), commission: Some(11),
}, },
]), ]),
loaded_addresses: LoadedAddresses {
writable: vec![Pubkey::new_unique()],
readonly: vec![Pubkey::new_unique()],
},
} }
} }

View File

@ -275,8 +275,9 @@ fn test_block_subscription() {
let maybe_actual = receiver.recv_timeout(Duration::from_millis(400)); let maybe_actual = receiver.recv_timeout(Duration::from_millis(400));
match maybe_actual { match maybe_actual {
Ok(actual) => { Ok(actual) => {
let complete_block = blockstore.get_complete_block(slot, false).unwrap(); let versioned_block = blockstore.get_complete_block(slot, false).unwrap();
let block = complete_block.clone().configure( let legacy_block = versioned_block.into_legacy_block().unwrap();
let block = legacy_block.clone().configure(
UiTransactionEncoding::Json, UiTransactionEncoding::Json,
TransactionDetails::Signatures, TransactionDetails::Signatures,
false, false,
@ -286,7 +287,7 @@ fn test_block_subscription() {
block: Some(block), block: Some(block),
err: None, err: None,
}; };
let block = complete_block.configure( let block = legacy_block.configure(
UiTransactionEncoding::Json, UiTransactionEncoding::Json,
TransactionDetails::Signatures, TransactionDetails::Signatures,
false, false,

View File

@ -20,6 +20,7 @@ pub const JSON_RPC_SERVER_ERROR_TRANSACTION_HISTORY_NOT_AVAILABLE: i64 = -32011;
pub const JSON_RPC_SCAN_ERROR: i64 = -32012; pub const JSON_RPC_SCAN_ERROR: i64 = -32012;
pub const JSON_RPC_SERVER_ERROR_TRANSACTION_SIGNATURE_LEN_MISMATCH: i64 = -32013; pub const JSON_RPC_SERVER_ERROR_TRANSACTION_SIGNATURE_LEN_MISMATCH: i64 = -32013;
pub const JSON_RPC_SERVER_ERROR_BLOCK_STATUS_NOT_AVAILABLE_YET: i64 = -32014; pub const JSON_RPC_SERVER_ERROR_BLOCK_STATUS_NOT_AVAILABLE_YET: i64 = -32014;
pub const JSON_RPC_SERVER_ERROR_UNSUPPORTED_TRANSACTION_VERSION: i64 = -32015;
#[derive(Error, Debug)] #[derive(Error, Debug)]
pub enum RpcCustomError { pub enum RpcCustomError {
@ -57,6 +58,8 @@ pub enum RpcCustomError {
TransactionSignatureLenMismatch, TransactionSignatureLenMismatch,
#[error("BlockStatusNotAvailableYet")] #[error("BlockStatusNotAvailableYet")]
BlockStatusNotAvailableYet { slot: Slot }, BlockStatusNotAvailableYet { slot: Slot },
#[error("UnsupportedTransactionVersion")]
UnsupportedTransactionVersion,
} }
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
@ -169,6 +172,11 @@ impl From<RpcCustomError> for Error {
message: format!("Block status not yet available for slot {}", slot), message: format!("Block status not yet available for slot {}", slot),
data: None, data: None,
}, },
RpcCustomError::UnsupportedTransactionVersion => Self {
code: ErrorCode::ServerError(JSON_RPC_SERVER_ERROR_UNSUPPORTED_TRANSACTION_VERSION),
message: "Versioned transactions are not supported".to_string(),
data: None,
},
} }
} }
} }

View File

@ -429,6 +429,9 @@ pub struct RpcInflationReward {
pub enum RpcBlockUpdateError { pub enum RpcBlockUpdateError {
#[error("block store error")] #[error("block store error")]
BlockStoreError, BlockStoreError,
#[error("unsupported transaction version")]
UnsupportedTransactionVersion,
} }
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]

View File

@ -33,6 +33,7 @@ rayon = "1.5.1"
retain_mut = "0.1.5" retain_mut = "0.1.5"
serde = "1.0.133" serde = "1.0.133"
serde_derive = "1.0.103" serde_derive = "1.0.103"
solana-address-lookup-table-program = { path = "../programs/address-lookup-table", version = "=1.10.0" }
solana-accountsdb-plugin-manager = { path = "../accountsdb-plugin-manager", version = "=1.10.0" } solana-accountsdb-plugin-manager = { path = "../accountsdb-plugin-manager", version = "=1.10.0" }
solana-client = { path = "../client", version = "=1.10.0" } solana-client = { path = "../client", version = "=1.10.0" }
solana-entry = { path = "../entry", version = "=1.10.0" } solana-entry = { path = "../entry", version = "=1.10.0" }

View File

@ -1477,7 +1477,8 @@ mod tests {
super::*, super::*,
crossbeam_channel::{unbounded, Receiver}, crossbeam_channel::{unbounded, Receiver},
itertools::Itertools, itertools::Itertools,
solana_entry::entry::{next_entry, Entry, EntrySlice}, solana_address_lookup_table_program::state::{AddressLookupTable, LookupTableMeta},
solana_entry::entry::{next_entry, next_versioned_entry, Entry, EntrySlice},
solana_gossip::{cluster_info::Node, contact_info::ContactInfo}, solana_gossip::{cluster_info::Node, contact_info::ContactInfo},
solana_ledger::{ solana_ledger::{
blockstore::{entries_to_test_shreds, Blockstore}, blockstore::{entries_to_test_shreds, Blockstore},
@ -1493,8 +1494,10 @@ mod tests {
solana_rpc::transaction_status_service::TransactionStatusService, solana_rpc::transaction_status_service::TransactionStatusService,
solana_runtime::bank::TransactionExecutionDetails, solana_runtime::bank::TransactionExecutionDetails,
solana_sdk::{ solana_sdk::{
account::AccountSharedData,
hash::Hash, hash::Hash,
instruction::InstructionError, instruction::InstructionError,
message::{v0, MessageHeader, VersionedMessage},
poh_config::PohConfig, poh_config::PohConfig,
signature::{Keypair, Signer}, signature::{Keypair, Signer},
system_instruction::SystemError, system_instruction::SystemError,
@ -1502,9 +1505,10 @@ mod tests {
transaction::{Transaction, TransactionError}, transaction::{Transaction, TransactionError},
}, },
solana_streamer::{recvmmsg::recv_mmsg, socket::SocketAddrSpace}, solana_streamer::{recvmmsg::recv_mmsg, socket::SocketAddrSpace},
solana_transaction_status::TransactionWithStatusMeta, solana_transaction_status::{TransactionStatusMeta, VersionedTransactionWithStatusMeta},
solana_vote_program::vote_transaction, solana_vote_program::vote_transaction,
std::{ std::{
borrow::Cow,
net::SocketAddr, net::SocketAddr,
path::Path, path::Path,
sync::atomic::{AtomicBool, Ordering}, sync::atomic::{AtomicBool, Ordering},
@ -2525,7 +2529,7 @@ mod tests {
let confirmed_block = blockstore.get_rooted_block(bank.slot(), false).unwrap(); let confirmed_block = blockstore.get_rooted_block(bank.slot(), false).unwrap();
assert_eq!(confirmed_block.transactions.len(), 3); assert_eq!(confirmed_block.transactions.len(), 3);
for TransactionWithStatusMeta { transaction, meta } in for VersionedTransactionWithStatusMeta { transaction, meta } in
confirmed_block.transactions.into_iter() confirmed_block.transactions.into_iter()
{ {
if transaction.signatures[0] == success_signature { if transaction.signatures[0] == success_signature {
@ -2555,6 +2559,165 @@ mod tests {
Blockstore::destroy(&ledger_path).unwrap(); Blockstore::destroy(&ledger_path).unwrap();
} }
fn generate_new_address_lookup_table(
authority: Option<Pubkey>,
num_addresses: usize,
) -> AddressLookupTable<'static> {
let mut addresses = Vec::with_capacity(num_addresses);
addresses.resize_with(num_addresses, Pubkey::new_unique);
AddressLookupTable {
meta: LookupTableMeta {
authority,
..LookupTableMeta::default()
},
addresses: Cow::Owned(addresses),
}
}
fn store_address_lookup_table(
bank: &Bank,
account_address: Pubkey,
address_lookup_table: AddressLookupTable<'static>,
) -> AccountSharedData {
let mut data = Vec::new();
address_lookup_table.serialize_for_tests(&mut data).unwrap();
let mut account =
AccountSharedData::new(1, data.len(), &solana_address_lookup_table_program::id());
account.set_data(data);
bank.store_account(&account_address, &account);
account
}
#[test]
fn test_write_persist_loaded_addresses() {
solana_logger::setup();
let GenesisConfigInfo {
genesis_config,
mint_keypair,
..
} = create_slow_genesis_config(10_000);
let bank = Arc::new(Bank::new_no_wallclock_throttle_for_tests(&genesis_config));
let keypair = Keypair::new();
let address_table_key = Pubkey::new_unique();
let address_table_state = generate_new_address_lookup_table(None, 2);
store_address_lookup_table(&bank, address_table_key, address_table_state);
let bank = Arc::new(Bank::new_from_parent(&bank, &Pubkey::new_unique(), 1));
let message = VersionedMessage::V0(v0::Message {
header: MessageHeader {
num_required_signatures: 1,
num_readonly_signed_accounts: 0,
num_readonly_unsigned_accounts: 0,
},
recent_blockhash: genesis_config.hash(),
account_keys: vec![keypair.pubkey()],
address_table_lookups: vec![MessageAddressTableLookup {
account_key: address_table_key,
writable_indexes: vec![0],
readonly_indexes: vec![1],
}],
instructions: vec![],
});
let tx = VersionedTransaction::try_new(message, &[&keypair]).unwrap();
let message_hash = tx.message.hash();
let sanitized_tx =
SanitizedTransaction::try_create(tx.clone(), message_hash, Some(false), |lookups| {
Ok(bank.load_lookup_table_addresses(lookups).unwrap())
})
.unwrap();
let entry = next_versioned_entry(&genesis_config.hash(), 1, vec![tx]);
let entries = vec![entry];
// todo: check if sig fees are needed
bank.transfer(1, &mint_keypair, &keypair.pubkey()).unwrap();
let ledger_path = get_tmp_ledger_path!();
{
let blockstore = Blockstore::open(&ledger_path)
.expect("Expected to be able to open database ledger");
let blockstore = Arc::new(blockstore);
let (poh_recorder, _entry_receiver, record_receiver) = PohRecorder::new(
bank.tick_height(),
bank.last_blockhash(),
bank.clone(),
Some((4, 4)),
bank.ticks_per_slot(),
&Pubkey::new_unique(),
&blockstore,
&Arc::new(LeaderScheduleCache::new_from_bank(&bank)),
&Arc::new(PohConfig::default()),
Arc::new(AtomicBool::default()),
);
let recorder = poh_recorder.recorder();
let poh_recorder = Arc::new(Mutex::new(poh_recorder));
let poh_simulator = simulate_poh(record_receiver, &poh_recorder);
poh_recorder.lock().unwrap().set_bank(&bank);
let shreds = entries_to_test_shreds(&entries, bank.slot(), 0, true, 0);
blockstore.insert_shreds(shreds, None, false).unwrap();
blockstore.set_roots(std::iter::once(&bank.slot())).unwrap();
let (transaction_status_sender, transaction_status_receiver) = unbounded();
let transaction_status_service = TransactionStatusService::new(
transaction_status_receiver,
Arc::new(AtomicU64::default()),
true,
None,
blockstore.clone(),
&Arc::new(AtomicBool::new(false)),
);
let (gossip_vote_sender, _gossip_vote_receiver) = unbounded();
let _ = BankingStage::process_and_record_transactions(
&bank,
&[sanitized_tx.clone()],
&recorder,
0,
Some(TransactionStatusSender {
sender: transaction_status_sender,
enable_cpi_and_log_storage: false,
}),
&gossip_vote_sender,
&QosService::new(Arc::new(RwLock::new(CostModel::default())), 1),
);
transaction_status_service.join().unwrap();
let mut confirmed_block = blockstore.get_rooted_block(bank.slot(), false).unwrap();
assert_eq!(confirmed_block.transactions.len(), 1);
let recorded_meta = confirmed_block.transactions.pop().unwrap().meta;
assert_eq!(
recorded_meta,
Some(TransactionStatusMeta {
status: Ok(()),
pre_balances: vec![1, 0, 0],
post_balances: vec![1, 0, 0],
pre_token_balances: Some(vec![]),
post_token_balances: Some(vec![]),
rewards: Some(vec![]),
loaded_addresses: sanitized_tx.get_loaded_addresses(),
..TransactionStatusMeta::default()
})
);
poh_recorder
.lock()
.unwrap()
.is_exited
.store(true, Ordering::Relaxed);
let _ = poh_simulator.join();
}
Blockstore::destroy(&ledger_path).unwrap();
}
#[allow(clippy::type_complexity)] #[allow(clippy::type_complexity)]
fn setup_conflicting_transactions( fn setup_conflicting_transactions(
ledger_path: &Path, ledger_path: &Path,

View File

@ -3003,7 +3003,7 @@ pub mod tests {
transaction::TransactionError, transaction::TransactionError,
}, },
solana_streamer::socket::SocketAddrSpace, solana_streamer::socket::SocketAddrSpace,
solana_transaction_status::TransactionWithStatusMeta, solana_transaction_status::VersionedTransactionWithStatusMeta,
solana_vote_program::{ solana_vote_program::{
vote_state::{VoteState, VoteStateVersions}, vote_state::{VoteState, VoteStateVersions},
vote_transaction, vote_transaction,
@ -3860,7 +3860,7 @@ pub mod tests {
let confirmed_block = blockstore.get_rooted_block(slot, false).unwrap(); let confirmed_block = blockstore.get_rooted_block(slot, false).unwrap();
assert_eq!(confirmed_block.transactions.len(), 3); assert_eq!(confirmed_block.transactions.len(), 3);
for TransactionWithStatusMeta { transaction, meta } in for VersionedTransactionWithStatusMeta { transaction, meta } in
confirmed_block.transactions.into_iter() confirmed_block.transactions.into_iter()
{ {
if transaction.signatures[0] == signatures[0] { if transaction.signatures[0] == signatures[0] {

View File

@ -898,8 +898,17 @@ pub fn create_random_ticks(num_ticks: u64, max_hashes_per_tick: u64, mut hash: H
/// Creates the next Tick or Transaction Entry `num_hashes` after `start_hash`. /// Creates the next Tick or Transaction Entry `num_hashes` after `start_hash`.
pub fn next_entry(prev_hash: &Hash, num_hashes: u64, transactions: Vec<Transaction>) -> Entry { pub fn next_entry(prev_hash: &Hash, num_hashes: u64, transactions: Vec<Transaction>) -> Entry {
assert!(num_hashes > 0 || transactions.is_empty());
let transactions = transactions.into_iter().map(Into::into).collect::<Vec<_>>(); let transactions = transactions.into_iter().map(Into::into).collect::<Vec<_>>();
next_versioned_entry(prev_hash, num_hashes, transactions)
}
/// Creates the next Tick or Transaction Entry `num_hashes` after `start_hash`.
pub fn next_versioned_entry(
prev_hash: &Hash,
num_hashes: u64,
transactions: Vec<VersionedTransaction>,
) -> Entry {
assert!(num_hashes > 0 || transactions.is_empty());
Entry { Entry {
num_hashes, num_hashes,
hash: next_hash(prev_hash, num_hashes, &transactions), hash: next_hash(prev_hash, num_hashes, &transactions),

View File

@ -73,7 +73,10 @@ async fn block(slot: Slot, output_format: OutputFormat) -> Result<(), Box<dyn st
.await .await
.map_err(|err| format!("Failed to connect to storage: {:?}", err))?; .map_err(|err| format!("Failed to connect to storage: {:?}", err))?;
let block = bigtable.get_confirmed_block(slot).await?; let versioned_block = bigtable.get_confirmed_block(slot).await?;
let block = versioned_block
.into_legacy_block()
.ok_or_else(|| "Failed to read versioned transaction in block".to_string())?;
let cli_block = CliBlock { let cli_block = CliBlock {
encoded_confirmed_block: block.encode(UiTransactionEncoding::Base64), encoded_confirmed_block: block.encode(UiTransactionEncoding::Base64),
@ -153,7 +156,11 @@ async fn confirm(
let mut get_transaction_error = None; let mut get_transaction_error = None;
if verbose { if verbose {
match bigtable.get_confirmed_transaction(signature).await { match bigtable.get_confirmed_transaction(signature).await {
Ok(Some(confirmed_transaction)) => { Ok(Some(versioned_confirmed_tx)) => {
let confirmed_transaction = versioned_confirmed_tx
.into_legacy_confirmed_transaction()
.ok_or_else(|| "Failed to read versioned transaction in block".to_string())?;
transaction = Some(CliTransaction { transaction = Some(CliTransaction {
transaction: confirmed_transaction transaction: confirmed_transaction
.transaction .transaction
@ -260,7 +267,10 @@ pub async fn transaction_history(
println!(" Unable to get confirmed transaction details: {}", err); println!(" Unable to get confirmed transaction details: {}", err);
break; break;
} }
Ok(block) => { Ok(versioned_block) => {
let block = versioned_block.into_legacy_block().ok_or_else(|| {
"Failed to read versioned transaction in block".to_string()
})?;
loaded_block = Some((result.slot, block)); loaded_block = Some((result.slot, block));
} }
} }

View File

@ -42,9 +42,9 @@ use {
}, },
solana_storage_proto::{StoredExtendedRewards, StoredTransactionStatusMeta}, solana_storage_proto::{StoredExtendedRewards, StoredTransactionStatusMeta},
solana_transaction_status::{ solana_transaction_status::{
ConfirmedBlock, ConfirmedTransactionStatusWithSignature, ConfirmedTransactionStatusWithSignature, Rewards, TransactionStatusMeta,
ConfirmedTransactionWithStatusMeta, Rewards, TransactionStatusMeta, VersionedConfirmedBlock, VersionedConfirmedTransactionWithStatusMeta,
TransactionWithStatusMeta, VersionedTransactionWithStatusMeta,
}, },
std::{ std::{
borrow::Cow, borrow::Cow,
@ -1923,7 +1923,7 @@ impl Blockstore {
&self, &self,
slot: Slot, slot: Slot,
require_previous_blockhash: bool, require_previous_blockhash: bool,
) -> Result<ConfirmedBlock> { ) -> Result<VersionedConfirmedBlock> {
datapoint_info!( datapoint_info!(
"blockstore-rpc-api", "blockstore-rpc-api",
("method", "get_rooted_block".to_string(), String) ("method", "get_rooted_block".to_string(), String)
@ -1940,7 +1940,7 @@ impl Blockstore {
&self, &self,
slot: Slot, slot: Slot,
require_previous_blockhash: bool, require_previous_blockhash: bool,
) -> Result<ConfirmedBlock> { ) -> Result<VersionedConfirmedBlock> {
let slot_meta_cf = self.db.column::<cf::SlotMeta>(); let slot_meta_cf = self.db.column::<cf::SlotMeta>();
let slot_meta = match slot_meta_cf.get(slot)? { let slot_meta = match slot_meta_cf.get(slot)? {
Some(slot_meta) => slot_meta, Some(slot_meta) => slot_meta,
@ -1998,7 +1998,7 @@ impl Blockstore {
let block_time = self.blocktime_cf.get(slot)?; let block_time = self.blocktime_cf.get(slot)?;
let block_height = self.block_height_cf.get(slot)?; let block_height = self.block_height_cf.get(slot)?;
let block = ConfirmedBlock { let block = VersionedConfirmedBlock {
previous_blockhash: previous_blockhash.to_string(), previous_blockhash: previous_blockhash.to_string(),
blockhash: blockhash.to_string(), blockhash: blockhash.to_string(),
// If the slot is full it should have parent_slot populated // If the slot is full it should have parent_slot populated
@ -2020,22 +2020,17 @@ impl Blockstore {
&self, &self,
slot: Slot, slot: Slot,
iterator: impl Iterator<Item = VersionedTransaction>, iterator: impl Iterator<Item = VersionedTransaction>,
) -> Result<Vec<TransactionWithStatusMeta>> { ) -> Result<Vec<VersionedTransactionWithStatusMeta>> {
iterator iterator
.map(|versioned_tx| { .map(|transaction| {
// TODO: add support for versioned transactions
if let Some(transaction) = versioned_tx.into_legacy_transaction() {
let signature = transaction.signatures[0]; let signature = transaction.signatures[0];
Ok(TransactionWithStatusMeta { Ok(VersionedTransactionWithStatusMeta {
transaction, transaction,
meta: self meta: self
.read_transaction_status((signature, slot)) .read_transaction_status((signature, slot))
.ok() .ok()
.flatten(), .flatten(),
}) })
} else {
Err(BlockstoreError::UnsupportedTransactionVersion)
}
}) })
.collect() .collect()
} }
@ -2287,7 +2282,7 @@ impl Blockstore {
pub fn get_rooted_transaction( pub fn get_rooted_transaction(
&self, &self,
signature: Signature, signature: Signature,
) -> Result<Option<ConfirmedTransactionWithStatusMeta>> { ) -> Result<Option<VersionedConfirmedTransactionWithStatusMeta>> {
datapoint_info!( datapoint_info!(
"blockstore-rpc-api", "blockstore-rpc-api",
("method", "get_rooted_transaction".to_string(), String) ("method", "get_rooted_transaction".to_string(), String)
@ -2300,7 +2295,7 @@ impl Blockstore {
&self, &self,
signature: Signature, signature: Signature,
highest_confirmed_slot: Slot, highest_confirmed_slot: Slot,
) -> Result<Option<ConfirmedTransactionWithStatusMeta>> { ) -> Result<Option<VersionedConfirmedTransactionWithStatusMeta>> {
datapoint_info!( datapoint_info!(
"blockstore-rpc-api", "blockstore-rpc-api",
("method", "get_complete_transaction".to_string(), String) ("method", "get_complete_transaction".to_string(), String)
@ -2317,7 +2312,7 @@ impl Blockstore {
&self, &self,
signature: Signature, signature: Signature,
confirmed_unrooted_slots: &[Slot], confirmed_unrooted_slots: &[Slot],
) -> Result<Option<ConfirmedTransactionWithStatusMeta>> { ) -> Result<Option<VersionedConfirmedTransactionWithStatusMeta>> {
if let Some((slot, status)) = if let Some((slot, status)) =
self.get_transaction_status(signature, confirmed_unrooted_slots)? self.get_transaction_status(signature, confirmed_unrooted_slots)?
{ {
@ -2325,15 +2320,10 @@ impl Blockstore {
.find_transaction_in_slot(slot, signature)? .find_transaction_in_slot(slot, signature)?
.ok_or(BlockstoreError::TransactionStatusSlotMismatch)?; // Should not happen .ok_or(BlockstoreError::TransactionStatusSlotMismatch)?; // Should not happen
// TODO: support retrieving versioned transactions
let transaction = transaction
.into_legacy_transaction()
.ok_or(BlockstoreError::UnsupportedTransactionVersion)?;
let block_time = self.get_block_time(slot)?; let block_time = self.get_block_time(slot)?;
Ok(Some(ConfirmedTransactionWithStatusMeta { Ok(Some(VersionedConfirmedTransactionWithStatusMeta {
slot, slot,
transaction: TransactionWithStatusMeta { tx_with_meta: VersionedTransactionWithStatusMeta {
transaction, transaction,
meta: Some(status), meta: Some(status),
}, },
@ -4146,6 +4136,7 @@ pub mod tests {
solana_sdk::{ solana_sdk::{
hash::{self, hash, Hash}, hash::{self, hash, Hash},
instruction::CompiledInstruction, instruction::CompiledInstruction,
message::v0::LoadedAddresses,
packet::PACKET_DATA_SIZE, packet::PACKET_DATA_SIZE,
pubkey::Pubkey, pubkey::Pubkey,
signature::Signature, signature::Signature,
@ -6237,20 +6228,15 @@ pub mod tests {
.put_meta_bytes(slot - 1, &serialize(&parent_meta).unwrap()) .put_meta_bytes(slot - 1, &serialize(&parent_meta).unwrap())
.unwrap(); .unwrap();
let expected_transactions: Vec<TransactionWithStatusMeta> = entries let expected_transactions: Vec<VersionedTransactionWithStatusMeta> = entries
.iter() .iter()
.cloned() .cloned()
.filter(|entry| !entry.is_tick()) .filter(|entry| !entry.is_tick())
.flat_map(|entry| entry.transactions) .flat_map(|entry| entry.transactions)
.map(|transaction| {
transaction
.into_legacy_transaction()
.expect("versioned transactions not supported")
})
.map(|transaction| { .map(|transaction| {
let mut pre_balances: Vec<u64> = vec![]; let mut pre_balances: Vec<u64> = vec![];
let mut post_balances: Vec<u64> = vec![]; let mut post_balances: Vec<u64> = vec![];
for (i, _account_key) in transaction.message.account_keys.iter().enumerate() { for i in 0..transaction.message.total_account_keys_len() {
pre_balances.push(i as u64 * 10); pre_balances.push(i as u64 * 10);
post_balances.push(i as u64 * 11); post_balances.push(i as u64 * 11);
} }
@ -6265,6 +6251,7 @@ pub mod tests {
pre_token_balances: Some(vec![]), pre_token_balances: Some(vec![]),
post_token_balances: Some(vec![]), post_token_balances: Some(vec![]),
rewards: Some(vec![]), rewards: Some(vec![]),
loaded_addresses: LoadedAddresses::default(),
} }
.into(); .into();
blockstore blockstore
@ -6281,6 +6268,7 @@ pub mod tests {
pre_token_balances: Some(vec![]), pre_token_balances: Some(vec![]),
post_token_balances: Some(vec![]), post_token_balances: Some(vec![]),
rewards: Some(vec![]), rewards: Some(vec![]),
loaded_addresses: LoadedAddresses::default(),
} }
.into(); .into();
blockstore blockstore
@ -6297,13 +6285,14 @@ pub mod tests {
pre_token_balances: Some(vec![]), pre_token_balances: Some(vec![]),
post_token_balances: Some(vec![]), post_token_balances: Some(vec![]),
rewards: Some(vec![]), rewards: Some(vec![]),
loaded_addresses: LoadedAddresses::default(),
} }
.into(); .into();
blockstore blockstore
.transaction_status_cf .transaction_status_cf
.put_protobuf((0, signature, slot + 2), &status) .put_protobuf((0, signature, slot + 2), &status)
.unwrap(); .unwrap();
TransactionWithStatusMeta { VersionedTransactionWithStatusMeta {
transaction, transaction,
meta: Some(TransactionStatusMeta { meta: Some(TransactionStatusMeta {
status: Ok(()), status: Ok(()),
@ -6315,6 +6304,7 @@ pub mod tests {
pre_token_balances: Some(vec![]), pre_token_balances: Some(vec![]),
post_token_balances: Some(vec![]), post_token_balances: Some(vec![]),
rewards: Some(vec![]), rewards: Some(vec![]),
loaded_addresses: LoadedAddresses::default(),
}), }),
} }
}) })
@ -6335,7 +6325,7 @@ pub mod tests {
// Test if require_previous_blockhash is false // Test if require_previous_blockhash is false
let confirmed_block = blockstore.get_rooted_block(slot, false).unwrap(); let confirmed_block = blockstore.get_rooted_block(slot, false).unwrap();
assert_eq!(confirmed_block.transactions.len(), 100); assert_eq!(confirmed_block.transactions.len(), 100);
let expected_block = ConfirmedBlock { let expected_block = VersionedConfirmedBlock {
transactions: expected_transactions.clone(), transactions: expected_transactions.clone(),
parent_slot: slot - 1, parent_slot: slot - 1,
blockhash: blockhash.to_string(), blockhash: blockhash.to_string(),
@ -6349,7 +6339,7 @@ pub mod tests {
let confirmed_block = blockstore.get_rooted_block(slot + 1, true).unwrap(); let confirmed_block = blockstore.get_rooted_block(slot + 1, true).unwrap();
assert_eq!(confirmed_block.transactions.len(), 100); assert_eq!(confirmed_block.transactions.len(), 100);
let mut expected_block = ConfirmedBlock { let mut expected_block = VersionedConfirmedBlock {
transactions: expected_transactions.clone(), transactions: expected_transactions.clone(),
parent_slot: slot, parent_slot: slot,
blockhash: blockhash.to_string(), blockhash: blockhash.to_string(),
@ -6366,7 +6356,7 @@ pub mod tests {
let complete_block = blockstore.get_complete_block(slot + 2, true).unwrap(); let complete_block = blockstore.get_complete_block(slot + 2, true).unwrap();
assert_eq!(complete_block.transactions.len(), 100); assert_eq!(complete_block.transactions.len(), 100);
let mut expected_complete_block = ConfirmedBlock { let mut expected_complete_block = VersionedConfirmedBlock {
transactions: expected_transactions, transactions: expected_transactions,
parent_slot: slot + 1, parent_slot: slot + 1,
blockhash: blockhash.to_string(), blockhash: blockhash.to_string(),
@ -6422,6 +6412,10 @@ pub mod tests {
let pre_token_balances_vec = vec![]; let pre_token_balances_vec = vec![];
let post_token_balances_vec = vec![]; let post_token_balances_vec = vec![];
let rewards_vec = vec![]; let rewards_vec = vec![];
let test_loaded_addresses = LoadedAddresses {
writable: vec![Pubkey::new_unique()],
readonly: vec![Pubkey::new_unique()],
};
// result not found // result not found
assert!(transaction_status_cf assert!(transaction_status_cf
@ -6440,6 +6434,7 @@ pub mod tests {
pre_token_balances: Some(pre_token_balances_vec.clone()), pre_token_balances: Some(pre_token_balances_vec.clone()),
post_token_balances: Some(post_token_balances_vec.clone()), post_token_balances: Some(post_token_balances_vec.clone()),
rewards: Some(rewards_vec.clone()), rewards: Some(rewards_vec.clone()),
loaded_addresses: test_loaded_addresses.clone(),
} }
.into(); .into();
assert!(transaction_status_cf assert!(transaction_status_cf
@ -6457,6 +6452,7 @@ pub mod tests {
pre_token_balances, pre_token_balances,
post_token_balances, post_token_balances,
rewards, rewards,
loaded_addresses,
} = transaction_status_cf } = transaction_status_cf
.get_protobuf_or_bincode::<StoredTransactionStatusMeta>((0, Signature::default(), 0)) .get_protobuf_or_bincode::<StoredTransactionStatusMeta>((0, Signature::default(), 0))
.unwrap() .unwrap()
@ -6472,6 +6468,7 @@ pub mod tests {
assert_eq!(pre_token_balances.unwrap(), pre_token_balances_vec); assert_eq!(pre_token_balances.unwrap(), pre_token_balances_vec);
assert_eq!(post_token_balances.unwrap(), post_token_balances_vec); assert_eq!(post_token_balances.unwrap(), post_token_balances_vec);
assert_eq!(rewards.unwrap(), rewards_vec); assert_eq!(rewards.unwrap(), rewards_vec);
assert_eq!(loaded_addresses, test_loaded_addresses);
// insert value // insert value
let status = TransactionStatusMeta { let status = TransactionStatusMeta {
@ -6484,6 +6481,7 @@ pub mod tests {
pre_token_balances: Some(pre_token_balances_vec.clone()), pre_token_balances: Some(pre_token_balances_vec.clone()),
post_token_balances: Some(post_token_balances_vec.clone()), post_token_balances: Some(post_token_balances_vec.clone()),
rewards: Some(rewards_vec.clone()), rewards: Some(rewards_vec.clone()),
loaded_addresses: test_loaded_addresses.clone(),
} }
.into(); .into();
assert!(transaction_status_cf assert!(transaction_status_cf
@ -6501,6 +6499,7 @@ pub mod tests {
pre_token_balances, pre_token_balances,
post_token_balances, post_token_balances,
rewards, rewards,
loaded_addresses,
} = transaction_status_cf } = transaction_status_cf
.get_protobuf_or_bincode::<StoredTransactionStatusMeta>(( .get_protobuf_or_bincode::<StoredTransactionStatusMeta>((
0, 0,
@ -6522,6 +6521,7 @@ pub mod tests {
assert_eq!(pre_token_balances.unwrap(), pre_token_balances_vec); assert_eq!(pre_token_balances.unwrap(), pre_token_balances_vec);
assert_eq!(post_token_balances.unwrap(), post_token_balances_vec); assert_eq!(post_token_balances.unwrap(), post_token_balances_vec);
assert_eq!(rewards.unwrap(), rewards_vec); assert_eq!(rewards.unwrap(), rewards_vec);
assert_eq!(loaded_addresses, test_loaded_addresses);
} }
#[test] #[test]
@ -6749,6 +6749,7 @@ pub mod tests {
pre_token_balances: Some(vec![]), pre_token_balances: Some(vec![]),
post_token_balances: Some(vec![]), post_token_balances: Some(vec![]),
rewards: Some(vec![]), rewards: Some(vec![]),
loaded_addresses: LoadedAddresses::default(),
} }
.into(); .into();
@ -6943,6 +6944,7 @@ pub mod tests {
pre_token_balances: Some(vec![]), pre_token_balances: Some(vec![]),
post_token_balances: Some(vec![]), post_token_balances: Some(vec![]),
rewards: Some(vec![]), rewards: Some(vec![]),
loaded_addresses: LoadedAddresses::default(),
} }
.into(); .into();
@ -7093,19 +7095,15 @@ pub mod tests {
blockstore.insert_shreds(shreds, None, false).unwrap(); blockstore.insert_shreds(shreds, None, false).unwrap();
blockstore.set_roots(vec![slot - 1, slot].iter()).unwrap(); blockstore.set_roots(vec![slot - 1, slot].iter()).unwrap();
let expected_transactions: Vec<TransactionWithStatusMeta> = entries let expected_transactions: Vec<VersionedTransactionWithStatusMeta> = entries
.iter() .iter()
.cloned() .cloned()
.filter(|entry| !entry.is_tick()) .filter(|entry| !entry.is_tick())
.flat_map(|entry| entry.transactions) .flat_map(|entry| entry.transactions)
.map(|tx| {
tx.into_legacy_transaction()
.expect("versioned transactions not supported")
})
.map(|transaction| { .map(|transaction| {
let mut pre_balances: Vec<u64> = vec![]; let mut pre_balances: Vec<u64> = vec![];
let mut post_balances: Vec<u64> = vec![]; let mut post_balances: Vec<u64> = vec![];
for (i, _account_key) in transaction.message.account_keys.iter().enumerate() { for i in 0..transaction.message.total_account_keys_len() {
pre_balances.push(i as u64 * 10); pre_balances.push(i as u64 * 10);
post_balances.push(i as u64 * 11); post_balances.push(i as u64 * 11);
} }
@ -7128,13 +7126,14 @@ pub mod tests {
pre_token_balances: pre_token_balances.clone(), pre_token_balances: pre_token_balances.clone(),
post_token_balances: post_token_balances.clone(), post_token_balances: post_token_balances.clone(),
rewards: rewards.clone(), rewards: rewards.clone(),
loaded_addresses: LoadedAddresses::default(),
} }
.into(); .into();
blockstore blockstore
.transaction_status_cf .transaction_status_cf
.put_protobuf((0, signature, slot), &status) .put_protobuf((0, signature, slot), &status)
.unwrap(); .unwrap();
TransactionWithStatusMeta { VersionedTransactionWithStatusMeta {
transaction, transaction,
meta: Some(TransactionStatusMeta { meta: Some(TransactionStatusMeta {
status: Ok(()), status: Ok(()),
@ -7146,18 +7145,19 @@ pub mod tests {
pre_token_balances, pre_token_balances,
post_token_balances, post_token_balances,
rewards, rewards,
loaded_addresses: LoadedAddresses::default(),
}), }),
} }
}) })
.collect(); .collect();
for transaction in expected_transactions.clone() { for tx_with_meta in expected_transactions.clone() {
let signature = transaction.transaction.signatures[0]; let signature = tx_with_meta.transaction.signatures[0];
assert_eq!( assert_eq!(
blockstore.get_rooted_transaction(signature).unwrap(), blockstore.get_rooted_transaction(signature).unwrap(),
Some(ConfirmedTransactionWithStatusMeta { Some(VersionedConfirmedTransactionWithStatusMeta {
slot, slot,
transaction: transaction.clone(), tx_with_meta: tx_with_meta.clone(),
block_time: None block_time: None
}) })
); );
@ -7165,9 +7165,9 @@ pub mod tests {
blockstore blockstore
.get_complete_transaction(signature, slot + 1) .get_complete_transaction(signature, slot + 1)
.unwrap(), .unwrap(),
Some(ConfirmedTransactionWithStatusMeta { Some(VersionedConfirmedTransactionWithStatusMeta {
slot, slot,
transaction, tx_with_meta,
block_time: None block_time: None
}) })
); );
@ -7175,7 +7175,7 @@ pub mod tests {
blockstore.run_purge(0, 2, PurgeType::PrimaryIndex).unwrap(); blockstore.run_purge(0, 2, PurgeType::PrimaryIndex).unwrap();
*blockstore.lowest_cleanup_slot.write().unwrap() = slot; *blockstore.lowest_cleanup_slot.write().unwrap() = slot;
for TransactionWithStatusMeta { transaction, .. } in expected_transactions { for VersionedTransactionWithStatusMeta { transaction, .. } in expected_transactions {
let signature = transaction.signatures[0]; let signature = transaction.signatures[0];
assert_eq!(blockstore.get_rooted_transaction(signature).unwrap(), None,); assert_eq!(blockstore.get_rooted_transaction(signature).unwrap(), None,);
assert_eq!( assert_eq!(
@ -7197,19 +7197,15 @@ pub mod tests {
let shreds = entries_to_test_shreds(&entries, slot, slot - 1, true, 0); let shreds = entries_to_test_shreds(&entries, slot, slot - 1, true, 0);
blockstore.insert_shreds(shreds, None, false).unwrap(); blockstore.insert_shreds(shreds, None, false).unwrap();
let expected_transactions: Vec<TransactionWithStatusMeta> = entries let expected_transactions: Vec<VersionedTransactionWithStatusMeta> = entries
.iter() .iter()
.cloned() .cloned()
.filter(|entry| !entry.is_tick()) .filter(|entry| !entry.is_tick())
.flat_map(|entry| entry.transactions) .flat_map(|entry| entry.transactions)
.map(|tx| {
tx.into_legacy_transaction()
.expect("versioned transactions not supported")
})
.map(|transaction| { .map(|transaction| {
let mut pre_balances: Vec<u64> = vec![]; let mut pre_balances: Vec<u64> = vec![];
let mut post_balances: Vec<u64> = vec![]; let mut post_balances: Vec<u64> = vec![];
for (i, _account_key) in transaction.message.account_keys.iter().enumerate() { for i in 0..transaction.message.total_account_keys_len() {
pre_balances.push(i as u64 * 10); pre_balances.push(i as u64 * 10);
post_balances.push(i as u64 * 11); post_balances.push(i as u64 * 11);
} }
@ -7232,13 +7228,14 @@ pub mod tests {
pre_token_balances: pre_token_balances.clone(), pre_token_balances: pre_token_balances.clone(),
post_token_balances: post_token_balances.clone(), post_token_balances: post_token_balances.clone(),
rewards: rewards.clone(), rewards: rewards.clone(),
loaded_addresses: LoadedAddresses::default(),
} }
.into(); .into();
blockstore blockstore
.transaction_status_cf .transaction_status_cf
.put_protobuf((0, signature, slot), &status) .put_protobuf((0, signature, slot), &status)
.unwrap(); .unwrap();
TransactionWithStatusMeta { VersionedTransactionWithStatusMeta {
transaction, transaction,
meta: Some(TransactionStatusMeta { meta: Some(TransactionStatusMeta {
status: Ok(()), status: Ok(()),
@ -7250,20 +7247,21 @@ pub mod tests {
pre_token_balances, pre_token_balances,
post_token_balances, post_token_balances,
rewards, rewards,
loaded_addresses: LoadedAddresses::default(),
}), }),
} }
}) })
.collect(); .collect();
for transaction in expected_transactions.clone() { for tx_with_meta in expected_transactions.clone() {
let signature = transaction.transaction.signatures[0]; let signature = tx_with_meta.transaction.signatures[0];
assert_eq!( assert_eq!(
blockstore blockstore
.get_complete_transaction(signature, slot) .get_complete_transaction(signature, slot)
.unwrap(), .unwrap(),
Some(ConfirmedTransactionWithStatusMeta { Some(VersionedConfirmedTransactionWithStatusMeta {
slot, slot,
transaction, tx_with_meta,
block_time: None block_time: None
}) })
); );
@ -7272,7 +7270,7 @@ pub mod tests {
blockstore.run_purge(0, 2, PurgeType::PrimaryIndex).unwrap(); blockstore.run_purge(0, 2, PurgeType::PrimaryIndex).unwrap();
*blockstore.lowest_cleanup_slot.write().unwrap() = slot; *blockstore.lowest_cleanup_slot.write().unwrap() = slot;
for TransactionWithStatusMeta { transaction, .. } in expected_transactions { for VersionedTransactionWithStatusMeta { transaction, .. } in expected_transactions {
let signature = transaction.signatures[0]; let signature = transaction.signatures[0];
assert_eq!( assert_eq!(
blockstore blockstore
@ -7560,16 +7558,13 @@ pub mod tests {
// Purge to freeze index 0 and write address-signatures in new primary index // Purge to freeze index 0 and write address-signatures in new primary index
blockstore.run_purge(0, 1, PurgeType::PrimaryIndex).unwrap(); blockstore.run_purge(0, 1, PurgeType::PrimaryIndex).unwrap();
} }
for tx in entry.transactions { for transaction in entry.transactions {
let transaction = tx
.into_legacy_transaction()
.expect("versioned transactions not supported");
assert_eq!(transaction.signatures.len(), 1); assert_eq!(transaction.signatures.len(), 1);
blockstore blockstore
.write_transaction_status( .write_transaction_status(
slot, slot,
transaction.signatures[0], transaction.signatures[0],
transaction.message.account_keys.iter().collect(), transaction.message.static_account_keys_iter().collect(),
vec![], vec![],
TransactionStatusMeta::default(), TransactionStatusMeta::default(),
) )
@ -7587,16 +7582,13 @@ pub mod tests {
blockstore.insert_shreds(shreds, None, false).unwrap(); blockstore.insert_shreds(shreds, None, false).unwrap();
for entry in entries.into_iter() { for entry in entries.into_iter() {
for tx in entry.transactions { for transaction in entry.transactions {
let transaction = tx
.into_legacy_transaction()
.expect("versioned transactions not supported");
assert_eq!(transaction.signatures.len(), 1); assert_eq!(transaction.signatures.len(), 1);
blockstore blockstore
.write_transaction_status( .write_transaction_status(
slot, slot,
transaction.signatures[0], transaction.signatures[0],
transaction.message.account_keys.iter().collect(), transaction.message.static_account_keys_iter().collect(),
vec![], vec![],
TransactionStatusMeta::default(), TransactionStatusMeta::default(),
) )
@ -8019,6 +8011,7 @@ pub mod tests {
pre_token_balances: Some(vec![]), pre_token_balances: Some(vec![]),
post_token_balances: Some(vec![]), post_token_balances: Some(vec![]),
rewards: Some(vec![]), rewards: Some(vec![]),
loaded_addresses: LoadedAddresses::default(),
} }
.into(); .into();
transaction_status_cf transaction_status_cf
@ -8570,8 +8563,9 @@ pub mod tests {
reward_type: Some(RewardType::Rent), reward_type: Some(RewardType::Rent),
commission: None, commission: None,
}]), }]),
loaded_addresses: LoadedAddresses::default(),
}; };
let deprecated_status: StoredTransactionStatusMeta = status.clone().into(); let deprecated_status: StoredTransactionStatusMeta = status.clone().try_into().unwrap();
let protobuf_status: generated::TransactionStatusMeta = status.into(); let protobuf_status: generated::TransactionStatusMeta = status.into();
for slot in 0..2 { for slot in 0..2 {

View File

@ -327,19 +327,24 @@ impl Blockstore {
let mut index1 = self.transaction_status_index_cf.get(1)?.unwrap_or_default(); let mut index1 = self.transaction_status_index_cf.get(1)?.unwrap_or_default();
for slot in from_slot..to_slot { for slot in from_slot..to_slot {
let slot_entries = self.get_any_valid_slot_entries(slot, 0); let slot_entries = self.get_any_valid_slot_entries(slot, 0);
for transaction in slot_entries let transactions = slot_entries
.iter() .into_iter()
.cloned() .flat_map(|entry| entry.transactions);
.flat_map(|entry| entry.transactions) for transaction in transactions {
{
if let Some(&signature) = transaction.signatures.get(0) { if let Some(&signature) = transaction.signatures.get(0) {
batch.delete::<cf::TransactionStatus>((0, signature, slot))?; batch.delete::<cf::TransactionStatus>((0, signature, slot))?;
batch.delete::<cf::TransactionStatus>((1, signature, slot))?; batch.delete::<cf::TransactionStatus>((1, signature, slot))?;
// TODO: support purging dynamically loaded addresses from versioned transactions
for pubkey in transaction.message.into_static_account_keys() { for pubkey in transaction.message.into_static_account_keys() {
batch.delete::<cf::AddressSignatures>((0, pubkey, slot, signature))?; batch.delete::<cf::AddressSignatures>((0, pubkey, slot, signature))?;
batch.delete::<cf::AddressSignatures>((1, pubkey, slot, signature))?; batch.delete::<cf::AddressSignatures>((1, pubkey, slot, signature))?;
} }
let meta = self.read_transaction_status((signature, slot))?;
let loaded_addresses =
meta.map(|meta| meta.loaded_addresses).unwrap_or_default();
for address in loaded_addresses.into_ordered_iter() {
batch.delete::<cf::AddressSignatures>((0, address, slot, signature))?;
batch.delete::<cf::AddressSignatures>((1, address, slot, signature))?;
}
} }
} }
} }

View File

@ -45,7 +45,7 @@ use solana_sdk::{
entrypoint::{MAX_PERMITTED_DATA_INCREASE, SUCCESS}, entrypoint::{MAX_PERMITTED_DATA_INCREASE, SUCCESS},
instruction::{AccountMeta, CompiledInstruction, Instruction, InstructionError}, instruction::{AccountMeta, CompiledInstruction, Instruction, InstructionError},
loader_instruction, loader_instruction,
message::{Message, SanitizedMessage}, message::{v0::LoadedAddresses, Message, SanitizedMessage},
pubkey::Pubkey, pubkey::Pubkey,
signature::{keypair_from_seed, Keypair, Signer}, signature::{keypair_from_seed, Keypair, Signer},
system_instruction::{self, MAX_PERMITTED_DATA_LENGTH}, system_instruction::{self, MAX_PERMITTED_DATA_LENGTH},
@ -421,6 +421,7 @@ fn execute_transactions(
inner_instructions, inner_instructions,
log_messages, log_messages,
rewards: None, rewards: None,
loaded_addresses: LoadedAddresses::default(),
}; };
Ok(ConfirmedTransactionWithStatusMeta { Ok(ConfirmedTransactionWithStatusMeta {

View File

@ -77,9 +77,10 @@ use {
solana_storage_bigtable::Error as StorageError, solana_storage_bigtable::Error as StorageError,
solana_streamer::socket::SocketAddrSpace, solana_streamer::socket::SocketAddrSpace,
solana_transaction_status::{ solana_transaction_status::{
ConfirmedBlock, ConfirmedTransactionStatusWithSignature, Encodable, ConfirmedTransactionStatusWithSignature, Encodable,
EncodedConfirmedTransactionWithStatusMeta, Reward, RewardType, EncodedConfirmedTransactionWithStatusMeta, Reward, RewardType,
TransactionConfirmationStatus, TransactionStatus, UiConfirmedBlock, UiTransactionEncoding, TransactionConfirmationStatus, TransactionStatus, UiConfirmedBlock, UiTransactionEncoding,
VersionedConfirmedBlock,
}, },
solana_vote_program::vote_state::{VoteState, MAX_LOCKOUT_HISTORY}, solana_vote_program::vote_state::{VoteState, MAX_LOCKOUT_HISTORY},
spl_token::{ spl_token::{
@ -1002,32 +1003,40 @@ impl JsonRpcRequestProcessor {
self.check_status_is_complete(slot)?; self.check_status_is_complete(slot)?;
let result = self.blockstore.get_rooted_block(slot, true); let result = self.blockstore.get_rooted_block(slot, true);
self.check_blockstore_root(&result, slot)?; self.check_blockstore_root(&result, slot)?;
let configure_block = |confirmed_block: ConfirmedBlock| { let configure_block = |versioned_block: VersionedConfirmedBlock| {
let confirmed_block = versioned_block
.into_legacy_block()
.ok_or(RpcCustomError::UnsupportedTransactionVersion)?;
let mut confirmed_block = let mut confirmed_block =
confirmed_block.configure(encoding, transaction_details, show_rewards); confirmed_block.configure(encoding, transaction_details, show_rewards);
if slot == 0 { if slot == 0 {
confirmed_block.block_time = Some(self.genesis_creation_time()); confirmed_block.block_time = Some(self.genesis_creation_time());
confirmed_block.block_height = Some(0); confirmed_block.block_height = Some(0);
} }
confirmed_block Ok(confirmed_block)
}; };
if result.is_err() { if result.is_err() {
if let Some(bigtable_ledger_storage) = &self.bigtable_ledger_storage { if let Some(bigtable_ledger_storage) = &self.bigtable_ledger_storage {
let bigtable_result = let bigtable_result =
bigtable_ledger_storage.get_confirmed_block(slot).await; bigtable_ledger_storage.get_confirmed_block(slot).await;
self.check_bigtable_result(&bigtable_result)?; self.check_bigtable_result(&bigtable_result)?;
return Ok(bigtable_result.ok().map(configure_block)); return bigtable_result.ok().map(configure_block).transpose();
} }
} }
self.check_slot_cleaned_up(&result, slot)?; self.check_slot_cleaned_up(&result, slot)?;
return Ok(result.ok().map(configure_block)); return result.ok().map(configure_block).transpose();
} else if commitment.is_confirmed() { } else if commitment.is_confirmed() {
// Check if block is confirmed // Check if block is confirmed
let confirmed_bank = self.bank(Some(CommitmentConfig::confirmed())); let confirmed_bank = self.bank(Some(CommitmentConfig::confirmed()));
if confirmed_bank.status_cache_ancestors().contains(&slot) { if confirmed_bank.status_cache_ancestors().contains(&slot) {
self.check_status_is_complete(slot)?; self.check_status_is_complete(slot)?;
let result = self.blockstore.get_complete_block(slot, true); let result = self.blockstore.get_complete_block(slot, true);
return Ok(result.ok().map(|mut confirmed_block| { return result
.ok()
.map(|versioned_block| {
let mut confirmed_block = versioned_block
.into_legacy_block()
.ok_or(RpcCustomError::UnsupportedTransactionVersion)?;
if confirmed_block.block_time.is_none() if confirmed_block.block_time.is_none()
|| confirmed_block.block_height.is_none() || confirmed_block.block_height.is_none()
{ {
@ -1035,15 +1044,21 @@ impl JsonRpcRequestProcessor {
let bank = r_bank_forks.get(slot).cloned(); let bank = r_bank_forks.get(slot).cloned();
if let Some(bank) = bank { if let Some(bank) = bank {
if confirmed_block.block_time.is_none() { if confirmed_block.block_time.is_none() {
confirmed_block.block_time = Some(bank.clock().unix_timestamp); confirmed_block.block_time =
Some(bank.clock().unix_timestamp);
} }
if confirmed_block.block_height.is_none() { if confirmed_block.block_height.is_none() {
confirmed_block.block_height = Some(bank.block_height()); confirmed_block.block_height = Some(bank.block_height());
} }
} }
} }
confirmed_block.configure(encoding, transaction_details, show_rewards) Ok(confirmed_block.configure(
})); encoding,
transaction_details,
show_rewards,
))
})
.transpose();
} }
} }
} else { } else {
@ -1368,15 +1383,20 @@ impl JsonRpcRequestProcessor {
if self.config.enable_rpc_transaction_history { if self.config.enable_rpc_transaction_history {
let confirmed_bank = self.bank(Some(CommitmentConfig::confirmed())); let confirmed_bank = self.bank(Some(CommitmentConfig::confirmed()));
let transaction = if commitment.is_confirmed() { let versioned_confirmed_tx = if commitment.is_confirmed() {
let highest_confirmed_slot = confirmed_bank.slot(); let highest_confirmed_slot = confirmed_bank.slot();
self.blockstore self.blockstore
.get_complete_transaction(signature, highest_confirmed_slot) .get_complete_transaction(signature, highest_confirmed_slot)
} else { } else {
self.blockstore.get_rooted_transaction(signature) self.blockstore.get_rooted_transaction(signature)
}; };
match transaction.unwrap_or(None) {
Some(mut confirmed_transaction) => { match versioned_confirmed_tx.unwrap_or(None) {
Some(versioned_confirmed_tx) => {
let mut confirmed_transaction = versioned_confirmed_tx
.into_legacy_confirmed_transaction()
.ok_or(RpcCustomError::UnsupportedTransactionVersion)?;
if commitment.is_confirmed() if commitment.is_confirmed()
&& confirmed_bank // should be redundant && confirmed_bank // should be redundant
.status_cache_ancestors() .status_cache_ancestors()
@ -1402,11 +1422,18 @@ impl JsonRpcRequestProcessor {
} }
None => { None => {
if let Some(bigtable_ledger_storage) = &self.bigtable_ledger_storage { if let Some(bigtable_ledger_storage) = &self.bigtable_ledger_storage {
return Ok(bigtable_ledger_storage return bigtable_ledger_storage
.get_confirmed_transaction(&signature) .get_confirmed_transaction(&signature)
.await .await
.unwrap_or(None) .unwrap_or(None)
.map(|confirmed| confirmed.encode(encoding))); .map(|versioned_confirmed_tx| {
let confirmed_tx = versioned_confirmed_tx
.into_legacy_confirmed_transaction()
.ok_or(RpcCustomError::UnsupportedTransactionVersion)?;
Ok(confirmed_tx.encode(encoding))
})
.transpose();
} }
} }
} }

View File

@ -957,7 +957,20 @@ impl RpcSubscriptions {
if s > max_complete_transaction_status_slot.load(Ordering::SeqCst) { if s > max_complete_transaction_status_slot.load(Ordering::SeqCst) {
break; break;
} }
match blockstore.get_complete_block(s, false) {
let block_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,
)
});
match block_result {
Ok(block) => { Ok(block) => {
if let Some(res) = filter_block_result_txs(block, s, params) if let Some(res) = filter_block_result_txs(block, s, params)
{ {
@ -975,17 +988,16 @@ impl RpcSubscriptions {
*w_last_unnotified_slot = s + 1; *w_last_unnotified_slot = s + 1;
} }
} }
Err(e) => { Err(err) => {
// we don't advance `w_last_unnotified_slot` so that // we don't advance `w_last_unnotified_slot` so that
// it'll retry on the next notification trigger // it'll retry on the next notification trigger
error!("get_complete_block error: {}", e);
notifier.notify( notifier.notify(
Response { Response {
context: RpcResponseContext { slot: s }, context: RpcResponseContext { slot: s },
value: RpcBlockUpdate { value: RpcBlockUpdate {
slot, slot,
block: None, block: None,
err: Some(RpcBlockUpdateError::BlockStoreError), err: Some(err),
}, },
}, },
subscription, subscription,
@ -1398,8 +1410,9 @@ pub(crate) mod tests {
let actual_resp = receiver.recv(); let actual_resp = receiver.recv();
let actual_resp = serde_json::from_str::<serde_json::Value>(&actual_resp).unwrap(); let actual_resp = serde_json::from_str::<serde_json::Value>(&actual_resp).unwrap();
let block = blockstore.get_complete_block(slot, false).unwrap(); let versioned_block = blockstore.get_complete_block(slot, false).unwrap();
let block = block.configure(params.encoding, params.transaction_details, false); let legacy_block = versioned_block.into_legacy_block().unwrap();
let block = legacy_block.configure(params.encoding, params.transaction_details, false);
let expected_resp = RpcBlockUpdate { let expected_resp = RpcBlockUpdate {
slot, slot,
block: Some(block), block: Some(block),
@ -1497,14 +1510,16 @@ pub(crate) mod tests {
let actual_resp = serde_json::from_str::<serde_json::Value>(&actual_resp).unwrap(); let actual_resp = serde_json::from_str::<serde_json::Value>(&actual_resp).unwrap();
// make sure it filtered out the other keypairs // make sure it filtered out the other keypairs
let mut block = blockstore.get_complete_block(slot, false).unwrap(); let versioned_block = blockstore.get_complete_block(slot, false).unwrap();
block.transactions.retain(|tx| { let mut legacy_block = versioned_block.into_legacy_block().unwrap();
tx.transaction legacy_block.transactions.retain(|tx_with_meta| {
tx_with_meta
.transaction
.message .message
.account_keys .account_keys
.contains(&keypair1.pubkey()) .contains(&keypair1.pubkey())
}); });
let block = block.configure(params.encoding, params.transaction_details, false); let block = legacy_block.configure(params.encoding, params.transaction_details, false);
let expected_resp = RpcBlockUpdate { let expected_resp = RpcBlockUpdate {
slot, slot,
block: Some(block), block: Some(block),
@ -1594,8 +1609,9 @@ pub(crate) mod tests {
let actual_resp = receiver.recv(); let actual_resp = receiver.recv();
let actual_resp = serde_json::from_str::<serde_json::Value>(&actual_resp).unwrap(); let actual_resp = serde_json::from_str::<serde_json::Value>(&actual_resp).unwrap();
let block = blockstore.get_complete_block(slot, false).unwrap(); let versioned_block = blockstore.get_complete_block(slot, false).unwrap();
let block = block.configure(params.encoding, params.transaction_details, false); let legacy_block = versioned_block.into_legacy_block().unwrap();
let block = legacy_block.configure(params.encoding, params.transaction_details, false);
let expected_resp = RpcBlockUpdate { let expected_resp = RpcBlockUpdate {
slot, slot,
block: Some(block), block: Some(block),

View File

@ -141,7 +141,7 @@ impl TransactionStatusService {
}) })
.collect(), .collect(),
); );
let loaded_addresses = transaction.get_loaded_addresses();
let transaction_status_meta = TransactionStatusMeta { let transaction_status_meta = TransactionStatusMeta {
status, status,
fee, fee,
@ -152,6 +152,7 @@ impl TransactionStatusService {
pre_token_balances, pre_token_balances,
post_token_balances, post_token_balances,
rewards, rewards,
loaded_addresses,
}; };
if let Some(transaction_notifier) = transaction_notifier.as_ref() { if let Some(transaction_notifier) = transaction_notifier.as_ref() {

View File

@ -71,6 +71,24 @@ impl VersionedMessage {
} }
} }
pub fn total_account_keys_len(&self) -> usize {
match self {
Self::Legacy(message) => message.account_keys.len(),
Self::V0(message) => message.account_keys.len().saturating_add(
message
.address_table_lookups
.iter()
.map(|lookup| {
lookup
.writable_indexes
.len()
.saturating_add(lookup.readonly_indexes.len())
})
.sum(),
),
}
}
pub fn recent_blockhash(&self) -> &Hash { pub fn recent_blockhash(&self) -> &Hash {
match self { match self {
Self::Legacy(message) => &message.recent_blockhash, Self::Legacy(message) => &message.recent_blockhash,
@ -85,6 +103,13 @@ impl VersionedMessage {
} }
} }
pub fn instructions(&self) -> &[CompiledInstruction] {
match self {
Self::Legacy(message) => &message.instructions,
Self::V0(message) => &message.instructions,
}
}
pub fn serialize(&self) -> Vec<u8> { pub fn serialize(&self) -> Vec<u8> {
bincode::serialize(self).unwrap() bincode::serialize(self).unwrap()
} }

View File

@ -5,7 +5,7 @@ use {
pubkey::Pubkey, pubkey::Pubkey,
sysvar, sysvar,
}, },
std::{collections::HashSet, convert::TryFrom, ops::Deref}, std::{collections::HashSet, ops::Deref},
}; };
/// Combination of a version #0 message and its loaded addresses /// Combination of a version #0 message and its loaded addresses
@ -47,6 +47,30 @@ impl FromIterator<LoadedAddresses> for LoadedAddresses {
} }
} }
impl LoadedAddresses {
/// Checks if there are no writable or readonly addresses
pub fn is_empty(&self) -> bool {
self.len() == 0
}
/// Combined length of loaded writable and readonly addresses
pub fn len(&self) -> usize {
self.writable.len().saturating_add(self.readonly.len())
}
/// Iterate over loaded addresses in the order that they are used
/// as account indexes
pub fn ordered_iter(&self) -> impl Iterator<Item = &Pubkey> {
self.writable.iter().chain(self.readonly.iter())
}
/// Iterate over loaded addresses in the order that they are used
/// as account indexes
pub fn into_ordered_iter(self) -> impl Iterator<Item = Pubkey> {
self.writable.into_iter().chain(self.readonly.into_iter())
}
}
impl LoadedMessage { impl LoadedMessage {
/// Returns an iterator of account key segments. The ordering of segments /// Returns an iterator of account key segments. The ordering of segments
/// affects how account indexes from compiled instructions are resolved and /// affects how account indexes from compiled instructions are resolved and

View File

@ -48,6 +48,9 @@ pub enum SignerError {
#[error("{0}")] #[error("{0}")]
UserCancel(String), UserCancel(String),
#[error("too many signers")]
TooManySigners,
} }
/// The `Signer` trait declares operations that all digital signature providers /// The `Signer` trait declares operations that all digital signature providers

View File

@ -176,6 +176,14 @@ impl SanitizedTransaction {
account_locks account_locks
} }
/// Return the list of addresses loaded from on-chain address lookup tables
pub fn get_loaded_addresses(&self) -> LoadedAddresses {
match &self.message {
SanitizedMessage::Legacy(_) => LoadedAddresses::default(),
SanitizedMessage::V0(message) => message.loaded_addresses.clone(),
}
}
/// If the transaction uses a durable nonce, return the pubkey of the nonce account /// If the transaction uses a durable nonce, return the pubkey of the nonce account
pub fn get_durable_nonce(&self, nonce_must_be_writable: bool) -> Option<&Pubkey> { pub fn get_durable_nonce(&self, nonce_must_be_writable: bool) -> Option<&Pubkey> {
self.message.get_durable_nonce(nonce_must_be_writable) self.message.get_durable_nonce(nonce_must_be_writable)

View File

@ -9,6 +9,8 @@ use {
sanitize::{Sanitize, SanitizeError}, sanitize::{Sanitize, SanitizeError},
short_vec, short_vec,
signature::Signature, signature::Signature,
signer::SignerError,
signers::Signers,
transaction::{Result, Transaction, TransactionError}, transaction::{Result, Transaction, TransactionError},
}, },
serde::Serialize, serde::Serialize,
@ -57,6 +59,40 @@ impl From<Transaction> for VersionedTransaction {
} }
impl VersionedTransaction { impl VersionedTransaction {
/// Signs a versioned message and if successful, returns a signed
/// transaction.
pub fn try_new<T: Signers>(
message: VersionedMessage,
keypairs: &T,
) -> std::result::Result<Self, SignerError> {
let static_account_keys = message.static_account_keys();
if static_account_keys.len() < message.header().num_required_signatures as usize {
return Err(SignerError::InvalidInput("invalid message".to_string()));
}
let signer_keys = keypairs.pubkeys();
let expected_signer_keys =
&static_account_keys[0..message.header().num_required_signatures as usize];
match signer_keys.len().cmp(&expected_signer_keys.len()) {
Ordering::Greater => Err(SignerError::TooManySigners),
Ordering::Less => Err(SignerError::NotEnoughSigners),
Ordering::Equal => Ok(()),
}?;
if signer_keys != expected_signer_keys {
return Err(SignerError::KeypairPubkeyMismatch);
}
let message_data = message.serialize();
let signatures = keypairs.try_sign_message(&message_data)?;
Ok(Self {
signatures,
message,
})
}
/// Returns a legacy transaction if the transaction message is legacy. /// Returns a legacy transaction if the transaction message is legacy.
pub fn into_legacy_transaction(self) -> Option<Transaction> { pub fn into_legacy_transaction(self) -> Option<Transaction> {
match self.message { match self.message {

View File

@ -790,10 +790,13 @@ mod tests {
super::*, super::*,
crate::StoredConfirmedBlock, crate::StoredConfirmedBlock,
prost::Message, prost::Message,
solana_sdk::{hash::Hash, signature::Keypair, system_transaction}, solana_sdk::{
hash::Hash, message::v0::LoadedAddresses, signature::Keypair, system_transaction,
},
solana_storage_proto::convert::generated, solana_storage_proto::convert::generated,
solana_transaction_status::{ solana_transaction_status::{
ConfirmedBlock, TransactionStatusMeta, TransactionWithStatusMeta, ConfirmedBlock, TransactionStatusMeta, TransactionWithStatusMeta,
VersionedConfirmedBlock,
}, },
std::convert::TryInto, std::convert::TryInto,
}; };
@ -815,6 +818,7 @@ mod tests {
pre_token_balances: Some(vec![]), pre_token_balances: Some(vec![]),
post_token_balances: Some(vec![]), post_token_balances: Some(vec![]),
rewards: Some(vec![]), rewards: Some(vec![]),
loaded_addresses: LoadedAddresses::default(),
}), }),
}; };
let block = ConfirmedBlock { let block = ConfirmedBlock {
@ -845,8 +849,9 @@ mod tests {
"".to_string(), "".to_string(),
) )
.unwrap(); .unwrap();
let expected_block: VersionedConfirmedBlock = block.into();
if let CellData::Protobuf(protobuf_block) = deserialized { if let CellData::Protobuf(protobuf_block) = deserialized {
assert_eq!(block, protobuf_block.try_into().unwrap()); assert_eq!(expected_block, protobuf_block.try_into().unwrap());
} else { } else {
panic!("deserialization should produce CellData::Protobuf"); panic!("deserialization should produce CellData::Protobuf");
} }
@ -861,7 +866,7 @@ mod tests {
) )
.unwrap(); .unwrap();
if let CellData::Bincode(bincode_block) = deserialized { if let CellData::Bincode(bincode_block) = deserialized {
let mut block = block; let mut block = expected_block;
if let Some(meta) = &mut block.transactions[0].meta { if let Some(meta) = &mut block.transactions[0].meta {
meta.inner_instructions = None; // Legacy bincode implementation does not support inner_instructions meta.inner_instructions = None; // Legacy bincode implementation does not support inner_instructions
meta.log_messages = None; // Legacy bincode implementation does not support log_messages meta.log_messages = None; // Legacy bincode implementation does not support log_messages

View File

@ -6,17 +6,18 @@ use {
solana_sdk::{ solana_sdk::{
clock::{Slot, UnixTimestamp}, clock::{Slot, UnixTimestamp},
deserialize_utils::default_on_eof, deserialize_utils::default_on_eof,
message::v0::LoadedAddresses,
pubkey::Pubkey, pubkey::Pubkey,
signature::Signature, signature::Signature,
sysvar::is_sysvar_id, sysvar::is_sysvar_id,
transaction::{Transaction, TransactionError}, transaction::{TransactionError, VersionedTransaction},
}, },
solana_storage_proto::convert::{generated, tx_by_addr}, solana_storage_proto::convert::{generated, tx_by_addr},
solana_transaction_status::{ solana_transaction_status::{
extract_and_fmt_memos, ConfirmedBlock, ConfirmedTransactionStatusWithSignature, extract_and_fmt_memos, ConfirmedBlock, ConfirmedTransactionStatusWithSignature, Reward,
ConfirmedTransactionWithStatusMeta, Reward, TransactionByAddrInfo, TransactionByAddrInfo, TransactionConfirmationStatus, TransactionStatus,
TransactionConfirmationStatus, TransactionStatus, TransactionStatusMeta, TransactionStatusMeta, TransactionWithStatusMeta, VersionedConfirmedBlock,
TransactionWithStatusMeta, VersionedConfirmedTransactionWithStatusMeta, VersionedTransactionWithStatusMeta,
}, },
std::{ std::{
collections::{HashMap, HashSet}, collections::{HashMap, HashSet},
@ -138,7 +139,7 @@ impl From<ConfirmedBlock> for StoredConfirmedBlock {
} }
} }
impl From<StoredConfirmedBlock> for ConfirmedBlock { impl From<StoredConfirmedBlock> for VersionedConfirmedBlock {
fn from(confirmed_block: StoredConfirmedBlock) -> Self { fn from(confirmed_block: StoredConfirmedBlock) -> Self {
let StoredConfirmedBlock { let StoredConfirmedBlock {
previous_blockhash, previous_blockhash,
@ -164,20 +165,20 @@ impl From<StoredConfirmedBlock> for ConfirmedBlock {
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
struct StoredConfirmedBlockTransaction { struct StoredConfirmedBlockTransaction {
transaction: Transaction, transaction: VersionedTransaction,
meta: Option<StoredConfirmedBlockTransactionStatusMeta>, meta: Option<StoredConfirmedBlockTransactionStatusMeta>,
} }
impl From<TransactionWithStatusMeta> for StoredConfirmedBlockTransaction { impl From<TransactionWithStatusMeta> for StoredConfirmedBlockTransaction {
fn from(value: TransactionWithStatusMeta) -> Self { fn from(value: TransactionWithStatusMeta) -> Self {
Self { Self {
transaction: value.transaction, transaction: value.transaction.into(),
meta: value.meta.map(|meta| meta.into()), meta: value.meta.map(|meta| meta.into()),
} }
} }
} }
impl From<StoredConfirmedBlockTransaction> for TransactionWithStatusMeta { impl From<StoredConfirmedBlockTransaction> for VersionedTransactionWithStatusMeta {
fn from(value: StoredConfirmedBlockTransaction) -> Self { fn from(value: StoredConfirmedBlockTransaction) -> Self {
Self { Self {
transaction: value.transaction, transaction: value.transaction,
@ -216,6 +217,7 @@ impl From<StoredConfirmedBlockTransactionStatusMeta> for TransactionStatusMeta {
pre_token_balances: None, pre_token_balances: None,
post_token_balances: None, post_token_balances: None,
rewards: None, rewards: None,
loaded_addresses: LoadedAddresses::default(),
} }
} }
} }
@ -392,7 +394,7 @@ impl LedgerStorage {
} }
/// Fetch the confirmed block from the desired slot /// Fetch the confirmed block from the desired slot
pub async fn get_confirmed_block(&self, slot: Slot) -> Result<ConfirmedBlock> { pub async fn get_confirmed_block(&self, slot: Slot) -> Result<VersionedConfirmedBlock> {
debug!( debug!(
"LedgerStorage::get_confirmed_block request received: {:?}", "LedgerStorage::get_confirmed_block request received: {:?}",
slot slot
@ -438,7 +440,7 @@ impl LedgerStorage {
pub async fn get_confirmed_transaction( pub async fn get_confirmed_transaction(
&self, &self,
signature: &Signature, signature: &Signature,
) -> Result<Option<ConfirmedTransactionWithStatusMeta>> { ) -> Result<Option<VersionedConfirmedTransactionWithStatusMeta>> {
debug!( debug!(
"LedgerStorage::get_confirmed_transaction request received: {:?}", "LedgerStorage::get_confirmed_transaction request received: {:?}",
signature signature
@ -471,9 +473,9 @@ impl LedgerStorage {
); );
Ok(None) Ok(None)
} else { } else {
Ok(Some(ConfirmedTransactionWithStatusMeta { Ok(Some(VersionedConfirmedTransactionWithStatusMeta {
slot, slot,
transaction: bucket_block_transaction, tx_with_meta: bucket_block_transaction,
block_time: block.block_time, block_time: block.block_time,
})) }))
} }
@ -627,7 +629,7 @@ impl LedgerStorage {
pub async fn upload_confirmed_block( pub async fn upload_confirmed_block(
&self, &self,
slot: Slot, slot: Slot,
confirmed_block: ConfirmedBlock, confirmed_block: VersionedConfirmedBlock,
) -> Result<()> { ) -> Result<()> {
let mut bytes_written = 0; let mut bytes_written = 0;
@ -635,13 +637,13 @@ impl LedgerStorage {
let mut tx_cells = vec![]; let mut tx_cells = vec![];
for (index, transaction_with_meta) in confirmed_block.transactions.iter().enumerate() { for (index, transaction_with_meta) in confirmed_block.transactions.iter().enumerate() {
let TransactionWithStatusMeta { meta, transaction } = transaction_with_meta; let VersionedTransactionWithStatusMeta { meta, transaction } = transaction_with_meta;
let err = meta.as_ref().and_then(|meta| meta.status.clone().err()); let err = meta.as_ref().and_then(|meta| meta.status.clone().err());
let index = index as u32; let index = index as u32;
let signature = transaction.signatures[0]; let signature = transaction.signatures[0];
let memo = extract_and_fmt_memos(&transaction.message); let memo = extract_and_fmt_memos(transaction_with_meta);
for address in &transaction.message.account_keys { for address in transaction_with_meta.account_keys_iter() {
if !is_sysvar_id(address) { if !is_sysvar_id(address) {
by_addr by_addr
.entry(address) .entry(address)
@ -723,12 +725,12 @@ impl LedgerStorage {
let mut expected_tx_infos: HashMap<String, UploadedTransaction> = HashMap::new(); let mut expected_tx_infos: HashMap<String, UploadedTransaction> = HashMap::new();
let confirmed_block = self.get_confirmed_block(slot).await?; let confirmed_block = self.get_confirmed_block(slot).await?;
for (index, transaction_with_meta) in confirmed_block.transactions.iter().enumerate() { for (index, transaction_with_meta) in confirmed_block.transactions.iter().enumerate() {
let TransactionWithStatusMeta { meta, transaction } = transaction_with_meta; let VersionedTransactionWithStatusMeta { transaction, meta } = transaction_with_meta;
let signature = transaction.signatures[0]; let signature = transaction.signatures[0];
let index = index as u32; let index = index as u32;
let err = meta.as_ref().and_then(|meta| meta.status.clone().err()); let err = meta.as_ref().and_then(|meta| meta.status.clone().err());
for address in &transaction.message.account_keys { for address in transaction_with_meta.account_keys_iter() {
if !is_sysvar_id(address) { if !is_sysvar_id(address) {
addresses.insert(address); addresses.insert(address);
} }

View File

@ -27,6 +27,8 @@ message Message {
repeated bytes account_keys = 2; repeated bytes account_keys = 2;
bytes recent_blockhash = 3; bytes recent_blockhash = 3;
repeated CompiledInstruction instructions = 4; repeated CompiledInstruction instructions = 4;
bool versioned = 5;
repeated MessageAddressTableLookup address_table_lookups = 6;
} }
message MessageHeader { message MessageHeader {
@ -35,6 +37,12 @@ message MessageHeader {
uint32 num_readonly_unsigned_accounts = 3; uint32 num_readonly_unsigned_accounts = 3;
} }
message MessageAddressTableLookup {
bytes account_key = 1;
bytes writable_indexes = 2;
bytes readonly_indexes = 3;
}
message TransactionStatusMeta { message TransactionStatusMeta {
TransactionError err = 1; TransactionError err = 1;
uint64 fee = 2; uint64 fee = 2;
@ -47,6 +55,8 @@ message TransactionStatusMeta {
repeated TokenBalance pre_token_balances = 7; repeated TokenBalance pre_token_balances = 7;
repeated TokenBalance post_token_balances = 8; repeated TokenBalance post_token_balances = 8;
repeated Reward rewards = 9; repeated Reward rewards = 9;
repeated bytes loaded_writable_addresses = 12;
repeated bytes loaded_readonly_addresses = 13;
} }
message TransactionError { message TransactionError {

View File

@ -4,14 +4,19 @@ use {
solana_sdk::{ solana_sdk::{
hash::Hash, hash::Hash,
instruction::{CompiledInstruction, InstructionError}, instruction::{CompiledInstruction, InstructionError},
message::{Message, MessageHeader}, message::{
legacy::Message as LegacyMessage,
v0::{self, LoadedAddresses, MessageAddressTableLookup},
MessageHeader, VersionedMessage,
},
pubkey::Pubkey, pubkey::Pubkey,
signature::Signature, signature::Signature,
transaction::{Transaction, TransactionError}, transaction::{Transaction, TransactionError, VersionedTransaction},
}, },
solana_transaction_status::{ solana_transaction_status::{
ConfirmedBlock, InnerInstructions, Reward, RewardType, TransactionByAddrInfo, ConfirmedBlock, InnerInstructions, Reward, RewardType, TransactionByAddrInfo,
TransactionStatusMeta, TransactionTokenBalance, TransactionWithStatusMeta, TransactionStatusMeta, TransactionTokenBalance, TransactionWithStatusMeta,
VersionedConfirmedBlock, VersionedTransactionWithStatusMeta,
}, },
std::{ std::{
convert::{TryFrom, TryInto}, convert::{TryFrom, TryInto},
@ -111,6 +116,30 @@ impl From<generated::Reward> for Reward {
} }
} }
impl From<VersionedConfirmedBlock> for generated::ConfirmedBlock {
fn from(confirmed_block: VersionedConfirmedBlock) -> Self {
let VersionedConfirmedBlock {
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 From<ConfirmedBlock> for generated::ConfirmedBlock { impl From<ConfirmedBlock> for generated::ConfirmedBlock {
fn from(confirmed_block: ConfirmedBlock) -> Self { fn from(confirmed_block: ConfirmedBlock) -> Self {
let ConfirmedBlock { let ConfirmedBlock {
@ -135,7 +164,7 @@ impl From<ConfirmedBlock> for generated::ConfirmedBlock {
} }
} }
impl TryFrom<generated::ConfirmedBlock> for ConfirmedBlock { impl TryFrom<generated::ConfirmedBlock> for VersionedConfirmedBlock {
type Error = bincode::Error; type Error = bincode::Error;
fn try_from( fn try_from(
confirmed_block: generated::ConfirmedBlock, confirmed_block: generated::ConfirmedBlock,
@ -157,7 +186,7 @@ impl TryFrom<generated::ConfirmedBlock> for ConfirmedBlock {
transactions: transactions transactions: transactions
.into_iter() .into_iter()
.map(|tx| tx.try_into()) .map(|tx| tx.try_into())
.collect::<std::result::Result<Vec<TransactionWithStatusMeta>, Self::Error>>()?, .collect::<std::result::Result<Vec<_>, Self::Error>>()?,
rewards: rewards.into_iter().map(|r| r.into()).collect(), rewards: rewards.into_iter().map(|r| r.into()).collect(),
block_time: block_time.map(|generated::UnixTimestamp { timestamp }| timestamp), block_time: block_time.map(|generated::UnixTimestamp { timestamp }| timestamp),
block_height: block_height.map(|generated::BlockHeight { block_height }| block_height), block_height: block_height.map(|generated::BlockHeight { block_height }| block_height),
@ -175,7 +204,17 @@ impl From<TransactionWithStatusMeta> for generated::ConfirmedTransaction {
} }
} }
impl TryFrom<generated::ConfirmedTransaction> for TransactionWithStatusMeta { 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,
}
}
}
impl TryFrom<generated::ConfirmedTransaction> for VersionedTransactionWithStatusMeta {
type Error = bincode::Error; type Error = bincode::Error;
fn try_from(value: generated::ConfirmedTransaction) -> std::result::Result<Self, Self::Error> { fn try_from(value: generated::ConfirmedTransaction) -> std::result::Result<Self, Self::Error> {
let meta = value.meta.map(|meta| meta.try_into()).transpose()?; let meta = value.meta.map(|meta| meta.try_into()).transpose()?;
@ -199,7 +238,20 @@ impl From<Transaction> for generated::Transaction {
} }
} }
impl From<generated::Transaction> for Transaction { impl From<VersionedTransaction> for generated::Transaction {
fn from(value: VersionedTransaction) -> Self {
Self {
signatures: value
.signatures
.into_iter()
.map(|signature| <Signature as AsRef<[u8]>>::as_ref(&signature).into())
.collect(),
message: Some(value.message.into()),
}
}
}
impl From<generated::Transaction> for VersionedTransaction {
fn from(value: generated::Transaction) -> Self { fn from(value: generated::Transaction) -> Self {
Self { Self {
signatures: value signatures: value
@ -212,32 +264,86 @@ impl From<generated::Transaction> for Transaction {
} }
} }
impl From<Message> for generated::Message { impl From<LegacyMessage> for generated::Message {
fn from(value: Message) -> Self { fn from(message: LegacyMessage) -> Self {
Self { Self {
header: Some(value.header.into()), header: Some(message.header.into()),
account_keys: value account_keys: message
.account_keys .account_keys
.into_iter() .iter()
.map(|key| <Pubkey as AsRef<[u8]>>::as_ref(&key).into()) .map(|key| <Pubkey as AsRef<[u8]>>::as_ref(key).into())
.collect(), .collect(),
recent_blockhash: value.recent_blockhash.to_bytes().into(), recent_blockhash: message.recent_blockhash.to_bytes().into(),
instructions: value.instructions.into_iter().map(|ix| ix.into()).collect(), instructions: message
.instructions
.into_iter()
.map(|ix| ix.into())
.collect(),
versioned: false,
address_table_lookups: vec![],
} }
} }
} }
impl From<generated::Message> for Message { impl From<VersionedMessage> for generated::Message {
fn from(message: VersionedMessage) -> Self {
match message {
VersionedMessage::Legacy(message) => Self::from(message),
VersionedMessage::V0(message) => Self {
header: Some(message.header.into()),
account_keys: message
.account_keys
.iter()
.map(|key| <Pubkey as AsRef<[u8]>>::as_ref(key).into())
.collect(),
recent_blockhash: message.recent_blockhash.to_bytes().into(),
instructions: message
.instructions
.into_iter()
.map(|ix| ix.into())
.collect(),
versioned: true,
address_table_lookups: message
.address_table_lookups
.into_iter()
.map(|lookup| lookup.into())
.collect(),
},
}
}
}
impl From<generated::Message> for VersionedMessage {
fn from(value: generated::Message) -> Self { fn from(value: generated::Message) -> Self {
Self { let header = value.header.expect("header is required").into();
header: value.header.expect("header is required").into(), let account_keys = value
account_keys: value
.account_keys .account_keys
.into_iter() .into_iter()
.map(|key| Pubkey::new(&key)) .map(|key| Pubkey::new(&key))
.collect(), .collect();
recent_blockhash: Hash::new(&value.recent_blockhash), let recent_blockhash = Hash::new(&value.recent_blockhash);
instructions: value.instructions.into_iter().map(|ix| ix.into()).collect(), let instructions = value.instructions.into_iter().map(|ix| ix.into()).collect();
let address_table_lookups = value
.address_table_lookups
.into_iter()
.map(|lookup| lookup.into())
.collect();
if !value.versioned {
Self::Legacy(LegacyMessage {
header,
account_keys,
recent_blockhash,
instructions,
})
} else {
Self::V0(v0::Message {
header,
account_keys,
recent_blockhash,
instructions,
address_table_lookups,
})
} }
} }
} }
@ -274,6 +380,7 @@ impl From<TransactionStatusMeta> for generated::TransactionStatusMeta {
pre_token_balances, pre_token_balances,
post_token_balances, post_token_balances,
rewards, rewards,
loaded_addresses,
} = value; } = value;
let err = match status { let err = match status {
Ok(()) => None, Ok(()) => None,
@ -304,6 +411,16 @@ impl From<TransactionStatusMeta> for generated::TransactionStatusMeta {
.into_iter() .into_iter()
.map(|reward| reward.into()) .map(|reward| reward.into())
.collect(); .collect();
let loaded_writable_addresses = loaded_addresses
.writable
.into_iter()
.map(|key| <Pubkey as AsRef<[u8]>>::as_ref(&key).into())
.collect();
let loaded_readonly_addresses = loaded_addresses
.readonly
.into_iter()
.map(|key| <Pubkey as AsRef<[u8]>>::as_ref(&key).into())
.collect();
Self { Self {
err, err,
@ -317,6 +434,8 @@ impl From<TransactionStatusMeta> for generated::TransactionStatusMeta {
pre_token_balances, pre_token_balances,
post_token_balances, post_token_balances,
rewards, rewards,
loaded_writable_addresses,
loaded_readonly_addresses,
} }
} }
} }
@ -344,6 +463,8 @@ impl TryFrom<generated::TransactionStatusMeta> for TransactionStatusMeta {
pre_token_balances, pre_token_balances,
post_token_balances, post_token_balances,
rewards, rewards,
loaded_writable_addresses,
loaded_readonly_addresses,
} = value; } = value;
let status = match &err { let status = match &err {
None => Ok(()), None => Ok(()),
@ -377,6 +498,16 @@ impl TryFrom<generated::TransactionStatusMeta> for TransactionStatusMeta {
.collect(), .collect(),
); );
let rewards = Some(rewards.into_iter().map(|reward| reward.into()).collect()); let rewards = Some(rewards.into_iter().map(|reward| reward.into()).collect());
let loaded_addresses = LoadedAddresses {
writable: loaded_writable_addresses
.into_iter()
.map(|key| Pubkey::new(&key))
.collect(),
readonly: loaded_readonly_addresses
.into_iter()
.map(|key| Pubkey::new(&key))
.collect(),
};
Ok(Self { Ok(Self {
status, status,
fee, fee,
@ -387,6 +518,7 @@ impl TryFrom<generated::TransactionStatusMeta> for TransactionStatusMeta {
pre_token_balances, pre_token_balances,
post_token_balances, post_token_balances,
rewards, rewards,
loaded_addresses,
}) })
} }
} }
@ -453,6 +585,26 @@ impl From<generated::TokenBalance> for TransactionTokenBalance {
} }
} }
impl From<MessageAddressTableLookup> for generated::MessageAddressTableLookup {
fn from(lookup: MessageAddressTableLookup) -> Self {
Self {
account_key: <Pubkey as AsRef<[u8]>>::as_ref(&lookup.account_key).into(),
writable_indexes: lookup.writable_indexes,
readonly_indexes: lookup.readonly_indexes,
}
}
}
impl From<generated::MessageAddressTableLookup> for MessageAddressTableLookup {
fn from(value: generated::MessageAddressTableLookup) -> Self {
Self {
account_key: Pubkey::new(&value.account_key),
writable_indexes: value.writable_indexes,
readonly_indexes: value.readonly_indexes,
}
}
}
impl From<CompiledInstruction> for generated::CompiledInstruction { impl From<CompiledInstruction> for generated::CompiledInstruction {
fn from(value: CompiledInstruction) -> Self { fn from(value: CompiledInstruction) -> Self {
Self { Self {

View File

@ -4,7 +4,9 @@ use {
parse_token::{real_number_string_trimmed, UiTokenAmount}, parse_token::{real_number_string_trimmed, UiTokenAmount},
StringAmount, StringAmount,
}, },
solana_sdk::{deserialize_utils::default_on_eof, transaction::Result}, solana_sdk::{
deserialize_utils::default_on_eof, message::v0::LoadedAddresses, transaction::Result,
},
solana_transaction_status::{ solana_transaction_status::{
InnerInstructions, Reward, RewardType, TransactionStatusMeta, TransactionTokenBalance, InnerInstructions, Reward, RewardType, TransactionStatusMeta, TransactionTokenBalance,
}, },
@ -193,12 +195,14 @@ impl From<StoredTransactionStatusMeta> for TransactionStatusMeta {
.map(|balances| balances.into_iter().map(|balance| balance.into()).collect()), .map(|balances| balances.into_iter().map(|balance| balance.into()).collect()),
rewards: rewards rewards: rewards
.map(|rewards| rewards.into_iter().map(|reward| reward.into()).collect()), .map(|rewards| rewards.into_iter().map(|reward| reward.into()).collect()),
loaded_addresses: LoadedAddresses::default(),
} }
} }
} }
impl From<TransactionStatusMeta> for StoredTransactionStatusMeta { impl TryFrom<TransactionStatusMeta> for StoredTransactionStatusMeta {
fn from(value: TransactionStatusMeta) -> Self { type Error = bincode::Error;
fn try_from(value: TransactionStatusMeta) -> std::result::Result<Self, Self::Error> {
let TransactionStatusMeta { let TransactionStatusMeta {
status, status,
fee, fee,
@ -209,8 +213,18 @@ impl From<TransactionStatusMeta> for StoredTransactionStatusMeta {
pre_token_balances, pre_token_balances,
post_token_balances, post_token_balances,
rewards, rewards,
loaded_addresses,
} = value; } = value;
Self {
if !loaded_addresses.is_empty() {
// Deprecated bincode serialized status metadata doesn't support
// loaded addresses.
return Err(
bincode::ErrorKind::Custom("Bincode serialization is deprecated".into()).into(),
);
}
Ok(Self {
status, status,
fee, fee,
pre_balances, pre_balances,
@ -223,6 +237,6 @@ impl From<TransactionStatusMeta> for StoredTransactionStatusMeta {
.map(|balances| balances.into_iter().map(|balance| balance.into()).collect()), .map(|balances| balances.into_iter().map(|balance| balance.into()).collect()),
rewards: rewards rewards: rewards
.map(|rewards| rewards.into_iter().map(|reward| reward.into()).collect()), .map(|rewards| rewards.into_iter().map(|reward| reward.into()).collect()),
} })
} }
} }

View File

@ -1,5 +1,5 @@
use { use {
crate::parse_instruction::parse_memo_data, crate::{parse_instruction::parse_memo_data, VersionedTransactionWithStatusMeta},
solana_sdk::{ solana_sdk::{
instruction::CompiledInstruction, instruction::CompiledInstruction,
message::{Message, SanitizedMessage}, message::{Message, SanitizedMessage},
@ -55,6 +55,15 @@ impl ExtractMemos for SanitizedMessage {
} }
} }
impl ExtractMemos for VersionedTransactionWithStatusMeta {
fn extract_memos(&self) -> Vec<String> {
extract_memos_inner(
self.account_keys_iter(),
self.transaction.message.instructions(),
)
}
}
enum KeyType<'a> { enum KeyType<'a> {
MemoProgram, MemoProgram,
OtherProgram, OtherProgram,

View File

@ -26,11 +26,11 @@ use {
clock::{Slot, UnixTimestamp}, clock::{Slot, UnixTimestamp},
commitment_config::CommitmentConfig, commitment_config::CommitmentConfig,
instruction::CompiledInstruction, instruction::CompiledInstruction,
message::{Message, MessageHeader}, message::{v0::LoadedAddresses, Message, MessageHeader},
pubkey::Pubkey, pubkey::Pubkey,
sanitize::Sanitize, sanitize::Sanitize,
signature::Signature, signature::Signature,
transaction::{Result, Transaction, TransactionError}, transaction::{Result, Transaction, TransactionError, VersionedTransaction},
}, },
std::fmt, std::fmt,
}; };
@ -82,13 +82,13 @@ pub enum UiInstruction {
} }
impl UiInstruction { impl UiInstruction {
fn parse(instruction: &CompiledInstruction, message: &Message) -> Self { fn parse(instruction: &CompiledInstruction, account_keys: &[Pubkey]) -> Self {
let program_id = instruction.program_id(&message.account_keys); let program_id = instruction.program_id(account_keys);
if let Ok(parsed_instruction) = parse(program_id, instruction, &message.account_keys) { if let Ok(parsed_instruction) = parse(program_id, instruction, account_keys) {
UiInstruction::Parsed(UiParsedInstruction::Parsed(parsed_instruction)) UiInstruction::Parsed(UiParsedInstruction::Parsed(parsed_instruction))
} else { } else {
UiInstruction::Parsed(UiParsedInstruction::PartiallyDecoded( UiInstruction::Parsed(UiParsedInstruction::PartiallyDecoded(
UiPartiallyDecodedInstruction::from(instruction, &message.account_keys), UiPartiallyDecodedInstruction::from(instruction, account_keys),
)) ))
} }
} }
@ -167,7 +167,7 @@ impl UiInnerInstructions {
instructions: inner_instructions instructions: inner_instructions
.instructions .instructions
.iter() .iter()
.map(|ix| UiInstruction::parse(ix, message)) .map(|ix| UiInstruction::parse(ix, &message.account_keys))
.collect(), .collect(),
} }
} }
@ -230,6 +230,7 @@ pub struct TransactionStatusMeta {
pub pre_token_balances: Option<Vec<TransactionTokenBalance>>, pub pre_token_balances: Option<Vec<TransactionTokenBalance>>,
pub post_token_balances: Option<Vec<TransactionTokenBalance>>, pub post_token_balances: Option<Vec<TransactionTokenBalance>>,
pub rewards: Option<Rewards>, pub rewards: Option<Rewards>,
pub loaded_addresses: LoadedAddresses,
} }
impl Default for TransactionStatusMeta { impl Default for TransactionStatusMeta {
@ -244,6 +245,7 @@ impl Default for TransactionStatusMeta {
pre_token_balances: None, pre_token_balances: None,
post_token_balances: None, post_token_balances: None,
rewards: None, rewards: None,
loaded_addresses: LoadedAddresses::default(),
} }
} }
} }
@ -397,6 +399,51 @@ pub struct ConfirmedBlock {
pub block_height: Option<u64>, pub block_height: Option<u64>,
} }
#[derive(Clone, Debug, Default, PartialEq)]
pub struct VersionedConfirmedBlock {
pub previous_blockhash: String,
pub blockhash: String,
pub parent_slot: Slot,
pub transactions: Vec<VersionedTransactionWithStatusMeta>,
pub rewards: Rewards,
pub block_time: Option<UnixTimestamp>,
pub block_height: Option<u64>,
}
impl VersionedConfirmedBlock {
/// 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 {
previous_blockhash: self.previous_blockhash,
blockhash: self.blockhash,
parent_slot: self.parent_slot,
transactions: self
.transactions
.into_iter()
.map(|tx_with_meta| tx_with_meta.into_legacy_transaction_with_meta())
.collect::<Option<Vec<_>>>()?,
rewards: self.rewards,
block_time: self.block_time,
block_height: self.block_height,
})
}
}
impl From<ConfirmedBlock> for VersionedConfirmedBlock {
fn from(block: ConfirmedBlock) -> Self {
VersionedConfirmedBlock {
previous_blockhash: block.previous_blockhash,
blockhash: block.blockhash,
parent_slot: block.parent_slot,
transactions: block.transactions.into_iter().map(|tx| tx.into()).collect(),
rewards: block.rewards,
block_time: block.block_time,
block_height: block.block_height,
}
}
}
impl Encodable for ConfirmedBlock { impl Encodable for ConfirmedBlock {
type Encoded = EncodedConfirmedBlock; type Encoded = EncodedConfirmedBlock;
fn encode(self, encoding: UiTransactionEncoding) -> Self::Encoded { fn encode(self, encoding: UiTransactionEncoding) -> Self::Encoded {
@ -428,7 +475,7 @@ impl ConfirmedBlock {
Some( Some(
self.transactions self.transactions
.into_iter() .into_iter()
.map(|tx| tx.encode(encoding)) .map(|tx_with_meta| tx_with_meta.encode(encoding))
.collect(), .collect(),
), ),
None, None,
@ -518,6 +565,41 @@ impl From<EncodedConfirmedBlock> for UiConfirmedBlock {
} }
} }
#[derive(Clone, Debug, PartialEq)]
pub struct VersionedTransactionWithStatusMeta {
pub transaction: VersionedTransaction,
pub meta: Option<TransactionStatusMeta>,
}
impl VersionedTransactionWithStatusMeta {
pub fn account_keys_iter(&self) -> impl Iterator<Item = &Pubkey> {
let static_keys_iter = self.transaction.message.static_account_keys().iter();
let dynamic_keys_iter = self
.meta
.iter()
.map(|meta| meta.loaded_addresses.ordered_iter())
.flatten();
static_keys_iter.chain(dynamic_keys_iter)
}
pub fn into_legacy_transaction_with_meta(self) -> Option<TransactionWithStatusMeta> {
Some(TransactionWithStatusMeta {
transaction: self.transaction.into_legacy_transaction()?,
meta: 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)] #[derive(Clone, Debug, PartialEq)]
pub struct TransactionWithStatusMeta { pub struct TransactionWithStatusMeta {
pub transaction: Transaction, pub transaction: Transaction,
@ -531,7 +613,7 @@ impl Encodable for TransactionWithStatusMeta {
transaction: self.transaction.encode(encoding), transaction: self.transaction.encode(encoding),
meta: self.meta.map(|meta| match encoding { meta: self.meta.map(|meta| match encoding {
UiTransactionEncoding::JsonParsed => { UiTransactionEncoding::JsonParsed => {
UiTransactionStatusMeta::parse(meta, self.transaction.message()) UiTransactionStatusMeta::parse(meta, &self.transaction.message)
} }
_ => UiTransactionStatusMeta::from(meta), _ => UiTransactionStatusMeta::from(meta),
}), }),
@ -545,6 +627,7 @@ pub struct EncodedTransactionWithStatusMeta {
pub transaction: EncodedTransaction, pub transaction: EncodedTransaction,
pub meta: Option<UiTransactionStatusMeta>, pub meta: Option<UiTransactionStatusMeta>,
} }
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct ConfirmedTransactionWithStatusMeta { pub struct ConfirmedTransactionWithStatusMeta {
pub slot: Slot, pub slot: Slot,
@ -563,6 +646,28 @@ impl Encodable for ConfirmedTransactionWithStatusMeta {
} }
} }
#[derive(Debug, Clone, PartialEq)]
pub struct VersionedConfirmedTransactionWithStatusMeta {
pub slot: Slot,
pub tx_with_meta: VersionedTransactionWithStatusMeta,
pub block_time: Option<UnixTimestamp>,
}
impl VersionedConfirmedTransactionWithStatusMeta {
/// 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,
},
block_time: self.block_time,
slot: self.slot,
})
}
}
#[derive(Debug, PartialEq, Serialize, Deserialize)] #[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct EncodedConfirmedTransactionWithStatusMeta { pub struct EncodedConfirmedTransactionWithStatusMeta {
@ -655,7 +760,7 @@ impl Encodable for &Message {
instructions: self instructions: self
.instructions .instructions
.iter() .iter()
.map(|instruction| UiInstruction::parse(instruction, self)) .map(|instruction| UiInstruction::parse(instruction, &self.account_keys))
.collect(), .collect(),
}) })
} else { } else {