Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions local-cluster/src/local_cluster.rs
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,7 @@ impl LocalCluster {
&keys_in_genesis,
stakes_in_genesis,
config.cluster_type,
false,
);
genesis_config.accounts.extend(
config
Expand Down
1 change: 1 addition & 0 deletions program-test/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -806,6 +806,7 @@ impl ProgramTest {
&bootstrap_validator_pubkey,
&voting_keypair.pubkey(),
&Pubkey::new_unique(),
None,
bootstrap_validator_stake_lamports,
42,
fee_rate_governor,
Expand Down
1 change: 1 addition & 0 deletions programs/sbf/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions programs/sbf/tests/programs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1661,6 +1661,7 @@ fn get_stable_genesis_config() -> GenesisConfigInfo {
&validator_pubkey,
&voting_keypair.pubkey(),
&stake_pubkey,
None,
bootstrap_validator_stake_lamports(),
42,
FeeRateGovernor::new(0, 0), // most tests can't handle transaction fees
Expand Down
41 changes: 31 additions & 10 deletions programs/stake/src/stake_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use {
solana_rent::Rent,
solana_sdk_ids::stake::id,
solana_stake_interface::stake_flags::StakeFlags,
solana_vote_interface::state::VoteStateV3,
solana_vote_interface::state::{VoteStateV3, VoteStateV4},
};

// utility function, used by Stakes, tests
Expand Down Expand Up @@ -40,15 +40,10 @@ pub fn meta_from(account: &AccountSharedData) -> Option<Meta> {
from(account).and_then(|state: StakeStateV2| state.meta())
}

fn new_stake(
stake: u64,
voter_pubkey: &Pubkey,
vote_state: &VoteStateV3,
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: vote_state.credits(),
credits_observed: credits,
}
}

Expand Down Expand Up @@ -93,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,
)
}

Expand All @@ -103,10 +117,17 @@ 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 vote_state = VoteStateV3::deserialize(vote_account.data()).expect("vote_state");
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 {
let vote_state = VoteStateV3::deserialize(vote_account.data()).expect("vote_state");
vote_state.credits()
};
Comment on lines +124 to +130
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm currently adding the implementation of v4 to the Vote program, and I think this change will have to get captured in that PR, so that it coincides with the feature gate I'm adding. In fact, I believe I need to add this now to get it to pass CI.

Is it feasible to rebase this onto #8163?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to make sure we are on the same page:
What we needed for Alpenglow upstreaming:

  1. Adding v4 account for tests: create_v4_account_with_authorized was added in Add create_v4_account_with_authorized and actually populate bls_pubkey_to_rank_map in EpochStakes. #8103 as a temp workardound, but after vote-program: handler: init v4 support #8120 we should convert it to create_new_vote_state_v4_for_tests
  2. We need to create v4 account only genesis config:
    2.1 We are adding genesis util functions here
    2.2 To make stake account creation work we are performing the operation here, you are saying once vote-program: outfit to fully support vote state v4 #8163 lands we should convert that to
    let mut vote_state = VoteStateHandler::deserialize_and_convert(vote_account, VoteStateTargetVersion::V4)?;

Of course we can do that. Do you know when #8163 will land?

Copy link
Copy Markdown
Author

@wen-coding wen-coding Sep 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, create_v4_account_with_authorized uses create_new_vote_state_v4_for_tests, so I guess we are good there. Is the only difference on #8163 to change:
let vote_state_v4 = VoteStateV4::deserialize(vote_account.data(), voter_pubkey).unwrap();
to
let vote_state = VoteStateHandler::deserialize_and_convert(vote_account, VoteStateTargetVersion::V4)?;
?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, exactly, and that should be covered by my PR once it's fully updated, since I'll need to update any callsites that don't yet know about the v4 state feature gate.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool, so sounds like it works both ways, if you manage to submit #8163 first, then I'll sync and change to the above call. If we submit this PR first, then it's just one-line change, so shouldn't be a big deal either.

In that case, is it okay if we ship this first to unblock other Alpenglow upstreaming?

Does rest of this PR look roughly correct to you?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can really only attest to the program stuff. I can't offer much of a review on the consensus side. But if you need the change immediately, sure, you can ship.


let rent_exempt_reserve = rent.minimum_balance(stake_account.data().len());

Expand All @@ -120,7 +141,7 @@ fn do_create_account(
new_stake(
lamports - rent_exempt_reserve, // underflow is an error, is basically: assert!(lamports > rent_exempt_reserve);
voter_pubkey,
&vote_state,
credits,
activation_epoch,
),
StakeFlags::empty(),
Expand Down
1 change: 1 addition & 0 deletions runtime/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ solana-version = { workspace = true }
solana-vote = { workspace = true }
solana-vote-interface = { workspace = true }
solana-vote-program = { workspace = true }
solana-votor-messages = { workspace = true }
spl-generic-token = { workspace = true }
static_assertions = { workspace = true }
strum = { workspace = true, features = ["derive"] }
Expand Down
135 changes: 114 additions & 21 deletions runtime/src/genesis_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ use {
agave_feature_set::{FeatureSet, FEATURE_NAMES},
log::*,
solana_account::{Account, AccountSharedData},
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,
Expand All @@ -15,7 +19,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::consensus_message::BLS_KEYPAIR_DERIVE_SEED,
std::borrow::Borrow,
};

Expand Down Expand Up @@ -99,6 +105,21 @@ pub fn create_genesis_config_with_vote_accounts(
voting_keypairs,
stakes,
ClusterType::Development,
false,
)
}

pub fn create_genesis_config_with_alpenglow_vote_accounts(
mint_lamports: u64,
voting_keypairs: &[impl Borrow<ValidatorVoteKeypairs>],
stakes: Vec<u64>,
) -> GenesisConfigInfo {
create_genesis_config_with_vote_accounts_and_cluster_type(
mint_lamports,
voting_keypairs,
stakes,
ClusterType::Development,
true,
)
}

Expand All @@ -107,6 +128,7 @@ pub fn create_genesis_config_with_vote_accounts_and_cluster_type(
voting_keypairs: &[impl Borrow<ValidatorVoteKeypairs>],
stakes: Vec<u64>,
cluster_type: ClusterType,
is_alpenglow: bool,
) -> GenesisConfigInfo {
assert!(!voting_keypairs.is_empty());
assert_eq!(voting_keypairs.len(), stakes.len());
Expand All @@ -115,12 +137,23 @@ pub fn create_genesis_config_with_vote_accounts_and_cluster_type(
let voting_keypair = voting_keypairs[0].borrow().vote_keypair.insecure_clone();

let validator_pubkey = voting_keypairs[0].borrow().node_keypair.pubkey();
let validator_bls_pubkey = if is_alpenglow {
let bls_keypair = BLSKeypair::derive_from_signer(
&voting_keypairs[0].borrow().vote_keypair,
BLS_KEYPAIR_DERIVE_SEED,
)
.unwrap();
Some(bls_pubkey_to_compressed_bytes(&bls_keypair.public))
} else {
None
};
let genesis_config = create_genesis_config_with_leader_ex(
mint_lamports,
&mint_keypair.pubkey(),
&validator_pubkey,
&voting_keypairs[0].borrow().vote_keypair.pubkey(),
&voting_keypairs[0].borrow().stake_keypair.pubkey(),
validator_bls_pubkey,
stakes[0],
VALIDATOR_LAMPORTS,
FeeRateGovernor::new(0, 0), // most tests can't handle transaction fees
Expand All @@ -143,14 +176,41 @@ 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 = 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 vote_account = if is_alpenglow {
let bls_keypair = BLSKeypair::derive_from_signer(
&validator_voting_keypairs.borrow().vote_keypair,
BLS_KEYPAIR_DERIVE_SEED,
)
.unwrap();
let bls_pubkey_compressed = bls_pubkey_to_compressed_bytes(&bls_keypair.public);
vote_state::create_v4_account_with_authorized(
&node_pubkey,
&vote_pubkey,
&vote_pubkey,
Some(bls_pubkey_compressed),
0,
*stake,
)
} else {
vote_state::create_account(&vote_pubkey, &node_pubkey, 0, *stake)
};
let stake_account = if is_alpenglow {
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);

Expand Down Expand Up @@ -204,6 +264,7 @@ pub fn create_genesis_config_with_leader_with_mint_keypair(
validator_pubkey,
&voting_keypair.pubkey(),
&Pubkey::new_unique(),
None,
validator_stake_lamports,
VALIDATOR_LAMPORTS,
FeeRateGovernor::new(0, 0), // most tests can't handle transaction fees
Expand Down Expand Up @@ -266,34 +327,64 @@ 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()
}

#[allow(clippy::too_many_arguments)]
pub fn create_genesis_config_with_leader_ex_no_features(
mint_lamports: u64,
mint_pubkey: &Pubkey,
validator_pubkey: &Pubkey,
validator_vote_account_pubkey: &Pubkey,
validator_stake_account_pubkey: &Pubkey,
validator_bls_pubkey: Option<[u8; BLS_PUBLIC_KEY_COMPRESSED_SIZE]>,
validator_stake_lamports: u64,
validator_lamports: u64,
fee_rate_governor: FeeRateGovernor,
rent: Rent,
cluster_type: ClusterType,
mut initial_accounts: Vec<(Pubkey, AccountSharedData)>,
) -> GenesisConfig {
let validator_vote_account = vote_state::create_account(
validator_vote_account_pubkey,
validator_pubkey,
0,
validator_stake_lamports,
);
let is_alpenglow = validator_bls_pubkey.is_some();
let validator_vote_account = if let Some(bls_pubkey_compressed) = validator_bls_pubkey {
vote_state::create_v4_account_with_authorized(
validator_pubkey,
validator_vote_account_pubkey,
validator_vote_account_pubkey,
Some(bls_pubkey_compressed),
0,
validator_stake_lamports,
)
} else {
vote_state::create_account(
validator_vote_account_pubkey,
validator_pubkey,
0,
validator_stake_lamports,
)
};

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 is_alpenglow {
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,
Expand Down Expand Up @@ -342,6 +433,7 @@ pub fn create_genesis_config_with_leader_ex(
validator_pubkey: &Pubkey,
validator_vote_account_pubkey: &Pubkey,
validator_stake_account_pubkey: &Pubkey,
validator_bls_pubkey: Option<[u8; BLS_PUBLIC_KEY_COMPRESSED_SIZE]>,
validator_stake_lamports: u64,
validator_lamports: u64,
fee_rate_governor: FeeRateGovernor,
Expand All @@ -355,6 +447,7 @@ pub fn create_genesis_config_with_leader_ex(
validator_pubkey,
validator_vote_account_pubkey,
validator_stake_account_pubkey,
validator_bls_pubkey,
validator_stake_lamports,
validator_lamports,
fee_rate_governor,
Expand Down
1 change: 1 addition & 0 deletions test-validator/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -927,6 +927,7 @@ impl TestValidator {
&validator_identity.pubkey(),
&validator_vote_account.pubkey(),
&validator_stake_account.pubkey(),
None,
validator_stake_lamports,
validator_identity_lamports,
config.fee_rate_governor.clone(),
Expand Down
7 changes: 7 additions & 0 deletions votor/src/commitment.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
use thiserror::Error;

#[derive(Debug, Error)]
pub enum AlpenglowCommitmentError {
#[error("Failed to send commitment data, channel disconnected")]
ChannelDisconnected,
}
Loading
Loading