1pub use solana_perf::report_target_features;
4use {
5 crate::{
6 admin_rpc_post_init::{AdminRpcRequestMetadataPostInit, KeyUpdaterType, KeyUpdaters},
7 banking_stage::{
8 BankingStage, transaction_scheduler::scheduler_controller::SchedulerConfig,
9 },
10 banking_trace::{self, BankingTracer, TraceError},
11 block_creation_loop::{BlockCreationLoop, BlockCreationLoopConfig, ReplayHighestFrozen},
12 cluster_info_vote_listener::VoteTracker,
13 completed_data_sets_service::CompletedDataSetsService,
14 consensus::{
15 ExternalRootSource, Tower, reconcile_blockstore_roots_with_external_source,
16 tower_storage::{NullTowerStorage, TowerStorage},
17 },
18 forwarding_stage::ForwardingClientConfig,
19 repair::{
20 self, repair_handler::RepairHandlerType, serve_repair_service::ServeRepairService,
21 },
22 resource_limits::{ResourceLimitError, adjust_nofile_limit},
23 sample_performance_service::SamplePerformanceService,
24 snapshot_packager_service::SnapshotPackagerService,
25 stats_reporter_service::StatsReporterService,
26 system_monitor_service::{
27 SystemMonitorService, SystemMonitorStatsReportConfig, XdpNetworkConfigReport,
28 verify_net_stats_access,
29 },
30 tpu::{Tpu, TpuSockets},
31 tvu::{AlpenglowInitializationState, Tvu, TvuConfig, TvuSockets},
32 },
33 agave_snapshots::{
34 SnapshotInterval, snapshot_archive_info::SnapshotArchiveInfoGetter as _,
35 snapshot_config::SnapshotConfig, snapshot_hash::StartingSnapshotHashes,
36 },
37 agave_votor::{
38 vote_history::{VoteHistory, VoteHistoryError},
39 vote_history_storage::{NullVoteHistoryStorage, VoteHistoryStorage},
40 voting_service::VotingServiceOverride,
41 },
42 agave_xdp::transmitter::{Transmitter, TransmitterBuilder},
43 anyhow::{Result, anyhow},
44 crossbeam_channel::{Receiver, bounded, unbounded},
45 serde::{Deserialize, Serialize},
46 solana_account::ReadableAccount,
47 solana_accounts_db::{
48 accounts_db::{ACCOUNTS_DB_CONFIG_FOR_TESTING, AccountsDbConfig},
49 accounts_update_notifier_interface::AccountsUpdateNotifier,
50 utils::validate_account_paths_for_direct_io,
51 },
52 solana_client::connection_cache::{ConnectionCache, Protocol},
53 solana_clock::Slot,
54 solana_entry::poh::compute_hash_time,
55 solana_epoch_schedule::MAX_LEADER_SCHEDULE_EPOCH_OFFSET,
56 solana_genesis_config::GenesisConfig,
57 solana_genesis_utils::{
58 MAX_GENESIS_ARCHIVE_UNPACKED_SIZE, OpenGenesisConfigError, open_genesis_config,
59 },
60 solana_geyser_plugin_manager::{
61 GeyserPluginManagerRequest,
62 contact_info_notifier::{
63 self as geyser_contact_info_notifier, ContactInfoNotifier as GeyserContactInfoNotifier,
64 },
65 geyser_plugin_service::GeyserPluginService,
66 },
67 solana_gossip::{
68 cluster_info::{
69 ClusterInfo, DEFAULT_CONTACT_DEBUG_INTERVAL_MILLIS,
70 DEFAULT_CONTACT_SAVE_INTERVAL_MILLIS,
71 },
72 contact_info::ContactInfo,
73 crds_gossip_pull::CRDS_GOSSIP_PULL_CRDS_TIMEOUT_MS,
74 gossip_service::GossipService,
75 node::{Node, NodeMultihoming},
76 },
77 solana_hard_forks::HardForks,
78 solana_hash::Hash,
79 solana_keypair::Keypair,
80 solana_leader_schedule::{FixedSchedule, SlotLeader},
81 solana_ledger::{
82 bank_forks_utils,
83 blockstore::{
84 Blockstore, BlockstoreError, MAX_COMPLETED_SLOTS_IN_CHANNEL,
85 MAX_REPLAY_WAKE_UP_SIGNALS, MAX_UPDATE_PARENT_SIGNALS, PurgeType, UpdateParentReceiver,
86 },
87 blockstore_metric_report_service::BlockstoreMetricReportService,
88 blockstore_options::{
89 BLOCKSTORE_DIRECTORY_ROCKS_LEVEL, BlockstoreCleanupStrategy, BlockstoreOptions,
90 },
91 blockstore_processor,
92 entry_notifier_interface::EntryNotifierArc,
93 entry_notifier_service::{EntryNotifierSender, EntryNotifierService},
94 leader_schedule_cache::LeaderScheduleCache,
95 shred::filter::TurbineMode,
96 use_snapshot_archives_at_startup::UseSnapshotArchivesAtStartup,
97 },
98 solana_measure::measure::Measure,
99 solana_metrics::{datapoint_info, metrics::metrics_config_sanity_check},
100 solana_net_utils::{PinnedXdpSender, SocketAddrSpace},
101 solana_poh::{
102 poh_controller::PohController,
103 poh_recorder::PohRecorder,
104 poh_service::{self, PohService},
105 record_channels::record_channels,
106 transaction_recorder::TransactionRecorder,
107 },
108 solana_pubkey::Pubkey,
109 solana_rpc::{
110 max_slots::MaxSlots,
111 optimistically_confirmed_bank_tracker::{
112 BankNotificationSenderConfig, OptimisticallyConfirmedBank,
113 OptimisticallyConfirmedBankTracker,
114 },
115 rpc::JsonRpcConfig,
116 rpc_completed_slots_service::RpcCompletedSlotsService,
117 rpc_pubsub_service::{PubSubConfig, PubSubService},
118 rpc_service::{JsonRpcService, JsonRpcServiceConfig, RpcTpuClientArgs},
119 rpc_subscriptions::RpcSubscriptions,
120 transaction_notifier_interface::TransactionNotifierArc,
121 transaction_status_service::TransactionStatusService,
122 },
123 solana_runtime::{
124 accounts_background_service::{
125 AbsRequestHandlers, AccountsBackgroundService, DroppedSlotsReceiver,
126 PendingSnapshotPackages, PrunedBanksRequestHandler, SnapshotRequestHandler,
127 },
128 bank::Bank,
129 bank_forks::BankForks,
130 bank_forks_controller::BankForksControllerHandle,
131 commitment::BlockCommitmentCache,
132 dependency_tracker::DependencyTracker,
133 prioritization_fee_cache::PrioritizationFeeCache,
134 runtime_config::RuntimeConfig,
135 snapshot_bank_utils,
136 snapshot_controller::SnapshotController,
137 snapshot_utils,
138 transaction_execution::TransactionStatusSender,
139 },
140 solana_send_transaction_service::send_transaction_service::Config as SendTransactionServiceConfig,
141 solana_shred_version::compute_shred_version,
142 solana_signer::Signer,
143 solana_streamer::{
144 nonblocking::{simple_qos::SimpleQosConfig, swqos::SwQosConfig},
145 quic::{QuicStreamerConfig, SimpleQosQuicStreamerConfig, SwQosQuicStreamerConfig},
146 streamer::StakedNodes,
147 },
148 solana_time_utils::timestamp,
149 solana_tpu_client::tpu_client::{DEFAULT_TPU_CONNECTION_POOL_SIZE, DEFAULT_VOTE_USE_QUIC},
150 solana_turbine::{self, broadcast_stage::BroadcastStageType},
151 solana_unified_scheduler_pool::DefaultSchedulerPool,
152 solana_validator_exit::Exit,
153 solana_vote_program::vote_state::{VoteStateV4, handler::VoteStateHandler},
154 std::{
155 borrow::Cow,
156 cmp,
157 collections::{HashMap, HashSet},
158 net::{Ipv4Addr, SocketAddr, SocketAddrV4},
159 num::{NonZeroU64, NonZeroUsize},
160 path::{Path, PathBuf},
161 str::FromStr,
162 sync::{
163 Arc, Mutex, RwLock,
164 atomic::{AtomicBool, AtomicU64, Ordering},
165 },
166 thread::{self, Builder, JoinHandle},
167 time::{Duration, Instant},
168 },
169 strum::VariantNames,
170 strum_macros::{Display, EnumCount, EnumIter, EnumString, IntoStaticStr},
171 thiserror::Error,
172 tokio::{runtime::Runtime as TokioRuntime, sync::mpsc},
173 tokio_util::sync::CancellationToken,
174};
175
176const MAX_COMPLETED_DATA_SETS_IN_CHANNEL: usize = 100_000;
177const WAIT_FOR_SUPERMAJORITY_THRESHOLD_PERCENT: u64 = 80;
178
179#[derive(Clone, EnumCount, EnumIter, EnumString, VariantNames, Default, IntoStaticStr, Display)]
180#[strum(serialize_all = "kebab-case")]
181pub enum BlockVerificationMethod {
182 #[default]
183 UnifiedScheduler,
184}
185
186impl BlockVerificationMethod {
187 pub const fn cli_names() -> &'static [&'static str] {
188 Self::VARIANTS
189 }
190
191 pub fn cli_message() -> &'static str {
192 "Switch transaction scheduling method for verifying ledger entries"
193 }
194}
195
196#[derive(
197 Clone,
198 Debug,
199 EnumCount,
200 EnumIter,
201 EnumString,
202 VariantNames,
203 Default,
204 IntoStaticStr,
205 Display,
206 Serialize,
207 Deserialize,
208 PartialEq,
209 Eq,
210)]
211#[strum(serialize_all = "kebab-case")]
212#[serde(rename_all = "kebab-case")]
213pub enum BlockProductionMethod {
214 CentralScheduler,
215 #[default]
216 CentralSchedulerGreedy,
217}
218
219impl BlockProductionMethod {
220 pub const fn cli_names() -> &'static [&'static str] {
221 Self::VARIANTS
222 }
223
224 pub fn cli_message() -> &'static str {
225 "Switch transaction scheduling method for producing ledger entries"
226 }
227
228 pub fn warn_if_deprecated_value(&self) {
229 if matches!(self, Self::CentralScheduler) {
230 warn!(
231 "`central-scheduler` is deprecated and will be removed in a future release; use \
232 `central-scheduler-greedy` instead"
233 );
234 }
235 }
236}
237
238#[derive(
239 Clone,
240 Debug,
241 EnumString,
242 VariantNames,
243 Default,
244 IntoStaticStr,
245 Display,
246 Serialize,
247 Deserialize,
248 PartialEq,
249 Eq,
250)]
251#[strum(serialize_all = "kebab-case")]
252#[serde(rename_all = "kebab-case")]
253pub enum TransactionStructure {
254 Sdk,
255 #[default]
256 View,
257}
258
259impl TransactionStructure {
260 pub const fn cli_names() -> &'static [&'static str] {
261 Self::VARIANTS
262 }
263
264 pub fn cli_message() -> &'static str {
265 "DEPRECATED: has no impact on banking stage; will be removed in a future version"
266 }
267}
268
269#[derive(
270 Clone, Debug, VariantNames, IntoStaticStr, Display, Serialize, Deserialize, PartialEq, Eq,
271)]
272#[strum(serialize_all = "kebab-case")]
273#[serde(rename_all = "kebab-case")]
274pub enum SchedulerPacing {
275 Disabled,
276 FillTimeMillis(NonZeroU64),
277}
278
279impl FromStr for SchedulerPacing {
280 type Err = String;
281
282 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
283 if s.eq_ignore_ascii_case("disabled") {
284 Ok(SchedulerPacing::Disabled)
285 } else {
286 match s.parse::<u64>() {
287 Ok(v) if v > 0 => Ok(SchedulerPacing::FillTimeMillis(
288 NonZeroU64::new(v).ok_or_else(|| "value must be non-zero".to_string())?,
289 )),
290 _ => Err("value must be a positive integer or 'disabled'".to_string()),
291 }
292 }
293 }
294}
295
296impl SchedulerPacing {
297 pub fn fill_time(&self) -> Option<Duration> {
298 match self {
299 SchedulerPacing::Disabled => None,
300 SchedulerPacing::FillTimeMillis(millis) => Some(Duration::from_millis(millis.get())),
301 }
302 }
303}
304
305#[derive(Clone, Debug)]
307pub struct GeneratorConfig {
308 pub accounts_path: String,
309 pub starting_keypairs: Arc<Vec<Keypair>>,
310}
311
312#[derive(Clone, Debug)]
313pub struct ValidatorLogConfig {
314 pub logfile: PathBuf,
316 pub logrotate_flag: Arc<AtomicBool>,
320}
321
322pub struct ValidatorConfig {
323 pub log_config: Option<ValidatorLogConfig>,
325 pub expected_genesis_hash: Option<Hash>,
326 pub expected_bank_hash: Option<Hash>,
327 pub expected_shred_version: Option<u16>,
328 pub voting_disabled: bool,
329 pub account_paths: Vec<PathBuf>,
330 pub account_snapshot_paths: Vec<PathBuf>,
331 pub rpc_config: JsonRpcConfig,
332 pub on_start_geyser_plugin_config_files: Option<Vec<PathBuf>>,
334 pub geyser_plugin_always_enabled: bool,
335 pub rpc_addrs: Option<(SocketAddr, SocketAddr)>, pub pubsub_config: PubSubConfig,
337 pub snapshot_config: SnapshotConfig,
338 pub blockstore_cleanup_strategy: BlockstoreCleanupStrategy,
339 pub blockstore_options: BlockstoreOptions,
340 pub broadcast_stage_type: BroadcastStageType,
341 pub turbine_mode: TurbineMode,
342 pub fixed_leader_schedule: Option<FixedSchedule>,
343 pub wait_for_supermajority: Option<Slot>,
344 pub new_hard_forks: Option<Vec<Slot>>,
345 pub known_validators: Option<HashSet<Pubkey>>, pub repair_validators: Option<HashSet<Pubkey>>, pub repair_whitelist: Arc<RwLock<HashSet<Pubkey>>>, pub gossip_validators: Option<HashSet<Pubkey>>, pub should_check_duplicate_instance: bool,
350 pub max_genesis_archive_unpacked_size: u64,
351 pub run_verification: bool,
354 pub require_tower: bool,
355 pub require_vote_history: bool,
356 pub tower_storage: Arc<dyn TowerStorage>,
357 pub vote_history_storage: Arc<dyn VoteHistoryStorage>,
358 pub debug_keys: Option<Arc<HashSet<Pubkey>>>,
359 pub filter_keys: Arc<HashSet<Pubkey>>,
360 pub contact_debug_interval: u64,
361 pub contact_save_interval: u64,
362 pub send_transaction_service_config: SendTransactionServiceConfig,
363 pub no_poh_speed_test: bool,
364 pub no_os_memory_stats_reporting: bool,
365 pub no_os_network_stats_reporting: bool,
366 pub xdp_network_config_report: Option<XdpNetworkConfigReport>,
367 pub no_os_cpu_stats_reporting: bool,
368 pub no_os_disk_stats_reporting: bool,
369 pub enforce_ulimit_nofile: bool,
370 pub poh_pinned_cpu_core: Option<usize>,
371 pub poh_hashes_per_batch: u64,
372 pub process_ledger_before_services: bool,
373 pub accounts_db_config: AccountsDbConfig,
374 pub warp_slot: Option<Slot>,
375 pub accounts_db_skip_shrink: bool,
376 pub accounts_db_force_initial_clean: bool,
377 pub staked_nodes_overrides: Arc<RwLock<HashMap<Pubkey, u64>>>,
378 pub validator_exit: Arc<RwLock<Exit>>,
379 pub validator_exit_backpressure: HashMap<String, Arc<AtomicBool>>,
380 pub no_wait_for_vote_to_start_leader: bool,
381 pub wait_to_vote_slot: Option<Slot>,
382 pub runtime_config: RuntimeConfig,
383 pub banking_trace_dir_byte_limit: banking_trace::DirByteLimit,
384 pub block_verification_method: BlockVerificationMethod,
385 pub block_production_method: BlockProductionMethod,
386 pub block_production_num_workers: NonZeroUsize,
387 pub block_production_scheduler_config: SchedulerConfig,
388 pub enable_block_production_forwarding: bool,
389 pub enable_scheduler_bindings: bool,
390 pub generator_config: Option<GeneratorConfig>,
391 pub use_snapshot_archives_at_startup: UseSnapshotArchivesAtStartup,
392 pub unified_scheduler_handler_threads: Option<usize>,
393 pub ip_echo_server_threads: NonZeroUsize,
394 pub rayon_global_threads: NonZeroUsize,
395 pub replay_forks_threads: NonZeroUsize,
396 pub replay_transactions_threads: NonZeroUsize,
397 pub tvu_shred_sigverify_threads: NonZeroUsize,
398 pub tvu_bls_sigverify_threads: NonZeroUsize,
399 pub delay_leader_block_for_pending_fork: bool,
400 pub voting_service_test_override: Option<VotingServiceOverride>,
401 pub repair_handler_type: RepairHandlerType,
402 pub snapshot_packager_niceness_adj: i8,
404}
405
406impl ValidatorConfig {
407 pub fn default_for_test() -> Self {
408 Self {
409 log_config: None,
410 expected_genesis_hash: None,
411 expected_bank_hash: None,
412 expected_shred_version: None,
413 voting_disabled: false,
414 blockstore_cleanup_strategy: BlockstoreCleanupStrategy::None,
415 blockstore_options: BlockstoreOptions::default_for_tests(),
416 account_paths: Vec::new(),
417 account_snapshot_paths: Vec::new(),
418 rpc_config: JsonRpcConfig::default_for_test(),
419 on_start_geyser_plugin_config_files: None,
420 geyser_plugin_always_enabled: false,
421 rpc_addrs: None,
422 pubsub_config: PubSubConfig::default_for_tests(),
423 snapshot_config: SnapshotConfig::new_load_only(),
424 broadcast_stage_type: BroadcastStageType::Standard,
425 turbine_mode: TurbineMode::default(),
426 fixed_leader_schedule: None,
427 wait_for_supermajority: None,
428 new_hard_forks: None,
429 known_validators: None,
430 repair_validators: None,
431 should_check_duplicate_instance: true,
432 repair_whitelist: Arc::new(RwLock::new(HashSet::default())),
433 gossip_validators: None,
434 max_genesis_archive_unpacked_size: MAX_GENESIS_ARCHIVE_UNPACKED_SIZE,
435 run_verification: true,
436 require_tower: false,
437 require_vote_history: false,
438 tower_storage: Arc::new(NullTowerStorage::default()),
439 vote_history_storage: Arc::new(NullVoteHistoryStorage::default()),
440 debug_keys: None,
441 filter_keys: Arc::default(),
442 contact_debug_interval: DEFAULT_CONTACT_DEBUG_INTERVAL_MILLIS,
443 contact_save_interval: DEFAULT_CONTACT_SAVE_INTERVAL_MILLIS,
444 send_transaction_service_config: SendTransactionServiceConfig::default(),
445 no_poh_speed_test: true,
446 no_os_memory_stats_reporting: true,
447 no_os_network_stats_reporting: true,
448 xdp_network_config_report: None,
449 no_os_cpu_stats_reporting: true,
450 no_os_disk_stats_reporting: true,
451 enforce_ulimit_nofile: false,
453 poh_pinned_cpu_core: poh_service::DEFAULT_PINNED_CPU_CORE,
454 poh_hashes_per_batch: poh_service::DEFAULT_HASHES_PER_BATCH,
455 process_ledger_before_services: false,
456 warp_slot: None,
457 accounts_db_skip_shrink: false,
458 accounts_db_force_initial_clean: false,
459 staked_nodes_overrides: Arc::new(RwLock::new(HashMap::new())),
460 validator_exit: Arc::new(RwLock::new(Exit::default())),
461 validator_exit_backpressure: HashMap::default(),
462 no_wait_for_vote_to_start_leader: true,
463 accounts_db_config: ACCOUNTS_DB_CONFIG_FOR_TESTING,
464 wait_to_vote_slot: None,
465 runtime_config: RuntimeConfig::default(),
466 banking_trace_dir_byte_limit: 0,
467 block_verification_method: BlockVerificationMethod::default(),
468 block_production_method: BlockProductionMethod::default(),
469 block_production_num_workers: BankingStage::default_num_workers(),
470 block_production_scheduler_config: SchedulerConfig::default(),
471 enable_block_production_forwarding: true,
473 enable_scheduler_bindings: false,
474 generator_config: None,
475 use_snapshot_archives_at_startup: UseSnapshotArchivesAtStartup::default(),
476 unified_scheduler_handler_threads: None,
477 ip_echo_server_threads: NonZeroUsize::new(1).expect("1 is non-zero"),
480 rayon_global_threads: NonZeroUsize::new(2).expect("2 is non-zero"),
481 replay_forks_threads: NonZeroUsize::new(1).expect("1 is non-zero"),
482 replay_transactions_threads: NonZeroUsize::new(2).expect("2 is non-zero"),
483 tvu_shred_sigverify_threads: NonZeroUsize::new(2).expect("2 is non-zero"),
484 tvu_bls_sigverify_threads: NonZeroUsize::new(2).expect("2 is non-zero"),
485 delay_leader_block_for_pending_fork: true,
486 voting_service_test_override: None,
487 repair_handler_type: RepairHandlerType::default(),
488 snapshot_packager_niceness_adj: 0,
489 }
490 }
491
492 #[cfg(feature = "dev-context-only-utils")]
493 pub fn enable_default_rpc_block_subscribe(&mut self) {
494 self.pubsub_config = PubSubConfig {
495 enable_block_subscription: true,
496 ..PubSubConfig::default_for_tests()
497 };
498 self.rpc_config = JsonRpcConfig {
499 enable_rpc_transaction_history: true,
500 ..JsonRpcConfig::default_for_test()
501 };
502 }
503}
504
505#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Default)]
509pub enum ValidatorStartProgress {
510 #[default]
511 Initializing, SearchingForRpcService,
513 DownloadingSnapshot {
514 slot: Slot,
515 rpc_addr: SocketAddr,
516 },
517 CleaningBlockStore,
518 CleaningAccounts,
519 LoadingLedger,
520 ProcessingLedger {
521 slot: Slot,
522 max_slot: Slot,
523 },
524 StartingServices,
525 Halted,
530 WaitingForSupermajority {
531 slot: Slot,
532 gossip_stake_percent: u64,
533 },
534
535 Running,
538}
539
540pub struct XdpTransmitSetup {
541 pub transmitter_builder: TransmitterBuilder,
542 pub src_ip: Ipv4Addr,
543}
544
545struct BlockstoreRootScan {
546 thread: Option<JoinHandle<Result<usize, BlockstoreError>>>,
547}
548
549impl BlockstoreRootScan {
550 fn new(config: &ValidatorConfig, blockstore: Arc<Blockstore>, exit: Arc<AtomicBool>) -> Self {
551 let thread = if config.rpc_addrs.is_some()
552 && config.rpc_config.enable_rpc_transaction_history
553 && config.rpc_config.rpc_scan_and_fix_roots
554 {
555 Some(
556 Builder::new()
557 .name("solBStoreRtScan".to_string())
558 .spawn(move || blockstore.scan_and_fix_roots(None, None, &exit))
559 .unwrap(),
560 )
561 } else {
562 None
563 };
564 Self { thread }
565 }
566
567 fn join(self) {
568 if let Some(blockstore_root_scan) = self.thread
569 && let Err(err) = blockstore_root_scan.join()
570 {
571 warn!("blockstore_root_scan failed to join {err:?}");
572 }
573 }
574}
575
576#[derive(Default)]
577struct TransactionHistoryServices {
578 transaction_status_sender: Option<TransactionStatusSender>,
579 transaction_status_service: Option<TransactionStatusService>,
580 max_complete_transaction_status_slot: Arc<AtomicU64>,
581}
582
583pub struct ValidatorTpuConfig {
585 pub vote_use_quic: bool,
587 pub tpu_connection_pool_size: usize,
589 pub tpu_quic_server_config: SwQosQuicStreamerConfig,
591 pub tpu_fwd_quic_server_config: SwQosQuicStreamerConfig,
593 pub vote_quic_server_config: SimpleQosQuicStreamerConfig,
595 pub sigverify_threads: NonZeroUsize,
597}
598
599impl ValidatorTpuConfig {
600 pub fn new_for_tests() -> Self {
603 let tpu_quic_server_config = SwQosQuicStreamerConfig {
604 quic_streamer_config: QuicStreamerConfig {
605 max_connections_per_ipaddr_per_min: 32,
606 stream_receive_window_size: solana_message::v1::MAX_TRANSACTION_SIZE as u32,
607 max_stream_data_bytes: solana_message::v1::MAX_TRANSACTION_SIZE as u32,
608 ..Default::default()
609 },
610 qos_config: SwQosConfig::default(),
611 };
612
613 let tpu_fwd_quic_server_config = SwQosQuicStreamerConfig {
614 quic_streamer_config: QuicStreamerConfig {
615 max_connections_per_ipaddr_per_min: 32,
616 ..Default::default()
617 },
618 qos_config: SwQosConfig {
619 max_unstaked_connections: 0,
620 ..Default::default()
621 },
622 };
623
624 let vote_quic_server_config = SimpleQosQuicStreamerConfig {
626 quic_streamer_config: QuicStreamerConfig {
627 max_connections_per_ipaddr_per_min: 32,
628 ..Default::default()
629 },
630 qos_config: SimpleQosConfig::default(),
631 };
632
633 let sigverify_threads = NonZeroUsize::new(2).expect("2 is non-zero");
635
636 ValidatorTpuConfig {
637 vote_use_quic: DEFAULT_VOTE_USE_QUIC,
638 tpu_connection_pool_size: DEFAULT_TPU_CONNECTION_POOL_SIZE,
639 tpu_quic_server_config,
640 tpu_fwd_quic_server_config,
641 vote_quic_server_config,
642 sigverify_threads,
643 }
644 }
645}
646
647pub struct Validator {
648 exit: Arc<AtomicBool>,
650 validator_exit: Arc<RwLock<Exit>>,
651 #[cfg_attr(not(unix), allow(dead_code))]
652 log_config: Option<ValidatorLogConfig>,
653 json_rpc_service: Option<JsonRpcService>,
654 pubsub_service: Option<PubSubService>,
655 rpc_completed_slots_service: Option<JoinHandle<()>>,
656 optimistically_confirmed_bank_tracker: Option<OptimisticallyConfirmedBankTracker>,
657 transaction_status_service: Option<TransactionStatusService>,
658 entry_notifier_service: Option<EntryNotifierService>,
659 system_monitor_service: Option<SystemMonitorService>,
660 sample_performance_service: Option<SamplePerformanceService>,
661 stats_reporter_service: StatsReporterService,
662 gossip_service: GossipService,
663 serve_repair_service: ServeRepairService,
664 completed_data_sets_service: Option<CompletedDataSetsService>,
665 snapshot_packager_service: SnapshotPackagerService,
666 poh_recorder: Arc<RwLock<PohRecorder>>,
667 poh_service: PohService,
668 block_creation_loop: BlockCreationLoop,
669 tpu: Tpu,
670 tvu: Tvu,
671 ip_echo_server: Option<solana_net_utils::IpEchoServer>,
672 pub cluster_info: Arc<ClusterInfo>,
673 pub bank_forks: Arc<RwLock<BankForks>>,
674 pub blockstore: Arc<Blockstore>,
675 geyser_plugin_service: Option<GeyserPluginService>,
676 _contact_info_notifier: Option<GeyserContactInfoNotifier>,
680 blockstore_metric_report_service: BlockstoreMetricReportService,
681 accounts_background_service: AccountsBackgroundService,
682 xdp_transmitter: Option<Transmitter>,
683 _tpu_client_next_runtime: Option<TokioRuntime>,
687}
688
689impl Validator {
690 #[allow(clippy::too_many_arguments)]
691 pub fn new(
692 node: Node,
693 identity_keypair: Arc<Keypair>,
694 ledger_path: &Path,
695 vote_account: &Pubkey,
696 authorized_voter_keypairs: Arc<RwLock<Vec<Arc<Keypair>>>>,
697 cluster_entrypoints: Vec<ContactInfo>,
698 config: &ValidatorConfig,
699 rpc_to_plugin_manager_receiver: Option<Receiver<GeyserPluginManagerRequest>>,
700 start_progress: Arc<RwLock<ValidatorStartProgress>>,
701 socket_addr_space: SocketAddrSpace,
702 tpu_config: ValidatorTpuConfig,
703 admin_rpc_service_post_init: Arc<RwLock<Option<AdminRpcRequestMetadataPostInit>>>,
704 xdp_transmit_setup: Option<XdpTransmitSetup>,
705 ) -> Result<Self> {
706 let exit = Arc::new(AtomicBool::new(false));
707 Self::new_with_exit(
708 node,
709 identity_keypair,
710 ledger_path,
711 vote_account,
712 authorized_voter_keypairs,
713 cluster_entrypoints,
714 config,
715 rpc_to_plugin_manager_receiver,
716 start_progress,
717 socket_addr_space,
718 tpu_config,
719 admin_rpc_service_post_init,
720 xdp_transmit_setup,
721 exit,
722 )
723 }
724
725 #[allow(clippy::too_many_arguments)]
726 pub fn new_with_exit(
727 mut node: Node,
728 identity_keypair: Arc<Keypair>,
729 ledger_path: &Path,
730 vote_account: &Pubkey,
731 authorized_voter_keypairs: Arc<RwLock<Vec<Arc<Keypair>>>>,
732 cluster_entrypoints: Vec<ContactInfo>,
733 config: &ValidatorConfig,
734 rpc_to_plugin_manager_receiver: Option<Receiver<GeyserPluginManagerRequest>>,
735 start_progress: Arc<RwLock<ValidatorStartProgress>>,
736 socket_addr_space: SocketAddrSpace,
737 tpu_config: ValidatorTpuConfig,
738 admin_rpc_service_post_init: Arc<RwLock<Option<AdminRpcRequestMetadataPostInit>>>,
739 xdp_transmit_setup: Option<XdpTransmitSetup>,
740 exit: Arc<AtomicBool>,
741 ) -> Result<Self> {
742 #[cfg(debug_assertions)]
743 const DEBUG_ASSERTION_STATUS: &str = "enabled";
744 #[cfg(not(debug_assertions))]
745 const DEBUG_ASSERTION_STATUS: &str = "disabled";
746 info!("debug-assertion status: {DEBUG_ASSERTION_STATUS}");
747
748 let ValidatorTpuConfig {
749 vote_use_quic,
750 tpu_connection_pool_size,
751 tpu_quic_server_config,
752 tpu_fwd_quic_server_config,
753 vote_quic_server_config,
754 sigverify_threads: tpu_sigverify_threads,
755 } = tpu_config;
756
757 let start_time = Instant::now();
758
759 adjust_nofile_limit(config.enforce_ulimit_nofile)?;
760
761 if rayon::ThreadPoolBuilder::new()
765 .thread_name(|i| format!("solRayonGlob{i:02}"))
766 .num_threads(config.rayon_global_threads.get())
767 .build_global()
768 .is_err()
769 {
770 warn!("Rayon global thread pool already initialized");
771 }
772
773 let id = identity_keypair.pubkey();
774 assert_eq!(&id, node.info.pubkey());
775
776 info!("identity pubkey: {id}");
777 info!("vote account pubkey: {vote_account}");
778
779 if !config.no_os_network_stats_reporting {
780 verify_net_stats_access().map_err(|e| {
781 ValidatorError::Other(format!("Failed to access network stats: {e:?}"))
782 })?;
783 }
784
785 let mut bank_notification_senders = Vec::new();
786
787 let geyser_plugin_config_files = config
788 .on_start_geyser_plugin_config_files
789 .as_ref()
790 .map(Cow::Borrowed)
791 .or_else(|| {
792 config
793 .geyser_plugin_always_enabled
794 .then_some(Cow::Owned(vec![]))
795 });
796 let geyser_plugin_service =
797 if let Some(geyser_plugin_config_files) = geyser_plugin_config_files {
798 let (confirmed_bank_sender, confirmed_bank_receiver) = unbounded();
799 bank_notification_senders.push(confirmed_bank_sender);
800 let rpc_to_plugin_manager_receiver_and_exit =
801 rpc_to_plugin_manager_receiver.map(|receiver| (receiver, exit.clone()));
802 Some(
803 GeyserPluginService::new_with_receiver(
804 confirmed_bank_receiver,
805 config.geyser_plugin_always_enabled,
806 geyser_plugin_config_files.as_ref(),
807 rpc_to_plugin_manager_receiver_and_exit,
808 )
809 .map_err(|err| {
810 ValidatorError::Other(format!("Failed to load the Geyser plugin: {err:?}"))
811 })?,
812 )
813 } else {
814 None
815 };
816
817 if config.voting_disabled {
818 warn!("voting disabled");
819 authorized_voter_keypairs.write().unwrap().clear();
820 } else {
821 for authorized_voter_keypair in authorized_voter_keypairs.read().unwrap().iter() {
822 warn!("authorized voter: {}", authorized_voter_keypair.pubkey());
823 }
824 }
825
826 for cluster_entrypoint in &cluster_entrypoints {
827 info!("entrypoint: {cluster_entrypoint:?}");
828 }
829
830 if !ledger_path.is_dir() {
831 return Err(anyhow!(
832 "ledger directory does not exist or is not accessible: {ledger_path:?}"
833 ));
834 }
835 let genesis_config = load_genesis(config, ledger_path)?;
836 metrics_config_sanity_check(genesis_config.cluster_type)?;
837
838 info!("Validating accounts paths...");
839 *start_progress.write().unwrap() = ValidatorStartProgress::CleaningAccounts;
840 let mut timer = Measure::start("validate_account_paths");
841 validate_account_paths(config)?;
842 timer.stop();
843 info!("Validating accounts paths done. {timer}");
844
845 snapshot_utils::purge_incomplete_bank_snapshots(&config.snapshot_config.bank_snapshots_dir);
846 snapshot_utils::purge_old_bank_snapshots_at_startup(
847 &config.snapshot_config.bank_snapshots_dir,
848 );
849
850 let cancel = CancellationToken::new();
852 {
853 let exit = exit.clone();
854 config
855 .validator_exit
856 .write()
857 .unwrap()
858 .register_exit(Box::new(move || exit.store(true, Ordering::Relaxed)));
859 let cancel = cancel.clone();
860 config
861 .validator_exit
862 .write()
863 .unwrap()
864 .register_exit(Box::new(move || cancel.cancel()));
865 }
866
867 let (
868 accounts_update_notifier,
869 transaction_notifier,
870 deshred_transaction_notifier,
871 entry_notifier,
872 block_metadata_notifier,
873 slot_status_notifier,
874 ) = if let Some(service) = &geyser_plugin_service {
875 (
876 service.get_accounts_update_notifier(),
877 service.get_transaction_notifier(),
878 service.get_deshred_transaction_notifier(),
879 service.get_entry_notifier(),
880 service.get_block_metadata_notifier(),
881 service.get_slot_status_notifier(),
882 )
883 } else {
884 (None, None, None, None, None, None)
885 };
886
887 info!(
888 "Geyser plugin: accounts_update_notifier: {}, transaction_notifier: {}, \
889 deshred_transaction_notifier: {}, entry_notifier: {}",
890 accounts_update_notifier.is_some(),
891 transaction_notifier.is_some(),
892 deshred_transaction_notifier.is_some(),
893 entry_notifier.is_some()
894 );
895
896 let system_monitor_service = Some(SystemMonitorService::new(
897 exit.clone(),
898 SystemMonitorStatsReportConfig {
899 report_os_memory_stats: !config.no_os_memory_stats_reporting,
900 report_os_network_stats: !config.no_os_network_stats_reporting,
901 xdp_network_config_report: config.xdp_network_config_report.clone(),
902 report_os_cpu_stats: !config.no_os_cpu_stats_reporting,
903 report_os_disk_stats: !config.no_os_disk_stats_reporting,
904 },
905 ));
906
907 let dependency_tracker = Arc::new(DependencyTracker::default());
908
909 let (
910 bank_forks,
911 blockstore,
912 original_blockstore_root,
913 ledger_signal_receiver,
914 update_parent_receiver,
915 leader_schedule_cache,
916 starting_snapshot_hashes,
917 TransactionHistoryServices {
918 transaction_status_sender,
919 transaction_status_service,
920 max_complete_transaction_status_slot,
921 },
922 blockstore_process_options,
923 blockstore_root_scan,
924 pruned_banks_receiver,
925 entry_notifier_service,
926 ) = load_blockstore(
927 config,
928 ledger_path,
929 &genesis_config,
930 exit.clone(),
931 &start_progress,
932 accounts_update_notifier,
933 transaction_notifier,
934 entry_notifier,
935 config
936 .rpc_addrs
937 .is_some()
938 .then(|| dependency_tracker.clone()),
939 )
940 .map_err(ValidatorError::Other)?;
941
942 let migration_status = bank_forks.read().unwrap().migration_status();
943
944 if !config.no_poh_speed_test && !migration_status.is_alpenglow_enabled() {
945 check_poh_speed(&bank_forks.read().unwrap().root_bank(), None)?;
946 }
947
948 let (root_slot, hard_forks) = {
949 let root_bank = bank_forks.read().unwrap().root_bank();
950 (root_bank.slot(), root_bank.hard_forks())
951 };
952 let shred_version = compute_shred_version(&genesis_config.hash(), Some(&hard_forks));
953 info!("shred version: {shred_version}, hard forks: {hard_forks:?}");
954
955 if let Some(expected_shred_version) = config.expected_shred_version
956 && expected_shred_version != shred_version
957 {
958 return Err(ValidatorError::ShredVersionMismatch {
959 actual: shred_version,
960 expected: expected_shred_version,
961 }
962 .into());
963 }
964
965 if let Some(start_slot) = should_cleanup_blockstore_incorrect_shred_versions(
966 config,
967 &blockstore,
968 root_slot,
969 &hard_forks,
970 )? {
971 *start_progress.write().unwrap() = ValidatorStartProgress::CleaningBlockStore;
972 cleanup_blockstore_incorrect_shred_versions(
973 &blockstore,
974 config,
975 start_slot,
976 shred_version,
977 )?;
978 } else {
979 info!("Skipping the blockstore check for shreds with incorrect version");
980 }
981
982 node.info.set_shred_version(shred_version);
983 node.info.set_wallclock(timestamp());
984 Self::print_node_info(&node);
985
986 let mut cluster_info = ClusterInfo::new(
987 node.info.clone(),
988 identity_keypair.clone(),
989 socket_addr_space,
990 );
991 cluster_info.set_contact_debug_interval(config.contact_debug_interval);
992 if let Some(known_validators) = &config.known_validators {
993 cluster_info
994 .set_trim_keep_pubkeys(known_validators.iter().copied())
995 .expect("set_trim_keep_pubkeys should succeed as ClusterInfo was just created");
996 }
997 cluster_info.set_entrypoints(cluster_entrypoints);
998 cluster_info.restore_contact_info(ledger_path, config.contact_save_interval);
999 cluster_info.set_bind_ip_addrs(node.bind_ip_addrs.clone());
1000 let cluster_info = Arc::new(cluster_info);
1001 cluster_info.set_migration_status(migration_status.clone());
1002 let node_multihoming = Arc::new(NodeMultihoming::from(&node));
1003 migration_status.set_pubkey(cluster_info.id());
1004
1005 let contact_info_notifier = geyser_plugin_service.as_ref().and_then(|service| {
1009 geyser_contact_info_notifier::attach(
1010 service.plugin_manager_handle(),
1011 cluster_info.as_ref(),
1012 geyser_contact_info_notifier::DEFAULT_CHANNEL_CAPACITY,
1013 )
1014 });
1015
1016 assert!(is_snapshot_config_valid(&config.snapshot_config));
1017
1018 let (snapshot_request_sender, snapshot_request_receiver) = unbounded();
1019 let snapshot_controller = Arc::new(SnapshotController::new(
1020 snapshot_request_sender,
1021 config.snapshot_config.clone(),
1022 bank_forks.read().unwrap().root(),
1023 ));
1024
1025 let pending_snapshot_packages = Arc::new(Mutex::new(PendingSnapshotPackages::default()));
1026 let exit_backpressure = config
1027 .validator_exit_backpressure
1028 .get(SnapshotPackagerService::NAME)
1029 .cloned();
1030 let enable_gossip_push = true;
1031 let snapshot_packager_service = SnapshotPackagerService::new(
1032 pending_snapshot_packages.clone(),
1033 starting_snapshot_hashes,
1034 exit.clone(),
1035 exit_backpressure,
1036 cluster_info.clone(),
1037 snapshot_controller.clone(),
1038 enable_gossip_push,
1039 config.snapshot_packager_niceness_adj,
1040 );
1041 let snapshot_request_handler = SnapshotRequestHandler {
1042 snapshot_controller: snapshot_controller.clone(),
1043 snapshot_request_receiver,
1044 pending_snapshot_packages,
1045 };
1046 let pruned_banks_request_handler = PrunedBanksRequestHandler {
1047 pruned_banks_receiver,
1048 };
1049 let accounts_background_service = AccountsBackgroundService::new(
1050 bank_forks.clone(),
1051 exit.clone(),
1052 AbsRequestHandlers {
1053 snapshot_request_handler,
1054 pruned_banks_request_handler,
1055 },
1056 );
1057 info!(
1058 "Using: block-verification-method: {}, block-production-method: {}",
1059 config.block_verification_method, config.block_production_method,
1060 );
1061
1062 let (replay_vote_sender, replay_vote_receiver) = unbounded();
1063
1064 let prioritization_fee_cache = if config.rpc_config.full_api {
1065 Some(Arc::new(PrioritizationFeeCache::default()))
1066 } else {
1067 None
1068 };
1069
1070 let leader_schedule_cache = Arc::new(leader_schedule_cache);
1071 let (poh_recorder, entry_receiver) = {
1072 let bank = &bank_forks.read().unwrap().working_bank();
1073 PohRecorder::new_with_clear_signal(
1074 bank.tick_height(),
1075 bank.last_blockhash(),
1076 bank.clone(),
1077 None,
1078 bank.ticks_per_slot(),
1079 config.delay_leader_block_for_pending_fork,
1080 blockstore.clone(),
1081 blockstore.get_new_shred_signal(0),
1082 &leader_schedule_cache,
1083 &genesis_config.poh_config,
1084 exit.clone(),
1085 )
1086 };
1087 let (record_sender, record_receiver) = record_channels(transaction_status_sender.is_some());
1088 let transaction_recorder = TransactionRecorder::new(record_sender);
1089 let poh_recorder = Arc::new(RwLock::new(poh_recorder));
1090 let (poh_controller, poh_service_message_receiver) = PohController::new();
1091 let (bank_forks_controller, bank_forks_controller_receiver) =
1092 BankForksControllerHandle::new();
1093 let bank_forks_controller = Arc::new(bank_forks_controller);
1094
1095 let (banking_tracer, tracer_thread) =
1096 BankingTracer::new((config.banking_trace_dir_byte_limit > 0).then_some((
1097 &blockstore.banking_trace_path(),
1098 exit.clone(),
1099 config.banking_trace_dir_byte_limit,
1100 )))?;
1101 if banking_tracer.is_enabled() {
1102 info!(
1103 "Enabled banking trace (dir_byte_limit: {})",
1104 config.banking_trace_dir_byte_limit
1105 );
1106 } else {
1107 info!("Disabled banking trace");
1108 }
1109 let banking_tracer_channels = banking_tracer.create_channels();
1110
1111 let scheduler_pool = DefaultSchedulerPool::new(
1112 config.unified_scheduler_handler_threads,
1113 config.runtime_config.log_messages_bytes_limit,
1114 transaction_status_sender.clone(),
1115 Some(replay_vote_sender.clone()),
1116 prioritization_fee_cache.clone(),
1117 );
1118 bank_forks
1119 .write()
1120 .unwrap()
1121 .install_scheduler_pool(scheduler_pool);
1122
1123 let entry_notification_sender = entry_notifier_service
1124 .as_ref()
1125 .map(|service| service.sender());
1126 let mut process_blockstore = ProcessBlockStore::new(
1127 &id,
1128 vote_account,
1129 &start_progress,
1130 &blockstore,
1131 original_blockstore_root,
1132 &bank_forks,
1133 &leader_schedule_cache,
1134 &blockstore_process_options,
1135 transaction_status_sender.as_ref(),
1136 entry_notification_sender,
1137 blockstore_root_scan,
1138 &snapshot_controller,
1139 config,
1140 cluster_info.my_shred_version(),
1141 );
1142
1143 maybe_warp_slot(
1144 config,
1145 &mut process_blockstore,
1146 ledger_path,
1147 &bank_forks,
1148 &leader_schedule_cache,
1149 &snapshot_controller,
1150 )
1151 .map_err(ValidatorError::Other)?;
1152
1153 if config.process_ledger_before_services {
1154 process_blockstore
1155 .process()
1156 .map_err(ValidatorError::Other)?;
1157 }
1158 *start_progress.write().unwrap() = ValidatorStartProgress::StartingServices;
1159
1160 let mut block_commitment_cache = BlockCommitmentCache::default();
1161 let bank_forks_guard = bank_forks.read().unwrap();
1162 block_commitment_cache.initialize_slots(
1163 bank_forks_guard.working_bank().slot(),
1164 bank_forks_guard.root(),
1165 );
1166 drop(bank_forks_guard);
1167 let block_commitment_cache = Arc::new(RwLock::new(block_commitment_cache));
1168
1169 let optimistically_confirmed_bank =
1170 OptimisticallyConfirmedBank::locked_from_bank_forks_root(&bank_forks);
1171
1172 let max_slots = Arc::new(MaxSlots::default());
1173
1174 let staked_nodes = Arc::new(RwLock::new(StakedNodes::default()));
1175
1176 let mut tpu_transactions_forwards_client_sockets =
1177 Some(node.sockets.tpu_transaction_forwarding_clients);
1178
1179 let vote_connection_cache = if vote_use_quic {
1180 let vote_connection_cache = ConnectionCache::new_with_client_options(
1181 "connection_cache_vote_quic",
1182 tpu_connection_pool_size,
1183 Some(node.sockets.quic_vote_client),
1184 Some((
1185 &identity_keypair,
1186 node.info
1187 .tpu_vote(Protocol::QUIC)
1188 .ok_or_else(|| {
1189 ValidatorError::Other(String::from("Invalid QUIC address for TPU Vote"))
1190 })?
1191 .ip(),
1192 )),
1193 Some((&staked_nodes, &identity_keypair.pubkey())),
1194 );
1195 Arc::new(vote_connection_cache)
1196 } else {
1197 Arc::new(ConnectionCache::with_udp(
1198 "connection_cache_vote_udp",
1199 tpu_connection_pool_size,
1200 ))
1201 };
1202
1203 let key_notifiers = Arc::new(RwLock::new(KeyUpdaters::default()));
1204
1205 let current_runtime_handle = tokio::runtime::Handle::try_current();
1211 let tpu_client_next_runtime = current_runtime_handle.is_err().then(|| {
1212 tokio::runtime::Builder::new_multi_thread()
1213 .enable_all()
1214 .worker_threads(2)
1215 .thread_name("solTpuClientRt")
1216 .build()
1217 .unwrap()
1218 });
1219
1220 let rpc_override_health_check =
1221 Arc::new(AtomicBool::new(config.rpc_config.disable_health_check));
1222 let (
1223 json_rpc_service,
1224 rpc_subscriptions,
1225 pubsub_service,
1226 rpc_completed_slots_service,
1227 sample_performance_service,
1228 optimistically_confirmed_bank_tracker,
1229 bank_notification_sender,
1230 ) = if let Some((rpc_addr, rpc_pubsub_addr)) = config.rpc_addrs {
1231 assert_eq!(
1232 node.info.rpc().map(|addr| socket_addr_space.check(&addr)),
1233 node.info
1234 .rpc_pubsub()
1235 .map(|addr| socket_addr_space.check(&addr))
1236 );
1237 let (bank_notification_sender, bank_notification_receiver) = unbounded();
1238 let confirmed_bank_subscribers = if !bank_notification_senders.is_empty() {
1239 Some(Arc::new(RwLock::new(bank_notification_senders)))
1240 } else {
1241 None
1242 };
1243
1244 let rpc_tpu_client_args = {
1245 let runtime_handle = tpu_client_next_runtime
1246 .as_ref()
1247 .map(TokioRuntime::handle)
1248 .unwrap_or_else(|| current_runtime_handle.as_ref().unwrap());
1249
1250 RpcTpuClientArgs(
1251 Arc::as_ref(&identity_keypair),
1252 node.sockets.rpc_sts_client,
1253 runtime_handle.clone(),
1254 cancel.clone(),
1255 )
1256 };
1257 let rpc_svc_config = JsonRpcServiceConfig {
1258 rpc_addr,
1259 rpc_config: config.rpc_config.clone(),
1260 snapshot_config: Some(snapshot_controller.snapshot_config().clone()),
1261 bank_forks: bank_forks.clone(),
1262 block_commitment_cache: block_commitment_cache.clone(),
1263 blockstore: blockstore.clone(),
1264 cluster_info: cluster_info.clone(),
1265 poh_recorder: Some(poh_recorder.clone()),
1266 genesis_hash: genesis_config.hash(),
1267 ledger_path: ledger_path.to_path_buf(),
1268 validator_exit: config.validator_exit.clone(),
1269 exit: exit.clone(),
1270 override_health_check: rpc_override_health_check.clone(),
1271 optimistically_confirmed_bank: optimistically_confirmed_bank.clone(),
1272 send_transaction_service_config: config.send_transaction_service_config.clone(),
1273 max_slots: max_slots.clone(),
1274 leader_schedule_cache: leader_schedule_cache.clone(),
1275 max_complete_transaction_status_slot: max_complete_transaction_status_slot.clone(),
1276 prioritization_fee_cache: prioritization_fee_cache.clone(),
1277 rpc_tpu_client_args,
1278 };
1279 let json_rpc_service =
1280 JsonRpcService::new_with_config(rpc_svc_config).map_err(ValidatorError::Other)?;
1281 let rpc_subscriptions = Arc::new(RpcSubscriptions::new_with_config(
1282 exit.clone(),
1283 max_complete_transaction_status_slot,
1284 blockstore.clone(),
1285 bank_forks.clone(),
1286 block_commitment_cache.clone(),
1287 optimistically_confirmed_bank.clone(),
1288 &config.pubsub_config,
1289 None,
1290 ));
1291 let pubsub_service = if !config.rpc_config.full_api {
1292 None
1293 } else {
1294 let (trigger, pubsub_service) = PubSubService::new(
1295 config.pubsub_config.clone(),
1296 &rpc_subscriptions,
1297 rpc_pubsub_addr,
1298 );
1299 config
1300 .validator_exit
1301 .write()
1302 .unwrap()
1303 .register_exit(Box::new(move || trigger.cancel()));
1304
1305 Some(pubsub_service)
1306 };
1307
1308 let rpc_completed_slots_service =
1309 if config.rpc_config.full_api || geyser_plugin_service.is_some() {
1310 let (completed_slots_sender, completed_slots_receiver) =
1311 bounded(MAX_COMPLETED_SLOTS_IN_CHANNEL);
1312 blockstore.add_completed_slots_signal(completed_slots_sender);
1313
1314 Some(RpcCompletedSlotsService::spawn(
1315 completed_slots_receiver,
1316 rpc_subscriptions.clone(),
1317 slot_status_notifier.clone(),
1318 exit.clone(),
1319 ))
1320 } else {
1321 None
1322 };
1323
1324 let sample_performance_service = if config.rpc_config.enable_rpc_transaction_history {
1325 Some(SamplePerformanceService::new(
1326 bank_forks.clone(),
1327 blockstore.clone(),
1328 exit.clone(),
1329 ))
1330 } else {
1331 None
1332 };
1333
1334 let dependency_tracker = transaction_status_sender
1335 .is_some()
1336 .then_some(dependency_tracker);
1337 let optimistically_confirmed_bank_tracker =
1338 Some(OptimisticallyConfirmedBankTracker::new(
1339 bank_notification_receiver,
1340 exit.clone(),
1341 bank_forks.clone(),
1342 optimistically_confirmed_bank,
1343 rpc_subscriptions.clone(),
1344 confirmed_bank_subscribers,
1345 prioritization_fee_cache.clone(),
1346 dependency_tracker.clone(),
1347 ));
1348 let bank_notification_sender_config = Some(BankNotificationSenderConfig {
1349 sender: bank_notification_sender,
1350 should_send_parents: geyser_plugin_service.is_some(),
1351 dependency_tracker,
1352 });
1353 (
1354 Some(json_rpc_service),
1355 Some(rpc_subscriptions),
1356 pubsub_service,
1357 rpc_completed_slots_service,
1358 sample_performance_service,
1359 optimistically_confirmed_bank_tracker,
1360 bank_notification_sender_config,
1361 )
1362 } else {
1363 (None, None, None, None, None, None, None)
1364 };
1365
1366 let (completed_data_sets_sender, completed_data_sets_service) =
1374 if config.rpc_config.full_api || deshred_transaction_notifier.is_some() {
1375 let (completed_data_sets_sender, completed_data_sets_receiver) =
1376 bounded(MAX_COMPLETED_DATA_SETS_IN_CHANNEL);
1377 let completed_data_sets_service = CompletedDataSetsService::new(
1378 completed_data_sets_receiver,
1379 blockstore.clone(),
1380 rpc_subscriptions.clone(),
1381 deshred_transaction_notifier.clone(),
1382 exit.clone(),
1383 max_slots.clone(),
1384 bank_forks.clone(),
1385 );
1386 (
1387 Some(completed_data_sets_sender),
1388 Some(completed_data_sets_service),
1389 )
1390 } else {
1391 (None, None)
1392 };
1393
1394 let ip_echo_server = match node.sockets.ip_echo {
1395 None => None,
1396 Some(tcp_listener) => Some(solana_net_utils::ip_echo_server(
1397 tcp_listener,
1398 config.ip_echo_server_threads,
1399 Some(node.info.shred_version()),
1400 )),
1401 };
1402
1403 let (stats_reporter_sender, stats_reporter_receiver) = unbounded();
1404
1405 let stats_reporter_service =
1406 StatsReporterService::new(stats_reporter_receiver, exit.clone());
1407
1408 let epoch_specs: Box<dyn solana_gossip::epoch_specs::EpochSpecs> =
1409 Box::new(crate::epoch_specs::EpochSpecs::from(bank_forks.clone()));
1410
1411 let (
1412 xdp_transmitter,
1413 turbine_xdp_sender,
1414 quic_xdp_sender,
1415 repair_xdp_sender,
1416 gossip_xdp_sender,
1417 ) = if let Some(XdpTransmitSetup {
1418 transmitter_builder,
1419 src_ip,
1420 }) = xdp_transmit_setup
1421 {
1422 let turbine_src_port = node.sockets.retransmit_sockets[0]
1423 .local_addr()
1424 .expect("retransmit socket should have local address")
1425 .port();
1426
1427 let repair_src_port = node
1428 .sockets
1429 .repair
1430 .local_addr()
1431 .expect("repair socket should have local address")
1432 .port();
1433
1434 let gossip_src_port = node.sockets.gossip[0]
1435 .local_addr()
1436 .expect("gossip socket should have local address")
1437 .port();
1438
1439 let (transmitter, sender) = transmitter_builder.build();
1440 (
1441 Some(transmitter),
1442 Some(PinnedXdpSender::new(
1443 sender.clone(),
1444 SocketAddrV4::new(src_ip, turbine_src_port),
1445 )),
1446 Some((sender.clone(), src_ip)),
1447 Some(PinnedXdpSender::new(
1448 sender.clone(),
1449 SocketAddrV4::new(src_ip, repair_src_port),
1450 )),
1451 Some(PinnedXdpSender::new(
1452 sender,
1453 SocketAddrV4::new(src_ip, gossip_src_port),
1454 )),
1455 )
1456 } else {
1457 (None, None, None, None, None)
1458 };
1459
1460 let gossip_service = GossipService::new(
1461 &cluster_info,
1462 Some(epoch_specs),
1463 node.sockets.gossip.clone(),
1464 gossip_xdp_sender,
1465 config.gossip_validators.clone(),
1466 config.should_check_duplicate_instance,
1467 Some(stats_reporter_sender.clone()),
1468 exit.clone(),
1469 );
1470 let serve_repair = {
1471 let bank_forks_r = bank_forks.read().unwrap();
1472 let leader_state = poh_recorder.read().unwrap().shared_leader_state();
1473 config.repair_handler_type.create_serve_repair(
1474 blockstore.clone(),
1475 cluster_info.clone(),
1476 bank_forks_r.sharable_banks(),
1477 config.repair_whitelist.clone(),
1478 leader_state,
1479 leader_schedule_cache.clone(),
1480 bank_forks_r.migration_status(),
1481 )
1482 };
1483
1484 let waited_for_supermajority = wait_for_supermajority(
1485 config,
1486 Some(&mut process_blockstore),
1487 &bank_forks,
1488 &cluster_info,
1489 rpc_override_health_check,
1490 &start_progress,
1491 )?;
1492
1493 let blockstore_metric_report_service =
1494 BlockstoreMetricReportService::new(blockstore.clone(), exit.clone());
1495
1496 let wait_for_vote_to_start_leader =
1497 !waited_for_supermajority && !config.no_wait_for_vote_to_start_leader;
1498
1499 let (record_receiver_sender, record_receiver_receiver) = bounded(1);
1502 let (leader_window_info_sender, leader_window_info_receiver) = bounded(7);
1506
1507 let poh_service = PohService::new(
1508 poh_recorder.clone(),
1509 &genesis_config.poh_config,
1510 exit.clone(),
1511 bank_forks.read().unwrap().root_bank().ticks_per_slot(),
1512 config.poh_pinned_cpu_core,
1513 config.poh_hashes_per_batch,
1514 record_receiver,
1515 poh_service_message_receiver,
1516 migration_status.clone(),
1517 record_receiver_sender,
1518 );
1519
1520 let replay_highest_frozen = Arc::new(ReplayHighestFrozen::default());
1521 let highest_parent_ready = Arc::new(RwLock::default());
1522 let highest_finalized = Arc::new(RwLock::new(None));
1524 let (optimistic_parent_sender, optimistic_parent_receiver) = bounded(100);
1528
1529 let banking_stage_sender_for_bcl = banking_tracer_channels.non_vote_sender.clone();
1530 let entry_notification_sender = entry_notifier_service
1531 .as_ref()
1532 .map(|service| service.sender_cloned());
1533
1534 let block_creation_loop_config = BlockCreationLoopConfig {
1535 exit: exit.clone(),
1536 bank_forks: bank_forks.clone(),
1537 bank_forks_controller: bank_forks_controller.clone(),
1538 blockstore: blockstore.clone(),
1539 cluster_info: cluster_info.clone(),
1540 poh_recorder: poh_recorder.clone(),
1541 leader_schedule_cache: leader_schedule_cache.clone(),
1542 rpc_subscriptions: rpc_subscriptions.clone(),
1543 banking_tracer: banking_tracer.clone(),
1544 slot_status_notifier: slot_status_notifier.clone(),
1545 entry_notification_sender: entry_notification_sender.clone(),
1546 leader_window_info_receiver,
1547 highest_parent_ready: highest_parent_ready.clone(),
1548 replay_highest_frozen: replay_highest_frozen.clone(),
1549 record_receiver_receiver,
1550 optimistic_parent_receiver: optimistic_parent_receiver.clone(),
1551 highest_finalized: highest_finalized.clone(),
1552 banking_stage_sender: banking_stage_sender_for_bcl,
1553 sharable_banks: bank_forks.read().unwrap().sharable_banks(),
1554 };
1555 let (block_creation_loop, reward_aggregates_sender) =
1556 BlockCreationLoop::new(block_creation_loop_config);
1557
1558 assert_eq!(
1559 blockstore.get_new_shred_signals_len(),
1560 1,
1561 "New shred signal for the TVU should be the same as the clear bank signal."
1562 );
1563
1564 let vote_tracker = Arc::<VoteTracker>::default();
1565
1566 let (retransmit_slots_sender, retransmit_slots_receiver) = unbounded();
1567 let (verified_vote_sender, verified_vote_receiver) = unbounded();
1568 let (gossip_verified_vote_hash_sender, gossip_verified_vote_hash_receiver) = unbounded();
1569 let (duplicate_confirmed_slot_sender, duplicate_confirmed_slots_receiver) = unbounded();
1570
1571 let serve_repair_service = ServeRepairService::new(
1572 serve_repair,
1573 node.sockets.serve_repair,
1574 socket_addr_space,
1575 stats_reporter_sender,
1576 exit.clone(),
1577 );
1578
1579 let (tower, vote_history) = process_blockstore.process().map_err(|e| {
1580 ValidatorError::Other(format!(
1581 "Unable to restore Tower or VoteHistory and either --require-tower was specified \
1582 or --do-not-require-vote-history was not specified. Aborting {e}"
1583 ))
1584 })?;
1585 info!("Tower state: {tower:?}, Vote History state: {vote_history:?}");
1586
1587 migration_status.log_phase();
1588
1589 let outstanding_repair_requests =
1590 Arc::<RwLock<repair::repair_service::OutstandingShredRepairs>>::default();
1591 let root_bank = bank_forks.read().unwrap().root_bank();
1592 let cluster_slots = Arc::new({
1593 crate::cluster_slots_service::cluster_slots::ClusterSlots::new(
1594 &root_bank,
1595 &cluster_info,
1596 )
1597 });
1598 let (votor_event_sender, votor_event_receiver) = bounded(1000);
1600
1601 let tvu = Tvu::new(
1602 vote_account,
1603 authorized_voter_keypairs,
1604 bank_forks.clone(),
1605 &cluster_info,
1606 TvuSockets {
1607 repair: node.sockets.repair.try_clone().unwrap(),
1608 retransmit: node.sockets.retransmit_sockets,
1609 fetch: node.sockets.tvu,
1610 ancestor_hashes_requests: node.sockets.ancestor_hashes_requests,
1611 block_id_repair: node.sockets.block_id_repair,
1612 },
1613 blockstore.clone(),
1614 ledger_signal_receiver,
1615 update_parent_receiver,
1616 rpc_subscriptions.clone(),
1617 &poh_recorder,
1618 poh_controller,
1619 tower,
1620 config.tower_storage.clone(),
1621 vote_history,
1622 config.vote_history_storage.clone(),
1623 &leader_schedule_cache,
1624 exit.clone(),
1625 block_commitment_cache,
1626 config.turbine_mode.clone(),
1627 transaction_status_sender.clone(),
1628 entry_notification_sender.clone(),
1629 vote_tracker.clone(),
1630 retransmit_slots_sender,
1631 gossip_verified_vote_hash_receiver,
1632 verified_vote_sender.clone(),
1633 verified_vote_receiver,
1634 replay_vote_sender.clone(),
1635 completed_data_sets_sender,
1636 bank_notification_sender.clone(),
1637 duplicate_confirmed_slots_receiver,
1638 TvuConfig {
1639 blockstore_cleanup_strategy: config.blockstore_cleanup_strategy,
1640 shred_version: node.info.shred_version(),
1641 repair_validators: config.repair_validators.clone(),
1642 repair_whitelist: config.repair_whitelist.clone(),
1643 wait_for_vote_to_start_leader,
1644 replay_forks_threads: config.replay_forks_threads,
1645 replay_transactions_threads: config.replay_transactions_threads,
1646 shred_sigverify_threads: config.tvu_shred_sigverify_threads,
1647 bls_sigverify_threads: config.tvu_bls_sigverify_threads,
1648 turbine_xdp_sender: turbine_xdp_sender.clone(),
1649 repair_xdp_sender,
1650 },
1651 &max_slots,
1652 block_metadata_notifier,
1653 config.wait_to_vote_slot,
1654 Some(snapshot_controller.clone()),
1655 banking_tracer,
1656 outstanding_repair_requests.clone(),
1657 cluster_slots.clone(),
1658 slot_status_notifier,
1659 vote_connection_cache,
1660 AlpenglowInitializationState {
1661 leader_window_info_sender,
1662 optimistic_parent_sender,
1663 optimistic_parent_receiver,
1664 replay_highest_frozen,
1665 highest_parent_ready,
1666 bank_forks_controller,
1667 bank_forks_controller_receiver,
1668 votor_event_sender: votor_event_sender.clone(),
1669 votor_event_receiver,
1670 cancel: cancel.child_token(),
1671 key_notifiers: key_notifiers.clone(),
1672 votor_server_sockets: node.sockets.votor_server,
1673 votor_client_socket: node.sockets.quic_votor_client,
1674 #[cfg(feature = "dev-context-only-utils")]
1675 voting_service_test_override: config.voting_service_test_override.clone(),
1676 highest_finalized,
1677 },
1678 reward_aggregates_sender,
1679 )
1680 .map_err(ValidatorError::Other)?;
1681
1682 let tpu_forwarding_client_config = {
1683 let runtime_handle = tpu_client_next_runtime
1684 .as_ref()
1685 .map(TokioRuntime::handle)
1686 .unwrap_or_else(|| current_runtime_handle.as_ref().unwrap());
1687 ForwardingClientConfig {
1688 stake_identity: Arc::as_ref(&identity_keypair),
1689 tpu_client_sockets: tpu_transactions_forwards_client_sockets.take().unwrap(),
1690 runtime_handle: runtime_handle.clone(),
1691 cancel: cancel.clone(),
1692 node_multihoming: node_multihoming.clone(),
1693 }
1694 };
1695 let (banking_control_sender, banking_control_receiver) = mpsc::channel(1);
1696 let tpu = Tpu::new_with_client(
1697 &cluster_info,
1698 &poh_recorder,
1699 transaction_recorder,
1700 entry_receiver,
1701 retransmit_slots_receiver,
1702 TpuSockets {
1703 vote: node.sockets.tpu_vote,
1704 broadcast: node.sockets.broadcast,
1705 transactions_quic: node.sockets.tpu_quic,
1706 transactions_forwards_quic: node.sockets.tpu_forwards_quic,
1707 vote_quic: node.sockets.tpu_vote_quic,
1708 vote_forwarding_client: node.sockets.tpu_vote_forwarding_client,
1709 },
1710 rpc_subscriptions,
1711 transaction_status_sender,
1712 entry_notification_sender,
1713 blockstore.clone(),
1714 &config.broadcast_stage_type,
1715 leader_schedule_cache.clone(),
1716 turbine_xdp_sender,
1717 quic_xdp_sender,
1718 exit.clone(),
1719 node.info.shred_version(),
1720 vote_tracker,
1721 bank_forks.clone(),
1722 verified_vote_sender,
1723 gossip_verified_vote_hash_sender,
1724 replay_vote_receiver,
1725 replay_vote_sender,
1726 bank_notification_sender,
1727 duplicate_confirmed_slot_sender,
1728 tpu_forwarding_client_config,
1729 &identity_keypair,
1730 config.runtime_config.log_messages_bytes_limit,
1731 &staked_nodes,
1732 config.staked_nodes_overrides.clone(),
1733 banking_tracer_channels,
1734 tracer_thread,
1735 tpu_quic_server_config,
1736 tpu_fwd_quic_server_config,
1737 vote_quic_server_config,
1738 prioritization_fee_cache,
1739 tpu_sigverify_threads,
1740 config.block_production_method.clone(),
1741 config.block_production_num_workers,
1742 config.block_production_scheduler_config.clone(),
1743 config.filter_keys.clone(),
1744 config.enable_block_production_forwarding,
1745 config.generator_config.clone(),
1746 key_notifiers.clone(),
1747 banking_control_receiver,
1748 config.enable_scheduler_bindings.then(|| {
1749 (
1750 ledger_path.join("scheduler_bindings.ipc"),
1751 banking_control_sender.clone(),
1752 )
1753 }),
1754 cancel,
1755 votor_event_sender.clone(),
1756 );
1757
1758 datapoint_info!(
1759 "validator-new",
1760 ("id", id.to_string(), String),
1761 ("version", solana_version::version!(), String),
1762 ("cluster_type", genesis_config.cluster_type as u32, i64),
1763 ("elapsed_ms", start_time.elapsed().as_millis() as i64, i64),
1764 ("waited_for_supermajority", waited_for_supermajority, bool),
1765 ("shred_version", shred_version as i64, i64),
1766 );
1767
1768 *start_progress.write().unwrap() = ValidatorStartProgress::Running;
1769 if let Some(json_rpc_service) = &json_rpc_service {
1770 key_notifiers.write().unwrap().add(
1771 KeyUpdaterType::RpcService,
1772 json_rpc_service.get_client_key_updater(),
1773 );
1774 }
1775
1776 *admin_rpc_service_post_init.write().unwrap() = Some(AdminRpcRequestMetadataPostInit {
1777 bank_forks: bank_forks.clone(),
1778 cluster_info: cluster_info.clone(),
1779 vote_account: *vote_account,
1780 repair_whitelist: config.repair_whitelist.clone(),
1781 notifies: key_notifiers,
1782 repair_socket: Arc::new(node.sockets.repair),
1783 outstanding_repair_requests,
1784 cluster_slots,
1785 node: Some(node_multihoming),
1786 banking_control_sender,
1787 snapshot_controller,
1788 blockstore: blockstore.clone(),
1789 votor_event_sender,
1790 });
1791
1792 Ok(Self {
1793 log_config: config.log_config.clone(),
1794 exit,
1795 stats_reporter_service,
1796 gossip_service,
1797 serve_repair_service,
1798 json_rpc_service,
1799 pubsub_service,
1800 rpc_completed_slots_service,
1801 optimistically_confirmed_bank_tracker,
1802 transaction_status_service,
1803 entry_notifier_service,
1804 system_monitor_service,
1805 sample_performance_service,
1806 snapshot_packager_service,
1807 completed_data_sets_service,
1808 tpu,
1809 tvu,
1810 poh_service,
1811 block_creation_loop,
1812 poh_recorder,
1813 ip_echo_server,
1814 validator_exit: config.validator_exit.clone(),
1815 cluster_info,
1816 bank_forks,
1817 blockstore,
1818 geyser_plugin_service,
1819 _contact_info_notifier: contact_info_notifier,
1820 blockstore_metric_report_service,
1821 accounts_background_service,
1822 xdp_transmitter,
1823 _tpu_client_next_runtime: tpu_client_next_runtime,
1824 })
1825 }
1826
1827 pub fn register_logrotate_signal_handler() -> Result<Arc<AtomicBool>> {
1831 let flag = Arc::new(AtomicBool::new(false));
1832 #[cfg(unix)]
1833 {
1834 signal_hook::flag::register(libc::SIGUSR1, flag.clone())?;
1835 }
1836 Ok(flag)
1837 }
1838
1839 pub fn listen_for_signals(&self) -> Result<()> {
1841 info!("Validator::listen_for_signals() has started");
1842 loop {
1843 if self.exit.load(Ordering::Relaxed) {
1844 break;
1845 }
1846
1847 #[cfg(unix)]
1848 if let Some(ValidatorLogConfig {
1849 logfile,
1850 logrotate_flag,
1851 }) = self.log_config.as_ref()
1852 && logrotate_flag.load(Ordering::Relaxed)
1853 {
1854 info!("Received SIGUSR1, reopening {}", logfile.display());
1855 agave_logger::redirect_stderr(logfile);
1856 logrotate_flag.store(false, Ordering::Relaxed);
1860 }
1861
1862 thread::sleep(Duration::from_secs(1));
1865 }
1866 info!("Validator::listen_for_signals() has stopped");
1867
1868 Ok(())
1869 }
1870
1871 pub fn exit(&mut self) {
1873 self.validator_exit.write().unwrap().exit();
1874
1875 self.blockstore.drop_signal();
1877 }
1878
1879 pub fn close(mut self) {
1880 self.exit();
1881 self.join();
1882 }
1883
1884 fn print_node_info(node: &Node) {
1885 info!("{:?}", node.info);
1886 info!(
1887 "local gossip address: {}",
1888 node.sockets.gossip[0].local_addr().unwrap()
1889 );
1890 info!(
1891 "local broadcast address: {}",
1892 node.sockets
1893 .broadcast
1894 .first()
1895 .unwrap()
1896 .local_addr()
1897 .unwrap()
1898 );
1899 info!(
1900 "local repair address: {}",
1901 node.sockets.repair.local_addr().unwrap()
1902 );
1903 info!(
1904 "local retransmit address: {}",
1905 node.sockets.retransmit_sockets[0].local_addr().unwrap()
1906 );
1907 info!(
1908 "local alpenglow votor server address: {}",
1909 node.sockets.votor_server[0].local_addr().unwrap()
1910 );
1911 }
1912
1913 pub fn join(self) {
1914 drop(self.bank_forks);
1915 drop(self.cluster_info);
1916
1917 self.poh_service.join().expect("poh_service");
1918 self.block_creation_loop
1919 .join()
1920 .expect("block_creation_loop");
1921 drop(self.poh_recorder);
1922
1923 if let Some(json_rpc_service) = self.json_rpc_service {
1924 json_rpc_service.join().expect("rpc_service");
1925 }
1926
1927 if let Some(pubsub_service) = self.pubsub_service {
1928 pubsub_service.join().expect("pubsub_service");
1929 }
1930
1931 if let Some(rpc_completed_slots_service) = self.rpc_completed_slots_service {
1932 rpc_completed_slots_service
1933 .join()
1934 .expect("rpc_completed_slots_service");
1935 }
1936
1937 if let Some(optimistically_confirmed_bank_tracker) =
1938 self.optimistically_confirmed_bank_tracker
1939 {
1940 optimistically_confirmed_bank_tracker
1941 .join()
1942 .expect("optimistically_confirmed_bank_tracker");
1943 }
1944
1945 if let Some(transaction_status_service) = self.transaction_status_service {
1946 transaction_status_service
1947 .join()
1948 .expect("transaction_status_service");
1949 }
1950
1951 if let Some(system_monitor_service) = self.system_monitor_service {
1952 system_monitor_service
1953 .join()
1954 .expect("system_monitor_service");
1955 }
1956
1957 if let Some(sample_performance_service) = self.sample_performance_service {
1958 sample_performance_service
1959 .join()
1960 .expect("sample_performance_service");
1961 }
1962
1963 if let Some(entry_notifier_service) = self.entry_notifier_service {
1964 entry_notifier_service
1965 .join()
1966 .expect("entry_notifier_service");
1967 }
1968
1969 self.snapshot_packager_service
1970 .join()
1971 .expect("snapshot_packager_service");
1972
1973 self.gossip_service.join().expect("gossip_service");
1974 self.serve_repair_service
1975 .join()
1976 .expect("serve_repair_service");
1977 self.stats_reporter_service
1978 .join()
1979 .expect("stats_reporter_service");
1980 self.blockstore_metric_report_service
1981 .join()
1982 .expect("ledger_metric_report_service");
1983 self.accounts_background_service
1984 .join()
1985 .expect("accounts_background_service");
1986 if let Some(xdp_transmitter) = self.xdp_transmitter {
1987 xdp_transmitter.join().expect("xdp_transmitter");
1988 }
1989 self.tpu.join().expect("tpu");
1990 self.tvu.join().expect("tvu");
1991 if let Some(completed_data_sets_service) = self.completed_data_sets_service {
1992 completed_data_sets_service
1993 .join()
1994 .expect("completed_data_sets_service");
1995 }
1996 if let Some(ip_echo_server) = self.ip_echo_server {
1997 ip_echo_server.shutdown_background();
1998 }
1999
2000 if let Some(geyser_plugin_service) = self.geyser_plugin_service {
2001 geyser_plugin_service.join().expect("geyser_plugin_service");
2002 }
2003 }
2004}
2005
2006fn active_vote_account_exists_in_bank(bank: &Bank, vote_account: &Pubkey) -> bool {
2007 if let Some(account) = &bank.get_account(vote_account)
2008 && let Ok(vote_state) = VoteStateV4::deserialize(account.data(), vote_account)
2009 {
2010 return !vote_state.votes.is_empty();
2011 }
2012 false
2013}
2014
2015pub fn should_require_vote_history_file(
2017 bank: &Bank,
2018 vote_account: &Pubkey,
2019 identity: &Pubkey,
2020) -> bool {
2021 let Some(genesis_certificate) = bank.get_alpenglow_genesis_certificate() else {
2022 return false;
2024 };
2025
2026 let Some(Ok(vote_state)) = bank
2027 .get_account(vote_account)
2028 .map(|acct| acct.deserialize_data())
2029 else {
2030 return false;
2032 };
2033
2034 let Ok(vote_state_handler) = VoteStateHandler::try_new_from_vote_state_versions(vote_state)
2035 else {
2036 return false;
2037 };
2038
2039 if vote_state_handler.node_pubkey() != identity {
2040 return false;
2043 }
2044
2045 let Some(last_voted_slot) = vote_state_handler.last_voted_slot() else {
2046 return false;
2048 };
2049 let genesis_slot = genesis_certificate.block.slot;
2050
2051 last_voted_slot > genesis_slot
2053}
2054
2055fn restore_vote_history(
2056 config: &ValidatorConfig,
2057 bank_forks: &RwLock<BankForks>,
2058 identity: &Pubkey,
2059 vote_account: &Pubkey,
2060) -> Result<VoteHistory, String> {
2061 match VoteHistory::restore(config.vote_history_storage.as_ref(), identity) {
2062 Ok(vote_history) => Ok(vote_history),
2063 Err(err) => {
2064 let should_require_vote_history = {
2065 let bank_forks = bank_forks.read().unwrap();
2066 should_require_vote_history_file(&bank_forks.working_bank(), vote_account, identity)
2067 };
2068 if config.require_vote_history && should_require_vote_history {
2069 return Err(format!(
2070 "Unable to retrieve vote history for identity {identity}. The vote account \
2071 {vote_account} has prior Alpenglow votes. If this is intentional, use \
2072 --do-not-require-vote-history: {err:?}"
2073 )
2074 .to_string());
2075 }
2076 if err.is_file_missing() && !should_require_vote_history {
2077 info!(
2078 "Ignoring expected failed vote history restore because this vote account has \
2079 not voted before"
2080 );
2081 } else {
2082 warn!("Unable to retrieve vote history: {err:?} creating default vote history...");
2083 }
2084 Ok(VoteHistory::new(*identity, 0))
2085 }
2086 }
2087}
2088
2089fn check_poh_speed(bank: &Bank, maybe_hash_samples: Option<u64>) -> Result<(), ValidatorError> {
2090 let Some(hashes_per_tick) = bank.hashes_per_tick() else {
2091 warn!("Unable to read hashes per tick from Bank, skipping PoH speed check");
2092 return Ok(());
2093 };
2094
2095 let ticks_per_slot = bank.ticks_per_slot();
2096 let hashes_per_slot = hashes_per_tick * ticks_per_slot;
2097 let hash_samples = maybe_hash_samples.unwrap_or(hashes_per_slot);
2098
2099 let hash_time = compute_hash_time(hash_samples);
2100 let my_hashes_per_second = (hash_samples as f64 / hash_time.as_secs_f64()) as u64;
2101
2102 let target_slot_duration = Duration::from_nanos(bank.ns_per_slot as u64);
2103 let target_hashes_per_second =
2104 (hashes_per_slot as f64 / target_slot_duration.as_secs_f64()) as u64;
2105
2106 info!(
2107 "PoH speed check: computed hashes per second {my_hashes_per_second}, target hashes per \
2108 second {target_hashes_per_second}"
2109 );
2110 if my_hashes_per_second < target_hashes_per_second {
2111 return Err(ValidatorError::PohTooSlow {
2112 mine: my_hashes_per_second,
2113 target: target_hashes_per_second,
2114 });
2115 }
2116
2117 Ok(())
2118}
2119
2120fn maybe_cluster_restart_with_hard_fork(config: &ValidatorConfig, root_slot: Slot) -> Option<Slot> {
2121 if let Some(wait_slot_for_supermajority) = config.wait_for_supermajority
2123 && wait_slot_for_supermajority == root_slot
2124 {
2125 return Some(wait_slot_for_supermajority);
2126 }
2127
2128 None
2129}
2130
2131fn post_process_restored_tower(
2132 restored_tower: crate::consensus::Result<Tower>,
2133 validator_identity: &Pubkey,
2134 vote_account: &Pubkey,
2135 config: &ValidatorConfig,
2136 bank_forks: &BankForks,
2137) -> Result<Tower, String> {
2138 let mut should_require_tower = config.require_tower;
2139
2140 let restored_tower = restored_tower.and_then(|tower| {
2141 let root_bank = bank_forks.root_bank();
2142 let slot_history = root_bank
2143 .get_slot_history()
2144 .expect("slot history must exist");
2145 let tower = tower.adjust_lockouts_after_replay(root_bank.slot(), &slot_history);
2147
2148 if let Some(hard_fork_restart_slot) =
2149 maybe_cluster_restart_with_hard_fork(config, root_bank.slot())
2150 {
2151 let message =
2155 format!("Hard fork is detected; discarding tower restoration result: {tower:?}");
2156 datapoint_error!("tower_error", ("error", message, String),);
2157 error!("{message}");
2158
2159 should_require_tower = false;
2162 return Err(crate::consensus::TowerError::HardFork(
2163 hard_fork_restart_slot,
2164 ));
2165 }
2166
2167 if let Some(warp_slot) = config.warp_slot {
2168 should_require_tower = false;
2171 return Err(crate::consensus::TowerError::HardFork(warp_slot));
2172 }
2173
2174 tower
2175 });
2176
2177 let restored_tower = match restored_tower {
2178 Ok(tower) => tower,
2179 Err(err) => {
2180 let voting_has_been_active =
2181 active_vote_account_exists_in_bank(&bank_forks.working_bank(), vote_account);
2182 if !err.is_file_missing() {
2183 datapoint_error!(
2184 "tower_error",
2185 ("error", format!("Unable to restore tower: {err}"), String),
2186 );
2187 }
2188 if should_require_tower && voting_has_been_active {
2189 return Err(format!(
2190 "Requested mandatory tower restore failed: {err}. And there is an existing \
2191 vote_account containing actual votes. Aborting due to possible conflicting \
2192 duplicate votes"
2193 ));
2194 }
2195 if err.is_file_missing() && !voting_has_been_active {
2196 info!(
2198 "Ignoring expected failed tower restore because this is the initial validator \
2199 start with the vote account..."
2200 );
2201 } else {
2202 error!(
2203 "Rebuilding a new tower from the latest vote account due to failed tower \
2204 restore: {err}"
2205 );
2206 }
2207
2208 Tower::new_from_bankforks(bank_forks, validator_identity, vote_account)
2209 }
2210 };
2211
2212 Ok(restored_tower)
2213}
2214
2215fn post_process_restored_vote_history(
2216 mut vote_history: VoteHistory,
2217 validator_identity: &Pubkey,
2218 config: &ValidatorConfig,
2219 bank_forks: &BankForks,
2220) -> Result<VoteHistory, String> {
2221 let mut should_require_vote_history = config.require_vote_history;
2222
2223 let restored_vote_history = {
2224 let root_bank = bank_forks.root_bank();
2225
2226 if vote_history.root() < root_bank.slot() {
2227 vote_history.set_root(root_bank.slot());
2229 }
2230
2231 if let Some(hard_fork_restart_slot) =
2232 maybe_cluster_restart_with_hard_fork(config, root_bank.slot())
2233 {
2234 let message = format!(
2238 "Hard fork is detected; discarding vote_history restoration result: \
2239 {vote_history:?}"
2240 );
2241 datapoint_error!("vote_history_error", ("error", message, String),);
2242 error!("{message}");
2243
2244 should_require_vote_history = false;
2246 Err(VoteHistoryError::HardFork(hard_fork_restart_slot))
2247 } else if let Some(warp_slot) = config.warp_slot {
2248 should_require_vote_history = false;
2250 Err(VoteHistoryError::HardFork(warp_slot))
2251 } else {
2252 Ok(vote_history)
2253 }
2254 };
2255
2256 let restored_vote_history = match restored_vote_history {
2257 Ok(vote_history) => vote_history,
2258 Err(err) => {
2259 if !err.is_file_missing() {
2260 datapoint_error!(
2261 "vote_history_error",
2262 (
2263 "error",
2264 format!("Unable to restore vote_history: {err}"),
2265 String
2266 ),
2267 );
2268 }
2269 if should_require_vote_history {
2270 return Err(format!(
2271 "Requested mandatory vote_history restore failed: {err}. Ensure that the vote \
2272 history storage file has been copied to the correct directory. Aborting"
2273 ));
2274 }
2275 error!("Rebuilding an empty vote_history from root slot due to failed restore: {err}");
2276
2277 VoteHistory::new(*validator_identity, bank_forks.root())
2278 }
2279 };
2280
2281 Ok(restored_vote_history)
2282}
2283
2284fn load_genesis(
2285 config: &ValidatorConfig,
2286 ledger_path: &Path,
2287) -> Result<GenesisConfig, ValidatorError> {
2288 let genesis_config = open_genesis_config(ledger_path, config.max_genesis_archive_unpacked_size)
2289 .map_err(ValidatorError::OpenGenesisConfig)?;
2290
2291 let leader_schedule_slot_offset = genesis_config.epoch_schedule.leader_schedule_slot_offset;
2294 let slots_per_epoch = genesis_config.epoch_schedule.slots_per_epoch;
2295 let leader_epoch_offset = leader_schedule_slot_offset.div_ceil(slots_per_epoch);
2296 assert!(leader_epoch_offset <= MAX_LEADER_SCHEDULE_EPOCH_OFFSET);
2297
2298 let genesis_hash = genesis_config.hash();
2299 info!("genesis hash: {genesis_hash}");
2300
2301 if let Some(expected_genesis_hash) = config.expected_genesis_hash
2302 && genesis_hash != expected_genesis_hash
2303 {
2304 return Err(ValidatorError::GenesisHashMismatch(
2305 genesis_hash,
2306 expected_genesis_hash,
2307 ));
2308 }
2309
2310 Ok(genesis_config)
2311}
2312
2313#[allow(clippy::type_complexity)]
2314fn load_blockstore(
2315 config: &ValidatorConfig,
2316 ledger_path: &Path,
2317 genesis_config: &GenesisConfig,
2318 exit: Arc<AtomicBool>,
2319 start_progress: &Arc<RwLock<ValidatorStartProgress>>,
2320 accounts_update_notifier: Option<AccountsUpdateNotifier>,
2321 transaction_notifier: Option<TransactionNotifierArc>,
2322 entry_notifier: Option<EntryNotifierArc>,
2323 dependency_tracker: Option<Arc<DependencyTracker>>,
2324) -> Result<
2325 (
2326 Arc<RwLock<BankForks>>,
2327 Arc<Blockstore>,
2328 Slot,
2329 Receiver<bool>,
2330 UpdateParentReceiver,
2331 LeaderScheduleCache,
2332 Option<StartingSnapshotHashes>,
2333 TransactionHistoryServices,
2334 blockstore_processor::ProcessOptions,
2335 BlockstoreRootScan,
2336 DroppedSlotsReceiver,
2337 Option<EntryNotifierService>,
2338 ),
2339 String,
2340> {
2341 info!("loading ledger from {ledger_path:?}...");
2342 *start_progress.write().unwrap() = ValidatorStartProgress::LoadingLedger;
2343
2344 let mut process_options = blockstore_processor::ProcessOptions {
2345 run_verification: config.run_verification,
2346 halt_at_slot: None,
2347 new_hard_forks: config.new_hard_forks.clone(),
2348 debug_keys: config.debug_keys.clone(),
2349 accounts_db_config: config.accounts_db_config.clone(),
2350 accounts_db_skip_shrink: config.accounts_db_skip_shrink,
2351 accounts_db_force_initial_clean: config.accounts_db_force_initial_clean,
2352 runtime_config: config.runtime_config.clone(),
2353 use_snapshot_archives_at_startup: config.use_snapshot_archives_at_startup,
2354 ..blockstore_processor::ProcessOptions::default()
2355 };
2356
2357 let (blockstore, bank_from_snapshot_opt) = thread::scope(|scope| {
2358 let load_snapshot_handle = thread::Builder::new()
2359 .name("solBnkFrkSnap".into())
2360 .spawn_scoped(scope, || {
2361 bank_forks_utils::try_load_bank_forks_from_snapshot(
2362 genesis_config,
2363 &config.account_paths,
2364 &config.snapshot_config,
2365 &process_options,
2366 accounts_update_notifier.clone(),
2367 exit.clone(),
2368 )
2369 })
2370 .expect("should spawn thread");
2371 let blockstore =
2372 Blockstore::open_with_options(ledger_path, config.blockstore_options.clone())
2373 .map_err(|err| format!("Failed to open Blockstore: {err:?}"))?;
2374 let bank_from_snapshot_result = load_snapshot_handle.join().expect("join thread");
2375
2376 Ok::<_, String>((Arc::new(blockstore), bank_from_snapshot_result.transpose()))
2377 })?;
2378
2379 let original_blockstore_root = blockstore.max_root();
2382 process_options.halt_at_slot = blockstore.highest_slot().unwrap_or(None);
2383
2384 let enable_rpc_transaction_history =
2385 config.rpc_addrs.is_some() && config.rpc_config.enable_rpc_transaction_history;
2386 let is_plugin_transaction_history_required = transaction_notifier.as_ref().is_some();
2387 let transaction_history_services =
2388 if enable_rpc_transaction_history || is_plugin_transaction_history_required {
2389 initialize_rpc_transaction_history_services(
2390 blockstore.clone(),
2391 exit.clone(),
2392 enable_rpc_transaction_history,
2393 config.rpc_config.enable_extended_tx_metadata_storage,
2394 transaction_notifier,
2395 dependency_tracker,
2396 )
2397 } else {
2398 TransactionHistoryServices::default()
2399 };
2400
2401 let entry_notifier_service = entry_notifier
2402 .map(|entry_notifier| EntryNotifierService::new(entry_notifier, exit.clone()));
2403
2404 let (bank_forks, starting_snapshot_hashes) = bank_from_snapshot_opt
2405 .unwrap_or_else(|| {
2406 bank_forks_utils::discard_previous_run_state(
2408 &config.snapshot_config.bank_snapshots_dir,
2409 &config.account_paths,
2410 );
2411 bank_forks_utils::load_bank_forks_from_genesis(
2412 genesis_config,
2413 &blockstore,
2414 config.account_paths.clone(),
2415 &process_options,
2416 transaction_history_services
2417 .transaction_status_sender
2418 .as_ref(),
2419 entry_notifier_service
2420 .as_ref()
2421 .map(|service| service.sender()),
2422 accounts_update_notifier,
2423 exit.clone(),
2424 )
2425 })
2426 .map_err(|err| err.to_string())?;
2427
2428 let mut leader_schedule_cache =
2429 LeaderScheduleCache::new_from_bank(&bank_forks.read().unwrap().root_bank());
2430 leader_schedule_cache.set_fixed_leader_schedule(config.fixed_leader_schedule.clone());
2431
2432 let pruned_banks_receiver =
2438 AccountsBackgroundService::setup_bank_drop_callback(bank_forks.clone());
2439
2440 let blockstore_root_scan = BlockstoreRootScan::new(config, blockstore.clone(), exit);
2441 let (ledger_signal_sender, ledger_signal_receiver) = bounded(MAX_REPLAY_WAKE_UP_SIGNALS);
2442 blockstore.add_new_shred_signal(ledger_signal_sender);
2443 let (update_parent_sender, update_parent_receiver) = bounded(MAX_UPDATE_PARENT_SIGNALS);
2444 blockstore.add_update_parent_signal(update_parent_sender);
2445
2446 Ok((
2447 bank_forks,
2448 blockstore,
2449 original_blockstore_root,
2450 ledger_signal_receiver,
2451 update_parent_receiver,
2452 leader_schedule_cache,
2453 starting_snapshot_hashes,
2454 transaction_history_services,
2455 process_options,
2456 blockstore_root_scan,
2457 pruned_banks_receiver,
2458 entry_notifier_service,
2459 ))
2460}
2461
2462pub struct ProcessBlockStore<'a> {
2463 id: &'a Pubkey,
2464 vote_account: &'a Pubkey,
2465 start_progress: &'a Arc<RwLock<ValidatorStartProgress>>,
2466 blockstore: &'a Blockstore,
2467 original_blockstore_root: Slot,
2468 bank_forks: &'a Arc<RwLock<BankForks>>,
2469 leader_schedule_cache: &'a LeaderScheduleCache,
2470 process_options: &'a blockstore_processor::ProcessOptions,
2471 transaction_status_sender: Option<&'a TransactionStatusSender>,
2472 entry_notification_sender: Option<&'a EntryNotifierSender>,
2473 blockstore_root_scan: Option<BlockstoreRootScan>,
2474 snapshot_controller: &'a SnapshotController,
2475 config: &'a ValidatorConfig,
2476 tower: Option<Tower>,
2477 vote_history: Option<VoteHistory>,
2478 my_shred_version: u16,
2479}
2480
2481impl<'a> ProcessBlockStore<'a> {
2482 #[allow(clippy::too_many_arguments)]
2483 fn new(
2484 id: &'a Pubkey,
2485 vote_account: &'a Pubkey,
2486 start_progress: &'a Arc<RwLock<ValidatorStartProgress>>,
2487 blockstore: &'a Blockstore,
2488 original_blockstore_root: Slot,
2489 bank_forks: &'a Arc<RwLock<BankForks>>,
2490 leader_schedule_cache: &'a LeaderScheduleCache,
2491 process_options: &'a blockstore_processor::ProcessOptions,
2492 transaction_status_sender: Option<&'a TransactionStatusSender>,
2493 entry_notification_sender: Option<&'a EntryNotifierSender>,
2494 blockstore_root_scan: BlockstoreRootScan,
2495 snapshot_controller: &'a SnapshotController,
2496 config: &'a ValidatorConfig,
2497 my_shred_version: u16,
2498 ) -> Self {
2499 Self {
2500 id,
2501 vote_account,
2502 start_progress,
2503 blockstore,
2504 original_blockstore_root,
2505 bank_forks,
2506 leader_schedule_cache,
2507 process_options,
2508 transaction_status_sender,
2509 entry_notification_sender,
2510 blockstore_root_scan: Some(blockstore_root_scan),
2511 snapshot_controller,
2512 config,
2513 tower: None,
2514 vote_history: None,
2515 my_shred_version,
2516 }
2517 }
2518
2519 pub(crate) fn process(&mut self) -> Result<(Tower, VoteHistory), String> {
2520 if let (Some(tower), Some(vote_history)) = (self.tower.as_ref(), self.vote_history.as_ref())
2521 {
2522 return Ok((tower.clone(), vote_history.clone()));
2523 }
2524
2525 let previous_start_process = *self.start_progress.read().unwrap();
2527 *self.start_progress.write().unwrap() = ValidatorStartProgress::LoadingLedger;
2528
2529 let exit = Arc::new(AtomicBool::new(false));
2530 if let Ok(Some(max_slot)) = self.blockstore.highest_slot() {
2531 let bank_forks = self.bank_forks.clone();
2532 let exit = exit.clone();
2533 let start_progress = self.start_progress.clone();
2534
2535 let _ = Builder::new()
2536 .name("solRptLdgrStat".to_string())
2537 .spawn(move || {
2538 while !exit.load(Ordering::Relaxed) {
2539 let slot = bank_forks.read().unwrap().working_bank().slot();
2540 *start_progress.write().unwrap() =
2541 ValidatorStartProgress::ProcessingLedger { slot, max_slot };
2542 thread::sleep(Duration::from_secs(2));
2543 }
2544 })
2545 .unwrap();
2546 }
2547
2548 blockstore_processor::process_blockstore_from_root(
2549 self.blockstore,
2550 self.bank_forks,
2551 self.my_shred_version,
2552 self.leader_schedule_cache,
2553 self.process_options,
2554 self.transaction_status_sender,
2555 self.entry_notification_sender,
2556 Some(self.snapshot_controller),
2557 )
2558 .map_err(|err| {
2559 exit.store(true, Ordering::Relaxed);
2560 format!("Failed to load ledger: {err:?}")
2561 })?;
2562 exit.store(true, Ordering::Relaxed);
2563
2564 if let Some(blockstore_root_scan) = self.blockstore_root_scan.take() {
2565 blockstore_root_scan.join();
2566 }
2567
2568 let tower = {
2570 let restored_tower = Tower::restore(self.config.tower_storage.as_ref(), self.id);
2571 if let Ok(tower) = &restored_tower {
2572 reconcile_blockstore_roots_with_external_source(
2574 ExternalRootSource::Tower(tower.root()),
2575 self.blockstore,
2576 &mut self.original_blockstore_root,
2577 )
2578 .map_err(|err| format!("Failed to reconcile blockstore with tower: {err:?}"))?;
2579 }
2580
2581 post_process_restored_tower(
2582 restored_tower,
2583 self.id,
2584 self.vote_account,
2585 self.config,
2586 &self.bank_forks.read().unwrap(),
2587 )?
2588 };
2589
2590 let vote_history = {
2592 let vote_history =
2593 restore_vote_history(self.config, self.bank_forks, self.id, self.vote_account)?;
2594 reconcile_blockstore_roots_with_external_source(
2596 ExternalRootSource::VoteHistory(vote_history.root()),
2597 self.blockstore,
2598 &mut self.original_blockstore_root,
2599 )
2600 .map_err(|err| format!("Failed to reconcile blockstore with vote history: {err:?}"))?;
2601
2602 post_process_restored_vote_history(
2603 vote_history,
2604 self.id,
2605 self.config,
2606 &self.bank_forks.read().unwrap(),
2607 )?
2608 };
2609
2610 if let Some(hard_fork_restart_slot) = maybe_cluster_restart_with_hard_fork(
2611 self.config,
2612 self.bank_forks.read().unwrap().root(),
2613 ) {
2614 reconcile_blockstore_roots_with_external_source(
2618 ExternalRootSource::HardFork(hard_fork_restart_slot),
2619 self.blockstore,
2620 &mut self.original_blockstore_root,
2621 )
2622 .map_err(|err| format!("Failed to reconcile blockstore with hard fork: {err:?}"))?;
2623 }
2624
2625 *self.start_progress.write().unwrap() = previous_start_process;
2626 self.tower = Some(tower.clone());
2627 self.vote_history = Some(vote_history.clone());
2628 Ok((tower, vote_history))
2629 }
2630}
2631
2632fn maybe_warp_slot(
2634 config: &ValidatorConfig,
2635 process_blockstore: &mut ProcessBlockStore,
2636 ledger_path: &Path,
2637 bank_forks: &RwLock<BankForks>,
2638 leader_schedule_cache: &LeaderScheduleCache,
2639 snapshot_controller: &SnapshotController,
2640) -> Result<(), String> {
2641 if let Some(warp_slot) = config.warp_slot {
2642 let root_bank = {
2643 let bank_forks_r = bank_forks.read().unwrap();
2644 let working_bank = bank_forks_r.working_bank();
2645 if warp_slot <= working_bank.slot() {
2646 return Err(format!(
2647 "warp slot ({}) cannot be less than the working bank slot ({})",
2648 warp_slot,
2649 working_bank.slot()
2650 ));
2651 }
2652 bank_forks_r.root_bank()
2653 };
2654
2655 info!("warping to slot {warp_slot}");
2656
2657 root_bank.squash();
2661 root_bank.force_flush_accounts_cache();
2662
2663 let warp_bank = Bank::warp_from_parent(root_bank, SlotLeader::default(), warp_slot);
2667
2668 let mut bank_forks = bank_forks.write().unwrap();
2669 bank_forks.insert(warp_bank);
2670 let warp_bank = bank_forks.get(warp_slot).unwrap();
2674 Bank::calculate_and_set_block_id_for_dcou(&warp_bank);
2675 bank_forks.set_root(warp_slot, Some(snapshot_controller), Some(warp_slot));
2676 leader_schedule_cache.set_root(&warp_bank);
2677
2678 let snapshot_config = SnapshotConfig {
2679 bank_snapshots_dir: ledger_path.to_path_buf(),
2680 ..config.snapshot_config.clone()
2681 };
2682 let full_snapshot_archive_info = match snapshot_bank_utils::bank_to_full_snapshot_archive(
2683 &snapshot_config,
2684 &warp_bank,
2685 ) {
2686 Ok(archive_info) => archive_info,
2687 Err(e) => return Err(format!("Unable to create snapshot: {e}")),
2688 };
2689 info!(
2690 "created snapshot: {}",
2691 full_snapshot_archive_info.path().display()
2692 );
2693
2694 drop(bank_forks);
2695 process_blockstore.process()?;
2698 }
2699 Ok(())
2700}
2701
2702fn should_cleanup_blockstore_incorrect_shred_versions(
2705 config: &ValidatorConfig,
2706 blockstore: &Blockstore,
2707 root_slot: Slot,
2708 hard_forks: &HardForks,
2709) -> Result<Option<Slot>, BlockstoreError> {
2710 let maybe_cluster_restart_slot = maybe_cluster_restart_with_hard_fork(config, root_slot);
2712 if maybe_cluster_restart_slot.is_some() {
2713 return Ok(Some(root_slot + 1));
2714 }
2715
2716 let Some(latest_hard_fork) = hard_forks.iter().last().map(|(slot, _)| *slot) else {
2718 return Ok(None);
2719 };
2720
2721 let Some(blockstore_max_slot) = blockstore.highest_slot()? else {
2723 return Ok(None);
2724 };
2725 let blockstore_min_slot = blockstore.lowest_slot();
2726 info!(
2727 "Blockstore contains data from slot {blockstore_min_slot} to {blockstore_max_slot}, the \
2728 latest hard fork is {latest_hard_fork}"
2729 );
2730
2731 if latest_hard_fork < blockstore_min_slot {
2732 Ok(None)
2740 } else if latest_hard_fork < blockstore_max_slot {
2741 Ok(Some(latest_hard_fork + 1))
2752 } else {
2753 Ok(None)
2761 }
2762}
2763
2764fn scan_blockstore_for_incorrect_shred_version(
2767 blockstore: &Blockstore,
2768 start_slot: Slot,
2769 expected_shred_version: u16,
2770) -> Result<Option<u16>, BlockstoreError> {
2771 const TIMEOUT: Duration = Duration::from_secs(60);
2772 let timer = Instant::now();
2773 let slot_meta_iterator = blockstore.slot_meta_iterator(start_slot)?;
2775
2776 info!(
2777 "Blockstore search for shreds with incorrect version starting from slot {start_slot}; \
2778 searching for 60s"
2779 );
2780 for (slot, _meta) in slot_meta_iterator {
2781 let shreds = blockstore.get_data_shreds_for_slot(slot, 0)?;
2782 for shred in &shreds {
2783 if shred.version() != expected_shred_version {
2784 info!(
2785 "Blockstore search found shred with incorrect version {} in slot {slot}",
2786 shred.version()
2787 );
2788 return Ok(Some(shred.version()));
2789 }
2790 }
2791 if timer.elapsed() > TIMEOUT {
2792 info!("Blockstore search did not find any shreds with incorrect version");
2793 break;
2794 }
2795 }
2796 Ok(None)
2797}
2798
2799fn cleanup_blockstore_incorrect_shred_versions(
2802 blockstore: &Blockstore,
2803 config: &ValidatorConfig,
2804 start_slot: Slot,
2805 expected_shred_version: u16,
2806) -> Result<(), BlockstoreError> {
2807 let incorrect_shred_version = scan_blockstore_for_incorrect_shred_version(
2808 blockstore,
2809 start_slot,
2810 expected_shred_version,
2811 )?;
2812 let Some(incorrect_shred_version) = incorrect_shred_version else {
2813 info!("Only shreds with the correct version were found in the blockstore");
2814 return Ok(());
2815 };
2816
2817 let end_slot = blockstore.highest_slot()?.unwrap();
2819
2820 let backup_folder = format!(
2823 "{BLOCKSTORE_DIRECTORY_ROCKS_LEVEL}_backup_{incorrect_shred_version}_{start_slot}_{end_slot}"
2824 );
2825 match Blockstore::open_with_options(
2826 &blockstore.ledger_path().join(backup_folder),
2827 config.blockstore_options.clone(),
2828 ) {
2829 Ok(backup_blockstore) => {
2830 info!("Backing up slots from {start_slot} to {end_slot}");
2831 let mut timer = Measure::start("blockstore backup");
2832
2833 const PRINT_INTERVAL: Duration = Duration::from_secs(5);
2834 let mut print_timer = Instant::now();
2835 let mut num_slots_copied = 0;
2836 let slot_meta_iterator = blockstore.slot_meta_iterator(start_slot)?;
2837 for (slot, _meta) in slot_meta_iterator {
2838 let shreds = blockstore.get_data_shreds_for_slot(slot, 0)?;
2839 let shreds = shreds.into_iter().map(Cow::Owned);
2840 let _ = backup_blockstore.insert_cow_shreds(shreds, true);
2841 num_slots_copied += 1;
2842
2843 if print_timer.elapsed() > PRINT_INTERVAL {
2844 info!("Backed up {num_slots_copied} slots thus far");
2845 print_timer = Instant::now();
2846 }
2847 }
2848
2849 timer.stop();
2850 info!("Backing up slots done. {timer}");
2851 }
2852 Err(err) => {
2853 warn!("Unable to backup shreds with incorrect shred version: {err}");
2854 }
2855 }
2856
2857 info!("Purging slots {start_slot} to {end_slot} from blockstore");
2858 let mut timer = Measure::start("blockstore purge");
2859 blockstore.purge_slots_cleanup_chaining(start_slot, end_slot, PurgeType::Exact)?;
2860 timer.stop();
2861 info!("Purging slots done. {timer}");
2862
2863 Ok(())
2864}
2865
2866fn initialize_rpc_transaction_history_services(
2867 blockstore: Arc<Blockstore>,
2868 exit: Arc<AtomicBool>,
2869 enable_rpc_transaction_history: bool,
2870 enable_extended_tx_metadata_storage: bool,
2871 transaction_notifier: Option<TransactionNotifierArc>,
2872 dependency_tracker: Option<Arc<DependencyTracker>>,
2873) -> TransactionHistoryServices {
2874 let max_complete_transaction_status_slot = Arc::new(AtomicU64::new(blockstore.max_root()));
2875 let (transaction_status_sender, transaction_status_receiver) = unbounded();
2876 let transaction_status_sender = Some(TransactionStatusSender {
2877 sender: transaction_status_sender,
2878 dependency_tracker: dependency_tracker.clone(),
2879 });
2880 let transaction_status_service = Some(TransactionStatusService::new(
2881 transaction_status_receiver,
2882 max_complete_transaction_status_slot.clone(),
2883 enable_rpc_transaction_history,
2884 transaction_notifier,
2885 blockstore,
2886 enable_extended_tx_metadata_storage,
2887 dependency_tracker,
2888 exit,
2889 ));
2890
2891 TransactionHistoryServices {
2892 transaction_status_sender,
2893 transaction_status_service,
2894 max_complete_transaction_status_slot,
2895 }
2896}
2897
2898#[derive(Error, Debug)]
2899pub enum ValidatorError {
2900 #[error("bank hash mismatch: actual={0}, expected={1}")]
2901 BankHashMismatch(Hash, Hash),
2902
2903 #[error("blockstore error: {0}")]
2904 Blockstore(#[source] BlockstoreError),
2905
2906 #[error("genesis hash mismatch: actual={0}, expected={1}")]
2907 GenesisHashMismatch(Hash, Hash),
2908
2909 #[error(
2910 "ledger does not have enough data to wait for supermajority: current slot={0}, needed \
2911 slot={1}"
2912 )]
2913 NotEnoughLedgerData(Slot, Slot),
2914
2915 #[error("failed to open genesis: {0}")]
2916 OpenGenesisConfig(#[source] OpenGenesisConfigError),
2917
2918 #[error("{0}")]
2919 Other(String),
2920
2921 #[error(
2922 "PoH hashes/second rate is slower than the cluster target: mine {mine}, cluster {target}"
2923 )]
2924 PohTooSlow { mine: u64, target: u64 },
2925
2926 #[error(transparent)]
2927 ResourceLimitError(#[from] ResourceLimitError),
2928
2929 #[error("shred version mismatch: actual {actual}, expected {expected}")]
2930 ShredVersionMismatch { actual: u16, expected: u16 },
2931
2932 #[error(transparent)]
2933 TraceError(#[from] TraceError),
2934}
2935
2936fn wait_for_supermajority(
2943 config: &ValidatorConfig,
2944 process_blockstore: Option<&mut ProcessBlockStore>,
2945 bank_forks: &RwLock<BankForks>,
2946 cluster_info: &ClusterInfo,
2947 rpc_override_health_check: Arc<AtomicBool>,
2948 start_progress: &Arc<RwLock<ValidatorStartProgress>>,
2949) -> Result<bool, ValidatorError> {
2950 match config.wait_for_supermajority {
2951 None => Ok(false),
2952 Some(wait_for_supermajority_slot) => {
2953 if let Some(process_blockstore) = process_blockstore {
2954 process_blockstore
2955 .process()
2956 .map_err(ValidatorError::Other)?;
2957 }
2958
2959 let bank = bank_forks.read().unwrap().working_bank();
2960 match wait_for_supermajority_slot.cmp(&bank.slot()) {
2961 std::cmp::Ordering::Less => return Ok(false),
2962 std::cmp::Ordering::Greater => {
2963 return Err(ValidatorError::NotEnoughLedgerData(
2964 bank.slot(),
2965 wait_for_supermajority_slot,
2966 ));
2967 }
2968 _ => {}
2969 }
2970
2971 if let Some(expected_bank_hash) = config.expected_bank_hash
2972 && bank.hash() != expected_bank_hash
2973 {
2974 return Err(ValidatorError::BankHashMismatch(
2975 bank.hash(),
2976 expected_bank_hash,
2977 ));
2978 }
2979
2980 for i in 1.. {
2981 let logging = i % 10 == 1;
2982 if logging {
2983 info!(
2984 "Waiting for {}% of activated stake at slot {} to be in gossip...",
2985 WAIT_FOR_SUPERMAJORITY_THRESHOLD_PERCENT,
2986 bank.slot()
2987 );
2988 }
2989
2990 let gossip_stake_percent =
2991 get_stake_percent_in_gossip(&bank, cluster_info, logging);
2992
2993 *start_progress.write().unwrap() =
2994 ValidatorStartProgress::WaitingForSupermajority {
2995 slot: wait_for_supermajority_slot,
2996 gossip_stake_percent,
2997 };
2998
2999 if gossip_stake_percent >= WAIT_FOR_SUPERMAJORITY_THRESHOLD_PERCENT {
3000 info!(
3001 "Supermajority reached, {gossip_stake_percent}% active stake detected, \
3002 starting up now.",
3003 );
3004 break;
3005 }
3006 rpc_override_health_check.store(true, Ordering::Relaxed);
3010 thread::sleep(Duration::new(1, 0));
3011 }
3012 rpc_override_health_check.store(false, Ordering::Relaxed);
3013 Ok(true)
3014 }
3015 }
3016}
3017
3018fn get_stake_percent_in_gossip(bank: &Bank, cluster_info: &ClusterInfo, log: bool) -> u64 {
3020 let mut online_stake = 0;
3021 let mut offline_stake = 0;
3022 let mut offline_nodes = vec![];
3023
3024 let mut total_activated_stake = 0;
3025 let now = timestamp();
3026 let peers: HashMap<_, _> = cluster_info
3030 .tvu_peers(ContactInfo::clone)
3031 .into_iter()
3032 .filter(|node| {
3033 let age = now.saturating_sub(node.wallclock());
3034 age < CRDS_GOSSIP_PULL_CRDS_TIMEOUT_MS
3036 })
3037 .map(|node| (*node.pubkey(), node))
3038 .collect();
3039 let my_id = cluster_info.id();
3040
3041 for (activated_stake, vote_account) in bank.vote_accounts().values() {
3042 let activated_stake = *activated_stake;
3043 total_activated_stake += activated_stake;
3044
3045 if activated_stake == 0 {
3046 continue;
3047 }
3048 let vote_state_node_pubkey = *vote_account.node_pubkey();
3049
3050 if peers.contains_key(&vote_state_node_pubkey) {
3051 trace!(
3052 "observed {vote_state_node_pubkey} in gossip, (activated_stake={activated_stake})"
3053 );
3054 online_stake += activated_stake;
3055 } else if vote_state_node_pubkey == my_id {
3056 online_stake += activated_stake; } else {
3058 offline_stake += activated_stake;
3059 offline_nodes.push((activated_stake, vote_state_node_pubkey));
3060 }
3061 }
3062
3063 let online_stake_percentage = (online_stake as f64 / total_activated_stake as f64) * 100.;
3064 if log {
3065 info!("{online_stake_percentage:.3}% of active stake visible in gossip");
3066
3067 if !offline_nodes.is_empty() {
3068 info!(
3069 "{:.3}% of active stake is not visible in gossip",
3070 (offline_stake as f64 / total_activated_stake as f64) * 100.
3071 );
3072 offline_nodes.sort_by_key(|a| cmp::Reverse(a.0)); for (stake, identity) in offline_nodes {
3074 info!(
3075 " {:.3}% - {}",
3076 (stake as f64 / total_activated_stake as f64) * 100.,
3077 identity
3078 );
3079 }
3080 }
3081 datapoint_info!(
3082 "wfsm_gossip",
3083 ("online_stake", online_stake, i64),
3084 ("offline_stake", offline_stake, i64),
3085 ("total_activated_stake", total_activated_stake, i64),
3086 );
3087 }
3088
3089 online_stake_percentage as u64
3090}
3091
3092fn validate_account_paths(config: &ValidatorConfig) -> std::io::Result<()> {
3093 validate_account_paths_for_direct_io(
3094 config.snapshot_config.use_direct_io,
3095 config
3096 .account_paths
3097 .iter()
3098 .chain(&config.account_snapshot_paths)
3099 .chain([
3100 &config.snapshot_config.full_snapshot_archives_dir,
3101 &config.snapshot_config.incremental_snapshot_archives_dir,
3102 &config.snapshot_config.bank_snapshots_dir,
3103 ]),
3104 )
3105}
3106
3107pub fn is_snapshot_config_valid(snapshot_config: &SnapshotConfig) -> bool {
3108 if !snapshot_config.should_generate_snapshots() {
3110 return true;
3111 }
3112
3113 let SnapshotInterval::Slots(full_snapshot_interval_slots) =
3114 snapshot_config.full_snapshot_archive_interval
3115 else {
3116 return false;
3118 };
3119
3120 match snapshot_config.incremental_snapshot_archive_interval {
3121 SnapshotInterval::Disabled => true,
3122 SnapshotInterval::Slots(incremental_snapshot_interval_slots) => {
3123 full_snapshot_interval_slots > incremental_snapshot_interval_slots
3124 }
3125 }
3126}
3127
3128#[cfg(test)]
3129mod tests {
3130 use {
3131 super::*,
3132 agave_votor_messages::certificate::{CertSignature, GenesisCert},
3133 crossbeam_channel::{RecvTimeoutError, bounded},
3134 solana_entry::entry,
3135 solana_genesis_config::create_genesis_config,
3136 solana_gossip::contact_info::ContactInfo,
3137 solana_leader_schedule::SlotLeader,
3138 solana_ledger::{
3139 blockstore, create_new_tmp_ledger, genesis_utils::create_genesis_config_with_leader,
3140 get_tmp_ledger_path_auto_delete,
3141 },
3142 solana_poh_config::PohConfig,
3143 solana_sha256_hasher::hash,
3144 solana_vote_program::vote_state::{LandedVote, Lockout, VoteStateVersions},
3145 std::{fs::remove_dir_all, num::NonZeroU64, thread, time::Duration},
3146 };
3147
3148 #[test]
3149 fn test_should_require_vote_history_file() {
3150 use {
3151 agave_votor_messages::consensus_message::Block,
3152 solana_account::{AccountSharedData, state_traits::StateMut},
3153 solana_bls_signatures::{BLS_SIGNATURE_AFFINE_SIZE, Signature as BLSSignature},
3154 };
3155
3156 let genesis_config = create_genesis_config(1_000_000).0;
3157 let bank = Bank::new_for_tests(&genesis_config);
3158 let vote_account_pubkey = Pubkey::new_unique();
3159 let identity = Pubkey::new_unique();
3160
3161 assert!(!active_vote_account_exists_in_bank(
3162 &bank,
3163 &vote_account_pubkey
3164 ));
3165 assert!(!should_require_vote_history_file(
3166 &bank,
3167 &vote_account_pubkey,
3168 &identity,
3169 ));
3170
3171 let mut vote_state = VoteStateV4 {
3172 node_pubkey: identity,
3173 ..VoteStateV4::default()
3174 };
3175 let mut vote_account =
3176 AccountSharedData::new(1, VoteStateV4::size_of(), &solana_vote_program::id());
3177 vote_account
3178 .set_state(&VoteStateVersions::new_v4(vote_state.clone()))
3179 .unwrap();
3180 bank.store_account(&vote_account_pubkey, &vote_account);
3181 assert!(!active_vote_account_exists_in_bank(
3182 &bank,
3183 &vote_account_pubkey
3184 ));
3185 assert!(!should_require_vote_history_file(
3186 &bank,
3187 &vote_account_pubkey,
3188 &identity,
3189 ));
3190
3191 vote_state.votes.push_back(LandedVote {
3192 latency: 0,
3193 lockout: Lockout::new(7),
3194 });
3195 vote_account
3196 .set_state(&VoteStateVersions::new_v4(vote_state.clone()))
3197 .unwrap();
3198 bank.store_account(&vote_account_pubkey, &vote_account);
3199 assert!(active_vote_account_exists_in_bank(
3200 &bank,
3201 &vote_account_pubkey
3202 ));
3203 assert!(!should_require_vote_history_file(
3204 &bank,
3205 &vote_account_pubkey,
3206 &identity,
3207 ));
3208
3209 let cert = GenesisCert {
3210 block: Block {
3211 slot: 40,
3212 block_id: Hash::new_unique(),
3213 },
3214 signature: CertSignature {
3215 signature: BLSSignature([0; BLS_SIGNATURE_AFFINE_SIZE]),
3216 bitmap: vec![],
3217 },
3218 };
3219 bank.set_alpenglow_genesis_certificate(&cert);
3220 assert!(!should_require_vote_history_file(
3221 &bank,
3222 &vote_account_pubkey,
3223 &identity,
3224 ));
3225
3226 vote_state.votes.push_back(LandedVote {
3227 latency: 0,
3228 lockout: Lockout::new(43),
3229 });
3230 vote_state.root_slot = Some(42);
3231 vote_account
3232 .set_state(&VoteStateVersions::new_v4(vote_state))
3233 .unwrap();
3234 bank.store_account(&vote_account_pubkey, &vote_account);
3235 assert!(should_require_vote_history_file(
3236 &bank,
3237 &vote_account_pubkey,
3238 &identity,
3239 ));
3240
3241 assert!(!should_require_vote_history_file(
3243 &bank,
3244 &vote_account_pubkey,
3245 &Pubkey::new_unique(),
3246 ));
3247 }
3248
3249 #[test]
3250 fn validator_exit() {
3251 agave_logger::setup();
3252 let leader_keypair = Keypair::new();
3253 let leader_node = Node::new_localhost_with_pubkey(&leader_keypair.pubkey());
3254
3255 let validator_keypair = Keypair::new();
3256 let validator_node = Node::new_localhost_with_pubkey(&validator_keypair.pubkey());
3257 let genesis_config =
3258 create_genesis_config_with_leader(10_000, &leader_keypair.pubkey(), 1000)
3259 .genesis_config;
3260 let (validator_ledger_path, _blockhash) = create_new_tmp_ledger!(&genesis_config);
3261
3262 let voting_keypair = Arc::new(Keypair::new());
3263 let config = ValidatorConfig {
3264 rpc_addrs: Some((
3265 validator_node.info.rpc().unwrap(),
3266 validator_node.info.rpc_pubsub().unwrap(),
3267 )),
3268 ..ValidatorConfig::default_for_test()
3269 };
3270 let start_progress = Arc::new(RwLock::new(ValidatorStartProgress::default()));
3271 let validator = Validator::new(
3272 validator_node,
3273 Arc::new(validator_keypair),
3274 &validator_ledger_path,
3275 &voting_keypair.pubkey(),
3276 Arc::new(RwLock::new(vec![voting_keypair])),
3277 vec![leader_node.info],
3278 &config,
3279 None, start_progress.clone(),
3281 SocketAddrSpace::Unspecified,
3282 ValidatorTpuConfig::new_for_tests(),
3283 Arc::new(RwLock::new(None)),
3284 None,
3285 )
3286 .expect("assume successful validator start");
3287 assert_eq!(
3288 *start_progress.read().unwrap(),
3289 ValidatorStartProgress::Running
3290 );
3291 validator.close();
3292 remove_dir_all(validator_ledger_path).unwrap();
3293 }
3294
3295 #[test]
3296 fn test_should_cleanup_blockstore_incorrect_shred_versions() {
3297 agave_logger::setup();
3298
3299 let ledger_path = get_tmp_ledger_path_auto_delete!();
3300 let blockstore = Blockstore::open(ledger_path.path()).unwrap();
3301
3302 let mut validator_config = ValidatorConfig::default_for_test();
3303 let mut hard_forks = HardForks::default();
3304 let mut root_slot;
3305
3306 root_slot = 10;
3308 validator_config.wait_for_supermajority = Some(root_slot);
3309 assert_eq!(
3310 should_cleanup_blockstore_incorrect_shred_versions(
3311 &validator_config,
3312 &blockstore,
3313 root_slot,
3314 &hard_forks
3315 )
3316 .unwrap(),
3317 Some(root_slot + 1)
3318 );
3319
3320 root_slot = 15;
3323 assert_eq!(
3324 should_cleanup_blockstore_incorrect_shred_versions(
3325 &validator_config,
3326 &blockstore,
3327 root_slot,
3328 &hard_forks
3329 )
3330 .unwrap(),
3331 None,
3332 );
3333
3334 hard_forks.register(10);
3337 assert_eq!(
3338 should_cleanup_blockstore_incorrect_shred_versions(
3339 &validator_config,
3340 &blockstore,
3341 root_slot,
3342 &hard_forks
3343 )
3344 .unwrap(),
3345 None,
3346 );
3347
3348 let entries = entry::create_ticks(1, 0, Hash::default());
3350 for i in 20..35 {
3351 let shreds = blockstore::entries_to_test_shreds(
3352 &entries,
3353 i, i - 1, true, 1, );
3358 blockstore.insert_shreds(shreds, true).unwrap();
3359 }
3360
3361 assert_eq!(
3363 should_cleanup_blockstore_incorrect_shred_versions(
3364 &validator_config,
3365 &blockstore,
3366 root_slot,
3367 &hard_forks
3368 )
3369 .unwrap(),
3370 None,
3371 );
3372
3373 root_slot = 25;
3376 hard_forks.register(root_slot);
3377 validator_config.wait_for_supermajority = Some(root_slot);
3378 assert_eq!(
3379 should_cleanup_blockstore_incorrect_shred_versions(
3380 &validator_config,
3381 &blockstore,
3382 root_slot,
3383 &hard_forks
3384 )
3385 .unwrap(),
3386 Some(root_slot + 1),
3387 );
3388 validator_config.wait_for_supermajority = None;
3389 assert_eq!(
3390 should_cleanup_blockstore_incorrect_shred_versions(
3391 &validator_config,
3392 &blockstore,
3393 root_slot,
3394 &hard_forks
3395 )
3396 .unwrap(),
3397 Some(root_slot + 1),
3398 );
3399
3400 root_slot = 30;
3403 let latest_hard_fork = hard_forks.iter().last().unwrap().0;
3404 assert_eq!(
3405 should_cleanup_blockstore_incorrect_shred_versions(
3406 &validator_config,
3407 &blockstore,
3408 root_slot,
3409 &hard_forks
3410 )
3411 .unwrap(),
3412 Some(latest_hard_fork + 1),
3413 );
3414
3415 blockstore
3418 .purge_slots(0, latest_hard_fork, PurgeType::Exact)
3419 .unwrap();
3420 assert_eq!(
3421 should_cleanup_blockstore_incorrect_shred_versions(
3422 &validator_config,
3423 &blockstore,
3424 root_slot,
3425 &hard_forks
3426 )
3427 .unwrap(),
3428 None,
3429 );
3430 }
3431
3432 #[test]
3433 fn test_cleanup_blockstore_incorrect_shred_versions() {
3434 agave_logger::setup();
3435
3436 let validator_config = ValidatorConfig::default_for_test();
3437 let ledger_path = get_tmp_ledger_path_auto_delete!();
3438 let blockstore = Blockstore::open(ledger_path.path()).unwrap();
3439
3440 let entries = entry::create_ticks(1, 0, Hash::default());
3441 for i in 1..10 {
3442 let shreds = blockstore::entries_to_test_shreds(
3443 &entries,
3444 i, i - 1, true, 1, );
3449 blockstore.insert_shreds(shreds, true).unwrap();
3450 }
3451
3452 cleanup_blockstore_incorrect_shred_versions(&blockstore, &validator_config, 5, 2).unwrap();
3454 assert!(blockstore.meta(4).unwrap().unwrap().next_slots.is_empty());
3456 for i in 5..10 {
3457 assert!(
3458 blockstore
3459 .get_data_shreds_for_slot(i, 0)
3460 .unwrap()
3461 .is_empty()
3462 );
3463 }
3464 }
3465
3466 #[test]
3467 fn validator_parallel_exit() {
3468 let leader_keypair = Keypair::new();
3469 let leader_node = Node::new_localhost_with_pubkey(&leader_keypair.pubkey());
3470 let genesis_config =
3471 create_genesis_config_with_leader(10_000, &leader_keypair.pubkey(), 1000)
3472 .genesis_config;
3473
3474 let mut ledger_paths = vec![];
3475 let mut validators: Vec<Validator> = (0..2)
3476 .map(|_| {
3477 let validator_keypair = Keypair::new();
3478 let validator_node = Node::new_localhost_with_pubkey(&validator_keypair.pubkey());
3479 let (validator_ledger_path, _blockhash) = create_new_tmp_ledger!(&genesis_config);
3480 ledger_paths.push(validator_ledger_path.clone());
3481 let vote_account_keypair = Keypair::new();
3482 let config = ValidatorConfig {
3483 rpc_addrs: Some((
3484 validator_node.info.rpc().unwrap(),
3485 validator_node.info.rpc_pubsub().unwrap(),
3486 )),
3487 ..ValidatorConfig::default_for_test()
3488 };
3489 Validator::new(
3490 validator_node,
3491 Arc::new(validator_keypair),
3492 &validator_ledger_path,
3493 &vote_account_keypair.pubkey(),
3494 Arc::new(RwLock::new(vec![Arc::new(vote_account_keypair)])),
3495 vec![leader_node.info.clone()],
3496 &config,
3497 None, Arc::new(RwLock::new(ValidatorStartProgress::default())),
3499 SocketAddrSpace::Unspecified,
3500 ValidatorTpuConfig::new_for_tests(),
3501 Arc::new(RwLock::new(None)),
3502 None,
3503 )
3504 .expect("assume successful validator start")
3505 })
3506 .collect();
3507
3508 validators.iter_mut().for_each(|v| v.exit());
3510
3511 let (sender, receiver) = bounded(0);
3513 let _ = thread::spawn(move || {
3514 validators.into_iter().for_each(|validator| {
3515 validator.join();
3516 });
3517 sender.send(()).unwrap();
3518 });
3519
3520 let timeout = Duration::from_secs(60);
3521 if let Err(RecvTimeoutError::Timeout) = receiver.recv_timeout(timeout) {
3522 panic!("timeout for shutting down validators",);
3523 }
3524
3525 for path in ledger_paths {
3526 remove_dir_all(path).unwrap();
3527 }
3528 }
3529
3530 #[test]
3531 fn test_wait_for_supermajority() {
3532 agave_logger::setup();
3533 let node_keypair = Arc::new(Keypair::new());
3534 let cluster_info = ClusterInfo::new(
3535 ContactInfo::new_localhost(&node_keypair.pubkey(), timestamp()),
3536 node_keypair,
3537 SocketAddrSpace::Unspecified,
3538 );
3539
3540 let (genesis_config, _mint_keypair) = create_genesis_config(1);
3541 let bank_forks = BankForks::new_rw_arc(Bank::new_for_tests(&genesis_config));
3542 let mut config = ValidatorConfig::default_for_test();
3543 let rpc_override_health_check = Arc::new(AtomicBool::new(false));
3544 let start_progress = Arc::new(RwLock::new(ValidatorStartProgress::default()));
3545
3546 assert!(
3547 !wait_for_supermajority(
3548 &config,
3549 None,
3550 &bank_forks,
3551 &cluster_info,
3552 rpc_override_health_check.clone(),
3553 &start_progress,
3554 )
3555 .unwrap()
3556 );
3557
3558 config.wait_for_supermajority = Some(1);
3560 assert!(matches!(
3561 wait_for_supermajority(
3562 &config,
3563 None,
3564 &bank_forks,
3565 &cluster_info,
3566 rpc_override_health_check.clone(),
3567 &start_progress,
3568 ),
3569 Err(ValidatorError::NotEnoughLedgerData(_, _)),
3570 ));
3571
3572 let bank_forks = BankForks::new_rw_arc(Bank::new_from_parent(
3574 bank_forks.read().unwrap().root_bank(),
3575 SlotLeader::default(),
3576 1,
3577 ));
3578 config.wait_for_supermajority = Some(0);
3579 assert!(
3580 !wait_for_supermajority(
3581 &config,
3582 None,
3583 &bank_forks,
3584 &cluster_info,
3585 rpc_override_health_check.clone(),
3586 &start_progress,
3587 )
3588 .unwrap()
3589 );
3590
3591 config.wait_for_supermajority = Some(1);
3593 config.expected_bank_hash = Some(hash(&[1]));
3594 assert!(matches!(
3595 wait_for_supermajority(
3596 &config,
3597 None,
3598 &bank_forks,
3599 &cluster_info,
3600 rpc_override_health_check,
3601 &start_progress,
3602 ),
3603 Err(ValidatorError::BankHashMismatch(_, _)),
3604 ));
3605 }
3606
3607 #[test]
3608 fn test_is_snapshot_config_valid() {
3609 fn new_snapshot_config(
3610 full_snapshot_archive_interval_slots: Slot,
3611 incremental_snapshot_archive_interval_slots: Slot,
3612 ) -> SnapshotConfig {
3613 SnapshotConfig {
3614 full_snapshot_archive_interval: SnapshotInterval::Slots(
3615 NonZeroU64::new(full_snapshot_archive_interval_slots).unwrap(),
3616 ),
3617 incremental_snapshot_archive_interval: SnapshotInterval::Slots(
3618 NonZeroU64::new(incremental_snapshot_archive_interval_slots).unwrap(),
3619 ),
3620 ..SnapshotConfig::default()
3621 }
3622 }
3623
3624 assert!(is_snapshot_config_valid(&SnapshotConfig::default()));
3626
3627 assert!(is_snapshot_config_valid(&SnapshotConfig {
3629 incremental_snapshot_archive_interval: SnapshotInterval::Disabled,
3630 ..SnapshotConfig::default()
3631 }));
3632
3633 assert!(!is_snapshot_config_valid(&SnapshotConfig {
3635 full_snapshot_archive_interval: SnapshotInterval::Disabled,
3636 ..SnapshotConfig::default()
3637 }));
3638
3639 assert!(is_snapshot_config_valid(&new_snapshot_config(400, 200)));
3641 assert!(is_snapshot_config_valid(&new_snapshot_config(100, 42)));
3642 assert!(is_snapshot_config_valid(&new_snapshot_config(444, 200)));
3643 assert!(is_snapshot_config_valid(&new_snapshot_config(400, 222)));
3644
3645 assert!(!is_snapshot_config_valid(&new_snapshot_config(42, 100)));
3647 assert!(!is_snapshot_config_valid(&new_snapshot_config(100, 100)));
3648 assert!(!is_snapshot_config_valid(&new_snapshot_config(100, 200)));
3649
3650 assert!(is_snapshot_config_valid(&SnapshotConfig::new_disabled()));
3652 assert!(is_snapshot_config_valid(&SnapshotConfig::new_load_only()));
3653 assert!(is_snapshot_config_valid(&SnapshotConfig {
3654 full_snapshot_archive_interval: SnapshotInterval::Slots(NonZeroU64::new(37).unwrap()),
3655 incremental_snapshot_archive_interval: SnapshotInterval::Slots(
3656 NonZeroU64::new(41).unwrap()
3657 ),
3658 ..SnapshotConfig::new_load_only()
3659 }));
3660 assert!(is_snapshot_config_valid(&SnapshotConfig {
3661 full_snapshot_archive_interval: SnapshotInterval::Disabled,
3662 incremental_snapshot_archive_interval: SnapshotInterval::Disabled,
3663 ..SnapshotConfig::new_load_only()
3664 }));
3665 }
3666
3667 fn target_tick_duration() -> Duration {
3668 let target_tick_duration_us =
3669 solana_clock::DEFAULT_MS_PER_SLOT * 1000 / solana_clock::DEFAULT_TICKS_PER_SLOT;
3670 assert_eq!(target_tick_duration_us, 6250);
3671 Duration::from_micros(target_tick_duration_us)
3672 }
3673
3674 #[test]
3675 fn test_poh_speed() {
3676 agave_logger::setup();
3677 let poh_config = PohConfig {
3678 target_tick_duration: target_tick_duration(),
3679 hashes_per_tick: Some(100 * solana_clock::DEFAULT_HASHES_PER_TICK),
3681 ..PohConfig::default()
3682 };
3683 let genesis_config = GenesisConfig {
3684 poh_config,
3685 ..GenesisConfig::default()
3686 };
3687 let bank = Bank::new_for_tests(&genesis_config);
3688 assert!(check_poh_speed(&bank, Some(10_000)).is_err());
3689 }
3690
3691 #[test]
3692 fn test_poh_speed_no_hashes_per_tick() {
3693 agave_logger::setup();
3694 let poh_config = PohConfig {
3695 target_tick_duration: target_tick_duration(),
3696 hashes_per_tick: None,
3697 ..PohConfig::default()
3698 };
3699 let genesis_config = GenesisConfig {
3700 poh_config,
3701 ..GenesisConfig::default()
3702 };
3703 let bank = Bank::new_for_tests(&genesis_config);
3704 check_poh_speed(&bank, Some(10_000)).unwrap();
3705 }
3706}