Make solana-validator check vote account at start (#6790)
* Make solana-validator check vote account at start * Don't abort tests... * Fix test breakage * Remove extra semicolon * Attempt to fix cluster-tests * rustfmt * Change behavior of vote_account ephemeral pubkeys * save * clean up * clean up * rustfmt && clippy * Reorder for simpler diff * Fix rebase... * Fix message a bit * Still more rebase fixes.... * Fix yet more * Use find_map over filter_map & next and revert message * More through error checks * rustfmt & clippy * Revert * Revert core/src/validator.rs * Cleanup * Cleanup * Cleanup * Rebase fix * Make clippy & rustfmt happy * save * Clean up * Show rpc error detail * Check node lamports only after pubkey matching * rustfmt
This commit is contained in:
		@@ -19,6 +19,7 @@ use solana_ledger::bank_forks::SnapshotConfig;
 | 
				
			|||||||
use solana_perf::recycler::enable_recycler_warming;
 | 
					use solana_perf::recycler::enable_recycler_warming;
 | 
				
			||||||
use solana_sdk::clock::Slot;
 | 
					use solana_sdk::clock::Slot;
 | 
				
			||||||
use solana_sdk::hash::Hash;
 | 
					use solana_sdk::hash::Hash;
 | 
				
			||||||
 | 
					use solana_sdk::pubkey::Pubkey;
 | 
				
			||||||
use solana_sdk::signature::{read_keypair_file, Keypair, KeypairUtil};
 | 
					use solana_sdk::signature::{read_keypair_file, Keypair, KeypairUtil};
 | 
				
			||||||
use std::fs::{self, File};
 | 
					use std::fs::{self, File};
 | 
				
			||||||
use std::io::{self, Read};
 | 
					use std::io::{self, Read};
 | 
				
			||||||
@@ -168,12 +169,9 @@ fn download_tar_bz2(
 | 
				
			|||||||
    Ok(())
 | 
					    Ok(())
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn initialize_ledger_path(
 | 
					fn create_rpc_client(
 | 
				
			||||||
    entrypoint: &ContactInfo,
 | 
					    entrypoint: &ContactInfo,
 | 
				
			||||||
    ledger_path: &Path,
 | 
					) -> Result<(std::net::SocketAddr, RpcClient), String> {
 | 
				
			||||||
    no_genesis_fetch: bool,
 | 
					 | 
				
			||||||
    no_snapshot_fetch: bool,
 | 
					 | 
				
			||||||
) -> Result<Hash, String> {
 | 
					 | 
				
			||||||
    let (nodes, _archivers) = discover(
 | 
					    let (nodes, _archivers) = discover(
 | 
				
			||||||
        &entrypoint.gossip,
 | 
					        &entrypoint.gossip,
 | 
				
			||||||
        Some(1),
 | 
					        Some(1),
 | 
				
			||||||
@@ -184,9 +182,7 @@ fn initialize_ledger_path(
 | 
				
			|||||||
    )
 | 
					    )
 | 
				
			||||||
    .map_err(|err| err.to_string())?;
 | 
					    .map_err(|err| err.to_string())?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let rpc_addr = nodes
 | 
					    let rpc_addr = nodes.iter().find_map(|contact_info| {
 | 
				
			||||||
        .iter()
 | 
					 | 
				
			||||||
        .filter_map(|contact_info| {
 | 
					 | 
				
			||||||
        if contact_info.gossip == entrypoint.gossip
 | 
					        if contact_info.gossip == entrypoint.gossip
 | 
				
			||||||
            && ContactInfo::is_valid_address(&contact_info.rpc)
 | 
					            && ContactInfo::is_valid_address(&contact_info.rpc)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
@@ -194,18 +190,78 @@ fn initialize_ledger_path(
 | 
				
			|||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            None
 | 
					            None
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        })
 | 
					 | 
				
			||||||
        .next()
 | 
					 | 
				
			||||||
        .unwrap_or_else(|| {
 | 
					 | 
				
			||||||
            error!(
 | 
					 | 
				
			||||||
                "Entrypoint ({:?}) is not running the RPC service",
 | 
					 | 
				
			||||||
                entrypoint.gossip
 | 
					 | 
				
			||||||
            );
 | 
					 | 
				
			||||||
            exit(1);
 | 
					 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let client = RpcClient::new_socket(rpc_addr);
 | 
					    if let Some(rpc_addr) = rpc_addr {
 | 
				
			||||||
    let genesis_hash = client.get_genesis_hash().map_err(|err| err.to_string())?;
 | 
					        Ok((rpc_addr, RpcClient::new_socket(rpc_addr)))
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        Err(format!(
 | 
				
			||||||
 | 
					            "Entrypoint ({:?}) is not running the RPC service",
 | 
				
			||||||
 | 
					            entrypoint.gossip
 | 
				
			||||||
 | 
					        ))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn check_vote_account(
 | 
				
			||||||
 | 
					    rpc_client: &RpcClient,
 | 
				
			||||||
 | 
					    vote_pubkey: &Pubkey,
 | 
				
			||||||
 | 
					    voting_pubkey: &Pubkey,
 | 
				
			||||||
 | 
					    node_pubkey: &Pubkey,
 | 
				
			||||||
 | 
					) -> Result<(), String> {
 | 
				
			||||||
 | 
					    let found_vote_account = rpc_client
 | 
				
			||||||
 | 
					        .get_account(vote_pubkey)
 | 
				
			||||||
 | 
					        .map_err(|err| format!("Failed to get vote account: {}", err.to_string()))?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if found_vote_account.owner != solana_vote_api::id() {
 | 
				
			||||||
 | 
					        return Err(format!(
 | 
				
			||||||
 | 
					            "not a vote account (owned by {}): {}",
 | 
				
			||||||
 | 
					            found_vote_account.owner, vote_pubkey
 | 
				
			||||||
 | 
					        ));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let found_node_account = rpc_client
 | 
				
			||||||
 | 
					        .get_account(node_pubkey)
 | 
				
			||||||
 | 
					        .map_err(|err| format!("Failed to get identity account: {}", err.to_string()))?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let found_vote_account = solana_vote_api::vote_state::VoteState::from(&found_vote_account);
 | 
				
			||||||
 | 
					    if let Some(found_vote_account) = found_vote_account {
 | 
				
			||||||
 | 
					        if found_vote_account.authorized_voter != *voting_pubkey {
 | 
				
			||||||
 | 
					            return Err(format!(
 | 
				
			||||||
 | 
					                "account's authorized voter ({}) does not match to the given voting keypair ({}).",
 | 
				
			||||||
 | 
					                found_vote_account.authorized_voter, voting_pubkey
 | 
				
			||||||
 | 
					            ));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if found_vote_account.node_pubkey != *node_pubkey {
 | 
				
			||||||
 | 
					            return Err(format!(
 | 
				
			||||||
 | 
					                "account's node pubkey ({}) does not match to the given identity keypair ({}).",
 | 
				
			||||||
 | 
					                found_vote_account.node_pubkey, node_pubkey
 | 
				
			||||||
 | 
					            ));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        return Err(format!("invalid vote account data: {}", vote_pubkey));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Maybe we can calculate minimum voting fee; rather than 1 lamport
 | 
				
			||||||
 | 
					    if found_node_account.lamports <= 1 {
 | 
				
			||||||
 | 
					        return Err(format!(
 | 
				
			||||||
 | 
					            "unfunded identity account ({}): only {} lamports (needs more fund to vote)",
 | 
				
			||||||
 | 
					            node_pubkey, found_node_account.lamports
 | 
				
			||||||
 | 
					        ));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Ok(())
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn initialize_ledger_path(
 | 
				
			||||||
 | 
					    rpc_addr: &std::net::SocketAddr,
 | 
				
			||||||
 | 
					    rpc_client: &RpcClient,
 | 
				
			||||||
 | 
					    ledger_path: &Path,
 | 
				
			||||||
 | 
					    no_genesis_fetch: bool,
 | 
				
			||||||
 | 
					    no_snapshot_fetch: bool,
 | 
				
			||||||
 | 
					) -> Result<Hash, String> {
 | 
				
			||||||
 | 
					    let genesis_hash = rpc_client
 | 
				
			||||||
 | 
					        .get_genesis_hash()
 | 
				
			||||||
 | 
					        .map_err(|err| err.to_string())?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fs::create_dir_all(ledger_path).map_err(|err| err.to_string())?;
 | 
					    fs::create_dir_all(ledger_path).map_err(|err| err.to_string())?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -228,7 +284,7 @@ fn initialize_ledger_path(
 | 
				
			|||||||
        .unwrap_or_else(|err| warn!("Unable to fetch snapshot: {:?}", err));
 | 
					        .unwrap_or_else(|err| warn!("Unable to fetch snapshot: {:?}", err));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    match client.get_slot() {
 | 
					    match rpc_client.get_slot() {
 | 
				
			||||||
        Ok(slot) => info!("Entrypoint currently at slot {}", slot),
 | 
					        Ok(slot) => info!("Entrypoint currently at slot {}", slot),
 | 
				
			||||||
        Err(err) => warn!("Failed to get_slot from entrypoint: {}", err),
 | 
					        Err(err) => warn!("Failed to get_slot from entrypoint: {}", err),
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -265,7 +321,7 @@ pub fn main() {
 | 
				
			|||||||
                .value_name("PATH")
 | 
					                .value_name("PATH")
 | 
				
			||||||
                .takes_value(true)
 | 
					                .takes_value(true)
 | 
				
			||||||
                .validator(is_keypair)
 | 
					                .validator(is_keypair)
 | 
				
			||||||
                .help("File containing the authorized voting keypair.  Default is an ephemeral keypair"),
 | 
					                .help("File containing the authorized voting keypair.  Default is an ephemeral keypair, which may disable voting without --vote-account."),
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        .arg(
 | 
					        .arg(
 | 
				
			||||||
            Arg::with_name("vote_account")
 | 
					            Arg::with_name("vote_account")
 | 
				
			||||||
@@ -273,7 +329,7 @@ pub fn main() {
 | 
				
			|||||||
                .value_name("PUBKEY")
 | 
					                .value_name("PUBKEY")
 | 
				
			||||||
                .takes_value(true)
 | 
					                .takes_value(true)
 | 
				
			||||||
                .validator(is_pubkey_or_keypair)
 | 
					                .validator(is_pubkey_or_keypair)
 | 
				
			||||||
                .help("Public key of the vote account to vote with.  Default is the public key of the voting keypair"),
 | 
					                .help("Public key of the vote account to vote with.  Default is the public key of --voting-keypair"),
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        .arg(
 | 
					        .arg(
 | 
				
			||||||
            Arg::with_name("storage_keypair")
 | 
					            Arg::with_name("storage_keypair")
 | 
				
			||||||
@@ -448,12 +504,14 @@ pub fn main() {
 | 
				
			|||||||
        Keypair::new()
 | 
					        Keypair::new()
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let mut ephemeral_voting_keypair = false;
 | 
				
			||||||
    let voting_keypair = if let Some(identity) = matches.value_of("voting_keypair") {
 | 
					    let voting_keypair = if let Some(identity) = matches.value_of("voting_keypair") {
 | 
				
			||||||
        read_keypair_file(identity).unwrap_or_else(|err| {
 | 
					        read_keypair_file(identity).unwrap_or_else(|err| {
 | 
				
			||||||
            error!("{}: Unable to open keypair file: {}", err, identity);
 | 
					            error!("{}: Unable to open keypair file: {}", err, identity);
 | 
				
			||||||
            exit(1);
 | 
					            exit(1);
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
 | 
					        ephemeral_voting_keypair = true;
 | 
				
			||||||
        Keypair::new()
 | 
					        Keypair::new()
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
    let storage_keypair = if let Some(storage_keypair) = matches.value_of("storage_keypair") {
 | 
					    let storage_keypair = if let Some(storage_keypair) = matches.value_of("storage_keypair") {
 | 
				
			||||||
@@ -465,9 +523,6 @@ pub fn main() {
 | 
				
			|||||||
        Keypair::new()
 | 
					        Keypair::new()
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let vote_account =
 | 
					 | 
				
			||||||
        pubkey_of(&matches, "vote_account").unwrap_or_else(|| voting_keypair.pubkey());
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    let ledger_path = PathBuf::from(matches.value_of("ledger_path").unwrap());
 | 
					    let ledger_path = PathBuf::from(matches.value_of("ledger_path").unwrap());
 | 
				
			||||||
    let entrypoint = matches.value_of("entrypoint");
 | 
					    let entrypoint = matches.value_of("entrypoint");
 | 
				
			||||||
    let init_complete_file = matches.value_of("init_complete_file");
 | 
					    let init_complete_file = matches.value_of("init_complete_file");
 | 
				
			||||||
@@ -481,8 +536,6 @@ pub fn main() {
 | 
				
			|||||||
    validator_config.dev_sigverify_disabled = matches.is_present("dev_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.dev_halt_at_slot = value_t!(matches, "dev_halt_at_slot", Slot).ok();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    validator_config.voting_disabled = matches.is_present("no_voting");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    validator_config.rpc_config.enable_validator_exit = matches.is_present("enable_rpc_exit");
 | 
					    validator_config.rpc_config.enable_validator_exit = matches.is_present("enable_rpc_exit");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    validator_config.rpc_config.drone_addr = matches.value_of("rpc_drone_addr").map(|address| {
 | 
					    validator_config.rpc_config.drone_addr = matches.value_of("rpc_drone_addr").map(|address| {
 | 
				
			||||||
@@ -578,6 +631,21 @@ pub fn main() {
 | 
				
			|||||||
        ]
 | 
					        ]
 | 
				
			||||||
        .join(","),
 | 
					        .join(","),
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if matches.is_present("no_voting") {
 | 
				
			||||||
 | 
					        validator_config.voting_disabled = true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let vote_account = pubkey_of(&matches, "vote_account").unwrap_or_else(|| {
 | 
				
			||||||
 | 
					        // Disable voting because normal (=not bootstrapping) validator rejects
 | 
				
			||||||
 | 
					        // non-voting accounts (= ephemeral keypairs).
 | 
				
			||||||
 | 
					        if ephemeral_voting_keypair && entrypoint.is_some() {
 | 
				
			||||||
 | 
					            warn!("Disabled voting due to the use of ephemeral key for vote account");
 | 
				
			||||||
 | 
					            validator_config.voting_disabled = true;
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        voting_keypair.pubkey()
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    solana_metrics::set_host_id(identity_keypair.pubkey().to_string());
 | 
					    solana_metrics::set_host_id(identity_keypair.pubkey().to_string());
 | 
				
			||||||
    solana_metrics::set_panic_hook("validator");
 | 
					    solana_metrics::set_panic_hook("validator");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -653,8 +721,27 @@ pub fn main() {
 | 
				
			|||||||
            &udp_sockets,
 | 
					            &udp_sockets,
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let (rpc_addr, rpc_client) = create_rpc_client(cluster_entrypoint).unwrap_or_else(|err| {
 | 
				
			||||||
 | 
					            error!("unable to create rpc client: {}", err);
 | 
				
			||||||
 | 
					            std::process::exit(1);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if !validator_config.voting_disabled {
 | 
				
			||||||
 | 
					            check_vote_account(
 | 
				
			||||||
 | 
					                &rpc_client,
 | 
				
			||||||
 | 
					                &vote_account,
 | 
				
			||||||
 | 
					                &voting_keypair.pubkey(),
 | 
				
			||||||
 | 
					                &identity_keypair.pubkey(),
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            .unwrap_or_else(|err| {
 | 
				
			||||||
 | 
					                error!("Failed to check vote account: {}", err);
 | 
				
			||||||
 | 
					                exit(1);
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let genesis_hash = initialize_ledger_path(
 | 
					        let genesis_hash = initialize_ledger_path(
 | 
				
			||||||
            cluster_entrypoint,
 | 
					            &rpc_addr,
 | 
				
			||||||
 | 
					            &rpc_client,
 | 
				
			||||||
            &ledger_path,
 | 
					            &ledger_path,
 | 
				
			||||||
            no_genesis_fetch,
 | 
					            no_genesis_fetch,
 | 
				
			||||||
            no_snapshot_fetch,
 | 
					            no_snapshot_fetch,
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user