From ebe367f72265bfcff0166c9d6bb889aa43789d2b Mon Sep 17 00:00:00 2001 From: carllin Date: Tue, 28 Jul 2020 23:44:10 -0700 Subject: [PATCH] Add utility function (#11262) Co-authored-by: Carl --- core/src/cluster_info_vote_listener.rs | 64 ++----------------- programs/vote/src/vote_transaction.rs | 85 +++++++++++++++++++++++++- 2 files changed, 88 insertions(+), 61 deletions(-) diff --git a/core/src/cluster_info_vote_listener.rs b/core/src/cluster_info_vote_listener.rs index 96139fe77d..ef00434516 100644 --- a/core/src/cluster_info_vote_listener.rs +++ b/core/src/cluster_info_vote_listener.rs @@ -31,11 +31,10 @@ use solana_sdk::{ clock::{Epoch, Slot}, epoch_schedule::EpochSchedule, hash::Hash, - program_utils::limited_deserialize, pubkey::Pubkey, transaction::Transaction, }; -use solana_vote_program::{vote_instruction::VoteInstruction, vote_state::Vote}; +use solana_vote_program::{self, vote_transaction}; use std::{ collections::{HashMap, HashSet}, sync::{ @@ -537,35 +536,6 @@ impl ClusterInfoVoteListener { Ok(vec![]) } - fn parse_vote_transaction(tx: &Transaction) -> Option<(Pubkey, Vote, Option)> { - // Check first instruction for a vote - tx.message - .instructions - .first() - .and_then(|first_instruction| { - first_instruction - .accounts - .first() - .and_then(|first_account| { - tx.message - .account_keys - .get(*first_account as usize) - .and_then(|key| { - let vote_instruction = - limited_deserialize(&first_instruction.data).ok(); - vote_instruction.and_then(|vote_instruction| match vote_instruction - { - VoteInstruction::Vote(vote) => Some((*key, vote, None)), - VoteInstruction::VoteSwitch(vote, hash) => { - Some((*key, vote, Some(hash))) - } - _ => None, - }) - }) - }) - }) - } - fn process_votes( vote_tracker: &VoteTracker, vote_txs: Vec, @@ -580,7 +550,8 @@ impl ClusterInfoVoteListener { let root = root_bank.slot(); { for tx in vote_txs { - if let Some((vote_pubkey, vote, _)) = Self::parse_vote_transaction(&tx) { + if let Some((vote_pubkey, vote, _)) = vote_transaction::parse_vote_transaction(&tx) + { if vote.slots.is_empty() { continue; } @@ -820,10 +791,9 @@ mod tests { commitment::BlockCommitmentCache, genesis_utils::{self, GenesisConfigInfo, ValidatorVoteKeypairs}, }; - use solana_sdk::hash::{self, Hash}; + use solana_sdk::hash::Hash; use solana_sdk::signature::Signature; use solana_sdk::signature::{Keypair, Signer}; - use solana_vote_program::vote_transaction; use std::collections::BTreeSet; #[test] @@ -1778,30 +1748,4 @@ mod tests { run_test_bad_vote(None); run_test_bad_vote(Some(Hash::default())); } - - fn run_test_parse_vote_transaction(input_hash: Option) { - let node_keypair = Keypair::new(); - let vote_keypair = Keypair::new(); - let auth_voter_keypair = Keypair::new(); - let bank_hash = Hash::default(); - let vote_tx = vote_transaction::new_vote_transaction( - vec![42], - bank_hash, - Hash::default(), - &node_keypair, - &vote_keypair, - &auth_voter_keypair, - input_hash, - ); - let (key, vote, hash) = ClusterInfoVoteListener::parse_vote_transaction(&vote_tx).unwrap(); - assert_eq!(hash, input_hash); - assert_eq!(vote, Vote::new(vec![42], bank_hash)); - assert_eq!(key, vote_keypair.pubkey()); - } - - #[test] - fn test_parse_vote_transaction() { - run_test_parse_vote_transaction(None); - run_test_parse_vote_transaction(Some(hash::hash(&[42u8]))); - } } diff --git a/programs/vote/src/vote_transaction.rs b/programs/vote/src/vote_transaction.rs index 99c6ecd555..fdbce17982 100644 --- a/programs/vote/src/vote_transaction.rs +++ b/programs/vote/src/vote_transaction.rs @@ -1,11 +1,52 @@ use solana_sdk::{ clock::Slot, hash::Hash, + program_utils::limited_deserialize, + pubkey::Pubkey, signature::{Keypair, Signer}, transaction::Transaction, }; -use crate::{vote_instruction, vote_state::Vote}; +use crate::{ + vote_instruction::{self, VoteInstruction}, + vote_state::Vote, +}; + +pub fn parse_vote_transaction(tx: &Transaction) -> Option<(Pubkey, Vote, Option)> { + // Check first instruction for a vote + let message = tx.message(); + message.instructions.get(0).and_then(|first_instruction| { + let prog_id_idx = first_instruction.program_id_index as usize; + match message.account_keys.get(prog_id_idx) { + Some(program_id) => { + if !crate::check_id(&program_id) { + return None; + } + } + _ => { + return None; + } + }; + first_instruction + .accounts + .first() + .and_then(|first_account| { + tx.message + .account_keys + .get(*first_account as usize) + .and_then(|key| { + let vote_instruction = limited_deserialize(&first_instruction.data).ok(); + vote_instruction.and_then(|vote_instruction| match vote_instruction { + VoteInstruction::Vote(vote) => Some((*key, vote, None)), + VoteInstruction::VoteSwitch(vote, hash) => { + Some((*key, vote, Some(hash))) + } + _ => None, + }) + }) + }) + }) +} pub fn new_vote_transaction( slots: Vec, @@ -38,3 +79,45 @@ pub fn new_vote_transaction( vote_tx.partial_sign(&[authorized_voter_keypair], blockhash); vote_tx } + +#[cfg(test)] +mod test { + use super::*; + use solana_sdk::hash::hash; + + fn run_test_parse_vote_transaction(input_hash: Option) { + let node_keypair = Keypair::new(); + let vote_keypair = Keypair::new(); + let auth_voter_keypair = Keypair::new(); + let bank_hash = Hash::default(); + let vote_tx = new_vote_transaction( + vec![42], + bank_hash, + Hash::default(), + &node_keypair, + &vote_keypair, + &auth_voter_keypair, + input_hash, + ); + let (key, vote, hash) = parse_vote_transaction(&vote_tx).unwrap(); + assert_eq!(hash, input_hash); + assert_eq!(vote, Vote::new(vec![42], bank_hash)); + assert_eq!(key, vote_keypair.pubkey()); + + // Test bad program id fails + let mut vote_ix = vote_instruction::vote( + &vote_keypair.pubkey(), + &auth_voter_keypair.pubkey(), + Vote::new(vec![1, 2], Hash::default()), + ); + vote_ix.program_id = Pubkey::default(); + let vote_tx = Transaction::new_with_payer(&[vote_ix], Some(&node_keypair.pubkey())); + assert!(parse_vote_transaction(&vote_tx).is_none()); + } + + #[test] + fn test_parse_vote_transaction() { + run_test_parse_vote_transaction(None); + run_test_parse_vote_transaction(Some(hash(&[42u8]))); + } +}