Store address table lookups in blockstore and bigtable (#22402)
This commit is contained in:
parent
4c577d7f8c
commit
f804ccdece
|
@ -4848,6 +4848,7 @@ dependencies = [
|
|||
"serde_json",
|
||||
"serial_test",
|
||||
"solana-accountsdb-plugin-manager",
|
||||
"solana-address-lookup-table-program",
|
||||
"solana-client",
|
||||
"solana-entry",
|
||||
"solana-frozen-abi 1.10.0",
|
||||
|
|
|
@ -1034,6 +1034,10 @@ pub(crate) mod tests {
|
|||
commission: Some(11),
|
||||
},
|
||||
]),
|
||||
loaded_addresses: LoadedAddresses {
|
||||
writable: vec![Pubkey::new_unique()],
|
||||
readonly: vec![Pubkey::new_unique()],
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -275,8 +275,9 @@ fn test_block_subscription() {
|
|||
let maybe_actual = receiver.recv_timeout(Duration::from_millis(400));
|
||||
match maybe_actual {
|
||||
Ok(actual) => {
|
||||
let complete_block = blockstore.get_complete_block(slot, false).unwrap();
|
||||
let block = complete_block.clone().configure(
|
||||
let versioned_block = blockstore.get_complete_block(slot, false).unwrap();
|
||||
let legacy_block = versioned_block.into_legacy_block().unwrap();
|
||||
let block = legacy_block.clone().configure(
|
||||
UiTransactionEncoding::Json,
|
||||
TransactionDetails::Signatures,
|
||||
false,
|
||||
|
@ -286,7 +287,7 @@ fn test_block_subscription() {
|
|||
block: Some(block),
|
||||
err: None,
|
||||
};
|
||||
let block = complete_block.configure(
|
||||
let block = legacy_block.configure(
|
||||
UiTransactionEncoding::Json,
|
||||
TransactionDetails::Signatures,
|
||||
false,
|
||||
|
|
|
@ -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_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_UNSUPPORTED_TRANSACTION_VERSION: i64 = -32015;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum RpcCustomError {
|
||||
|
@ -57,6 +58,8 @@ pub enum RpcCustomError {
|
|||
TransactionSignatureLenMismatch,
|
||||
#[error("BlockStatusNotAvailableYet")]
|
||||
BlockStatusNotAvailableYet { slot: Slot },
|
||||
#[error("UnsupportedTransactionVersion")]
|
||||
UnsupportedTransactionVersion,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
|
@ -169,6 +172,11 @@ impl From<RpcCustomError> for Error {
|
|||
message: format!("Block status not yet available for slot {}", slot),
|
||||
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,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -429,6 +429,9 @@ pub struct RpcInflationReward {
|
|||
pub enum RpcBlockUpdateError {
|
||||
#[error("block store error")]
|
||||
BlockStoreError,
|
||||
|
||||
#[error("unsupported transaction version")]
|
||||
UnsupportedTransactionVersion,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
|
|
|
@ -33,6 +33,7 @@ rayon = "1.5.1"
|
|||
retain_mut = "0.1.5"
|
||||
serde = "1.0.133"
|
||||
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-client = { path = "../client", version = "=1.10.0" }
|
||||
solana-entry = { path = "../entry", version = "=1.10.0" }
|
||||
|
|
|
@ -1477,7 +1477,8 @@ mod tests {
|
|||
super::*,
|
||||
crossbeam_channel::{unbounded, Receiver},
|
||||
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_ledger::{
|
||||
blockstore::{entries_to_test_shreds, Blockstore},
|
||||
|
@ -1493,8 +1494,10 @@ mod tests {
|
|||
solana_rpc::transaction_status_service::TransactionStatusService,
|
||||
solana_runtime::bank::TransactionExecutionDetails,
|
||||
solana_sdk::{
|
||||
account::AccountSharedData,
|
||||
hash::Hash,
|
||||
instruction::InstructionError,
|
||||
message::{v0, MessageHeader, VersionedMessage},
|
||||
poh_config::PohConfig,
|
||||
signature::{Keypair, Signer},
|
||||
system_instruction::SystemError,
|
||||
|
@ -1502,9 +1505,10 @@ mod tests {
|
|||
transaction::{Transaction, TransactionError},
|
||||
},
|
||||
solana_streamer::{recvmmsg::recv_mmsg, socket::SocketAddrSpace},
|
||||
solana_transaction_status::TransactionWithStatusMeta,
|
||||
solana_transaction_status::{TransactionStatusMeta, VersionedTransactionWithStatusMeta},
|
||||
solana_vote_program::vote_transaction,
|
||||
std::{
|
||||
borrow::Cow,
|
||||
net::SocketAddr,
|
||||
path::Path,
|
||||
sync::atomic::{AtomicBool, Ordering},
|
||||
|
@ -2525,7 +2529,7 @@ mod tests {
|
|||
let confirmed_block = blockstore.get_rooted_block(bank.slot(), false).unwrap();
|
||||
assert_eq!(confirmed_block.transactions.len(), 3);
|
||||
|
||||
for TransactionWithStatusMeta { transaction, meta } in
|
||||
for VersionedTransactionWithStatusMeta { transaction, meta } in
|
||||
confirmed_block.transactions.into_iter()
|
||||
{
|
||||
if transaction.signatures[0] == success_signature {
|
||||
|
@ -2555,6 +2559,165 @@ mod tests {
|
|||
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)]
|
||||
fn setup_conflicting_transactions(
|
||||
ledger_path: &Path,
|
||||
|
|
|
@ -3003,7 +3003,7 @@ pub mod tests {
|
|||
transaction::TransactionError,
|
||||
},
|
||||
solana_streamer::socket::SocketAddrSpace,
|
||||
solana_transaction_status::TransactionWithStatusMeta,
|
||||
solana_transaction_status::VersionedTransactionWithStatusMeta,
|
||||
solana_vote_program::{
|
||||
vote_state::{VoteState, VoteStateVersions},
|
||||
vote_transaction,
|
||||
|
@ -3860,7 +3860,7 @@ pub mod tests {
|
|||
let confirmed_block = blockstore.get_rooted_block(slot, false).unwrap();
|
||||
assert_eq!(confirmed_block.transactions.len(), 3);
|
||||
|
||||
for TransactionWithStatusMeta { transaction, meta } in
|
||||
for VersionedTransactionWithStatusMeta { transaction, meta } in
|
||||
confirmed_block.transactions.into_iter()
|
||||
{
|
||||
if transaction.signatures[0] == signatures[0] {
|
||||
|
|
|
@ -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`.
|
||||
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<_>>();
|
||||
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 {
|
||||
num_hashes,
|
||||
hash: next_hash(prev_hash, num_hashes, &transactions),
|
||||
|
|
|
@ -73,7 +73,10 @@ async fn block(slot: Slot, output_format: OutputFormat) -> Result<(), Box<dyn st
|
|||
.await
|
||||
.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 {
|
||||
encoded_confirmed_block: block.encode(UiTransactionEncoding::Base64),
|
||||
|
@ -153,7 +156,11 @@ async fn confirm(
|
|||
let mut get_transaction_error = None;
|
||||
if verbose {
|
||||
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: confirmed_transaction
|
||||
.transaction
|
||||
|
@ -260,7 +267,10 @@ pub async fn transaction_history(
|
|||
println!(" Unable to get confirmed transaction details: {}", err);
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,9 +42,9 @@ use {
|
|||
},
|
||||
solana_storage_proto::{StoredExtendedRewards, StoredTransactionStatusMeta},
|
||||
solana_transaction_status::{
|
||||
ConfirmedBlock, ConfirmedTransactionStatusWithSignature,
|
||||
ConfirmedTransactionWithStatusMeta, Rewards, TransactionStatusMeta,
|
||||
TransactionWithStatusMeta,
|
||||
ConfirmedTransactionStatusWithSignature, Rewards, TransactionStatusMeta,
|
||||
VersionedConfirmedBlock, VersionedConfirmedTransactionWithStatusMeta,
|
||||
VersionedTransactionWithStatusMeta,
|
||||
},
|
||||
std::{
|
||||
borrow::Cow,
|
||||
|
@ -1923,7 +1923,7 @@ impl Blockstore {
|
|||
&self,
|
||||
slot: Slot,
|
||||
require_previous_blockhash: bool,
|
||||
) -> Result<ConfirmedBlock> {
|
||||
) -> Result<VersionedConfirmedBlock> {
|
||||
datapoint_info!(
|
||||
"blockstore-rpc-api",
|
||||
("method", "get_rooted_block".to_string(), String)
|
||||
|
@ -1940,7 +1940,7 @@ impl Blockstore {
|
|||
&self,
|
||||
slot: Slot,
|
||||
require_previous_blockhash: bool,
|
||||
) -> Result<ConfirmedBlock> {
|
||||
) -> Result<VersionedConfirmedBlock> {
|
||||
let slot_meta_cf = self.db.column::<cf::SlotMeta>();
|
||||
let slot_meta = match slot_meta_cf.get(slot)? {
|
||||
Some(slot_meta) => slot_meta,
|
||||
|
@ -1998,7 +1998,7 @@ impl Blockstore {
|
|||
let block_time = self.blocktime_cf.get(slot)?;
|
||||
let block_height = self.block_height_cf.get(slot)?;
|
||||
|
||||
let block = ConfirmedBlock {
|
||||
let block = VersionedConfirmedBlock {
|
||||
previous_blockhash: previous_blockhash.to_string(),
|
||||
blockhash: blockhash.to_string(),
|
||||
// If the slot is full it should have parent_slot populated
|
||||
|
@ -2020,22 +2020,17 @@ impl Blockstore {
|
|||
&self,
|
||||
slot: Slot,
|
||||
iterator: impl Iterator<Item = VersionedTransaction>,
|
||||
) -> Result<Vec<TransactionWithStatusMeta>> {
|
||||
) -> Result<Vec<VersionedTransactionWithStatusMeta>> {
|
||||
iterator
|
||||
.map(|versioned_tx| {
|
||||
// TODO: add support for versioned transactions
|
||||
if let Some(transaction) = versioned_tx.into_legacy_transaction() {
|
||||
.map(|transaction| {
|
||||
let signature = transaction.signatures[0];
|
||||
Ok(TransactionWithStatusMeta {
|
||||
Ok(VersionedTransactionWithStatusMeta {
|
||||
transaction,
|
||||
meta: self
|
||||
.read_transaction_status((signature, slot))
|
||||
.ok()
|
||||
.flatten(),
|
||||
})
|
||||
} else {
|
||||
Err(BlockstoreError::UnsupportedTransactionVersion)
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
@ -2287,7 +2282,7 @@ impl Blockstore {
|
|||
pub fn get_rooted_transaction(
|
||||
&self,
|
||||
signature: Signature,
|
||||
) -> Result<Option<ConfirmedTransactionWithStatusMeta>> {
|
||||
) -> Result<Option<VersionedConfirmedTransactionWithStatusMeta>> {
|
||||
datapoint_info!(
|
||||
"blockstore-rpc-api",
|
||||
("method", "get_rooted_transaction".to_string(), String)
|
||||
|
@ -2300,7 +2295,7 @@ impl Blockstore {
|
|||
&self,
|
||||
signature: Signature,
|
||||
highest_confirmed_slot: Slot,
|
||||
) -> Result<Option<ConfirmedTransactionWithStatusMeta>> {
|
||||
) -> Result<Option<VersionedConfirmedTransactionWithStatusMeta>> {
|
||||
datapoint_info!(
|
||||
"blockstore-rpc-api",
|
||||
("method", "get_complete_transaction".to_string(), String)
|
||||
|
@ -2317,7 +2312,7 @@ impl Blockstore {
|
|||
&self,
|
||||
signature: Signature,
|
||||
confirmed_unrooted_slots: &[Slot],
|
||||
) -> Result<Option<ConfirmedTransactionWithStatusMeta>> {
|
||||
) -> Result<Option<VersionedConfirmedTransactionWithStatusMeta>> {
|
||||
if let Some((slot, status)) =
|
||||
self.get_transaction_status(signature, confirmed_unrooted_slots)?
|
||||
{
|
||||
|
@ -2325,15 +2320,10 @@ impl Blockstore {
|
|||
.find_transaction_in_slot(slot, signature)?
|
||||
.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)?;
|
||||
Ok(Some(ConfirmedTransactionWithStatusMeta {
|
||||
Ok(Some(VersionedConfirmedTransactionWithStatusMeta {
|
||||
slot,
|
||||
transaction: TransactionWithStatusMeta {
|
||||
tx_with_meta: VersionedTransactionWithStatusMeta {
|
||||
transaction,
|
||||
meta: Some(status),
|
||||
},
|
||||
|
@ -4146,6 +4136,7 @@ pub mod tests {
|
|||
solana_sdk::{
|
||||
hash::{self, hash, Hash},
|
||||
instruction::CompiledInstruction,
|
||||
message::v0::LoadedAddresses,
|
||||
packet::PACKET_DATA_SIZE,
|
||||
pubkey::Pubkey,
|
||||
signature::Signature,
|
||||
|
@ -6237,20 +6228,15 @@ pub mod tests {
|
|||
.put_meta_bytes(slot - 1, &serialize(&parent_meta).unwrap())
|
||||
.unwrap();
|
||||
|
||||
let expected_transactions: Vec<TransactionWithStatusMeta> = entries
|
||||
let expected_transactions: Vec<VersionedTransactionWithStatusMeta> = entries
|
||||
.iter()
|
||||
.cloned()
|
||||
.filter(|entry| !entry.is_tick())
|
||||
.flat_map(|entry| entry.transactions)
|
||||
.map(|transaction| {
|
||||
transaction
|
||||
.into_legacy_transaction()
|
||||
.expect("versioned transactions not supported")
|
||||
})
|
||||
.map(|transaction| {
|
||||
let mut pre_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);
|
||||
post_balances.push(i as u64 * 11);
|
||||
}
|
||||
|
@ -6265,6 +6251,7 @@ pub mod tests {
|
|||
pre_token_balances: Some(vec![]),
|
||||
post_token_balances: Some(vec![]),
|
||||
rewards: Some(vec![]),
|
||||
loaded_addresses: LoadedAddresses::default(),
|
||||
}
|
||||
.into();
|
||||
blockstore
|
||||
|
@ -6281,6 +6268,7 @@ pub mod tests {
|
|||
pre_token_balances: Some(vec![]),
|
||||
post_token_balances: Some(vec![]),
|
||||
rewards: Some(vec![]),
|
||||
loaded_addresses: LoadedAddresses::default(),
|
||||
}
|
||||
.into();
|
||||
blockstore
|
||||
|
@ -6297,13 +6285,14 @@ pub mod tests {
|
|||
pre_token_balances: Some(vec![]),
|
||||
post_token_balances: Some(vec![]),
|
||||
rewards: Some(vec![]),
|
||||
loaded_addresses: LoadedAddresses::default(),
|
||||
}
|
||||
.into();
|
||||
blockstore
|
||||
.transaction_status_cf
|
||||
.put_protobuf((0, signature, slot + 2), &status)
|
||||
.unwrap();
|
||||
TransactionWithStatusMeta {
|
||||
VersionedTransactionWithStatusMeta {
|
||||
transaction,
|
||||
meta: Some(TransactionStatusMeta {
|
||||
status: Ok(()),
|
||||
|
@ -6315,6 +6304,7 @@ pub mod tests {
|
|||
pre_token_balances: Some(vec![]),
|
||||
post_token_balances: Some(vec![]),
|
||||
rewards: Some(vec![]),
|
||||
loaded_addresses: LoadedAddresses::default(),
|
||||
}),
|
||||
}
|
||||
})
|
||||
|
@ -6335,7 +6325,7 @@ pub mod tests {
|
|||
// Test if require_previous_blockhash is false
|
||||
let confirmed_block = blockstore.get_rooted_block(slot, false).unwrap();
|
||||
assert_eq!(confirmed_block.transactions.len(), 100);
|
||||
let expected_block = ConfirmedBlock {
|
||||
let expected_block = VersionedConfirmedBlock {
|
||||
transactions: expected_transactions.clone(),
|
||||
parent_slot: slot - 1,
|
||||
blockhash: blockhash.to_string(),
|
||||
|
@ -6349,7 +6339,7 @@ pub mod tests {
|
|||
let confirmed_block = blockstore.get_rooted_block(slot + 1, true).unwrap();
|
||||
assert_eq!(confirmed_block.transactions.len(), 100);
|
||||
|
||||
let mut expected_block = ConfirmedBlock {
|
||||
let mut expected_block = VersionedConfirmedBlock {
|
||||
transactions: expected_transactions.clone(),
|
||||
parent_slot: slot,
|
||||
blockhash: blockhash.to_string(),
|
||||
|
@ -6366,7 +6356,7 @@ pub mod tests {
|
|||
let complete_block = blockstore.get_complete_block(slot + 2, true).unwrap();
|
||||
assert_eq!(complete_block.transactions.len(), 100);
|
||||
|
||||
let mut expected_complete_block = ConfirmedBlock {
|
||||
let mut expected_complete_block = VersionedConfirmedBlock {
|
||||
transactions: expected_transactions,
|
||||
parent_slot: slot + 1,
|
||||
blockhash: blockhash.to_string(),
|
||||
|
@ -6422,6 +6412,10 @@ pub mod tests {
|
|||
let pre_token_balances_vec = vec![];
|
||||
let post_token_balances_vec = vec![];
|
||||
let rewards_vec = vec![];
|
||||
let test_loaded_addresses = LoadedAddresses {
|
||||
writable: vec![Pubkey::new_unique()],
|
||||
readonly: vec![Pubkey::new_unique()],
|
||||
};
|
||||
|
||||
// result not found
|
||||
assert!(transaction_status_cf
|
||||
|
@ -6440,6 +6434,7 @@ pub mod tests {
|
|||
pre_token_balances: Some(pre_token_balances_vec.clone()),
|
||||
post_token_balances: Some(post_token_balances_vec.clone()),
|
||||
rewards: Some(rewards_vec.clone()),
|
||||
loaded_addresses: test_loaded_addresses.clone(),
|
||||
}
|
||||
.into();
|
||||
assert!(transaction_status_cf
|
||||
|
@ -6457,6 +6452,7 @@ pub mod tests {
|
|||
pre_token_balances,
|
||||
post_token_balances,
|
||||
rewards,
|
||||
loaded_addresses,
|
||||
} = transaction_status_cf
|
||||
.get_protobuf_or_bincode::<StoredTransactionStatusMeta>((0, Signature::default(), 0))
|
||||
.unwrap()
|
||||
|
@ -6472,6 +6468,7 @@ pub mod tests {
|
|||
assert_eq!(pre_token_balances.unwrap(), pre_token_balances_vec);
|
||||
assert_eq!(post_token_balances.unwrap(), post_token_balances_vec);
|
||||
assert_eq!(rewards.unwrap(), rewards_vec);
|
||||
assert_eq!(loaded_addresses, test_loaded_addresses);
|
||||
|
||||
// insert value
|
||||
let status = TransactionStatusMeta {
|
||||
|
@ -6484,6 +6481,7 @@ pub mod tests {
|
|||
pre_token_balances: Some(pre_token_balances_vec.clone()),
|
||||
post_token_balances: Some(post_token_balances_vec.clone()),
|
||||
rewards: Some(rewards_vec.clone()),
|
||||
loaded_addresses: test_loaded_addresses.clone(),
|
||||
}
|
||||
.into();
|
||||
assert!(transaction_status_cf
|
||||
|
@ -6501,6 +6499,7 @@ pub mod tests {
|
|||
pre_token_balances,
|
||||
post_token_balances,
|
||||
rewards,
|
||||
loaded_addresses,
|
||||
} = transaction_status_cf
|
||||
.get_protobuf_or_bincode::<StoredTransactionStatusMeta>((
|
||||
0,
|
||||
|
@ -6522,6 +6521,7 @@ pub mod tests {
|
|||
assert_eq!(pre_token_balances.unwrap(), pre_token_balances_vec);
|
||||
assert_eq!(post_token_balances.unwrap(), post_token_balances_vec);
|
||||
assert_eq!(rewards.unwrap(), rewards_vec);
|
||||
assert_eq!(loaded_addresses, test_loaded_addresses);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -6749,6 +6749,7 @@ pub mod tests {
|
|||
pre_token_balances: Some(vec![]),
|
||||
post_token_balances: Some(vec![]),
|
||||
rewards: Some(vec![]),
|
||||
loaded_addresses: LoadedAddresses::default(),
|
||||
}
|
||||
.into();
|
||||
|
||||
|
@ -6943,6 +6944,7 @@ pub mod tests {
|
|||
pre_token_balances: Some(vec![]),
|
||||
post_token_balances: Some(vec![]),
|
||||
rewards: Some(vec![]),
|
||||
loaded_addresses: LoadedAddresses::default(),
|
||||
}
|
||||
.into();
|
||||
|
||||
|
@ -7093,19 +7095,15 @@ 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<VersionedTransactionWithStatusMeta> = entries
|
||||
.iter()
|
||||
.cloned()
|
||||
.filter(|entry| !entry.is_tick())
|
||||
.flat_map(|entry| entry.transactions)
|
||||
.map(|tx| {
|
||||
tx.into_legacy_transaction()
|
||||
.expect("versioned transactions not supported")
|
||||
})
|
||||
.map(|transaction| {
|
||||
let mut pre_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);
|
||||
post_balances.push(i as u64 * 11);
|
||||
}
|
||||
|
@ -7128,13 +7126,14 @@ pub mod tests {
|
|||
pre_token_balances: pre_token_balances.clone(),
|
||||
post_token_balances: post_token_balances.clone(),
|
||||
rewards: rewards.clone(),
|
||||
loaded_addresses: LoadedAddresses::default(),
|
||||
}
|
||||
.into();
|
||||
blockstore
|
||||
.transaction_status_cf
|
||||
.put_protobuf((0, signature, slot), &status)
|
||||
.unwrap();
|
||||
TransactionWithStatusMeta {
|
||||
VersionedTransactionWithStatusMeta {
|
||||
transaction,
|
||||
meta: Some(TransactionStatusMeta {
|
||||
status: Ok(()),
|
||||
|
@ -7146,18 +7145,19 @@ pub mod tests {
|
|||
pre_token_balances,
|
||||
post_token_balances,
|
||||
rewards,
|
||||
loaded_addresses: LoadedAddresses::default(),
|
||||
}),
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
for transaction in expected_transactions.clone() {
|
||||
let signature = transaction.transaction.signatures[0];
|
||||
for tx_with_meta in expected_transactions.clone() {
|
||||
let signature = tx_with_meta.transaction.signatures[0];
|
||||
assert_eq!(
|
||||
blockstore.get_rooted_transaction(signature).unwrap(),
|
||||
Some(ConfirmedTransactionWithStatusMeta {
|
||||
Some(VersionedConfirmedTransactionWithStatusMeta {
|
||||
slot,
|
||||
transaction: transaction.clone(),
|
||||
tx_with_meta: tx_with_meta.clone(),
|
||||
block_time: None
|
||||
})
|
||||
);
|
||||
|
@ -7165,9 +7165,9 @@ pub mod tests {
|
|||
blockstore
|
||||
.get_complete_transaction(signature, slot + 1)
|
||||
.unwrap(),
|
||||
Some(ConfirmedTransactionWithStatusMeta {
|
||||
Some(VersionedConfirmedTransactionWithStatusMeta {
|
||||
slot,
|
||||
transaction,
|
||||
tx_with_meta,
|
||||
block_time: None
|
||||
})
|
||||
);
|
||||
|
@ -7175,7 +7175,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 VersionedTransactionWithStatusMeta { transaction, .. } in expected_transactions {
|
||||
let signature = transaction.signatures[0];
|
||||
assert_eq!(blockstore.get_rooted_transaction(signature).unwrap(), None,);
|
||||
assert_eq!(
|
||||
|
@ -7197,19 +7197,15 @@ pub mod tests {
|
|||
let shreds = entries_to_test_shreds(&entries, slot, slot - 1, true, 0);
|
||||
blockstore.insert_shreds(shreds, None, false).unwrap();
|
||||
|
||||
let expected_transactions: Vec<TransactionWithStatusMeta> = entries
|
||||
let expected_transactions: Vec<VersionedTransactionWithStatusMeta> = entries
|
||||
.iter()
|
||||
.cloned()
|
||||
.filter(|entry| !entry.is_tick())
|
||||
.flat_map(|entry| entry.transactions)
|
||||
.map(|tx| {
|
||||
tx.into_legacy_transaction()
|
||||
.expect("versioned transactions not supported")
|
||||
})
|
||||
.map(|transaction| {
|
||||
let mut pre_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);
|
||||
post_balances.push(i as u64 * 11);
|
||||
}
|
||||
|
@ -7232,13 +7228,14 @@ pub mod tests {
|
|||
pre_token_balances: pre_token_balances.clone(),
|
||||
post_token_balances: post_token_balances.clone(),
|
||||
rewards: rewards.clone(),
|
||||
loaded_addresses: LoadedAddresses::default(),
|
||||
}
|
||||
.into();
|
||||
blockstore
|
||||
.transaction_status_cf
|
||||
.put_protobuf((0, signature, slot), &status)
|
||||
.unwrap();
|
||||
TransactionWithStatusMeta {
|
||||
VersionedTransactionWithStatusMeta {
|
||||
transaction,
|
||||
meta: Some(TransactionStatusMeta {
|
||||
status: Ok(()),
|
||||
|
@ -7250,20 +7247,21 @@ pub mod tests {
|
|||
pre_token_balances,
|
||||
post_token_balances,
|
||||
rewards,
|
||||
loaded_addresses: LoadedAddresses::default(),
|
||||
}),
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
for transaction in expected_transactions.clone() {
|
||||
let signature = transaction.transaction.signatures[0];
|
||||
for tx_with_meta in expected_transactions.clone() {
|
||||
let signature = tx_with_meta.transaction.signatures[0];
|
||||
assert_eq!(
|
||||
blockstore
|
||||
.get_complete_transaction(signature, slot)
|
||||
.unwrap(),
|
||||
Some(ConfirmedTransactionWithStatusMeta {
|
||||
Some(VersionedConfirmedTransactionWithStatusMeta {
|
||||
slot,
|
||||
transaction,
|
||||
tx_with_meta,
|
||||
block_time: None
|
||||
})
|
||||
);
|
||||
|
@ -7272,7 +7270,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 VersionedTransactionWithStatusMeta { transaction, .. } in expected_transactions {
|
||||
let signature = transaction.signatures[0];
|
||||
assert_eq!(
|
||||
blockstore
|
||||
|
@ -7560,16 +7558,13 @@ pub mod tests {
|
|||
// Purge to freeze index 0 and write address-signatures in new primary index
|
||||
blockstore.run_purge(0, 1, PurgeType::PrimaryIndex).unwrap();
|
||||
}
|
||||
for tx in entry.transactions {
|
||||
let transaction = tx
|
||||
.into_legacy_transaction()
|
||||
.expect("versioned transactions not supported");
|
||||
for transaction in entry.transactions {
|
||||
assert_eq!(transaction.signatures.len(), 1);
|
||||
blockstore
|
||||
.write_transaction_status(
|
||||
slot,
|
||||
transaction.signatures[0],
|
||||
transaction.message.account_keys.iter().collect(),
|
||||
transaction.message.static_account_keys_iter().collect(),
|
||||
vec![],
|
||||
TransactionStatusMeta::default(),
|
||||
)
|
||||
|
@ -7587,16 +7582,13 @@ pub mod tests {
|
|||
blockstore.insert_shreds(shreds, None, false).unwrap();
|
||||
|
||||
for entry in entries.into_iter() {
|
||||
for tx in entry.transactions {
|
||||
let transaction = tx
|
||||
.into_legacy_transaction()
|
||||
.expect("versioned transactions not supported");
|
||||
for transaction in entry.transactions {
|
||||
assert_eq!(transaction.signatures.len(), 1);
|
||||
blockstore
|
||||
.write_transaction_status(
|
||||
slot,
|
||||
transaction.signatures[0],
|
||||
transaction.message.account_keys.iter().collect(),
|
||||
transaction.message.static_account_keys_iter().collect(),
|
||||
vec![],
|
||||
TransactionStatusMeta::default(),
|
||||
)
|
||||
|
@ -8019,6 +8011,7 @@ pub mod tests {
|
|||
pre_token_balances: Some(vec![]),
|
||||
post_token_balances: Some(vec![]),
|
||||
rewards: Some(vec![]),
|
||||
loaded_addresses: LoadedAddresses::default(),
|
||||
}
|
||||
.into();
|
||||
transaction_status_cf
|
||||
|
@ -8570,8 +8563,9 @@ pub mod tests {
|
|||
reward_type: Some(RewardType::Rent),
|
||||
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();
|
||||
|
||||
for slot in 0..2 {
|
||||
|
|
|
@ -327,19 +327,24 @@ impl Blockstore {
|
|||
let mut index1 = self.transaction_status_index_cf.get(1)?.unwrap_or_default();
|
||||
for slot in from_slot..to_slot {
|
||||
let slot_entries = self.get_any_valid_slot_entries(slot, 0);
|
||||
for transaction in slot_entries
|
||||
.iter()
|
||||
.cloned()
|
||||
.flat_map(|entry| entry.transactions)
|
||||
{
|
||||
let transactions = slot_entries
|
||||
.into_iter()
|
||||
.flat_map(|entry| entry.transactions);
|
||||
for transaction in transactions {
|
||||
if let Some(&signature) = transaction.signatures.get(0) {
|
||||
batch.delete::<cf::TransactionStatus>((0, 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() {
|
||||
batch.delete::<cf::AddressSignatures>((0, 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))?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,7 +45,7 @@ use solana_sdk::{
|
|||
entrypoint::{MAX_PERMITTED_DATA_INCREASE, SUCCESS},
|
||||
instruction::{AccountMeta, CompiledInstruction, Instruction, InstructionError},
|
||||
loader_instruction,
|
||||
message::{Message, SanitizedMessage},
|
||||
message::{v0::LoadedAddresses, Message, SanitizedMessage},
|
||||
pubkey::Pubkey,
|
||||
signature::{keypair_from_seed, Keypair, Signer},
|
||||
system_instruction::{self, MAX_PERMITTED_DATA_LENGTH},
|
||||
|
@ -421,6 +421,7 @@ fn execute_transactions(
|
|||
inner_instructions,
|
||||
log_messages,
|
||||
rewards: None,
|
||||
loaded_addresses: LoadedAddresses::default(),
|
||||
};
|
||||
|
||||
Ok(ConfirmedTransactionWithStatusMeta {
|
||||
|
|
|
@ -77,9 +77,10 @@ use {
|
|||
solana_storage_bigtable::Error as StorageError,
|
||||
solana_streamer::socket::SocketAddrSpace,
|
||||
solana_transaction_status::{
|
||||
ConfirmedBlock, ConfirmedTransactionStatusWithSignature, Encodable,
|
||||
ConfirmedTransactionStatusWithSignature, Encodable,
|
||||
EncodedConfirmedTransactionWithStatusMeta, Reward, RewardType,
|
||||
TransactionConfirmationStatus, TransactionStatus, UiConfirmedBlock, UiTransactionEncoding,
|
||||
VersionedConfirmedBlock,
|
||||
},
|
||||
solana_vote_program::vote_state::{VoteState, MAX_LOCKOUT_HISTORY},
|
||||
spl_token::{
|
||||
|
@ -1002,32 +1003,40 @@ 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 = |versioned_block: VersionedConfirmedBlock| {
|
||||
let confirmed_block = versioned_block
|
||||
.into_legacy_block()
|
||||
.ok_or(RpcCustomError::UnsupportedTransactionVersion)?;
|
||||
let mut confirmed_block =
|
||||
confirmed_block.configure(encoding, transaction_details, show_rewards);
|
||||
if slot == 0 {
|
||||
confirmed_block.block_time = Some(self.genesis_creation_time());
|
||||
confirmed_block.block_height = Some(0);
|
||||
}
|
||||
confirmed_block
|
||||
Ok(confirmed_block)
|
||||
};
|
||||
if result.is_err() {
|
||||
if let Some(bigtable_ledger_storage) = &self.bigtable_ledger_storage {
|
||||
let bigtable_result =
|
||||
bigtable_ledger_storage.get_confirmed_block(slot).await;
|
||||
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)?;
|
||||
return Ok(result.ok().map(configure_block));
|
||||
return result.ok().map(configure_block).transpose();
|
||||
} else if commitment.is_confirmed() {
|
||||
// Check if block is confirmed
|
||||
let confirmed_bank = self.bank(Some(CommitmentConfig::confirmed()));
|
||||
if confirmed_bank.status_cache_ancestors().contains(&slot) {
|
||||
self.check_status_is_complete(slot)?;
|
||||
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()
|
||||
|| confirmed_block.block_height.is_none()
|
||||
{
|
||||
|
@ -1035,15 +1044,21 @@ impl JsonRpcRequestProcessor {
|
|||
let bank = r_bank_forks.get(slot).cloned();
|
||||
if let Some(bank) = bank {
|
||||
if confirmed_block.block_time.is_none() {
|
||||
confirmed_block.block_time = Some(bank.clock().unix_timestamp);
|
||||
confirmed_block.block_time =
|
||||
Some(bank.clock().unix_timestamp);
|
||||
}
|
||||
if confirmed_block.block_height.is_none() {
|
||||
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 {
|
||||
|
@ -1368,15 +1383,20 @@ impl JsonRpcRequestProcessor {
|
|||
|
||||
if self.config.enable_rpc_transaction_history {
|
||||
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();
|
||||
self.blockstore
|
||||
.get_complete_transaction(signature, highest_confirmed_slot)
|
||||
} else {
|
||||
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()
|
||||
&& confirmed_bank // should be redundant
|
||||
.status_cache_ancestors()
|
||||
|
@ -1402,11 +1422,18 @@ impl JsonRpcRequestProcessor {
|
|||
}
|
||||
None => {
|
||||
if let Some(bigtable_ledger_storage) = &self.bigtable_ledger_storage {
|
||||
return Ok(bigtable_ledger_storage
|
||||
return bigtable_ledger_storage
|
||||
.get_confirmed_transaction(&signature)
|
||||
.await
|
||||
.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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -957,7 +957,20 @@ impl RpcSubscriptions {
|
|||
if s > max_complete_transaction_status_slot.load(Ordering::SeqCst) {
|
||||
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) => {
|
||||
if let Some(res) = filter_block_result_txs(block, s, params)
|
||||
{
|
||||
|
@ -975,17 +988,16 @@ impl RpcSubscriptions {
|
|||
*w_last_unnotified_slot = s + 1;
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
Err(err) => {
|
||||
// we don't advance `w_last_unnotified_slot` so that
|
||||
// it'll retry on the next notification trigger
|
||||
error!("get_complete_block error: {}", e);
|
||||
notifier.notify(
|
||||
Response {
|
||||
context: RpcResponseContext { slot: s },
|
||||
value: RpcBlockUpdate {
|
||||
slot,
|
||||
block: None,
|
||||
err: Some(RpcBlockUpdateError::BlockStoreError),
|
||||
err: Some(err),
|
||||
},
|
||||
},
|
||||
subscription,
|
||||
|
@ -1398,8 +1410,9 @@ pub(crate) mod tests {
|
|||
let actual_resp = receiver.recv();
|
||||
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 versioned_block = blockstore.get_complete_block(slot, false).unwrap();
|
||||
let legacy_block = versioned_block.into_legacy_block().unwrap();
|
||||
let block = legacy_block.configure(params.encoding, params.transaction_details, false);
|
||||
let expected_resp = RpcBlockUpdate {
|
||||
slot,
|
||||
block: Some(block),
|
||||
|
@ -1497,14 +1510,16 @@ pub(crate) mod tests {
|
|||
let actual_resp = serde_json::from_str::<serde_json::Value>(&actual_resp).unwrap();
|
||||
|
||||
// make sure it filtered out the other keypairs
|
||||
let mut block = blockstore.get_complete_block(slot, false).unwrap();
|
||||
block.transactions.retain(|tx| {
|
||||
tx.transaction
|
||||
let versioned_block = blockstore.get_complete_block(slot, false).unwrap();
|
||||
let mut legacy_block = versioned_block.into_legacy_block().unwrap();
|
||||
legacy_block.transactions.retain(|tx_with_meta| {
|
||||
tx_with_meta
|
||||
.transaction
|
||||
.message
|
||||
.account_keys
|
||||
.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 {
|
||||
slot,
|
||||
block: Some(block),
|
||||
|
@ -1594,8 +1609,9 @@ pub(crate) mod tests {
|
|||
let actual_resp = receiver.recv();
|
||||
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 versioned_block = blockstore.get_complete_block(slot, false).unwrap();
|
||||
let legacy_block = versioned_block.into_legacy_block().unwrap();
|
||||
let block = legacy_block.configure(params.encoding, params.transaction_details, false);
|
||||
let expected_resp = RpcBlockUpdate {
|
||||
slot,
|
||||
block: Some(block),
|
||||
|
|
|
@ -141,7 +141,7 @@ impl TransactionStatusService {
|
|||
})
|
||||
.collect(),
|
||||
);
|
||||
|
||||
let loaded_addresses = transaction.get_loaded_addresses();
|
||||
let transaction_status_meta = TransactionStatusMeta {
|
||||
status,
|
||||
fee,
|
||||
|
@ -152,6 +152,7 @@ impl TransactionStatusService {
|
|||
pre_token_balances,
|
||||
post_token_balances,
|
||||
rewards,
|
||||
loaded_addresses,
|
||||
};
|
||||
|
||||
if let Some(transaction_notifier) = transaction_notifier.as_ref() {
|
||||
|
|
|
@ -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 {
|
||||
match self {
|
||||
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> {
|
||||
bincode::serialize(self).unwrap()
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ use {
|
|||
pubkey::Pubkey,
|
||||
sysvar,
|
||||
},
|
||||
std::{collections::HashSet, convert::TryFrom, ops::Deref},
|
||||
std::{collections::HashSet, ops::Deref},
|
||||
};
|
||||
|
||||
/// 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 {
|
||||
/// Returns an iterator of account key segments. The ordering of segments
|
||||
/// affects how account indexes from compiled instructions are resolved and
|
||||
|
|
|
@ -48,6 +48,9 @@ pub enum SignerError {
|
|||
|
||||
#[error("{0}")]
|
||||
UserCancel(String),
|
||||
|
||||
#[error("too many signers")]
|
||||
TooManySigners,
|
||||
}
|
||||
|
||||
/// The `Signer` trait declares operations that all digital signature providers
|
||||
|
|
|
@ -176,6 +176,14 @@ impl SanitizedTransaction {
|
|||
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
|
||||
pub fn get_durable_nonce(&self, nonce_must_be_writable: bool) -> Option<&Pubkey> {
|
||||
self.message.get_durable_nonce(nonce_must_be_writable)
|
||||
|
|
|
@ -9,6 +9,8 @@ use {
|
|||
sanitize::{Sanitize, SanitizeError},
|
||||
short_vec,
|
||||
signature::Signature,
|
||||
signer::SignerError,
|
||||
signers::Signers,
|
||||
transaction::{Result, Transaction, TransactionError},
|
||||
},
|
||||
serde::Serialize,
|
||||
|
@ -57,6 +59,40 @@ impl From<Transaction> for 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.
|
||||
pub fn into_legacy_transaction(self) -> Option<Transaction> {
|
||||
match self.message {
|
||||
|
|
|
@ -790,10 +790,13 @@ mod tests {
|
|||
super::*,
|
||||
crate::StoredConfirmedBlock,
|
||||
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_transaction_status::{
|
||||
ConfirmedBlock, TransactionStatusMeta, TransactionWithStatusMeta,
|
||||
VersionedConfirmedBlock,
|
||||
},
|
||||
std::convert::TryInto,
|
||||
};
|
||||
|
@ -815,6 +818,7 @@ mod tests {
|
|||
pre_token_balances: Some(vec![]),
|
||||
post_token_balances: Some(vec![]),
|
||||
rewards: Some(vec![]),
|
||||
loaded_addresses: LoadedAddresses::default(),
|
||||
}),
|
||||
};
|
||||
let block = ConfirmedBlock {
|
||||
|
@ -845,8 +849,9 @@ mod tests {
|
|||
"".to_string(),
|
||||
)
|
||||
.unwrap();
|
||||
let expected_block: VersionedConfirmedBlock = block.into();
|
||||
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 {
|
||||
panic!("deserialization should produce CellData::Protobuf");
|
||||
}
|
||||
|
@ -861,7 +866,7 @@ mod tests {
|
|||
)
|
||||
.unwrap();
|
||||
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 {
|
||||
meta.inner_instructions = None; // Legacy bincode implementation does not support inner_instructions
|
||||
meta.log_messages = None; // Legacy bincode implementation does not support log_messages
|
||||
|
|
|
@ -6,17 +6,18 @@ use {
|
|||
solana_sdk::{
|
||||
clock::{Slot, UnixTimestamp},
|
||||
deserialize_utils::default_on_eof,
|
||||
message::v0::LoadedAddresses,
|
||||
pubkey::Pubkey,
|
||||
signature::Signature,
|
||||
sysvar::is_sysvar_id,
|
||||
transaction::{Transaction, TransactionError},
|
||||
transaction::{TransactionError, VersionedTransaction},
|
||||
},
|
||||
solana_storage_proto::convert::{generated, tx_by_addr},
|
||||
solana_transaction_status::{
|
||||
extract_and_fmt_memos, ConfirmedBlock, ConfirmedTransactionStatusWithSignature,
|
||||
ConfirmedTransactionWithStatusMeta, Reward, TransactionByAddrInfo,
|
||||
TransactionConfirmationStatus, TransactionStatus, TransactionStatusMeta,
|
||||
TransactionWithStatusMeta,
|
||||
extract_and_fmt_memos, ConfirmedBlock, ConfirmedTransactionStatusWithSignature, Reward,
|
||||
TransactionByAddrInfo, TransactionConfirmationStatus, TransactionStatus,
|
||||
TransactionStatusMeta, TransactionWithStatusMeta, VersionedConfirmedBlock,
|
||||
VersionedConfirmedTransactionWithStatusMeta, VersionedTransactionWithStatusMeta,
|
||||
},
|
||||
std::{
|
||||
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 {
|
||||
let StoredConfirmedBlock {
|
||||
previous_blockhash,
|
||||
|
@ -164,20 +165,20 @@ impl From<StoredConfirmedBlock> for ConfirmedBlock {
|
|||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct StoredConfirmedBlockTransaction {
|
||||
transaction: Transaction,
|
||||
transaction: VersionedTransaction,
|
||||
meta: Option<StoredConfirmedBlockTransactionStatusMeta>,
|
||||
}
|
||||
|
||||
impl From<TransactionWithStatusMeta> for StoredConfirmedBlockTransaction {
|
||||
fn from(value: TransactionWithStatusMeta) -> Self {
|
||||
Self {
|
||||
transaction: value.transaction,
|
||||
transaction: value.transaction.into(),
|
||||
meta: value.meta.map(|meta| meta.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<StoredConfirmedBlockTransaction> for TransactionWithStatusMeta {
|
||||
impl From<StoredConfirmedBlockTransaction> for VersionedTransactionWithStatusMeta {
|
||||
fn from(value: StoredConfirmedBlockTransaction) -> Self {
|
||||
Self {
|
||||
transaction: value.transaction,
|
||||
|
@ -216,6 +217,7 @@ impl From<StoredConfirmedBlockTransactionStatusMeta> for TransactionStatusMeta {
|
|||
pre_token_balances: None,
|
||||
post_token_balances: None,
|
||||
rewards: None,
|
||||
loaded_addresses: LoadedAddresses::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -392,7 +394,7 @@ 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<VersionedConfirmedBlock> {
|
||||
debug!(
|
||||
"LedgerStorage::get_confirmed_block request received: {:?}",
|
||||
slot
|
||||
|
@ -438,7 +440,7 @@ impl LedgerStorage {
|
|||
pub async fn get_confirmed_transaction(
|
||||
&self,
|
||||
signature: &Signature,
|
||||
) -> Result<Option<ConfirmedTransactionWithStatusMeta>> {
|
||||
) -> Result<Option<VersionedConfirmedTransactionWithStatusMeta>> {
|
||||
debug!(
|
||||
"LedgerStorage::get_confirmed_transaction request received: {:?}",
|
||||
signature
|
||||
|
@ -471,9 +473,9 @@ impl LedgerStorage {
|
|||
);
|
||||
Ok(None)
|
||||
} else {
|
||||
Ok(Some(ConfirmedTransactionWithStatusMeta {
|
||||
Ok(Some(VersionedConfirmedTransactionWithStatusMeta {
|
||||
slot,
|
||||
transaction: bucket_block_transaction,
|
||||
tx_with_meta: bucket_block_transaction,
|
||||
block_time: block.block_time,
|
||||
}))
|
||||
}
|
||||
|
@ -627,7 +629,7 @@ impl LedgerStorage {
|
|||
pub async fn upload_confirmed_block(
|
||||
&self,
|
||||
slot: Slot,
|
||||
confirmed_block: ConfirmedBlock,
|
||||
confirmed_block: VersionedConfirmedBlock,
|
||||
) -> Result<()> {
|
||||
let mut bytes_written = 0;
|
||||
|
||||
|
@ -635,13 +637,13 @@ 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 VersionedTransactionWithStatusMeta { meta, transaction } = transaction_with_meta;
|
||||
let err = meta.as_ref().and_then(|meta| meta.status.clone().err());
|
||||
let index = index as u32;
|
||||
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) {
|
||||
by_addr
|
||||
.entry(address)
|
||||
|
@ -723,12 +725,12 @@ 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 VersionedTransactionWithStatusMeta { transaction, meta } = transaction_with_meta;
|
||||
let signature = transaction.signatures[0];
|
||||
let index = index as u32;
|
||||
let err = meta.as_ref().and_then(|meta| meta.status.clone().err());
|
||||
|
||||
for address in &transaction.message.account_keys {
|
||||
for address in transaction_with_meta.account_keys_iter() {
|
||||
if !is_sysvar_id(address) {
|
||||
addresses.insert(address);
|
||||
}
|
||||
|
|
|
@ -27,6 +27,8 @@ message Message {
|
|||
repeated bytes account_keys = 2;
|
||||
bytes recent_blockhash = 3;
|
||||
repeated CompiledInstruction instructions = 4;
|
||||
bool versioned = 5;
|
||||
repeated MessageAddressTableLookup address_table_lookups = 6;
|
||||
}
|
||||
|
||||
message MessageHeader {
|
||||
|
@ -35,6 +37,12 @@ message MessageHeader {
|
|||
uint32 num_readonly_unsigned_accounts = 3;
|
||||
}
|
||||
|
||||
message MessageAddressTableLookup {
|
||||
bytes account_key = 1;
|
||||
bytes writable_indexes = 2;
|
||||
bytes readonly_indexes = 3;
|
||||
}
|
||||
|
||||
message TransactionStatusMeta {
|
||||
TransactionError err = 1;
|
||||
uint64 fee = 2;
|
||||
|
@ -47,6 +55,8 @@ message TransactionStatusMeta {
|
|||
repeated TokenBalance pre_token_balances = 7;
|
||||
repeated TokenBalance post_token_balances = 8;
|
||||
repeated Reward rewards = 9;
|
||||
repeated bytes loaded_writable_addresses = 12;
|
||||
repeated bytes loaded_readonly_addresses = 13;
|
||||
}
|
||||
|
||||
message TransactionError {
|
||||
|
|
|
@ -4,14 +4,19 @@ use {
|
|||
solana_sdk::{
|
||||
hash::Hash,
|
||||
instruction::{CompiledInstruction, InstructionError},
|
||||
message::{Message, MessageHeader},
|
||||
message::{
|
||||
legacy::Message as LegacyMessage,
|
||||
v0::{self, LoadedAddresses, MessageAddressTableLookup},
|
||||
MessageHeader, VersionedMessage,
|
||||
},
|
||||
pubkey::Pubkey,
|
||||
signature::Signature,
|
||||
transaction::{Transaction, TransactionError},
|
||||
transaction::{Transaction, TransactionError, VersionedTransaction},
|
||||
},
|
||||
solana_transaction_status::{
|
||||
ConfirmedBlock, InnerInstructions, Reward, RewardType, TransactionByAddrInfo,
|
||||
TransactionStatusMeta, TransactionTokenBalance, TransactionWithStatusMeta,
|
||||
VersionedConfirmedBlock, VersionedTransactionWithStatusMeta,
|
||||
},
|
||||
std::{
|
||||
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 {
|
||||
fn from(confirmed_block: ConfirmedBlock) -> Self {
|
||||
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;
|
||||
fn try_from(
|
||||
confirmed_block: generated::ConfirmedBlock,
|
||||
|
@ -157,7 +186,7 @@ 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<_>, 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),
|
||||
|
@ -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;
|
||||
fn try_from(value: generated::ConfirmedTransaction) -> std::result::Result<Self, Self::Error> {
|
||||
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 {
|
||||
Self {
|
||||
signatures: value
|
||||
|
@ -212,32 +264,86 @@ impl From<generated::Transaction> for Transaction {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<Message> for generated::Message {
|
||||
fn from(value: Message) -> Self {
|
||||
impl From<LegacyMessage> for generated::Message {
|
||||
fn from(message: LegacyMessage) -> Self {
|
||||
Self {
|
||||
header: Some(value.header.into()),
|
||||
account_keys: value
|
||||
header: Some(message.header.into()),
|
||||
account_keys: message
|
||||
.account_keys
|
||||
.into_iter()
|
||||
.map(|key| <Pubkey as AsRef<[u8]>>::as_ref(&key).into())
|
||||
.iter()
|
||||
.map(|key| <Pubkey as AsRef<[u8]>>::as_ref(key).into())
|
||||
.collect(),
|
||||
recent_blockhash: value.recent_blockhash.to_bytes().into(),
|
||||
instructions: value.instructions.into_iter().map(|ix| ix.into()).collect(),
|
||||
recent_blockhash: message.recent_blockhash.to_bytes().into(),
|
||||
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 {
|
||||
Self {
|
||||
header: value.header.expect("header is required").into(),
|
||||
account_keys: value
|
||||
let header = value.header.expect("header is required").into();
|
||||
let account_keys = value
|
||||
.account_keys
|
||||
.into_iter()
|
||||
.map(|key| Pubkey::new(&key))
|
||||
.collect(),
|
||||
recent_blockhash: Hash::new(&value.recent_blockhash),
|
||||
instructions: value.instructions.into_iter().map(|ix| ix.into()).collect(),
|
||||
.collect();
|
||||
let recent_blockhash = Hash::new(&value.recent_blockhash);
|
||||
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,
|
||||
post_token_balances,
|
||||
rewards,
|
||||
loaded_addresses,
|
||||
} = value;
|
||||
let err = match status {
|
||||
Ok(()) => None,
|
||||
|
@ -304,6 +411,16 @@ impl From<TransactionStatusMeta> for generated::TransactionStatusMeta {
|
|||
.into_iter()
|
||||
.map(|reward| reward.into())
|
||||
.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 {
|
||||
err,
|
||||
|
@ -317,6 +434,8 @@ impl From<TransactionStatusMeta> for generated::TransactionStatusMeta {
|
|||
pre_token_balances,
|
||||
post_token_balances,
|
||||
rewards,
|
||||
loaded_writable_addresses,
|
||||
loaded_readonly_addresses,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -344,6 +463,8 @@ impl TryFrom<generated::TransactionStatusMeta> for TransactionStatusMeta {
|
|||
pre_token_balances,
|
||||
post_token_balances,
|
||||
rewards,
|
||||
loaded_writable_addresses,
|
||||
loaded_readonly_addresses,
|
||||
} = value;
|
||||
let status = match &err {
|
||||
None => Ok(()),
|
||||
|
@ -377,6 +498,16 @@ impl TryFrom<generated::TransactionStatusMeta> for TransactionStatusMeta {
|
|||
.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 {
|
||||
status,
|
||||
fee,
|
||||
|
@ -387,6 +518,7 @@ impl TryFrom<generated::TransactionStatusMeta> for TransactionStatusMeta {
|
|||
pre_token_balances,
|
||||
post_token_balances,
|
||||
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 {
|
||||
fn from(value: CompiledInstruction) -> Self {
|
||||
Self {
|
||||
|
|
|
@ -4,7 +4,9 @@ use {
|
|||
parse_token::{real_number_string_trimmed, UiTokenAmount},
|
||||
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::{
|
||||
InnerInstructions, Reward, RewardType, TransactionStatusMeta, TransactionTokenBalance,
|
||||
},
|
||||
|
@ -193,12 +195,14 @@ impl From<StoredTransactionStatusMeta> for TransactionStatusMeta {
|
|||
.map(|balances| balances.into_iter().map(|balance| balance.into()).collect()),
|
||||
rewards: rewards
|
||||
.map(|rewards| rewards.into_iter().map(|reward| reward.into()).collect()),
|
||||
loaded_addresses: LoadedAddresses::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TransactionStatusMeta> for StoredTransactionStatusMeta {
|
||||
fn from(value: TransactionStatusMeta) -> Self {
|
||||
impl TryFrom<TransactionStatusMeta> for StoredTransactionStatusMeta {
|
||||
type Error = bincode::Error;
|
||||
fn try_from(value: TransactionStatusMeta) -> std::result::Result<Self, Self::Error> {
|
||||
let TransactionStatusMeta {
|
||||
status,
|
||||
fee,
|
||||
|
@ -209,8 +213,18 @@ impl From<TransactionStatusMeta> for StoredTransactionStatusMeta {
|
|||
pre_token_balances,
|
||||
post_token_balances,
|
||||
rewards,
|
||||
loaded_addresses,
|
||||
} = 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,
|
||||
fee,
|
||||
pre_balances,
|
||||
|
@ -223,6 +237,6 @@ impl From<TransactionStatusMeta> for StoredTransactionStatusMeta {
|
|||
.map(|balances| balances.into_iter().map(|balance| balance.into()).collect()),
|
||||
rewards: rewards
|
||||
.map(|rewards| rewards.into_iter().map(|reward| reward.into()).collect()),
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use {
|
||||
crate::parse_instruction::parse_memo_data,
|
||||
crate::{parse_instruction::parse_memo_data, VersionedTransactionWithStatusMeta},
|
||||
solana_sdk::{
|
||||
instruction::CompiledInstruction,
|
||||
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> {
|
||||
MemoProgram,
|
||||
OtherProgram,
|
||||
|
|
|
@ -26,11 +26,11 @@ use {
|
|||
clock::{Slot, UnixTimestamp},
|
||||
commitment_config::CommitmentConfig,
|
||||
instruction::CompiledInstruction,
|
||||
message::{Message, MessageHeader},
|
||||
message::{v0::LoadedAddresses, Message, MessageHeader},
|
||||
pubkey::Pubkey,
|
||||
sanitize::Sanitize,
|
||||
signature::Signature,
|
||||
transaction::{Result, Transaction, TransactionError},
|
||||
transaction::{Result, Transaction, TransactionError, VersionedTransaction},
|
||||
},
|
||||
std::fmt,
|
||||
};
|
||||
|
@ -82,13 +82,13 @@ pub enum UiInstruction {
|
|||
}
|
||||
|
||||
impl UiInstruction {
|
||||
fn parse(instruction: &CompiledInstruction, message: &Message) -> Self {
|
||||
let program_id = instruction.program_id(&message.account_keys);
|
||||
if let Ok(parsed_instruction) = parse(program_id, instruction, &message.account_keys) {
|
||||
fn parse(instruction: &CompiledInstruction, account_keys: &[Pubkey]) -> Self {
|
||||
let program_id = instruction.program_id(account_keys);
|
||||
if let Ok(parsed_instruction) = parse(program_id, instruction, account_keys) {
|
||||
UiInstruction::Parsed(UiParsedInstruction::Parsed(parsed_instruction))
|
||||
} else {
|
||||
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
|
||||
.iter()
|
||||
.map(|ix| UiInstruction::parse(ix, message))
|
||||
.map(|ix| UiInstruction::parse(ix, &message.account_keys))
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
|
@ -230,6 +230,7 @@ pub struct TransactionStatusMeta {
|
|||
pub pre_token_balances: Option<Vec<TransactionTokenBalance>>,
|
||||
pub post_token_balances: Option<Vec<TransactionTokenBalance>>,
|
||||
pub rewards: Option<Rewards>,
|
||||
pub loaded_addresses: LoadedAddresses,
|
||||
}
|
||||
|
||||
impl Default for TransactionStatusMeta {
|
||||
|
@ -244,6 +245,7 @@ impl Default for TransactionStatusMeta {
|
|||
pre_token_balances: None,
|
||||
post_token_balances: None,
|
||||
rewards: None,
|
||||
loaded_addresses: LoadedAddresses::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -397,6 +399,51 @@ pub struct ConfirmedBlock {
|
|||
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 {
|
||||
type Encoded = EncodedConfirmedBlock;
|
||||
fn encode(self, encoding: UiTransactionEncoding) -> Self::Encoded {
|
||||
|
@ -428,7 +475,7 @@ impl ConfirmedBlock {
|
|||
Some(
|
||||
self.transactions
|
||||
.into_iter()
|
||||
.map(|tx| tx.encode(encoding))
|
||||
.map(|tx_with_meta| tx_with_meta.encode(encoding))
|
||||
.collect(),
|
||||
),
|
||||
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)]
|
||||
pub struct TransactionWithStatusMeta {
|
||||
pub transaction: Transaction,
|
||||
|
@ -531,7 +613,7 @@ impl Encodable for TransactionWithStatusMeta {
|
|||
transaction: self.transaction.encode(encoding),
|
||||
meta: self.meta.map(|meta| match encoding {
|
||||
UiTransactionEncoding::JsonParsed => {
|
||||
UiTransactionStatusMeta::parse(meta, self.transaction.message())
|
||||
UiTransactionStatusMeta::parse(meta, &self.transaction.message)
|
||||
}
|
||||
_ => UiTransactionStatusMeta::from(meta),
|
||||
}),
|
||||
|
@ -545,6 +627,7 @@ pub struct EncodedTransactionWithStatusMeta {
|
|||
pub transaction: EncodedTransaction,
|
||||
pub meta: Option<UiTransactionStatusMeta>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct ConfirmedTransactionWithStatusMeta {
|
||||
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)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct EncodedConfirmedTransactionWithStatusMeta {
|
||||
|
@ -655,7 +760,7 @@ impl Encodable for &Message {
|
|||
instructions: self
|
||||
.instructions
|
||||
.iter()
|
||||
.map(|instruction| UiInstruction::parse(instruction, self))
|
||||
.map(|instruction| UiInstruction::parse(instruction, &self.account_keys))
|
||||
.collect(),
|
||||
})
|
||||
} else {
|
||||
|
|
Loading…
Reference in New Issue