From 7656034bd40bdb0d91d8bb983fc5bb5ca38e6b16 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 18 Sep 2020 05:34:37 +0000 Subject: [PATCH] Fix blockstore processor squash (#12319) (#12323) (cherry picked from commit 3533e11786922e13859fae8aa52c91f6c90153a7) Co-authored-by: carllin --- ledger/src/blockstore_processor.rs | 97 +++++++++++++++++++++++++----- 1 file changed, 83 insertions(+), 14 deletions(-) diff --git a/ledger/src/blockstore_processor.rs b/ledger/src/blockstore_processor.rs index 7c80585ba5..6b6f5a785c 100644 --- a/ledger/src/blockstore_processor.rs +++ b/ledger/src/blockstore_processor.rs @@ -24,6 +24,7 @@ use solana_runtime::{ vote_sender_types::ReplayVoteSender, }; use solana_sdk::{ + account::Account, clock::{Slot, MAX_PROCESSING_AGE}, genesis_config::GenesisConfig, hash::Hash, @@ -35,7 +36,7 @@ use solana_sdk::{ use solana_vote_program::vote_state::VoteState; use std::{ cell::RefCell, - collections::{BTreeMap, HashMap}, + collections::HashMap, path::PathBuf, result, sync::Arc, @@ -877,7 +878,8 @@ fn load_frozen_forks( // for newer cluster confirmed roots let new_root_bank = { if *root == max_root { - supermajority_root_from_bank(&bank).and_then(|supermajority_root| { + supermajority_root_from_vote_accounts(bank.slot(), bank.total_epoch_stake(), bank.vote_accounts() + .into_iter()).and_then(|supermajority_root| { if supermajority_root > *root { // If there's a cluster confirmed root greater than our last // replayed root, then beccause the cluster confirmed root should @@ -944,23 +946,36 @@ fn load_frozen_forks( Ok(initial_forks.values().cloned().collect::>()) } -fn supermajority_root(roots: &BTreeMap, total_epoch_stake: u64) -> Option { +// `roots` is sorted largest to smallest by root slot +fn supermajority_root(roots: &[(Slot, u64)], total_epoch_stake: u64) -> Option { + if roots.is_empty() { + return None; + } + // Find latest root let mut total = 0; - for (root, stake) in roots.iter().rev() { + let mut prev_root = roots[0].0; + for (root, stake) in roots.iter() { + assert!(*root <= prev_root); total += stake; if total as f64 / total_epoch_stake as f64 > VOTE_THRESHOLD_SIZE { return Some(*root); } + prev_root = *root; } None } -fn supermajority_root_from_bank(bank: &Bank) -> Option { - let roots: BTreeMap = bank - .vote_accounts() - .into_iter() +fn supermajority_root_from_vote_accounts( + bank_slot: Slot, + total_epoch_stake: u64, + vote_accounts_iter: I, +) -> Option +where + I: Iterator, +{ + let mut roots_stakes: Vec<(Slot, u64)> = vote_accounts_iter .filter_map(|(key, (stake, account))| { if stake == 0 { return None; @@ -970,8 +985,7 @@ fn supermajority_root_from_bank(bank: &Bank) -> Option { if vote_state.is_none() { warn!( "Unable to get vote_state from account {} in bank: {}", - key, - bank.slot() + key, bank_slot ); return None; } @@ -983,10 +997,11 @@ fn supermajority_root_from_bank(bank: &Bank) -> Option { }) .collect(); - let total_epoch_stake = bank.total_epoch_stake(); + // Sort from greatest to smallest slot + roots_stakes.sort_unstable_by(|a, b| a.0.cmp(&b.0).reverse()); // Find latest root - supermajority_root(&roots, total_epoch_stake) + supermajority_root(&roots_stakes, total_epoch_stake) } // Processes and replays the contents of a single slot, returns Error @@ -1099,7 +1114,6 @@ pub mod tests { use solana_runtime::genesis_utils::{ self, create_genesis_config_with_vote_accounts, ValidatorVoteKeypairs, }; - use solana_sdk::account::Account; use solana_sdk::{ epoch_schedule::EpochSchedule, hash::Hash, @@ -1109,7 +1123,11 @@ pub mod tests { system_transaction, transaction::{Transaction, TransactionError}, }; - use solana_vote_program::{vote_state::MAX_LOCKOUT_HISTORY, vote_transaction}; + use solana_vote_program::{ + self, + vote_state::{VoteStateVersions, MAX_LOCKOUT_HISTORY}, + vote_transaction, + }; use std::{collections::BTreeSet, sync::RwLock}; use trees::tr; @@ -3123,6 +3141,57 @@ pub mod tests { run_test_process_blockstore_with_supermajority_root(Some(1)) } + #[test] + fn test_supermajority_root_from_vote_accounts() { + let convert_to_vote_accounts = + |roots_stakes: Vec<(Slot, u64)>| -> Vec<(Pubkey, (u64, Account))> { + roots_stakes + .into_iter() + .map(|(root, stake)| { + let mut vote_state = VoteState::default(); + vote_state.root_slot = Some(root); + let mut vote_account = + Account::new(1, VoteState::size_of(), &solana_vote_program::id()); + let versioned = VoteStateVersions::Current(Box::new(vote_state)); + VoteState::serialize(&versioned, &mut vote_account.data).unwrap(); + (Pubkey::new_rand(), (stake, vote_account)) + }) + .collect_vec() + }; + + let total_stake = 10; + let slot = 100; + + // Supermajority root should be None + assert!( + supermajority_root_from_vote_accounts(slot, total_stake, std::iter::empty()).is_none() + ); + + // Supermajority root should be None + let roots_stakes = vec![(8, 1), (3, 1), (4, 1), (8, 1)]; + let accounts = convert_to_vote_accounts(roots_stakes); + assert!( + supermajority_root_from_vote_accounts(slot, total_stake, accounts.into_iter()) + .is_none() + ); + + // Supermajority root should be 4, has 7/10 of the stake + let roots_stakes = vec![(8, 1), (3, 1), (4, 1), (8, 5)]; + let accounts = convert_to_vote_accounts(roots_stakes); + assert_eq!( + supermajority_root_from_vote_accounts(slot, total_stake, accounts.into_iter()).unwrap(), + 4 + ); + + // Supermajority root should be 8, it has 7/10 of the stake + let roots_stakes = vec![(8, 1), (3, 1), (4, 1), (8, 6)]; + let accounts = convert_to_vote_accounts(roots_stakes); + assert_eq!( + supermajority_root_from_vote_accounts(slot, total_stake, accounts.into_iter()).unwrap(), + 8 + ); + } + #[test] fn test_process_blockstore_feature_activations_since_genesis() { let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(123);