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",
|
"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",
|
||||||
|
|
|
@ -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()],
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)]
|
||||||
|
|
|
@ -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" }
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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] {
|
||||||
|
|
|
@ -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),
|
||||||
|
|
|
@ -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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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))?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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),
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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()),
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
Loading…
Reference in New Issue