cli: Add transaction-history (#9614)

automerge
This commit is contained in:
Michael Vines 2020-04-20 22:01:09 -07:00 committed by GitHub
parent 6e908a1be8
commit 914b022663
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 132 additions and 12 deletions

View File

@ -227,6 +227,11 @@ pub enum CliCommand {
use_lamports_unit: bool,
commitment_config: CommitmentConfig,
},
TransactionHistory {
address: Pubkey,
end_slot: Option<Slot>, // None == latest slot
slot_limit: u64,
},
// Nonce commands
AuthorizeNonceAccount {
nonce_account: Pubkey,
@ -618,6 +623,9 @@ pub fn parse_command(
}),
("stakes", Some(matches)) => parse_show_stakes(matches, wallet_manager),
("validators", Some(matches)) => parse_show_validators(matches),
("transaction-history", Some(matches)) => {
parse_transaction_history(matches, wallet_manager)
}
// Nonce Commands
("authorize-nonce-account", Some(matches)) => {
parse_authorize_nonce_account(matches, default_signer_path, wallet_manager)
@ -1729,6 +1737,11 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
use_lamports_unit,
commitment_config,
} => process_show_validators(&rpc_client, config, *use_lamports_unit, *commitment_config),
CliCommand::TransactionHistory {
address,
end_slot,
slot_limit,
} => process_transaction_history(&rpc_client, address, *end_slot, *slot_limit),
// Nonce Commands

View File

@ -14,6 +14,7 @@ use solana_clap_utils::{input_parsers::*, input_validators::*, keypair::signer_f
use solana_client::{
pubsub_client::{PubsubClient, SlotInfoMessage},
rpc_client::RpcClient,
rpc_request::MAX_GET_CONFIRMED_SIGNATURES_FOR_ADDRESS_SLOT_RANGE,
};
use solana_remote_wallet::remote_wallet::RemoteWalletManager;
use solana_sdk::{
@ -273,6 +274,39 @@ impl ClusterQuerySubCommands for App<'_, '_> {
.help("Display balance in lamports instead of SOL"),
),
)
.subcommand(
SubCommand::with_name("transaction-history")
.about("Show historical transactions affecting the given address, \
ordered based on the slot in which they were confirmed in \
from lowest to highest slot")
.arg(
pubkey!(Arg::with_name("address")
.index(1)
.value_name("ADDRESS")
.required(true),
"Account address"),
)
.arg(
Arg::with_name("end_slot")
.takes_value(false)
.value_name("SLOT")
.index(2)
.validator(is_slot)
.help(
"Slot to start from [default: latest slot at maximum commitment]"
),
)
.arg(
Arg::with_name("limit")
.long("limit")
.takes_value(true)
.value_name("NUMBER OF SLOTS")
.validator(is_slot)
.help(
"Limit the search to this many slots"
),
),
)
}
}
@ -436,6 +470,25 @@ pub fn parse_show_validators(matches: &ArgMatches<'_>) -> Result<CliCommandInfo,
})
}
pub fn parse_transaction_history(
matches: &ArgMatches<'_>,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> {
let address = pubkey_of_signer(matches, "address", wallet_manager)?.unwrap();
let end_slot = value_t!(matches, "end_slot", Slot).ok();
let slot_limit = value_t!(matches, "limit", u64)
.unwrap_or(MAX_GET_CONFIRMED_SIGNATURES_FOR_ADDRESS_SLOT_RANGE);
Ok(CliCommandInfo {
command: CliCommand::TransactionHistory {
address,
end_slot,
slot_limit,
},
signers: vec![],
})
}
/// Creates a new process bar for processing that will take an unknown amount of time
fn new_spinner_progress_bar() -> ProgressBar {
let progress_bar = ProgressBar::new(42);
@ -1170,6 +1223,33 @@ pub fn process_show_validators(
Ok("".to_string())
}
pub fn process_transaction_history(
rpc_client: &RpcClient,
address: &Pubkey,
end_slot: Option<Slot>, // None == use latest slot
slot_limit: u64,
) -> ProcessResult {
let end_slot = {
if let Some(end_slot) = end_slot {
end_slot
} else {
rpc_client.get_slot_with_commitment(CommitmentConfig::max())?
}
};
let start_slot = end_slot.saturating_sub(slot_limit);
println!(
"Transactions affecting {} within slots [{},{}]",
address, start_slot, end_slot
);
let signatures =
rpc_client.get_confirmed_signatures_for_address(address, start_slot, end_slot)?;
for signature in &signatures {
println!("{}", signature);
}
Ok(format!("{} transactions found", signatures.len(),))
}
#[cfg(test)]
mod tests {
use super::*;

View File

@ -289,14 +289,25 @@ impl RpcClient {
.client
.send(
&RpcRequest::GetConfirmedSignaturesForAddress,
json!([address, start_slot, end_slot]),
json!([address.to_string(), start_slot, end_slot]),
0,
)
.map_err(|err| err.into_with_command("GetConfirmedSignaturesForAddress"))?;
serde_json::from_value(response).map_err(|err| {
ClientError::new_with_command(err.into(), "GetConfirmedSignaturesForAddress")
})
let signatures_base58_str: Vec<String> =
serde_json::from_value(response).map_err(|err| {
ClientError::new_with_command(err.into(), "GetConfirmedSignaturesForAddress")
})?;
let mut signatures = vec![];
for signature_base58_str in signatures_base58_str {
signatures.push(
signature_base58_str.parse::<Signature>().map_err(|err| {
Into::<ClientError>::into(RpcError::ParseError(err.to_string()))
})?,
);
}
Ok(signatures)
}
pub fn get_confirmed_transaction(

View File

@ -42,6 +42,9 @@ pub enum RpcRequest {
MinimumLedgerSlot,
}
pub const MAX_GET_SIGNATURE_STATUSES_QUERY_ITEMS: usize = 256;
pub const MAX_GET_CONFIRMED_SIGNATURES_FOR_ADDRESS_SLOT_RANGE: u64 = 10_000;
impl RpcRequest {
pub(crate) fn build_request_json(&self, id: u64, params: Value) -> Value {
let jsonrpc = "2.0";

View File

@ -7,7 +7,12 @@ use crate::{
use bincode::serialize;
use jsonrpc_core::{Error, Metadata, Result};
use jsonrpc_derive::rpc;
use solana_client::rpc_response::*;
use solana_client::{
rpc_request::{
MAX_GET_CONFIRMED_SIGNATURES_FOR_ADDRESS_SLOT_RANGE, MAX_GET_SIGNATURE_STATUSES_QUERY_ITEMS,
},
rpc_response::*,
};
use solana_faucet::faucet::request_airdrop_transaction;
use solana_ledger::{
bank_forks::BankForks, blockstore::Blockstore, rooted_slot_iterator::RootedSlotIterator,
@ -38,9 +43,6 @@ use std::{
time::{Duration, Instant},
};
const MAX_QUERY_ITEMS: usize = 256;
const MAX_SLOT_RANGE: u64 = 10_000;
type RpcResponse<T> = Result<Response<T>>;
fn new_response<T>(bank: &Bank, value: T) -> RpcResponse<T> {
@ -1058,10 +1060,10 @@ impl RpcSol for RpcSolImpl {
signature_strs: Vec<String>,
config: Option<RpcSignatureStatusConfig>,
) -> RpcResponse<Vec<Option<TransactionStatus>>> {
if signature_strs.len() > MAX_QUERY_ITEMS {
if signature_strs.len() > MAX_GET_SIGNATURE_STATUSES_QUERY_ITEMS {
return Err(Error::invalid_params(format!(
"Too many inputs provided; max {}",
MAX_QUERY_ITEMS
MAX_GET_SIGNATURE_STATUSES_QUERY_ITEMS
)));
}
let mut signatures: Vec<Signature> = vec![];
@ -1360,10 +1362,10 @@ impl RpcSol for RpcSolImpl {
start_slot, end_slot
)));
}
if end_slot - start_slot > MAX_SLOT_RANGE {
if end_slot - start_slot > MAX_GET_CONFIRMED_SIGNATURES_FOR_ADDRESS_SLOT_RANGE {
return Err(Error::invalid_params(format!(
"Slot range too large; max {}",
MAX_SLOT_RANGE
MAX_GET_CONFIRMED_SIGNATURES_FOR_ADDRESS_SLOT_RANGE
)));
}
meta.request_processor

View File

@ -499,6 +499,17 @@ fn analyze_storage(database: &Database) -> Result<(), String> {
"TransactionStatus",
TransactionStatus::key_size(),
)?;
analyze_column::<TransactionStatus>(
database,
"TransactionStatusIndex",
TransactionStatusIndex::key_size(),
)?;
analyze_column::<AddressSignatures>(
database,
"AddressSignatures",
AddressSignatures::key_size(),
)?;
analyze_column::<Rewards>(database, "Rewards", Rewards::key_size())?;
Ok(())
}