Rpc: Add getConfirmedSignaturesForAddress (#9407) (#9417)

automerge
This commit is contained in:
mergify[bot]
2020-04-09 21:20:28 -07:00
committed by GitHub
parent e00eb0a069
commit b087dabf4f
5 changed files with 297 additions and 35 deletions

View File

@ -38,6 +38,7 @@ use std::{
};
const MAX_QUERY_ITEMS: usize = 256;
const MAX_SLOT_RANGE: u64 = 10_000;
type RpcResponse<T> = Result<Response<T>>;
@ -520,6 +521,22 @@ impl JsonRpcRequestProcessor {
Ok(None)
}
}
pub fn get_confirmed_signatures_for_address(
&self,
pubkey: Pubkey,
start_slot: Slot,
end_slot: Slot,
) -> Result<Vec<Signature>> {
if self.config.enable_rpc_transaction_history {
Ok(self
.blockstore
.get_confirmed_signatures_for_address(pubkey, start_slot, end_slot)
.unwrap_or_else(|_| vec![]))
} else {
Ok(vec![])
}
}
}
fn get_tpu_addr(cluster_info: &Arc<RwLock<ClusterInfo>>) -> Result<SocketAddr> {
@ -769,6 +786,15 @@ pub trait RpcSol {
signature_str: String,
encoding: Option<TransactionEncoding>,
) -> Result<Option<ConfirmedTransaction>>;
#[rpc(meta, name = "getConfirmedSignaturesForAddress")]
fn get_confirmed_signatures_for_address(
&self,
meta: Self::Metadata,
pubkey_str: String,
start_slot: Slot,
end_slot: Slot,
) -> Result<Vec<String>>;
}
pub struct RpcSolImpl;
@ -1325,6 +1351,38 @@ impl RpcSol for RpcSolImpl {
.unwrap()
.get_confirmed_transaction(signature, encoding)
}
fn get_confirmed_signatures_for_address(
&self,
meta: Self::Metadata,
pubkey_str: String,
start_slot: Slot,
end_slot: Slot,
) -> Result<Vec<String>> {
let pubkey = verify_pubkey(pubkey_str)?;
if end_slot <= start_slot {
return Err(Error::invalid_params(format!(
"start_slot {} must be smaller than end_slot {}",
start_slot, end_slot
)));
}
if end_slot - start_slot > MAX_SLOT_RANGE {
return Err(Error::invalid_params(format!(
"Slot range too large; max {}",
MAX_SLOT_RANGE
)));
}
meta.request_processor
.read()
.unwrap()
.get_confirmed_signatures_for_address(pubkey, start_slot, end_slot)
.map(|signatures| {
signatures
.iter()
.map(|signature| signature.to_string())
.collect()
})
}
}
#[cfg(test)]

View File

@ -21,6 +21,7 @@ To interact with a Solana node inside a JavaScript application, use the [solana-
* [getClusterNodes](jsonrpc-api.md#getclusternodes)
* [getConfirmedBlock](jsonrpc-api.md#getconfirmedblock)
* [getConfirmedBlocks](jsonrpc-api.md#getconfirmedblocks)
* [getConfirmedSignaturesForAddress](jsonrpc-api.md#getconfirmedsignaturesforaddress)
* [getConfirmedTransaction](jsonrpc-api.md#getconfirmedtransaction)
* [getEpochInfo](jsonrpc-api.md#getepochinfo)
* [getEpochSchedule](jsonrpc-api.md#getepochschedule)
@ -346,6 +347,33 @@ curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc": "2.0","id":1,"m
{"jsonrpc":"2.0","result":[5,6,7,8,9,10],"id":1}
```
### getConfirmedSignaturesForAddress
Returns a list of all the confirmed signatures for transactions involving an address, within a specified Slot range. Max range allowed is 10_000 Slots.
#### Parameters:
* `<string>` - account address as base-58 encoded string
* `<u64>` - start slot, inclusive
* `<u64>` - end slot, inclusive
#### Results:
The result field will be an array of:
* `<string>` - transaction signature as base-58 encoded string
The signatures will be ordered based on the Slot in which they were confirmed in, from lowest to highest Slot
#### Example:
```bash
// Request
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc": "2.0","id":1,"method":"getConfirmedSignaturesForAddress","params":["6H94zdiaYfRfPfKjYLjyr2VFBg6JHXygy84r3qhc3NsC", 0, 100]}' localhost:8899
// Result
{"jsonrpc":"2.0","result":{["35YGay1Lwjwgxe9zaH6APSHbt9gYQUCtBWTNL3aVwVGn9xTFw2fgds7qK5AL29mP63A9j3rh8KpN1TgSR62XCaby","4bJdGN8Tt2kLWZ3Fa1dpwPSEkXWWTSszPSf1rRVsCwNjxbbUdwTeiWtmi8soA26YmwnKD4aAxNp8ci1Gjpdv4gsr","4LQ14a7BYY27578Uj8LPCaVhSdJGLn9DJqnUJHpy95FMqdKf9acAhUhecPQNjNUy6VoNFUbvwYkPociFSf87cWbG"]},"id":1}
```
### getConfirmedTransaction
Returns transaction details for a confirmed transaction

View File

@ -1633,20 +1633,14 @@ impl Blockstore {
.put((primary_index, signature, slot), status)?;
for address in writable_keys {
self.address_signatures_cf.put(
(primary_index, *address, slot),
&AddressSignatureMeta {
signature,
writeable: true,
},
(primary_index, *address, slot, signature),
&AddressSignatureMeta { writeable: true },
)?;
}
for address in readonly_keys {
self.address_signatures_cf.put(
(primary_index, *address, slot),
&AddressSignatureMeta {
signature,
writeable: false,
},
(primary_index, *address, slot, signature),
&AddressSignatureMeta { writeable: false },
)?;
}
Ok(())
@ -1723,6 +1717,49 @@ impl Blockstore {
.find(|transaction| transaction.signatures[0] == signature))
}
// Returns all cached signatures for an address, ordered by slot that the transaction was
// processed in
fn find_address_signatures(
&self,
pubkey: Pubkey,
start_slot: Slot,
end_slot: Slot,
) -> Result<Vec<(Slot, Signature)>> {
let mut signatures: Vec<(Slot, Signature)> = vec![];
for transaction_status_cf_primary_index in 0..=1 {
let index_iterator = self.address_signatures_cf.iter(IteratorMode::From(
(
transaction_status_cf_primary_index,
pubkey,
start_slot,
Signature::default(),
),
IteratorDirection::Forward,
))?;
for ((i, address, slot, signature), _) in index_iterator {
if i != transaction_status_cf_primary_index || slot > end_slot || address != pubkey
{
break;
}
if self.is_root(slot) {
signatures.push((slot, signature));
}
}
}
signatures.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap());
Ok(signatures)
}
pub fn get_confirmed_signatures_for_address(
&self,
pubkey: Pubkey,
start_slot: Slot,
end_slot: Slot,
) -> Result<Vec<Signature>> {
self.find_address_signatures(pubkey, start_slot, end_slot)
.map(|signatures| signatures.iter().map(|(_, signature)| *signature).collect())
}
pub fn read_rewards(&self, index: Slot) -> Result<Option<Rewards>> {
self.rewards_cf.get(index)
}
@ -2858,7 +2895,7 @@ pub mod tests {
.iter::<cf::AddressSignatures>(IteratorMode::Start)
.unwrap()
.next()
.map(|((primary_index, _, slot), _)| {
.map(|((primary_index, _, slot, _), _)| {
slot >= min_slot || (primary_index == 2 && slot == 0)
})
.unwrap_or(true)
@ -5466,7 +5503,7 @@ pub mod tests {
let first_status_entry = blockstore
.db
.iter::<cf::TransactionStatus>(IteratorMode::From(
(0, Signature::default(), 0),
cf::TransactionStatus::as_index(0),
IteratorDirection::Forward,
))
.unwrap()
@ -5478,7 +5515,7 @@ pub mod tests {
let first_address_entry = blockstore
.db
.iter::<cf::AddressSignatures>(IteratorMode::From(
(0, Pubkey::default(), 0),
cf::AddressSignatures::as_index(0),
IteratorDirection::Forward,
))
.unwrap()
@ -5536,7 +5573,7 @@ pub mod tests {
let first_status_entry = blockstore
.db
.iter::<cf::TransactionStatus>(IteratorMode::From(
(0, Signature::default(), 0),
cf::TransactionStatus::as_index(0),
IteratorDirection::Forward,
))
.unwrap()
@ -5548,7 +5585,7 @@ pub mod tests {
let first_address_entry = blockstore
.db
.iter::<cf::AddressSignatures>(IteratorMode::From(
(0, Pubkey::default(), 0),
cf::AddressSignatures::as_index(0),
IteratorDirection::Forward,
))
.unwrap()
@ -5561,7 +5598,7 @@ pub mod tests {
let index1_first_status_entry = blockstore
.db
.iter::<cf::TransactionStatus>(IteratorMode::From(
(1, Signature::default(), 0),
cf::TransactionStatus::as_index(1),
IteratorDirection::Forward,
))
.unwrap()
@ -5573,7 +5610,7 @@ pub mod tests {
let index1_first_address_entry = blockstore
.db
.iter::<cf::AddressSignatures>(IteratorMode::From(
(1, Pubkey::default(), 0),
cf::AddressSignatures::as_index(1),
IteratorDirection::Forward,
))
.unwrap()
@ -5604,7 +5641,7 @@ pub mod tests {
let first_status_entry = blockstore
.db
.iter::<cf::TransactionStatus>(IteratorMode::From(
(0, Signature::default(), 0),
cf::TransactionStatus::as_index(0),
IteratorDirection::Forward,
))
.unwrap()
@ -5616,7 +5653,7 @@ pub mod tests {
let first_address_entry = blockstore
.db
.iter::<cf::AddressSignatures>(IteratorMode::From(
(0, Pubkey::default(), 0),
cf::AddressSignatures::as_index(0),
IteratorDirection::Forward,
))
.unwrap()
@ -5653,7 +5690,7 @@ pub mod tests {
let mut status_entry_iterator = blockstore
.db
.iter::<cf::TransactionStatus>(IteratorMode::From(
(0, Signature::default(), 0),
cf::TransactionStatus::as_index(0),
IteratorDirection::Forward,
))
.unwrap();
@ -5665,7 +5702,7 @@ pub mod tests {
let mut address_transactions_iterator = blockstore
.db
.iter::<cf::AddressSignatures>(IteratorMode::From(
(0, Pubkey::default(), 0),
(0, Pubkey::default(), 0, Signature::default()),
IteratorDirection::Forward,
))
.unwrap();
@ -5687,7 +5724,7 @@ pub mod tests {
let mut status_entry_iterator = blockstore
.db
.iter::<cf::TransactionStatus>(IteratorMode::From(
(0, Signature::default(), 0),
cf::TransactionStatus::as_index(0),
IteratorDirection::Forward,
))
.unwrap();
@ -5699,7 +5736,7 @@ pub mod tests {
let mut address_transactions_iterator = blockstore
.db
.iter::<cf::AddressSignatures>(IteratorMode::From(
(0, Pubkey::default(), 0),
cf::AddressSignatures::as_index(0),
IteratorDirection::Forward,
))
.unwrap();
@ -5721,7 +5758,7 @@ pub mod tests {
let mut status_entry_iterator = blockstore
.db
.iter::<cf::TransactionStatus>(IteratorMode::From(
(0, Signature::default(), 0),
cf::TransactionStatus::as_index(0),
IteratorDirection::Forward,
))
.unwrap();
@ -5733,7 +5770,7 @@ pub mod tests {
let mut address_transactions_iterator = blockstore
.db
.iter::<cf::AddressSignatures>(IteratorMode::From(
(0, Pubkey::default(), 0),
cf::AddressSignatures::as_index(0),
IteratorDirection::Forward,
))
.unwrap();
@ -5754,7 +5791,7 @@ pub mod tests {
let mut status_entry_iterator = blockstore
.db
.iter::<cf::TransactionStatus>(IteratorMode::From(
(0, Signature::default(), 0),
cf::TransactionStatus::as_index(0),
IteratorDirection::Forward,
))
.unwrap();
@ -5765,7 +5802,7 @@ pub mod tests {
let mut address_transactions_iterator = blockstore
.db
.iter::<cf::AddressSignatures>(IteratorMode::From(
(0, Pubkey::default(), 0),
cf::AddressSignatures::as_index(0),
IteratorDirection::Forward,
))
.unwrap();
@ -5995,6 +6032,144 @@ pub mod tests {
}
}
#[test]
fn test_get_confirmed_signatures_for_address() {
let blockstore_path = get_tmp_ledger_path!();
{
let blockstore = Blockstore::open(&blockstore_path).unwrap();
let address0 = Pubkey::new_rand();
let address1 = Pubkey::new_rand();
let slot0 = 10;
for x in 1..5 {
let signature = Signature::new(&[x; 64]);
blockstore
.write_transaction_status(
slot0,
signature,
vec![&address0],
vec![&address1],
&TransactionStatusMeta::default(),
)
.unwrap();
}
// Purge to freeze index 0
blockstore.run_purge(0, 1).unwrap();
let slot1 = 20;
for x in 5..9 {
let signature = Signature::new(&[x; 64]);
blockstore
.write_transaction_status(
slot1,
signature,
vec![&address0],
vec![&address1],
&TransactionStatusMeta::default(),
)
.unwrap();
}
blockstore.set_roots(&[slot0, slot1]).unwrap();
let all0 = blockstore
.get_confirmed_signatures_for_address(address0, 0, 50)
.unwrap();
assert_eq!(all0.len(), 8);
for x in 1..9 {
let expected_signature = Signature::new(&[x; 64]);
assert_eq!(all0[x as usize - 1], expected_signature);
}
assert_eq!(
blockstore
.get_confirmed_signatures_for_address(address0, 20, 50)
.unwrap()
.len(),
4
);
assert_eq!(
blockstore
.get_confirmed_signatures_for_address(address0, 0, 10)
.unwrap()
.len(),
4
);
assert!(blockstore
.get_confirmed_signatures_for_address(address0, 1, 5)
.unwrap()
.is_empty());
assert_eq!(
blockstore
.get_confirmed_signatures_for_address(address0, 1, 15)
.unwrap()
.len(),
4
);
let all1 = blockstore
.get_confirmed_signatures_for_address(address1, 0, 50)
.unwrap();
assert_eq!(all1.len(), 8);
for x in 1..9 {
let expected_signature = Signature::new(&[x; 64]);
assert_eq!(all1[x as usize - 1], expected_signature);
}
// Purge index 0
blockstore.run_purge(0, 10).unwrap();
assert_eq!(
blockstore
.get_confirmed_signatures_for_address(address0, 0, 50)
.unwrap()
.len(),
4
);
assert_eq!(
blockstore
.get_confirmed_signatures_for_address(address0, 20, 50)
.unwrap()
.len(),
4
);
assert!(blockstore
.get_confirmed_signatures_for_address(address0, 0, 10)
.unwrap()
.is_empty());
assert!(blockstore
.get_confirmed_signatures_for_address(address0, 1, 5)
.unwrap()
.is_empty());
assert_eq!(
blockstore
.get_confirmed_signatures_for_address(address0, 1, 25)
.unwrap()
.len(),
4
);
// Test sort, regardless of entry order or signature value
for slot in (21..25).rev() {
let random_bytes: Vec<u8> = (0..64).map(|_| rand::random::<u8>()).collect();
let signature = Signature::new(&random_bytes);
blockstore
.write_transaction_status(
slot,
signature,
vec![&address0],
vec![&address1],
&TransactionStatusMeta::default(),
)
.unwrap();
}
blockstore.set_roots(&[21, 22, 23, 24]).unwrap();
let mut past_slot = 0;
for (slot, _) in blockstore.find_address_signatures(address0, 1, 25).unwrap() {
assert!(slot >= past_slot);
past_slot = slot;
}
}
Blockstore::destroy(&blockstore_path).expect("Expected successful database destruction");
}
#[test]
fn test_get_last_hash() {
let mut entries: Vec<Entry> = vec![];

View File

@ -356,21 +356,23 @@ impl ColumnName for columns::TransactionStatus {
}
impl Column for columns::AddressSignatures {
type Index = (u64, Pubkey, Slot);
type Index = (u64, Pubkey, Slot, Signature);
fn key((index, pubkey, slot): (u64, Pubkey, Slot)) -> Vec<u8> {
let mut key = vec![0; 8 + 32 + 8]; // size_of u64 + size_of Pubkey + size_of Slot
fn key((index, pubkey, slot, signature): (u64, Pubkey, Slot, Signature)) -> Vec<u8> {
let mut key = vec![0; 8 + 32 + 8 + 64]; // size_of u64 + size_of Pubkey + size_of Slot + size_of Signature
BigEndian::write_u64(&mut key[0..8], index);
key[8..40].clone_from_slice(&pubkey.as_ref()[0..32]);
BigEndian::write_u64(&mut key[40..48], slot);
key[48..112].clone_from_slice(&signature.as_ref()[0..64]);
key
}
fn index(key: &[u8]) -> (u64, Pubkey, Slot) {
fn index(key: &[u8]) -> (u64, Pubkey, Slot, Signature) {
let index = BigEndian::read_u64(&key[0..8]);
let pubkey = Pubkey::new(&key[8..40]);
let slot = BigEndian::read_u64(&key[40..48]);
(index, pubkey, slot)
let signature = Signature::new(&key[48..112]);
(index, pubkey, slot, signature)
}
fn primary_index(index: Self::Index) -> u64 {
@ -378,7 +380,7 @@ impl Column for columns::AddressSignatures {
}
fn as_index(index: u64) -> Self::Index {
(index, Pubkey::default(), 0)
(index, Pubkey::default(), 0, Signature::default())
}
}

View File

@ -1,6 +1,6 @@
use crate::erasure::ErasureConfig;
use serde::{Deserialize, Serialize};
use solana_sdk::{clock::Slot, signature::Signature};
use solana_sdk::clock::Slot;
use std::{collections::BTreeSet, ops::RangeBounds};
#[derive(Clone, Debug, Default, Deserialize, Serialize, Eq, PartialEq)]
@ -230,7 +230,6 @@ pub struct TransactionStatusIndexMeta {
#[derive(Debug, Default, Deserialize, Serialize, PartialEq)]
pub struct AddressSignatureMeta {
pub signature: Signature,
pub writeable: bool,
}