validator/ cleanup (bp #12340) (#12352)

* validator/ cleanup

(cherry picked from commit 1a03afccb1)

# Conflicts:
#	core/src/validator.rs

* Move TestValidator into its own module

(cherry picked from commit 208dd1de3a)

# Conflicts:
#	core/src/validator.rs
#	tokens/tests/commands.rs

* Rebase

Co-authored-by: Michael Vines <mvines@gmail.com>
This commit is contained in:
mergify[bot]
2020-09-19 22:45:09 +00:00
committed by GitHub
parent 54b87b34c3
commit a26e1f62bb
16 changed files with 454 additions and 363 deletions

View File

@ -1,7 +1,7 @@
use serde_json::Value; use serde_json::Value;
use solana_cli::cli::{process_command, CliCommand, CliConfig}; use solana_cli::cli::{process_command, CliCommand, CliConfig};
use solana_client::rpc_client::RpcClient; use solana_client::rpc_client::RpcClient;
use solana_core::validator::TestValidator; use solana_core::test_validator::TestValidator;
use solana_faucet::faucet::run_local_faucet; use solana_faucet::faucet::run_local_faucet;
use solana_sdk::{ use solana_sdk::{
bpf_loader, bpf_loader,

View File

@ -11,7 +11,7 @@ use solana_cli::{
}; };
use solana_client::rpc_client::RpcClient; use solana_client::rpc_client::RpcClient;
use solana_core::contact_info::ContactInfo; use solana_core::contact_info::ContactInfo;
use solana_core::validator::{TestValidator, TestValidatorOptions}; use solana_core::test_validator::{TestValidator, TestValidatorOptions};
use solana_faucet::faucet::run_local_faucet; use solana_faucet::faucet::run_local_faucet;
use solana_sdk::{ use solana_sdk::{
commitment_config::CommitmentConfig, commitment_config::CommitmentConfig,

View File

@ -12,7 +12,7 @@ use solana_cli::{
test_utils::check_recent_balance, test_utils::check_recent_balance,
}; };
use solana_client::rpc_client::RpcClient; use solana_client::rpc_client::RpcClient;
use solana_core::validator::TestValidator; use solana_core::test_validator::TestValidator;
use solana_faucet::faucet::run_local_faucet; use solana_faucet::faucet::run_local_faucet;
use solana_sdk::{ use solana_sdk::{
commitment_config::CommitmentConfig, commitment_config::CommitmentConfig,

View File

@ -1,6 +1,6 @@
use solana_cli::cli::{process_command, CliCommand, CliConfig}; use solana_cli::cli::{process_command, CliCommand, CliConfig};
use solana_client::rpc_client::RpcClient; use solana_client::rpc_client::RpcClient;
use solana_core::validator::TestValidator; use solana_core::test_validator::TestValidator;
use solana_faucet::faucet::run_local_faucet; use solana_faucet::faucet::run_local_faucet;
use solana_sdk::{commitment_config::CommitmentConfig, signature::Keypair}; use solana_sdk::{commitment_config::CommitmentConfig, signature::Keypair};
use std::{fs::remove_dir_all, sync::mpsc::channel}; use std::{fs::remove_dir_all, sync::mpsc::channel};

View File

@ -10,7 +10,7 @@ use solana_cli::{
test_utils::{check_ready, check_recent_balance}, test_utils::{check_ready, check_recent_balance},
}; };
use solana_client::rpc_client::RpcClient; use solana_client::rpc_client::RpcClient;
use solana_core::validator::{TestValidator, TestValidatorOptions}; use solana_core::test_validator::{TestValidator, TestValidatorOptions};
use solana_faucet::faucet::run_local_faucet; use solana_faucet::faucet::run_local_faucet;
use solana_sdk::{ use solana_sdk::{
account_utils::StateMut, account_utils::StateMut,

View File

@ -10,7 +10,7 @@ use solana_cli::{
test_utils::{check_ready, check_recent_balance}, test_utils::{check_ready, check_recent_balance},
}; };
use solana_client::rpc_client::RpcClient; use solana_client::rpc_client::RpcClient;
use solana_core::validator::{TestValidator, TestValidatorOptions}; use solana_core::test_validator::{TestValidator, TestValidatorOptions};
use solana_faucet::faucet::run_local_faucet; use solana_faucet::faucet::run_local_faucet;
use solana_sdk::{ use solana_sdk::{
commitment_config::CommitmentConfig, commitment_config::CommitmentConfig,

View File

@ -5,7 +5,7 @@ use solana_cli::{
test_utils::check_recent_balance, test_utils::check_recent_balance,
}; };
use solana_client::rpc_client::RpcClient; use solana_client::rpc_client::RpcClient;
use solana_core::validator::TestValidator; use solana_core::test_validator::TestValidator;
use solana_faucet::faucet::run_local_faucet; use solana_faucet::faucet::run_local_faucet;
use solana_sdk::{ use solana_sdk::{
account_utils::StateMut, account_utils::StateMut,

View File

@ -65,6 +65,7 @@ pub mod sigverify;
pub mod sigverify_shreds; pub mod sigverify_shreds;
pub mod sigverify_stage; pub mod sigverify_stage;
pub mod snapshot_packager_service; pub mod snapshot_packager_service;
pub mod test_validator;
pub mod tpu; pub mod tpu;
pub mod transaction_status_service; pub mod transaction_status_service;
pub mod tree_diff; pub mod tree_diff;

106
core/src/test_validator.rs Normal file
View File

@ -0,0 +1,106 @@
use crate::{
cluster_info::Node,
contact_info::ContactInfo,
gossip_service::discover_cluster,
validator::{Validator, ValidatorConfig},
};
use solana_ledger::create_new_tmp_ledger;
use solana_sdk::{
hash::Hash,
pubkey::Pubkey,
signature::{Keypair, Signer},
};
use std::{path::PathBuf, sync::Arc};
pub struct TestValidator {
pub server: Validator,
pub leader_data: ContactInfo,
pub alice: Keypair,
pub ledger_path: PathBuf,
pub genesis_hash: Hash,
pub vote_pubkey: Pubkey,
}
pub struct TestValidatorOptions {
pub fees: u64,
pub bootstrap_validator_lamports: u64,
pub mint_lamports: u64,
}
impl Default for TestValidatorOptions {
fn default() -> Self {
use solana_ledger::genesis_utils::BOOTSTRAP_VALIDATOR_LAMPORTS;
TestValidatorOptions {
fees: 0,
bootstrap_validator_lamports: BOOTSTRAP_VALIDATOR_LAMPORTS,
mint_lamports: 1_000_000,
}
}
}
impl TestValidator {
pub fn run() -> Self {
Self::run_with_options(TestValidatorOptions::default())
}
pub fn run_with_options(options: TestValidatorOptions) -> Self {
use solana_ledger::genesis_utils::{
create_genesis_config_with_leader_ex, GenesisConfigInfo,
};
use solana_sdk::fee_calculator::FeeRateGovernor;
let TestValidatorOptions {
fees,
bootstrap_validator_lamports,
mint_lamports,
} = options;
let node_keypair = Arc::new(Keypair::new());
let node = Node::new_localhost_with_pubkey(&node_keypair.pubkey());
let contact_info = node.info.clone();
let GenesisConfigInfo {
mut genesis_config,
mint_keypair,
voting_keypair,
} = create_genesis_config_with_leader_ex(
mint_lamports,
&contact_info.id,
&Keypair::new(),
&Pubkey::new_rand(),
42,
bootstrap_validator_lamports,
);
genesis_config
.native_instruction_processors
.push(solana_budget_program!());
genesis_config.rent.lamports_per_byte_year = 1;
genesis_config.rent.exemption_threshold = 1.0;
genesis_config.fee_rate_governor = FeeRateGovernor::new(fees, 0);
let (ledger_path, blockhash) = create_new_tmp_ledger!(&genesis_config);
let config = ValidatorConfig {
rpc_addrs: Some((node.info.rpc, node.info.rpc_pubsub, node.info.rpc_banks)),
..ValidatorConfig::default()
};
let vote_pubkey = voting_keypair.pubkey();
let node = Validator::new(
node,
&node_keypair,
&ledger_path,
&voting_keypair.pubkey(),
vec![Arc::new(voting_keypair)],
None,
&config,
);
discover_cluster(&contact_info.gossip, 1).expect("Node startup failed");
TestValidator {
server: node,
leader_data: contact_info,
alice: mint_keypair,
ledger_path,
genesis_hash: blockhash,
vote_pubkey,
}
}
}

View File

@ -6,7 +6,7 @@ use crate::{
cluster_info_vote_listener::VoteTracker, cluster_info_vote_listener::VoteTracker,
completed_data_sets_service::CompletedDataSetsService, completed_data_sets_service::CompletedDataSetsService,
contact_info::ContactInfo, contact_info::ContactInfo,
gossip_service::{discover_cluster, GossipService}, gossip_service::GossipService,
poh_recorder::{PohRecorder, GRACE_TICKS_FACTOR, MAX_GRACE_SLOTS}, poh_recorder::{PohRecorder, GRACE_TICKS_FACTOR, MAX_GRACE_SLOTS},
poh_service::PohService, poh_service::PohService,
rewards_recorder_service::{RewardsRecorderSender, RewardsRecorderService}, rewards_recorder_service::{RewardsRecorderSender, RewardsRecorderService},
@ -30,7 +30,6 @@ use solana_ledger::{
blockstore::{Blockstore, BlockstoreSignals, CompletedSlotsReceiver, PurgeType}, blockstore::{Blockstore, BlockstoreSignals, CompletedSlotsReceiver, PurgeType},
blockstore_db::BlockstoreRecoveryMode, blockstore_db::BlockstoreRecoveryMode,
blockstore_processor::{self, TransactionStatusSender}, blockstore_processor::{self, TransactionStatusSender},
create_new_tmp_ledger,
leader_schedule::FixedSchedule, leader_schedule::FixedSchedule,
leader_schedule_cache::LeaderScheduleCache, leader_schedule_cache::LeaderScheduleCache,
}; };
@ -65,7 +64,7 @@ use std::{
time::Duration, time::Duration,
}; };
pub const MAX_COMPLETED_DATA_SETS_IN_CHANNEL: usize = 100_000; const MAX_COMPLETED_DATA_SETS_IN_CHANNEL: usize = 100_000;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct ValidatorConfig { pub struct ValidatorConfig {
@ -94,6 +93,8 @@ pub struct ValidatorConfig {
pub accounts_hash_interval_slots: u64, pub accounts_hash_interval_slots: u64,
pub max_genesis_archive_unpacked_size: u64, pub max_genesis_archive_unpacked_size: u64,
pub wal_recovery_mode: Option<BlockstoreRecoveryMode>, pub wal_recovery_mode: Option<BlockstoreRecoveryMode>,
pub poh_verify: bool, // Perform PoH verification during blockstore processing at boo
pub cuda: bool,
} }
impl Default for ValidatorConfig { impl Default for ValidatorConfig {
@ -124,6 +125,8 @@ impl Default for ValidatorConfig {
accounts_hash_interval_slots: std::u64::MAX, accounts_hash_interval_slots: std::u64::MAX,
max_genesis_archive_unpacked_size: MAX_GENESIS_ARCHIVE_UNPACKED_SIZE, max_genesis_archive_unpacked_size: MAX_GENESIS_ARCHIVE_UNPACKED_SIZE,
wal_recovery_mode: None, wal_recovery_mode: None,
poh_verify: true,
cuda: false,
} }
} }
} }
@ -171,18 +174,16 @@ pub struct Validator {
} }
impl Validator { impl Validator {
#[allow(clippy::cognitive_complexity)]
pub fn new( pub fn new(
mut node: Node, mut node: Node,
keypair: &Arc<Keypair>, identity_keypair: &Arc<Keypair>,
ledger_path: &Path, ledger_path: &Path,
vote_account: &Pubkey, vote_account: &Pubkey,
mut authorized_voter_keypairs: Vec<Arc<Keypair>>, mut authorized_voter_keypairs: Vec<Arc<Keypair>>,
entrypoint_info_option: Option<&ContactInfo>, cluster_entrypoint: Option<&ContactInfo>,
poh_verify: bool,
config: &ValidatorConfig, config: &ValidatorConfig,
) -> Self { ) -> Self {
let id = keypair.pubkey(); let id = identity_keypair.pubkey();
assert_eq!(id, node.info.id); assert_eq!(id, node.info.id);
warn!("identity: {}", id); warn!("identity: {}", id);
@ -198,7 +199,7 @@ impl Validator {
} }
report_target_features(); report_target_features();
info!("entrypoint: {:?}", entrypoint_info_option); info!("entrypoint: {:?}", cluster_entrypoint);
if solana_perf::perf_libs::api().is_some() { if solana_perf::perf_libs::api().is_some() {
info!("Initializing sigverify, this could take a while..."); info!("Initializing sigverify, this could take a while...");
@ -208,6 +209,14 @@ impl Validator {
sigverify::init(); sigverify::init();
info!("Done."); info!("Done.");
if !ledger_path.is_dir() {
error!(
"ledger directory does not exist or is not accessible: {:?}",
ledger_path
);
process::exit(1);
}
if let Some(shred_version) = config.expected_shred_version { if let Some(shred_version) = config.expected_shred_version {
if let Some(wait_for_supermajority_slot) = config.wait_for_supermajority { if let Some(wait_for_supermajority_slot) = config.wait_for_supermajority {
backup_and_clear_blockstore( backup_and_clear_blockstore(
@ -247,7 +256,7 @@ impl Validator {
rewards_recorder_sender, rewards_recorder_sender,
rewards_recorder_service, rewards_recorder_service,
}, },
) = new_banks_from_ledger(config, ledger_path, poh_verify, &exit); ) = new_banks_from_ledger(config, ledger_path, config.poh_verify, &exit);
let leader_schedule_cache = Arc::new(leader_schedule_cache); let leader_schedule_cache = Arc::new(leader_schedule_cache);
let bank = bank_forks.working_bank(); let bank = bank_forks.working_bank();
@ -279,7 +288,10 @@ impl Validator {
} }
} }
let cluster_info = Arc::new(ClusterInfo::new(node.info.clone(), keypair.clone())); let cluster_info = Arc::new(ClusterInfo::new(
node.info.clone(),
identity_keypair.clone(),
));
let mut block_commitment_cache = BlockCommitmentCache::default(); let mut block_commitment_cache = BlockCommitmentCache::default();
block_commitment_cache.initialize_slots(bank.slot()); block_commitment_cache.initialize_slots(bank.slot());
let block_commitment_cache = Arc::new(RwLock::new(block_commitment_cache)); let block_commitment_cache = Arc::new(RwLock::new(block_commitment_cache));
@ -405,8 +417,8 @@ impl Validator {
// Insert the entrypoint info, should only be None if this node // Insert the entrypoint info, should only be None if this node
// is the bootstrap validator // is the bootstrap validator
if let Some(entrypoint_info) = entrypoint_info_option { if let Some(cluster_entrypoint) = cluster_entrypoint {
cluster_info.set_entrypoint(entrypoint_info.clone()); cluster_info.set_entrypoint(cluster_entrypoint.clone());
} }
let (snapshot_packager_service, snapshot_package_sender) = let (snapshot_packager_service, snapshot_package_sender) =
@ -833,100 +845,6 @@ fn wait_for_supermajority(
false false
} }
pub struct TestValidator {
pub server: Validator,
pub leader_data: ContactInfo,
pub alice: Keypair,
pub ledger_path: PathBuf,
pub genesis_hash: Hash,
pub vote_pubkey: Pubkey,
}
pub struct TestValidatorOptions {
pub fees: u64,
pub bootstrap_validator_lamports: u64,
pub mint_lamports: u64,
}
impl Default for TestValidatorOptions {
fn default() -> Self {
use solana_ledger::genesis_utils::BOOTSTRAP_VALIDATOR_LAMPORTS;
TestValidatorOptions {
fees: 0,
bootstrap_validator_lamports: BOOTSTRAP_VALIDATOR_LAMPORTS,
mint_lamports: 1_000_000,
}
}
}
impl TestValidator {
pub fn run() -> Self {
Self::run_with_options(TestValidatorOptions::default())
}
pub fn run_with_options(options: TestValidatorOptions) -> Self {
use solana_ledger::genesis_utils::{
create_genesis_config_with_leader_ex, GenesisConfigInfo,
};
use solana_sdk::fee_calculator::FeeRateGovernor;
let TestValidatorOptions {
fees,
bootstrap_validator_lamports,
mint_lamports,
} = options;
let node_keypair = Arc::new(Keypair::new());
let node = Node::new_localhost_with_pubkey(&node_keypair.pubkey());
let contact_info = node.info.clone();
let GenesisConfigInfo {
mut genesis_config,
mint_keypair,
voting_keypair,
} = create_genesis_config_with_leader_ex(
mint_lamports,
&contact_info.id,
&Keypair::new(),
&Pubkey::new_rand(),
42,
bootstrap_validator_lamports,
);
genesis_config
.native_instruction_processors
.push(solana_budget_program!());
genesis_config.rent.lamports_per_byte_year = 1;
genesis_config.rent.exemption_threshold = 1.0;
genesis_config.fee_rate_governor = FeeRateGovernor::new(fees, 0);
let (ledger_path, blockhash) = create_new_tmp_ledger!(&genesis_config);
let config = ValidatorConfig {
rpc_addrs: Some((node.info.rpc, node.info.rpc_pubsub, node.info.rpc_banks)),
..ValidatorConfig::default()
};
let vote_pubkey = voting_keypair.pubkey();
let node = Validator::new(
node,
&node_keypair,
&ledger_path,
&voting_keypair.pubkey(),
vec![Arc::new(voting_keypair)],
None,
true,
&config,
);
discover_cluster(&contact_info.gossip, 1).expect("Node startup failed");
TestValidator {
server: node,
leader_data: contact_info,
alice: mint_keypair,
ledger_path,
genesis_hash: blockhash,
vote_pubkey,
}
}
}
fn report_target_features() { fn report_target_features() {
warn!( warn!(
"CUDA is {}abled", "CUDA is {}abled",
@ -1049,7 +967,7 @@ fn cleanup_accounts_path(account_path: &std::path::Path) {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use solana_ledger::genesis_utils::create_genesis_config_with_leader; use solana_ledger::{create_new_tmp_ledger, genesis_utils::create_genesis_config_with_leader};
use std::fs::remove_dir_all; use std::fs::remove_dir_all;
#[test] #[test]
@ -1081,7 +999,6 @@ mod tests {
&voting_keypair.pubkey(), &voting_keypair.pubkey(),
vec![voting_keypair.clone()], vec![voting_keypair.clone()],
Some(&leader_node.info), Some(&leader_node.info),
true,
&config, &config,
); );
validator.close().unwrap(); validator.close().unwrap();
@ -1156,7 +1073,6 @@ mod tests {
&vote_account_keypair.pubkey(), &vote_account_keypair.pubkey(),
vec![Arc::new(vote_account_keypair)], vec![Arc::new(vote_account_keypair)],
Some(&leader_node.info), Some(&leader_node.info),
true,
&config, &config,
) )
}) })

View File

@ -1,7 +1,7 @@
use solana_client::{pubsub_client::PubsubClient, rpc_client::RpcClient, rpc_response::SlotInfo}; use solana_client::{pubsub_client::PubsubClient, rpc_client::RpcClient, rpc_response::SlotInfo};
use solana_core::{ use solana_core::{
rpc_pubsub_service::PubSubService, rpc_subscriptions::RpcSubscriptions, rpc_pubsub_service::PubSubService, rpc_subscriptions::RpcSubscriptions,
validator::TestValidator, test_validator::TestValidator,
}; };
use solana_runtime::{ use solana_runtime::{
bank::Bank, bank::Bank,

View File

@ -13,7 +13,7 @@ use solana_client::{
rpc_response::{Response, RpcSignatureResult}, rpc_response::{Response, RpcSignatureResult},
}; };
use solana_core::contact_info::ContactInfo; use solana_core::contact_info::ContactInfo;
use solana_core::{rpc_pubsub::gen_client::Client as PubsubClient, validator::TestValidator}; use solana_core::{rpc_pubsub::gen_client::Client as PubsubClient, test_validator::TestValidator};
use solana_sdk::{ use solana_sdk::{
commitment_config::CommitmentConfig, hash::Hash, pubkey::Pubkey, signature::Signer, commitment_config::CommitmentConfig, hash::Hash, pubkey::Pubkey, signature::Signer,
system_transaction, transaction::Transaction, system_transaction, transaction::Transaction,

View File

@ -217,7 +217,6 @@ impl LocalCluster {
&leader_vote_keypair.pubkey(), &leader_vote_keypair.pubkey(),
vec![leader_vote_keypair.clone()], vec![leader_vote_keypair.clone()],
None, None,
true,
&leader_config, &leader_config,
); );
@ -365,7 +364,6 @@ impl LocalCluster {
&voting_keypair.pubkey(), &voting_keypair.pubkey(),
vec![voting_keypair.clone()], vec![voting_keypair.clone()],
Some(&self.entry_point_info), Some(&self.entry_point_info),
true,
&config, &config,
); );
@ -648,7 +646,6 @@ impl Cluster for LocalCluster {
&validator_info.voting_keypair.pubkey(), &validator_info.voting_keypair.pubkey(),
vec![validator_info.voting_keypair.clone()], vec![validator_info.voting_keypair.clone()],
entry_point_info, entry_point_info,
true,
&cluster_validator_info.config, &cluster_validator_info.config,
); );

View File

@ -1,5 +1,5 @@
use solana_client::rpc_client::RpcClient; use solana_client::rpc_client::RpcClient;
use solana_core::validator::{TestValidator, TestValidatorOptions}; use solana_core::test_validator::{TestValidator, TestValidatorOptions};
use solana_sdk::native_token::sol_to_lamports; use solana_sdk::native_token::sol_to_lamports;
use solana_tokens::commands::test_process_distribute_tokens_with_client; use solana_tokens::commands::test_process_distribute_tokens_with_client;
use std::fs::remove_dir_all; use std::fs::remove_dir_all;

View File

@ -35,5 +35,12 @@ solana-vote-signer = { path = "../vote-signer", version = "1.3.12" }
libc = "0.2.72" libc = "0.2.72"
signal-hook = "0.1.15" signal-hook = "0.1.15"
#[[bin]]
#name = "solana-validator"
#path = "src/main.rs"
#
#[lib]
#name = "solana_validator"
[package.metadata.docs.rs] [package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"] targets = ["x86_64-unknown-linux-gnu"]

View File

@ -42,7 +42,7 @@ use std::{
env, env,
fs::{self, File}, fs::{self, File},
net::{SocketAddr, TcpListener, UdpSocket}, net::{SocketAddr, TcpListener, UdpSocket},
path::PathBuf, path::{Path, PathBuf},
process::exit, process::exit,
str::FromStr, str::FromStr,
sync::{ sync::{
@ -514,7 +514,270 @@ fn start_logger(logfile: Option<String>) -> Option<JoinHandle<()>> {
logger_thread logger_thread
} }
#[allow(clippy::cognitive_complexity)] fn verify_reachable_ports(node: &Node, cluster_entrypoint: &ContactInfo) {
let mut udp_sockets = vec![&node.sockets.gossip, &node.sockets.repair];
if ContactInfo::is_valid_address(&node.info.serve_repair) {
udp_sockets.push(&node.sockets.serve_repair);
}
if ContactInfo::is_valid_address(&node.info.tpu) {
udp_sockets.extend(node.sockets.tpu.iter());
}
if ContactInfo::is_valid_address(&node.info.tpu_forwards) {
udp_sockets.extend(node.sockets.tpu_forwards.iter());
}
if ContactInfo::is_valid_address(&node.info.tvu) {
udp_sockets.extend(node.sockets.tvu.iter());
udp_sockets.extend(node.sockets.broadcast.iter());
udp_sockets.extend(node.sockets.retransmit_sockets.iter());
}
if ContactInfo::is_valid_address(&node.info.tvu_forwards) {
udp_sockets.extend(node.sockets.tvu_forwards.iter());
}
let mut tcp_listeners = vec![];
for (purpose, addr) in &[
("RPC", node.info.rpc),
("RPC pubsub", node.info.rpc_pubsub),
("RPC banks", node.info.rpc_banks),
] {
if ContactInfo::is_valid_address(&addr) {
tcp_listeners.push((
addr.port(),
TcpListener::bind(addr).unwrap_or_else(|err| {
error!(
"Unable to bind to tcp/{} for {}: {}",
addr.port(),
purpose,
err
);
exit(1);
}),
));
}
}
if let Some(ip_echo) = &node.sockets.ip_echo {
let ip_echo = ip_echo.try_clone().expect("unable to clone tcp_listener");
tcp_listeners.push((ip_echo.local_addr().unwrap().port(), ip_echo));
}
if !solana_net_utils::verify_reachable_ports(
&cluster_entrypoint.gossip,
tcp_listeners,
&udp_sockets,
) {
exit(1);
}
}
struct RpcBootstrapConfig {
no_genesis_fetch: bool,
no_snapshot_fetch: bool,
no_untrusted_rpc: bool,
max_genesis_archive_unpacked_size: u64,
no_check_vote_account: bool,
}
impl Default for RpcBootstrapConfig {
fn default() -> Self {
Self {
no_genesis_fetch: true,
no_snapshot_fetch: true,
no_untrusted_rpc: true,
max_genesis_archive_unpacked_size: MAX_GENESIS_ARCHIVE_UNPACKED_SIZE,
no_check_vote_account: true,
}
}
}
fn rpc_bootstrap(
node: &Node,
identity_keypair: &Arc<Keypair>,
ledger_path: &Path,
vote_account: &Pubkey,
authorized_voter_keypairs: &[Arc<Keypair>],
cluster_entrypoint: &ContactInfo,
validator_config: &mut ValidatorConfig,
bootstrap_config: RpcBootstrapConfig,
) {
verify_reachable_ports(&node, cluster_entrypoint);
if bootstrap_config.no_genesis_fetch && bootstrap_config.no_snapshot_fetch {
return;
}
let mut blacklisted_rpc_nodes = HashSet::new();
let mut gossip = None;
loop {
if gossip.is_none() {
gossip = Some(start_gossip_node(
&identity_keypair,
&cluster_entrypoint.gossip,
&node.info.gossip,
node.sockets.gossip.try_clone().unwrap(),
validator_config.expected_shred_version,
validator_config.gossip_validators.clone(),
));
}
let (rpc_contact_info, snapshot_hash) = get_rpc_node(
&gossip.as_ref().unwrap().0,
&cluster_entrypoint.gossip,
&validator_config,
&mut blacklisted_rpc_nodes,
bootstrap_config.no_snapshot_fetch,
bootstrap_config.no_untrusted_rpc,
);
info!(
"Using RPC service from node {}: {:?}",
rpc_contact_info.id, rpc_contact_info.rpc
);
let rpc_client = RpcClient::new_socket(rpc_contact_info.rpc);
let result = match rpc_client.get_version() {
Ok(rpc_version) => {
info!("RPC node version: {}", rpc_version.solana_core);
Ok(())
}
Err(err) => Err(format!("Failed to get RPC node version: {}", err)),
}
.and_then(|_| {
let genesis_hash = download_then_check_genesis_hash(
&rpc_contact_info.rpc,
&ledger_path,
validator_config.expected_genesis_hash,
bootstrap_config.max_genesis_archive_unpacked_size,
bootstrap_config.no_genesis_fetch,
);
if let Ok(genesis_hash) = genesis_hash {
if validator_config.expected_genesis_hash.is_none() {
info!("Expected genesis hash set to {}", genesis_hash);
validator_config.expected_genesis_hash = Some(genesis_hash);
}
}
if let Some(expected_genesis_hash) = validator_config.expected_genesis_hash {
// Sanity check that the RPC node is using the expected genesis hash before
// downloading a snapshot from it
let rpc_genesis_hash = rpc_client
.get_genesis_hash()
.map_err(|err| format!("Failed to get genesis hash: {}", err))?;
if expected_genesis_hash != rpc_genesis_hash {
return Err(format!(
"Genesis hash mismatch: expected {} but RPC node genesis hash is {}",
expected_genesis_hash, rpc_genesis_hash
));
}
}
if let Some(snapshot_hash) = snapshot_hash {
rpc_client
.get_slot_with_commitment(CommitmentConfig::root())
.map_err(|err| format!("Failed to get RPC node slot: {}", err))
.and_then(|slot| {
info!("RPC node root slot: {}", slot);
let (_cluster_info, gossip_exit_flag, gossip_service) =
gossip.take().unwrap();
gossip_exit_flag.store(true, Ordering::Relaxed);
let ret =
download_snapshot(&rpc_contact_info.rpc, &ledger_path, snapshot_hash);
gossip_service.join().unwrap();
ret
})
} else {
Ok(())
}
})
.map(|_| {
if !validator_config.voting_disabled && !bootstrap_config.no_check_vote_account {
check_vote_account(
&rpc_client,
&identity_keypair.pubkey(),
&vote_account,
&authorized_voter_keypairs
.iter()
.map(|k| k.pubkey())
.collect::<Vec<_>>(),
)
.unwrap_or_else(|err| {
// Consider failures here to be more likely due to user error (eg,
// incorrect `solana-validator` command-line arguments) rather than the
// RPC node failing.
//
// Power users can always use the `--no-check-vote-account` option to
// bypass this check entirely
error!("{}", err);
exit(1);
});
}
});
if result.is_ok() {
break;
}
warn!("{}", result.unwrap_err());
if let Some(ref trusted_validators) = validator_config.trusted_validators {
if trusted_validators.contains(&rpc_contact_info.id) {
continue; // Never blacklist a trusted node
}
}
info!(
"Excluding {} as a future RPC candidate",
rpc_contact_info.id
);
blacklisted_rpc_nodes.insert(rpc_contact_info.id);
}
if let Some((_cluster_info, gossip_exit_flag, gossip_service)) = gossip.take() {
gossip_exit_flag.store(true, Ordering::Relaxed);
gossip_service.join().unwrap();
}
}
fn create_validator(
node: Node,
identity_keypair: &Arc<Keypair>,
ledger_path: &Path,
vote_account: &Pubkey,
authorized_voter_keypairs: Vec<Arc<Keypair>>,
cluster_entrypoint: Option<ContactInfo>,
mut validator_config: ValidatorConfig,
rpc_bootstrap_config: RpcBootstrapConfig,
) -> Validator {
if validator_config.cuda {
solana_perf::perf_libs::init_cuda();
enable_recycler_warming();
}
solana_ledger::entry::init_poh();
if let Some(ref cluster_entrypoint) = cluster_entrypoint {
rpc_bootstrap(
&node,
&identity_keypair,
&ledger_path,
&vote_account,
&authorized_voter_keypairs,
cluster_entrypoint,
&mut validator_config,
rpc_bootstrap_config,
);
}
Validator::new(
node,
&identity_keypair,
&ledger_path,
&vote_account,
authorized_voter_keypairs,
cluster_entrypoint.as_ref(),
&validator_config,
)
}
pub fn main() { pub fn main() {
let default_dynamic_port_range = let default_dynamic_port_range =
&format!("{}-{}", VALIDATOR_PORT_RANGE.0, VALIDATOR_PORT_RANGE.1); &format!("{}-{}", VALIDATOR_PORT_RANGE.0, VALIDATOR_PORT_RANGE.1);
@ -964,11 +1227,19 @@ pub fn main() {
let ledger_path = PathBuf::from(matches.value_of("ledger_path").unwrap()); let ledger_path = PathBuf::from(matches.value_of("ledger_path").unwrap());
let init_complete_file = matches.value_of("init_complete_file"); let init_complete_file = matches.value_of("init_complete_file");
let skip_poh_verify = matches.is_present("skip_poh_verify");
let cuda = matches.is_present("cuda"); let rpc_bootstrap_config = RpcBootstrapConfig {
let no_genesis_fetch = matches.is_present("no_genesis_fetch"); no_genesis_fetch: matches.is_present("no_genesis_fetch"),
let no_snapshot_fetch = matches.is_present("no_snapshot_fetch"); no_snapshot_fetch: matches.is_present("no_snapshot_fetch"),
let no_check_vote_account = matches.is_present("no_check_vote_account"); no_check_vote_account: matches.is_present("no_check_vote_account"),
no_untrusted_rpc: matches.is_present("no_untrusted_rpc"),
max_genesis_archive_unpacked_size: value_t_or_exit!(
matches,
"max_genesis_archive_unpacked_size",
u64
),
};
let private_rpc = matches.is_present("private_rpc"); let private_rpc = matches.is_present("private_rpc");
let no_rocksdb_compaction = matches.is_present("no_rocksdb_compaction"); let no_rocksdb_compaction = matches.is_present("no_rocksdb_compaction");
let wal_recovery_mode = matches let wal_recovery_mode = matches
@ -982,7 +1253,6 @@ pub fn main() {
exit(1); exit(1);
}); });
let no_untrusted_rpc = matches.is_present("no_untrusted_rpc");
let trusted_validators = validators_set( let trusted_validators = validators_set(
&identity_keypair.pubkey(), &identity_keypair.pubkey(),
&matches, &matches,
@ -1015,6 +1285,7 @@ pub fn main() {
let mut validator_config = ValidatorConfig { let mut validator_config = ValidatorConfig {
dev_halt_at_slot: value_t!(matches, "dev_halt_at_slot", Slot).ok(), dev_halt_at_slot: value_t!(matches, "dev_halt_at_slot", Slot).ok(),
cuda: matches.is_present("cuda"),
expected_genesis_hash: matches expected_genesis_hash: matches
.value_of("expected_genesis_hash") .value_of("expected_genesis_hash")
.map(|s| Hash::from_str(&s).unwrap()), .map(|s| Hash::from_str(&s).unwrap()),
@ -1055,6 +1326,7 @@ pub fn main() {
frozen_accounts: values_t!(matches, "frozen_accounts", Pubkey).unwrap_or_default(), frozen_accounts: values_t!(matches, "frozen_accounts", Pubkey).unwrap_or_default(),
no_rocksdb_compaction, no_rocksdb_compaction,
wal_recovery_mode, wal_recovery_mode,
poh_verify: !matches.is_present("skip_poh_verify"),
..ValidatorConfig::default() ..ValidatorConfig::default()
}; };
@ -1174,6 +1446,13 @@ pub fn main() {
warn!("--vote-signer-address ignored"); warn!("--vote-signer-address ignored");
} }
let entrypoint_addr = matches.value_of("entrypoint").map(|entrypoint| {
solana_net_utils::parse_host_port(entrypoint).unwrap_or_else(|e| {
eprintln!("failed to parse entrypoint address: {}", e);
exit(1);
})
});
let logfile = { let logfile = {
let logfile = matches let logfile = matches
.value_of("logfile") .value_of("logfile")
@ -1194,40 +1473,14 @@ pub fn main() {
env::set_var("RUST_BACKTRACE", "1") env::set_var("RUST_BACKTRACE", "1")
} }
info!("{} {}", crate_name!(), solana_version::version!()); let gossip_host = if let Some(entrypoint_addr) = entrypoint_addr {
info!("Starting validator with: {:#?}", std::env::args_os()); solana_net_utils::get_public_ip_addr(&entrypoint_addr).unwrap_or_else(|err| {
eprintln!(
solana_metrics::set_host_id(identity_keypair.pubkey().to_string()); "Failed to contact cluster entrypoint {}: {}",
solana_metrics::set_panic_hook("validator"); entrypoint_addr, err
);
if cuda {
solana_perf::perf_libs::init_cuda();
enable_recycler_warming();
}
solana_ledger::entry::init_poh();
let entrypoint_addr = matches.value_of("entrypoint").map(|entrypoint| {
solana_net_utils::parse_host_port(entrypoint).unwrap_or_else(|e| {
eprintln!("failed to parse entrypoint address: {}", e);
exit(1); exit(1);
}) })
});
let gossip_host = if let Some(entrypoint_addr) = entrypoint_addr {
let ip_addr =
solana_net_utils::get_public_ip_addr(&entrypoint_addr).unwrap_or_else(|err| {
eprintln!(
"Failed to contact cluster entrypoint {}: {}",
entrypoint_addr, err
);
exit(1);
});
info!(
"{} reports the IP address for this machine as {}",
entrypoint_addr, ip_addr
);
ip_addr
} else { } else {
solana_net_utils::parse_host(matches.value_of("gossip_host").unwrap_or("127.0.0.1")) solana_net_utils::parse_host(matches.value_of("gossip_host").unwrap_or("127.0.0.1"))
.unwrap_or_else(|err| { .unwrap_or_else(|err| {
@ -1247,8 +1500,6 @@ pub fn main() {
) )
}), }),
); );
let max_genesis_archive_unpacked_size =
value_t_or_exit!(matches, "max_genesis_archive_unpacked_size", u64);
let cluster_entrypoint = entrypoint_addr let cluster_entrypoint = entrypoint_addr
.as_ref() .as_ref()
@ -1271,6 +1522,9 @@ pub fn main() {
node.info.tvu = any; node.info.tvu = any;
node.info.tvu_forwards = any; node.info.tvu_forwards = any;
node.info.serve_repair = any; node.info.serve_repair = any;
// A node in this configuration shouldn't be an entrypoint to other nodes
node.sockets.ip_echo = None;
} }
if !private_rpc { if !private_rpc {
@ -1281,211 +1535,21 @@ pub fn main() {
} }
} }
if let Some(ref cluster_entrypoint) = cluster_entrypoint { info!("{} {}", crate_name!(), solana_version::version!());
let mut udp_sockets = vec![&node.sockets.gossip, &node.sockets.repair]; info!("Starting validator with: {:#?}", std::env::args_os());
if ContactInfo::is_valid_address(&node.info.serve_repair) { solana_metrics::set_host_id(identity_keypair.pubkey().to_string());
udp_sockets.push(&node.sockets.serve_repair); solana_metrics::set_panic_hook("validator");
}
if ContactInfo::is_valid_address(&node.info.tpu) {
udp_sockets.extend(node.sockets.tpu.iter());
}
if ContactInfo::is_valid_address(&node.info.tpu_forwards) {
udp_sockets.extend(node.sockets.tpu_forwards.iter());
}
if ContactInfo::is_valid_address(&node.info.tvu) {
udp_sockets.extend(node.sockets.tvu.iter());
udp_sockets.extend(node.sockets.broadcast.iter());
udp_sockets.extend(node.sockets.retransmit_sockets.iter());
}
if ContactInfo::is_valid_address(&node.info.tvu_forwards) {
udp_sockets.extend(node.sockets.tvu_forwards.iter());
}
let mut tcp_listeners = vec![]; let validator = create_validator(
if let Some((rpc_addr, rpc_pubsub_addr, rpc_banks_addr)) = validator_config.rpc_addrs {
for (purpose, addr) in &[
("RPC", rpc_addr),
("RPC pubsub", rpc_pubsub_addr),
("RPC banks", rpc_banks_addr),
] {
if !private_rpc && ContactInfo::is_valid_address(&addr) {
tcp_listeners.push((
addr.port(),
TcpListener::bind(addr).unwrap_or_else(|err| {
error!(
"Unable to bind to tcp/{} for {}: {}",
addr.port(),
purpose,
err
);
exit(1);
}),
));
}
}
}
if !restricted_repair_only_mode {
if let Some(ip_echo) = &node.sockets.ip_echo {
let ip_echo = ip_echo.try_clone().expect("unable to clone tcp_listener");
tcp_listeners.push((ip_echo.local_addr().unwrap().port(), ip_echo));
}
}
if !solana_net_utils::verify_reachable_ports(
&cluster_entrypoint.gossip,
tcp_listeners,
&udp_sockets,
) {
exit(1);
}
if !no_genesis_fetch || !no_snapshot_fetch {
let mut blacklisted_rpc_nodes = HashSet::new();
let mut gossip = None;
loop {
if gossip.is_none() {
gossip = Some(start_gossip_node(
&identity_keypair,
&cluster_entrypoint.gossip,
&node.info.gossip,
node.sockets.gossip.try_clone().unwrap(),
validator_config.expected_shred_version,
validator_config.gossip_validators.clone(),
));
}
let (rpc_contact_info, snapshot_hash) = get_rpc_node(
&gossip.as_ref().unwrap().0,
&cluster_entrypoint.gossip,
&validator_config,
&mut blacklisted_rpc_nodes,
no_snapshot_fetch,
no_untrusted_rpc,
);
info!(
"Using RPC service from node {}: {:?}",
rpc_contact_info.id, rpc_contact_info.rpc
);
let rpc_client = RpcClient::new_socket(rpc_contact_info.rpc);
let result = match rpc_client.get_version() {
Ok(rpc_version) => {
info!("RPC node version: {}", rpc_version.solana_core);
Ok(())
}
Err(err) => Err(format!("Failed to get RPC node version: {}", err)),
}
.and_then(|_| {
let genesis_hash = download_then_check_genesis_hash(
&rpc_contact_info.rpc,
&ledger_path,
validator_config.expected_genesis_hash,
max_genesis_archive_unpacked_size,
no_genesis_fetch,
);
if let Ok(genesis_hash) = genesis_hash {
if validator_config.expected_genesis_hash.is_none() {
info!("Expected genesis hash set to {}", genesis_hash);
validator_config.expected_genesis_hash = Some(genesis_hash);
}
}
genesis_hash
})
.and_then(|_| {
if let Some(expected_genesis_hash) = validator_config.expected_genesis_hash {
// Sanity check that the RPC node is using the expected genesis hash before
// downloading a snapshot from it
let rpc_genesis_hash = rpc_client
.get_genesis_hash()
.map_err(|err| format!("Failed to get genesis hash: {}", err))?;
if expected_genesis_hash != rpc_genesis_hash {
return Err(format!("Genesis hash mismatch: expected {} but RPC node genesis hash is {}",
expected_genesis_hash, rpc_genesis_hash));
}
}
Ok(())
})
.and_then(|_| {
if let Some(snapshot_hash) = snapshot_hash {
rpc_client.get_slot_with_commitment(CommitmentConfig::root())
.map_err(|err| format!("Failed to get RPC node slot: {}", err))
.and_then(|slot| {
info!("RPC node root slot: {}", slot);
let (_cluster_info, gossip_exit_flag, gossip_service) = gossip.take().unwrap();
gossip_exit_flag.store(true, Ordering::Relaxed);
let ret = download_snapshot(&rpc_contact_info.rpc, &ledger_path, snapshot_hash);
gossip_service.join().unwrap();
ret
})
} else {
Ok(())
}
})
.map(|_| {
if !validator_config.voting_disabled && !no_check_vote_account {
check_vote_account(
&rpc_client,
&identity_keypair.pubkey(),
&vote_account,
&authorized_voter_keypairs.iter().map(|k| k.pubkey()).collect::<Vec<_>>(),
).unwrap_or_else(|err| {
// Consider failures here to be more likely due to user error (eg,
// incorrect `solana-validator` command-line arguments) rather than the
// RPC node failing.
//
// Power users can always use the `--no-check-vote-account` option to
// bypass this check entirely
error!("{}", err);
exit(1);
});
}
});
if result.is_ok() {
break;
}
warn!("{}", result.unwrap_err());
if let Some(ref trusted_validators) = validator_config.trusted_validators {
if trusted_validators.contains(&rpc_contact_info.id) {
continue; // Never blacklist a trusted node
}
}
info!(
"Excluding {} as a future RPC candidate",
rpc_contact_info.id
);
blacklisted_rpc_nodes.insert(rpc_contact_info.id);
}
if let Some((_cluster_info, gossip_exit_flag, gossip_service)) = gossip.take() {
gossip_exit_flag.store(true, Ordering::Relaxed);
gossip_service.join().unwrap();
}
}
}
if !ledger_path.is_dir() {
error!(
"ledger directory does not exist or is not accessible: {:?}",
ledger_path
);
exit(1);
}
let validator = Validator::new(
node, node,
&identity_keypair, &identity_keypair,
&ledger_path, &ledger_path,
&vote_account, &vote_account,
authorized_voter_keypairs, authorized_voter_keypairs,
cluster_entrypoint.as_ref(), cluster_entrypoint,
!skip_poh_verify, validator_config,
&validator_config, rpc_bootstrap_config,
); );
if let Some(filename) = init_complete_file { if let Some(filename) = init_complete_file {