diff --git a/genesis/src/main.rs b/genesis/src/main.rs index 4cb4bbb509..be0b0efb0b 100644 --- a/genesis/src/main.rs +++ b/genesis/src/main.rs @@ -41,7 +41,9 @@ use { solana_rent::Rent, solana_rpc_client::rpc_client::RpcClient, solana_rpc_client_api::request::MAX_MULTIPLE_ACCOUNTS, - solana_runtime::genesis_utils::include_alpenglow_bpf_program, + solana_runtime::genesis_utils::{ + bls_pubkey_to_compressed_bytes, include_alpenglow_bpf_program, + }, solana_sdk_ids::system_program, solana_signer::Signer, solana_stake_interface::state::StakeStateV2, @@ -268,13 +270,13 @@ fn add_validator_accounts( let bls_pubkey = bls_pubkeys_iter .next() .expect("Missing BLS pubkey for {identity_pubkey}"); - AlpenglowVoteState::create_account_with_authorized( + vote_state::create_v4_account_with_authorized( identity_pubkey, identity_pubkey, identity_pubkey, - commission, + Some(bls_pubkey_to_compressed_bytes(bls_pubkey)), + commission.into(), AlpenglowVoteState::get_rent_exempt_reserve(rent).max(1), - *bls_pubkey, ) } else { vote_state::create_account_with_authorized( diff --git a/programs/stake/src/stake_state.rs b/programs/stake/src/stake_state.rs index 106e8f03f5..3b820f9e84 100644 --- a/programs/stake/src/stake_state.rs +++ b/programs/stake/src/stake_state.rs @@ -12,8 +12,7 @@ use { solana_rent::Rent, solana_sdk_ids::stake::id, solana_stake_interface::stake_flags::StakeFlags, - solana_vote_interface::state::VoteStateV3, - solana_votor_messages::state::VoteState as AlpenglowVoteState, + solana_vote_interface::state::{VoteStateV3, VoteStateV4}, }; // utility function, used by Stakes, tests @@ -41,12 +40,7 @@ pub fn meta_from(account: &AccountSharedData) -> Option { from(account).and_then(|state: StakeStateV2| state.meta()) } -pub(crate) fn new_stake_with_credits( - stake: u64, - voter_pubkey: &Pubkey, - credits: u64, - activation_epoch: Epoch, -) -> Stake { +fn new_stake(stake: u64, voter_pubkey: &Pubkey, credits: u64, activation_epoch: Epoch) -> Stake { Stake { delegation: Delegation::new(voter_pubkey, stake, activation_epoch), credits_observed: credits, @@ -94,6 +88,25 @@ pub fn create_account( rent, lamports, Epoch::MAX, + false, + ) +} + +pub fn create_alpenglow_account( + authorized: &Pubkey, + voter_pubkey: &Pubkey, + vote_account: &AccountSharedData, + rent: &Rent, + lamports: u64, +) -> AccountSharedData { + do_create_account( + authorized, + voter_pubkey, + vote_account, + rent, + lamports, + Epoch::MAX, + true, ) } @@ -104,18 +117,16 @@ fn do_create_account( rent: &Rent, lamports: u64, activation_epoch: Epoch, + is_alpenglow: bool, ) -> AccountSharedData { let mut stake_account = AccountSharedData::new(lamports, StakeStateV2::size_of(), &id()); - let credits = if solana_votor_messages::check_id(vote_account.owner()) { - AlpenglowVoteState::deserialize(vote_account.data()) - .expect("alpenglow_vote_state") - .epoch_credits() - .credits() + let credits = if is_alpenglow { + let vote_state_v4 = VoteStateV4::deserialize(vote_account.data(), voter_pubkey).unwrap(); + vote_state_v4.epoch_credits.last().map_or(0, |(_, c, _)| *c) } else { - VoteStateV3::deserialize(vote_account.data()) - .expect("vote_state") - .credits() + let vote_state = VoteStateV3::deserialize(vote_account.data()).expect("vote_state"); + vote_state.credits() }; let rent_exempt_reserve = rent.minimum_balance(stake_account.data().len()); @@ -127,7 +138,7 @@ fn do_create_account( rent_exempt_reserve, ..Meta::default() }, - new_stake_with_credits( + new_stake( lamports - rent_exempt_reserve, // underflow is an error, is basically: assert!(lamports > rent_exempt_reserve); voter_pubkey, credits, diff --git a/programs/vote/src/vote_state/mod.rs b/programs/vote/src/vote_state/mod.rs index 2839175a6e..db8c95e4dc 100644 --- a/programs/vote/src/vote_state/mod.rs +++ b/programs/vote/src/vote_state/mod.rs @@ -16,7 +16,7 @@ use { solana_rent::Rent, solana_slot_hashes::SlotHash, solana_transaction_context::{BorrowedInstructionAccount, IndexOfAccount, InstructionContext}, - solana_vote_interface::{error::VoteError, program::id}, + solana_vote_interface::{authorized_voters::AuthorizedVoters, error::VoteError, program::id}, std::{ cmp::Ordering, collections::{HashSet, VecDeque}, @@ -1044,6 +1044,51 @@ pub fn create_account_with_authorized( vote_account } +// TODO(wen): when we have VoteStateV4::new(), switch all users there. +pub fn new_v4_vote_state( + node_pubkey: &Pubkey, + authorized_voter: &Pubkey, + authorized_withdrawer: &Pubkey, + bls_pubkey_compressed: Option<[u8; BLS_PUBLIC_KEY_COMPRESSED_SIZE]>, + inflation_rewards_commission_bps: u16, +) -> VoteStateV4 { + VoteStateV4 { + node_pubkey: *node_pubkey, + authorized_voters: AuthorizedVoters::new(0, *authorized_voter), + authorized_withdrawer: *authorized_withdrawer, + bls_pubkey_compressed, + inflation_rewards_commission_bps, + ..VoteStateV4::default() + } +} + +pub fn create_v4_account_with_authorized( + node_pubkey: &Pubkey, + authorized_voter: &Pubkey, + authorized_withdrawer: &Pubkey, + bls_pubkey_compressed: Option<[u8; BLS_PUBLIC_KEY_COMPRESSED_SIZE]>, + inflation_rewards_commission_bps: u16, + lamports: u64, +) -> AccountSharedData { + let mut vote_account = AccountSharedData::new(lamports, VoteStateV4::size_of(), &id()); + + let vote_state = new_v4_vote_state( + node_pubkey, + authorized_voter, + authorized_withdrawer, + bls_pubkey_compressed, + inflation_rewards_commission_bps, + ); + + VoteStateV4::serialize( + &VoteStateVersions::V4(Box::new(vote_state)), + vote_account.data_as_mut_slice(), + ) + .unwrap(); + + vote_account +} + // create_account() should be removed, use create_account_with_authorized() instead pub fn create_account( vote_pubkey: &Pubkey, @@ -1084,7 +1129,7 @@ mod tests { fn create_test_account() -> (Pubkey, RefCell) { let rent = Rent::default(); - let balance = VoteStateV3::get_rent_exempt_reserve(&rent); + let balance = rent.minimum_balance(VoteStateV3::size_of()); let vote_pubkey = solana_pubkey::new_rand(); ( vote_pubkey, @@ -1223,7 +1268,7 @@ mod tests { // Test that when the feature is enabled, if the vote account does have sufficient lamports, the // new vote state is written out assert_eq!( - borrowed_account.set_lamports(rent.minimum_balance(VoteStateV3::size_of()),), + borrowed_account.set_lamports(rent.minimum_balance(VoteStateV3::size_of())), Ok(()) ); assert_eq!( @@ -3535,4 +3580,40 @@ mod tests { expected_allowed ); } + + #[test] + fn test_create_v4_account_with_authorized() { + let node_pubkey = Pubkey::new_unique(); + let authorized_voter = Pubkey::new_unique(); + let authorized_withdrawer = Pubkey::new_unique(); + let bls_pubkey_compressed = [42; 48]; + let inflation_rewards_commission_bps = 10000; + let lamports = 100; + let vote_account = create_v4_account_with_authorized( + &node_pubkey, + &authorized_voter, + &authorized_withdrawer, + Some(bls_pubkey_compressed), + inflation_rewards_commission_bps, + lamports, + ); + assert_eq!(vote_account.lamports(), lamports); + assert_eq!(vote_account.owner(), &id()); + assert_eq!(vote_account.data().len(), VoteStateV4::size_of()); + let vote_state_v4 = VoteStateV4::deserialize(vote_account.data(), &node_pubkey).unwrap(); + assert_eq!(vote_state_v4.node_pubkey, node_pubkey); + assert_eq!( + vote_state_v4.authorized_voters, + AuthorizedVoters::new(0, authorized_voter) + ); + assert_eq!(vote_state_v4.authorized_withdrawer, authorized_withdrawer); + assert_eq!( + vote_state_v4.bls_pubkey_compressed, + Some(bls_pubkey_compressed) + ); + assert_eq!( + vote_state_v4.inflation_rewards_commission_bps, + inflation_rewards_commission_bps + ); + } } diff --git a/runtime/src/bank/serde_snapshot.rs b/runtime/src/bank/serde_snapshot.rs index 24f965c1b5..53cb408c7b 100644 --- a/runtime/src/bank/serde_snapshot.rs +++ b/runtime/src/bank/serde_snapshot.rs @@ -28,7 +28,6 @@ mod tests { solana_pubkey::Pubkey, solana_signer::Signer, solana_vote::vote_account::VoteAccount, - solana_votor_messages::state::VoteState as AlpenglowVoteState, std::{ collections::HashMap, io::{BufReader, BufWriter, Cursor}, @@ -208,7 +207,7 @@ mod tests { // Give some real stake distribution and generate real VersionedEpochStakes, to // make sure bls_pubkey_to_rank_map is populated correctly after deserialize. let vote_account = if is_alpenglow { - VoteAccount::new_from_alpenglow_vote_state(&AlpenglowVoteState::default()) + VoteAccount::new_random_alpenglow() } else { VoteAccount::new_random() }; diff --git a/runtime/src/bank/tests.rs b/runtime/src/bank/tests.rs index 084e68dab9..73a6f471f6 100644 --- a/runtime/src/bank/tests.rs +++ b/runtime/src/bank/tests.rs @@ -119,7 +119,7 @@ use { vote_instruction, vote_state::{ self, create_account_with_authorized, BlockTimestamp, VoteAuthorize, VoteInit, - VoteStateV3, VoteStateVersions, MAX_LOCKOUT_HISTORY, + VoteStateV3, VoteStateV4, VoteStateVersions, MAX_LOCKOUT_HISTORY, }, }, spl_generic_token::token, @@ -731,13 +731,20 @@ where // generate some rewards if is_alpenglow { let mut vote_state = - *solana_votor_messages::state::VoteState::deserialize(vote_account.data()).unwrap(); + VoteStateV4::deserialize(vote_account.data(), &Pubkey::default()).unwrap(); + vote_state.epoch_credits.push((0, 0, 0)); for _ in 0..MAX_LOCKOUT_HISTORY + 42 { - let mut epoch_credits = *vote_state.epoch_credits(); - epoch_credits.set_credits(epoch_credits.credits() + 16); - vote_state.set_epoch_credits(epoch_credits); - vote_state.serialize_into(vote_account.data_as_mut_slice()); + let (_, current_credits, _) = vote_state.epoch_credits.last_mut().unwrap(); + *current_credits += 16; + let versioned = VoteStateVersions::V4(Box::new(vote_state.clone())); + vote_state::to(&versioned, &mut vote_account).unwrap(); bank0.store_account_and_update_capitalization(&vote_id, &vote_account); + match versioned { + VoteStateVersions::V4(v) => { + vote_state = *v; + } + _ => panic!("Has to be of type Current"), + }; } } else { let mut vote_state = Some(vote_state::from(&vote_account).unwrap()); diff --git a/runtime/src/epoch_stakes.rs b/runtime/src/epoch_stakes.rs index 6d78f4cb17..27fb8f71dd 100644 --- a/runtime/src/epoch_stakes.rs +++ b/runtime/src/epoch_stakes.rs @@ -1,7 +1,7 @@ use { crate::stakes::SerdeStakesToStakeFormat, serde::{Deserialize, Serialize}, - solana_bls_signatures::Pubkey as BLSPubkey, + solana_bls_signatures::{Pubkey as BLSPubkey, PubkeyCompressed as BLSPubkeyCompressed}, solana_clock::Epoch, solana_pubkey::Pubkey, solana_vote::vote_account::VoteAccountsHashMap, @@ -13,7 +13,6 @@ use { pub type NodeIdToVoteAccounts = HashMap; pub type EpochAuthorizedVoters = HashMap; -pub type SortedPubkeys = Vec<(Pubkey, BLSPubkey)>; #[derive(Clone, Debug, Default)] #[cfg_attr(feature = "frozen-abi", derive(AbiExample))] @@ -31,9 +30,13 @@ impl BLSPubkeyToRankMap { .iter() .filter_map(|(pubkey, (stake, account))| { if *stake > 0 { - account - .bls_pubkey() - .map(|bls_pubkey| (*pubkey, *bls_pubkey, *stake)) + account.vote_state_view().and_then(|vote_state| { + let bls_pubkey_compressed_bytes = vote_state.bls_pubkey_compressed()?; + let bls_pubkey_compressed = + BLSPubkeyCompressed(bls_pubkey_compressed_bytes); + let bls_pubkey = BLSPubkey::try_from(bls_pubkey_compressed).ok()?; + Some((*pubkey, bls_pubkey, *stake)) + }) } else { None } @@ -200,18 +203,20 @@ impl VersionedEpochStakes { let epoch_authorized_voters = epoch_vote_accounts .iter() .filter_map(|(key, (stake, account))| { + let vote_state = account.vote_state_view()?; + if *stake > 0 { if let Some(authorized_voter) = - account.get_authorized_voter(leader_schedule_epoch) + vote_state.get_authorized_voter(leader_schedule_epoch) { let node_vote_accounts = node_id_to_vote_accounts - .entry(*account.node_pubkey()) + .entry(*vote_state.node_pubkey()) .or_default(); node_vote_accounts.total_stake += stake; node_vote_accounts.vote_accounts.push(*key); - Some((*key, authorized_voter)) + Some((*key, *authorized_voter)) } else { None } @@ -231,11 +236,15 @@ impl VersionedEpochStakes { #[cfg(test)] pub(crate) mod tests { use { - super::*, solana_account::AccountSharedData, + super::*, + crate::genesis_utils::bls_pubkey_to_compressed_bytes, + solana_account::AccountSharedData, solana_bls_signatures::keypair::Keypair as BLSKeypair, solana_vote::vote_account::VoteAccount, - solana_vote_program::vote_state::create_account_with_authorized, - solana_votor_messages::state::VoteState as AlpenglowVoteState, std::iter, + solana_vote_program::vote_state::{ + create_account_with_authorized, create_v4_account_with_authorized, + }, + std::iter, test_case::test_case, }; @@ -258,15 +267,14 @@ pub(crate) mod tests { node_id, iter::repeat_with(|| { let authorized_voter = solana_pubkey::new_rand(); - let bls_keypair = BLSKeypair::new(); let account = if is_alpenglow { - AlpenglowVoteState::create_account_with_authorized( + create_v4_account_with_authorized( &node_id, &authorized_voter, &node_id, + Some(bls_pubkey_to_compressed_bytes(&BLSKeypair::new().public)), 0, 100, - bls_keypair.public, ) } else { create_account_with_authorized( @@ -400,6 +408,7 @@ pub(crate) mod tests { #[test_case(1; "single_vote_account")] #[test_case(2; "multiple_vote_accounts")] fn test_bls_pubkey_rank_map(num_vote_accounts_per_node: usize) { + solana_logger::setup(); let num_nodes = 10; let num_vote_accounts = num_nodes * num_vote_accounts_per_node; @@ -416,13 +425,17 @@ pub(crate) mod tests { let bls_pubkey_to_rank_map = epoch_stakes.bls_pubkey_to_rank_map(); assert_eq!(bls_pubkey_to_rank_map.len(), num_vote_accounts); for (pubkey, (_, vote_account)) in epoch_vote_accounts { - let index = bls_pubkey_to_rank_map - .get_rank(vote_account.bls_pubkey().unwrap()) - .unwrap(); + let vote_state_view = vote_account.vote_state_view().unwrap(); + let bls_pubkey_compressed = bincode::deserialize::( + &vote_state_view.bls_pubkey_compressed().unwrap(), + ) + .unwrap(); + let bls_pubkey = BLSPubkey::try_from(bls_pubkey_compressed).unwrap(); + let index = bls_pubkey_to_rank_map.get_rank(&bls_pubkey).unwrap(); assert!(index >= &0 && index < &(num_vote_accounts as u16)); assert_eq!( bls_pubkey_to_rank_map.get_pubkey(*index as usize), - Some(&(pubkey, *vote_account.bls_pubkey().unwrap())) + Some(&(pubkey, bls_pubkey)) ); } diff --git a/runtime/src/genesis_utils.rs b/runtime/src/genesis_utils.rs index c49a1e17af..313598e385 100644 --- a/runtime/src/genesis_utils.rs +++ b/runtime/src/genesis_utils.rs @@ -2,7 +2,10 @@ use { agave_feature_set::{FeatureSet, FEATURE_NAMES}, log::*, solana_account::{Account, AccountSharedData}, - solana_bls_signatures::{keypair::Keypair as BLSKeypair, Pubkey as BLSPubkey}, + solana_bls_signatures::{ + keypair::Keypair as BLSKeypair, pubkey::PubkeyCompressed as BLSPubkeyCompressed, + Pubkey as BLSPubkey, + }, solana_cluster_type::ClusterType, solana_feature_gate_interface::{self as feature, Feature}, solana_fee_calculator::FeeRateGovernor, @@ -17,10 +20,9 @@ use { solana_stake_interface::state::StakeStateV2, solana_stake_program::stake_state, solana_system_interface::program as system_program, + solana_vote_interface::state::BLS_PUBLIC_KEY_COMPRESSED_SIZE, solana_vote_program::vote_state, - solana_votor_messages::{ - self, consensus_message::BLS_KEYPAIR_DERIVE_SEED, state::VoteState as AlpenglowVoteState, - }, + solana_votor_messages::{self, consensus_message::BLS_KEYPAIR_DERIVE_SEED}, std::{borrow::Borrow, fs::File, io::Read}, }; @@ -174,24 +176,34 @@ pub fn create_genesis_config_with_vote_accounts_and_cluster_type( // Create accounts let node_account = Account::new(VALIDATOR_LAMPORTS, 0, &system_program::id()); let vote_account = if alpenglow_so_path.is_some() { - AlpenglowVoteState::create_account_with_authorized( + vote_state::create_v4_account_with_authorized( &node_pubkey, &vote_pubkey, &vote_pubkey, + Some(bls_pubkey_to_compressed_bytes(&bls_pubkey)), 0, *stake, - bls_pubkey, ) } else { vote_state::create_account(&vote_pubkey, &node_pubkey, 0, *stake) }; - let stake_account = Account::from(stake_state::create_account( - &stake_pubkey, - &vote_pubkey, - &vote_account, - &genesis_config_info.genesis_config.rent, - *stake, - )); + let stake_account = if alpenglow_so_path.is_some() { + Account::from(stake_state::create_alpenglow_account( + &stake_pubkey, + &vote_pubkey, + &vote_account, + &genesis_config_info.genesis_config.rent, + *stake, + )) + } else { + Account::from(stake_state::create_account( + &stake_pubkey, + &vote_pubkey, + &vote_account, + &genesis_config_info.genesis_config.rent, + *stake, + )) + }; let vote_account = Account::from(vote_account); @@ -351,6 +363,13 @@ pub fn activate_feature(genesis_config: &mut GenesisConfig, feature_id: Pubkey) ); } +pub fn bls_pubkey_to_compressed_bytes( + bls_pubkey: &BLSPubkey, +) -> [u8; BLS_PUBLIC_KEY_COMPRESSED_SIZE] { + let key = BLSPubkeyCompressed::try_from(bls_pubkey).unwrap(); + bincode::serialize(&key).unwrap().try_into().unwrap() +} + pub fn include_alpenglow_bpf_program(genesis_config: &mut GenesisConfig, alpenglow_so_path: &str) { // Parse out the elf let mut program_data_elf: Vec = vec![]; @@ -428,13 +447,13 @@ pub fn create_genesis_config_with_leader_ex_no_features( alpenglow_so_path: Option<&str>, ) -> GenesisConfig { let validator_vote_account = if alpenglow_so_path.is_some() { - AlpenglowVoteState::create_account_with_authorized( + vote_state::create_v4_account_with_authorized( validator_pubkey, validator_vote_account_pubkey, validator_vote_account_pubkey, + validator_bls_pubkey.map(bls_pubkey_to_compressed_bytes), 0, validator_stake_lamports, - *validator_bls_pubkey.unwrap(), ) } else { vote_state::create_account( @@ -445,13 +464,23 @@ pub fn create_genesis_config_with_leader_ex_no_features( ) }; - let validator_stake_account = stake_state::create_account( - validator_stake_account_pubkey, - validator_vote_account_pubkey, - &validator_vote_account, - &rent, - validator_stake_lamports, - ); + let validator_stake_account = if alpenglow_so_path.is_some() { + stake_state::create_alpenglow_account( + validator_stake_account_pubkey, + validator_vote_account_pubkey, + &validator_vote_account, + &rent, + validator_stake_lamports, + ) + } else { + stake_state::create_account( + validator_stake_account_pubkey, + validator_vote_account_pubkey, + &validator_vote_account, + &rent, + validator_stake_lamports, + ) + }; initial_accounts.push(( *mint_pubkey, diff --git a/runtime/src/inflation_rewards/mod.rs b/runtime/src/inflation_rewards/mod.rs index fd3ccf3852..bf837163d8 100644 --- a/runtime/src/inflation_rewards/mod.rs +++ b/runtime/src/inflation_rewards/mod.rs @@ -260,8 +260,7 @@ mod tests { use { self::points::null_tracer, super::*, solana_native_token::LAMPORTS_PER_SOL, solana_pubkey::Pubkey, solana_stake_interface::state::Delegation, - solana_vote_program::vote_state::VoteStateV3, - solana_votor_messages::state::VoteState as AlpenglowVoteState, test_case::test_case, + solana_vote_program::vote_state::VoteStateV3, test_case::test_case, }; fn new_stake( @@ -701,7 +700,6 @@ mod tests { #[test] fn test_stake_state_calculate_points_with_typical_values() { let vote_state = VoteStateV3::default(); - let alpenglow_vote_state = AlpenglowVoteState::default(); // bootstrap means fully-vested stake at epoch 0 with // 10_000_000 SOL is a big but not unreasaonable stake @@ -737,7 +735,7 @@ mod tests { rewards: 1_000_000_000, points: 1 }, - &VoteAccount::new_from_alpenglow_vote_state(&alpenglow_vote_state), + &VoteAccount::new_random_alpenglow(), &StakeHistory::default(), null_tracer(), None, diff --git a/runtime/src/stakes.rs b/runtime/src/stakes.rs index cd7d33f01b..13ac3eb264 100644 --- a/runtime/src/stakes.rs +++ b/runtime/src/stakes.rs @@ -556,15 +556,15 @@ fn refresh_vote_accounts( pub(crate) mod tests { use { super::*, + crate::genesis_utils::bls_pubkey_to_compressed_bytes, rayon::ThreadPoolBuilder, solana_account::WritableAccount, solana_bls_signatures::keypair::Keypair as BLSKeypair, solana_pubkey::Pubkey, solana_rent::Rent, solana_stake_program::stake_state, - solana_vote_interface::state::{VoteStateV3, VoteStateVersions}, + solana_vote_interface::state::{VoteStateV3, VoteStateV4, VoteStateVersions}, solana_vote_program::vote_state, - solana_votor_messages::state::VoteState as AlpenglowVoteState, test_case::test_case, }; @@ -576,13 +576,13 @@ pub(crate) mod tests { let vote_pubkey = solana_pubkey::new_rand(); let bls_keypair = BLSKeypair::new(); let vote_account = if is_alpenglow { - AlpenglowVoteState::create_account_with_authorized( - &vote_pubkey, + vote_state::create_v4_account_with_authorized( + &solana_pubkey::new_rand(), &vote_pubkey, &vote_pubkey, + Some(bls_pubkey_to_compressed_bytes(&bls_keypair.public)), 0, 1, - bls_keypair.public, ) } else { vote_state::create_account(&vote_pubkey, &solana_pubkey::new_rand(), 0, 1) @@ -695,9 +695,9 @@ pub(crate) mod tests { stakes_cache.check_and_store(&stake11_pubkey, &stake11_account, None); let vote11_node_pubkey = if is_alpenglow { - *AlpenglowVoteState::deserialize(vote11_account.data()) + VoteStateV4::deserialize(vote11_account.data(), &vote11_pubkey) .unwrap() - .node_pubkey() + .node_pubkey } else { vote_state::from(&vote11_account).unwrap().node_pubkey }; @@ -763,9 +763,9 @@ pub(crate) mod tests { // Vote account uninitialized if is_alpenglow { - vote_account.set_data(cache_data.clone()); - let default_vote_state = AlpenglowVoteState::default(); - default_vote_state.serialize_into(vote_account.data_as_mut_slice()); + let default_vote_state = VoteStateV4::default(); + let versioned = VoteStateVersions::new_v4(default_vote_state); + vote_state::to(&versioned, &mut vote_account).unwrap(); } else { let default_vote_state = VoteStateV3::default(); let versioned = VoteStateVersions::new_v3(default_vote_state); diff --git a/vote/src/vote_account.rs b/vote/src/vote_account.rs index 307581230a..9c9ec5c2fc 100644 --- a/vote/src/vote_account.rs +++ b/vote/src/vote_account.rs @@ -1,3 +1,6 @@ +// The following imports are only needed for dev-context-only-utils. +#[cfg(feature = "dev-context-only-utils")] +use solana_vote_interface::state::{VoteStateV3, VoteStateV4, VoteStateVersions}; use { crate::vote_state_view::VoteStateView, itertools::Itertools, @@ -6,11 +9,15 @@ use { ser::{Serialize, Serializer}, }, solana_account::{AccountSharedData, ReadableAccount}, - solana_bls_signatures::Pubkey as BLSPubkey, + solana_bls_signatures::{ + keypair::Keypair as BLSKeypair, + pubkey::{AsPubkey, PubkeyCompressed as BLSPubkeyCompressed}, + Pubkey as BLSPubkey, + }, solana_instruction::error::InstructionError, solana_program::program_error::ProgramError, solana_pubkey::Pubkey, - solana_vote_interface::state::BlockTimestamp, + solana_vote_interface::{authorized_voters::AuthorizedVoters, state::BlockTimestamp}, solana_votor_messages::state::VoteState as AlpenglowVoteState, std::{ cmp::Ordering, @@ -22,12 +29,6 @@ use { }, thiserror::Error, }; -// The following imports are only needed for dev-context-only-utils. -#[cfg(feature = "dev-context-only-utils")] -use { - solana_account::WritableAccount, - solana_vote_interface::state::{VoteStateV3, VoteStateVersions}, -}; #[cfg_attr(feature = "frozen-abi", derive(AbiExample))] #[derive(Clone, Debug, PartialEq)] @@ -194,14 +195,14 @@ impl VoteAccount { } } - pub fn bls_pubkey(&self) -> Option<&BLSPubkey> { + pub fn bls_pubkey(&self) -> Option { match &self.0.vote_state_view { - VoteAccountState::TowerBFT(_) => None, - VoteAccountState::Alpenglow => Some( - AlpenglowVoteState::deserialize(self.0.account.data()) - .unwrap() - .bls_pubkey(), - ), + VoteAccountState::TowerBFT(vote_state) => vote_state.bls_pubkey_compressed().map(|b| { + let bls_pubkey_compressed = + bincode::deserialize::(&b).unwrap(); + BLSPubkeyCompressed::try_as_affine(&bls_pubkey_compressed).unwrap() + }), + VoteAccountState::Alpenglow => None, } } @@ -252,13 +253,23 @@ impl VoteAccount { } #[cfg(feature = "dev-context-only-utils")] - pub fn new_from_alpenglow_vote_state(vote_state: &AlpenglowVoteState) -> VoteAccount { - let mut account = AccountSharedData::new( + pub fn new_random_alpenglow() -> VoteAccount { + let bls_pubkey_compressed: BLSPubkeyCompressed = + BLSKeypair::new().public.try_into().unwrap(); + let bls_pubkey_compressed_buffer = bincode::serialize(&bls_pubkey_compressed).unwrap(); + let vote_state = VoteStateV4 { + node_pubkey: Pubkey::new_unique(), + authorized_voters: AuthorizedVoters::new(0, Pubkey::new_unique()), + authorized_withdrawer: Pubkey::new_unique(), + bls_pubkey_compressed: Some(bls_pubkey_compressed_buffer.try_into().unwrap()), + ..VoteStateV4::default() + }; + let account = AccountSharedData::new_data( 100, // lamports - AlpenglowVoteState::size(), - &solana_votor_messages::id(), - ); - vote_state.serialize_into(account.data_as_mut_slice()); + &VoteStateVersions::new_v4(vote_state), + &solana_sdk_ids::vote::id(), // owner + ) + .unwrap(); VoteAccount::try_from(account).unwrap() } diff --git a/votor/src/voting_utils.rs b/votor/src/voting_utils.rs index 392571fb68..1299f2ed4b 100644 --- a/votor/src/voting_utils.rs +++ b/votor/src/voting_utils.rs @@ -6,7 +6,10 @@ use { voting_service::BLSOp, }, crossbeam_channel::{SendError, Sender}, - solana_bls_signatures::{keypair::Keypair as BLSKeypair, BlsError, Pubkey as BLSPubkey}, + solana_bls_signatures::{ + keypair::Keypair as BLSKeypair, pubkey::PubkeyCompressed as BLSPubkeyCompressed, BlsError, + Pubkey as BLSPubkey, + }, solana_clock::Slot, solana_keypair::Keypair, solana_pubkey::Pubkey, @@ -169,34 +172,43 @@ pub fn generate_vote_tx( let Some(vote_account) = bank.get_vote_account(&vote_account_pubkey) else { return GenerateVoteTxResult::VoteAccountNotFound(vote_account_pubkey); }; - let Some(vote_state) = vote_account.alpenglow_vote_state() else { + let Some(vote_state_view) = vote_account.vote_state_view() else { return GenerateVoteTxResult::NoVoteState(vote_account_pubkey); }; - if *vote_state.node_pubkey() != context.identity_keypair.pubkey() { + if vote_state_view.node_pubkey() != &context.identity_keypair.pubkey() { info!( "Vote account node_pubkey mismatch: {} (expected: {}). Unable to vote", - vote_state.node_pubkey(), + vote_state_view.node_pubkey(), context.identity_keypair.pubkey() ); return GenerateVoteTxResult::HotSpare; } - bls_pubkey_in_vote_account = match vote_account.bls_pubkey() { + let bls_pubkey_serialized = match vote_state_view.bls_pubkey_compressed() { None => { panic!( "No BLS pubkey in vote account {}", context.identity_keypair.pubkey() ); } - Some(key) => *key, + Some(key) => key, }; - - let Some(authorized_voter_pubkey) = vote_state.get_authorized_voter(bank.epoch()) else { + bls_pubkey_in_vote_account = + (bincode::deserialize::(&bls_pubkey_serialized).unwrap()) + .try_into() + .unwrap_or_else(|_| { + panic!( + "Failed to decompress BLS pubkey in vote account {}", + context.identity_keypair.pubkey() + ); + }); + let Some(authorized_voter_pubkey) = vote_state_view.get_authorized_voter(bank.epoch()) + else { return GenerateVoteTxResult::NoAuthorizedVoter(vote_account_pubkey, bank.epoch()); }; let Some(keypair) = authorized_voter_keypairs .iter() - .find(|keypair| keypair.pubkey() == authorized_voter_pubkey) + .find(|keypair| &keypair.pubkey() == authorized_voter_pubkey) else { warn!( "The authorized keypair {authorized_voter_pubkey} for vote account \