2019-02-16 19:58:07 -08:00
|
|
|
//! The `bank_forks` module implments BankForks a DAG of checkpointed Banks
|
|
|
|
|
2019-05-30 21:31:35 -07:00
|
|
|
use bincode::{deserialize_from, serialize_into};
|
2019-05-10 08:33:58 -07:00
|
|
|
use solana_metrics::inc_new_counter_info;
|
2019-05-30 21:31:35 -07:00
|
|
|
use solana_runtime::bank::{Bank, BankRc, StatusCacheRc};
|
2019-06-02 14:05:32 -07:00
|
|
|
use solana_sdk::genesis_block::GenesisBlock;
|
2019-03-29 21:21:59 -07:00
|
|
|
use solana_sdk::timing;
|
2019-05-30 21:31:35 -07:00
|
|
|
use std::collections::{HashMap, HashSet};
|
|
|
|
use std::fs;
|
|
|
|
use std::fs::File;
|
|
|
|
use std::io::{BufReader, BufWriter, Error, ErrorKind};
|
2019-02-28 10:57:58 -08:00
|
|
|
use std::ops::Index;
|
2019-05-30 21:31:35 -07:00
|
|
|
use std::path::{Path, PathBuf};
|
2019-02-16 19:58:07 -08:00
|
|
|
use std::sync::Arc;
|
2019-03-29 21:21:59 -07:00
|
|
|
use std::time::Instant;
|
2019-02-16 19:58:07 -08:00
|
|
|
|
|
|
|
pub struct BankForks {
|
|
|
|
banks: HashMap<u64, Arc<Bank>>,
|
2019-02-28 10:57:58 -08:00
|
|
|
working_bank: Arc<Bank>,
|
2019-05-07 23:34:10 -07:00
|
|
|
root: u64,
|
2019-05-30 21:31:35 -07:00
|
|
|
slots: HashSet<u64>,
|
|
|
|
snapshot_path: Option<String>,
|
2019-02-28 10:57:58 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Index<u64> for BankForks {
|
|
|
|
type Output = Arc<Bank>;
|
2019-03-04 16:40:28 -08:00
|
|
|
fn index(&self, bank_slot: u64) -> &Arc<Bank> {
|
|
|
|
&self.banks[&bank_slot]
|
2019-02-28 10:57:58 -08:00
|
|
|
}
|
2019-02-16 19:58:07 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
impl BankForks {
|
2019-03-04 16:40:28 -08:00
|
|
|
pub fn new(bank_slot: u64, bank: Bank) -> Self {
|
2019-02-16 19:58:07 -08:00
|
|
|
let mut banks = HashMap::new();
|
2019-02-28 10:57:58 -08:00
|
|
|
let working_bank = Arc::new(bank);
|
2019-03-04 16:40:28 -08:00
|
|
|
banks.insert(bank_slot, working_bank.clone());
|
2019-02-16 19:58:07 -08:00
|
|
|
Self {
|
|
|
|
banks,
|
2019-02-28 10:57:58 -08:00
|
|
|
working_bank,
|
2019-05-07 23:34:10 -07:00
|
|
|
root: 0,
|
2019-05-30 21:31:35 -07:00
|
|
|
slots: HashSet::new(),
|
|
|
|
snapshot_path: None,
|
2019-02-16 19:58:07 -08:00
|
|
|
}
|
|
|
|
}
|
2019-03-18 12:12:33 -07:00
|
|
|
|
|
|
|
/// Create a map of bank slot id to the set of ancestors for the bank slot.
|
|
|
|
pub fn ancestors(&self) -> HashMap<u64, HashSet<u64>> {
|
|
|
|
let mut ancestors = HashMap::new();
|
2019-03-18 16:04:36 -07:00
|
|
|
for bank in self.banks.values() {
|
2019-04-28 10:27:09 -07:00
|
|
|
let mut set: HashSet<u64> = bank.ancestors.keys().cloned().collect();
|
|
|
|
set.remove(&bank.slot());
|
2019-03-18 12:12:33 -07:00
|
|
|
ancestors.insert(bank.slot(), set);
|
|
|
|
}
|
|
|
|
ancestors
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Create a map of bank slot id to the set of all of its descendants
|
2019-05-30 21:31:35 -07:00
|
|
|
#[allow(clippy::or_fun_call)]
|
2019-03-18 15:20:04 -07:00
|
|
|
pub fn descendants(&self) -> HashMap<u64, HashSet<u64>> {
|
|
|
|
let mut descendants = HashMap::new();
|
2019-03-18 16:04:36 -07:00
|
|
|
for bank in self.banks.values() {
|
2019-03-18 15:20:04 -07:00
|
|
|
let _ = descendants.entry(bank.slot()).or_insert(HashSet::new());
|
2019-04-28 10:27:09 -07:00
|
|
|
let mut set: HashSet<u64> = bank.ancestors.keys().cloned().collect();
|
|
|
|
set.remove(&bank.slot());
|
|
|
|
for parent in set {
|
2019-03-18 15:20:04 -07:00
|
|
|
descendants
|
2019-04-28 10:27:09 -07:00
|
|
|
.entry(parent)
|
2019-03-18 12:12:33 -07:00
|
|
|
.or_insert(HashSet::new())
|
|
|
|
.insert(bank.slot());
|
|
|
|
}
|
|
|
|
}
|
2019-03-18 15:20:04 -07:00
|
|
|
descendants
|
2019-03-18 12:12:33 -07:00
|
|
|
}
|
|
|
|
|
2019-02-26 21:57:45 -08:00
|
|
|
pub fn frozen_banks(&self) -> HashMap<u64, Arc<Bank>> {
|
2019-03-18 16:04:36 -07:00
|
|
|
self.banks
|
|
|
|
.iter()
|
|
|
|
.filter(|(_, b)| b.is_frozen())
|
|
|
|
.map(|(k, b)| (*k, b.clone()))
|
|
|
|
.collect()
|
2019-02-26 21:57:45 -08:00
|
|
|
}
|
2019-03-18 16:04:36 -07:00
|
|
|
|
2019-02-26 21:57:45 -08:00
|
|
|
pub fn active_banks(&self) -> Vec<u64> {
|
2019-03-03 16:44:06 -08:00
|
|
|
self.banks
|
|
|
|
.iter()
|
|
|
|
.filter(|(_, v)| !v.is_frozen())
|
|
|
|
.map(|(k, _v)| *k)
|
|
|
|
.collect()
|
2019-02-26 21:57:45 -08:00
|
|
|
}
|
2019-03-19 17:30:36 -07:00
|
|
|
|
2019-03-04 16:40:28 -08:00
|
|
|
pub fn get(&self, bank_slot: u64) -> Option<&Arc<Bank>> {
|
|
|
|
self.banks.get(&bank_slot)
|
2019-02-26 21:57:45 -08:00
|
|
|
}
|
2019-02-16 19:58:07 -08:00
|
|
|
|
2019-05-07 23:34:10 -07:00
|
|
|
pub fn new_from_banks(initial_banks: &[Arc<Bank>], root: u64) -> Self {
|
2019-02-28 12:09:19 -08:00
|
|
|
let mut banks = HashMap::new();
|
|
|
|
let working_bank = initial_banks[0].clone();
|
|
|
|
for bank in initial_banks {
|
|
|
|
banks.insert(bank.slot(), bank.clone());
|
|
|
|
}
|
|
|
|
Self {
|
2019-05-07 23:34:10 -07:00
|
|
|
root,
|
2019-02-28 12:09:19 -08:00
|
|
|
banks,
|
|
|
|
working_bank,
|
2019-05-30 21:31:35 -07:00
|
|
|
slots: HashSet::new(),
|
|
|
|
snapshot_path: None,
|
2019-02-28 12:09:19 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-18 20:23:34 -07:00
|
|
|
pub fn insert(&mut self, bank: Bank) {
|
2019-03-18 16:04:36 -07:00
|
|
|
let bank = Arc::new(bank);
|
2019-03-18 20:23:34 -07:00
|
|
|
let prev = self.banks.insert(bank.slot(), bank.clone());
|
2019-03-03 16:44:06 -08:00
|
|
|
assert!(prev.is_none());
|
2019-02-26 09:18:24 -08:00
|
|
|
|
2019-03-11 13:58:23 -07:00
|
|
|
self.working_bank = bank.clone();
|
2019-02-16 19:58:07 -08:00
|
|
|
}
|
|
|
|
|
2019-02-28 10:57:58 -08:00
|
|
|
// TODO: really want to kill this...
|
|
|
|
pub fn working_bank(&self) -> Arc<Bank> {
|
|
|
|
self.working_bank.clone()
|
2019-02-16 19:58:07 -08:00
|
|
|
}
|
2019-03-18 16:04:36 -07:00
|
|
|
|
|
|
|
pub fn set_root(&mut self, root: u64) {
|
2019-05-07 23:34:10 -07:00
|
|
|
self.root = root;
|
2019-03-29 21:21:59 -07:00
|
|
|
let set_root_start = Instant::now();
|
2019-03-18 16:04:36 -07:00
|
|
|
let root_bank = self
|
|
|
|
.banks
|
|
|
|
.get(&root)
|
|
|
|
.expect("root bank didn't exist in bank_forks");
|
2019-05-13 13:23:52 -07:00
|
|
|
let root_tx_count = root_bank
|
|
|
|
.parents()
|
|
|
|
.last()
|
|
|
|
.map(|bank| bank.transaction_count())
|
|
|
|
.unwrap_or(0);
|
2019-03-18 16:04:36 -07:00
|
|
|
root_bank.squash();
|
2019-05-13 13:23:52 -07:00
|
|
|
let new_tx_count = root_bank.transaction_count();
|
2019-03-18 16:04:36 -07:00
|
|
|
self.prune_non_root(root);
|
2019-05-07 23:34:10 -07:00
|
|
|
|
2019-03-29 21:21:59 -07:00
|
|
|
inc_new_counter_info!(
|
|
|
|
"bank-forks_set_root_ms",
|
|
|
|
timing::duration_as_ms(&set_root_start.elapsed()) as usize
|
|
|
|
);
|
2019-05-13 13:23:52 -07:00
|
|
|
inc_new_counter_info!(
|
|
|
|
"bank-forks_set_root_tx_count",
|
|
|
|
(new_tx_count - root_tx_count) as usize
|
|
|
|
);
|
2019-03-18 16:04:36 -07:00
|
|
|
}
|
|
|
|
|
2019-05-07 23:34:10 -07:00
|
|
|
pub fn root(&self) -> u64 {
|
|
|
|
self.root
|
|
|
|
}
|
|
|
|
|
2019-03-18 16:04:36 -07:00
|
|
|
fn prune_non_root(&mut self, root: u64) {
|
2019-05-30 21:31:35 -07:00
|
|
|
let slots: HashSet<u64> = self
|
|
|
|
.banks
|
|
|
|
.iter()
|
|
|
|
.filter(|(_, b)| b.is_frozen())
|
|
|
|
.map(|(k, _)| *k)
|
|
|
|
.collect();
|
2019-04-28 10:27:09 -07:00
|
|
|
let descendants = self.descendants();
|
2019-03-18 20:23:34 -07:00
|
|
|
self.banks
|
2019-05-30 21:31:35 -07:00
|
|
|
.retain(|slot, _| descendants[&root].contains(slot));
|
|
|
|
if self.snapshot_path.is_some() {
|
|
|
|
let diff: HashSet<_> = slots.symmetric_difference(&self.slots).collect();
|
|
|
|
trace!("prune non root {} - {:?}", root, diff);
|
|
|
|
for slot in diff.iter() {
|
|
|
|
if **slot > root {
|
|
|
|
let _ = self.add_snapshot(**slot, root);
|
2019-06-13 22:03:54 -07:00
|
|
|
} else {
|
2019-05-30 21:31:35 -07:00
|
|
|
BankForks::remove_snapshot(**slot, &self.snapshot_path);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
self.slots = slots.clone();
|
|
|
|
}
|
|
|
|
|
|
|
|
fn get_io_error(error: &str) -> Error {
|
|
|
|
warn!("BankForks error: {:?}", error);
|
|
|
|
Error::new(ErrorKind::Other, error)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn get_snapshot_path(path: &Option<String>) -> PathBuf {
|
|
|
|
Path::new(&path.clone().unwrap()).to_path_buf()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn add_snapshot(&self, slot: u64, root: u64) -> Result<(), Error> {
|
|
|
|
let path = BankForks::get_snapshot_path(&self.snapshot_path);
|
|
|
|
fs::create_dir_all(path.clone())?;
|
|
|
|
let bank_file = format!("{}", slot);
|
|
|
|
let bank_file_path = path.join(bank_file);
|
|
|
|
trace!("path: {:?}", bank_file_path);
|
|
|
|
let file = File::create(bank_file_path)?;
|
|
|
|
let mut stream = BufWriter::new(file);
|
|
|
|
let bank_slot = self.get(slot);
|
|
|
|
if bank_slot.is_none() {
|
|
|
|
return Err(BankForks::get_io_error("bank_forks get error"));
|
|
|
|
}
|
|
|
|
let bank = bank_slot.unwrap().clone();
|
|
|
|
serialize_into(&mut stream, &*bank)
|
|
|
|
.map_err(|_| BankForks::get_io_error("serialize bank error"))?;
|
|
|
|
let mut parent_slot: u64 = 0;
|
|
|
|
if let Some(parent_bank) = bank.parent() {
|
|
|
|
parent_slot = parent_bank.slot();
|
|
|
|
}
|
|
|
|
serialize_into(&mut stream, &parent_slot)
|
|
|
|
.map_err(|_| BankForks::get_io_error("serialize bank parent error"))?;
|
|
|
|
serialize_into(&mut stream, &root)
|
|
|
|
.map_err(|_| BankForks::get_io_error("serialize root error"))?;
|
|
|
|
serialize_into(&mut stream, &bank.src)
|
|
|
|
.map_err(|_| BankForks::get_io_error("serialize bank status cache error"))?;
|
|
|
|
serialize_into(&mut stream, &bank.rc)
|
|
|
|
.map_err(|_| BankForks::get_io_error("serialize bank accounts error"))?;
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn remove_snapshot(slot: u64, path: &Option<String>) {
|
|
|
|
let path = BankForks::get_snapshot_path(path);
|
|
|
|
let bank_file = format!("{}", slot);
|
|
|
|
let bank_file_path = path.join(bank_file);
|
|
|
|
let _ = fs::remove_file(bank_file_path);
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_snapshot_config(&mut self, path: Option<String>) {
|
|
|
|
self.snapshot_path = path;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn load_snapshots(
|
|
|
|
names: &[u64],
|
2019-06-05 21:51:44 -07:00
|
|
|
bank0: &mut Bank,
|
2019-05-30 21:31:35 -07:00
|
|
|
bank_maps: &mut Vec<(u64, u64, Bank)>,
|
|
|
|
status_cache_rc: &StatusCacheRc,
|
|
|
|
snapshot_path: &Option<String>,
|
2019-06-05 21:51:44 -07:00
|
|
|
) -> Option<u64> {
|
2019-05-30 21:31:35 -07:00
|
|
|
let path = BankForks::get_snapshot_path(snapshot_path);
|
2019-06-05 21:51:44 -07:00
|
|
|
let mut bank_root: Option<u64> = None;
|
2019-05-30 21:31:35 -07:00
|
|
|
|
|
|
|
for bank_slot in names.iter().rev() {
|
|
|
|
let bank_path = format!("{}", bank_slot);
|
|
|
|
let bank_file_path = path.join(bank_path.clone());
|
|
|
|
info!("Load from {:?}", bank_file_path);
|
|
|
|
let file = File::open(bank_file_path);
|
|
|
|
if file.is_err() {
|
|
|
|
warn!("Snapshot file open failed for {}", bank_slot);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
let file = file.unwrap();
|
|
|
|
let mut stream = BufReader::new(file);
|
|
|
|
let bank: Result<Bank, std::io::Error> = deserialize_from(&mut stream)
|
|
|
|
.map_err(|_| BankForks::get_io_error("deserialize bank error"));
|
|
|
|
let slot: Result<u64, std::io::Error> = deserialize_from(&mut stream)
|
|
|
|
.map_err(|_| BankForks::get_io_error("deserialize bank parent error"));
|
|
|
|
let parent_slot = if slot.is_ok() { slot.unwrap() } else { 0 };
|
|
|
|
let root: Result<u64, std::io::Error> = deserialize_from(&mut stream)
|
|
|
|
.map_err(|_| BankForks::get_io_error("deserialize root error"));
|
|
|
|
let status_cache: Result<StatusCacheRc, std::io::Error> = deserialize_from(&mut stream)
|
|
|
|
.map_err(|_| BankForks::get_io_error("deserialize bank status cache error"));
|
2019-06-05 21:51:44 -07:00
|
|
|
if bank_root.is_none() && bank0.rc.update_from_stream(&mut stream).is_ok() {
|
|
|
|
bank_root = Some(root.unwrap());
|
2019-05-30 21:31:35 -07:00
|
|
|
}
|
2019-06-05 21:51:44 -07:00
|
|
|
if bank_root.is_some() {
|
2019-05-30 21:31:35 -07:00
|
|
|
match bank {
|
|
|
|
Ok(v) => {
|
|
|
|
if status_cache.is_ok() {
|
|
|
|
status_cache_rc.append(&status_cache.unwrap());
|
|
|
|
}
|
|
|
|
bank_maps.push((*bank_slot, parent_slot, v));
|
|
|
|
}
|
|
|
|
Err(_) => warn!("Load snapshot failed for {}", bank_slot),
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
BankForks::remove_snapshot(*bank_slot, snapshot_path);
|
|
|
|
warn!("Load snapshot rc failed for {}", bank_slot);
|
|
|
|
}
|
|
|
|
}
|
2019-06-05 21:51:44 -07:00
|
|
|
|
|
|
|
bank_root
|
2019-05-30 21:31:35 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
fn setup_banks(
|
|
|
|
bank_maps: &mut Vec<(u64, u64, Bank)>,
|
|
|
|
bank_rc: &BankRc,
|
|
|
|
status_cache_rc: &StatusCacheRc,
|
|
|
|
) -> (HashMap<u64, Arc<Bank>>, HashSet<u64>, u64) {
|
|
|
|
let mut banks = HashMap::new();
|
|
|
|
let mut slots = HashSet::new();
|
|
|
|
let (last_slot, last_parent_slot, mut last_bank) = bank_maps.remove(0);
|
|
|
|
last_bank.set_bank_rc(&bank_rc, &status_cache_rc);
|
|
|
|
|
|
|
|
while let Some((slot, parent_slot, mut bank)) = bank_maps.pop() {
|
|
|
|
bank.set_bank_rc(&bank_rc, &status_cache_rc);
|
|
|
|
if parent_slot != 0 {
|
|
|
|
if let Some(parent) = banks.get(&parent_slot) {
|
|
|
|
bank.set_parent(parent);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if slot > 0 {
|
|
|
|
banks.insert(slot, Arc::new(bank));
|
|
|
|
slots.insert(slot);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if last_parent_slot != 0 {
|
|
|
|
if let Some(parent) = banks.get(&last_parent_slot) {
|
|
|
|
last_bank.set_parent(parent);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
banks.insert(last_slot, Arc::new(last_bank));
|
|
|
|
slots.insert(last_slot);
|
|
|
|
|
|
|
|
(banks, slots, last_slot)
|
|
|
|
}
|
|
|
|
|
2019-06-05 21:51:44 -07:00
|
|
|
pub fn load_from_snapshot(
|
|
|
|
genesis_block: &GenesisBlock,
|
|
|
|
account_paths: Option<String>,
|
|
|
|
snapshot_path: &Option<String>,
|
|
|
|
) -> Result<Self, Error> {
|
2019-05-30 21:31:35 -07:00
|
|
|
let path = BankForks::get_snapshot_path(snapshot_path);
|
|
|
|
let paths = fs::read_dir(path)?;
|
|
|
|
let mut names = paths
|
|
|
|
.filter_map(|entry| {
|
|
|
|
entry.ok().and_then(|e| {
|
|
|
|
e.path()
|
|
|
|
.file_name()
|
|
|
|
.and_then(|n| n.to_str().map(|s| s.parse::<u64>().unwrap()))
|
|
|
|
})
|
|
|
|
})
|
|
|
|
.collect::<Vec<u64>>();
|
|
|
|
|
|
|
|
names.sort();
|
|
|
|
let mut bank_maps = vec![];
|
|
|
|
let status_cache_rc = StatusCacheRc::default();
|
2019-06-25 07:21:45 -07:00
|
|
|
let id = (names[names.len() - 1] + 1) as usize;
|
2019-06-05 21:51:44 -07:00
|
|
|
let mut bank0 =
|
2019-06-25 07:21:45 -07:00
|
|
|
Bank::create_with_genesis(&genesis_block, account_paths.clone(), &status_cache_rc, id);
|
2019-06-05 21:51:44 -07:00
|
|
|
bank0.freeze();
|
|
|
|
let bank_root = BankForks::load_snapshots(
|
|
|
|
&names,
|
|
|
|
&mut bank0,
|
|
|
|
&mut bank_maps,
|
|
|
|
&status_cache_rc,
|
|
|
|
snapshot_path,
|
|
|
|
);
|
|
|
|
if bank_maps.is_empty() || bank_root.is_none() {
|
2019-05-30 21:31:35 -07:00
|
|
|
BankForks::remove_snapshot(0, snapshot_path);
|
|
|
|
return Err(Error::new(ErrorKind::Other, "no snapshots loaded"));
|
|
|
|
}
|
|
|
|
|
2019-06-05 21:51:44 -07:00
|
|
|
let root = bank_root.unwrap();
|
2019-05-30 21:31:35 -07:00
|
|
|
let (banks, slots, last_slot) =
|
2019-06-05 21:51:44 -07:00
|
|
|
BankForks::setup_banks(&mut bank_maps, &bank0.rc, &status_cache_rc);
|
2019-05-30 21:31:35 -07:00
|
|
|
let working_bank = banks[&last_slot].clone();
|
|
|
|
Ok(BankForks {
|
|
|
|
banks,
|
|
|
|
working_bank,
|
|
|
|
root,
|
|
|
|
slots,
|
|
|
|
snapshot_path: snapshot_path.clone(),
|
|
|
|
})
|
2019-03-18 16:04:36 -07:00
|
|
|
}
|
2019-02-16 19:58:07 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
2019-05-22 20:39:00 -07:00
|
|
|
use crate::genesis_utils::{create_genesis_block, GenesisBlockInfo};
|
2019-02-20 21:31:24 -08:00
|
|
|
use solana_sdk::hash::Hash;
|
2019-03-01 16:39:23 -08:00
|
|
|
use solana_sdk::pubkey::Pubkey;
|
2019-05-30 21:31:35 -07:00
|
|
|
use solana_sdk::signature::{Keypair, KeypairUtil};
|
|
|
|
use solana_sdk::system_transaction;
|
|
|
|
use std::env;
|
|
|
|
use std::fs::remove_dir_all;
|
2019-02-16 19:58:07 -08:00
|
|
|
|
|
|
|
#[test]
|
2019-02-28 10:57:58 -08:00
|
|
|
fn test_bank_forks() {
|
2019-05-22 20:39:00 -07:00
|
|
|
let GenesisBlockInfo { genesis_block, .. } = create_genesis_block(10_000);
|
2019-02-20 16:14:58 -08:00
|
|
|
let bank = Bank::new(&genesis_block);
|
2019-02-18 14:35:02 -08:00
|
|
|
let mut bank_forks = BankForks::new(0, bank);
|
2019-03-09 19:28:43 -08:00
|
|
|
let child_bank = Bank::new_from_parent(&bank_forks[0u64], &Pubkey::default(), 1);
|
2019-02-16 19:58:07 -08:00
|
|
|
child_bank.register_tick(&Hash::default());
|
2019-03-18 20:23:34 -07:00
|
|
|
bank_forks.insert(child_bank);
|
2019-02-28 10:57:58 -08:00
|
|
|
assert_eq!(bank_forks[1u64].tick_height(), 1);
|
2019-02-26 09:18:24 -08:00
|
|
|
assert_eq!(bank_forks.working_bank().tick_height(), 1);
|
2019-02-16 19:58:07 -08:00
|
|
|
}
|
2019-02-26 21:57:45 -08:00
|
|
|
|
2019-03-18 12:12:33 -07:00
|
|
|
#[test]
|
2019-03-18 15:20:04 -07:00
|
|
|
fn test_bank_forks_descendants() {
|
2019-05-22 20:39:00 -07:00
|
|
|
let GenesisBlockInfo { genesis_block, .. } = create_genesis_block(10_000);
|
2019-03-18 12:12:33 -07:00
|
|
|
let bank = Bank::new(&genesis_block);
|
|
|
|
let mut bank_forks = BankForks::new(0, bank);
|
|
|
|
let bank0 = bank_forks[0].clone();
|
|
|
|
let bank = Bank::new_from_parent(&bank0, &Pubkey::default(), 1);
|
2019-03-18 20:23:34 -07:00
|
|
|
bank_forks.insert(bank);
|
2019-03-18 12:12:33 -07:00
|
|
|
let bank = Bank::new_from_parent(&bank0, &Pubkey::default(), 2);
|
2019-03-18 20:23:34 -07:00
|
|
|
bank_forks.insert(bank);
|
2019-03-18 15:20:04 -07:00
|
|
|
let descendants = bank_forks.descendants();
|
2019-05-30 21:31:35 -07:00
|
|
|
let children: HashSet<u64> = [1u64, 2u64].to_vec().into_iter().collect();
|
|
|
|
assert_eq!(children, *descendants.get(&0).unwrap());
|
2019-03-18 15:20:04 -07:00
|
|
|
assert!(descendants[&1].is_empty());
|
|
|
|
assert!(descendants[&2].is_empty());
|
2019-03-18 12:12:33 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_bank_forks_ancestors() {
|
2019-05-22 20:39:00 -07:00
|
|
|
let GenesisBlockInfo { genesis_block, .. } = create_genesis_block(10_000);
|
2019-03-18 12:12:33 -07:00
|
|
|
let bank = Bank::new(&genesis_block);
|
|
|
|
let mut bank_forks = BankForks::new(0, bank);
|
|
|
|
let bank0 = bank_forks[0].clone();
|
|
|
|
let bank = Bank::new_from_parent(&bank0, &Pubkey::default(), 1);
|
2019-03-18 20:23:34 -07:00
|
|
|
bank_forks.insert(bank);
|
2019-03-18 12:12:33 -07:00
|
|
|
let bank = Bank::new_from_parent(&bank0, &Pubkey::default(), 2);
|
2019-03-18 20:23:34 -07:00
|
|
|
bank_forks.insert(bank);
|
2019-03-18 12:12:33 -07:00
|
|
|
let ancestors = bank_forks.ancestors();
|
|
|
|
assert!(ancestors[&0].is_empty());
|
|
|
|
let parents: Vec<u64> = ancestors[&1].iter().cloned().collect();
|
|
|
|
assert_eq!(parents, vec![0]);
|
|
|
|
let parents: Vec<u64> = ancestors[&2].iter().cloned().collect();
|
|
|
|
assert_eq!(parents, vec![0]);
|
|
|
|
}
|
|
|
|
|
2019-02-26 21:57:45 -08:00
|
|
|
#[test]
|
|
|
|
fn test_bank_forks_frozen_banks() {
|
2019-05-22 20:39:00 -07:00
|
|
|
let GenesisBlockInfo { genesis_block, .. } = create_genesis_block(10_000);
|
2019-02-26 21:57:45 -08:00
|
|
|
let bank = Bank::new(&genesis_block);
|
|
|
|
let mut bank_forks = BankForks::new(0, bank);
|
2019-03-09 19:28:43 -08:00
|
|
|
let child_bank = Bank::new_from_parent(&bank_forks[0u64], &Pubkey::default(), 1);
|
2019-03-18 20:23:34 -07:00
|
|
|
bank_forks.insert(child_bank);
|
2019-02-26 21:57:45 -08:00
|
|
|
assert!(bank_forks.frozen_banks().get(&0).is_some());
|
|
|
|
assert!(bank_forks.frozen_banks().get(&1).is_none());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_bank_forks_active_banks() {
|
2019-05-22 20:39:00 -07:00
|
|
|
let GenesisBlockInfo { genesis_block, .. } = create_genesis_block(10_000);
|
2019-02-26 21:57:45 -08:00
|
|
|
let bank = Bank::new(&genesis_block);
|
|
|
|
let mut bank_forks = BankForks::new(0, bank);
|
2019-03-09 19:28:43 -08:00
|
|
|
let child_bank = Bank::new_from_parent(&bank_forks[0u64], &Pubkey::default(), 1);
|
2019-03-18 20:23:34 -07:00
|
|
|
bank_forks.insert(child_bank);
|
2019-02-26 21:57:45 -08:00
|
|
|
assert_eq!(bank_forks.active_banks(), vec![1]);
|
|
|
|
}
|
|
|
|
|
2019-05-30 21:31:35 -07:00
|
|
|
struct TempPaths {
|
|
|
|
pub paths: String,
|
|
|
|
}
|
|
|
|
|
2019-07-08 15:55:50 -07:00
|
|
|
impl TempPaths {
|
|
|
|
fn remove_all(&self) {
|
|
|
|
let paths: Vec<String> = self.paths.split(',').map(|s| s.to_string()).collect();
|
|
|
|
paths.iter().for_each(|p| {
|
|
|
|
let _ignored = remove_dir_all(p);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-30 21:31:35 -07:00
|
|
|
#[macro_export]
|
|
|
|
macro_rules! tmp_bank_accounts_name {
|
|
|
|
() => {
|
|
|
|
&format!("{}-{}", file!(), line!())
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
#[macro_export]
|
|
|
|
macro_rules! get_tmp_bank_accounts_path {
|
|
|
|
() => {
|
|
|
|
get_tmp_bank_accounts_path(tmp_bank_accounts_name!())
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Drop for TempPaths {
|
|
|
|
fn drop(&mut self) {
|
2019-07-08 15:55:50 -07:00
|
|
|
self.remove_all()
|
2019-05-30 21:31:35 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn get_paths_vec(paths: &str) -> Vec<String> {
|
|
|
|
paths.split(',').map(|s| s.to_string()).collect()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn get_tmp_snapshots_path() -> TempPaths {
|
|
|
|
let out_dir = env::var("OUT_DIR").unwrap_or_else(|_| "target".to_string());
|
|
|
|
let path = format!("{}/snapshots", out_dir);
|
|
|
|
TempPaths {
|
|
|
|
paths: path.to_string(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn get_tmp_bank_accounts_path(paths: &str) -> TempPaths {
|
|
|
|
let vpaths = get_paths_vec(paths);
|
|
|
|
let out_dir = env::var("OUT_DIR").unwrap_or_else(|_| "target".to_string());
|
|
|
|
let vpaths: Vec<_> = vpaths
|
|
|
|
.iter()
|
|
|
|
.map(|path| format!("{}/{}", out_dir, path))
|
|
|
|
.collect();
|
|
|
|
TempPaths {
|
|
|
|
paths: vpaths.join(","),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-05 21:51:44 -07:00
|
|
|
fn restore_from_snapshot(
|
|
|
|
genesis_block: &GenesisBlock,
|
|
|
|
bank_forks: BankForks,
|
|
|
|
account_paths: Option<String>,
|
|
|
|
last_slot: u64,
|
|
|
|
) {
|
|
|
|
let new =
|
|
|
|
BankForks::load_from_snapshot(&genesis_block, account_paths, &bank_forks.snapshot_path)
|
|
|
|
.unwrap();
|
2019-05-30 21:31:35 -07:00
|
|
|
for (slot, _) in new.banks.iter() {
|
2019-06-02 14:05:32 -07:00
|
|
|
if *slot > 0 {
|
|
|
|
let bank = bank_forks.banks.get(slot).unwrap().clone();
|
|
|
|
let new_bank = new.banks.get(slot).unwrap();
|
|
|
|
bank.compare_bank(&new_bank);
|
|
|
|
}
|
2019-05-30 21:31:35 -07:00
|
|
|
}
|
|
|
|
assert_eq!(new.working_bank().slot(), last_slot);
|
|
|
|
for (slot, _) in new.banks.iter() {
|
|
|
|
BankForks::remove_snapshot(*slot, &bank_forks.snapshot_path);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_bank_forks_snapshot_n() {
|
|
|
|
solana_logger::setup();
|
|
|
|
let path = get_tmp_bank_accounts_path!();
|
|
|
|
let spath = get_tmp_snapshots_path();
|
|
|
|
let GenesisBlockInfo {
|
|
|
|
genesis_block,
|
|
|
|
mint_keypair,
|
|
|
|
..
|
|
|
|
} = create_genesis_block(10_000);
|
2019-07-08 15:55:50 -07:00
|
|
|
path.remove_all();
|
|
|
|
spath.remove_all();
|
2019-05-30 21:31:35 -07:00
|
|
|
for index in 0..10 {
|
|
|
|
let bank0 = Bank::new_with_paths(&genesis_block, Some(path.paths.clone()));
|
|
|
|
bank0.freeze();
|
|
|
|
let slot = bank0.slot();
|
|
|
|
let mut bank_forks = BankForks::new(0, bank0);
|
|
|
|
bank_forks.set_snapshot_config(Some(spath.paths.clone()));
|
|
|
|
bank_forks.add_snapshot(slot, 0).unwrap();
|
|
|
|
for forks in 0..index {
|
|
|
|
let bank = Bank::new_from_parent(&bank_forks[forks], &Pubkey::default(), forks + 1);
|
|
|
|
let key1 = Keypair::new().pubkey();
|
|
|
|
let tx = system_transaction::create_user_account(
|
|
|
|
&mint_keypair,
|
|
|
|
&key1,
|
|
|
|
1,
|
|
|
|
genesis_block.hash(),
|
|
|
|
);
|
|
|
|
assert_eq!(bank.process_transaction(&tx), Ok(()));
|
|
|
|
bank.freeze();
|
|
|
|
let slot = bank.slot();
|
|
|
|
bank_forks.insert(bank);
|
|
|
|
bank_forks.add_snapshot(slot, 0).unwrap();
|
|
|
|
}
|
2019-06-05 21:51:44 -07:00
|
|
|
restore_from_snapshot(&genesis_block, bank_forks, Some(path.paths.clone()), index);
|
2019-05-30 21:31:35 -07:00
|
|
|
}
|
|
|
|
}
|
2019-02-16 19:58:07 -08:00
|
|
|
}
|