* validator/ cleanup (cherry picked from commit1a03afccb1
) # Conflicts: # core/src/validator.rs * Move TestValidator into its own module (cherry picked from commit208dd1de3a
) # Conflicts: # core/src/validator.rs # tokens/tests/commands.rs * Rebase Co-authored-by: Michael Vines <mvines@gmail.com>
This commit is contained in:
@ -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,
|
||||||
|
@ -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,
|
||||||
|
@ -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,
|
||||||
|
@ -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};
|
||||||
|
@ -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,
|
||||||
|
@ -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,
|
||||||
|
@ -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,
|
||||||
|
@ -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
106
core/src/test_validator.rs
Normal 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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -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,
|
||||||
|
@ -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,
|
||||||
|
@ -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,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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"]
|
||||||
|
@ -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!());
|
|
||||||
info!("Starting validator with: {:#?}", std::env::args_os());
|
|
||||||
|
|
||||||
solana_metrics::set_host_id(identity_keypair.pubkey().to_string());
|
|
||||||
solana_metrics::set_panic_hook("validator");
|
|
||||||
|
|
||||||
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);
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
let gossip_host = if let Some(entrypoint_addr) = entrypoint_addr {
|
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| {
|
solana_net_utils::get_public_ip_addr(&entrypoint_addr).unwrap_or_else(|err| {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"Failed to contact cluster entrypoint {}: {}",
|
"Failed to contact cluster entrypoint {}: {}",
|
||||||
entrypoint_addr, err
|
entrypoint_addr, err
|
||||||
);
|
);
|
||||||
exit(1);
|
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 {
|
||||||
|
Reference in New Issue
Block a user