Skip to main content

solana_core/
validator.rs

1//! The `validator` module hosts all the validator microservices.
2
3pub 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/// Configuration for the block generator invalidator for replay.
306#[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    /// The destination file for validator logs
315    pub logfile: PathBuf,
316    /// A flag to indicate that a logrotate rotation has occurred and that the
317    /// logfile should be reopened. The flag itself is toggled when the process
318    /// receives the SIGUSR1 signal
319    pub logrotate_flag: Arc<AtomicBool>,
320}
321
322pub struct ValidatorConfig {
323    /// Log messages go to `stderr` if `None`
324    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    /// Specifies which plugins to start up with
333    pub on_start_geyser_plugin_config_files: Option<Vec<PathBuf>>,
334    pub geyser_plugin_always_enabled: bool,
335    pub rpc_addrs: Option<(SocketAddr, SocketAddr)>, // (JsonRpc, JsonRpcPubSub)
336    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>>, // None = trust all
346    pub repair_validators: Option<HashSet<Pubkey>>, // None = repair from all
347    pub repair_whitelist: Arc<RwLock<HashSet<Pubkey>>>, // Empty = repair with all
348    pub gossip_validators: Option<HashSet<Pubkey>>, // None = gossip with all
349    pub should_check_duplicate_instance: bool,
350    pub max_genesis_archive_unpacked_size: u64,
351    /// Run PoH, transaction signature and other transaction verification during blockstore
352    /// processing.
353    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    // Thread niceness adjustment for snapshot packager service
403    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            // No need to enforce nofile limit in tests
452            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 forwarding by default for tests
472            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            // Fix threadpools to small and reasonable sizes; unit tests should
478            // not be creating excessive load and benches can configure more
479            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// `ValidatorStartProgress` contains status information that is surfaced to the node operator over
506// the admin RPC channel to help them to follow the general progress of node startup without
507// having to watch log messages.
508#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Default)]
509pub enum ValidatorStartProgress {
510    #[default]
511    Initializing, // Catch all, default state
512    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    // This case corresponds to a state that is entered by using the now
526    // deprecated `--dev-halt-at-slot` flag. A different version of the
527    // validator may be used to monitor a running validator so leave the case
528    // here to avoid any compatibility concerns
529    Halted,
530    WaitingForSupermajority {
531        slot: Slot,
532        gossip_stake_percent: u64,
533    },
534
535    // `Running` is the terminal state once the validator fully starts and all services are
536    // operational
537    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
583/// A struct easing passing Validator TPU Configurations
584pub struct ValidatorTpuConfig {
585    /// Controls if to use QUIC for sending TPU votes
586    pub vote_use_quic: bool,
587    /// Controls the connection cache pool size
588    pub tpu_connection_pool_size: usize,
589    /// QUIC server config for regular TPU
590    pub tpu_quic_server_config: SwQosQuicStreamerConfig,
591    /// QUIC server config for TPU forward
592    pub tpu_fwd_quic_server_config: SwQosQuicStreamerConfig,
593    /// QUIC server config for Vote
594    pub vote_quic_server_config: SimpleQosQuicStreamerConfig,
595    /// Number of threads to use for signature verification
596    pub sigverify_threads: NonZeroUsize,
597}
598
599impl ValidatorTpuConfig {
600    /// A convenient function to build a ValidatorTpuConfig for testing with good
601    /// default.
602    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        // vote and tpu_fwd share the same characteristics -- disallow non-staked connections:
625        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        // Two threads is reasonable for tests; benches are free to set more
634        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    /// A global flag to indicate communicate shutdown between threads
649    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    /// Held for the lifetime of the validator so the dispatch thread keeps
677    /// running. `None` when no loaded plugin opted into contact info
678    /// notifications.
679    _contact_info_notifier: Option<GeyserContactInfoNotifier>,
680    blockstore_metric_report_service: BlockstoreMetricReportService,
681    accounts_background_service: AccountsBackgroundService,
682    xdp_transmitter: Option<Transmitter>,
683    // This runtime is used to run the client owned by SendTransactionService.
684    // We don't wait for its JoinHandle here because ownership and shutdown
685    // are managed elsewhere. This variable is intentionally unused.
686    _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        // Initialize the global rayon pool first to ensure the value in config
762        // is honored. Otherwise, some code accessing the global pool could
763        // cause it to get initialized with Rayon's default (not ours)
764        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        // token used to cancel tpu-client-next, streamer and BLS streamer.
851        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        // Opt-in Geyser notifications for gossip contact info changes. If
1006        // no loaded plugin opts in, this returns `None` and gossip's hot
1007        // path performs no work for these notifications.
1008        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        // test-validator crate may start the validator in a tokio runtime
1206        // context which forces us to use the same runtime because a nested
1207        // runtime will cause panic at drop. Outside test-validator crate, we
1208        // always need a tokio runtime (and the respective handle) to initialize
1209        // the QUIC endpoints.
1210        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        // CompletedDataSetsService feeds two independent sinks: RPC signatureSubscribe
1367        // notifications (which need rpc_subscriptions) and the geyser deshred-transaction notifier
1368        // (which does not). Spawn it whenever either sink wants it, kept out of the rpc_addrs block
1369        // above so a geyser node started without --rpc-port still gets deshred notifications.
1370        // Gating on the notifier itself rather than on a plugin being loaded keeps the per-data-set
1371        // blockstore reads off nodes whose plugins don't subscribe; --geyser-plugin-always-enabled
1372        // is the exception, where the notifier is present with no subscribers.
1373        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        // Pass RecordReceiver from PohService to BlockCreationLoop when shutting down. Gives us a strong guarentee
1500        // that both block producers are not running at the same time
1501        let (record_receiver_sender, record_receiver_receiver) = bounded(1);
1502        // Sender for notifications about our leader window. We allow for a maximum of 7 leader windows in case we have
1503        // consecutive leader windows and are slow. There is an early give up if our leader window is skipped because we
1504        // are too slow, so in practice this channel should never be full.
1505        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        // Shared state for highest finalized certificates (updated by Votor, read by block creation loop)
1523        let highest_finalized = Arc::new(RwLock::new(None));
1524        // This channel growing > ~1 indicates problems, so bound channel at a
1525        // small (but highly overprovisioned) number for performance and easier
1526        // debug if things go off the rails.
1527        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        // This channel backing up indicates a serious problem in votor
1599        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    /// Register a signal handler to toggle the returned `AtomicBool` when the
1828    /// `SIGUSR1` signal is received. The `SIGUSR1` signal provides a hook for
1829    /// the validator to support logrotate
1830    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    /// Monitor registered signal handlers and the validator's exit flag
1840    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                // Reset the flag to `false` to allow detection of the
1857                // signal again and to avoid hitting this case every
1858                // iteration
1859                logrotate_flag.store(false, Ordering::Relaxed);
1860            }
1861
1862            // One second is a reasonable response time for these signals to
1863            // avoid this thread from being overly active
1864            thread::sleep(Duration::from_secs(1));
1865        }
1866        info!("Validator::listen_for_signals() has stopped");
1867
1868        Ok(())
1869    }
1870
1871    // Used for notifying many nodes in parallel to exit
1872    pub fn exit(&mut self) {
1873        self.validator_exit.write().unwrap().exit();
1874
1875        // drop all signals in blockstore
1876        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
2015/// Should we require that a vote history file is present
2016pub 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        // Vote history is only used when Alpenglow is active
2023        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        // Must have a vote account
2031        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        // We are starting up or set-identity with a dummy keypair
2041        // We don't need to require the vote history file
2042        return false;
2043    }
2044
2045    let Some(last_voted_slot) = vote_state_handler.last_voted_slot() else {
2046        // New vote account
2047        return false;
2048    };
2049    let genesis_slot = genesis_certificate.block.slot;
2050
2051    // We've voted past the alpenglow genesis
2052    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    // detect cluster restart (hard fork) indirectly via wait_for_supermajority...
2122    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        // make sure tower isn't corrupted first before the following hard fork check
2146        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            // intentionally fail to restore tower; we're supposedly in a new hard fork; past
2152            // out-of-chain vote state doesn't make sense at all
2153            // what if --wait-for-supermajority again if the validator restarted?
2154            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            // unconditionally relax tower requirement so that we can always restore tower
2160            // from root bank.
2161            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            // unconditionally relax tower requirement so that we can always restore tower
2169            // from root bank after the warp
2170            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                // Currently, don't protect against spoofed snapshots with no tower at all
2197                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 is old, update
2228            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            // intentionally fail to restore vote_history; we're supposedly in a new hard fork; past
2235            // out-of-chain votor state doesn't make sense at all
2236            // what if --wait-for-supermajority again if the validator restarted?
2237            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            // unconditionally relax vote_history requirement
2245            should_require_vote_history = false;
2246            Err(VoteHistoryError::HardFork(hard_fork_restart_slot))
2247        } else if let Some(warp_slot) = config.warp_slot {
2248            // unconditionally relax vote_history requirement
2249            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    // This needs to be limited otherwise the state in the VoteAccount data
2292    // grows too large
2293    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    // following boot sequence (esp BankForks) could set root. so stash the original value
2380    // of blockstore root away here as soon as possible.
2381    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            // Clean run from genesis — must not use any existing state from previous runs.
2407            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    // Before replay starts, set the callbacks in each of the banks in BankForks so that
2433    // all dropped banks come through the `pruned_banks_receiver` channel. This way all bank
2434    // drop behavior can be safely synchronized with any other ongoing accounts activity like
2435    // cache flush, clean, shrink, as long as the same thread performing those activities also
2436    // is processing the dropped banks from the `pruned_banks_receiver` channel.
2437    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        // This means we have not fully processed blockstore yet. Attempt to load and process
2526        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        // Load and post process tower
2569        let tower = {
2570            let restored_tower = Tower::restore(self.config.tower_storage.as_ref(), self.id);
2571            if let Ok(tower) = &restored_tower {
2572                // reconciliation attempt 1 of 2 with tower
2573                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        // Load and post process vote history
2591        let vote_history = {
2592            let vote_history =
2593                restore_vote_history(self.config, self.bank_forks, self.id, self.vote_account)?;
2594            // reconciliation attempt 1 of 2 with vote history
2595            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            // reconciliation attempt 2 of 2 with hard fork
2615            // it is intentional that we do this second, as having the hard fork root < tower/vote_history root
2616            // is invalid! This means we've hard forked and missed a finalized slot
2617            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
2632// `--warp-slot`: runs at startup only (before PoH/replay), so fork graph access is serial here.
2633fn 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        // An accounts hash calculation from storages will occur in warp_from_parent() below.  This
2658        // requires that the accounts cache has been flushed, which requires the parent slot to be
2659        // rooted.
2660        root_bank.squash();
2661        root_bank.force_flush_accounts_cache();
2662
2663        // Do not call `Bank::warp_from_parent` while holding `bank_forks.write()`: child bank
2664        // construction runs `ProgramCache::extract`, which takes `fork_graph.read()` on this same
2665        // `RwLock<BankForks>` (deadlock with an exclusive lock).
2666        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        // The bank must have a block id set to take a snapshot.
2671        // Also must be set before calling set_root() just incase the warp slot triggers a
2672        // snapshot request based on the snapshot config inside snapshot_controller.
2673        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 after warping bank forks to make sure tower and
2696        // bank forks are in sync.
2697        process_blockstore.process()?;
2698    }
2699    Ok(())
2700}
2701
2702/// Returns the starting slot at which the blockstore should be scanned for
2703/// shreds with an incorrect shred version, or None if the check is unnecessary
2704fn 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    // Perform the check if we are booting as part of a cluster restart at slot root_slot
2711    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    // If there are no hard forks, the shred version cannot have changed
2717    let Some(latest_hard_fork) = hard_forks.iter().last().map(|(slot, _)| *slot) else {
2718        return Ok(None);
2719    };
2720
2721    // If the blockstore is empty, there are certainly no shreds with an incorrect version
2722    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        // latest_hard_fork < blockstore_min_slot <= blockstore_max_slot
2733        //
2734        // All slots in the blockstore are newer than the latest hard fork, and only shreds with
2735        // the correct shred version should have been inserted since the latest hard fork
2736        //
2737        // This is the normal case where the last cluster restart & hard fork was a while ago; we
2738        // can skip the check for this case
2739        Ok(None)
2740    } else if latest_hard_fork < blockstore_max_slot {
2741        // blockstore_min_slot < latest_hard_fork < blockstore_max_slot
2742        //
2743        // This could be a case where there was a cluster restart, but this node was not part of
2744        // the supermajority that actually restarted the cluster. Rather, this node likely
2745        // downloaded a new snapshot while retaining the blockstore, including slots beyond the
2746        // chosen restart slot. We need to perform the blockstore check for this case
2747        //
2748        // Note that the downloaded snapshot slot (root_slot) could be greater than the latest hard
2749        // fork slot. Even though this node will only replay slots after root_slot, start the check
2750        // at latest_hard_fork + 1 to check (and possibly purge) any invalid state.
2751        Ok(Some(latest_hard_fork + 1))
2752    } else {
2753        // blockstore_min_slot <= blockstore_max_slot <= latest_hard_fork
2754        //
2755        // All slots in the blockstore are older than the latest hard fork. The blockstore check
2756        // would start from latest_hard_fork + 1; skip the check as there are no slots to check
2757        //
2758        // This is kind of an unusual case to hit, maybe a node has been offline for a long time
2759        // and just restarted with a new downloaded snapshot but the old blockstore
2760        Ok(None)
2761    }
2762}
2763
2764/// Searches the blockstore for data shreds with a shred version that differs
2765/// from the passed `expected_shred_version`
2766fn 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    // Search for shreds with incompatible version in blockstore
2774    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
2799/// If the blockstore contains any shreds with the incorrect shred version,
2800/// copy them to a backup blockstore and purge them from the actual blockstore.
2801fn 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    // .unwrap() safe because getting to this point implies blockstore has slots/shreds
2818    let end_slot = blockstore.highest_slot()?.unwrap();
2819
2820    // Backing up the shreds that will be deleted from primary blockstore is
2821    // not critical, so swallow errors from backup blockstore operations.
2822    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
2936// Return if the validator waited on other nodes to start. In this case
2937// it should not wait for one of it's votes to land to produce blocks
2938// because if the whole network is waiting, then it will stall.
2939//
2940// Error indicates that a bad hash was encountered or another condition
2941// that is unrecoverable and the validator should exit.
2942fn 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                // The normal RPC health checks don't apply as the node is waiting, so feign health to
3007                // prevent load balancers from removing the node from their list of candidates during a
3008                // manual restart.
3009                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
3018// Get the activated stake percentage (based on the provided bank) that is visible in gossip
3019fn 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    // Nodes contact infos are saved to disk and restored on validator startup.
3027    // Staked nodes entries will not expire until an epoch after. So it
3028    // is necessary here to filter for recent entries to establish liveness.
3029    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            // Contact infos are refreshed twice during this period.
3035            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; // This node is online
3057        } 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)); // sort by reverse stake weight
3073            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 the snapshot config is configured to *not* take snapshots, then it is valid
3109    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        // if we *are* generating snapshots, then the full snapshot interval cannot be disabled
3117        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        // Use an unstaked identity
3242        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, // rpc_to_plugin_manager_receiver
3280            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        // Do check from root_slot + 1 if wait_for_supermajority (10) == root_slot (10)
3307        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        // No check if wait_for_supermajority (10) < root_slot (15) (no hard forks)
3321        // Arguably operator error to pass a value for wait_for_supermajority in this case
3322        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        // Emulate cluster restart at slot 10
3335        // No check if wait_for_supermajority (10) < root_slot (15) (empty blockstore)
3336        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        // Insert some shreds at newer slots than hard fork
3349        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,     // slot
3354                i - 1, // parent_slot
3355                true,  // is_full_slot
3356                1,     // version
3357            );
3358            blockstore.insert_shreds(shreds, true).unwrap();
3359        }
3360
3361        // No check as all blockstore data is newer than latest hard fork
3362        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        // Emulate cluster restart at slot 25
3374        // Do check from root_slot + 1 regardless of whether wait_for_supermajority set correctly
3375        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        // Do check with advanced root slot, even without wait_for_supermajority set correctly
3401        // Check starts from latest hard fork + 1
3402        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        // Purge blockstore up to latest hard fork
3416        // No check since all blockstore data newer than latest hard fork
3417        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,     // slot
3445                i - 1, // parent_slot
3446                true,  // is_full_slot
3447                1,     // version
3448            );
3449            blockstore.insert_shreds(shreds, true).unwrap();
3450        }
3451
3452        // this purges and compacts all slots greater than or equal to 5
3453        cleanup_blockstore_incorrect_shred_versions(&blockstore, &validator_config, 5, 2).unwrap();
3454        // assert that slots less than 5 aren't affected
3455        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, // rpc_to_plugin_manager_receiver
3498                    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        // Each validator can exit in parallel to speed many sequential calls to join`
3509        validators.iter_mut().for_each(|v| v.exit());
3510
3511        // spawn a new thread to wait for the join of the validator
3512        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        // bank=0, wait=1, should fail
3559        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        // bank=1, wait=0, should pass, bank is past the wait slot
3573        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        // bank=1, wait=1, equal, but bad hash provided
3592        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        // default config must be valid
3625        assert!(is_snapshot_config_valid(&SnapshotConfig::default()));
3626
3627        // disabled incremental snapshot must be valid
3628        assert!(is_snapshot_config_valid(&SnapshotConfig {
3629            incremental_snapshot_archive_interval: SnapshotInterval::Disabled,
3630            ..SnapshotConfig::default()
3631        }));
3632
3633        // disabled full snapshot must be invalid though (if generating snapshots)
3634        assert!(!is_snapshot_config_valid(&SnapshotConfig {
3635            full_snapshot_archive_interval: SnapshotInterval::Disabled,
3636            ..SnapshotConfig::default()
3637        }));
3638
3639        // simple config must be valid
3640        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        // config where full interval is not larger than incremental interval must be invalid
3646        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        // config with snapshots disabled (or load-only) must be valid
3651        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            // make PoH rate really fast to cause the panic condition
3680            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}