Vote InitializeAccount and UpdateNode instructions now need a signature from the validator identity (#8947)

automerge
This commit is contained in:
Michael Vines
2020-03-19 01:58:52 -07:00
committed by GitHub
parent 24d871b529
commit f78a90bce2
10 changed files with 213 additions and 141 deletions

View File

@ -51,6 +51,7 @@ impl<E> DecodeError<E> for VoteError {
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub enum VoteInstruction {
/// Initialize the VoteState for this `vote account`
/// requires VoteInit::node_pubkey signature
///
/// Expects 3 Accounts:
/// 0 - Uninitialized Vote account
@ -87,13 +88,13 @@ pub enum VoteInstruction {
Withdraw(u64),
/// Update the vote account's validator identity (node id)
/// requires authorized voter signature
/// requires authorized withdrawer and new validator identity signature
///
/// Expects 2 Accounts:
/// 0 - Vote account to be updated with the Pubkey for authorization
/// 1 - Clock sysvar Account that carries clock bank epoch
/// 1 - New validator identity (node id)
///
UpdateNode(Pubkey),
UpdateNode,
}
fn initialize_account(vote_pubkey: &Pubkey, vote_init: &VoteInit) -> Instruction {
@ -101,7 +102,9 @@ fn initialize_account(vote_pubkey: &Pubkey, vote_init: &VoteInit) -> Instruction
AccountMeta::new(*vote_pubkey, false),
AccountMeta::new_readonly(sysvar::rent::id(), false),
AccountMeta::new_readonly(sysvar::clock::id(), false),
];
]
.with_signer(&vote_init.node_pubkey);
Instruction::new(
id(),
&VoteInstruction::InitializeAccount(*vote_init),
@ -165,20 +168,16 @@ pub fn authorize(
pub fn update_node(
vote_pubkey: &Pubkey,
authorized_voter_pubkey: &Pubkey,
authorized_withdrawer_pubkey: &Pubkey,
node_pubkey: &Pubkey,
) -> Instruction {
let account_metas = vec![
AccountMeta::new(*vote_pubkey, false),
AccountMeta::new_readonly(sysvar::clock::id(), false),
AccountMeta::new_readonly(*node_pubkey, true),
]
.with_signer(authorized_voter_pubkey);
.with_signer(authorized_withdrawer_pubkey);
Instruction::new(
id(),
&VoteInstruction::UpdateNode(*node_pubkey),
account_metas,
)
Instruction::new(id(), &VoteInstruction::UpdateNode, account_metas)
}
pub fn vote(vote_pubkey: &Pubkey, authorized_voter_pubkey: &Pubkey, vote: Vote) -> Instruction {
@ -194,7 +193,7 @@ pub fn vote(vote_pubkey: &Pubkey, authorized_voter_pubkey: &Pubkey, vote: Vote)
pub fn withdraw(
vote_pubkey: &Pubkey,
withdrawer_pubkey: &Pubkey,
authorized_withdrawer_pubkey: &Pubkey,
lamports: u64,
to_pubkey: &Pubkey,
) -> Instruction {
@ -202,7 +201,7 @@ pub fn withdraw(
AccountMeta::new(*vote_pubkey, false),
AccountMeta::new(*to_pubkey, false),
]
.with_signer(withdrawer_pubkey);
.with_signer(authorized_withdrawer_pubkey);
Instruction::new(id(), &VoteInstruction::Withdraw(lamports), account_metas)
}
@ -228,6 +227,7 @@ pub fn process_instruction(
vote_state::initialize_account(
me,
&vote_init,
&signers,
&Clock::from_keyed_account(next_keyed_account(keyed_accounts)?)?,
)
}
@ -238,11 +238,10 @@ pub fn process_instruction(
&signers,
&Clock::from_keyed_account(next_keyed_account(keyed_accounts)?)?,
),
VoteInstruction::UpdateNode(node_pubkey) => vote_state::update_node(
VoteInstruction::UpdateNode => vote_state::update_node(
me,
&node_pubkey,
next_keyed_account(keyed_accounts)?.unsigned_key(),
&signers,
&Clock::from_keyed_account(next_keyed_account(keyed_accounts)?)?,
),
VoteInstruction::Vote(vote) => {
inc_new_counter_info!("vote-native", 1);

View File

@ -582,16 +582,15 @@ pub fn update_node<S: std::hash::BuildHasher>(
vote_account: &KeyedAccount,
node_pubkey: &Pubkey,
signers: &HashSet<Pubkey, S>,
clock: &Clock,
) -> Result<(), InstructionError> {
let mut vote_state: VoteState =
State::<VoteStateVersions>::state(vote_account)?.convert_to_current();
let authorized_voter = vote_state
.get_and_update_authorized_voter(clock.epoch)
.expect("the clock epoch is monotonically increasing, so authorized voter must be known");
// current authorized voter must say "yay"
verify_authorized_signer(&authorized_voter, signers)?;
// current authorized withdrawer must say "yay"
verify_authorized_signer(&vote_state.authorized_withdrawer, signers)?;
// new node must say "yay"
verify_authorized_signer(&node_pubkey, signers)?;
vote_state.node_pubkey = *node_pubkey;
@ -632,9 +631,10 @@ pub fn withdraw<S: std::hash::BuildHasher>(
/// Initialize the vote_state for a vote account
/// Assumes that the account is being init as part of a account creation or balance transfer and
/// that the transaction must be signed by the staker's keys
pub fn initialize_account(
pub fn initialize_account<S: std::hash::BuildHasher>(
vote_account: &KeyedAccount,
vote_init: &VoteInit,
signers: &HashSet<Pubkey, S>,
clock: &Clock,
) -> Result<(), InstructionError> {
let versioned = State::<VoteStateVersions>::state(vote_account)?;
@ -643,6 +643,9 @@ pub fn initialize_account(
return Err(InstructionError::AccountAlreadyInitialized);
}
// node must agree to accept this vote account
verify_authorized_signer(&vote_init.node_pubkey, signers)?;
vote_account.set_state(&VoteStateVersions::Current(Box::new(VoteState::new(
vote_init, clock,
))))
@ -664,7 +667,7 @@ pub fn process_vote<S: std::hash::BuildHasher>(
let mut vote_state = versioned.convert_to_current();
let authorized_voter = vote_state
.get_and_update_authorized_voter(clock.epoch)
.expect("the clock epoch is monotonically increasinig, so authorized voter must be known");
.expect("the clock epoch is monotonically increasing, so authorized voter must be known");
verify_authorized_signer(&authorized_voter, signers)?;
vote_state.process_vote(vote, slot_hashes, clock.epoch)?;
@ -745,11 +748,14 @@ mod tests {
fn test_initialize_vote_account() {
let vote_account_pubkey = Pubkey::new_rand();
let vote_account = Account::new_ref(100, VoteState::size_of(), &id());
let vote_account = KeyedAccount::new(&vote_account_pubkey, false, &vote_account);
let node_pubkey = Pubkey::new_rand();
let node_account = RefCell::new(Account::default());
let keyed_accounts = &[];
let signers: HashSet<Pubkey> = get_signers(keyed_accounts);
//init should pass
let vote_account = KeyedAccount::new(&vote_account_pubkey, false, &vote_account);
//init should fail, node_pubkey didn't sign the transaction
let res = initialize_account(
&vote_account,
&VoteInit {
@ -758,6 +764,24 @@ mod tests {
authorized_withdrawer: vote_account_pubkey,
commission: 0,
},
&signers,
&Clock::default(),
);
assert_eq!(res, Err(InstructionError::MissingRequiredSignature));
let keyed_accounts = &[KeyedAccount::new(&node_pubkey, true, &node_account)];
let signers: HashSet<Pubkey> = get_signers(keyed_accounts);
//init should pass
let res = initialize_account(
&vote_account,
&VoteInit {
node_pubkey,
authorized_voter: vote_account_pubkey,
authorized_withdrawer: vote_account_pubkey,
commission: 0,
},
&signers,
&Clock::default(),
);
assert_eq!(res, Ok(()));
@ -771,6 +795,7 @@ mod tests {
authorized_withdrawer: vote_account_pubkey,
commission: 0,
},
&signers,
&Clock::default(),
);
assert_eq!(res, Err(InstructionError::AccountAlreadyInitialized));
@ -789,6 +814,25 @@ mod tests {
)
}
fn create_test_account_with_authorized() -> (Pubkey, Pubkey, Pubkey, RefCell<Account>) {
let vote_pubkey = Pubkey::new_rand();
let authorized_voter = Pubkey::new_rand();
let authorized_withdrawer = Pubkey::new_rand();
(
vote_pubkey,
authorized_voter,
authorized_withdrawer,
RefCell::new(vote_state::create_account_with_authorized(
&Pubkey::new_rand(),
&authorized_voter,
&authorized_withdrawer,
0,
100,
)),
)
}
fn simulate_process_vote(
vote_pubkey: &Pubkey,
vote_account: &RefCell<Account>,
@ -906,43 +950,46 @@ mod tests {
#[test]
fn test_vote_update_node_id() {
let (vote_pubkey, vote_account) = create_test_account();
let (vote_pubkey, _authorized_voter, authorized_withdrawer, vote_account) =
create_test_account_with_authorized();
let node_pubkey = Pubkey::new_rand();
let node_account = RefCell::new(Account::default());
let authorized_withdrawer_account = RefCell::new(Account::default());
let keyed_accounts = &[KeyedAccount::new(&vote_pubkey, false, &vote_account)];
let keyed_accounts = &[
KeyedAccount::new(&vote_pubkey, true, &vote_account),
KeyedAccount::new(&node_pubkey, false, &node_account),
KeyedAccount::new(&authorized_withdrawer, true, &authorized_withdrawer_account),
];
let signers: HashSet<Pubkey> = get_signers(keyed_accounts);
let res = update_node(
&keyed_accounts[0],
&node_pubkey,
&signers,
&Clock::default(),
);
let res = update_node(&keyed_accounts[0], &node_pubkey, &signers);
assert_eq!(res, Err(InstructionError::MissingRequiredSignature));
let keyed_accounts = &[
KeyedAccount::new(&vote_pubkey, true, &vote_account),
KeyedAccount::new(&node_pubkey, true, &node_account),
KeyedAccount::new(
&authorized_withdrawer,
false,
&authorized_withdrawer_account,
),
];
let signers: HashSet<Pubkey> = get_signers(keyed_accounts);
let res = update_node(&keyed_accounts[0], &node_pubkey, &signers);
assert_eq!(res, Err(InstructionError::MissingRequiredSignature));
let vote_state: VoteState = StateMut::<VoteStateVersions>::state(&*vote_account.borrow())
.unwrap()
.convert_to_current();
assert!(vote_state.node_pubkey != node_pubkey);
let keyed_accounts = &[KeyedAccount::new(&vote_pubkey, true, &vote_account)];
let keyed_accounts = &[
KeyedAccount::new(&vote_pubkey, true, &vote_account),
KeyedAccount::new(&node_pubkey, true, &node_account),
KeyedAccount::new(&authorized_withdrawer, true, &authorized_withdrawer_account),
];
let signers: HashSet<Pubkey> = get_signers(keyed_accounts);
let res = update_node(
&keyed_accounts[0],
&node_pubkey,
&signers,
&Clock::default(),
);
assert_eq!(res, Ok(()));
let vote_state: VoteState = StateMut::<VoteStateVersions>::state(&*vote_account.borrow())
.unwrap()
.convert_to_current();
assert_eq!(vote_state.node_pubkey, node_pubkey);
let keyed_accounts = &[KeyedAccount::new(&vote_pubkey, true, &vote_account)];
let signers: HashSet<Pubkey> = get_signers(keyed_accounts);
let mut clock = Clock::default();
clock.epoch += 10;
let res = update_node(&keyed_accounts[0], &node_pubkey, &signers, &clock);
let res = update_node(&keyed_accounts[0], &node_pubkey, &signers);
assert_eq!(res, Ok(()));
let vote_state: VoteState = StateMut::<VoteStateVersions>::state(&*vote_account.borrow())
.unwrap()