diff --git a/core/src/blocktree_processor.rs b/core/src/blocktree_processor.rs index def50e11d3..c280308957 100644 --- a/core/src/blocktree_processor.rs +++ b/core/src/blocktree_processor.rs @@ -8,8 +8,7 @@ use solana_metrics::{datapoint, datapoint_error, inc_new_counter_debug}; use solana_runtime::bank::Bank; use solana_runtime::locked_accounts_results::LockedAccountsResults; use solana_sdk::genesis_block::GenesisBlock; -use solana_sdk::timing::duration_as_ms; -use solana_sdk::timing::MAX_RECENT_BLOCKHASHES; +use solana_sdk::timing::{duration_as_ms, Slot, MAX_RECENT_BLOCKHASHES}; use solana_sdk::transaction::Result; use std::result; use std::sync::Arc; @@ -143,6 +142,7 @@ pub fn process_blocktree( blocktree: &Blocktree, account_paths: Option, verify_ledger: bool, + dev_halt_at_slot: Option, ) -> result::Result<(BankForks, Vec, LeaderScheduleCache), BlocktreeProcessorError> { let now = Instant::now(); info!("processing ledger..."); @@ -173,6 +173,7 @@ pub fn process_blocktree( let mut fork_info = vec![]; let mut last_status_report = Instant::now(); let mut root = 0; + let dev_halt_at_slot = dev_halt_at_slot.unwrap_or(std::u64::MAX); while !pending_slots.is_empty() { let (slot, meta, bank, mut entry_height, mut last_entry_hash) = pending_slots.pop().unwrap(); @@ -233,6 +234,15 @@ pub fn process_blocktree( fork_info.clear(); } + if slot >= dev_halt_at_slot { + let bfi = BankForksInfo { + bank_slot: slot, + entry_height, + }; + fork_info.push((bank, bfi)); + break; + } + if meta.next_slots.is_empty() { // Reached the end of this fork. Record the final entry height and last entry.hash let bfi = BankForksInfo { @@ -376,7 +386,7 @@ pub mod tests { fill_blocktree_slot_with_ticks(&blocktree, ticks_per_slot, 2, 1, blockhash); let (mut _bank_forks, bank_forks_info, _) = - process_blocktree(&genesis_block, &blocktree, None, true).unwrap(); + process_blocktree(&genesis_block, &blocktree, None, true, None).unwrap(); assert_eq!(bank_forks_info.len(), 1); assert_eq!( @@ -435,7 +445,7 @@ pub mod tests { blocktree.set_roots(&[4, 1, 0]).unwrap(); let (bank_forks, bank_forks_info, _) = - process_blocktree(&genesis_block, &blocktree, None, true).unwrap(); + process_blocktree(&genesis_block, &blocktree, None, true, None).unwrap(); assert_eq!(bank_forks_info.len(), 1); // One fork, other one is ignored b/c not a descendant of the root @@ -509,7 +519,7 @@ pub mod tests { blocktree.set_roots(&[0, 1]).unwrap(); let (bank_forks, bank_forks_info, _) = - process_blocktree(&genesis_block, &blocktree, None, true).unwrap(); + process_blocktree(&genesis_block, &blocktree, None, true, None).unwrap(); assert_eq!(bank_forks_info.len(), 2); // There are two forks assert_eq!( @@ -590,7 +600,7 @@ pub mod tests { // Check that we can properly restart the ledger / leader scheduler doesn't fail let (bank_forks, bank_forks_info, _) = - process_blocktree(&genesis_block, &blocktree, None, true).unwrap(); + process_blocktree(&genesis_block, &blocktree, None, true, None).unwrap(); assert_eq!(bank_forks_info.len(), 1); // There is one fork assert_eq!( @@ -726,7 +736,7 @@ pub mod tests { .unwrap(); let entry_height = genesis_block.ticks_per_slot + entries.len() as u64; let (bank_forks, bank_forks_info, _) = - process_blocktree(&genesis_block, &blocktree, None, true).unwrap(); + process_blocktree(&genesis_block, &blocktree, None, true, None).unwrap(); assert_eq!(bank_forks_info.len(), 1); assert_eq!(bank_forks.root(), 0); @@ -757,7 +767,7 @@ pub mod tests { let blocktree = Blocktree::open(&ledger_path).unwrap(); let (bank_forks, bank_forks_info, _) = - process_blocktree(&genesis_block, &blocktree, None, true).unwrap(); + process_blocktree(&genesis_block, &blocktree, None, true, None).unwrap(); assert_eq!(bank_forks_info.len(), 1); assert_eq!( diff --git a/core/src/validator.rs b/core/src/validator.rs index f42bf4f581..0414da1688 100644 --- a/core/src/validator.rs +++ b/core/src/validator.rs @@ -25,7 +25,7 @@ use solana_sdk::genesis_block::GenesisBlock; use solana_sdk::poh_config::PohConfig; use solana_sdk::pubkey::Pubkey; use solana_sdk::signature::{Keypair, KeypairUtil}; -use solana_sdk::timing::{timestamp, DEFAULT_SLOTS_PER_TURN}; +use solana_sdk::timing::{timestamp, Slot, DEFAULT_SLOTS_PER_TURN}; use std::fs; use std::net::{IpAddr, Ipv4Addr, SocketAddr}; use std::path::{Path, PathBuf}; @@ -36,7 +36,8 @@ use std::thread::Result; #[derive(Clone, Debug)] pub struct ValidatorConfig { - pub sigverify_disabled: bool, + pub dev_sigverify_disabled: bool, + pub dev_halt_at_slot: Option, pub voting_disabled: bool, pub blockstream_unix_socket: Option, pub storage_slots_per_turn: u64, @@ -51,7 +52,8 @@ pub struct ValidatorConfig { impl Default for ValidatorConfig { fn default() -> Self { Self { - sigverify_disabled: false, + dev_sigverify_disabled: false, + dev_halt_at_slot: None, voting_disabled: false, blockstream_unix_socket: None, storage_slots_per_turn: DEFAULT_SLOTS_PER_TURN, @@ -90,6 +92,12 @@ impl Validator { verify_ledger: bool, config: &ValidatorConfig, ) -> Self { + info!("node info: {:?}", node.info); + info!("node entrypoint_info: {:?}", entrypoint_info_option); + info!( + "node local gossip address: {}", + node.sockets.gossip.local_addr().unwrap() + ); warn!("CUDA is {}abled", if cfg!(cuda) { "en" } else { "dis" }); let id = keypair.pubkey(); @@ -109,12 +117,64 @@ impl Validator { config.account_paths.clone(), config.snapshot_config.clone(), verify_ledger, + config.dev_halt_at_slot, ); let leader_schedule_cache = Arc::new(leader_schedule_cache); let exit = Arc::new(AtomicBool::new(false)); let bank_info = &bank_forks_info[0]; let bank = bank_forks[bank_info.bank_slot].clone(); + let bank_forks = Arc::new(RwLock::new(bank_forks)); + + node.info.wallclock = timestamp(); + let cluster_info = Arc::new(RwLock::new(ClusterInfo::new( + node.info.clone(), + keypair.clone(), + ))); + + let storage_state = StorageState::new( + &bank.last_blockhash(), + config.storage_slots_per_turn, + bank.slots_per_segment(), + ); + + let rpc_service = if node.info.rpc.port() == 0 { + None + } else { + Some(JsonRpcService::new( + &cluster_info, + SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), node.info.rpc.port()), + storage_state.clone(), + config.rpc_config.clone(), + bank_forks.clone(), + ledger_path, + &exit, + )) + }; + + let subscriptions = Arc::new(RpcSubscriptions::default()); + let rpc_pubsub_service = if node.info.rpc_pubsub.port() == 0 { + None + } else { + Some(PubSubService::new( + &subscriptions, + SocketAddr::new( + IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), + node.info.rpc_pubsub.port(), + ), + &exit, + )) + }; + + if config.dev_halt_at_slot.is_some() { + // Park with the RPC service running, ready for inspection! + warn!( + "Validator halted at slot {} (epoch {})", + bank.slot(), + bank.epoch() + ); + std::thread::park(); + } info!( "starting PoH... {} {}", @@ -148,58 +208,9 @@ impl Validator { "New blob signal for the TVU should be the same as the clear bank signal." ); - info!("node info: {:?}", node.info); - info!("node entrypoint_info: {:?}", entrypoint_info_option); - info!( - "node local gossip address: {}", - node.sockets.gossip.local_addr().unwrap() - ); - - let bank_forks = Arc::new(RwLock::new(bank_forks)); - - node.info.wallclock = timestamp(); - let cluster_info = Arc::new(RwLock::new(ClusterInfo::new( - node.info.clone(), - keypair.clone(), - ))); - - let storage_state = StorageState::new( - &bank.last_blockhash(), - config.storage_slots_per_turn, - bank.slots_per_segment(), - ); - - let rpc_service = if node.info.rpc.port() == 0 { - None - } else { - Some(JsonRpcService::new( - &cluster_info, - SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), node.info.rpc.port()), - storage_state.clone(), - config.rpc_config.clone(), - bank_forks.clone(), - ledger_path, - &exit, - )) - }; - let ip_echo_server = solana_netutil::ip_echo_server(node.sockets.gossip.local_addr().unwrap().port()); - let subscriptions = Arc::new(RpcSubscriptions::default()); - let rpc_pubsub_service = if node.info.rpc_pubsub.port() == 0 { - None - } else { - Some(PubSubService::new( - &subscriptions, - SocketAddr::new( - IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), - node.info.rpc_pubsub.port(), - ), - &exit, - )) - }; - let gossip_service = GossipService::new( &cluster_info, Some(blocktree.clone()), @@ -210,7 +221,6 @@ impl Validator { // Insert the entrypoint info, should only be None if this node // is the bootstrap leader - if let Some(entrypoint_info) = entrypoint_info_option { cluster_info .write() @@ -262,7 +272,7 @@ impl Validator { completed_slots_receiver, ); - if config.sigverify_disabled { + if config.dev_sigverify_disabled { warn!("signature verification disabled"); } @@ -273,7 +283,7 @@ impl Validator { node.sockets.tpu, node.sockets.tpu_forwards, node.sockets.broadcast, - config.sigverify_disabled, + config.dev_sigverify_disabled, &blocktree, &config.broadcast_stage_type, &config.erasure_config, @@ -312,6 +322,7 @@ fn get_bank_forks( account_paths: Option, snapshot_config: Option, verify_ledger: bool, + dev_halt_at_slot: Option, ) -> (BankForks, Vec, LeaderScheduleCache) { let (mut bank_forks, bank_forks_info, leader_schedule_cache) = { let mut result = None; @@ -362,6 +373,7 @@ fn get_bank_forks( &blocktree, account_paths, verify_ledger, + dev_halt_at_slot, ) .expect("process_blocktree failed"), ); @@ -382,6 +394,7 @@ pub fn new_banks_from_blocktree( account_paths: Option, snapshot_config: Option, verify_ledger: bool, + dev_halt_at_slot: Option, ) -> ( BankForks, Vec, @@ -404,6 +417,7 @@ pub fn new_banks_from_blocktree( account_paths, snapshot_config, verify_ledger, + dev_halt_at_slot, ); ( diff --git a/core/tests/tvu.rs b/core/tests/tvu.rs index 9f3c2756d7..9031f2f896 100644 --- a/core/tests/tvu.rs +++ b/core/tests/tvu.rs @@ -97,7 +97,7 @@ fn test_replay() { completed_slots_receiver, leader_schedule_cache, _, - ) = validator::new_banks_from_blocktree(&blocktree_path, None, None, true); + ) = validator::new_banks_from_blocktree(&blocktree_path, None, None, true, None); let working_bank = bank_forks.working_bank(); assert_eq!( working_bank.get_balance(&mint_keypair.pubkey()), diff --git a/ledger-tool/src/main.rs b/ledger-tool/src/main.rs index ff4031a8a3..536f4f53db 100644 --- a/ledger-tool/src/main.rs +++ b/ledger-tool/src/main.rs @@ -149,7 +149,7 @@ fn main() { } ("verify", _) => { println!("Verifying ledger..."); - match process_blocktree(&genesis_block, &blocktree, None, true) { + match process_blocktree(&genesis_block, &blocktree, None, true, None) { Ok((_bank_forks, bank_forks_info, _)) => { println!("{:?}", bank_forks_info); } diff --git a/multinode-demo/validator.sh b/multinode-demo/validator.sh index 311e1208b0..2eb4f2fb9c 100755 --- a/multinode-demo/validator.sh +++ b/multinode-demo/validator.sh @@ -97,7 +97,7 @@ while [[ -n $1 ]]; do elif [[ $1 = --skip-ledger-verify ]]; then args+=("$1") shift - elif [[ $1 = --no-sigverify ]]; then + elif [[ $1 = --dev-no-sigverify ]]; then args+=("$1") shift elif [[ $1 = --limit-ledger-size ]]; then diff --git a/validator/src/main.rs b/validator/src/main.rs index e2e28cef2e..05a3322271 100644 --- a/validator/src/main.rs +++ b/validator/src/main.rs @@ -1,4 +1,4 @@ -use clap::{crate_description, crate_name, crate_version, App, Arg}; +use clap::{crate_description, crate_name, crate_version, value_t, App, Arg}; use log::*; use solana::bank_forks::SnapshotConfig; use solana::cluster_info::{Node, FULLNODE_PORT_RANGE}; @@ -10,6 +10,7 @@ use solana::socketaddr; use solana::validator::{Validator, ValidatorConfig}; use solana_netutil::parse_port_range; use solana_sdk::signature::{read_keypair, Keypair, KeypairUtil}; +use solana_sdk::timing::Slot; use std::fs; use std::fs::File; use std::net::SocketAddr; @@ -101,12 +102,18 @@ fn main() { .help("Launch node without voting"), ) .arg( - Arg::with_name("no_sigverify") - .short("v") - .long("no-sigverify") + Arg::with_name("dev_no_sigverify") + .long("dev-no-sigverify") .takes_value(false) .help("Run without signature verification"), ) + .arg( + Arg::with_name("dev_halt_at_slot") + .long("dev-halt-at-slot") + .value_name("SLOT") + .takes_value(true) + .help("Halt the validator when it reaches the given slot"), + ) .arg( Arg::with_name("rpc_port") .long("rpc-port") @@ -213,7 +220,8 @@ fn main() { let ledger_path = PathBuf::from(matches.value_of("ledger_path").unwrap()); - validator_config.sigverify_disabled = matches.is_present("no_sigverify"); + validator_config.dev_sigverify_disabled = matches.is_present("dev_no_sigverify"); + validator_config.dev_halt_at_slot = value_t!(matches, "dev_halt_at_slot", Slot).ok(); validator_config.voting_disabled = matches.is_present("no_voting");