diff --git a/core/src/cluster_info.rs b/core/src/cluster_info.rs index 876ce5cd7..6d48c010a 100644 --- a/core/src/cluster_info.rs +++ b/core/src/cluster_info.rs @@ -2052,10 +2052,11 @@ pub fn stake_weight_peers( #[cfg(test)] mod tests { use super::*; - use crate::crds_value::CrdsValueLabel; + use crate::crds_value::{CrdsValue, CrdsValueLabel, Vote as CrdsVote}; use rayon::prelude::*; use solana_perf::test_tx::test_tx; use solana_sdk::signature::{Keypair, Signer}; + use solana_vote_program::{vote_instruction, vote_state::Vote}; use std::collections::HashSet; use std::net::{IpAddr, Ipv4Addr}; use std::sync::Arc; @@ -2763,4 +2764,31 @@ mod tests { assert_eq!(slots, range); assert!(since.is_some()); } + + #[test] + fn test_vote_size() { + let slots = vec![1; 32]; + let vote = Vote::new(slots, Hash::default()); + let keypair = Arc::new(Keypair::new()); + + // Create the biggest possible vote transaction + let vote_ix = vote_instruction::vote_switch( + &keypair.pubkey(), + &keypair.pubkey(), + vote, + Hash::default(), + ); + let mut vote_tx = Transaction::new_with_payer(&[vote_ix], Some(&keypair.pubkey())); + + vote_tx.partial_sign(&[keypair.as_ref()], Hash::default()); + vote_tx.partial_sign(&[keypair.as_ref()], Hash::default()); + + let vote = CrdsVote { + from: keypair.pubkey(), + transaction: vote_tx, + wallclock: 0, + }; + let vote = CrdsValue::new_signed(CrdsData::Vote(1, vote), &Keypair::new()); + assert!(bincode::serialized_size(&vote).unwrap() <= MAX_PROTOCOL_PAYLOAD_SIZE); + } } diff --git a/programs/vote/src/vote_instruction.rs b/programs/vote/src/vote_instruction.rs index cc2654e92..e2012a87d 100644 --- a/programs/vote/src/vote_instruction.rs +++ b/programs/vote/src/vote_instruction.rs @@ -11,6 +11,7 @@ use serde_derive::{Deserialize, Serialize}; use solana_metrics::inc_new_counter_info; use solana_sdk::{ account::{get_signers, KeyedAccount}, + hash::Hash, instruction::{AccountMeta, Instruction, InstructionError, WithSigner}, program_utils::{limited_deserialize, next_keyed_account, DecodeError}, pubkey::Pubkey, @@ -95,6 +96,15 @@ pub enum VoteInstruction { /// 1 - New validator identity (node_pubkey) /// UpdateValidatorIdentity, + + /// A Vote instruction with recent votes + /// requires authorized voter signature + /// + /// Expects 3 Accounts: + /// 0 - Vote account to vote with + /// 1 - Slot hashes sysvar + /// 2 - Clock sysvar + VoteSwitch(Vote, Hash), } fn initialize_account(vote_pubkey: &Pubkey, vote_init: &VoteInit) -> Instruction { @@ -195,6 +205,26 @@ pub fn vote(vote_pubkey: &Pubkey, authorized_voter_pubkey: &Pubkey, vote: Vote) Instruction::new(id(), &VoteInstruction::Vote(vote), account_metas) } +pub fn vote_switch( + vote_pubkey: &Pubkey, + authorized_voter_pubkey: &Pubkey, + vote: Vote, + proof_hash: Hash, +) -> Instruction { + let account_metas = vec![ + AccountMeta::new(*vote_pubkey, false), + AccountMeta::new_readonly(sysvar::slot_hashes::id(), false), + AccountMeta::new_readonly(sysvar::clock::id(), false), + ] + .with_signer(authorized_voter_pubkey); + + Instruction::new( + id(), + &VoteInstruction::VoteSwitch(vote, proof_hash), + account_metas, + ) +} + pub fn withdraw( vote_pubkey: &Pubkey, authorized_withdrawer_pubkey: &Pubkey, @@ -245,7 +275,7 @@ pub fn process_instruction( next_keyed_account(keyed_accounts)?.unsigned_key(), &signers, ), - VoteInstruction::Vote(vote) => { + VoteInstruction::Vote(vote) | VoteInstruction::VoteSwitch(vote, _) => { inc_new_counter_info!("vote-native", 1); vote_state::process_vote( me, @@ -328,6 +358,15 @@ mod tests { )), Err(InstructionError::InvalidAccountData), ); + assert_eq!( + process_instruction(&vote_switch( + &Pubkey::default(), + &Pubkey::default(), + Vote::default(), + Hash::default(), + )), + Err(InstructionError::InvalidAccountData), + ); assert_eq!( process_instruction(&authorize( &Pubkey::default(),