Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,5 @@ log-*/
.DS_Store
# scripts that may be generated by cargo *-bpf commands
**/cargo-*-bpf-child-script-*.sh

.claude/
1 change: 1 addition & 0 deletions cli/src/cluster_query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2297,6 +2297,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
62 changes: 39 additions & 23 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,41 @@ 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()
.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 +3231,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 +3248,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 +3355,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 +3365,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 +3387,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 +3404,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 +3422,7 @@ impl Blockstore {
err,
memo,
block_time,
index,
});
}
get_status_info_timer.stop();
Expand Down Expand Up @@ -8868,14 +8878,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 +8896,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 +9003,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 +9012,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 +9148,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
3 changes: 3 additions & 0 deletions rpc-client-types/src/response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,7 @@ pub struct RpcConfirmedTransactionStatusWithSignature {
pub memo: Option<String>,
pub block_time: Option<UnixTimestamp>,
pub confirmation_status: Option<TransactionConfirmationStatus>,
pub transaction_index: u32,
}

#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
Expand Down Expand Up @@ -543,6 +544,7 @@ impl From<ConfirmedTransactionStatusWithSignature> for RpcConfirmedTransactionSt
err,
memo,
block_time,
index,
} = value;
Self {
signature: signature.to_string(),
Expand All @@ -551,6 +553,7 @@ impl From<ConfirmedTransactionStatusWithSignature> for RpcConfirmedTransactionSt
memo,
block_time,
confirmation_status: None,
transaction_index: index,
}
}
}
Expand Down
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: 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: 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 @@ -697,6 +697,7 @@ impl LedgerStorage {
slot,
tx_with_meta: tx_with_meta.clone(),
block_time: block.block_time,
index,
})
}
})
Expand Down Expand Up @@ -740,6 +741,7 @@ impl LedgerStorage {
slot,
tx_with_meta,
block_time: block.block_time,
index,
}))
}
}
Expand Down Expand Up @@ -880,6 +882,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
2 changes: 2 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,
}

#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
Expand Down Expand Up @@ -710,6 +711,7 @@ pub struct EncodedConfirmedTransactionWithStatusMeta {
#[serde(flatten)]
pub transaction: EncodedTransactionWithStatusMeta,
pub block_time: Option<i64>,
pub transaction_index: u32,
}

#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
Expand Down
2 changes: 2 additions & 0 deletions transaction-status/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -596,6 +596,7 @@ pub struct ConfirmedTransactionWithStatusMeta {
pub slot: Slot,
pub tx_with_meta: TransactionWithStatusMeta,
pub block_time: Option<UnixTimestamp>,
pub index: u32,
}

#[derive(Debug, Clone, PartialEq)]
Expand All @@ -619,6 +620,7 @@ impl ConfirmedTransactionWithStatusMeta {
true,
)?,
block_time: self.block_time,
transaction_index: self.index,
})
}

Expand Down