Add getConfirmedSignaturesForAddress2 RPC method
This commit is contained in:
@ -37,8 +37,9 @@ use solana_sdk::{
|
||||
transaction::Transaction,
|
||||
};
|
||||
use solana_transaction_status::{
|
||||
ConfirmedBlock, ConfirmedTransaction, EncodedTransaction, Rewards, TransactionStatusMeta,
|
||||
TransactionWithStatusMeta, UiTransactionEncoding, UiTransactionStatusMeta,
|
||||
ConfirmedBlock, ConfirmedTransaction, ConfirmedTransactionStatusWithSignature,
|
||||
EncodedTransaction, Rewards, TransactionStatusMeta, TransactionWithStatusMeta, UiMessage,
|
||||
UiTransactionEncoding, UiTransactionStatusMeta,
|
||||
};
|
||||
use solana_vote_program::{vote_instruction::VoteInstruction, vote_state::TIMESTAMP_SLOT_INTERVAL};
|
||||
use std::{
|
||||
@ -1921,6 +1922,127 @@ impl Blockstore {
|
||||
.map(|signatures| signatures.iter().map(|(_, signature)| *signature).collect())
|
||||
}
|
||||
|
||||
pub fn get_confirmed_signatures_for_address2(
|
||||
&self,
|
||||
address: Pubkey,
|
||||
highest_confirmed_root: Slot,
|
||||
start_after: Option<Signature>,
|
||||
limit: usize,
|
||||
) -> Result<Vec<ConfirmedTransactionStatusWithSignature>> {
|
||||
datapoint_info!(
|
||||
"blockstore-rpc-api",
|
||||
(
|
||||
"method",
|
||||
"get_confirmed_signatures_for_address2".to_string(),
|
||||
String
|
||||
)
|
||||
);
|
||||
|
||||
let (mut slot, mut start_after) = match start_after {
|
||||
None => (highest_confirmed_root, None),
|
||||
Some(start_after) => {
|
||||
let confirmed_transaction =
|
||||
self.get_confirmed_transaction(start_after, Some(UiTransactionEncoding::Json))?;
|
||||
match confirmed_transaction {
|
||||
None => return Ok(vec![]),
|
||||
Some(ConfirmedTransaction { slot, transaction }) => {
|
||||
// Ensure that `start_after` is from a transaction that contains `address`
|
||||
match transaction.transaction {
|
||||
EncodedTransaction::Json(ui_transaction) => {
|
||||
match ui_transaction.message {
|
||||
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,
|
||||
"Unexpected transaction encoding".to_string(),
|
||||
)));
|
||||
}
|
||||
}
|
||||
(slot, Some(start_after))
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Fetch the list of signatures that affect the given address
|
||||
let first_available_block = self.get_first_available_block()?;
|
||||
let mut address_signatures = vec![];
|
||||
loop {
|
||||
if address_signatures.len() >= limit {
|
||||
address_signatures.truncate(limit);
|
||||
break;
|
||||
}
|
||||
|
||||
let mut signatures = self.find_address_signatures(address, slot, slot)?;
|
||||
if let Some(start_after) = start_after {
|
||||
if let Some(index) = signatures
|
||||
.iter()
|
||||
.position(|(_slot, signature)| *signature == start_after)
|
||||
{
|
||||
address_signatures.append(&mut signatures.split_off(index + 1));
|
||||
signatures.clear();
|
||||
} else {
|
||||
// Should never happen...
|
||||
warn!(
|
||||
"Blockstore corruption? Expected to find {} in slot {}",
|
||||
start_after, slot
|
||||
);
|
||||
}
|
||||
}
|
||||
address_signatures.append(&mut signatures);
|
||||
start_after = None;
|
||||
|
||||
if slot == first_available_block {
|
||||
break;
|
||||
}
|
||||
slot -= 1;
|
||||
}
|
||||
address_signatures.truncate(limit);
|
||||
|
||||
// Fill in the status information for each found transaction
|
||||
let mut infos = vec![];
|
||||
for (slot, signature) in address_signatures.into_iter() {
|
||||
let transaction_status = self.get_transaction_status(signature)?;
|
||||
let err = match transaction_status {
|
||||
None => None,
|
||||
Some((_slot, status)) => status.status.err(),
|
||||
};
|
||||
infos.push(ConfirmedTransactionStatusWithSignature {
|
||||
signature,
|
||||
slot,
|
||||
err,
|
||||
memo: None,
|
||||
});
|
||||
}
|
||||
|
||||
Ok(infos)
|
||||
}
|
||||
|
||||
pub fn read_rewards(&self, index: Slot) -> Result<Option<Rewards>> {
|
||||
self.rewards_cf.get(index)
|
||||
}
|
||||
@ -6082,6 +6204,143 @@ pub mod tests {
|
||||
Blockstore::destroy(&blockstore_path).expect("Expected successful database destruction");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_confirmed_signatures_for_address2() {
|
||||
let blockstore_path = get_tmp_ledger_path!();
|
||||
{
|
||||
let blockstore = Blockstore::open(&blockstore_path).unwrap();
|
||||
|
||||
fn make_slot_entries_with_transaction_addresses(addresses: &[Pubkey]) -> Vec<Entry> {
|
||||
let mut entries: Vec<Entry> = Vec::new();
|
||||
for address in addresses {
|
||||
let transaction = Transaction::new_with_compiled_instructions(
|
||||
&[&Keypair::new()],
|
||||
&[*address],
|
||||
Hash::default(),
|
||||
vec![Pubkey::new_rand()],
|
||||
vec![CompiledInstruction::new(1, &(), vec![0])],
|
||||
);
|
||||
entries.push(next_entry_mut(&mut Hash::default(), 0, vec![transaction]));
|
||||
let mut tick = create_ticks(1, 0, hash(&serialize(address).unwrap()));
|
||||
entries.append(&mut tick);
|
||||
}
|
||||
entries
|
||||
}
|
||||
|
||||
let address0 = Pubkey::new_rand();
|
||||
let address1 = Pubkey::new_rand();
|
||||
|
||||
for slot in 2..=4 {
|
||||
let entries = make_slot_entries_with_transaction_addresses(&[
|
||||
address0, address1, address0, address1,
|
||||
]);
|
||||
let shreds = entries_to_test_shreds(entries.clone(), slot, slot - 1, true, 0);
|
||||
blockstore.insert_shreds(shreds, None, false).unwrap();
|
||||
|
||||
for entry in &entries {
|
||||
for transaction in &entry.transactions {
|
||||
assert_eq!(transaction.signatures.len(), 1);
|
||||
blockstore
|
||||
.write_transaction_status(
|
||||
slot,
|
||||
transaction.signatures[0],
|
||||
transaction.message.account_keys.iter().collect(),
|
||||
vec![],
|
||||
&TransactionStatusMeta::default(),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
blockstore.set_roots(&[1, 2, 3, 4]).unwrap();
|
||||
let highest_confirmed_root = 4;
|
||||
|
||||
// Fetch all signatures for address 0 at once...
|
||||
let all0 = blockstore
|
||||
.get_confirmed_signatures_for_address2(
|
||||
address0,
|
||||
highest_confirmed_root,
|
||||
None,
|
||||
usize::MAX,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(all0.len(), 6);
|
||||
|
||||
// Fetch all signatures for address 1 at once...
|
||||
let all1 = blockstore
|
||||
.get_confirmed_signatures_for_address2(
|
||||
address1,
|
||||
highest_confirmed_root,
|
||||
None,
|
||||
usize::MAX,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(all1.len(), 6);
|
||||
|
||||
assert!(all0 != all1);
|
||||
|
||||
// Fetch all signatures for address 0 individually
|
||||
for i in 0..all0.len() {
|
||||
let results = blockstore
|
||||
.get_confirmed_signatures_for_address2(
|
||||
address0,
|
||||
highest_confirmed_root,
|
||||
if i == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(all0[i - 1].signature)
|
||||
},
|
||||
1,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(results.len(), 1);
|
||||
assert_eq!(results[0], all0[i]);
|
||||
}
|
||||
|
||||
assert!(blockstore
|
||||
.get_confirmed_signatures_for_address2(
|
||||
address0,
|
||||
highest_confirmed_root,
|
||||
Some(all0[all0.len() - 1].signature),
|
||||
1,
|
||||
)
|
||||
.unwrap()
|
||||
.is_empty());
|
||||
|
||||
// Fetch all signatures for address 0, three at a time
|
||||
assert!(all0.len() % 3 == 0);
|
||||
for i in (0..all0.len()).step_by(3) {
|
||||
let results = blockstore
|
||||
.get_confirmed_signatures_for_address2(
|
||||
address0,
|
||||
highest_confirmed_root,
|
||||
if i == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(all0[i - 1].signature)
|
||||
},
|
||||
3,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(results.len(), 3);
|
||||
assert_eq!(results[0], all0[i]);
|
||||
assert_eq!(results[1], all0[i + 1]);
|
||||
assert_eq!(results[2], all0[i + 2]);
|
||||
}
|
||||
|
||||
// `start_after` signature from address1 should fail
|
||||
blockstore
|
||||
.get_confirmed_signatures_for_address2(
|
||||
address0,
|
||||
highest_confirmed_root,
|
||||
Some(all1[0].signature),
|
||||
usize::MAX,
|
||||
)
|
||||
.unwrap_err();
|
||||
}
|
||||
Blockstore::destroy(&blockstore_path).expect("Expected successful database destruction");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_last_hash() {
|
||||
let mut entries: Vec<Entry> = vec![];
|
||||
|
Reference in New Issue
Block a user