vote account withdraw authority may change the authorized voter

This commit is contained in:
Michael Vines 2022-01-14 17:38:01 -08:00
parent 3bd5a89d6f
commit 65f1e0fcc2
3 changed files with 73 additions and 4 deletions

View File

@ -86,7 +86,14 @@ pub fn process_instruction(
keyed_account_at_index(keyed_accounts, first_instruction_account + 1)?, keyed_account_at_index(keyed_accounts, first_instruction_account + 1)?,
invoke_context, invoke_context,
)?; )?;
vote_state::authorize(me, &voter_pubkey, vote_authorize, &signers, &clock) vote_state::authorize(
me,
&voter_pubkey,
vote_authorize,
&signers,
&clock,
&invoke_context.feature_set,
)
} }
VoteInstruction::UpdateValidatorIdentity => vote_state::update_validator_identity( VoteInstruction::UpdateValidatorIdentity => vote_state::update_validator_identity(
me, me,
@ -166,6 +173,7 @@ pub fn process_instruction(
keyed_accounts, keyed_accounts,
first_instruction_account + 1, first_instruction_account + 1,
)?)?, )?)?,
&invoke_context.feature_set,
) )
} else { } else {
Err(InstructionError::InvalidInstructionData) Err(InstructionError::InvalidInstructionData)

View File

@ -10,7 +10,7 @@ use {
account_utils::State, account_utils::State,
clock::{Epoch, Slot, UnixTimestamp}, clock::{Epoch, Slot, UnixTimestamp},
epoch_schedule::MAX_LEADER_SCHEDULE_EPOCH_OFFSET, epoch_schedule::MAX_LEADER_SCHEDULE_EPOCH_OFFSET,
feature_set::{filter_votes_outside_slot_hashes, FeatureSet}, feature_set::{self, filter_votes_outside_slot_hashes, FeatureSet},
hash::Hash, hash::Hash,
instruction::InstructionError, instruction::InstructionError,
keyed_account::KeyedAccount, keyed_account::KeyedAccount,
@ -921,21 +921,37 @@ pub fn authorize<S: std::hash::BuildHasher>(
vote_authorize: VoteAuthorize, vote_authorize: VoteAuthorize,
signers: &HashSet<Pubkey, S>, signers: &HashSet<Pubkey, S>,
clock: &Clock, clock: &Clock,
feature_set: &FeatureSet,
) -> Result<(), InstructionError> { ) -> Result<(), InstructionError> {
let mut vote_state: VoteState = let mut vote_state: VoteState =
State::<VoteStateVersions>::state(vote_account)?.convert_to_current(); State::<VoteStateVersions>::state(vote_account)?.convert_to_current();
// current authorized signer must say "yay"
match vote_authorize { match vote_authorize {
VoteAuthorize::Voter => { VoteAuthorize::Voter => {
let authorized_withdrawer_signer = if feature_set
.is_active(&feature_set::vote_withdraw_authority_may_change_authorized_voter::id())
{
verify_authorized_signer(&vote_state.authorized_withdrawer, signers).is_ok()
} else {
false
};
vote_state.set_new_authorized_voter( vote_state.set_new_authorized_voter(
authorized, authorized,
clock.epoch, clock.epoch,
clock.leader_schedule_epoch + 1, clock.leader_schedule_epoch + 1,
|epoch_authorized_voter| verify_authorized_signer(&epoch_authorized_voter, signers), |epoch_authorized_voter| {
// current authorized withdrawer or authorized voter must say "yay"
if authorized_withdrawer_signer {
Ok(())
} else {
verify_authorized_signer(&epoch_authorized_voter, signers)
}
},
)?; )?;
} }
VoteAuthorize::Withdrawer => { VoteAuthorize::Withdrawer => {
// current authorized withdrawer must say "yay"
verify_authorized_signer(&vote_state.authorized_withdrawer, signers)?; verify_authorized_signer(&vote_state.authorized_withdrawer, signers)?;
vote_state.authorized_withdrawer = *authorized; vote_state.authorized_withdrawer = *authorized;
} }
@ -1558,6 +1574,7 @@ mod tests {
leader_schedule_epoch: 2, leader_schedule_epoch: 2,
..Clock::default() ..Clock::default()
}, },
&FeatureSet::default(),
); );
assert_eq!(res, Err(InstructionError::MissingRequiredSignature)); assert_eq!(res, Err(InstructionError::MissingRequiredSignature));
@ -1573,6 +1590,7 @@ mod tests {
leader_schedule_epoch: 2, leader_schedule_epoch: 2,
..Clock::default() ..Clock::default()
}, },
&FeatureSet::default(),
); );
assert_eq!(res, Ok(())); assert_eq!(res, Ok(()));
@ -1588,6 +1606,7 @@ mod tests {
leader_schedule_epoch: 2, leader_schedule_epoch: 2,
..Clock::default() ..Clock::default()
}, },
&FeatureSet::default(),
); );
assert_eq!(res, Err(VoteError::TooSoonToReauthorize.into())); assert_eq!(res, Err(VoteError::TooSoonToReauthorize.into()));
@ -1610,6 +1629,7 @@ mod tests {
leader_schedule_epoch: 4, leader_schedule_epoch: 4,
..Clock::default() ..Clock::default()
}, },
&FeatureSet::default(),
); );
assert_eq!(res, Ok(())); assert_eq!(res, Ok(()));
@ -1628,6 +1648,7 @@ mod tests {
leader_schedule_epoch: 4, leader_schedule_epoch: 4,
..Clock::default() ..Clock::default()
}, },
&FeatureSet::default(),
); );
assert_eq!(res, Ok(())); assert_eq!(res, Ok(()));
@ -1648,6 +1669,7 @@ mod tests {
leader_schedule_epoch: 4, leader_schedule_epoch: 4,
..Clock::default() ..Clock::default()
}, },
&FeatureSet::default(),
); );
assert_eq!(res, Ok(())); assert_eq!(res, Ok(()));
@ -1690,6 +1712,39 @@ mod tests {
&FeatureSet::default(), &FeatureSet::default(),
); );
assert_eq!(res, Ok(())); assert_eq!(res, Ok(()));
// verify authorized_withdrawer can authorize a new authorized_voter when
// `feature_set::vote_withdraw_authority_may_change_authorized_voter` is enabled
let keyed_accounts = &[
KeyedAccount::new(&vote_pubkey, false, &vote_account),
KeyedAccount::new(&authorized_withdrawer_pubkey, true, &withdrawer_account),
];
let another_authorized_voter_pubkey = solana_sdk::pubkey::new_rand();
let signers: HashSet<Pubkey> = get_signers(keyed_accounts);
for (feature_set, expected_res) in [
(
FeatureSet::default(),
Err(InstructionError::MissingRequiredSignature),
),
(FeatureSet::all_enabled(), Ok(())),
]
.into_iter()
{
let res = authorize(
&keyed_accounts[0],
&another_authorized_voter_pubkey,
VoteAuthorize::Voter,
&signers,
&Clock {
epoch: 4,
leader_schedule_epoch: 5,
..Clock::default()
},
&feature_set,
);
assert_eq!(res, expected_res)
}
} }
#[test] #[test]
@ -2189,6 +2244,7 @@ mod tests {
VoteAuthorize::Withdrawer, VoteAuthorize::Withdrawer,
&signers, &signers,
&Clock::default(), &Clock::default(),
&FeatureSet::default(),
); );
assert_eq!(res, Ok(())); assert_eq!(res, Ok(()));

View File

@ -307,6 +307,10 @@ pub mod update_syscall_base_costs {
solana_sdk::declare_id!("2h63t332mGCCsWK2nqqqHhN4U9ayyqhLVFvczznHDoTZ"); solana_sdk::declare_id!("2h63t332mGCCsWK2nqqqHhN4U9ayyqhLVFvczznHDoTZ");
} }
pub mod vote_withdraw_authority_may_change_authorized_voter {
solana_sdk::declare_id!("AVZS3ZsN4gi6Rkx2QUibYuSJG3S6QHib7xCYhG6vGJxU");
}
lazy_static! { lazy_static! {
/// Map of feature identifiers to user-visible description /// Map of feature identifiers to user-visible description
pub static ref FEATURE_NAMES: HashMap<Pubkey, &'static str> = [ pub static ref FEATURE_NAMES: HashMap<Pubkey, &'static str> = [
@ -378,6 +382,7 @@ lazy_static! {
(require_rent_exempt_accounts::id(), "require all new transaction accounts with data to be rent-exempt"), (require_rent_exempt_accounts::id(), "require all new transaction accounts with data to be rent-exempt"),
(filter_votes_outside_slot_hashes::id(), "filter vote slots older than the slot hashes history"), (filter_votes_outside_slot_hashes::id(), "filter vote slots older than the slot hashes history"),
(update_syscall_base_costs::id(), "Update syscall base costs"), (update_syscall_base_costs::id(), "Update syscall base costs"),
(vote_withdraw_authority_may_change_authorized_voter::id(), "vote account withdraw authority may change the authorized voter #22521"),
/*************** ADD NEW FEATURES HERE ***************/ /*************** ADD NEW FEATURES HERE ***************/
] ]
.iter() .iter()