Rework get_confirmed_signatures_for_address2

This commit is contained in:
Michael Vines 2020-08-05 11:21:22 -07:00
parent 087fd32ce3
commit a11f137810
1 changed files with 84 additions and 64 deletions

View File

@ -38,14 +38,14 @@ use solana_sdk::{
}; };
use solana_transaction_status::{ use solana_transaction_status::{
ConfirmedBlock, ConfirmedTransaction, ConfirmedTransactionStatusWithSignature, ConfirmedBlock, ConfirmedTransaction, ConfirmedTransactionStatusWithSignature,
EncodedTransaction, Rewards, TransactionStatusMeta, TransactionWithStatusMeta, UiMessage, EncodedTransaction, Rewards, TransactionStatusMeta, TransactionWithStatusMeta,
UiTransactionEncoding, UiTransactionStatusMeta, UiTransactionEncoding, UiTransactionStatusMeta,
}; };
use solana_vote_program::{vote_instruction::VoteInstruction, vote_state::TIMESTAMP_SLOT_INTERVAL}; use solana_vote_program::{vote_instruction::VoteInstruction, vote_state::TIMESTAMP_SLOT_INTERVAL};
use std::{ use std::{
cell::RefCell, cell::RefCell,
cmp, cmp,
collections::HashMap, collections::{HashMap, HashSet},
fs, fs,
io::{Error as IOError, ErrorKind}, io::{Error as IOError, ErrorKind},
path::{Path, PathBuf}, path::{Path, PathBuf},
@ -1872,7 +1872,8 @@ impl Blockstore {
} }
// Returns all cached signatures for an address, ordered by slot that the transaction was // Returns all cached signatures for an address, ordered by slot that the transaction was
// processed in // processed in. Within each slot the transactions will be ordered by signature, and NOT by
// the order in which the transactions exist in the block
fn find_address_signatures( fn find_address_signatures(
&self, &self,
pubkey: Pubkey, pubkey: Pubkey,
@ -1938,52 +1939,53 @@ impl Blockstore {
) )
); );
let (mut slot, mut start_after) = match start_after { // Figure the `slot` to start listing signatures at, based on the ledger location of the
// `start_after` signature if present. Also generate a HashSet of signatures that should
// be excluded from the results.
let (mut slot, mut excluded_signatures) = match start_after {
None => (highest_confirmed_root, None), None => (highest_confirmed_root, None),
Some(start_after) => { Some(start_after) => {
let confirmed_transaction = let transaction_status = self.get_transaction_status(start_after)?;
self.get_confirmed_transaction(start_after, Some(UiTransactionEncoding::Json))?; match transaction_status {
match confirmed_transaction {
None => return Ok(vec![]), None => return Ok(vec![]),
Some(ConfirmedTransaction { slot, transaction }) => { Some((slot, _)) => {
// Ensure that `start_after` is from a transaction that contains `address` let confirmed_block = self
match transaction.transaction { .get_confirmed_block(slot, Some(UiTransactionEncoding::Binary))
EncodedTransaction::Json(ui_transaction) => { .map_err(|err| {
match ui_transaction.message { BlockstoreError::IO(IOError::new(
UiMessage::Raw(message) => {
let address = address.to_string();
if !message
.account_keys
.iter()
.any(|account_address| *account_address == address)
{
return Err(BlockstoreError::IO(IOError::new(
ErrorKind::Other,
format!(
"Invalid start_after signature: {}",
start_after
),
)));
}
}
_ => {
// Should never happen...
return Err(BlockstoreError::IO(IOError::new(
ErrorKind::Other,
"Unexpected transaction message encoding".to_string(),
)));
}
}
}
_ => {
// Should never happen...
return Err(BlockstoreError::IO(IOError::new(
ErrorKind::Other, ErrorKind::Other,
"Unexpected transaction encoding".to_string(), format!("Unable to get confirmed block: {}", err),
))); ))
} })?;
// Load all signatures for the block
let mut slot_signatures: Vec<_> = confirmed_block
.transactions
.iter()
.filter_map(|transaction_with_meta| {
if let Some(transaction) =
transaction_with_meta.transaction.decode()
{
transaction.signatures.into_iter().next()
} else {
None
}
})
.collect();
// Sort signatures as a way to entire a stable ordering within a slot, as
// `self.find_address_signatures()` is ordered by signatures ordered and
// not by block ordering
slot_signatures.sort();
if let Some(pos) = slot_signatures.iter().position(|&x| x == start_after) {
slot_signatures.truncate(pos + 1);
} }
(slot, Some(start_after))
(
slot,
Some(slot_signatures.into_iter().collect::<HashSet<_>>()),
)
} }
} }
} }
@ -1999,23 +2001,16 @@ impl Blockstore {
} }
let mut signatures = self.find_address_signatures(address, slot, slot)?; let mut signatures = self.find_address_signatures(address, slot, slot)?;
if let Some(start_after) = start_after { if let Some(excluded_signatures) = excluded_signatures.take() {
if let Some(index) = signatures address_signatures.extend(
.iter() signatures
.position(|(_slot, signature)| *signature == start_after) .into_iter()
{ .filter(|(_, signature)| !excluded_signatures.contains(&signature)),
address_signatures.append(&mut signatures.split_off(index + 1)); )
signatures.clear(); } else {
} else { address_signatures.append(&mut signatures);
// Should never happen...
warn!(
"Blockstore corruption? Expected to find {} in slot {}",
start_after, slot
);
}
} }
address_signatures.append(&mut signatures); excluded_signatures = None;
start_after = None;
if slot == first_available_block { if slot == first_available_block {
break; break;
@ -6294,7 +6289,7 @@ pub mod tests {
) )
.unwrap(); .unwrap();
assert_eq!(results.len(), 1); assert_eq!(results.len(), 1);
assert_eq!(results[0], all0[i]); assert_eq!(results[0], all0[i], "Unexpected result for {}", i);
} }
assert!(blockstore assert!(blockstore
@ -6328,15 +6323,40 @@ pub mod tests {
assert_eq!(results[2], all0[i + 2]); assert_eq!(results[2], all0[i + 2]);
} }
// `start_after` signature from address1 should fail // Ensure that the signatures within a slot are ordered by signature
blockstore // (current limitation of the .get_confirmed_signatures_for_address2())
for i in (0..all1.len()).step_by(2) {
let results = blockstore
.get_confirmed_signatures_for_address2(
address1,
highest_confirmed_root,
if i == 0 {
None
} else {
Some(all1[i - 1].signature)
},
2,
)
.unwrap();
assert_eq!(results.len(), 2);
assert_eq!(results[0].slot, results[1].slot);
assert!(results[0].signature <= results[1].signature);
assert_eq!(results[0], all1[i]);
assert_eq!(results[1], all1[i + 1]);
}
// A search for address 0 with a `start_after` signature from address1 should also work
let results = blockstore
.get_confirmed_signatures_for_address2( .get_confirmed_signatures_for_address2(
address0, address0,
highest_confirmed_root, highest_confirmed_root,
Some(all1[0].signature), Some(all1[0].signature),
usize::MAX, usize::MAX,
) )
.unwrap_err(); .unwrap();
// The exact number of results returned is variable, based on the sort order of the
// random signatures that are generated
assert!(!results.is_empty());
} }
Blockstore::destroy(&blockstore_path).expect("Expected successful database destruction"); Blockstore::destroy(&blockstore_path).expect("Expected successful database destruction");
} }