@@ -102,6 +102,7 @@ use {
102102 consensus_message:: {
103103 CertificateType , ConsensusMessage , VoteMessage , BLS_KEYPAIR_DERIVE_SEED ,
104104 } ,
105+ migration:: MIGRATION_SLOT_OFFSET ,
105106 vote:: Vote ,
106107 } ,
107108 std:: {
@@ -6211,6 +6212,140 @@ fn test_alpenglow_imbalanced_stakes_catchup() {
62116212 ) ;
62126213}
62136214
6215+ fn test_alpenglow_migration ( num_nodes : usize ) {
6216+ solana_logger:: setup_with_default ( AG_DEBUG_LOG_FILTER ) ;
6217+ let test_name = & format ! ( "test_alpenglow_migration_{num_nodes}" ) ;
6218+
6219+ let vote_listener_socket = solana_net_utils:: bind_to_localhost ( ) . unwrap ( ) ;
6220+ let vote_listener_addr = vote_listener_socket. try_clone ( ) . unwrap ( ) ;
6221+ let mut validator_config = ValidatorConfig :: default_for_test ( ) ;
6222+ validator_config. voting_service_test_override = Some ( VotingServiceOverride {
6223+ additional_listeners : vec ! [ vote_listener_addr. local_addr( ) . unwrap( ) ] ,
6224+ alpenglow_port_override : AlpenglowPortOverride :: default ( ) ,
6225+ } ) ;
6226+ // Since we don't skip warmup slots and have very small epochs + tight MIGRATION_SLOT_OFFSET
6227+ // we can't afford any forking due to rolling start
6228+ validator_config. wait_for_supermajority = Some ( 0 ) ;
6229+
6230+ let validator_keys = ( 0 ..num_nodes)
6231+ . map ( |i| ( Arc :: new ( keypair_from_seed ( & [ i as u8 ; 32 ] ) . unwrap ( ) ) , true ) )
6232+ . collect :: < Vec < _ > > ( ) ;
6233+ let node_stakes = vec ! [ DEFAULT_NODE_STAKE ; num_nodes] ;
6234+
6235+ let mut cluster_config = ClusterConfig {
6236+ validator_configs : make_identical_validator_configs ( & validator_config, num_nodes) ,
6237+ validator_keys : Some ( validator_keys) ,
6238+ node_stakes : node_stakes. clone ( ) ,
6239+ slots_per_epoch : 2 * MINIMUM_SLOTS_PER_EPOCH ,
6240+ stakers_slot_offset : 2 * MINIMUM_SLOTS_PER_EPOCH ,
6241+ // So we don't have to wait so long
6242+ skip_warmup_slots : false ,
6243+ ..ClusterConfig :: default ( )
6244+ } ;
6245+
6246+ // Create local cluster with alpenglow accounts but feature not activated
6247+ let cluster = LocalCluster :: new_pre_migration_alpenglow (
6248+ & mut cluster_config,
6249+ SocketAddrSpace :: Unspecified ,
6250+ ) ;
6251+
6252+ let validator_keys: Vec < Arc < Keypair > > = cluster
6253+ . validators
6254+ . values ( )
6255+ . map ( |v| v. info . keypair . clone ( ) )
6256+ . collect ( ) ;
6257+
6258+ // Send feature activation transaction
6259+ info ! ( "Sending feature activation transaction" ) ;
6260+ let client = RpcClient :: new_socket_with_commitment (
6261+ cluster. entry_point_info . rpc ( ) . unwrap ( ) ,
6262+ CommitmentConfig :: processed ( ) ,
6263+ ) ;
6264+ let faucet_keypair = & cluster. funding_keypair ;
6265+ let feature_keypair = & * agave_feature_set:: alpenglow:: TEST_KEYPAIR ;
6266+ let blockhash = client. get_latest_blockhash ( ) . unwrap ( ) ;
6267+ let lamports = client
6268+ . get_minimum_balance_for_rent_exemption ( solana_feature_gate_interface:: Feature :: size_of ( ) )
6269+ . unwrap ( ) ;
6270+
6271+ let activation_message = solana_message:: Message :: new (
6272+ & solana_feature_gate_interface:: activate_with_lamports (
6273+ & agave_feature_set:: alpenglow:: id ( ) ,
6274+ & faucet_keypair. pubkey ( ) ,
6275+ lamports,
6276+ ) ,
6277+ Some ( & faucet_keypair. pubkey ( ) ) ,
6278+ ) ;
6279+ let activation_tx = solana_transaction:: Transaction :: new (
6280+ & [ & feature_keypair, & faucet_keypair] ,
6281+ activation_message,
6282+ blockhash,
6283+ ) ;
6284+
6285+ client. send_and_confirm_transaction ( & activation_tx) . unwrap ( ) ;
6286+ info ! ( "Feature activation transaction confirmed" ) ;
6287+
6288+ // Monitor for feature activation
6289+ let activation_slot;
6290+ loop {
6291+ if let Ok ( account) = client. get_account ( & agave_feature_set:: alpenglow:: id ( ) ) {
6292+ if let Some ( feature) = solana_feature_gate_interface:: from_account ( & account) {
6293+ if let Some ( slot) = feature. activated_at {
6294+ activation_slot = slot;
6295+ info ! ( "Feature activated at slot {slot}" ) ;
6296+ break ;
6297+ }
6298+ }
6299+ }
6300+ std:: thread:: sleep ( std:: time:: Duration :: from_millis ( 100 ) ) ;
6301+ }
6302+
6303+ // The migration happens at a fixed offset from feature activation
6304+ let migration_slot = activation_slot + MIGRATION_SLOT_OFFSET ;
6305+ info ! ( "Waiting for migration slot {migration_slot}" ) ;
6306+
6307+ loop {
6308+ let slot = client. get_slot ( ) . unwrap ( ) ;
6309+ if slot >= migration_slot - 1 {
6310+ break ;
6311+ }
6312+ std:: thread:: sleep ( std:: time:: Duration :: from_millis ( 100 ) ) ;
6313+ }
6314+
6315+ info ! ( "Migration slot reached, checking for notarized votes" ) ;
6316+
6317+ // Check for new notarized votes
6318+ cluster. check_for_new_notarized_votes (
6319+ 4 ,
6320+ test_name,
6321+ SocketAddrSpace :: Unspecified ,
6322+ vote_listener_addr,
6323+ & validator_keys,
6324+ & node_stakes,
6325+ ) ;
6326+
6327+ // Additionally ensure that roots are being made
6328+ cluster. check_for_new_roots ( 8 , test_name, SocketAddrSpace :: Unspecified ) ;
6329+ }
6330+
6331+ #[ test]
6332+ #[ serial]
6333+ fn test_alpenglow_migration_1 ( ) {
6334+ test_alpenglow_migration ( 1 )
6335+ }
6336+
6337+ #[ test]
6338+ #[ serial]
6339+ fn test_alpenglow_migration_2 ( ) {
6340+ test_alpenglow_migration ( 2 )
6341+ }
6342+
6343+ #[ test]
6344+ #[ serial]
6345+ fn test_alpenglow_migration_4 ( ) {
6346+ test_alpenglow_migration ( 4 )
6347+ }
6348+
62146349fn broadcast_vote (
62156350 message : ConsensusMessage ,
62166351 tpu_socket_addrs : & [ std:: net:: SocketAddr ] ,
0 commit comments