Skip to content
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions cli/src/cluster_query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2299,6 +2299,7 @@ pub async fn process_transaction_history(
block_time,
slot,
transaction: transaction_with_meta,
..
} = confirmed_transaction;

let decoded_transaction =
Expand Down
1 change: 1 addition & 0 deletions cli/src/wallet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -777,6 +777,7 @@ pub async fn process_confirm(
block_time,
slot,
transaction: transaction_with_meta,
..
} = confirmed_transaction;

let decoded_transaction =
Expand Down
65 changes: 40 additions & 25 deletions ledger/src/blockstore.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3166,7 +3166,7 @@ impl Blockstore {
if let Some((slot, meta)) =
self.get_transaction_status(signature, confirmed_unrooted_slots)?
{
let transaction = self
let (transaction, index) = self
.find_transaction_in_slot(slot, signature)?
.ok_or(BlockstoreError::TransactionStatusSlotMismatch)?; // Should not happen

Expand All @@ -3177,32 +3177,40 @@ impl Blockstore {
VersionedTransactionWithStatusMeta { transaction, meta },
),
block_time,
index,
}))
} else {
Ok(None)
}
}

/// Finds a transaction by signature in the given slot and returns it along with its index.
///
/// The index represents the transaction's 0-based position in the flattened list of all
/// transactions across all entries in this slot. This matches the `transaction_index`
/// stored in `AddressSignatures` when `write_transaction_status` is called during block
/// processing.
fn find_transaction_in_slot(
&self,
slot: Slot,
signature: Signature,
) -> Result<Option<VersionedTransaction>> {
) -> Result<Option<(VersionedTransaction, u32)>> {
let slot_entries = self.get_slot_entries(slot, 0)?;
Ok(slot_entries
.iter()
.cloned()
.into_iter()
.flat_map(|entry| entry.transactions)
.map(|transaction| {
.enumerate()
.map(|(index, transaction)| {
if let Err(err) = transaction.sanitize() {
warn!(
"Blockstore::find_transaction_in_slot sanitize failed: {err:?}, slot: \
{slot:?}, {transaction:?}",
);
}
transaction
(index, transaction)
})
.find(|transaction| transaction.signatures[0] == signature))
.find(|(_, transaction)| transaction.signatures[0] == signature)
.map(|(index, transaction)| (transaction, index as u32)))
}

// DEPRECATED and decommissioned
Expand All @@ -3222,9 +3230,9 @@ impl Blockstore {
&self,
pubkey: Pubkey,
slot: Slot,
) -> Result<Vec<(Slot, Signature)>> {
) -> Result<Vec<(Slot, Signature, u32)>> {
let (lock, lowest_available_slot) = self.ensure_lowest_cleanup_slot();
let mut signatures: Vec<(Slot, Signature)> = vec![];
let mut signatures: Vec<(Slot, Signature, u32)> = vec![];
if slot < lowest_available_slot {
return Ok(signatures);
}
Expand All @@ -3239,11 +3247,11 @@ impl Blockstore {
),
IteratorDirection::Forward,
))?;
for ((address, transaction_slot, _transaction_index, signature), _) in index_iterator {
for ((address, transaction_slot, transaction_index, signature), _) in index_iterator {
if transaction_slot > slot || address != pubkey {
break;
}
signatures.push((slot, signature));
signatures.push((slot, signature, transaction_index));
}
drop(lock);
Ok(signatures)
Expand Down Expand Up @@ -3346,7 +3354,7 @@ impl Blockstore {
get_until_slot_timer.stop();

// Fetch the list of signatures that affect the given address
let mut address_signatures = vec![];
let mut address_signatures: Vec<(Slot, Signature, u32)> = vec![];

// Get signatures in `slot`
let mut get_initial_slot_timer = Measure::start("get_initial_slot_timer");
Expand All @@ -3356,7 +3364,7 @@ impl Blockstore {
address_signatures.extend(
signatures
.into_iter()
.filter(|(_, signature)| !excluded_signatures.contains(signature)),
.filter(|(_, signature, _)| !excluded_signatures.contains(signature)),
)
} else {
address_signatures.append(&mut signatures);
Expand All @@ -3378,13 +3386,13 @@ impl Blockstore {

// Iterate until limit is reached
while address_signatures.len() < limit {
if let Some(((key_address, slot, _transaction_index, signature), _)) = iterator.next() {
if let Some(((key_address, slot, transaction_index, signature), _)) = iterator.next() {
if slot < lowest_slot {
break;
}
if key_address == address {
if self.is_root(slot) || confirmed_unrooted_slots.contains(&slot) {
address_signatures.push((slot, signature));
address_signatures.push((slot, signature, transaction_index));
}
continue;
}
Expand All @@ -3395,13 +3403,13 @@ impl Blockstore {

let address_signatures_iter = address_signatures
.into_iter()
.filter(|(_, signature)| !until_excluded_signatures.contains(signature))
.filter(|(_, signature, _)| !until_excluded_signatures.contains(signature))
.take(limit);

// Fill in the status information for each found transaction
let mut get_status_info_timer = Measure::start("get_status_info_timer");
let mut infos = vec![];
for (slot, signature) in address_signatures_iter {
for (slot, signature, index) in address_signatures_iter {
let transaction_status =
self.get_transaction_status(signature, &confirmed_unrooted_slots)?;
let err = transaction_status.and_then(|(_slot, status)| status.status.err());
Expand All @@ -3413,6 +3421,7 @@ impl Blockstore {
err,
memo,
block_time,
index,
});
}
get_status_info_timer.stop();
Expand Down Expand Up @@ -8868,14 +8877,15 @@ pub mod tests {
})
.collect();

for tx_with_meta in expected_transactions.clone() {
for (index, tx_with_meta) in expected_transactions.clone().into_iter().enumerate() {
let signature = tx_with_meta.transaction.signatures[0];
assert_eq!(
blockstore.get_rooted_transaction(signature).unwrap(),
Some(ConfirmedTransactionWithStatusMeta {
slot,
tx_with_meta: TransactionWithStatusMeta::Complete(tx_with_meta.clone()),
block_time: None
block_time: None,
index: index as u32,
})
);
assert_eq!(
Expand All @@ -8885,7 +8895,8 @@ pub mod tests {
Some(ConfirmedTransactionWithStatusMeta {
slot,
tx_with_meta: TransactionWithStatusMeta::Complete(tx_with_meta),
block_time: None
block_time: None,
index: index as u32,
})
);
}
Expand Down Expand Up @@ -8991,7 +9002,7 @@ pub mod tests {
})
.collect();

for tx_with_meta in expected_transactions.clone() {
for (index, tx_with_meta) in expected_transactions.clone().into_iter().enumerate() {
let signature = tx_with_meta.transaction.signatures[0];
assert_eq!(
blockstore
Expand All @@ -9000,7 +9011,8 @@ pub mod tests {
Some(ConfirmedTransactionWithStatusMeta {
slot,
tx_with_meta: TransactionWithStatusMeta::Complete(tx_with_meta),
block_time: None
block_time: None,
index: index as u32,
})
);
assert_eq!(blockstore.get_rooted_transaction(signature).unwrap(), None);
Expand Down Expand Up @@ -9135,25 +9147,28 @@ pub mod tests {
let slot1_signatures = blockstore
.find_address_signatures_for_slot(address0, 1)
.unwrap();
for (i, (slot, signature)) in slot1_signatures.iter().enumerate() {
for (i, (slot, signature, index)) in slot1_signatures.iter().enumerate() {
assert_eq!(*slot, slot1);
assert_eq!(*signature, Signature::from([i as u8 + 1; 64]));
assert_eq!(*index, (i + 1) as u32);
}

let slot2_signatures = blockstore
.find_address_signatures_for_slot(address0, 2)
.unwrap();
for (i, (slot, signature)) in slot2_signatures.iter().enumerate() {
for (i, (slot, signature, index)) in slot2_signatures.iter().enumerate() {
assert_eq!(*slot, slot2);
assert_eq!(*signature, Signature::from([i as u8 + 5; 64]));
assert_eq!(*index, (i + 5) as u32);
}

let slot3_signatures = blockstore
.find_address_signatures_for_slot(address0, 3)
.unwrap();
for (i, (slot, signature)) in slot3_signatures.iter().enumerate() {
for (i, (slot, signature, index)) in slot3_signatures.iter().enumerate() {
assert_eq!(*slot, slot3);
assert_eq!(*signature, Signature::from([i as u8 + 9; 64]));
assert_eq!(*index, (i + 9) as u32);
}
}

Expand Down
1 change: 1 addition & 0 deletions rpc-client-types/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,4 @@ thiserror = { workspace = true }
[dev-dependencies]
const_format = { workspace = true }
solana-pubkey = { workspace = true, features = ["rand"] }
solana-signature = { workspace = true }
27 changes: 27 additions & 0 deletions rpc-client-types/src/response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,8 @@ pub struct RpcConfirmedTransactionStatusWithSignature {
pub memo: Option<String>,
pub block_time: Option<UnixTimestamp>,
pub confirmation_status: Option<TransactionConfirmationStatus>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub transaction_index: Option<u32>,
}

#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
Expand Down Expand Up @@ -545,6 +547,7 @@ impl From<ConfirmedTransactionStatusWithSignature> for RpcConfirmedTransactionSt
err,
memo,
block_time,
index,
} = value;
Self {
signature: signature.to_string(),
Expand All @@ -553,6 +556,7 @@ impl From<ConfirmedTransactionStatusWithSignature> for RpcConfirmedTransactionSt
memo,
block_time,
confirmation_status: None,
transaction_index: Some(index),
}
}
}
Expand Down Expand Up @@ -633,4 +637,27 @@ pub mod tests {

assert_eq!(actual, expected);
}

#[test]
fn test_rpc_confirmed_transaction_status_with_signature_from() {
let signature = solana_signature::Signature::from([1u8; 64]);
let input = ConfirmedTransactionStatusWithSignature {
signature,
slot: 123,
err: None,
memo: Some("test memo".to_string()),
block_time: Some(1234567890),
index: 42,
};

let result: RpcConfirmedTransactionStatusWithSignature = input.into();

assert_eq!(result.signature, signature.to_string());
assert_eq!(result.slot, 123);
assert_eq!(result.err, None);
assert_eq!(result.memo, Some("test memo".to_string()));
assert_eq!(result.block_time, Some(1234567890));
assert_eq!(result.confirmation_status, None);
assert_eq!(result.transaction_index, Some(42));
}
}
2 changes: 2 additions & 0 deletions rpc-client/src/mock_sender.rs
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,7 @@ impl RpcSender for MockSender {
}),
},
block_time: Some(1628633791),
transaction_index: Some(0),
})?,
"getTransactionCount" => json![1234],
"getSlot" => json![0],
Expand Down Expand Up @@ -430,6 +431,7 @@ impl RpcSender for MockSender {
memo: None,
block_time: None,
confirmation_status: Some(TransactionConfirmationStatus::Finalized),
transaction_index: Some(0),
}])?
}
"getBlockTime" => serde_json::to_value(UnixTimestamp::default())?,
Expand Down
3 changes: 3 additions & 0 deletions storage-bigtable/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -698,6 +698,7 @@ impl LedgerStorage {
slot,
tx_with_meta: tx_with_meta.clone(),
block_time: block.block_time,
index,
})
}
})
Expand Down Expand Up @@ -741,6 +742,7 @@ impl LedgerStorage {
slot,
tx_with_meta,
block_time: block.block_time,
index,
}))
}
}
Expand Down Expand Up @@ -881,6 +883,7 @@ impl LedgerStorage {
err: tx_by_addr_info.err,
memo: tx_by_addr_info.memo,
block_time: tx_by_addr_info.block_time,
index: tx_by_addr_info.index,
},
tx_by_addr_info.index,
));
Expand Down
3 changes: 3 additions & 0 deletions transaction-status-client-types/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ pub struct ConfirmedTransactionStatusWithSignature {
pub err: Option<TransactionError>,
pub memo: Option<String>,
pub block_time: Option<i64>,
pub index: u32,
Comment thread
sam0x17 marked this conversation as resolved.
}

#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
Expand Down Expand Up @@ -712,6 +713,8 @@ pub struct EncodedConfirmedTransactionWithStatusMeta {
#[serde(flatten)]
pub transaction: EncodedTransactionWithStatusMeta,
pub block_time: Option<i64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub transaction_index: Option<u32>,
}

#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
Expand Down
Loading
Loading