diff --git a/programs/vote/src/vote_processor.rs b/programs/vote/src/vote_processor.rs index 1ec740f0c0fea4..f7fe50a7d7fadd 100644 --- a/programs/vote/src/vote_processor.rs +++ b/programs/vote/src/vote_processor.rs @@ -677,6 +677,8 @@ mod tests { ); let vote_state = VoteStateV3::deserialize(accounts[0].data()).unwrap(); assert_eq!(vote_state.node_pubkey, node_pubkey); + // TODO: When the feature gate for vote state v4 is added, check the + // block revenue collector here for the v4 target. } #[test] diff --git a/programs/vote/src/vote_state/handler.rs b/programs/vote/src/vote_state/handler.rs index be220baf668cee..9a0eb0e666733f 100644 --- a/programs/vote/src/vote_state/handler.rs +++ b/programs/vote/src/vote_state/handler.rs @@ -61,6 +61,8 @@ pub trait VoteStateHandle { fn set_node_pubkey(&mut self, node_pubkey: Pubkey); + fn set_block_revenue_collector(&mut self, collector: Pubkey); + fn votes(&self) -> &VecDeque; fn votes_mut(&mut self) -> &mut VecDeque; @@ -353,6 +355,10 @@ impl VoteStateHandle for VoteStateV3 { self.node_pubkey = node_pubkey; } + fn set_block_revenue_collector(&mut self, _collector: Pubkey) { + // No-op for v3: field does not exist. + } + fn votes(&self) -> &VecDeque { &self.votes } @@ -516,6 +522,10 @@ impl VoteStateHandle for VoteStateV4 { self.node_pubkey = node_pubkey; } + fn set_block_revenue_collector(&mut self, collector: Pubkey) { + self.block_revenue_collector = collector; + } + fn votes(&self) -> &VecDeque { &self.votes } @@ -749,6 +759,13 @@ impl VoteStateHandle for VoteStateHandler { } } + fn set_block_revenue_collector(&mut self, collector: Pubkey) { + match &mut self.target_state { + TargetVoteState::V3(v3) => v3.set_block_revenue_collector(collector), + TargetVoteState::V4(v4) => v4.set_block_revenue_collector(collector), + } + } + fn votes(&self) -> &VecDeque { match &self.target_state { TargetVoteState::V3(v3) => v3.votes(), diff --git a/programs/vote/src/vote_state/mod.rs b/programs/vote/src/vote_state/mod.rs index 36bf8985571ccb..231131ddd2b2ff 100644 --- a/programs/vote/src/vote_state/mod.rs +++ b/programs/vote/src/vote_state/mod.rs @@ -727,6 +727,9 @@ pub fn update_validator_identity( verify_authorized_signer(node_pubkey, signers)?; vote_state.set_node_pubkey(*node_pubkey); + // Keep block_revenue_collector in sync with node_pubkey until SIMD-0232 + // is implemented. + vote_state.set_block_revenue_collector(*node_pubkey); vote_state.set_vote_account_state(vote_account) } @@ -3658,4 +3661,77 @@ mod tests { inflation_rewards_commission_bps ); } + + #[test] + fn test_update_validator_identity_syncs_block_revenue_collector() { + let vote_state = + vote_state_new_for_test(&solana_pubkey::new_rand(), VoteStateTargetVersion::V4); + let node_pubkey = *vote_state.node_pubkey(); + let withdrawer_pubkey = *vote_state.authorized_withdrawer(); + + let serialized = vote_state.serialize(); + let serialized_len = serialized.len(); + let rent = Rent::default(); + let lamports = rent.minimum_balance(serialized_len); + let mut vote_account = AccountSharedData::new(lamports, serialized_len, &id()); + vote_account.set_data_from_slice(&serialized); + + let processor_account = AccountSharedData::new(0, 0, &solana_sdk_ids::native_loader::id()); + let mut transaction_context = TransactionContext::new( + vec![(id(), processor_account), (node_pubkey, vote_account)], + rent, + 0, + 0, + ); + transaction_context + .configure_next_instruction_for_tests( + 0, + vec![InstructionAccount::new(1, false, true)], + vec![], + ) + .unwrap(); + let instruction_context = transaction_context.get_next_instruction_context().unwrap(); + let mut borrowed_account = instruction_context + .try_borrow_instruction_account(0) + .unwrap(); + + let new_node_pubkey = solana_pubkey::new_rand(); + let signers: HashSet = vec![withdrawer_pubkey, new_node_pubkey] + .into_iter() + .collect(); + + update_validator_identity( + &mut borrowed_account, + VoteStateTargetVersion::V4, + &new_node_pubkey, + &signers, + ) + .unwrap(); + + // Both `node_pubkey` and `block_revenue_collector` should be set to + // the new node pubkey. + let vote_state = + VoteStateV4::deserialize(borrowed_account.get_data(), &new_node_pubkey).unwrap(); + assert_eq!(vote_state.node_pubkey, new_node_pubkey); + assert_eq!(vote_state.block_revenue_collector, new_node_pubkey); + + // Run it again. + let new_node_pubkey = solana_pubkey::new_rand(); + let signers: HashSet = vec![withdrawer_pubkey, new_node_pubkey] + .into_iter() + .collect(); + + update_validator_identity( + &mut borrowed_account, + VoteStateTargetVersion::V4, + &new_node_pubkey, + &signers, + ) + .unwrap(); + + let vote_state = + VoteStateV4::deserialize(borrowed_account.get_data(), &new_node_pubkey).unwrap(); + assert_eq!(vote_state.node_pubkey, new_node_pubkey); + assert_eq!(vote_state.block_revenue_collector, new_node_pubkey); + } }