use {
    crate::{
        consensus::{heaviest_subtree_fork_choice::HeaviestSubtreeForkChoice, tree_diff::TreeDiff},
        repair::{
            repair_generic_traversal::{get_closest_completion, get_unknown_last_index},
            repair_service::{RepairMetrics, RepairService},
            repair_weighted_traversal,
            serve_repair::ShredRepairType,
        },
        replay_stage::DUPLICATE_THRESHOLD,
    },
    solana_clock::{Epoch, Slot},
    solana_epoch_schedule::EpochSchedule,
    solana_hash::Hash,
    solana_ledger::{
        ancestor_iterator::AncestorIterator, blockstore::Blockstore, blockstore_meta::SlotMeta,
    },
    solana_measure::measure::Measure,
    solana_pubkey::Pubkey,
    solana_runtime::epoch_stakes::VersionedEpochStakes,
    std::{
        collections::{HashMap, HashSet, VecDeque},
        iter,
    },
};

#[derive(PartialEq, Eq, Copy, Clone, Hash, Debug)]
enum TreeRoot {
    Root(Slot),
    PrunedRoot(Slot),
}

impl TreeRoot {
    pub fn is_pruned(&self) -> bool {
        matches!(self, Self::PrunedRoot(_))
    }

    #[cfg(test)]
    pub fn slot(&self) -> Slot {
        match self {
            Self::Root(slot) => *slot,
            Self::PrunedRoot(slot) => *slot,
        }
    }
}

impl From<TreeRoot> for Slot {
    fn from(val: TreeRoot) -> Self {
        match val {
            TreeRoot::Root(slot) => slot,
            TreeRoot::PrunedRoot(slot) => slot,
        }
    }
}

#[derive(Clone)]
pub struct RepairWeight {
    // Map from root -> a subtree rooted at that `root`
    trees: HashMap<Slot, HeaviestSubtreeForkChoice>,
    // Map from root -> pruned subtree
    // In the case of duplicate blocks linking back to a slot which is pruned, it is important to
    // hold onto pruned trees so that we can repair / ancestor hashes repair when necessary. Since
    // the parent slot is pruned these blocks will never be replayed / marked dead, so the existing
    // dead duplicate confirmed pathway will not catch this special case.
    // We manage the size by removing slots < root
    pruned_trees: HashMap<Slot, HeaviestSubtreeForkChoice>,

    // Maps each slot to the root of the tree that contains it
    slot_to_tree: HashMap<Slot, TreeRoot>,
    root: Slot,
}

impl RepairWeight {
    pub fn new(root: Slot) -> Self {
        let root_tree = HeaviestSubtreeForkChoice::new((root, Hash::default()));
        let slot_to_tree = HashMap::from([(root, TreeRoot::Root(root))]);
        let trees = HashMap::from([(root, root_tree)]);
        Self {
            trees,
            slot_to_tree,
            root,
            pruned_trees: HashMap::new(),
        }
    }

    pub fn add_votes<I>(
        &mut self,
        blockstore: &Blockstore,
        votes: I,
        epoch_stakes: &HashMap<Epoch, VersionedEpochStakes>,
        epoch_schedule: &EpochSchedule,
    ) where
        I: Iterator<Item = (Slot, Vec<Pubkey>)>,
    {
        let mut all_subtree_updates: HashMap<TreeRoot, HashMap<Pubkey, Slot>> = HashMap::new();
        for (slot, pubkey_votes) in votes {
            if slot < self.root {
                continue;
            }
            let mut tree_root = self.get_tree_root(slot);
            let mut new_ancestors = VecDeque::new();
            // If we don't know know  how this slot chains to any existing trees
            // in `self.trees` or `self.pruned_trees`, then use `blockstore` to see if this chains
            // any existing trees in `self.trees`
            if tree_root.is_none() {
                let (discovered_ancestors, existing_subtree_root) =
                    self.find_ancestor_subtree_of_slot(blockstore, slot);
                new_ancestors = discovered_ancestors;
                tree_root = existing_subtree_root;
            }

            let (tree_root, tree) = {
                match (tree_root, *new_ancestors.front().unwrap_or(&slot)) {
                    (Some(tree_root), _) if !tree_root.is_pruned() => (
                        tree_root,
                        self.trees
                            .get_mut(&tree_root.into())
                            .expect("If tree root was found, it must exist in `self.trees`"),
                    ),
                    (Some(tree_root), _) => (
                        tree_root,
                        self.pruned_trees.get_mut(&tree_root.into()).expect(
                            "If a pruned tree root was found, it must exist in `self.pruned_trees`",
                        ),
                    ),
                    (None, earliest_ancestor) => {
                        // There is no known subtree that contains `slot`. Thus, create a new
                        // subtree rooted at the earliest known ancestor of `slot`.
                        // If this earliest known ancestor is not part of the rooted path, create a new
                        // pruned tree from the ancestor that is `> self.root` instead.
                        if earliest_ancestor < self.root {
                            // If the next ancestor exists, it is guaranteed to be `> self.root` because
                            // `find_ancestor_subtree_of_slot` can return at max one ancestor `<
                            // self.root`.
                            let next_earliest_ancestor = *new_ancestors.get(1).unwrap_or(&slot);
                            assert!(next_earliest_ancestor > self.root);
                            // We also guarantee that next_earliest_ancestor does not
                            // already exist as a pruned tree (pre condition for inserting a new
                            // pruned tree) otherwise `tree_root` would not be None.
                            self.insert_new_pruned_tree(next_earliest_ancestor);
                            // Remove `earliest_ancestor` as it should not be added to the tree (we
                            // maintain the invariant that the tree only contains slots >=
                            // `self.root` by checking before `add_new_leaf_slot` and `set_root`)
                            assert_eq!(Some(earliest_ancestor), new_ancestors.pop_front());
                            (
                                TreeRoot::PrunedRoot(next_earliest_ancestor),
                                self.pruned_trees.get_mut(&next_earliest_ancestor).unwrap(),
                            )
                        } else {
                            // We guarantee that `earliest_ancestor` does not already exist in
                            // `self.trees` otherwise `tree_root` would not be None
                            self.insert_new_tree(earliest_ancestor);
                            (
                                TreeRoot::Root(earliest_ancestor),
                                self.trees.get_mut(&earliest_ancestor).unwrap(),
                            )
                        }
                    }
                }
            };

            // First element in `ancestors` must be either:
            // 1) Leaf of some existing subtree
            // 2) Root of new subtree that was just created above through `self.insert_new_tree` or
            //    `self.insert_new_pruned_tree`
            new_ancestors.push_back(slot);
            if new_ancestors.len() > 1 {
                for i in 0..new_ancestors.len() - 1 {
                    // TODO: Repair right now does not distinguish between votes for different
                    // versions of the same slot.
                    tree.add_new_leaf_slot(
                        (new_ancestors[i + 1], Hash::default()),
                        Some((new_ancestors[i], Hash::default())),
                    );
                    self.slot_to_tree.insert(new_ancestors[i + 1], tree_root);
                }
            }

            // Now we know which subtree this slot chains to,
            // add the votes to the list of updates
            let subtree_updates = all_subtree_updates.entry(tree_root).or_default();
            for pubkey in pubkey_votes {
                let cur_max = subtree_updates.entry(pubkey).or_default();
                *cur_max = std::cmp::max(*cur_max, slot);
            }
        }

        for (tree_root, updates) in all_subtree_updates {
            let tree = self
                .get_tree_mut(tree_root)
                .expect("Tree for `tree_root` must exist here");
            let updates: Vec<_> = updates.into_iter().collect();
            tree.add_votes(
                updates
                    .iter()
                    .map(|(pubkey, slot)| (*pubkey, (*slot, Hash::default()))),
                epoch_stakes,
                epoch_schedule,
            );
        }
    }

    #[allow(clippy::too_many_arguments)]
    pub fn get_best_weighted_repairs(
        &mut self,
        blockstore: &Blockstore,
        epoch_stakes: &HashMap<Epoch, VersionedEpochStakes>,
        epoch_schedule: &EpochSchedule,
        max_new_orphans: usize,
        max_new_shreds: usize,
        max_unknown_last_index_repairs: usize,
        max_closest_completion_repairs: usize,
        repair_metrics: &mut RepairMetrics,
        outstanding_repairs: &mut HashMap<ShredRepairType, u64>,
    ) -> Vec<ShredRepairType> {
        let mut repairs = vec![];
        let mut processed_slots = HashSet::from([self.root]);
        let mut slot_meta_cache = HashMap::default();

        let mut get_best_orphans_elapsed = Measure::start("get_best_orphans");
        // Find the best orphans in order from heaviest stake to least heavy
        self.get_best_orphans(
            blockstore,
            &mut processed_slots,
            &mut repairs,
            epoch_stakes,
            epoch_schedule,
            max_new_orphans,
            outstanding_repairs,
        );
        // Subtract 1 because the root is not processed as an orphan
        let num_orphan_slots = processed_slots.len() - 1;
        let num_orphan_repairs = repairs.len();
        get_best_orphans_elapsed.stop();

        let mut get_best_shreds_elapsed = Measure::start("get_best_shreds");
        let mut best_shreds_repairs = Vec::default();
        // Find the best incomplete slots in rooted subtree
        self.get_best_shreds(
            blockstore,
            &mut slot_meta_cache,
            &mut best_shreds_repairs,
            max_new_shreds,
            outstanding_repairs,
        );
        let num_best_shreds_repairs = best_shreds_repairs.len();
        let repair_slots_set: HashSet<Slot> =
            best_shreds_repairs.iter().map(|r| r.slot()).collect();
        let num_best_shreds_slots = repair_slots_set.len();
        processed_slots.extend(repair_slots_set);
        repairs.extend(best_shreds_repairs);
        get_best_shreds_elapsed.stop();

        // Although we have generated repairs for orphan roots and slots in the rooted subtree,
        // if we have space we should generate repairs for slots in orphan trees in preparation for
        // when they are no longer rooted. Here we generate repairs for slots with unknown last
        // indices as well as slots that are close to completion.

        let mut get_unknown_last_index_elapsed = Measure::start("get_unknown_last_index");
        let pre_num_slots = processed_slots.len();
        let unknown_last_index_repairs = self.get_best_unknown_last_index(
            blockstore,
            &mut slot_meta_cache,
            &mut processed_slots,
            max_unknown_last_index_repairs,
            outstanding_repairs,
        );
        let num_unknown_last_index_repairs = unknown_last_index_repairs.len();
        let num_unknown_last_index_slots = processed_slots.len() - pre_num_slots;
        repairs.extend(unknown_last_index_repairs);
        get_unknown_last_index_elapsed.stop();

        let mut get_closest_completion_elapsed = Measure::start("get_closest_completion");
        let pre_num_slots = processed_slots.len();
        let (closest_completion_repairs, total_slots_processed) = self.get_best_closest_completion(
            blockstore,
            &mut slot_meta_cache,
            &mut processed_slots,
            max_closest_completion_repairs,
            outstanding_repairs,
        );
        let num_closest_completion_repairs = closest_completion_repairs.len();
        let num_closest_completion_slots = processed_slots.len() - pre_num_slots;
        let num_closest_completion_slots_path =
            total_slots_processed.saturating_sub(num_closest_completion_slots);
        repairs.extend(closest_completion_repairs);
        get_closest_completion_elapsed.stop();

        repair_metrics.best_repairs_stats.update(
            num_orphan_slots as u64,
            num_orphan_repairs as u64,
            num_best_shreds_slots as u64,
            num_best_shreds_repairs as u64,
            num_unknown_last_index_slots as u64,
            num_unknown_last_index_repairs as u64,
            num_closest_completion_slots as u64,
            num_closest_completion_slots_path as u64,
            num_closest_completion_repairs as u64,
            self.trees.len() as u64,
        );
        repair_metrics.timing.get_best_orphans_elapsed += get_best_orphans_elapsed.as_us();
        repair_metrics.timing.get_best_shreds_elapsed += get_best_shreds_elapsed.as_us();
        repair_metrics.timing.get_unknown_last_index_elapsed +=
            get_unknown_last_index_elapsed.as_us();
        repair_metrics.timing.get_closest_completion_elapsed +=
            get_closest_completion_elapsed.as_us();

        repairs
    }

    /// Split `slot` and descendants into an orphan tree in repair weighting.
    ///
    /// If repair holds a subtree `ST` which contains `slot`, we split `ST` into `(T, T')` where `T'` is
    /// a subtree rooted at `slot` and `T` is what remains (if anything) of `ST` after the split.
    /// If `T` is non-empty, it is inserted back into the set which held the original `ST`
    /// (self.trees for potentially rootable trees, or self.pruned_trees for pruned trees).
    /// `T'` is always inserted into the potentially rootable set `self.trees`.
    /// This function removes and destroys the original `ST`.
    ///
    /// Assumes that `slot` is greater than `self.root`.
    /// Returns slots that were orphaned
    pub fn split_off(&mut self, slot: Slot) -> HashSet<Slot> {
        assert!(slot >= self.root);
        if slot == self.root {
            error!("Trying to orphan root of repair tree {slot}");
            return HashSet::new();
        }
        match self.slot_to_tree.get(&slot).copied() {
            Some(TreeRoot::Root(subtree_root)) => {
                if subtree_root == slot {
                    info!("{slot} is already orphan, skipping");
                    return HashSet::new();
                }
                let subtree = self
                    .trees
                    .get_mut(&subtree_root)
                    .expect("`self.slot_to_tree` and `self.trees` must be in sync");
                let orphaned_tree = subtree.split_off(&(slot, Hash::default()));
                self.rename_tree_root(&orphaned_tree, TreeRoot::Root(slot));
                self.trees.insert(slot, orphaned_tree);
                self.trees.get(&slot).unwrap().slots_iter().collect()
            }
            Some(TreeRoot::PrunedRoot(subtree_root)) => {
                // Even if these orphaned slots were previously pruned, they should be added back to
                // `self.trees` as we are no longer sure of their ancestry.
                // After they are repaired there is a chance that they  are now part of the rooted path.
                // This is possible for a duplicate slot with multiple ancestors, if the
                // version we had pruned before had the wrong ancestor, and the correct version is
                // descended from the rooted path.
                // If not they will once again be attached to the pruned set in
                // `update_orphan_ancestors`.

                info!("Dumping pruned slot {slot} of tree {subtree_root} in repair");
                let mut subtree = self
                    .pruned_trees
                    .remove(&subtree_root)
                    .expect("`self.slot_to_tree` and `self.pruned_trees` must be in sync");

                if subtree_root == slot {
                    // In this case we simply unprune the entire subtree by adding this subtree
                    // back into the main set of trees, self.trees
                    self.rename_tree_root(&subtree, TreeRoot::Root(subtree_root));
                    self.trees.insert(subtree_root, subtree);
                    self.trees
                        .get(&subtree_root)
                        .unwrap()
                        .slots_iter()
                        .collect()
                } else {
                    let orphaned_tree = subtree.split_off(&(slot, Hash::default()));
                    self.pruned_trees.insert(subtree_root, subtree);
                    self.rename_tree_root(&orphaned_tree, TreeRoot::Root(slot));
                    self.trees.insert(slot, orphaned_tree);
                    self.trees.get(&slot).unwrap().slots_iter().collect()
                }
            }
            None => {
                warn!("Trying to split off slot {slot} which doesn't currently exist in repair");
                HashSet::new()
            }
        }
    }

    pub fn set_root(&mut self, new_root: Slot) {
        // Roots should be monotonically increasing
        assert!(self.root <= new_root);

        if new_root == self.root {
            return;
        }

        // Root slot of the tree that contains `new_root`, if one exists
        let new_root_tree_root = self
            .slot_to_tree
            .get(&new_root)
            .copied()
            .map(|root| match root {
                TreeRoot::Root(r) => r,
                TreeRoot::PrunedRoot(r) => {
                    panic!("New root {new_root} chains to a pruned tree with root {r}")
                }
            });

        // Purge outdated trees from `self.trees`
        // These are all subtrees that have a `tree_root` < `new_root` and are not part of the
        // rooted subtree
        let subtrees_to_purge: Vec<_> = self
            .trees
            .keys()
            .filter(|subtree_root| {
                **subtree_root < new_root
                    && new_root_tree_root
                        .map(|new_root_tree_root| **subtree_root != new_root_tree_root)
                        .unwrap_or(true)
            })
            .copied()
            .collect();
        for subtree_root in subtrees_to_purge {
            let subtree = self
                .trees
                .remove(&subtree_root)
                .expect("Must exist, was found in `self.trees` above");

            // Track these trees as part of the pruned set
            self.rename_tree_root(&subtree, TreeRoot::PrunedRoot(subtree_root));
            self.pruned_trees.insert(subtree_root, subtree);
        }

        if let Some(new_root_tree_root) = new_root_tree_root {
            let mut new_root_tree = self
                .trees
                .remove(&new_root_tree_root)
                .expect("Found slot root earlier in self.slot_to_trees, tree must exist");

            // Find all descendants of `self.root` that are not reachable from `new_root`.
            // Prune these out and add to `self.pruned_trees`
            trace!("pruning tree {new_root_tree_root} with {new_root}");
            let (removed, pruned) = new_root_tree.purge_prune((new_root, Hash::default()));
            for pruned_tree in pruned {
                let pruned_tree_root = pruned_tree.tree_root().0;
                self.rename_tree_root(&pruned_tree, TreeRoot::PrunedRoot(pruned_tree_root));
                self.pruned_trees.insert(pruned_tree_root, pruned_tree);
            }

            for (slot, _) in removed {
                self.slot_to_tree.remove(&slot);
            }

            // Update `self.slot_to_tree` to reflect new root
            new_root_tree.set_tree_root((new_root, Hash::default()));
            self.rename_tree_root(&new_root_tree, TreeRoot::Root(new_root));

            // Insert the tree for the new root
            self.trees.insert(new_root, new_root_tree);
        } else {
            // Guaranteed that `new_root` is not part of `self.trees` because `new_root_tree_root`
            // is None
            self.insert_new_tree(new_root);
        }

        // Clean up the pruned set by trimming slots that are less than `new_root` and removing
        // empty trees
        self.pruned_trees = self
            .pruned_trees
            .drain()
            .flat_map(|(tree_root, mut pruned_tree)| {
                if tree_root < new_root {
                    trace!("pruning tree {tree_root} with {new_root}");
                    let (removed, pruned) = pruned_tree.purge_prune((new_root, Hash::default()));
                    for (slot, _) in removed {
                        self.slot_to_tree.remove(&slot);
                    }
                    pruned
                        .into_iter()
                        .chain(iter::once(pruned_tree)) // Add back the original pruned tree
                        .filter(|pruned_tree| !pruned_tree.is_empty()) // Clean up empty trees
                        .map(|new_pruned_subtree| {
                            let new_pruned_tree_root = new_pruned_subtree.tree_root().0;
                            // Resync `self.slot_to_tree`
                            for ((slot, _), _) in new_pruned_subtree.all_slots_stake_voted_subtree()
                            {
                                *self.slot_to_tree.get_mut(slot).unwrap() =
                                    TreeRoot::PrunedRoot(new_pruned_tree_root);
                            }
                            (new_pruned_tree_root, new_pruned_subtree)
                        })
                        .collect()
                } else {
                    vec![(tree_root, pruned_tree)]
                }
            })
            .collect::<HashMap<u64, HeaviestSubtreeForkChoice>>();
        self.root = new_root;
    }

    pub fn root(&self) -> Slot {
        self.root
    }

    // Generate shred repairs for main subtree rooted at `self.root`
    fn get_best_shreds(
        &mut self,
        blockstore: &Blockstore,
        slot_meta_cache: &mut HashMap<Slot, Option<SlotMeta>>,
        repairs: &mut Vec<ShredRepairType>,
        max_new_shreds: usize,
        outstanding_repairs: &mut HashMap<ShredRepairType, u64>,
    ) {
        let root_tree = self.trees.get(&self.root).expect("Root tree must exist");
        repair_weighted_traversal::get_best_repair_shreds(
            root_tree,
            blockstore,
            slot_meta_cache,
            repairs,
            max_new_shreds,
            outstanding_repairs,
        );
    }

    fn get_best_orphans(
        &mut self,
        blockstore: &Blockstore,
        processed_slots: &mut HashSet<Slot>,
        repairs: &mut Vec<ShredRepairType>,
        epoch_stakes: &HashMap<Epoch, VersionedEpochStakes>,
        epoch_schedule: &EpochSchedule,
        max_new_orphans: usize,
        outstanding_repairs: &mut HashMap<ShredRepairType, u64>,
    ) {
        // Sort each tree in `self.trees`, by the amount of stake that has voted on each,
        // tiebreaker going to earlier slots, thus prioritizing earlier slots on the same fork
        // to ensure replay can continue as soon as possible.
        let mut stake_weighted_trees: Vec<(Slot, u64)> = self
            .trees
            .iter()
            .map(|(slot, tree)| {
                (
                    *slot,
                    tree.stake_voted_subtree(&(*slot, Hash::default()))
                        .expect("Tree must have weight at its own root"),
                )
            })
            .collect();

        // Heavier, smaller slots come first
        Self::sort_by_stake_weight_slot(&mut stake_weighted_trees);
        let mut new_best_orphan_requests = 0;
        for (heaviest_tree_root, _) in stake_weighted_trees {
            if new_best_orphan_requests >= max_new_orphans {
                break;
            }
            if processed_slots.contains(&heaviest_tree_root) {
                continue;
            }
            // Ignore trees that were merged in a previous iteration
            if self.trees.contains_key(&heaviest_tree_root) {
                let new_orphan_root = self.update_orphan_ancestors(
                    blockstore,
                    heaviest_tree_root,
                    epoch_stakes,
                    epoch_schedule,
                );
                if let Some(new_orphan_root) = new_orphan_root {
                    if new_orphan_root != self.root {
                        if let Some(repair_request) = RepairService::request_repair_if_needed(
                            outstanding_repairs,
                            ShredRepairType::Orphan(new_orphan_root),
                        ) {
                            repairs.push(repair_request);
                            processed_slots.insert(new_orphan_root);
                            new_best_orphan_requests += 1;
                        }
                    }
                }
            }
        }

        // If there are fewer than `max_new_orphans`, just grab the next
        // available ones.
        for new_orphan in blockstore.orphans_iterator(self.root + 1).unwrap() {
            if new_best_orphan_requests >= max_new_orphans {
                break;
            }
            if let Some(repair_request) = RepairService::request_repair_if_needed(
                outstanding_repairs,
                ShredRepairType::Orphan(new_orphan),
            ) {
                repairs.push(repair_request);
                processed_slots.insert(new_orphan);
                new_best_orphan_requests += 1;
            }
        }
    }

    /// For all remaining trees (orphan and rooted), generate repairs for slots missing last_index info
    /// prioritized by # shreds received.
    fn get_best_unknown_last_index(
        &mut self,
        blockstore: &Blockstore,
        slot_meta_cache: &mut HashMap<Slot, Option<SlotMeta>>,
        processed_slots: &mut HashSet<Slot>,
        max_new_repairs: usize,
        outstanding_repairs: &mut HashMap<ShredRepairType, u64>,
    ) -> Vec<ShredRepairType> {
        let mut repairs = Vec::default();
        for (_slot, tree) in self.trees.iter() {
            if repairs.len() >= max_new_repairs {
                break;
            }
            let new_repairs = get_unknown_last_index(
                tree,
                blockstore,
                slot_meta_cache,
                processed_slots,
                max_new_repairs - repairs.len(),
                outstanding_repairs,
            );
            repairs.extend(new_repairs);
        }
        repairs
    }

    /// For all remaining trees (orphan and rooted), generate repairs for subtrees that have last
    /// index info but are missing shreds prioritized by how close to completion they are. These
    /// repairs are also prioritized by age of ancestors, so slots close to completion will first
    /// start by repairing broken ancestors.
    fn get_best_closest_completion(
        &mut self,
        blockstore: &Blockstore,
        slot_meta_cache: &mut HashMap<Slot, Option<SlotMeta>>,
        processed_slots: &mut HashSet<Slot>,
        max_new_repairs: usize,
        outstanding_repairs: &mut HashMap<ShredRepairType, u64>,
    ) -> (Vec<ShredRepairType>, /* processed slots */ usize) {
        let mut repairs = Vec::default();
        let mut total_processed_slots = 0;
        for (_slot, tree) in self.trees.iter() {
            if repairs.len() >= max_new_repairs {
                break;
            }
            let (new_repairs, new_processed_slots) = get_closest_completion(
                tree,
                blockstore,
                self.root,
                slot_meta_cache,
                processed_slots,
                max_new_repairs - repairs.len(),
                outstanding_repairs,
            );
            repairs.extend(new_repairs);
            total_processed_slots += new_processed_slots;
        }
        (repairs, total_processed_slots)
    }

    /// Attempts to chain the orphan subtree rooted at `orphan_tree_root`
    /// to any earlier subtree with new ancestry information in `blockstore`.
    /// Returns the earliest known ancestor of `heaviest_tree_root`.
    fn update_orphan_ancestors(
        &mut self,
        blockstore: &Blockstore,
        mut orphan_tree_root: Slot,
        epoch_stakes: &HashMap<Epoch, VersionedEpochStakes>,
        epoch_schedule: &EpochSchedule,
    ) -> Option<Slot> {
        // Must only be called on existing orphan trees
        assert!(self.trees.contains_key(&orphan_tree_root));

        // Check blockstore for any new parents of the heaviest orphan tree. Attempt
        // to chain the orphan back to the main fork rooted at `self.root`.
        while orphan_tree_root != self.root {
            let (new_ancestors, parent_tree_root) =
                self.find_ancestor_subtree_of_slot(blockstore, orphan_tree_root);
            {
                let heaviest_tree = self
                    .trees
                    .get_mut(&orphan_tree_root)
                    .expect("Orphan must exist");

                // Skip the leaf of the parent tree that the orphan would merge
                // with later in a call to `merge_trees`
                let num_skip = usize::from(parent_tree_root.is_some());

                for ancestor in new_ancestors.iter().skip(num_skip).rev() {
                    // We temporarily use orphan_tree_root as the tree root and later
                    // rename tree root to either the parent_tree_root or the earliest_ancestor
                    if *ancestor >= self.root {
                        self.slot_to_tree
                            .insert(*ancestor, TreeRoot::Root(orphan_tree_root));
                        heaviest_tree.add_root_parent((*ancestor, Hash::default()));
                    }
                }
            }
            if let Some(parent_tree_root) = parent_tree_root {
                // If another subtree is discovered to be the parent
                // of this subtree, merge the two.
                self.merge_trees(
                    TreeRoot::Root(orphan_tree_root),
                    parent_tree_root,
                    *new_ancestors
                        .front()
                        .expect("Must exist leaf to merge to if `tree_to_merge`.is_some()"),
                    epoch_stakes,
                    epoch_schedule,
                );
                if let TreeRoot::Root(parent_tree_root) = parent_tree_root {
                    orphan_tree_root = parent_tree_root;
                } else {
                    // This orphan is now part of a pruned tree, no need to chain further
                    return None;
                }
            } else {
                // If there's no other subtree to merge with, then return
                // the current known earliest ancestor of this branch
                if let Some(earliest_ancestor) = new_ancestors.front() {
                    assert!(*earliest_ancestor != self.root); // In this case we would merge above
                    let orphan_tree = self
                        .trees
                        .remove(&orphan_tree_root)
                        .expect("orphan tree must exist");
                    if *earliest_ancestor > self.root {
                        self.rename_tree_root(&orphan_tree, TreeRoot::Root(*earliest_ancestor));
                        assert!(self.trees.insert(*earliest_ancestor, orphan_tree).is_none());
                        orphan_tree_root = *earliest_ancestor;
                    } else {
                        // In this case we should create a new pruned subtree
                        let next_earliest_ancestor =
                            new_ancestors.get(1).unwrap_or(&orphan_tree_root);
                        self.rename_tree_root(
                            &orphan_tree,
                            TreeRoot::PrunedRoot(*next_earliest_ancestor),
                        );
                        assert!(self
                            .pruned_trees
                            .insert(*next_earliest_ancestor, orphan_tree)
                            .is_none());
                        return None;
                    }
                }
                break;
            }
        }

        // Return the (potentially new in the case of some merges)
        // root of this orphan subtree
        Some(orphan_tree_root)
    }

    /// If any pruned trees reach the `DUPLICATE_THRESHOLD`, there is a high chance that they are
    /// duplicate confirmed (can't say for sure because we don't differentiate by hash in
    /// `repair_weight`).
    /// For each such pruned tree, find the deepest child which has reached `DUPLICATE_THRESHOLD`
    /// for handling in ancestor hashes repair
    /// We refer to such trees as "popular" pruned forks, and the deepest child as the "popular" pruned
    /// slot of the fork.
    ///
    /// `DUPLICATE_THRESHOLD` is expected to be > 50%.
    /// It is expected that no two children of a parent could both reach `DUPLICATE_THRESHOLD`.
    pub fn get_popular_pruned_forks(
        &self,
        epoch_stakes: &HashMap<Epoch, VersionedEpochStakes>,
        epoch_schedule: &EpochSchedule,
    ) -> Vec<Slot> {
        #[cfg(test)]
        static_assertions::const_assert!(DUPLICATE_THRESHOLD > 0.5);
        let mut repairs = vec![];
        for (pruned_root, pruned_tree) in self.pruned_trees.iter() {
            let mut slot_to_start_repair = (*pruned_root, Hash::default());

            // This pruned tree *could* span an epoch boundary. To be safe we use the
            // minimum DUPLICATE_THRESHOLD across slots in case of stake modification. This
            // *could* lead to a false positive.
            //
            // Additionally, we could have a case where a slot that reached `DUPLICATE_THRESHOLD`
            // no longer reaches threshold post epoch boundary due to stake modifications.
            //
            // Post boundary, we have 2 cases:
            //      1) The previously popular slot stays as the majority fork. In this
            //         case it will eventually reach the new duplicate threshold and
            //         validators missing the correct version will be able to trigger this pruned
            //         repair pathway.
            //      2) With the stake modifications, this previously popular slot no
            //         longer holds the majority stake. The remaining stake is now expected to
            //         reach consensus on a new fork post epoch boundary. Once this consensus is
            //         reached, validators on the popular pruned fork will be able to switch
            //         to the new majority fork.
            //
            // In either case, `HeaviestSubtreeForkChoice` updates the stake only when observing new
            // votes leading to a potential mixed bag of stakes being observed. It is safest to use
            // the minimum threshold from either side of the boundary.
            let min_total_stake = pruned_tree
                .slots_iter()
                .map(|slot| {
                    epoch_stakes
                        .get(&epoch_schedule.get_epoch(slot))
                        .expect("Pruned tree cannot contain slots more than an epoch behind")
                        .total_stake()
                })
                .min()
                .expect("Pruned tree cannot be empty");
            let duplicate_confirmed_threshold =
                ((min_total_stake as f64) * DUPLICATE_THRESHOLD) as u64;

            // TODO: `HeaviestSubtreeForkChoice` subtracts and migrates stake as validators switch
            // forks within the rooted subtree, however `repair_weight` does not migrate stake
            // across subtrees. This could lead to an additional false positive if validators
            // switch post prune as stake added to a pruned tree it is never removed.
            // A further optimization could be to store an additional `latest_votes`
            // in `repair_weight` to manage switching across subtrees.
            if pruned_tree
                .stake_voted_subtree(&slot_to_start_repair)
                .expect("Root of tree must exist")
                >= duplicate_confirmed_threshold
            {
                // Search to find the deepest node that still has >= duplicate_confirmed_threshold (could
                // just use best slot but this is a slight optimization that will save us some iterations
                // in ancestor repair)
                while let Some(child) = pruned_tree
                    .children(&slot_to_start_repair)
                    .expect("Found earlier, this slot should exist")
                    .find(|c| {
                        pruned_tree
                            .stake_voted_subtree(c)
                            .expect("Found in children must exist")
                            >= duplicate_confirmed_threshold
                    })
                {
                    slot_to_start_repair = *child;
                }
                repairs.push(slot_to_start_repair.0);
            }
        }
        repairs
    }

    fn get_tree(&self, tree_root: TreeRoot) -> Option<&HeaviestSubtreeForkChoice> {
        match tree_root {
            TreeRoot::Root(r) => self.trees.get(&r),
            TreeRoot::PrunedRoot(r) => self.pruned_trees.get(&r),
        }
    }

    fn get_tree_mut(&mut self, tree_root: TreeRoot) -> Option<&mut HeaviestSubtreeForkChoice> {
        match tree_root {
            TreeRoot::Root(r) => self.trees.get_mut(&r),
            TreeRoot::PrunedRoot(r) => self.pruned_trees.get_mut(&r),
        }
    }

    fn remove_tree(&mut self, tree_root: TreeRoot) -> Option<HeaviestSubtreeForkChoice> {
        match tree_root {
            TreeRoot::Root(r) => self.trees.remove(&r),
            TreeRoot::PrunedRoot(r) => self.pruned_trees.remove(&r),
        }
    }

    fn get_tree_root(&self, slot: Slot) -> Option<TreeRoot> {
        self.slot_to_tree.get(&slot).copied()
    }

    /// Returns true iff `slot` is currently tracked and in a pruned tree
    pub fn is_pruned(&self, slot: Slot) -> bool {
        self.get_tree_root(slot)
            .as_ref()
            .map(TreeRoot::is_pruned)
            .unwrap_or(false)
    }

    /// Returns true iff `slot1` and `slot2` are both tracked and belong to the same tree
    pub fn same_tree(&self, slot1: Slot, slot2: Slot) -> bool {
        self.get_tree_root(slot1)
            .and_then(|tree_root| self.get_tree(tree_root))
            .map(|tree| tree.contains_block(&(slot2, Hash::default())))
            .unwrap_or(false)
    }

    /// Assumes that `new_tree_root` does not already exist in `self.trees`
    fn insert_new_tree(&mut self, new_tree_root: Slot) {
        assert!(!self.trees.contains_key(&new_tree_root));

        // Update `self.slot_to_tree`
        self.slot_to_tree
            .insert(new_tree_root, TreeRoot::Root(new_tree_root));
        self.trees.insert(
            new_tree_root,
            HeaviestSubtreeForkChoice::new((new_tree_root, Hash::default())),
        );
    }

    /// Assumes that `new_pruned_tree_root` does not already exist in `self.pruned_trees`
    fn insert_new_pruned_tree(&mut self, new_pruned_tree_root: Slot) {
        assert!(!self.pruned_trees.contains_key(&new_pruned_tree_root));

        // Update `self.slot_to_tree`
        self.slot_to_tree.insert(
            new_pruned_tree_root,
            TreeRoot::PrunedRoot(new_pruned_tree_root),
        );
        self.pruned_trees.insert(
            new_pruned_tree_root,
            HeaviestSubtreeForkChoice::new((new_pruned_tree_root, Hash::default())),
        );
    }

    /// Finds any ancestors available from `blockstore` for `slot`.
    /// Ancestor search is stopped when finding one that chains to any
    /// tree in `self.trees` or `self.pruned_trees` or if the ancestor is < self.root.
    ///
    /// Returns ancestors (including the one that triggered the search stop condition),
    /// and possibly the tree_root that `slot` belongs to
    fn find_ancestor_subtree_of_slot(
        &self,
        blockstore: &Blockstore,
        slot: Slot,
    ) -> (VecDeque<Slot>, Option<TreeRoot>) {
        let ancestors = AncestorIterator::new(slot, blockstore);
        let mut ancestors_to_add = VecDeque::new();
        let mut tree = None;
        // This means `heaviest_tree_root` has not been
        // chained back to slots currently being replayed
        // in BankForks. Try to see if blockstore has sufficient
        // information to link this slot back
        for a in ancestors {
            ancestors_to_add.push_front(a);
            // If an ancestor chains back to another subtree or pruned, then return
            tree = self.get_tree_root(a);
            if tree.is_some() || a < self.root {
                break;
            }
        }

        (ancestors_to_add, tree)
    }

    /// Attaches `tree1` rooted at `root1` to `tree2` rooted at `root2`
    /// at the leaf in `tree2` given by `merge_leaf`
    /// Expects `root1` and `root2` to exist in `self.trees` or `self.pruned_trees`
    fn merge_trees(
        &mut self,
        root1: TreeRoot,
        root2: TreeRoot,
        merge_leaf: Slot,
        epoch_stakes: &HashMap<Epoch, VersionedEpochStakes>,
        epoch_schedule: &EpochSchedule,
    ) {
        // Update self.slot_to_tree to reflect the merge
        let tree1 = self.remove_tree(root1).expect("tree to merge must exist");
        self.rename_tree_root(&tree1, root2);

        // Merge trees
        let tree2 = self
            .get_tree_mut(root2)
            .expect("tree to be merged into must exist");

        tree2.merge(
            tree1,
            &(merge_leaf, Hash::default()),
            epoch_stakes,
            epoch_schedule,
        );
    }

    // Update all slots in the `tree1` to point to `root2`,
    fn rename_tree_root(&mut self, tree1: &HeaviestSubtreeForkChoice, root2: TreeRoot) {
        let all_slots = tree1.all_slots_stake_voted_subtree();
        for ((slot, _), _) in all_slots {
            *self
                .slot_to_tree
                .get_mut(slot)
                .expect("Nodes in tree must exist in `self.slot_to_tree`") = root2;
        }
    }

    // Heavier, smaller slots come first
    fn sort_by_stake_weight_slot(slot_stake_voted: &mut [(Slot, u64)]) {
        slot_stake_voted.sort_by(|(slot, stake_voted), (slot_, stake_voted_)| {
            if stake_voted == stake_voted_ {
                slot.cmp(slot_)
            } else {
                stake_voted.cmp(stake_voted_).reverse()
            }
        });
    }
}

#[cfg(test)]
mod test {
    use {
        super::*,
        itertools::Itertools,
        solana_accounts_db::contains::Contains,
        solana_hash::Hash,
        solana_ledger::{
            blockstore::{make_chaining_slot_entries, Blockstore},
            get_tmp_ledger_path,
        },
        solana_runtime::{bank::Bank, bank_utils},
        trees::tr,
    };

    #[test]
    fn test_sort_by_stake_weight_slot() {
        let mut slots = vec![(3, 30), (2, 30), (5, 31)];
        RepairWeight::sort_by_stake_weight_slot(&mut slots);
        assert_eq!(slots, vec![(5, 31), (2, 30), (3, 30)]);
    }

    #[test]
    fn test_add_votes_invalid() {
        let (blockstore, bank, mut repair_weight) = setup_orphan_repair_weight();
        let root = 3;
        repair_weight.set_root(root);

        // Try to add a vote for slot < root and a slot that is unrooted
        for old_slot in &[2, 4] {
            if *old_slot > root {
                assert!(repair_weight
                    .slot_to_tree
                    .get(old_slot)
                    .unwrap()
                    .is_pruned());
            } else {
                assert!(!repair_weight.slot_to_tree.contains(old_slot));
            }
            let votes = vec![(*old_slot, vec![Pubkey::default()])];
            repair_weight.add_votes(
                &blockstore,
                votes.into_iter(),
                bank.epoch_stakes_map(),
                bank.epoch_schedule(),
            );
            if *old_slot > root {
                assert!(repair_weight.pruned_trees.contains_key(old_slot));
                assert!(repair_weight
                    .slot_to_tree
                    .get(old_slot)
                    .unwrap()
                    .is_pruned());
            } else {
                assert!(!repair_weight.trees.contains_key(old_slot));
                assert!(!repair_weight.slot_to_tree.contains_key(old_slot));
            }
        }
    }

    #[test]
    fn test_add_votes() {
        let blockstore = setup_forks();
        let stake = 100;
        let (bank, vote_pubkeys) = bank_utils::setup_bank_and_vote_pubkeys_for_tests(3, stake);
        let votes = vec![(1, vote_pubkeys.clone())];

        let mut repair_weight = RepairWeight::new(0);
        repair_weight.add_votes(
            &blockstore,
            votes.into_iter(),
            bank.epoch_stakes_map(),
            bank.epoch_schedule(),
        );

        // repair_weight should contain one subtree 0->1
        assert_eq!(repair_weight.trees.len(), 1);
        assert_eq!(
            repair_weight
                .trees
                .get(&0)
                .unwrap()
                .ancestors((1, Hash::default())),
            vec![(0, Hash::default())]
        );
        for i in &[0, 1] {
            assert_eq!(
                *repair_weight.slot_to_tree.get(i).unwrap(),
                TreeRoot::Root(0)
            );
        }

        // Add slots 6 and 4 with the same set of pubkeys,
        // should discover the rest of the tree and the weights,
        // and should only count the latest votes
        let votes = vec![(4, vote_pubkeys.clone()), (6, vote_pubkeys)];
        repair_weight.add_votes(
            &blockstore,
            votes.into_iter(),
            bank.epoch_stakes_map(),
            bank.epoch_schedule(),
        );
        assert_eq!(repair_weight.trees.len(), 1);
        assert_eq!(
            repair_weight
                .trees
                .get(&0)
                .unwrap()
                .ancestors((4, Hash::default()))
                .into_iter()
                .map(|slot_hash| slot_hash.0)
                .collect::<Vec<_>>(),
            vec![2, 1, 0]
        );
        assert_eq!(
            repair_weight
                .trees
                .get(&0)
                .unwrap()
                .ancestors((6, Hash::default()))
                .into_iter()
                .map(|slot_hash| slot_hash.0)
                .collect::<Vec<_>>(),
            vec![5, 3, 1, 0]
        );
        for slot in 0..=6 {
            assert_eq!(
                *repair_weight.slot_to_tree.get(&slot).unwrap(),
                TreeRoot::Root(0)
            );
            let stake_voted_at = repair_weight
                .trees
                .get(&0)
                .unwrap()
                .stake_voted_at(&(slot, Hash::default()))
                .unwrap();
            if slot == 6 {
                assert_eq!(stake_voted_at, 3 * stake);
            } else {
                assert_eq!(stake_voted_at, 0);
            }
        }

        for slot in &[6, 5, 3, 1, 0] {
            let stake_voted_subtree = repair_weight
                .trees
                .get(&0)
                .unwrap()
                .stake_voted_subtree(&(*slot, Hash::default()))
                .unwrap();
            assert_eq!(stake_voted_subtree, 3 * stake);
        }
        for slot in &[4, 2] {
            let stake_voted_subtree = repair_weight
                .trees
                .get(&0)
                .unwrap()
                .stake_voted_subtree(&(*slot, Hash::default()))
                .unwrap();
            assert_eq!(stake_voted_subtree, 0);
        }
    }

    #[test]
    fn test_add_votes_orphans() {
        let blockstore = setup_orphans();
        let stake = 100;
        let (bank, vote_pubkeys) = bank_utils::setup_bank_and_vote_pubkeys_for_tests(3, stake);
        let votes = vec![(1, vote_pubkeys.clone()), (8, vote_pubkeys.clone())];

        let mut repair_weight = RepairWeight::new(0);
        repair_weight.add_votes(
            &blockstore,
            votes.into_iter(),
            bank.epoch_stakes_map(),
            bank.epoch_schedule(),
        );

        // Should contain two trees, one for main fork, one for the orphan
        // branch
        assert_eq!(repair_weight.trees.len(), 2);
        assert_eq!(
            repair_weight
                .trees
                .get(&0)
                .unwrap()
                .ancestors((1, Hash::default())),
            vec![(0, Hash::default())]
        );
        assert!(repair_weight
            .trees
            .get(&8)
            .unwrap()
            .ancestors((8, Hash::default()))
            .is_empty());

        let votes = vec![(1, vote_pubkeys.clone()), (10, vote_pubkeys.clone())];
        let mut repair_weight = RepairWeight::new(0);
        repair_weight.add_votes(
            &blockstore,
            votes.into_iter(),
            bank.epoch_stakes_map(),
            bank.epoch_schedule(),
        );

        // Should contain two trees, one for main fork, one for the orphan
        // branch
        assert_eq!(repair_weight.trees.len(), 2);
        assert_eq!(
            repair_weight
                .trees
                .get(&0)
                .unwrap()
                .ancestors((1, Hash::default())),
            vec![(0, Hash::default())]
        );
        assert_eq!(
            repair_weight
                .trees
                .get(&8)
                .unwrap()
                .ancestors((10, Hash::default())),
            vec![(8, Hash::default())]
        );

        // Connect orphan back to main fork
        blockstore.add_tree(tr(6) / (tr(8)), true, true, 2, Hash::default());
        assert_eq!(
            AncestorIterator::new(8, &blockstore).collect::<Vec<_>>(),
            vec![6, 5, 3, 1, 0]
        );
        let votes = vec![(11, vote_pubkeys)];

        // Should not resolve orphans because `update_orphan_ancestors` has
        // not been called, but should add to the orphan branch
        repair_weight.add_votes(
            &blockstore,
            votes.into_iter(),
            bank.epoch_stakes_map(),
            bank.epoch_schedule(),
        );
        assert_eq!(
            repair_weight
                .trees
                .get(&8)
                .unwrap()
                .ancestors((11, Hash::default()))
                .into_iter()
                .map(|slot_hash| slot_hash.0)
                .collect::<Vec<_>>(),
            vec![10, 8]
        );

        for slot in &[8, 10, 11] {
            assert_eq!(
                *repair_weight.slot_to_tree.get(slot).unwrap(),
                TreeRoot::Root(8)
            );
        }
        for slot in 0..=1 {
            assert_eq!(
                *repair_weight.slot_to_tree.get(&slot).unwrap(),
                TreeRoot::Root(0)
            );
        }

        // Call `update_orphan_ancestors` to resolve orphan
        repair_weight.update_orphan_ancestors(
            &blockstore,
            8,
            bank.epoch_stakes_map(),
            bank.epoch_schedule(),
        );

        for slot in &[8, 10, 11] {
            assert_eq!(
                *repair_weight.slot_to_tree.get(slot).unwrap(),
                TreeRoot::Root(0)
            );
        }
        assert_eq!(repair_weight.trees.len(), 1);
        assert!(repair_weight.trees.contains_key(&0));
    }

    #[test]
    fn test_add_votes_pruned() {
        // Connect orphan to main fork
        let blockstore = setup_orphans();
        blockstore.add_tree(tr(4) / tr(8), true, true, 2, Hash::default());

        let stake = 100;
        let (bank, vote_pubkeys) = bank_utils::setup_bank_and_vote_pubkeys_for_tests(3, stake);
        let votes = vec![(6, vote_pubkeys.clone()), (11, vote_pubkeys.clone())];

        let mut repair_weight = RepairWeight::new(0);
        repair_weight.add_votes(
            &blockstore,
            votes.into_iter(),
            bank.epoch_stakes_map(),
            bank.epoch_schedule(),
        );

        // Should contain rooted tree
        assert_eq!(repair_weight.trees.len(), 1);
        assert_eq!(repair_weight.pruned_trees.len(), 0);
        assert_eq!(
            *repair_weight.trees.get(&0).unwrap(),
            HeaviestSubtreeForkChoice::new_from_tree(
                tr(0)
                    / (tr(1)
                        / (tr(2) / (tr(4) / (tr(8) / (tr(10) / tr(11)))))
                        / (tr(3) / (tr(5) / tr(6))))
            )
        );

        // Update root to create a pruned tree
        repair_weight.set_root(2);
        assert_eq!(repair_weight.trees.len(), 1);
        assert_eq!(repair_weight.pruned_trees.len(), 1);

        // Add a vote to a slot chaining to pruned
        blockstore.add_tree(tr(6) / tr(20), true, true, 2, Hash::default());
        let votes = vec![(23, vote_pubkeys.iter().take(1).copied().collect_vec())];
        repair_weight.add_votes(
            &blockstore,
            votes.into_iter(),
            bank.epoch_stakes_map(),
            bank.epoch_schedule(),
        );

        // Should be part of the now pruned tree
        assert_eq!(repair_weight.trees.len(), 1);
        assert_eq!(repair_weight.pruned_trees.len(), 1);
        assert_eq!(
            *repair_weight.trees.get(&2).unwrap(),
            HeaviestSubtreeForkChoice::new_from_tree(tr(2) / (tr(4) / (tr(8) / (tr(10) / tr(11)))))
        );
        assert_eq!(
            *repair_weight.pruned_trees.get(&3).unwrap(),
            HeaviestSubtreeForkChoice::new_from_tree(
                tr(3) / (tr(5) / (tr(6) / (tr(20) / (tr(22) / tr(23)))))
            )
        );
        assert_eq!(
            *repair_weight.slot_to_tree.get(&23).unwrap(),
            TreeRoot::PrunedRoot(3)
        );

        // Pruned tree should now have 1 vote
        assert_eq!(
            repair_weight
                .pruned_trees
                .get(&3)
                .unwrap()
                .stake_voted_subtree(&(3, Hash::default()))
                .unwrap(),
            stake
        );
        assert_eq!(
            repair_weight
                .trees
                .get(&2)
                .unwrap()
                .stake_voted_subtree(&(2, Hash::default()))
                .unwrap(),
            3 * stake
        );

        // Add the rest of the stake
        let votes = vec![(23, vote_pubkeys.iter().skip(1).copied().collect_vec())];
        repair_weight.add_votes(
            &blockstore,
            votes.into_iter(),
            bank.epoch_stakes_map(),
            bank.epoch_schedule(),
        );

        // Pruned tree should have all the stake as well
        assert_eq!(
            repair_weight
                .pruned_trees
                .get(&3)
                .unwrap()
                .stake_voted_subtree(&(3, Hash::default()))
                .unwrap(),
            3 * stake
        );
        assert_eq!(
            repair_weight
                .trees
                .get(&2)
                .unwrap()
                .stake_voted_subtree(&(2, Hash::default()))
                .unwrap(),
            3 * stake
        );

        // Update root and trim pruned tree
        repair_weight.set_root(10);
        // Add a vote to an orphan, where earliest ancestor is unrooted, should still add as pruned
        blockstore.add_tree(
            tr(7) / (tr(9) / (tr(12) / tr(13))),
            true,
            true,
            2,
            Hash::default(),
        );
        let votes = vec![(13, vote_pubkeys)];
        repair_weight.add_votes(
            &blockstore,
            votes.into_iter(),
            bank.epoch_stakes_map(),
            bank.epoch_schedule(),
        );

        assert_eq!(repair_weight.trees.len(), 1);
        assert_eq!(repair_weight.pruned_trees.len(), 2);
        assert_eq!(
            *repair_weight.trees.get(&10).unwrap(),
            HeaviestSubtreeForkChoice::new_from_tree(tr(10) / tr(11))
        );
        assert_eq!(
            *repair_weight.pruned_trees.get(&12).unwrap(),
            HeaviestSubtreeForkChoice::new_from_tree(tr(12) / tr(13))
        );
        assert_eq!(
            *repair_weight.pruned_trees.get(&20).unwrap(),
            HeaviestSubtreeForkChoice::new_from_tree(tr(20) / (tr(22) / tr(23)))
        );
        assert_eq!(
            *repair_weight.slot_to_tree.get(&13).unwrap(),
            TreeRoot::PrunedRoot(12)
        );
        assert_eq!(
            *repair_weight.slot_to_tree.get(&23).unwrap(),
            TreeRoot::PrunedRoot(20)
        );
    }

    #[test]
    fn test_update_orphan_ancestors() {
        let blockstore = setup_orphans();
        let stake = 100;
        let (bank, vote_pubkeys) = bank_utils::setup_bank_and_vote_pubkeys_for_tests(3, stake);
        // Create votes for both orphan branches
        let votes = vec![
            (10, vote_pubkeys[0..1].to_vec()),
            (22, vote_pubkeys[1..3].to_vec()),
        ];

        let mut repair_weight = RepairWeight::new(0);
        repair_weight.add_votes(
            &blockstore,
            votes.into_iter(),
            bank.epoch_stakes_map(),
            bank.epoch_schedule(),
        );

        assert_eq!(repair_weight.trees.len(), 3);
        // Roots of the orphan branches should exist
        assert!(repair_weight.trees.contains_key(&0));
        assert!(repair_weight.trees.contains_key(&8));
        assert!(repair_weight.trees.contains_key(&20));

        // Call `update_orphan_ancestors` to resolve orphan
        repair_weight.update_orphan_ancestors(
            &blockstore,
            8,
            bank.epoch_stakes_map(),
            bank.epoch_schedule(),
        );

        // Nothing has changed because no orphans were
        // resolved
        assert_eq!(repair_weight.trees.len(), 3);
        // Roots of the orphan branches should exist
        assert!(repair_weight.trees.contains_key(&0));
        assert!(repair_weight.trees.contains_key(&8));
        assert!(repair_weight.trees.contains_key(&20));

        // Resolve orphans in blockstore
        blockstore.add_tree(tr(6) / (tr(8)), true, true, 2, Hash::default());
        blockstore.add_tree(tr(11) / (tr(20)), true, true, 2, Hash::default());
        // Call `update_orphan_ancestors` to resolve orphan
        repair_weight.update_orphan_ancestors(
            &blockstore,
            20,
            bank.epoch_stakes_map(),
            bank.epoch_schedule(),
        );

        // Only the main fork should exist now
        assert_eq!(repair_weight.trees.len(), 1);
        assert!(repair_weight.trees.contains_key(&0));
    }

    #[test]
    fn test_get_best_orphans() {
        let blockstore = setup_orphans();
        let stake = 100;
        let (bank, vote_pubkeys) = bank_utils::setup_bank_and_vote_pubkeys_for_tests(2, stake);
        let votes = vec![(8, vec![vote_pubkeys[0]]), (20, vec![vote_pubkeys[1]])];
        let mut repair_weight = RepairWeight::new(0);
        repair_weight.add_votes(
            &blockstore,
            votes.into_iter(),
            bank.epoch_stakes_map(),
            bank.epoch_schedule(),
        );

        // Ask for only 1 orphan. Because the orphans have the same weight,
        // should prioritize smaller orphan first
        let mut repairs = vec![];
        let mut outstanding_repairs = HashMap::new();
        let mut processed_slots: HashSet<Slot> = vec![repair_weight.root].into_iter().collect();
        repair_weight.get_best_orphans(
            &blockstore,
            &mut processed_slots,
            &mut repairs,
            bank.epoch_stakes_map(),
            bank.epoch_schedule(),
            1,
            &mut outstanding_repairs,
        );
        assert_eq!(
            repair_weight
                .trees
                .get(&8)
                .unwrap()
                .stake_voted_subtree(&(8, Hash::default()))
                .unwrap(),
            repair_weight
                .trees
                .get(&20)
                .unwrap()
                .stake_voted_subtree(&(20, Hash::default()))
                .unwrap()
        );
        assert_eq!(repairs.len(), 1);
        assert_eq!(outstanding_repairs.len(), repairs.len());
        assert_eq!(repairs[0].slot(), 8);

        // New vote on same orphan branch, without any new slot chaining
        // information blockstore should not resolve the orphan
        repairs = vec![];
        outstanding_repairs = HashMap::new();
        processed_slots = vec![repair_weight.root].into_iter().collect();
        let votes = vec![(10, vec![vote_pubkeys[0]])];
        repair_weight.add_votes(
            &blockstore,
            votes.into_iter(),
            bank.epoch_stakes_map(),
            bank.epoch_schedule(),
        );
        repair_weight.get_best_orphans(
            &blockstore,
            &mut processed_slots,
            &mut repairs,
            bank.epoch_stakes_map(),
            bank.epoch_schedule(),
            1,
            &mut outstanding_repairs,
        );
        assert_eq!(repairs.len(), 1);
        assert_eq!(outstanding_repairs.len(), repairs.len());
        assert_eq!(repairs[0].slot(), 8);

        // Ask for 2 orphans, should return all the orphans
        repairs = vec![];
        outstanding_repairs = HashMap::new();
        processed_slots = vec![repair_weight.root].into_iter().collect();
        repair_weight.get_best_orphans(
            &blockstore,
            &mut processed_slots,
            &mut repairs,
            bank.epoch_stakes_map(),
            bank.epoch_schedule(),
            2,
            &mut outstanding_repairs,
        );
        assert_eq!(repairs.len(), 2);
        assert_eq!(outstanding_repairs.len(), repairs.len());
        assert_eq!(repairs[0].slot(), 8);
        assert_eq!(repairs[1].slot(), 20);

        // Ensure redundant repairs are not generated.
        repair_weight.get_best_orphans(
            &blockstore,
            &mut processed_slots,
            &mut repairs,
            bank.epoch_stakes_map(),
            bank.epoch_schedule(),
            2,
            &mut outstanding_repairs,
        );
        assert_eq!(repairs.len(), 2);
        assert_eq!(outstanding_repairs.len(), repairs.len());

        // If one orphan gets heavier, should pick that one
        repairs = vec![];
        outstanding_repairs = HashMap::new();
        processed_slots = vec![repair_weight.root].into_iter().collect();
        let votes = vec![(20, vec![vote_pubkeys[0]])];
        repair_weight.add_votes(
            &blockstore,
            votes.into_iter(),
            bank.epoch_stakes_map(),
            bank.epoch_schedule(),
        );
        repair_weight.get_best_orphans(
            &blockstore,
            &mut processed_slots,
            &mut repairs,
            bank.epoch_stakes_map(),
            bank.epoch_schedule(),
            1,
            &mut outstanding_repairs,
        );
        assert_eq!(repairs.len(), 1);
        assert_eq!(outstanding_repairs.len(), repairs.len());
        assert_eq!(repairs[0].slot(), 20);

        // Resolve the orphans, should now return no
        // orphans
        repairs = vec![];
        outstanding_repairs = HashMap::new();
        processed_slots = vec![repair_weight.root].into_iter().collect();
        blockstore.add_tree(tr(6) / (tr(8)), true, true, 2, Hash::default());
        blockstore.add_tree(tr(11) / (tr(20)), true, true, 2, Hash::default());
        repair_weight.get_best_orphans(
            &blockstore,
            &mut processed_slots,
            &mut repairs,
            bank.epoch_stakes_map(),
            bank.epoch_schedule(),
            1,
            &mut outstanding_repairs,
        );
        assert!(repairs.is_empty());
        assert!(outstanding_repairs.is_empty());
    }

    #[test]
    fn test_get_extra_orphans() {
        let blockstore = setup_orphans();
        let stake = 100;
        let (bank, vote_pubkeys) = bank_utils::setup_bank_and_vote_pubkeys_for_tests(2, stake);
        let votes = vec![(8, vec![vote_pubkeys[0]])];
        let mut repair_weight = RepairWeight::new(0);
        repair_weight.add_votes(
            &blockstore,
            votes.into_iter(),
            bank.epoch_stakes_map(),
            bank.epoch_schedule(),
        );

        // Should only be one orphan in the `trees` map
        assert_eq!(repair_weight.trees.len(), 2);
        // Roots of the orphan branches should exist
        assert!(repair_weight.trees.contains_key(&0));
        assert!(repair_weight.trees.contains_key(&8));

        // Ask for 2 orphans. Even though there's only one
        // orphan in the `trees` map, we should search for
        // exactly one more of the remaining two
        let mut repairs = vec![];
        let mut outstanding_repairs = HashMap::new();
        let mut processed_slots: HashSet<Slot> = vec![repair_weight.root].into_iter().collect();
        blockstore.add_tree(tr(100) / (tr(101)), true, true, 2, Hash::default());
        repair_weight.get_best_orphans(
            &blockstore,
            &mut processed_slots,
            &mut repairs,
            bank.epoch_stakes_map(),
            bank.epoch_schedule(),
            2,
            &mut outstanding_repairs,
        );
        assert_eq!(repairs.len(), 2);
        assert_eq!(outstanding_repairs.len(), repairs.len());
        assert_eq!(repairs[0].slot(), 8);
        assert_eq!(repairs[1].slot(), 20);

        // If we ask for 3 orphans, we should get all of them
        let mut repairs = vec![];
        outstanding_repairs = HashMap::new();
        processed_slots = vec![repair_weight.root].into_iter().collect();
        repair_weight.get_best_orphans(
            &blockstore,
            &mut processed_slots,
            &mut repairs,
            bank.epoch_stakes_map(),
            bank.epoch_schedule(),
            3,
            &mut outstanding_repairs,
        );
        assert_eq!(repairs.len(), 3);
        assert_eq!(outstanding_repairs.len(), repairs.len());
        assert_eq!(repairs[0].slot(), 8);
        assert_eq!(repairs[1].slot(), 20);
        assert_eq!(repairs[2].slot(), 100);
    }

    #[test]
    fn test_set_root() {
        let (_, _, mut repair_weight) = setup_orphan_repair_weight();

        // Set root at 1
        repair_weight.set_root(1);
        check_old_root_purged_verify_new_root(0, 1, &repair_weight);
        assert!(repair_weight.pruned_trees.is_empty());

        // Other later slots in the fork should be updated to map to the
        // the new root
        assert_eq!(
            *repair_weight.slot_to_tree.get(&1).unwrap(),
            TreeRoot::Root(1)
        );
        assert_eq!(
            *repair_weight.slot_to_tree.get(&2).unwrap(),
            TreeRoot::Root(1)
        );

        // Trees tracked should be updated
        assert_eq!(repair_weight.trees.get(&1).unwrap().tree_root().0, 1);

        // Orphan slots should not be changed
        for orphan in &[8, 20] {
            assert_eq!(
                repair_weight.trees.get(orphan).unwrap().tree_root().0,
                *orphan
            );
            assert_eq!(
                *repair_weight.slot_to_tree.get(orphan).unwrap(),
                TreeRoot::Root(*orphan)
            );
        }
    }

    #[test]
    fn test_set_missing_root() {
        let (_, _, mut repair_weight) = setup_orphan_repair_weight();

        // Make sure the slot to root has not been added to the set
        let missing_root_slot = 5;
        assert!(!repair_weight.slot_to_tree.contains_key(&missing_root_slot));

        // Set root at 5
        repair_weight.set_root(missing_root_slot);
        check_old_root_purged_verify_new_root(0, missing_root_slot, &repair_weight);

        // Should purge [0, 5) from tree
        for slot in 0..5 {
            assert!(!repair_weight.slot_to_tree.contains_key(&slot));
        }

        // Orphan slots should not be changed
        for orphan in &[8, 20] {
            assert_eq!(
                repair_weight.trees.get(orphan).unwrap().tree_root().0,
                *orphan
            );
            assert_eq!(
                *repair_weight.slot_to_tree.get(orphan).unwrap(),
                TreeRoot::Root(*orphan)
            );
        }
    }

    #[test]
    fn test_set_root_existing_non_root_tree() {
        let (_, _, mut repair_weight) = setup_orphan_repair_weight();

        // Set root in an existing orphan branch, slot 10
        repair_weight.set_root(10);
        check_old_root_purged_verify_new_root(0, 10, &repair_weight);

        // Should purge old root tree [0, 6]
        for slot in 0..6 {
            assert!(!repair_weight.slot_to_tree.contains_key(&slot));
        }

        // Should purge orphan parent as well
        assert!(!repair_weight.slot_to_tree.contains_key(&8));

        // Other higher orphan branch rooted at slot `20` remains unchanged
        assert_eq!(repair_weight.trees.get(&20).unwrap().tree_root().0, 20);
        assert_eq!(
            *repair_weight.slot_to_tree.get(&20).unwrap(),
            TreeRoot::Root(20)
        );
    }

    #[test]
    fn test_set_root_check_pruned_slots() {
        let (blockstore, bank, mut repair_weight) = setup_orphan_repair_weight();

        // Chain orphan 8 back to the main fork, but don't
        // touch orphan 20
        blockstore.add_tree(tr(4) / (tr(8)), true, true, 2, Hash::default());

        // Call `update_orphan_ancestors` to resolve orphan
        repair_weight.update_orphan_ancestors(
            &blockstore,
            8,
            bank.epoch_stakes_map(),
            bank.epoch_schedule(),
        );

        // Set a root at 3
        repair_weight.set_root(3);
        check_old_root_purged_verify_new_root(0, 3, &repair_weight);

        // Setting root at 3 should purge all slots < 3 and prune slots > 3 that are not part of
        // the rooted path. Additionally slot 4 should now be a standalone pruned tree
        for purged_slot in 0..3 {
            assert!(!repair_weight.slot_to_tree.contains_key(&purged_slot));
            assert!(!repair_weight.trees.contains_key(&purged_slot));
        }
        for pruned_slot in &[4, 8, 10] {
            assert!(repair_weight
                .slot_to_tree
                .get(pruned_slot)
                .unwrap()
                .is_pruned());
        }
        assert_eq!(
            repair_weight.pruned_trees.keys().copied().collect_vec(),
            vec![4]
        );

        // Orphan 20 should still exist
        assert_eq!(repair_weight.trees.len(), 2);
        assert_eq!(repair_weight.trees.get(&20).unwrap().tree_root().0, 20);
        assert_eq!(
            *repair_weight.slot_to_tree.get(&20).unwrap(),
            TreeRoot::Root(20)
        );

        // Now set root at a slot 30 that doesnt exist in `repair_weight`, but is
        // higher than the remaining orphan
        assert!(!repair_weight.slot_to_tree.contains_key(&30));
        repair_weight.set_root(30);
        check_old_root_purged_verify_new_root(3, 30, &repair_weight);

        // Previously pruned tree should now also be purged
        assert_eq!(repair_weight.pruned_trees.len(), 0);

        // Trees tracked should be updated
        assert_eq!(repair_weight.trees.len(), 1);
        assert_eq!(repair_weight.slot_to_tree.len(), 1);
        assert_eq!(repair_weight.root, 30);
        assert_eq!(repair_weight.trees.get(&30).unwrap().tree_root().0, 30);
    }

    #[test]
    fn test_set_root_pruned_tree_trim_and_cleanup() {
        let blockstore = setup_big_forks();
        let stake = 100;
        let (bank, vote_pubkeys) = bank_utils::setup_bank_and_vote_pubkeys_for_tests(3, stake);
        let votes = vec![
            (4, vote_pubkeys.clone()),
            (6, vote_pubkeys.clone()),
            (11, vote_pubkeys.clone()),
            (23, vote_pubkeys),
        ];

        let mut repair_weight = RepairWeight::new(0);
        repair_weight.add_votes(
            &blockstore,
            votes.into_iter(),
            bank.epoch_stakes_map(),
            bank.epoch_schedule(),
        );

        // Should be 1 rooted tree
        assert_eq!(repair_weight.trees.len(), 1);
        assert_eq!(repair_weight.pruned_trees.len(), 0);

        // Set root to 3, should now prune 4 and 8 trees
        repair_weight.set_root(3);
        assert_eq!(repair_weight.trees.len(), 1);
        assert_eq!(repair_weight.pruned_trees.len(), 2);
        assert_eq!(
            *repair_weight.trees.get(&3).unwrap(),
            HeaviestSubtreeForkChoice::new_from_tree(
                tr(3) / (tr(5) / tr(6)) / (tr(9) / (tr(20) / (tr(22) / tr(23))))
            )
        );
        assert_eq!(
            *repair_weight.pruned_trees.get(&4).unwrap(),
            HeaviestSubtreeForkChoice::new_from_tree(tr(4))
        );
        assert_eq!(
            *repair_weight.pruned_trees.get(&8).unwrap(),
            HeaviestSubtreeForkChoice::new_from_tree(tr(8) / (tr(10) / tr(11)))
        );
        assert_eq!(
            *repair_weight.slot_to_tree.get(&6).unwrap(),
            TreeRoot::Root(3)
        );
        assert_eq!(
            *repair_weight.slot_to_tree.get(&23).unwrap(),
            TreeRoot::Root(3)
        );
        assert_eq!(
            *repair_weight.slot_to_tree.get(&4).unwrap(),
            TreeRoot::PrunedRoot(4)
        );
        assert_eq!(
            *repair_weight.slot_to_tree.get(&11).unwrap(),
            TreeRoot::PrunedRoot(8)
        );

        // Set root to 9,
        //  5, 6 should be purged
        //  Pruned tree 4 should be cleaned up entirely
        //  Pruned tree 8 should be trimmed to 10
        repair_weight.set_root(9);
        assert_eq!(repair_weight.trees.len(), 1);
        assert_eq!(repair_weight.pruned_trees.len(), 1);
        assert_eq!(
            *repair_weight.trees.get(&9).unwrap(),
            HeaviestSubtreeForkChoice::new_from_tree(tr(9) / (tr(20) / (tr(22) / tr(23))))
        );
        assert_eq!(
            *repair_weight.pruned_trees.get(&10).unwrap(),
            HeaviestSubtreeForkChoice::new_from_tree(tr(10) / tr(11))
        );
        assert!(!repair_weight.slot_to_tree.contains(&6));
        assert_eq!(
            *repair_weight.slot_to_tree.get(&23).unwrap(),
            TreeRoot::Root(9)
        );
        assert!(!repair_weight.slot_to_tree.contains(&4));
        assert_eq!(
            *repair_weight.slot_to_tree.get(&11).unwrap(),
            TreeRoot::PrunedRoot(10)
        );

        // Set root to 20,
        //  Pruned tree 10 (formerly 8) should be cleaned up entirely
        repair_weight.set_root(20);
        assert_eq!(repair_weight.trees.len(), 1);
        assert_eq!(repair_weight.pruned_trees.len(), 0);
        assert_eq!(
            *repair_weight.trees.get(&20).unwrap(),
            HeaviestSubtreeForkChoice::new_from_tree(tr(20) / (tr(22) / tr(23)))
        );
        assert!(!repair_weight.slot_to_tree.contains(&6));
        assert_eq!(
            *repair_weight.slot_to_tree.get(&23).unwrap(),
            TreeRoot::Root(20)
        );
        assert!(!repair_weight.slot_to_tree.contains(&4));
        assert!(!repair_weight.slot_to_tree.contains(&11));
    }

    #[test]
    fn test_set_root_pruned_tree_split() {
        let blockstore = setup_big_forks();
        let stake = 100;
        let (bank, vote_pubkeys) = bank_utils::setup_bank_and_vote_pubkeys_for_tests(3, stake);
        let votes = vec![
            (4, vote_pubkeys.clone()),
            (6, vote_pubkeys.clone()),
            (11, vote_pubkeys.clone()),
            (23, vote_pubkeys),
        ];

        let mut repair_weight = RepairWeight::new(0);
        repair_weight.add_votes(
            &blockstore,
            votes.into_iter(),
            bank.epoch_stakes_map(),
            bank.epoch_schedule(),
        );

        // Should be 1 rooted tree
        assert_eq!(repair_weight.trees.len(), 1);
        assert_eq!(repair_weight.pruned_trees.len(), 0);

        // Set root to 2, subtree at 3 should be pruned
        repair_weight.set_root(2);
        assert_eq!(repair_weight.trees.len(), 1);
        assert_eq!(repair_weight.pruned_trees.len(), 1);

        assert_eq!(
            *repair_weight.trees.get(&2).unwrap(),
            HeaviestSubtreeForkChoice::new_from_tree(tr(2) / tr(4) / (tr(8) / (tr(10) / tr(11))))
        );
        assert_eq!(
            *repair_weight.pruned_trees.get(&3).unwrap(),
            HeaviestSubtreeForkChoice::new_from_tree(
                tr(3) / (tr(5) / tr(6)) / (tr(9) / (tr(20) / (tr(22) / tr(23))))
            )
        );
        assert_eq!(
            *repair_weight.slot_to_tree.get(&4).unwrap(),
            TreeRoot::Root(2)
        );
        assert_eq!(
            *repair_weight.slot_to_tree.get(&11).unwrap(),
            TreeRoot::Root(2)
        );
        assert_eq!(
            *repair_weight.slot_to_tree.get(&6).unwrap(),
            TreeRoot::PrunedRoot(3)
        );
        assert_eq!(
            *repair_weight.slot_to_tree.get(&23).unwrap(),
            TreeRoot::PrunedRoot(3)
        );

        // Now update root to 4
        //  Subtree at 8 will now be pruned
        //  Pruned subtree at 3 will now be *split* into 2 pruned subtrees 5, and 9
        repair_weight.set_root(4);
        assert_eq!(repair_weight.trees.len(), 1);
        assert_eq!(repair_weight.pruned_trees.len(), 3);

        assert_eq!(
            *repair_weight.trees.get(&4).unwrap(),
            HeaviestSubtreeForkChoice::new_from_tree(tr(4))
        );
        assert_eq!(
            *repair_weight.pruned_trees.get(&8).unwrap(),
            HeaviestSubtreeForkChoice::new_from_tree(tr(8) / (tr(10) / tr(11)))
        );
        assert_eq!(
            *repair_weight.pruned_trees.get(&5).unwrap(),
            HeaviestSubtreeForkChoice::new_from_tree(tr(5) / tr(6))
        );
        assert_eq!(
            *repair_weight.pruned_trees.get(&9).unwrap(),
            HeaviestSubtreeForkChoice::new_from_tree(tr(9) / (tr(20) / (tr(22) / tr(23))))
        );
        assert_eq!(
            *repair_weight.slot_to_tree.get(&4).unwrap(),
            TreeRoot::Root(4)
        );
        assert_eq!(
            *repair_weight.slot_to_tree.get(&11).unwrap(),
            TreeRoot::PrunedRoot(8)
        );
        assert_eq!(
            *repair_weight.slot_to_tree.get(&6).unwrap(),
            TreeRoot::PrunedRoot(5)
        );
        assert_eq!(
            *repair_weight.slot_to_tree.get(&23).unwrap(),
            TreeRoot::PrunedRoot(9)
        );
    }

    #[test]
    fn test_add_votes_update_orphans_unrooted() {
        let root = 3;
        // Test chaining back to slots that were purged when the root 3 was set.
        // 1) Slot 2 < root should be purged and subsequent children purged
        // 2) Slot 4 > root should be pruned and subsequent children pruned
        for old_parent in &[2, 4] {
            let (blockstore, bank, mut repair_weight) = setup_orphan_repair_weight();
            // Set a root at 3
            repair_weight.set_root(root);

            // Check
            assert_eq!(repair_weight.pruned_trees.len(), 1);
            if *old_parent > root {
                assert!(repair_weight
                    .slot_to_tree
                    .get(old_parent)
                    .unwrap()
                    .is_pruned());
                assert_eq!(
                    repair_weight
                        .pruned_trees
                        .get(old_parent)
                        .unwrap()
                        .tree_root()
                        .0,
                    *old_parent
                );
                assert_eq!(
                    *repair_weight.slot_to_tree.get(old_parent).unwrap(),
                    TreeRoot::PrunedRoot(*old_parent)
                );
            } else {
                assert!(!repair_weight.slot_to_tree.contains_key(old_parent));
                assert!(!repair_weight.trees.contains_key(old_parent));
                assert!(!repair_weight.pruned_trees.contains_key(old_parent));
            }

            // Chain orphan 8 back to slot `old_parent`
            blockstore.add_tree(tr(*old_parent) / (tr(8)), true, true, 2, Hash::default());

            // Chain orphan 20 back to orphan 8
            blockstore.add_tree(tr(8) / (tr(20)), true, true, 2, Hash::default());

            // Call `update_orphan_ancestors` to resolve orphan
            repair_weight.update_orphan_ancestors(
                &blockstore,
                20,
                bank.epoch_stakes_map(),
                bank.epoch_schedule(),
            );

            // Should prune entire branch
            for slot in &[*old_parent, 8, 10, 20] {
                if *slot > root {
                    assert!(repair_weight.slot_to_tree.get(slot).unwrap().is_pruned());
                } else {
                    assert!(!repair_weight.slot_to_tree.contains_key(slot));
                }
            }
            if *old_parent > root {
                assert_eq!(repair_weight.pruned_trees.len(), 1);

                assert_eq!(
                    repair_weight
                        .pruned_trees
                        .get(old_parent)
                        .unwrap()
                        .tree_root()
                        .0,
                    *old_parent
                );
                assert_eq!(
                    *repair_weight.slot_to_tree.get(old_parent).unwrap(),
                    TreeRoot::PrunedRoot(*old_parent)
                );
                assert_eq!(
                    *repair_weight.slot_to_tree.get(&8).unwrap(),
                    TreeRoot::PrunedRoot(*old_parent)
                );
                assert_eq!(
                    *repair_weight.slot_to_tree.get(&10).unwrap(),
                    TreeRoot::PrunedRoot(*old_parent)
                );
                assert_eq!(
                    *repair_weight.slot_to_tree.get(&20).unwrap(),
                    TreeRoot::PrunedRoot(*old_parent)
                );
            } else {
                // Should create a new pruned tree alongside 4
                assert_eq!(repair_weight.pruned_trees.len(), 2);

                assert_eq!(repair_weight.pruned_trees.get(&4).unwrap().tree_root().0, 4);
                assert_eq!(
                    *repair_weight.slot_to_tree.get(&4).unwrap(),
                    TreeRoot::PrunedRoot(4)
                );

                assert_eq!(repair_weight.pruned_trees.get(&8).unwrap().tree_root().0, 8);
                assert_eq!(
                    *repair_weight.slot_to_tree.get(&8).unwrap(),
                    TreeRoot::PrunedRoot(8)
                );
                assert_eq!(
                    *repair_weight.slot_to_tree.get(&10).unwrap(),
                    TreeRoot::PrunedRoot(8)
                );
                assert_eq!(
                    *repair_weight.slot_to_tree.get(&20).unwrap(),
                    TreeRoot::PrunedRoot(8)
                );

                // Old parent remains missing
                assert!(!repair_weight.slot_to_tree.contains_key(old_parent));
                assert!(!repair_weight.trees.contains_key(old_parent));
                assert!(!repair_weight.pruned_trees.contains_key(old_parent));
            }

            // Add a vote that chains back to `old_parent`, should be added to appropriate pruned
            // tree
            let new_vote_slot = 100;
            blockstore.add_tree(
                tr(*old_parent) / tr(new_vote_slot),
                true,
                true,
                2,
                Hash::default(),
            );
            repair_weight.add_votes(
                &blockstore,
                vec![(new_vote_slot, vec![Pubkey::default()])].into_iter(),
                bank.epoch_stakes_map(),
                bank.epoch_schedule(),
            );

            assert!(repair_weight
                .slot_to_tree
                .get(&new_vote_slot)
                .unwrap()
                .is_pruned());
            if *old_parent > root {
                // Adds to new tree
                assert_eq!(repair_weight.pruned_trees.len(), 1);
                assert_eq!(
                    repair_weight
                        .pruned_trees
                        .get(old_parent)
                        .unwrap()
                        .tree_root()
                        .0,
                    *old_parent
                );
                assert_eq!(
                    *repair_weight.slot_to_tree.get(&new_vote_slot).unwrap(),
                    TreeRoot::PrunedRoot(*old_parent)
                );
            } else {
                // Creates new tree
                assert_eq!(repair_weight.pruned_trees.len(), 3);

                assert_eq!(repair_weight.pruned_trees.get(&4).unwrap().tree_root().0, 4);
                assert_eq!(
                    *repair_weight.slot_to_tree.get(&4).unwrap(),
                    TreeRoot::PrunedRoot(4)
                );

                assert_eq!(repair_weight.pruned_trees.get(&8).unwrap().tree_root().0, 8);
                assert_eq!(
                    *repair_weight.slot_to_tree.get(&8).unwrap(),
                    TreeRoot::PrunedRoot(8)
                );
                assert_eq!(
                    *repair_weight.slot_to_tree.get(&10).unwrap(),
                    TreeRoot::PrunedRoot(8)
                );
                assert_eq!(
                    *repair_weight.slot_to_tree.get(&20).unwrap(),
                    TreeRoot::PrunedRoot(8)
                );

                assert_eq!(
                    repair_weight
                        .pruned_trees
                        .get(&new_vote_slot)
                        .unwrap()
                        .tree_root()
                        .0,
                    new_vote_slot
                );
                assert_eq!(
                    *repair_weight.slot_to_tree.get(&new_vote_slot).unwrap(),
                    TreeRoot::PrunedRoot(new_vote_slot)
                );

                // Old parent remains missing
                assert!(!repair_weight.slot_to_tree.contains_key(old_parent));
                assert!(!repair_weight.trees.contains_key(old_parent));
                assert!(!repair_weight.pruned_trees.contains_key(old_parent));
            }
        }
    }

    #[test]
    fn test_find_ancestor_subtree_of_slot() {
        let (blockstore, _, mut repair_weight) = setup_orphan_repair_weight();

        // Ancestor of slot 4 is slot 2, with an existing subtree rooted at 0
        // because there was a vote for a descendant
        assert_eq!(
            repair_weight.find_ancestor_subtree_of_slot(&blockstore, 4),
            (VecDeque::from([2]), Some(TreeRoot::Root(0)))
        );

        // Ancestors of 5 are [1, 3], with an existing subtree rooted at 0
        // because there was a vote for a descendant
        assert_eq!(
            repair_weight.find_ancestor_subtree_of_slot(&blockstore, 5),
            (VecDeque::from([1, 3]), Some(TreeRoot::Root(0)))
        );

        // Ancestors of slot 23 are [20, 22], with an existing subtree of 20
        // because there was a vote for 20
        assert_eq!(
            repair_weight.find_ancestor_subtree_of_slot(&blockstore, 23),
            (VecDeque::from([20, 22]), Some(TreeRoot::Root(20)))
        );

        // Add 22 to `pruned_trees`, ancestor search should now
        // chain it back to 20
        repair_weight.remove_tree(TreeRoot::Root(20)).unwrap();
        repair_weight.insert_new_pruned_tree(20);
        assert_eq!(
            repair_weight.find_ancestor_subtree_of_slot(&blockstore, 23),
            (VecDeque::from([20, 22]), Some(TreeRoot::PrunedRoot(20)))
        );

        // Ancestors of slot 31 are [30], with no existing subtree
        blockstore.add_tree(tr(30) / (tr(31)), true, true, 2, Hash::default());
        assert_eq!(
            repair_weight.find_ancestor_subtree_of_slot(&blockstore, 31),
            (VecDeque::from([30]), None),
        );

        // Set a root at 5
        repair_weight.set_root(5);

        // Ancestor of slot 6 shouldn't include anything from before the root
        // at slot 5
        assert_eq!(
            repair_weight.find_ancestor_subtree_of_slot(&blockstore, 6),
            (VecDeque::from([5]), Some(TreeRoot::Root(5)))
        );

        // Chain orphan 8 back to slot 4 on a different fork
        // Will still include a slot (4) < root, but only one such slot
        blockstore.add_tree(tr(4) / (tr(8)), true, true, 2, Hash::default());
        assert_eq!(
            repair_weight.find_ancestor_subtree_of_slot(&blockstore, 8),
            (VecDeque::from([4]), None),
        );

        // Don't skip, even though 8's chain info is present in blockstore since the merge hasn't
        // happened stop there
        blockstore.add_tree(tr(8) / (tr(40) / tr(41)), true, true, 2, Hash::default());
        assert_eq!(
            repair_weight.find_ancestor_subtree_of_slot(&blockstore, 41),
            (VecDeque::from([8, 40]), Some(TreeRoot::Root(8))),
        )
    }

    #[test]
    fn test_split_off_copy_weight() {
        let (blockstore, _, mut repair_weight) = setup_orphan_repair_weight();
        let stake = 100;
        let (bank, vote_pubkeys) = bank_utils::setup_bank_and_vote_pubkeys_for_tests(1, stake);
        repair_weight.add_votes(
            &blockstore,
            vec![(6, vote_pubkeys)].into_iter(),
            bank.epoch_stakes_map(),
            bank.epoch_schedule(),
        );

        // Simulate dump from replay
        blockstore.clear_unconfirmed_slot(3);
        repair_weight.split_off(3);
        blockstore.clear_unconfirmed_slot(10);
        repair_weight.split_off(10);

        // Verify orphans
        let mut orphans = repair_weight.trees.keys().copied().collect_vec();
        orphans.sort();
        assert_eq!(vec![0, 3, 8, 10, 20], orphans);

        // Verify weighting
        assert_eq!(
            0,
            repair_weight
                .trees
                .get(&8)
                .unwrap()
                .stake_voted_subtree(&(8, Hash::default()))
                .unwrap()
        );
        assert_eq!(
            stake,
            repair_weight
                .trees
                .get(&3)
                .unwrap()
                .stake_voted_subtree(&(3, Hash::default()))
                .unwrap()
        );
        assert_eq!(
            2 * stake,
            repair_weight
                .trees
                .get(&10)
                .unwrap()
                .stake_voted_subtree(&(10, Hash::default()))
                .unwrap()
        );

        // Get best orphans works as usual
        let mut repairs = vec![];
        let mut outstanding_repairs = HashMap::new();
        let mut processed_slots = vec![repair_weight.root].into_iter().collect();
        repair_weight.get_best_orphans(
            &blockstore,
            &mut processed_slots,
            &mut repairs,
            bank.epoch_stakes_map(),
            bank.epoch_schedule(),
            4,
            &mut outstanding_repairs,
        );
        assert_eq!(repairs.len(), 4);
        assert_eq!(repairs[0].slot(), 10);
        assert_eq!(repairs[1].slot(), 20);
        assert_eq!(repairs[2].slot(), 3);
        assert_eq!(repairs[3].slot(), 8);
        assert_eq!(outstanding_repairs.len(), repairs.len());
    }

    #[test]
    fn test_split_off_multi_dump_repair() {
        let blockstore = setup_forks();
        let stake = 100;
        let (bank, vote_pubkeys) = bank_utils::setup_bank_and_vote_pubkeys_for_tests(1, stake);
        let mut repair_weight = RepairWeight::new(0);
        repair_weight.add_votes(
            &blockstore,
            vec![(6, vote_pubkeys)].into_iter(),
            bank.epoch_stakes_map(),
            bank.epoch_schedule(),
        );

        // Simulate multiple dumps (whole branch is duplicate) from replay
        blockstore.clear_unconfirmed_slot(3);
        repair_weight.split_off(3);
        blockstore.clear_unconfirmed_slot(5);
        repair_weight.split_off(5);
        blockstore.clear_unconfirmed_slot(6);
        repair_weight.split_off(6);

        // Verify orphans
        let mut orphans = repair_weight.trees.keys().copied().collect_vec();
        orphans.sort();
        assert_eq!(vec![0, 3, 5, 6], orphans);

        // Get best orphans works as usual
        let mut repairs = vec![];
        let mut outstanding_repairs = HashMap::new();
        let mut processed_slots = vec![repair_weight.root].into_iter().collect();
        repair_weight.get_best_orphans(
            &blockstore,
            &mut processed_slots,
            &mut repairs,
            bank.epoch_stakes_map(),
            bank.epoch_schedule(),
            4,
            &mut outstanding_repairs,
        );
        assert_eq!(repairs.len(), 3);
        assert_eq!(repairs[0].slot(), 6);
        assert_eq!(repairs[1].slot(), 3);
        assert_eq!(repairs[2].slot(), 5);
        assert_eq!(outstanding_repairs.len(), repairs.len());

        // Simulate repair on 6 and 5
        for (shreds, _) in make_chaining_slot_entries(&[5, 6], 100, 0) {
            blockstore.insert_shreds(shreds, None, true).unwrap();
        }

        // Verify orphans properly updated and chained
        let mut repairs = vec![];
        outstanding_repairs = HashMap::new();
        let mut processed_slots = vec![repair_weight.root].into_iter().collect();
        repair_weight.get_best_orphans(
            &blockstore,
            &mut processed_slots,
            &mut repairs,
            bank.epoch_stakes_map(),
            bank.epoch_schedule(),
            4,
            &mut outstanding_repairs,
        );
        assert_eq!(repairs.len(), 1);
        assert_eq!(repairs[0].slot(), 3);
        assert_eq!(outstanding_repairs.len(), repairs.len());

        let mut orphans = repair_weight.trees.keys().copied().collect_vec();
        orphans.sort();
        assert_eq!(orphans, vec![0, 3]);
    }

    #[test]
    fn test_get_popular_pruned_forks() {
        let blockstore = setup_big_forks();
        let stake = 100;
        let (bank, vote_pubkeys) = bank_utils::setup_bank_and_vote_pubkeys_for_tests(10, stake);
        let epoch_stakes = bank.epoch_stakes_map();
        let epoch_schedule = bank.epoch_schedule();

        // Add a little stake for each fork
        let votes = vec![
            (4, vec![vote_pubkeys[0]]),
            (11, vec![vote_pubkeys[1]]),
            (6, vec![vote_pubkeys[2]]),
            (23, vec![vote_pubkeys[3]]),
        ];
        let mut repair_weight = RepairWeight::new(0);
        repair_weight.add_votes(
            &blockstore,
            votes.into_iter(),
            bank.epoch_stakes_map(),
            bank.epoch_schedule(),
        );

        // Set root to 4, there should now be 3 pruned trees with `stake`
        repair_weight.set_root(4);
        assert_eq!(repair_weight.trees.len(), 1);
        assert_eq!(repair_weight.pruned_trees.len(), 3);
        assert!(repair_weight
            .pruned_trees
            .iter()
            .all(
                |(root, pruned_tree)| pruned_tree.stake_voted_subtree(&(*root, Hash::default()))
                    == Some(stake)
            ));

        // No fork has DUPLICATE_THRESHOLD, should not be any popular forks
        assert!(repair_weight
            .get_popular_pruned_forks(epoch_stakes, epoch_schedule)
            .is_empty());

        // 500 stake, still less than DUPLICATE_THRESHOLD, should not be any popular forks
        let five_votes = vote_pubkeys.iter().copied().take(5).collect_vec();
        let votes = vec![(11, five_votes.clone()), (6, five_votes)];
        repair_weight.add_votes(
            &blockstore,
            votes.into_iter(),
            bank.epoch_stakes_map(),
            bank.epoch_schedule(),
        );
        assert!(repair_weight
            .get_popular_pruned_forks(epoch_stakes, epoch_schedule)
            .is_empty());

        // 600 stake, since we voted for leaf, leaf should be returned
        let votes = vec![(11, vec![vote_pubkeys[5]]), (6, vec![vote_pubkeys[6]])];
        repair_weight.add_votes(
            &blockstore,
            votes.into_iter(),
            bank.epoch_stakes_map(),
            bank.epoch_schedule(),
        );
        assert_eq!(
            vec![6, 11],
            repair_weight
                .get_popular_pruned_forks(epoch_stakes, epoch_schedule)
                .into_iter()
                .sorted()
                .collect_vec()
        );

        // For the last pruned tree we leave 100 stake on 23 and 22 and put 600 stake on 20. We
        // should return 20 and not traverse the tree deeper
        let six_votes = vote_pubkeys.iter().copied().take(6).collect_vec();
        let votes = vec![(20, six_votes)];
        repair_weight.add_votes(
            &blockstore,
            votes.into_iter(),
            bank.epoch_stakes_map(),
            bank.epoch_schedule(),
        );
        assert_eq!(
            vec![6, 11, 20],
            repair_weight
                .get_popular_pruned_forks(epoch_stakes, epoch_schedule)
                .into_iter()
                .sorted()
                .collect_vec()
        );
    }

    #[test]
    fn test_get_popular_pruned_forks_forks() {
        let blockstore = setup_big_forks();
        let stake = 100;
        let (bank, vote_pubkeys) = bank_utils::setup_bank_and_vote_pubkeys_for_tests(10, stake);
        let epoch_stakes = bank.epoch_stakes_map();
        let epoch_schedule = bank.epoch_schedule();

        // Add a little stake for each fork
        let votes = vec![
            (4, vec![vote_pubkeys[0]]),
            (11, vec![vote_pubkeys[1]]),
            (6, vec![vote_pubkeys[2]]),
            (23, vec![vote_pubkeys[3]]),
        ];
        let mut repair_weight = RepairWeight::new(0);
        repair_weight.add_votes(
            &blockstore,
            votes.into_iter(),
            bank.epoch_stakes_map(),
            bank.epoch_schedule(),
        );

        // Prune the entire tree
        std::mem::swap(&mut repair_weight.trees, &mut repair_weight.pruned_trees);
        repair_weight
            .slot_to_tree
            .iter_mut()
            .for_each(|(_, s)| *s = TreeRoot::PrunedRoot(s.slot()));

        // Traverse to 20
        let mut repair_weight_20 = repair_weight.clone();
        repair_weight_20.add_votes(
            &blockstore,
            vec![(20, vote_pubkeys.clone())].into_iter(),
            bank.epoch_stakes_map(),
            bank.epoch_schedule(),
        );
        assert_eq!(
            vec![20],
            repair_weight_20.get_popular_pruned_forks(epoch_stakes, epoch_schedule)
        );

        // 4 and 8 individually do not have enough stake, but 2 is popular
        let votes = vec![(10, vote_pubkeys.iter().copied().skip(6).collect_vec())];
        repair_weight.add_votes(
            &blockstore,
            votes.into_iter(),
            bank.epoch_stakes_map(),
            bank.epoch_schedule(),
        );
        assert_eq!(
            vec![2],
            repair_weight.get_popular_pruned_forks(epoch_stakes, epoch_schedule)
        );
    }

    #[test]
    fn test_get_popular_pruned_forks_stake_change_across_epoch_boundary() {
        let blockstore = setup_big_forks();
        let stake = 100;
        let (bank, vote_pubkeys) = bank_utils::setup_bank_and_vote_pubkeys_for_tests(10, stake);
        let mut epoch_stakes = bank.epoch_stakes_map().clone();
        let mut epoch_schedule = bank.epoch_schedule().clone();

        // Simulate epoch boundary at slot 10, where half of the stake deactivates
        // Additional epoch boundary at slot 20, where 30% of the stake reactivates
        let initial_stakes = epoch_stakes
            .get(&epoch_schedule.get_epoch(0))
            .unwrap()
            .clone();
        let mut dec_stakes = epoch_stakes
            .get(&epoch_schedule.get_epoch(0))
            .unwrap()
            .clone();
        let mut inc_stakes = epoch_stakes
            .get(&epoch_schedule.get_epoch(0))
            .unwrap()
            .clone();
        epoch_schedule.first_normal_slot = 0;
        epoch_schedule.slots_per_epoch = 10;
        assert_eq!(
            epoch_schedule.get_epoch(10),
            epoch_schedule.get_epoch(9) + 1
        );
        assert_eq!(
            epoch_schedule.get_epoch(20),
            epoch_schedule.get_epoch(19) + 1
        );
        dec_stakes.set_total_stake(dec_stakes.total_stake() - 5 * stake);
        inc_stakes.set_total_stake(dec_stakes.total_stake() + 3 * stake);
        epoch_stakes.insert(epoch_schedule.get_epoch(0), initial_stakes);
        epoch_stakes.insert(epoch_schedule.get_epoch(10), dec_stakes);
        epoch_stakes.insert(epoch_schedule.get_epoch(20), inc_stakes);

        // Add a little stake for each fork
        let votes = vec![
            (4, vec![vote_pubkeys[0]]),
            (11, vec![vote_pubkeys[1]]),
            (6, vec![vote_pubkeys[2]]),
            (23, vec![vote_pubkeys[3]]),
        ];
        let mut repair_weight = RepairWeight::new(0);
        repair_weight.add_votes(
            &blockstore,
            votes.into_iter(),
            bank.epoch_stakes_map(),
            bank.epoch_schedule(),
        );

        // Set root to 4, there should now be 3 pruned trees with `stake`
        repair_weight.set_root(4);
        assert_eq!(repair_weight.trees.len(), 1);
        assert_eq!(repair_weight.pruned_trees.len(), 3);
        assert!(repair_weight
            .pruned_trees
            .iter()
            .all(
                |(root, pruned_tree)| pruned_tree.stake_voted_subtree(&(*root, Hash::default()))
                    == Some(stake)
            ));

        // No fork hash `DUPLICATE_THRESHOLD`, should not be any popular forks
        assert!(repair_weight
            .get_popular_pruned_forks(&epoch_stakes, &epoch_schedule)
            .is_empty());

        // 400 stake, For the 6 tree it will be less than `DUPLICATE_THRESHOLD`, however 11
        // has epoch modifications where at some point 400 stake is enough. For 22, although it
        // does cross the second epoch where the stake requirement was less, because it doesn't
        // have any blocks in that epoch the minimum total stake is still 800 which fails.
        let four_votes = vote_pubkeys.iter().copied().take(4).collect_vec();
        let votes = vec![
            (11, four_votes.clone()),
            (6, four_votes.clone()),
            (22, four_votes),
        ];
        repair_weight.add_votes(
            &blockstore,
            votes.into_iter(),
            bank.epoch_stakes_map(),
            bank.epoch_schedule(),
        );
        assert_eq!(
            vec![11],
            repair_weight.get_popular_pruned_forks(&epoch_stakes, &epoch_schedule)
        );
    }

    fn setup_orphan_repair_weight() -> (Blockstore, Bank, RepairWeight) {
        let blockstore = setup_orphans();
        let stake = 100;
        let (bank, vote_pubkeys) = bank_utils::setup_bank_and_vote_pubkeys_for_tests(2, stake);

        // Add votes for the main fork and orphan forks
        let votes = vec![
            (4, vote_pubkeys.clone()),
            (8, vote_pubkeys.clone()),
            (10, vote_pubkeys.clone()),
            (20, vote_pubkeys),
        ];

        let mut repair_weight = RepairWeight::new(0);
        repair_weight.add_votes(
            &blockstore,
            votes.into_iter(),
            bank.epoch_stakes_map(),
            bank.epoch_schedule(),
        );

        assert!(repair_weight.slot_to_tree.contains_key(&0));

        // Check orphans are present
        for orphan in &[8, 20] {
            assert_eq!(
                repair_weight.trees.get(orphan).unwrap().tree_root().0,
                *orphan
            );
            assert_eq!(
                *repair_weight.slot_to_tree.get(orphan).unwrap(),
                TreeRoot::Root(*orphan)
            );
        }
        (blockstore, bank, repair_weight)
    }

    fn check_old_root_purged_verify_new_root(
        old_root: Slot,
        new_root: Slot,
        repair_weight: &RepairWeight,
    ) {
        // Check old root is purged
        assert!(!repair_weight.trees.contains_key(&old_root));
        assert!(!repair_weight.slot_to_tree.contains_key(&old_root));

        // Validate new root
        assert_eq!(
            repair_weight.trees.get(&new_root).unwrap().tree_root().0,
            new_root
        );
        assert_eq!(
            *repair_weight.slot_to_tree.get(&new_root).unwrap(),
            TreeRoot::Root(new_root)
        );
        assert_eq!(repair_weight.root, new_root);
    }

    fn setup_orphans() -> Blockstore {
        /*
            Build fork structure:
                 slot 0
                   |
                 slot 1
                 /    \
            slot 2    |
               |    slot 3
            slot 4    |
                    slot 5
                      |
                    slot 6

            Orphans:
               slot 8
                  |
               slot 10
                  |
               slot 11

            Orphans:
              slot 20
                 |
              slot 22
                 |
              slot 23
        */

        let blockstore = setup_forks();
        blockstore.add_tree(tr(8) / (tr(10) / (tr(11))), true, true, 2, Hash::default());
        blockstore.add_tree(tr(20) / (tr(22) / (tr(23))), true, true, 2, Hash::default());
        assert!(blockstore.orphan(8).unwrap().is_some());
        blockstore
    }

    fn setup_big_forks() -> Blockstore {
        /*
            Build fork structure:
                      slot 0
                        |
                      slot 1
                      /    \
                /----|      |----|
            slot 2               |
            /    \            slot 3
        slot 4  slot 8      /        \
                   |     slot 5    slot 9
                slot 10     |         |
                   |     slot 6    slot 20
                slot 11               |
                                   slot 22
                                      |
                                   slot 23
        */
        let blockstore = setup_orphans();
        // Connect orphans to main fork
        blockstore.add_tree(tr(2) / tr(8), true, true, 2, Hash::default());
        blockstore.add_tree(tr(3) / (tr(9) / tr(20)), true, true, 2, Hash::default());
        blockstore
    }

    fn setup_forks() -> Blockstore {
        /*
            Build fork structure:
                 slot 0
                   |
                 slot 1
                 /    \
            slot 2    |
               |    slot 3
            slot 4    |
                    slot 5
                      |
                    slot 6
        */
        let forks = tr(0) / (tr(1) / (tr(2) / (tr(4))) / (tr(3) / (tr(5) / (tr(6)))));
        let ledger_path = get_tmp_ledger_path!();
        let blockstore = Blockstore::open(&ledger_path).unwrap();
        blockstore.add_tree(forks, false, true, 2, Hash::default());
        blockstore
    }
}
