From 14d24e446780398dfbb971f41fb02260ab4a6307 Mon Sep 17 00:00:00 2001 From: Carl Date: Tue, 3 Mar 2020 17:40:05 -0800 Subject: [PATCH 1/2] Fix broken confirmation, add test --- core/src/replay_stage.rs | 121 +++++++++++++++++++++++++- programs/vote/src/lib.rs | 1 + programs/vote/src/vote_transaction.rs | 30 +++++++ 3 files changed, 150 insertions(+), 2 deletions(-) create mode 100644 programs/vote/src/vote_transaction.rs diff --git a/core/src/replay_stage.rs b/core/src/replay_stage.rs index f3767875b5c839..17992eaa6af0a1 100644 --- a/core/src/replay_stage.rs +++ b/core/src/replay_stage.rs @@ -852,7 +852,7 @@ impl ReplayStage { progress: &mut ProgressMap, ) -> Vec { frozen_banks.sort_by_key(|bank| bank.slot()); - let new_stats = vec![]; + let mut new_stats = vec![]; for bank in frozen_banks { // Only time progress map should be missing a bank slot // is if this node was the leader for this slot as those banks @@ -895,6 +895,7 @@ impl ReplayStage { stats.stake_lockouts = stake_lockouts; stats.block_height = bank.block_height(); stats.computed = true; + new_stats.push(stats.slot); } stats.vote_threshold = tower.check_vote_stake_threshold( bank.slot(), @@ -1306,7 +1307,10 @@ pub(crate) mod tests { transaction::TransactionError, }; use solana_stake_program::stake_state; - use solana_vote_program::vote_state::{self, Vote, VoteState, VoteStateVersions}; + use solana_vote_program::{ + vote_state::{self, Vote, VoteState, VoteStateVersions}, + vote_transaction, + }; use std::{ fs::remove_dir_all, iter, @@ -2157,6 +2161,119 @@ pub(crate) mod tests { Blockstore::destroy(&ledger_path).unwrap(); } + #[test] + fn test_compute_bank_stats_confirmed() { + let node_keypair = Keypair::new(); + let vote_keypair = Keypair::new(); + let stake_keypair = Keypair::new(); + let node_pubkey = node_keypair.pubkey(); + let mut keypairs = HashMap::new(); + keypairs.insert( + node_pubkey, + ValidatorVoteKeypairs::new(node_keypair, vote_keypair, stake_keypair), + ); + + let (bank_forks, mut progress) = initialize_state(&keypairs); + let bank0 = bank_forks.get(0).unwrap().clone(); + let my_keypairs = keypairs.get(&node_pubkey).unwrap(); + let vote_tx = vote_transaction::new_vote_transaction( + vec![0], + bank0.hash(), + bank0.last_blockhash(), + &my_keypairs.node_keypair, + &my_keypairs.vote_keypair, + &my_keypairs.vote_keypair, + ); + + let bank_forks = RwLock::new(bank_forks); + let bank1 = Bank::new_from_parent(&bank0, &node_pubkey, 1); + bank1.process_transaction(&vote_tx).unwrap(); + bank1.freeze(); + + // Test confirmations + let ancestors = bank_forks.read().unwrap().ancestors(); + let mut frozen_banks: Vec<_> = bank_forks + .read() + .unwrap() + .frozen_banks() + .values() + .cloned() + .collect(); + let tower = Tower::new_for_tests(0, 0.67); + let newly_computed = ReplayStage::compute_bank_stats( + &node_pubkey, + &ancestors, + &mut frozen_banks, + &tower, + &mut progress, + ); + assert_eq!(newly_computed, vec![0]); + // The only vote is in bank 1, and bank_forks does not currently contain + // bank 1, so no slot should be confirmed. + { + let fork_progress = progress.get(&0).unwrap(); + let confirmed_forks = ReplayStage::confirm_forks( + &tower, + &fork_progress.fork_stats.stake_lockouts, + fork_progress.fork_stats.total_staked, + &progress, + &bank_forks, + ); + + assert!(confirmed_forks.is_empty()) + } + + // Insert the bank that contains a vote for slot 0, which confirms slot 0 + bank_forks.write().unwrap().insert(bank1); + progress.insert(1, ForkProgress::new(bank0.last_blockhash())); + let ancestors = bank_forks.read().unwrap().ancestors(); + let mut frozen_banks: Vec<_> = bank_forks + .read() + .unwrap() + .frozen_banks() + .values() + .cloned() + .collect(); + let newly_computed = ReplayStage::compute_bank_stats( + &node_pubkey, + &ancestors, + &mut frozen_banks, + &tower, + &mut progress, + ); + + assert_eq!(newly_computed, vec![1]); + { + let fork_progress = progress.get(&1).unwrap(); + let confirmed_forks = ReplayStage::confirm_forks( + &tower, + &fork_progress.fork_stats.stake_lockouts, + fork_progress.fork_stats.total_staked, + &progress, + &bank_forks, + ); + assert_eq!(confirmed_forks, vec![0]); + } + + let ancestors = bank_forks.read().unwrap().ancestors(); + let mut frozen_banks: Vec<_> = bank_forks + .read() + .unwrap() + .frozen_banks() + .values() + .cloned() + .collect(); + let newly_computed = ReplayStage::compute_bank_stats( + &node_pubkey, + &ancestors, + &mut frozen_banks, + &tower, + &mut progress, + ); + // No new stats should have been computed + assert!(newly_computed.is_empty()); + } + #[test] fn test_child_bank_heavier() { let node_keypair = Keypair::new(); diff --git a/programs/vote/src/lib.rs b/programs/vote/src/lib.rs index 481584fcb6c48e..e6d0b272e33736 100644 --- a/programs/vote/src/lib.rs +++ b/programs/vote/src/lib.rs @@ -1,6 +1,7 @@ pub mod authorized_voters; pub mod vote_instruction; pub mod vote_state; +pub mod vote_transaction; #[macro_use] extern crate solana_metrics; diff --git a/programs/vote/src/vote_transaction.rs b/programs/vote/src/vote_transaction.rs new file mode 100644 index 00000000000000..c50929ff56f221 --- /dev/null +++ b/programs/vote/src/vote_transaction.rs @@ -0,0 +1,30 @@ +use solana_sdk::{ + clock::Slot, + hash::Hash, + signature::{Keypair, Signer}, + transaction::Transaction, +}; + +use crate::{vote_instruction, vote_state::Vote}; + +pub fn new_vote_transaction( + slots: Vec, + bank_hash: Hash, + blockhash: Hash, + node_keypair: &Keypair, + vote_keypair: &Keypair, + authorized_voter_keypair: &Keypair, +) -> Transaction { + let votes = Vote::new(slots, bank_hash); + let vote_ix = vote_instruction::vote( + &vote_keypair.pubkey(), + &authorized_voter_keypair.pubkey(), + votes, + ); + + let mut vote_tx = Transaction::new_with_payer(vec![vote_ix], Some(&node_keypair.pubkey())); + + vote_tx.partial_sign(&[node_keypair], blockhash); + vote_tx.partial_sign(&[authorized_voter_keypair], blockhash); + vote_tx +} From 877596abd86c3aed59ddc27492ed338463acf449 Mon Sep 17 00:00:00 2001 From: Carl Date: Tue, 3 Mar 2020 23:19:33 -0800 Subject: [PATCH 2/2] Logging --- core/src/replay_stage.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/replay_stage.rs b/core/src/replay_stage.rs index 17992eaa6af0a1..4d2e0f1cd52bd3 100644 --- a/core/src/replay_stage.rs +++ b/core/src/replay_stage.rs @@ -1172,6 +1172,7 @@ impl ReplayStage { bank: Arc, slot_full_senders: &[Sender<(u64, Pubkey)>], ) { + info!("bank frozen: {}", bank.slot()); bank.freeze(); slot_full_senders.iter().for_each(|sender| { if let Err(e) = sender.send((bank.slot(), *bank.collector_id())) {