2019-10-04 19:54:09 -07:00
|
|
|
use crate::{
|
2019-10-06 21:47:24 -07:00
|
|
|
cli::{
|
2019-10-21 16:08:09 -07:00
|
|
|
build_balance_message, check_account_for_fee, CliCommand, CliCommandInfo, CliConfig,
|
|
|
|
CliError, ProcessResult,
|
2019-10-06 21:47:24 -07:00
|
|
|
},
|
2019-10-04 19:54:09 -07:00
|
|
|
display::println_name_value,
|
|
|
|
};
|
2020-03-02 14:58:15 -08:00
|
|
|
use chrono::{DateTime, NaiveDateTime, SecondsFormat, Utc};
|
2019-12-18 10:38:54 -08:00
|
|
|
use clap::{value_t, value_t_or_exit, App, Arg, ArgMatches, SubCommand};
|
2019-10-04 19:54:09 -07:00
|
|
|
use console::{style, Emoji};
|
2019-11-13 14:58:14 -08:00
|
|
|
use indicatif::{ProgressBar, ProgressStyle};
|
2020-02-24 16:03:30 -08:00
|
|
|
use solana_clap_utils::{input_parsers::*, input_validators::*, keypair::signer_from_path};
|
2020-02-06 11:16:30 -08:00
|
|
|
use solana_client::{
|
|
|
|
pubsub_client::{PubsubClient, SlotInfoMessage},
|
|
|
|
rpc_client::RpcClient,
|
|
|
|
rpc_response::RpcVoteAccountInfo,
|
|
|
|
};
|
2020-02-24 16:03:30 -08:00
|
|
|
use solana_remote_wallet::remote_wallet::RemoteWalletManager;
|
2019-10-04 19:54:09 -07:00
|
|
|
use solana_sdk::{
|
2020-01-22 17:54:06 -08:00
|
|
|
account_utils::StateMut,
|
2019-11-25 23:40:36 -08:00
|
|
|
clock::{self, Slot},
|
2019-11-06 13:15:00 -08:00
|
|
|
commitment_config::CommitmentConfig,
|
2020-02-03 10:37:50 -08:00
|
|
|
epoch_schedule::Epoch,
|
2019-10-04 19:54:09 -07:00
|
|
|
hash::Hash,
|
2020-02-21 13:55:53 -08:00
|
|
|
message::Message,
|
2020-03-09 01:28:44 -07:00
|
|
|
native_token::lamports_to_sol,
|
2019-11-13 14:58:14 -08:00
|
|
|
pubkey::Pubkey,
|
2020-02-20 13:28:55 -08:00
|
|
|
signature::{Keypair, Signer},
|
2020-02-21 13:55:53 -08:00
|
|
|
system_instruction,
|
|
|
|
transaction::Transaction,
|
2019-10-04 19:54:09 -07:00
|
|
|
};
|
|
|
|
use std::{
|
2019-12-18 10:38:54 -08:00
|
|
|
collections::{HashMap, VecDeque},
|
2019-11-15 12:15:34 -08:00
|
|
|
net::SocketAddr,
|
2020-02-06 11:16:30 -08:00
|
|
|
sync::{
|
|
|
|
atomic::{AtomicBool, Ordering},
|
|
|
|
Arc,
|
|
|
|
},
|
2019-11-13 14:58:14 -08:00
|
|
|
thread::sleep,
|
2019-10-04 19:54:09 -07:00
|
|
|
time::{Duration, Instant},
|
|
|
|
};
|
|
|
|
|
|
|
|
static CHECK_MARK: Emoji = Emoji("✅ ", "");
|
|
|
|
static CROSS_MARK: Emoji = Emoji("❌ ", "");
|
2019-10-23 21:40:35 -07:00
|
|
|
static WARNING: Emoji = Emoji("⚠️", "!");
|
2019-10-04 19:54:09 -07:00
|
|
|
|
|
|
|
pub trait ClusterQuerySubCommands {
|
|
|
|
fn cluster_query_subcommands(self) -> Self;
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ClusterQuerySubCommands for App<'_, '_> {
|
|
|
|
fn cluster_query_subcommands(self) -> Self {
|
|
|
|
self.subcommand(
|
2019-11-13 14:58:14 -08:00
|
|
|
SubCommand::with_name("catchup")
|
|
|
|
.about("Wait for a validator to catch up to the cluster")
|
|
|
|
.arg(
|
|
|
|
Arg::with_name("node_pubkey")
|
|
|
|
.index(1)
|
|
|
|
.takes_value(true)
|
|
|
|
.value_name("PUBKEY")
|
|
|
|
.validator(is_pubkey_or_keypair)
|
|
|
|
.required(true)
|
|
|
|
.help("Identity pubkey of the validator"),
|
2020-03-04 11:44:13 -08:00
|
|
|
)
|
|
|
|
.arg(
|
|
|
|
Arg::with_name("node_json_rpc_url")
|
|
|
|
.index(2)
|
|
|
|
.value_name("URL")
|
|
|
|
.takes_value(true)
|
|
|
|
.validator(is_url)
|
|
|
|
.help("JSON RPC URL for validator, which is useful for validators with a private RPC service")
|
2019-11-13 14:58:14 -08:00
|
|
|
),
|
|
|
|
)
|
|
|
|
.subcommand(
|
2019-10-04 19:54:09 -07:00
|
|
|
SubCommand::with_name("cluster-version")
|
|
|
|
.about("Get the version of the cluster entrypoint"),
|
|
|
|
)
|
|
|
|
.subcommand(SubCommand::with_name("fees").about("Display current cluster fees"))
|
2020-01-20 22:06:47 -08:00
|
|
|
.subcommand(SubCommand::with_name("block-time")
|
2019-11-25 23:40:36 -08:00
|
|
|
.about("Get estimated production time of a block")
|
2020-01-20 22:06:47 -08:00
|
|
|
.alias("get-block-time")
|
2019-11-25 23:40:36 -08:00
|
|
|
.arg(
|
|
|
|
Arg::with_name("slot")
|
|
|
|
.index(1)
|
|
|
|
.takes_value(true)
|
|
|
|
.value_name("SLOT")
|
|
|
|
.required(true)
|
|
|
|
.help("Slot number of the block to query")
|
|
|
|
)
|
|
|
|
)
|
2020-01-28 20:52:52 -08:00
|
|
|
.subcommand(SubCommand::with_name("leader-schedule").about("Display leader schedule"))
|
2019-10-04 19:54:09 -07:00
|
|
|
.subcommand(
|
2020-01-20 22:06:47 -08:00
|
|
|
SubCommand::with_name("epoch-info")
|
2019-11-24 16:34:18 -08:00
|
|
|
.about("Get information about the current epoch")
|
2020-01-20 22:06:47 -08:00
|
|
|
.alias("get-epoch-info")
|
2019-11-24 16:34:18 -08:00
|
|
|
.arg(
|
|
|
|
Arg::with_name("confirmed")
|
|
|
|
.long("confirmed")
|
|
|
|
.takes_value(false)
|
|
|
|
.help(
|
|
|
|
"Return information at maximum-lockout commitment level",
|
|
|
|
),
|
|
|
|
),
|
2019-10-04 19:54:09 -07:00
|
|
|
)
|
|
|
|
.subcommand(
|
2020-01-20 22:06:47 -08:00
|
|
|
SubCommand::with_name("genesis-hash")
|
|
|
|
.about("Get the genesis hash")
|
|
|
|
.alias("get-genesis-hash")
|
2019-10-04 19:54:09 -07:00
|
|
|
)
|
|
|
|
.subcommand(
|
2020-01-20 22:06:47 -08:00
|
|
|
SubCommand::with_name("slot").about("Get current slot")
|
|
|
|
.alias("get-slot")
|
2019-11-24 16:34:18 -08:00
|
|
|
.arg(
|
|
|
|
Arg::with_name("confirmed")
|
|
|
|
.long("confirmed")
|
|
|
|
.takes_value(false)
|
|
|
|
.help(
|
|
|
|
"Return slot at maximum-lockout commitment level",
|
|
|
|
),
|
|
|
|
),
|
|
|
|
)
|
2020-03-09 01:28:44 -07:00
|
|
|
.subcommand(
|
|
|
|
SubCommand::with_name("total-supply").about("Get total number of SOL")
|
|
|
|
.arg(
|
|
|
|
Arg::with_name("confirmed")
|
|
|
|
.long("confirmed")
|
|
|
|
.takes_value(false)
|
|
|
|
.help(
|
|
|
|
"Return count at maximum-lockout commitment level",
|
|
|
|
),
|
|
|
|
),
|
|
|
|
)
|
2019-11-24 16:34:18 -08:00
|
|
|
.subcommand(
|
2020-01-20 22:06:47 -08:00
|
|
|
SubCommand::with_name("transaction-count").about("Get current transaction count")
|
|
|
|
.alias("get-transaction-count")
|
2019-11-24 16:34:18 -08:00
|
|
|
.arg(
|
|
|
|
Arg::with_name("confirmed")
|
|
|
|
.long("confirmed")
|
|
|
|
.takes_value(false)
|
|
|
|
.help(
|
|
|
|
"Return count at maximum-lockout commitment level",
|
|
|
|
),
|
|
|
|
),
|
2019-10-04 19:54:09 -07:00
|
|
|
)
|
|
|
|
.subcommand(
|
|
|
|
SubCommand::with_name("ping")
|
|
|
|
.about("Submit transactions sequentially")
|
|
|
|
.arg(
|
|
|
|
Arg::with_name("interval")
|
|
|
|
.short("i")
|
|
|
|
.long("interval")
|
|
|
|
.value_name("SECONDS")
|
|
|
|
.takes_value(true)
|
|
|
|
.default_value("2")
|
|
|
|
.help("Wait interval seconds between submitting the next transaction"),
|
|
|
|
)
|
|
|
|
.arg(
|
|
|
|
Arg::with_name("count")
|
|
|
|
.short("c")
|
|
|
|
.long("count")
|
|
|
|
.value_name("NUMBER")
|
|
|
|
.takes_value(true)
|
|
|
|
.help("Stop after submitting count transactions"),
|
|
|
|
)
|
2019-11-18 21:50:09 -08:00
|
|
|
.arg(
|
|
|
|
Arg::with_name("lamports")
|
|
|
|
.long("lamports")
|
|
|
|
.value_name("NUMBER")
|
|
|
|
.takes_value(true)
|
|
|
|
.default_value("1")
|
2019-12-10 10:29:17 -08:00
|
|
|
.validator(is_amount)
|
2019-11-18 21:50:09 -08:00
|
|
|
.help("Number of lamports to transfer for each transaction"),
|
|
|
|
)
|
2019-10-04 19:54:09 -07:00
|
|
|
.arg(
|
|
|
|
Arg::with_name("timeout")
|
|
|
|
.short("t")
|
|
|
|
.long("timeout")
|
|
|
|
.value_name("SECONDS")
|
|
|
|
.takes_value(true)
|
2019-11-06 13:15:00 -08:00
|
|
|
.default_value("15")
|
2019-10-04 19:54:09 -07:00
|
|
|
.help("Wait up to timeout seconds for transaction confirmation"),
|
2019-11-06 17:54:17 -08:00
|
|
|
)
|
|
|
|
.arg(
|
|
|
|
Arg::with_name("confirmed")
|
|
|
|
.long("confirmed")
|
|
|
|
.takes_value(false)
|
|
|
|
.help(
|
|
|
|
"Wait until the transaction is confirmed at maximum-lockout commitment level",
|
|
|
|
),
|
2019-10-04 19:54:09 -07:00
|
|
|
),
|
|
|
|
)
|
2020-02-06 11:16:30 -08:00
|
|
|
.subcommand(
|
|
|
|
SubCommand::with_name("live-slots")
|
2020-02-29 08:39:07 -08:00
|
|
|
.about("Show information about the current slot progression"),
|
2020-02-06 11:16:30 -08:00
|
|
|
)
|
2019-12-18 10:38:54 -08:00
|
|
|
.subcommand(
|
2020-01-20 22:06:47 -08:00
|
|
|
SubCommand::with_name("block-production")
|
2019-12-18 10:38:54 -08:00
|
|
|
.about("Show information about block production")
|
2020-01-20 22:06:47 -08:00
|
|
|
.alias("show-block-production")
|
2019-12-18 10:38:54 -08:00
|
|
|
.arg(
|
|
|
|
Arg::with_name("epoch")
|
|
|
|
.long("epoch")
|
|
|
|
.takes_value(true)
|
|
|
|
.help("Epoch to show block production for [default: current epoch]"),
|
|
|
|
)
|
|
|
|
.arg(
|
|
|
|
Arg::with_name("slot_limit")
|
|
|
|
.long("slot-limit")
|
|
|
|
.takes_value(true)
|
|
|
|
.help("Limit results to this many slots from the end of the epoch [default: full epoch]"),
|
|
|
|
),
|
|
|
|
)
|
2019-11-15 12:15:34 -08:00
|
|
|
.subcommand(
|
2020-01-20 22:06:47 -08:00
|
|
|
SubCommand::with_name("gossip")
|
|
|
|
.about("Show the current gossip network nodes")
|
|
|
|
.alias("show-gossip")
|
2019-11-15 12:15:34 -08:00
|
|
|
)
|
2020-01-17 11:10:52 -08:00
|
|
|
.subcommand(
|
2020-01-20 22:06:47 -08:00
|
|
|
SubCommand::with_name("stakes")
|
2020-01-17 11:10:52 -08:00
|
|
|
.about("Show stake account information")
|
|
|
|
.arg(
|
|
|
|
Arg::with_name("vote_account_pubkeys")
|
|
|
|
.index(1)
|
|
|
|
.value_name("VOTE ACCOUNT PUBKEYS")
|
|
|
|
.takes_value(true)
|
|
|
|
.multiple(true)
|
|
|
|
.validator(is_pubkey_or_keypair)
|
|
|
|
.help("Only show stake accounts delegated to the provided vote accounts"),
|
|
|
|
)
|
|
|
|
.arg(
|
|
|
|
Arg::with_name("lamports")
|
|
|
|
.long("lamports")
|
|
|
|
.takes_value(false)
|
|
|
|
.help("Display balance in lamports instead of SOL"),
|
|
|
|
),
|
|
|
|
)
|
2019-10-06 21:47:24 -07:00
|
|
|
.subcommand(
|
2020-01-20 22:06:47 -08:00
|
|
|
SubCommand::with_name("validators")
|
2020-01-17 11:10:52 -08:00
|
|
|
.about("Show summary information about the current validators")
|
2020-01-20 22:06:47 -08:00
|
|
|
.alias("show-validators")
|
2020-03-03 16:53:30 -08:00
|
|
|
.arg(
|
|
|
|
Arg::with_name("confirmed")
|
|
|
|
.long("confirmed")
|
|
|
|
.takes_value(false)
|
|
|
|
.help(
|
|
|
|
"Return information at maximum-lockout commitment level",
|
|
|
|
),
|
|
|
|
)
|
2019-10-06 21:47:24 -07:00
|
|
|
.arg(
|
|
|
|
Arg::with_name("lamports")
|
|
|
|
.long("lamports")
|
|
|
|
.takes_value(false)
|
|
|
|
.help("Display balance in lamports instead of SOL"),
|
|
|
|
),
|
|
|
|
)
|
2019-10-04 19:54:09 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-13 14:58:14 -08:00
|
|
|
pub fn parse_catchup(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
|
|
|
|
let node_pubkey = pubkey_of(matches, "node_pubkey").unwrap();
|
2020-03-04 11:44:13 -08:00
|
|
|
let node_json_rpc_url = value_t!(matches, "node_json_rpc_url", String).ok();
|
2019-11-13 14:58:14 -08:00
|
|
|
Ok(CliCommandInfo {
|
2020-03-04 11:44:13 -08:00
|
|
|
command: CliCommand::Catchup {
|
|
|
|
node_pubkey,
|
|
|
|
node_json_rpc_url,
|
|
|
|
},
|
2020-02-24 16:03:30 -08:00
|
|
|
signers: vec![],
|
2019-11-13 14:58:14 -08:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2020-02-24 16:03:30 -08:00
|
|
|
pub fn parse_cluster_ping(
|
|
|
|
matches: &ArgMatches<'_>,
|
|
|
|
default_signer_path: &str,
|
|
|
|
wallet_manager: Option<&Arc<RemoteWalletManager>>,
|
|
|
|
) -> Result<CliCommandInfo, CliError> {
|
2019-11-18 21:50:09 -08:00
|
|
|
let lamports = value_t_or_exit!(matches, "lamports", u64);
|
2019-10-04 19:54:09 -07:00
|
|
|
let interval = Duration::from_secs(value_t_or_exit!(matches, "interval", u64));
|
|
|
|
let count = if matches.is_present("count") {
|
|
|
|
Some(value_t_or_exit!(matches, "count", u64))
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
};
|
|
|
|
let timeout = Duration::from_secs(value_t_or_exit!(matches, "timeout", u64));
|
2019-11-06 17:54:17 -08:00
|
|
|
let commitment_config = if matches.is_present("confirmed") {
|
|
|
|
CommitmentConfig::default()
|
|
|
|
} else {
|
|
|
|
CommitmentConfig::recent()
|
|
|
|
};
|
2019-10-21 16:08:09 -07:00
|
|
|
Ok(CliCommandInfo {
|
|
|
|
command: CliCommand::Ping {
|
2019-11-18 21:50:09 -08:00
|
|
|
lamports,
|
2019-10-21 16:08:09 -07:00
|
|
|
interval,
|
|
|
|
count,
|
|
|
|
timeout,
|
2019-11-06 17:54:17 -08:00
|
|
|
commitment_config,
|
2019-10-21 16:08:09 -07:00
|
|
|
},
|
2020-02-24 16:03:30 -08:00
|
|
|
signers: vec![signer_from_path(
|
|
|
|
matches,
|
|
|
|
default_signer_path,
|
|
|
|
"keypair",
|
|
|
|
wallet_manager,
|
|
|
|
)?],
|
2019-10-04 19:54:09 -07:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2019-11-25 23:40:36 -08:00
|
|
|
pub fn parse_get_block_time(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
|
|
|
|
let slot = value_t_or_exit!(matches, "slot", u64);
|
|
|
|
Ok(CliCommandInfo {
|
|
|
|
command: CliCommand::GetBlockTime { slot },
|
2020-02-24 16:03:30 -08:00
|
|
|
signers: vec![],
|
2019-11-25 23:40:36 -08:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2019-11-24 16:34:18 -08:00
|
|
|
pub fn parse_get_epoch_info(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
|
|
|
|
let commitment_config = if matches.is_present("confirmed") {
|
|
|
|
CommitmentConfig::default()
|
|
|
|
} else {
|
|
|
|
CommitmentConfig::recent()
|
|
|
|
};
|
|
|
|
Ok(CliCommandInfo {
|
|
|
|
command: CliCommand::GetEpochInfo { commitment_config },
|
2020-02-24 16:03:30 -08:00
|
|
|
signers: vec![],
|
2019-11-24 16:34:18 -08:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn parse_get_slot(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
|
|
|
|
let commitment_config = if matches.is_present("confirmed") {
|
|
|
|
CommitmentConfig::default()
|
|
|
|
} else {
|
|
|
|
CommitmentConfig::recent()
|
|
|
|
};
|
|
|
|
Ok(CliCommandInfo {
|
|
|
|
command: CliCommand::GetSlot { commitment_config },
|
2020-02-24 16:03:30 -08:00
|
|
|
signers: vec![],
|
2019-11-24 16:34:18 -08:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2020-03-09 01:28:44 -07:00
|
|
|
pub fn parse_total_supply(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
|
|
|
|
let commitment_config = if matches.is_present("confirmed") {
|
|
|
|
CommitmentConfig::default()
|
|
|
|
} else {
|
|
|
|
CommitmentConfig::recent()
|
|
|
|
};
|
|
|
|
Ok(CliCommandInfo {
|
|
|
|
command: CliCommand::TotalSupply { commitment_config },
|
|
|
|
signers: vec![],
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2019-11-24 16:34:18 -08:00
|
|
|
pub fn parse_get_transaction_count(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
|
|
|
|
let commitment_config = if matches.is_present("confirmed") {
|
|
|
|
CommitmentConfig::default()
|
|
|
|
} else {
|
|
|
|
CommitmentConfig::recent()
|
|
|
|
};
|
|
|
|
Ok(CliCommandInfo {
|
|
|
|
command: CliCommand::GetTransactionCount { commitment_config },
|
2020-02-24 16:03:30 -08:00
|
|
|
signers: vec![],
|
2019-11-24 16:34:18 -08:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2020-01-17 11:10:52 -08:00
|
|
|
pub fn parse_show_stakes(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
|
|
|
|
let use_lamports_unit = matches.is_present("lamports");
|
|
|
|
let vote_account_pubkeys = pubkeys_of(matches, "vote_account_pubkeys");
|
|
|
|
|
|
|
|
Ok(CliCommandInfo {
|
|
|
|
command: CliCommand::ShowStakes {
|
|
|
|
use_lamports_unit,
|
|
|
|
vote_account_pubkeys,
|
|
|
|
},
|
2020-02-24 16:03:30 -08:00
|
|
|
signers: vec![],
|
2020-01-17 11:10:52 -08:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2019-10-21 16:08:09 -07:00
|
|
|
pub fn parse_show_validators(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
|
2019-10-06 21:47:24 -07:00
|
|
|
let use_lamports_unit = matches.is_present("lamports");
|
2020-03-03 16:53:30 -08:00
|
|
|
let commitment_config = if matches.is_present("confirmed") {
|
|
|
|
CommitmentConfig::default()
|
|
|
|
} else {
|
|
|
|
CommitmentConfig::recent()
|
|
|
|
};
|
2019-10-06 21:47:24 -07:00
|
|
|
|
2019-10-21 16:08:09 -07:00
|
|
|
Ok(CliCommandInfo {
|
2020-03-03 16:53:30 -08:00
|
|
|
command: CliCommand::ShowValidators {
|
|
|
|
use_lamports_unit,
|
|
|
|
commitment_config,
|
|
|
|
},
|
2020-02-24 16:03:30 -08:00
|
|
|
signers: vec![],
|
2019-10-21 16:08:09 -07:00
|
|
|
})
|
2019-10-06 21:47:24 -07:00
|
|
|
}
|
|
|
|
|
2019-11-13 14:58:14 -08:00
|
|
|
/// 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);
|
|
|
|
progress_bar
|
|
|
|
.set_style(ProgressStyle::default_spinner().template("{spinner:.green} {wide_msg}"));
|
|
|
|
progress_bar.enable_steady_tick(100);
|
|
|
|
progress_bar
|
|
|
|
}
|
|
|
|
|
2020-03-04 11:44:13 -08:00
|
|
|
pub fn process_catchup(
|
|
|
|
rpc_client: &RpcClient,
|
|
|
|
node_pubkey: &Pubkey,
|
|
|
|
node_json_rpc_url: &Option<String>,
|
|
|
|
) -> ProcessResult {
|
2019-11-13 14:58:14 -08:00
|
|
|
let cluster_nodes = rpc_client.get_cluster_nodes()?;
|
|
|
|
|
2020-03-04 11:44:13 -08:00
|
|
|
let node_client = if let Some(node_json_rpc_url) = node_json_rpc_url {
|
|
|
|
RpcClient::new(node_json_rpc_url.to_string())
|
|
|
|
} else {
|
|
|
|
RpcClient::new_socket(
|
|
|
|
cluster_nodes
|
|
|
|
.iter()
|
|
|
|
.find(|contact_info| contact_info.pubkey == node_pubkey.to_string())
|
|
|
|
.ok_or_else(|| format!("Contact information not found for {}", node_pubkey))?
|
|
|
|
.rpc
|
|
|
|
.ok_or_else(|| format!("RPC service not found for {}", node_pubkey))?,
|
|
|
|
)
|
|
|
|
};
|
2019-11-13 14:58:14 -08:00
|
|
|
|
2020-03-04 14:44:21 -08:00
|
|
|
let reported_node_pubkey = node_client.get_identity()?;
|
|
|
|
if reported_node_pubkey != *node_pubkey {
|
|
|
|
return Err(format!(
|
|
|
|
"The identity reported by node RPC URL does not match. Expected: {:?}. Reported: {:?}",
|
|
|
|
node_pubkey, reported_node_pubkey
|
|
|
|
)
|
|
|
|
.into());
|
|
|
|
}
|
|
|
|
|
|
|
|
if rpc_client.get_identity()? == *node_pubkey {
|
|
|
|
return Err("Both RPC URLs reference the same node, unable to monitor for catchup. Try a different --url".into());
|
|
|
|
}
|
|
|
|
|
2019-11-13 14:58:14 -08:00
|
|
|
let progress_bar = new_spinner_progress_bar();
|
|
|
|
progress_bar.set_message("Connecting...");
|
|
|
|
|
|
|
|
let mut previous_rpc_slot = std::u64::MAX;
|
|
|
|
let mut previous_slot_distance = 0;
|
|
|
|
let sleep_interval = 5;
|
|
|
|
loop {
|
|
|
|
let rpc_slot = rpc_client.get_slot_with_commitment(CommitmentConfig::recent())?;
|
|
|
|
let node_slot = node_client.get_slot_with_commitment(CommitmentConfig::recent())?;
|
|
|
|
if node_slot > std::cmp::min(previous_rpc_slot, rpc_slot) {
|
|
|
|
progress_bar.finish_and_clear();
|
|
|
|
return Ok(format!(
|
|
|
|
"{} has caught up (us:{} them:{})",
|
|
|
|
node_pubkey, node_slot, rpc_slot,
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
let slot_distance = rpc_slot as i64 - node_slot as i64;
|
|
|
|
progress_bar.set_message(&format!(
|
|
|
|
"Validator is {} slots away (us:{} them:{}){}",
|
|
|
|
slot_distance,
|
|
|
|
node_slot,
|
|
|
|
rpc_slot,
|
|
|
|
if previous_rpc_slot == std::u64::MAX {
|
|
|
|
"".to_string()
|
|
|
|
} else {
|
|
|
|
let slots_per_second =
|
|
|
|
(previous_slot_distance - slot_distance) as f64 / f64::from(sleep_interval);
|
|
|
|
|
|
|
|
format!(
|
|
|
|
" and {} at {:.1} slots/second",
|
|
|
|
if slots_per_second < 0.0 {
|
|
|
|
"falling behind"
|
|
|
|
} else {
|
|
|
|
"gaining"
|
|
|
|
},
|
|
|
|
slots_per_second,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
));
|
|
|
|
|
|
|
|
sleep(Duration::from_secs(sleep_interval as u64));
|
|
|
|
previous_rpc_slot = rpc_slot;
|
|
|
|
previous_slot_distance = slot_distance;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn process_cluster_version(rpc_client: &RpcClient) -> ProcessResult {
|
2019-11-12 21:01:04 -08:00
|
|
|
let remote_version = rpc_client.get_version()?;
|
|
|
|
Ok(remote_version.solana_core)
|
2019-10-04 19:54:09 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn process_fees(rpc_client: &RpcClient) -> ProcessResult {
|
|
|
|
let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
|
|
|
|
|
|
|
|
Ok(format!(
|
|
|
|
"blockhash: {}\nlamports per signature: {}",
|
|
|
|
recent_blockhash, fee_calculator.lamports_per_signature
|
|
|
|
))
|
|
|
|
}
|
|
|
|
|
2020-01-28 20:52:52 -08:00
|
|
|
pub fn process_leader_schedule(rpc_client: &RpcClient) -> ProcessResult {
|
|
|
|
let epoch_info = rpc_client.get_epoch_info()?;
|
|
|
|
let first_slot_in_epoch = epoch_info.absolute_slot - epoch_info.slot_index;
|
|
|
|
|
|
|
|
let leader_schedule = rpc_client.get_leader_schedule(Some(first_slot_in_epoch))?;
|
|
|
|
if leader_schedule.is_none() {
|
|
|
|
return Err(format!(
|
|
|
|
"Unable to fetch leader schedule for slot {}",
|
|
|
|
first_slot_in_epoch
|
|
|
|
)
|
|
|
|
.into());
|
|
|
|
}
|
|
|
|
let leader_schedule = leader_schedule.unwrap();
|
|
|
|
|
|
|
|
let mut leader_per_slot_index = Vec::new();
|
|
|
|
for (pubkey, leader_slots) in leader_schedule.iter() {
|
|
|
|
for slot_index in leader_slots.iter() {
|
|
|
|
if *slot_index >= leader_per_slot_index.len() {
|
|
|
|
leader_per_slot_index.resize(*slot_index + 1, "?");
|
|
|
|
}
|
|
|
|
leader_per_slot_index[*slot_index] = pubkey;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (slot_index, leader) in leader_per_slot_index.iter().enumerate() {
|
|
|
|
println!(
|
|
|
|
" {:<15} {:<44}",
|
|
|
|
first_slot_in_epoch + slot_index as u64,
|
|
|
|
leader
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok("".to_string())
|
|
|
|
}
|
|
|
|
|
2019-11-25 23:40:36 -08:00
|
|
|
pub fn process_get_block_time(rpc_client: &RpcClient, slot: Slot) -> ProcessResult {
|
|
|
|
let timestamp = rpc_client.get_block_time(slot)?;
|
2020-03-02 14:58:15 -08:00
|
|
|
let result = format!(
|
|
|
|
"{} (UnixTimestamp: {})",
|
|
|
|
DateTime::<Utc>::from_utc(NaiveDateTime::from_timestamp(timestamp, 0), Utc)
|
|
|
|
.to_rfc3339_opts(SecondsFormat::Secs, true),
|
|
|
|
timestamp
|
|
|
|
);
|
|
|
|
Ok(result)
|
2019-11-25 23:40:36 -08:00
|
|
|
}
|
|
|
|
|
2020-01-16 16:39:47 -08:00
|
|
|
fn slot_to_human_time(slot: Slot) -> String {
|
|
|
|
humantime::format_duration(Duration::from_secs(
|
|
|
|
slot * clock::DEFAULT_TICKS_PER_SLOT / clock::DEFAULT_TICKS_PER_SECOND,
|
|
|
|
))
|
|
|
|
.to_string()
|
|
|
|
}
|
|
|
|
|
2019-11-24 16:34:18 -08:00
|
|
|
pub fn process_get_epoch_info(
|
|
|
|
rpc_client: &RpcClient,
|
2020-03-03 16:53:30 -08:00
|
|
|
commitment_config: CommitmentConfig,
|
2019-11-24 16:34:18 -08:00
|
|
|
) -> ProcessResult {
|
|
|
|
let epoch_info = rpc_client.get_epoch_info_with_commitment(commitment_config.clone())?;
|
2019-10-04 19:54:09 -07:00
|
|
|
println!();
|
2020-01-16 16:39:47 -08:00
|
|
|
println_name_value("Slot:", &epoch_info.absolute_slot.to_string());
|
|
|
|
println_name_value("Epoch:", &epoch_info.epoch.to_string());
|
2020-01-23 20:44:37 -08:00
|
|
|
let start_slot = epoch_info.absolute_slot - epoch_info.slot_index;
|
|
|
|
let end_slot = start_slot + epoch_info.slots_in_epoch;
|
|
|
|
println_name_value(
|
2020-02-05 10:14:44 -08:00
|
|
|
"Epoch Slot Range:",
|
2020-01-23 20:44:37 -08:00
|
|
|
&format!("[{}..{})", start_slot, end_slot),
|
|
|
|
);
|
2019-10-04 19:54:09 -07:00
|
|
|
println_name_value(
|
2020-02-05 10:14:44 -08:00
|
|
|
"Epoch Completed Percent:",
|
2020-01-16 16:39:47 -08:00
|
|
|
&format!(
|
|
|
|
"{:>3.3}%",
|
|
|
|
epoch_info.slot_index as f64 / epoch_info.slots_in_epoch as f64 * 100_f64
|
|
|
|
),
|
2019-10-04 19:54:09 -07:00
|
|
|
);
|
|
|
|
let remaining_slots_in_epoch = epoch_info.slots_in_epoch - epoch_info.slot_index;
|
|
|
|
println_name_value(
|
2020-02-05 10:14:44 -08:00
|
|
|
"Epoch Completed Slots:",
|
2020-01-16 16:39:47 -08:00
|
|
|
&format!(
|
|
|
|
"{}/{} ({} remaining)",
|
|
|
|
epoch_info.slot_index, epoch_info.slots_in_epoch, remaining_slots_in_epoch
|
|
|
|
),
|
2019-10-04 19:54:09 -07:00
|
|
|
);
|
|
|
|
println_name_value(
|
2020-02-05 10:14:44 -08:00
|
|
|
"Epoch Completed Time:",
|
2020-01-16 16:39:47 -08:00
|
|
|
&format!(
|
|
|
|
"{}/{} ({} remaining)",
|
|
|
|
slot_to_human_time(epoch_info.slot_index),
|
|
|
|
slot_to_human_time(epoch_info.slots_in_epoch),
|
|
|
|
slot_to_human_time(remaining_slots_in_epoch)
|
|
|
|
),
|
2019-10-04 19:54:09 -07:00
|
|
|
);
|
|
|
|
Ok("".to_string())
|
|
|
|
}
|
|
|
|
|
2019-11-08 20:56:57 -08:00
|
|
|
pub fn process_get_genesis_hash(rpc_client: &RpcClient) -> ProcessResult {
|
|
|
|
let genesis_hash = rpc_client.get_genesis_hash()?;
|
|
|
|
Ok(genesis_hash.to_string())
|
2019-10-04 19:54:09 -07:00
|
|
|
}
|
|
|
|
|
2019-11-24 16:34:18 -08:00
|
|
|
pub fn process_get_slot(
|
|
|
|
rpc_client: &RpcClient,
|
2020-03-03 16:53:30 -08:00
|
|
|
commitment_config: CommitmentConfig,
|
2019-11-24 16:34:18 -08:00
|
|
|
) -> ProcessResult {
|
|
|
|
let slot = rpc_client.get_slot_with_commitment(commitment_config.clone())?;
|
2019-10-04 19:54:09 -07:00
|
|
|
Ok(slot.to_string())
|
|
|
|
}
|
|
|
|
|
2019-12-18 10:38:54 -08:00
|
|
|
pub fn parse_show_block_production(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
|
|
|
|
let epoch = value_t!(matches, "epoch", Epoch).ok();
|
|
|
|
let slot_limit = value_t!(matches, "slot_limit", u64).ok();
|
|
|
|
|
|
|
|
Ok(CliCommandInfo {
|
|
|
|
command: CliCommand::ShowBlockProduction { epoch, slot_limit },
|
2020-02-24 16:03:30 -08:00
|
|
|
signers: vec![],
|
2019-12-18 10:38:54 -08:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn process_show_block_production(
|
|
|
|
rpc_client: &RpcClient,
|
2019-12-20 08:38:08 -08:00
|
|
|
config: &CliConfig,
|
2019-12-18 10:38:54 -08:00
|
|
|
epoch: Option<Epoch>,
|
|
|
|
slot_limit: Option<u64>,
|
|
|
|
) -> ProcessResult {
|
|
|
|
let epoch_schedule = rpc_client.get_epoch_schedule()?;
|
|
|
|
let epoch_info = rpc_client.get_epoch_info_with_commitment(CommitmentConfig::max())?;
|
|
|
|
|
|
|
|
let epoch = epoch.unwrap_or(epoch_info.epoch);
|
|
|
|
if epoch > epoch_info.epoch {
|
|
|
|
return Err(format!("Epoch {} is in the future", epoch).into());
|
|
|
|
}
|
|
|
|
|
2020-01-21 12:05:59 -08:00
|
|
|
let minimum_ledger_slot = rpc_client.minimum_ledger_slot()?;
|
|
|
|
|
2019-12-20 08:38:08 -08:00
|
|
|
let first_slot_in_epoch = epoch_schedule.get_first_slot_in_epoch(epoch);
|
2019-12-18 10:38:54 -08:00
|
|
|
let end_slot = std::cmp::min(
|
|
|
|
epoch_info.absolute_slot,
|
|
|
|
epoch_schedule.get_last_slot_in_epoch(epoch),
|
|
|
|
);
|
2019-12-20 08:38:08 -08:00
|
|
|
|
2020-01-21 12:05:59 -08:00
|
|
|
let mut start_slot = if let Some(slot_limit) = slot_limit {
|
2019-12-20 08:38:08 -08:00
|
|
|
std::cmp::max(end_slot.saturating_sub(slot_limit), first_slot_in_epoch)
|
|
|
|
} else {
|
|
|
|
first_slot_in_epoch
|
2019-12-18 10:38:54 -08:00
|
|
|
};
|
2020-01-21 12:05:59 -08:00
|
|
|
|
|
|
|
if minimum_ledger_slot > end_slot {
|
|
|
|
return Err(format!(
|
|
|
|
"Ledger data not available for slots {} to {} (minimum ledger slot is {})",
|
|
|
|
start_slot, end_slot, minimum_ledger_slot
|
|
|
|
)
|
|
|
|
.into());
|
|
|
|
}
|
|
|
|
|
|
|
|
if minimum_ledger_slot > start_slot {
|
|
|
|
println!(
|
|
|
|
"\n{}",
|
|
|
|
style(format!(
|
|
|
|
"Note: Requested start slot was {} but minimum ledger slot is {}",
|
|
|
|
start_slot, minimum_ledger_slot
|
|
|
|
))
|
|
|
|
.italic(),
|
|
|
|
);
|
|
|
|
start_slot = minimum_ledger_slot;
|
|
|
|
}
|
2019-12-18 10:38:54 -08:00
|
|
|
|
|
|
|
let progress_bar = new_spinner_progress_bar();
|
|
|
|
progress_bar.set_message(&format!(
|
|
|
|
"Fetching confirmed blocks between slots {} and {}...",
|
|
|
|
start_slot, end_slot
|
|
|
|
));
|
|
|
|
let confirmed_blocks = rpc_client.get_confirmed_blocks(start_slot, Some(end_slot))?;
|
|
|
|
|
2020-01-21 12:05:59 -08:00
|
|
|
let start_slot_index = (start_slot - first_slot_in_epoch) as usize;
|
|
|
|
let end_slot_index = (end_slot - first_slot_in_epoch) as usize;
|
2019-12-20 08:38:08 -08:00
|
|
|
let total_slots = end_slot_index - start_slot_index + 1;
|
2019-12-18 10:38:54 -08:00
|
|
|
let total_blocks = confirmed_blocks.len();
|
2019-12-19 08:48:10 -08:00
|
|
|
assert!(total_blocks <= total_slots);
|
2019-12-22 21:39:47 -08:00
|
|
|
let total_slots_skipped = total_slots - total_blocks;
|
2019-12-18 10:38:54 -08:00
|
|
|
let mut leader_slot_count = HashMap::new();
|
2019-12-22 21:39:47 -08:00
|
|
|
let mut leader_skipped_slots = HashMap::new();
|
2019-12-18 10:38:54 -08:00
|
|
|
|
2019-12-20 08:38:08 -08:00
|
|
|
progress_bar.set_message(&format!("Fetching leader schedule for epoch {}...", epoch));
|
2019-12-19 08:48:10 -08:00
|
|
|
let leader_schedule = rpc_client
|
|
|
|
.get_leader_schedule_with_commitment(Some(start_slot), CommitmentConfig::max())?;
|
|
|
|
if leader_schedule.is_none() {
|
|
|
|
return Err(format!("Unable to fetch leader schedule for slot {}", start_slot).into());
|
|
|
|
}
|
|
|
|
let leader_schedule = leader_schedule.unwrap();
|
|
|
|
|
|
|
|
let mut leader_per_slot_index = Vec::new();
|
2019-12-20 08:38:08 -08:00
|
|
|
leader_per_slot_index.resize(total_slots, "?");
|
2019-12-19 08:48:10 -08:00
|
|
|
for (pubkey, leader_slots) in leader_schedule.iter() {
|
|
|
|
for slot_index in leader_slots.iter() {
|
2019-12-20 08:38:08 -08:00
|
|
|
if *slot_index >= start_slot_index && *slot_index <= end_slot_index {
|
|
|
|
leader_per_slot_index[*slot_index - start_slot_index] = pubkey;
|
2019-12-18 10:38:54 -08:00
|
|
|
}
|
2019-12-19 08:48:10 -08:00
|
|
|
}
|
|
|
|
}
|
2019-12-18 10:38:54 -08:00
|
|
|
|
2019-12-19 08:48:10 -08:00
|
|
|
progress_bar.set_message(&format!(
|
|
|
|
"Processing {} slots containing {} blocks and {} empty slots...",
|
2019-12-22 21:39:47 -08:00
|
|
|
total_slots, total_blocks, total_slots_skipped
|
2019-12-19 08:48:10 -08:00
|
|
|
));
|
|
|
|
|
|
|
|
let mut confirmed_blocks_index = 0;
|
2019-12-20 08:38:08 -08:00
|
|
|
let mut individual_slot_status = vec![];
|
|
|
|
for (slot_index, leader) in leader_per_slot_index.iter().enumerate() {
|
2019-12-18 10:38:54 -08:00
|
|
|
let slot = start_slot + slot_index as u64;
|
|
|
|
let slot_count = leader_slot_count.entry(leader).or_insert(0);
|
|
|
|
*slot_count += 1;
|
2019-12-22 21:39:47 -08:00
|
|
|
let skipped_slots = leader_skipped_slots.entry(leader).or_insert(0);
|
2019-12-18 10:38:54 -08:00
|
|
|
|
2019-12-19 08:48:10 -08:00
|
|
|
loop {
|
2019-12-21 08:19:57 -08:00
|
|
|
if confirmed_blocks_index < confirmed_blocks.len() {
|
2019-12-19 08:48:10 -08:00
|
|
|
let slot_of_next_confirmed_block = confirmed_blocks[confirmed_blocks_index];
|
|
|
|
if slot_of_next_confirmed_block < slot {
|
|
|
|
confirmed_blocks_index += 1;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if slot_of_next_confirmed_block == slot {
|
2019-12-20 08:38:08 -08:00
|
|
|
individual_slot_status
|
|
|
|
.push(style(format!(" {:<15} {:<44}", slot, leader)).to_string());
|
2019-12-19 08:48:10 -08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2019-12-22 21:39:47 -08:00
|
|
|
*skipped_slots += 1;
|
2019-12-20 08:38:08 -08:00
|
|
|
individual_slot_status.push(
|
2019-12-22 09:19:12 -08:00
|
|
|
style(format!(" {:<15} {:<44} SKIPPED", slot, leader))
|
2019-12-20 08:38:08 -08:00
|
|
|
.red()
|
|
|
|
.to_string(),
|
|
|
|
);
|
2019-12-19 08:48:10 -08:00
|
|
|
break;
|
2019-12-18 10:38:54 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
progress_bar.finish_and_clear();
|
|
|
|
println!(
|
|
|
|
"\n{}",
|
|
|
|
style(format!(
|
|
|
|
" {:<44} {:>15} {:>15} {:>15} {:>23}",
|
|
|
|
"Identity Pubkey",
|
|
|
|
"Leader Slots",
|
|
|
|
"Blocks Produced",
|
2019-12-22 21:39:47 -08:00
|
|
|
"Skipped Slots",
|
|
|
|
"Skipped Slot Percentage",
|
2019-12-18 10:38:54 -08:00
|
|
|
))
|
|
|
|
.bold()
|
|
|
|
);
|
|
|
|
|
2019-12-19 08:48:10 -08:00
|
|
|
let mut table = vec![];
|
2019-12-18 10:38:54 -08:00
|
|
|
for (leader, leader_slots) in leader_slot_count.iter() {
|
2019-12-22 21:39:47 -08:00
|
|
|
let skipped_slots = leader_skipped_slots.get(leader).unwrap();
|
|
|
|
let blocks_produced = leader_slots - skipped_slots;
|
2019-12-19 08:48:10 -08:00
|
|
|
table.push(format!(
|
2019-12-18 10:38:54 -08:00
|
|
|
" {:<44} {:>15} {:>15} {:>15} {:>22.2}%",
|
|
|
|
leader,
|
|
|
|
leader_slots,
|
|
|
|
blocks_produced,
|
2019-12-22 21:39:47 -08:00
|
|
|
skipped_slots,
|
|
|
|
*skipped_slots as f64 / *leader_slots as f64 * 100.
|
2019-12-19 08:48:10 -08:00
|
|
|
));
|
2019-12-18 10:38:54 -08:00
|
|
|
}
|
2019-12-19 08:48:10 -08:00
|
|
|
table.sort();
|
2019-12-18 10:38:54 -08:00
|
|
|
|
|
|
|
println!(
|
2019-12-19 08:48:10 -08:00
|
|
|
"{}\n\n {:<44} {:>15} {:>15} {:>15} {:>22.2}%",
|
|
|
|
table.join("\n"),
|
2019-12-18 10:38:54 -08:00
|
|
|
format!("Epoch {} total:", epoch),
|
|
|
|
total_slots,
|
|
|
|
total_blocks,
|
2019-12-22 21:39:47 -08:00
|
|
|
total_slots_skipped,
|
|
|
|
total_slots_skipped as f64 / total_slots as f64 * 100.
|
2019-12-18 10:38:54 -08:00
|
|
|
);
|
|
|
|
println!(
|
|
|
|
" (using data from {} slots: {} to {})",
|
|
|
|
total_slots, start_slot, end_slot
|
|
|
|
);
|
2019-12-20 08:38:08 -08:00
|
|
|
|
|
|
|
if config.verbose {
|
|
|
|
println!(
|
|
|
|
"\n\n{}\n{}",
|
|
|
|
style(format!(" {:<15} {:<44}", "Slot", "Identity Pubkey")).bold(),
|
|
|
|
individual_slot_status.join("\n")
|
|
|
|
);
|
|
|
|
}
|
2019-12-18 10:38:54 -08:00
|
|
|
Ok("".to_string())
|
|
|
|
}
|
|
|
|
|
2020-03-09 01:28:44 -07:00
|
|
|
pub fn process_total_supply(
|
|
|
|
rpc_client: &RpcClient,
|
|
|
|
commitment_config: CommitmentConfig,
|
|
|
|
) -> ProcessResult {
|
|
|
|
let total_supply = rpc_client.total_supply_with_commitment(commitment_config.clone())?;
|
|
|
|
Ok(format!("{} SOL", lamports_to_sol(total_supply)))
|
|
|
|
}
|
|
|
|
|
2019-11-24 16:34:18 -08:00
|
|
|
pub fn process_get_transaction_count(
|
|
|
|
rpc_client: &RpcClient,
|
2020-03-03 16:53:30 -08:00
|
|
|
commitment_config: CommitmentConfig,
|
2019-11-24 16:34:18 -08:00
|
|
|
) -> ProcessResult {
|
|
|
|
let transaction_count =
|
|
|
|
rpc_client.get_transaction_count_with_commitment(commitment_config.clone())?;
|
2019-10-04 19:54:09 -07:00
|
|
|
Ok(transaction_count.to_string())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn process_ping(
|
|
|
|
rpc_client: &RpcClient,
|
|
|
|
config: &CliConfig,
|
2019-11-18 21:50:09 -08:00
|
|
|
lamports: u64,
|
2019-10-04 19:54:09 -07:00
|
|
|
interval: &Duration,
|
|
|
|
count: &Option<u64>,
|
|
|
|
timeout: &Duration,
|
2020-03-03 16:53:30 -08:00
|
|
|
commitment_config: CommitmentConfig,
|
2019-10-04 19:54:09 -07:00
|
|
|
) -> ProcessResult {
|
|
|
|
let to = Keypair::new().pubkey();
|
|
|
|
|
2020-02-24 16:03:30 -08:00
|
|
|
println_name_value("Source Account:", &config.signers[0].pubkey().to_string());
|
2020-02-05 10:14:44 -08:00
|
|
|
println_name_value("Destination Account:", &to.to_string());
|
2019-10-04 19:54:09 -07:00
|
|
|
println!();
|
|
|
|
|
|
|
|
let (signal_sender, signal_receiver) = std::sync::mpsc::channel();
|
|
|
|
ctrlc::set_handler(move || {
|
|
|
|
let _ = signal_sender.send(());
|
|
|
|
})
|
|
|
|
.expect("Error setting Ctrl-C handler");
|
|
|
|
|
|
|
|
let mut last_blockhash = Hash::default();
|
|
|
|
let mut submit_count = 0;
|
|
|
|
let mut confirmed_count = 0;
|
|
|
|
let mut confirmation_time: VecDeque<u64> = VecDeque::with_capacity(1024);
|
|
|
|
|
|
|
|
'mainloop: for seq in 0..count.unwrap_or(std::u64::MAX) {
|
|
|
|
let (recent_blockhash, fee_calculator) = rpc_client.get_new_blockhash(&last_blockhash)?;
|
|
|
|
last_blockhash = recent_blockhash;
|
|
|
|
|
2020-02-24 16:03:30 -08:00
|
|
|
let ix = system_instruction::transfer(&config.signers[0].pubkey(), &to, lamports);
|
2020-02-21 13:55:53 -08:00
|
|
|
let message = Message::new(vec![ix]);
|
|
|
|
let mut transaction = Transaction::new_unsigned(message);
|
2020-02-24 16:03:30 -08:00
|
|
|
transaction.try_sign(&config.signers, recent_blockhash)?;
|
2019-12-09 23:11:04 -08:00
|
|
|
check_account_for_fee(
|
|
|
|
rpc_client,
|
2020-02-24 16:03:30 -08:00
|
|
|
&config.signers[0].pubkey(),
|
2019-12-09 23:11:04 -08:00
|
|
|
&fee_calculator,
|
|
|
|
&transaction.message,
|
|
|
|
)?;
|
2019-10-04 19:54:09 -07:00
|
|
|
|
|
|
|
match rpc_client.send_transaction(&transaction) {
|
|
|
|
Ok(signature) => {
|
|
|
|
let transaction_sent = Instant::now();
|
|
|
|
loop {
|
2019-11-06 13:15:00 -08:00
|
|
|
let signature_status = rpc_client.get_signature_status_with_commitment(
|
|
|
|
&signature,
|
2019-11-06 17:54:17 -08:00
|
|
|
commitment_config.clone(),
|
2019-11-06 13:15:00 -08:00
|
|
|
)?;
|
2019-10-04 19:54:09 -07:00
|
|
|
let elapsed_time = Instant::now().duration_since(transaction_sent);
|
|
|
|
if let Some(transaction_status) = signature_status {
|
|
|
|
match transaction_status {
|
|
|
|
Ok(()) => {
|
|
|
|
let elapsed_time_millis = elapsed_time.as_millis() as u64;
|
|
|
|
confirmation_time.push_back(elapsed_time_millis);
|
|
|
|
println!(
|
2019-11-18 21:50:09 -08:00
|
|
|
"{}{} lamport(s) transferred: seq={:<3} time={:>4}ms signature={}",
|
|
|
|
CHECK_MARK, lamports, seq, elapsed_time_millis, signature
|
2019-10-04 19:54:09 -07:00
|
|
|
);
|
|
|
|
confirmed_count += 1;
|
|
|
|
}
|
|
|
|
Err(err) => {
|
|
|
|
println!(
|
|
|
|
"{}Transaction failed: seq={:<3} error={:?} signature={}",
|
|
|
|
CROSS_MARK, seq, err, signature
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if elapsed_time >= *timeout {
|
|
|
|
println!(
|
|
|
|
"{}Confirmation timeout: seq={:<3} signature={}",
|
|
|
|
CROSS_MARK, seq, signature
|
|
|
|
);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Sleep for half a slot
|
|
|
|
if signal_receiver
|
|
|
|
.recv_timeout(Duration::from_millis(
|
2019-11-25 23:40:36 -08:00
|
|
|
500 * clock::DEFAULT_TICKS_PER_SLOT / clock::DEFAULT_TICKS_PER_SECOND,
|
2019-10-04 19:54:09 -07:00
|
|
|
))
|
|
|
|
.is_ok()
|
|
|
|
{
|
|
|
|
break 'mainloop;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Err(err) => {
|
|
|
|
println!(
|
|
|
|
"{}Submit failed: seq={:<3} error={:?}",
|
|
|
|
CROSS_MARK, seq, err
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
submit_count += 1;
|
|
|
|
|
|
|
|
if signal_receiver.recv_timeout(*interval).is_ok() {
|
|
|
|
break 'mainloop;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
println!();
|
|
|
|
println!("--- transaction statistics ---");
|
|
|
|
println!(
|
|
|
|
"{} transactions submitted, {} transactions confirmed, {:.1}% transaction loss",
|
|
|
|
submit_count,
|
|
|
|
confirmed_count,
|
|
|
|
(100. - f64::from(confirmed_count) / f64::from(submit_count) * 100.)
|
|
|
|
);
|
|
|
|
if !confirmation_time.is_empty() {
|
|
|
|
let samples: Vec<f64> = confirmation_time.iter().map(|t| *t as f64).collect();
|
|
|
|
let dist = criterion_stats::Distribution::from(samples.into_boxed_slice());
|
|
|
|
let mean = dist.mean();
|
|
|
|
println!(
|
|
|
|
"confirmation min/mean/max/stddev = {:.0}/{:.0}/{:.0}/{:.0} ms",
|
|
|
|
dist.min(),
|
|
|
|
mean,
|
|
|
|
dist.max(),
|
|
|
|
dist.std_dev(Some(mean))
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok("".to_string())
|
|
|
|
}
|
|
|
|
|
2020-02-06 11:16:30 -08:00
|
|
|
pub fn process_live_slots(url: &str) -> ProcessResult {
|
|
|
|
let exit = Arc::new(AtomicBool::new(false));
|
|
|
|
|
2020-02-25 17:04:54 -08:00
|
|
|
// Disable Ctrl+C handler as sometimes the PubsubClient shutdown can stall. Also it doesn't
|
|
|
|
// really matter that the shutdown is clean because the process is terminating.
|
|
|
|
/*
|
|
|
|
let exit_clone = exit.clone();
|
2020-02-06 11:16:30 -08:00
|
|
|
ctrlc::set_handler(move || {
|
|
|
|
exit_clone.store(true, Ordering::Relaxed);
|
|
|
|
})?;
|
2020-02-25 17:04:54 -08:00
|
|
|
*/
|
2020-02-06 11:16:30 -08:00
|
|
|
|
|
|
|
let mut current: Option<SlotInfoMessage> = None;
|
|
|
|
let mut message = "".to_string();
|
|
|
|
|
|
|
|
let slot_progress = new_spinner_progress_bar();
|
|
|
|
slot_progress.set_message("Connecting...");
|
|
|
|
let (mut client, receiver) = PubsubClient::slot_subscribe(url)?;
|
|
|
|
slot_progress.set_message("Connected.");
|
|
|
|
|
2020-02-29 08:39:07 -08:00
|
|
|
let spacer = "|";
|
|
|
|
slot_progress.println(spacer);
|
|
|
|
|
2020-02-25 17:04:54 -08:00
|
|
|
let mut last_root = std::u64::MAX;
|
|
|
|
let mut last_root_update = Instant::now();
|
|
|
|
let mut slots_per_second = std::f64::NAN;
|
2020-02-06 11:16:30 -08:00
|
|
|
loop {
|
|
|
|
if exit.load(Ordering::Relaxed) {
|
|
|
|
eprintln!("{}", message);
|
|
|
|
client.shutdown().unwrap();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
match receiver.recv() {
|
|
|
|
Ok(new_info) => {
|
2020-02-25 17:04:54 -08:00
|
|
|
if last_root == std::u64::MAX {
|
|
|
|
last_root = new_info.root;
|
|
|
|
last_root_update = Instant::now();
|
|
|
|
}
|
|
|
|
if last_root_update.elapsed().as_secs() >= 5 {
|
|
|
|
let root = new_info.root;
|
|
|
|
slots_per_second =
|
|
|
|
(root - last_root) as f64 / last_root_update.elapsed().as_secs() as f64;
|
|
|
|
last_root_update = Instant::now();
|
|
|
|
last_root = root;
|
|
|
|
}
|
|
|
|
|
|
|
|
message = if slots_per_second.is_nan() {
|
|
|
|
format!("{:?}", new_info)
|
|
|
|
} else {
|
|
|
|
format!(
|
|
|
|
"{:?} | root slot advancing at {:.2} slots/second",
|
|
|
|
new_info, slots_per_second
|
|
|
|
)
|
|
|
|
}
|
|
|
|
.to_owned();
|
2020-02-06 11:16:30 -08:00
|
|
|
slot_progress.set_message(&message);
|
|
|
|
|
|
|
|
if let Some(previous) = current {
|
|
|
|
let slot_delta: i64 = new_info.slot as i64 - previous.slot as i64;
|
|
|
|
let root_delta: i64 = new_info.root as i64 - previous.root as i64;
|
|
|
|
|
|
|
|
//
|
|
|
|
// if slot has advanced out of step with the root, we detect
|
|
|
|
// a mismatch and output the slot information
|
|
|
|
//
|
|
|
|
if slot_delta != root_delta {
|
|
|
|
let prev_root = format!(
|
2020-02-29 08:39:07 -08:00
|
|
|
"|<--- {} <- … <- {} <- {} (prev)",
|
2020-02-06 11:16:30 -08:00
|
|
|
previous.root, previous.parent, previous.slot
|
2020-02-29 08:39:07 -08:00
|
|
|
);
|
2020-02-06 11:16:30 -08:00
|
|
|
slot_progress.println(&prev_root);
|
2020-02-29 08:39:07 -08:00
|
|
|
|
|
|
|
let new_root = format!(
|
|
|
|
"| '- {} <- … <- {} <- {} (next)",
|
|
|
|
new_info.root, new_info.parent, new_info.slot
|
|
|
|
);
|
|
|
|
|
|
|
|
slot_progress.println(prev_root);
|
|
|
|
slot_progress.println(new_root);
|
|
|
|
slot_progress.println(spacer);
|
2020-02-06 11:16:30 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
current = Some(new_info);
|
|
|
|
}
|
|
|
|
Err(err) => {
|
|
|
|
eprintln!("disconnected: {:?}", err);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok("".to_string())
|
|
|
|
}
|
|
|
|
|
2019-11-15 12:15:34 -08:00
|
|
|
pub fn process_show_gossip(rpc_client: &RpcClient) -> ProcessResult {
|
|
|
|
let cluster_nodes = rpc_client.get_cluster_nodes()?;
|
|
|
|
|
|
|
|
fn format_port(addr: Option<SocketAddr>) -> String {
|
|
|
|
addr.map(|addr| addr.port().to_string())
|
|
|
|
.unwrap_or_else(|| "none".to_string())
|
|
|
|
}
|
|
|
|
|
|
|
|
let s: Vec<_> = cluster_nodes
|
|
|
|
.iter()
|
|
|
|
.map(|node| {
|
|
|
|
format!(
|
|
|
|
"{:15} | {:44} | {:6} | {:5} | {:5}",
|
|
|
|
node.gossip
|
|
|
|
.map(|addr| addr.ip().to_string())
|
|
|
|
.unwrap_or_else(|| "none".to_string()),
|
|
|
|
node.pubkey,
|
|
|
|
format_port(node.gossip),
|
|
|
|
format_port(node.tpu),
|
|
|
|
format_port(node.rpc),
|
|
|
|
)
|
|
|
|
})
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
Ok(format!(
|
|
|
|
"IP Address | Node identifier \
|
|
|
|
| Gossip | TPU | RPC\n\
|
|
|
|
----------------+----------------------------------------------+\
|
|
|
|
--------+-------+-------\n\
|
|
|
|
{}\n\
|
|
|
|
Nodes: {}",
|
|
|
|
s.join("\n"),
|
|
|
|
s.len(),
|
|
|
|
))
|
|
|
|
}
|
|
|
|
|
2020-01-17 11:10:52 -08:00
|
|
|
pub fn process_show_stakes(
|
|
|
|
rpc_client: &RpcClient,
|
|
|
|
use_lamports_unit: bool,
|
|
|
|
vote_account_pubkeys: Option<&[Pubkey]>,
|
|
|
|
) -> ProcessResult {
|
|
|
|
use crate::stake::print_stake_state;
|
|
|
|
use solana_stake_program::stake_state::StakeState;
|
|
|
|
|
|
|
|
let progress_bar = new_spinner_progress_bar();
|
|
|
|
progress_bar.set_message("Fetching stake accounts...");
|
|
|
|
let all_stake_accounts = rpc_client.get_program_accounts(&solana_stake_program::id())?;
|
|
|
|
progress_bar.finish_and_clear();
|
|
|
|
|
|
|
|
for (stake_pubkey, stake_account) in all_stake_accounts {
|
|
|
|
if let Ok(stake_state) = stake_account.state() {
|
|
|
|
match stake_state {
|
|
|
|
StakeState::Initialized(_) => {
|
|
|
|
if vote_account_pubkeys.is_none() {
|
|
|
|
println!("\nstake pubkey: {}", stake_pubkey);
|
|
|
|
print_stake_state(stake_account.lamports, &stake_state, use_lamports_unit);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
StakeState::Stake(_, stake) => {
|
|
|
|
if vote_account_pubkeys.is_none()
|
|
|
|
|| vote_account_pubkeys
|
|
|
|
.unwrap()
|
|
|
|
.contains(&stake.delegation.voter_pubkey)
|
|
|
|
{
|
|
|
|
println!("\nstake pubkey: {}", stake_pubkey);
|
|
|
|
print_stake_state(stake_account.lamports, &stake_state, use_lamports_unit);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Ok("".to_string())
|
|
|
|
}
|
|
|
|
|
2020-03-03 16:53:30 -08:00
|
|
|
pub fn process_show_validators(
|
|
|
|
rpc_client: &RpcClient,
|
|
|
|
use_lamports_unit: bool,
|
|
|
|
commitment_config: CommitmentConfig,
|
|
|
|
) -> ProcessResult {
|
|
|
|
let epoch_info = rpc_client.get_epoch_info_with_commitment(commitment_config)?;
|
|
|
|
let vote_accounts = rpc_client.get_vote_accounts_with_commitment(commitment_config)?;
|
2019-10-23 21:40:35 -07:00
|
|
|
let total_active_stake = vote_accounts
|
2019-10-06 21:47:24 -07:00
|
|
|
.current
|
|
|
|
.iter()
|
|
|
|
.chain(vote_accounts.delinquent.iter())
|
|
|
|
.fold(0, |acc, vote_account| acc + vote_account.activated_stake)
|
|
|
|
as f64;
|
|
|
|
|
2019-10-23 21:40:35 -07:00
|
|
|
let total_deliquent_stake = vote_accounts
|
|
|
|
.delinquent
|
|
|
|
.iter()
|
|
|
|
.fold(0, |acc, vote_account| acc + vote_account.activated_stake)
|
|
|
|
as f64;
|
|
|
|
let total_current_stake = total_active_stake - total_deliquent_stake;
|
|
|
|
|
|
|
|
println_name_value(
|
|
|
|
"Active Stake:",
|
2019-10-25 09:20:08 -07:00
|
|
|
&build_balance_message(total_active_stake as u64, use_lamports_unit, true),
|
2019-10-23 21:40:35 -07:00
|
|
|
);
|
|
|
|
if total_deliquent_stake > 0. {
|
|
|
|
println_name_value(
|
|
|
|
"Current Stake:",
|
|
|
|
&format!(
|
|
|
|
"{} ({:0.2}%)",
|
2019-10-25 09:20:08 -07:00
|
|
|
&build_balance_message(total_current_stake as u64, use_lamports_unit, true),
|
2019-10-23 21:40:35 -07:00
|
|
|
100. * total_current_stake / total_active_stake
|
|
|
|
),
|
|
|
|
);
|
|
|
|
println_name_value(
|
|
|
|
"Delinquent Stake:",
|
|
|
|
&format!(
|
|
|
|
"{} ({:0.2}%)",
|
2019-10-25 09:20:08 -07:00
|
|
|
&build_balance_message(total_deliquent_stake as u64, use_lamports_unit, true),
|
2019-10-23 21:40:35 -07:00
|
|
|
100. * total_deliquent_stake / total_active_stake
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
println!();
|
|
|
|
|
2019-10-06 21:47:24 -07:00
|
|
|
println!(
|
|
|
|
"{}",
|
|
|
|
style(format!(
|
2019-12-11 22:04:54 -08:00
|
|
|
" {:<44} {:<44} {} {} {} {:>7} {}",
|
2019-10-06 21:47:24 -07:00
|
|
|
"Identity Pubkey",
|
2019-10-14 23:00:13 -07:00
|
|
|
"Vote Account Pubkey",
|
2019-10-06 21:47:24 -07:00
|
|
|
"Commission",
|
|
|
|
"Last Vote",
|
|
|
|
"Root Block",
|
2020-02-03 10:37:50 -08:00
|
|
|
"Credits",
|
2019-10-06 21:47:24 -07:00
|
|
|
"Active Stake",
|
|
|
|
))
|
|
|
|
.bold()
|
|
|
|
);
|
|
|
|
|
2019-10-23 21:40:35 -07:00
|
|
|
fn print_vote_account(
|
2019-12-11 22:04:54 -08:00
|
|
|
vote_account: RpcVoteAccountInfo,
|
2020-02-03 10:37:50 -08:00
|
|
|
current_epoch: Epoch,
|
2019-10-23 21:40:35 -07:00
|
|
|
total_active_stake: f64,
|
|
|
|
use_lamports_unit: bool,
|
|
|
|
delinquent: bool,
|
|
|
|
) {
|
2019-10-06 21:47:24 -07:00
|
|
|
fn non_zero_or_dash(v: u64) -> String {
|
|
|
|
if v == 0 {
|
|
|
|
"-".into()
|
|
|
|
} else {
|
|
|
|
format!("{}", v)
|
|
|
|
}
|
|
|
|
}
|
2019-12-11 22:04:54 -08:00
|
|
|
|
2019-10-06 21:47:24 -07:00
|
|
|
println!(
|
2019-12-11 22:04:54 -08:00
|
|
|
"{} {:<44} {:<44} {:>9}% {:>8} {:>10} {:>7} {}",
|
2019-10-23 21:40:35 -07:00
|
|
|
if delinquent {
|
|
|
|
WARNING.to_string()
|
|
|
|
} else {
|
|
|
|
" ".to_string()
|
|
|
|
},
|
2019-10-06 21:47:24 -07:00
|
|
|
vote_account.node_pubkey,
|
|
|
|
vote_account.vote_pubkey,
|
|
|
|
vote_account.commission,
|
|
|
|
non_zero_or_dash(vote_account.last_vote),
|
|
|
|
non_zero_or_dash(vote_account.root_slot),
|
2020-02-03 10:37:50 -08:00
|
|
|
vote_account
|
|
|
|
.epoch_credits
|
|
|
|
.iter()
|
|
|
|
.find_map(|(epoch, credits, _)| if *epoch == current_epoch {
|
|
|
|
Some(*credits)
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
})
|
|
|
|
.unwrap_or(0),
|
2019-10-06 21:47:24 -07:00
|
|
|
if vote_account.activated_stake > 0 {
|
|
|
|
format!(
|
|
|
|
"{} ({:.2}%)",
|
2019-10-25 09:20:08 -07:00
|
|
|
build_balance_message(vote_account.activated_stake, use_lamports_unit, true),
|
2019-10-23 21:40:35 -07:00
|
|
|
100. * vote_account.activated_stake as f64 / total_active_stake
|
2019-10-06 21:47:24 -07:00
|
|
|
)
|
|
|
|
} else {
|
|
|
|
"-".into()
|
|
|
|
},
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2019-12-11 22:04:54 -08:00
|
|
|
for vote_account in vote_accounts.current.into_iter() {
|
|
|
|
print_vote_account(
|
|
|
|
vote_account,
|
2020-02-03 10:37:50 -08:00
|
|
|
epoch_info.epoch,
|
2019-12-11 22:04:54 -08:00
|
|
|
total_active_stake,
|
|
|
|
use_lamports_unit,
|
|
|
|
false,
|
|
|
|
);
|
2019-10-23 21:40:35 -07:00
|
|
|
}
|
2019-12-11 22:04:54 -08:00
|
|
|
for vote_account in vote_accounts.delinquent.into_iter() {
|
|
|
|
print_vote_account(
|
|
|
|
vote_account,
|
2020-02-03 10:37:50 -08:00
|
|
|
epoch_info.epoch,
|
2019-12-11 22:04:54 -08:00
|
|
|
total_active_stake,
|
|
|
|
use_lamports_unit,
|
|
|
|
true,
|
|
|
|
);
|
2019-10-23 21:40:35 -07:00
|
|
|
}
|
|
|
|
|
2019-10-06 21:47:24 -07:00
|
|
|
Ok("".to_string())
|
|
|
|
}
|
|
|
|
|
2019-10-04 19:54:09 -07:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
use crate::cli::{app, parse_command};
|
2020-02-24 16:03:30 -08:00
|
|
|
use solana_sdk::signature::{write_keypair, Keypair};
|
|
|
|
use tempfile::NamedTempFile;
|
|
|
|
|
|
|
|
fn make_tmp_file() -> (String, NamedTempFile) {
|
|
|
|
let tmp_file = NamedTempFile::new().unwrap();
|
|
|
|
(String::from(tmp_file.path().to_str().unwrap()), tmp_file)
|
|
|
|
}
|
2019-10-04 19:54:09 -07:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_parse_command() {
|
|
|
|
let test_commands = app("test", "desc", "version");
|
2020-02-24 16:03:30 -08:00
|
|
|
let default_keypair = Keypair::new();
|
|
|
|
let (default_keypair_file, mut tmp_file) = make_tmp_file();
|
|
|
|
write_keypair(&default_keypair, tmp_file.as_file_mut()).unwrap();
|
2019-10-04 19:54:09 -07:00
|
|
|
|
|
|
|
let test_cluster_version = test_commands
|
|
|
|
.clone()
|
|
|
|
.get_matches_from(vec!["test", "cluster-version"]);
|
|
|
|
assert_eq!(
|
2020-02-24 16:03:30 -08:00
|
|
|
parse_command(&test_cluster_version, &default_keypair_file, None).unwrap(),
|
2019-10-21 16:08:09 -07:00
|
|
|
CliCommandInfo {
|
|
|
|
command: CliCommand::ClusterVersion,
|
2020-02-24 16:03:30 -08:00
|
|
|
signers: vec![],
|
2019-10-21 16:08:09 -07:00
|
|
|
}
|
2019-10-04 19:54:09 -07:00
|
|
|
);
|
|
|
|
|
|
|
|
let test_fees = test_commands.clone().get_matches_from(vec!["test", "fees"]);
|
|
|
|
assert_eq!(
|
2020-02-24 16:03:30 -08:00
|
|
|
parse_command(&test_fees, &default_keypair_file, None).unwrap(),
|
2019-10-21 16:08:09 -07:00
|
|
|
CliCommandInfo {
|
|
|
|
command: CliCommand::Fees,
|
2020-02-24 16:03:30 -08:00
|
|
|
signers: vec![],
|
2019-10-21 16:08:09 -07:00
|
|
|
}
|
2019-10-04 19:54:09 -07:00
|
|
|
);
|
|
|
|
|
2019-11-25 23:40:36 -08:00
|
|
|
let slot = 100;
|
2020-01-20 22:06:47 -08:00
|
|
|
let test_get_block_time =
|
|
|
|
test_commands
|
|
|
|
.clone()
|
|
|
|
.get_matches_from(vec!["test", "block-time", &slot.to_string()]);
|
2019-11-25 23:40:36 -08:00
|
|
|
assert_eq!(
|
2020-02-24 16:03:30 -08:00
|
|
|
parse_command(&test_get_block_time, &default_keypair_file, None).unwrap(),
|
2019-11-25 23:40:36 -08:00
|
|
|
CliCommandInfo {
|
|
|
|
command: CliCommand::GetBlockTime { slot },
|
2020-02-24 16:03:30 -08:00
|
|
|
signers: vec![],
|
2019-11-25 23:40:36 -08:00
|
|
|
}
|
|
|
|
);
|
|
|
|
|
2019-10-04 19:54:09 -07:00
|
|
|
let test_get_epoch_info = test_commands
|
|
|
|
.clone()
|
2020-01-20 22:06:47 -08:00
|
|
|
.get_matches_from(vec!["test", "epoch-info"]);
|
2019-10-04 19:54:09 -07:00
|
|
|
assert_eq!(
|
2020-02-24 16:03:30 -08:00
|
|
|
parse_command(&test_get_epoch_info, &default_keypair_file, None).unwrap(),
|
2019-10-21 16:08:09 -07:00
|
|
|
CliCommandInfo {
|
2019-11-24 16:34:18 -08:00
|
|
|
command: CliCommand::GetEpochInfo {
|
|
|
|
commitment_config: CommitmentConfig::recent(),
|
|
|
|
},
|
2020-02-24 16:03:30 -08:00
|
|
|
signers: vec![],
|
2019-10-21 16:08:09 -07:00
|
|
|
}
|
2019-10-04 19:54:09 -07:00
|
|
|
);
|
|
|
|
|
2019-11-08 20:56:57 -08:00
|
|
|
let test_get_genesis_hash = test_commands
|
2019-10-04 19:54:09 -07:00
|
|
|
.clone()
|
2020-01-20 22:06:47 -08:00
|
|
|
.get_matches_from(vec!["test", "genesis-hash"]);
|
2019-10-04 19:54:09 -07:00
|
|
|
assert_eq!(
|
2020-02-24 16:03:30 -08:00
|
|
|
parse_command(&test_get_genesis_hash, &default_keypair_file, None).unwrap(),
|
2019-10-21 16:08:09 -07:00
|
|
|
CliCommandInfo {
|
2019-11-08 20:56:57 -08:00
|
|
|
command: CliCommand::GetGenesisHash,
|
2020-02-24 16:03:30 -08:00
|
|
|
signers: vec![],
|
2019-10-21 16:08:09 -07:00
|
|
|
}
|
2019-10-04 19:54:09 -07:00
|
|
|
);
|
|
|
|
|
2020-01-20 22:06:47 -08:00
|
|
|
let test_get_slot = test_commands.clone().get_matches_from(vec!["test", "slot"]);
|
2019-10-04 19:54:09 -07:00
|
|
|
assert_eq!(
|
2020-02-24 16:03:30 -08:00
|
|
|
parse_command(&test_get_slot, &default_keypair_file, None).unwrap(),
|
2019-10-21 16:08:09 -07:00
|
|
|
CliCommandInfo {
|
2019-11-24 16:34:18 -08:00
|
|
|
command: CliCommand::GetSlot {
|
|
|
|
commitment_config: CommitmentConfig::recent(),
|
|
|
|
},
|
2020-02-24 16:03:30 -08:00
|
|
|
signers: vec![],
|
2019-10-21 16:08:09 -07:00
|
|
|
}
|
2019-10-04 19:54:09 -07:00
|
|
|
);
|
|
|
|
|
2020-03-09 01:28:44 -07:00
|
|
|
let test_total_supply = test_commands
|
|
|
|
.clone()
|
|
|
|
.get_matches_from(vec!["test", "total-supply"]);
|
|
|
|
assert_eq!(
|
|
|
|
parse_command(&test_total_supply, &default_keypair_file, None).unwrap(),
|
|
|
|
CliCommandInfo {
|
|
|
|
command: CliCommand::TotalSupply {
|
|
|
|
commitment_config: CommitmentConfig::recent(),
|
|
|
|
},
|
|
|
|
signers: vec![],
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
2019-10-04 19:54:09 -07:00
|
|
|
let test_transaction_count = test_commands
|
|
|
|
.clone()
|
2020-01-20 22:06:47 -08:00
|
|
|
.get_matches_from(vec!["test", "transaction-count"]);
|
2019-10-04 19:54:09 -07:00
|
|
|
assert_eq!(
|
2020-02-24 16:03:30 -08:00
|
|
|
parse_command(&test_transaction_count, &default_keypair_file, None).unwrap(),
|
2019-10-21 16:08:09 -07:00
|
|
|
CliCommandInfo {
|
2019-11-24 16:34:18 -08:00
|
|
|
command: CliCommand::GetTransactionCount {
|
|
|
|
commitment_config: CommitmentConfig::recent(),
|
|
|
|
},
|
2020-02-24 16:03:30 -08:00
|
|
|
signers: vec![],
|
2019-10-21 16:08:09 -07:00
|
|
|
}
|
2019-10-04 19:54:09 -07:00
|
|
|
);
|
|
|
|
|
2019-11-06 17:54:17 -08:00
|
|
|
let test_ping = test_commands.clone().get_matches_from(vec![
|
|
|
|
"test",
|
|
|
|
"ping",
|
|
|
|
"-i",
|
|
|
|
"1",
|
|
|
|
"-c",
|
|
|
|
"2",
|
|
|
|
"-t",
|
|
|
|
"3",
|
|
|
|
"--confirmed",
|
|
|
|
]);
|
2019-10-04 19:54:09 -07:00
|
|
|
assert_eq!(
|
2020-02-24 16:03:30 -08:00
|
|
|
parse_command(&test_ping, &default_keypair_file, None).unwrap(),
|
2019-10-21 16:08:09 -07:00
|
|
|
CliCommandInfo {
|
|
|
|
command: CliCommand::Ping {
|
2019-11-18 21:50:09 -08:00
|
|
|
lamports: 1,
|
2019-10-21 16:08:09 -07:00
|
|
|
interval: Duration::from_secs(1),
|
|
|
|
count: Some(2),
|
|
|
|
timeout: Duration::from_secs(3),
|
2019-11-06 17:54:17 -08:00
|
|
|
commitment_config: CommitmentConfig::default(),
|
2019-10-21 16:08:09 -07:00
|
|
|
},
|
2020-02-24 16:03:30 -08:00
|
|
|
signers: vec![default_keypair.into()],
|
2019-10-04 19:54:09 -07:00
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|