Track reset bank in PohRecorder (#19810)

This commit is contained in:
carllin
2021-09-13 16:55:35 -07:00
committed by GitHub
parent 97fc64dd0c
commit 87a7f00926
8 changed files with 317 additions and 314 deletions

View File

@@ -18,7 +18,11 @@ use {
},
log::*,
solana_entry::{entry::Entry, poh::Poh},
solana_ledger::{blockstore::Blockstore, leader_schedule_cache::LeaderScheduleCache},
solana_ledger::{
blockstore::Blockstore,
genesis_utils::{create_genesis_config, GenesisConfigInfo},
leader_schedule_cache::LeaderScheduleCache,
},
solana_runtime::bank::Bank,
solana_sdk::{
clock::NUM_CONSECUTIVE_LEADER_SLOTS, hash::Hash, poh_config::PohConfig, pubkey::Pubkey,
@@ -155,7 +159,7 @@ pub struct PohRecorder {
pub poh: Arc<Mutex<Poh>>,
tick_height: u64,
clear_bank_signal: Option<SyncSender<bool>>,
start_slot: Slot, // parent slot
start_bank: Arc<Bank>, // parent slot
start_tick_height: u64, // first tick_height this recorder will observe
tick_cache: Vec<(Entry, u64)>, // cache of entry and its tick_height
working_bank: Option<WorkingBank>,
@@ -268,7 +272,7 @@ impl PohRecorder {
// previous leader's slots.
// If so, PoH is currently building on the previous leader's blocks
// If not, PoH is building on a different fork
slot == self.start_slot
slot == self.start_slot()
})
}
@@ -296,12 +300,12 @@ impl PohRecorder {
|| !self.is_same_fork_as_previous_leader(current_slot)))
}
pub fn last_reset_slot(&self) -> Slot {
self.start_slot
pub fn start_slot(&self) -> Slot {
self.start_bank.slot()
}
/// returns if leader slot has been reached, how many grace ticks were afforded,
/// imputed leader_slot and self.start_slot
/// imputed leader_slot and self.start_slot()
/// reached_leader_slot() == true means "ready for a bank"
pub fn reached_leader_slot(&self) -> (bool, u64, Slot, Slot) {
trace!(
@@ -325,11 +329,11 @@ impl PohRecorder {
true,
self.tick_height.saturating_sub(ideal_target_tick_height),
next_leader_slot,
self.start_slot,
self.start_slot(),
);
}
}
(false, 0, next_leader_slot, self.start_slot)
(false, 0, next_leader_slot, self.start_slot())
}
// returns (leader_first_tick_height, leader_last_tick_height, grace_ticks) given the next
@@ -364,14 +368,10 @@ impl PohRecorder {
}
// synchronize PoH with a bank
pub fn reset(
&mut self,
blockhash: Hash,
start_slot: Slot,
next_leader_slot: Option<(Slot, Slot)>,
) {
pub fn reset(&mut self, reset_bank: Arc<Bank>, next_leader_slot: Option<(Slot, Slot)>) {
self.clear_bank();
let mut cache = vec![];
let blockhash = reset_bank.last_blockhash();
let poh_hash = {
let mut poh = self.poh.lock().unwrap();
poh.reset(blockhash, self.poh_config.hashes_per_tick);
@@ -379,13 +379,17 @@ impl PohRecorder {
};
info!(
"reset poh from: {},{},{} to: {},{}",
poh_hash, self.tick_height, self.start_slot, blockhash, start_slot
poh_hash,
self.tick_height,
self.start_slot(),
blockhash,
reset_bank.slot()
);
std::mem::swap(&mut cache, &mut self.tick_cache);
self.start_slot = start_slot;
self.tick_height = (start_slot + 1) * self.ticks_per_slot;
self.start_bank = reset_bank;
self.tick_height = (self.start_slot() + 1) * self.ticks_per_slot;
self.start_tick_height = self.tick_height + 1;
let (leader_first_tick_height, leader_last_tick_height, grace_ticks) =
@@ -395,15 +399,6 @@ impl PohRecorder {
self.leader_last_tick_height = leader_last_tick_height;
}
pub fn set_working_bank(&mut self, working_bank: WorkingBank) {
trace!("new working bank");
assert_eq!(working_bank.bank.ticks_per_slot(), self.ticks_per_slot());
self.working_bank = Some(working_bank);
// TODO: adjust the working_bank.start time based on number of ticks
// that have already elapsed based on current tick height.
let _ = self.flush_cache(false);
}
pub fn set_bank(&mut self, bank: &Arc<Bank>) {
let working_bank = WorkingBank {
bank: bank.clone(),
@@ -411,7 +406,12 @@ impl PohRecorder {
min_tick_height: bank.tick_height(),
max_tick_height: bank.max_tick_height(),
};
self.set_working_bank(working_bank);
trace!("new working bank");
assert_eq!(working_bank.bank.ticks_per_slot(), self.ticks_per_slot());
self.working_bank = Some(working_bank);
// TODO: adjust the working_bank.start time based on number of ticks
// that have already elapsed based on current tick height.
let _ = self.flush_cache(false);
}
// Flush cache will delay flushing the cache for a bank until it past the WorkingBank::min_tick_height
@@ -462,9 +462,8 @@ impl PohRecorder {
working_bank.max_tick_height,
working_bank.bank.slot()
);
let working_slot =
(working_bank.max_tick_height / self.ticks_per_slot).saturating_sub(1);
self.start_slot = working_slot;
self.start_bank = working_bank.bank.clone();
let working_slot = self.start_slot();
self.start_tick_height = working_slot * self.ticks_per_slot + 1;
self.clear_bank();
}
@@ -620,7 +619,7 @@ impl PohRecorder {
pub fn new_with_clear_signal(
tick_height: u64,
last_entry_hash: Hash,
start_slot: Slot,
start_bank: Arc<Bank>,
next_leader_slot: Option<(Slot, Slot)>,
ticks_per_slot: u64,
id: &Pubkey,
@@ -654,7 +653,7 @@ impl PohRecorder {
working_bank: None,
sender,
clear_bank_signal,
start_slot,
start_bank,
start_tick_height: tick_height + 1,
leader_first_tick_height,
leader_last_tick_height,
@@ -691,7 +690,7 @@ impl PohRecorder {
pub fn new(
tick_height: u64,
last_entry_hash: Hash,
start_slot: Slot,
start_bank: Arc<Bank>,
next_leader_slot: Option<(Slot, Slot)>,
ticks_per_slot: u64,
id: &Pubkey,
@@ -703,7 +702,7 @@ impl PohRecorder {
Self::new_with_clear_signal(
tick_height,
last_entry_hash,
start_slot,
start_bank,
next_leader_slot,
ticks_per_slot,
id,
@@ -729,7 +728,9 @@ impl PohRecorder {
// Used in tests
pub fn schedule_dummy_max_height_reached_failure(&mut self) {
self.reset(Hash::default(), 1, None);
let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(2);
let bank = Arc::new(Bank::new_for_tests(&genesis_config));
self.reset(bank, None);
}
}
@@ -748,7 +749,7 @@ pub fn create_test_recorder(
let (mut poh_recorder, entry_receiver, record_receiver) = PohRecorder::new(
bank.tick_height(),
bank.last_blockhash(),
bank.slot(),
bank.clone(),
Some((4, 4)),
bank.ticks_per_slot(),
&Pubkey::default(),
@@ -778,12 +779,7 @@ mod tests {
use {
super::*,
bincode::serialize,
solana_ledger::{
blockstore::Blockstore,
blockstore_meta::SlotMeta,
genesis_utils::{create_genesis_config, GenesisConfigInfo},
get_tmp_ledger_path,
},
solana_ledger::{blockstore::Blockstore, blockstore_meta::SlotMeta, get_tmp_ledger_path},
solana_perf::test_tx::test_tx,
solana_sdk::{clock::DEFAULT_TICKS_PER_SLOT, hash::hash},
std::sync::mpsc::sync_channel,
@@ -797,10 +793,12 @@ mod tests {
let blockstore = Blockstore::open(&ledger_path)
.expect("Expected to be able to open database ledger");
let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(2);
let bank = Arc::new(Bank::new_for_tests(&genesis_config));
let (mut poh_recorder, _entry_receiver, _record_receiver) = PohRecorder::new(
0,
prev_hash,
0,
bank,
Some((4, 4)),
DEFAULT_TICKS_PER_SLOT,
&Pubkey::default(),
@@ -825,10 +823,12 @@ mod tests {
let blockstore = Blockstore::open(&ledger_path)
.expect("Expected to be able to open database ledger");
let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(2);
let bank = Arc::new(Bank::new_for_tests(&genesis_config));
let (mut poh_recorder, _entry_receiver, _record_receiver) = PohRecorder::new(
0,
prev_hash,
0,
bank,
Some((4, 4)),
DEFAULT_TICKS_PER_SLOT,
&Pubkey::default(),
@@ -852,10 +852,12 @@ mod tests {
{
let blockstore = Blockstore::open(&ledger_path)
.expect("Expected to be able to open database ledger");
let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(2);
let bank0 = Arc::new(Bank::new_for_tests(&genesis_config));
let (mut poh_recorder, _entry_receiver, _record_receiver) = PohRecorder::new(
0,
Hash::default(),
0,
bank0.clone(),
Some((4, 4)),
DEFAULT_TICKS_PER_SLOT,
&Pubkey::default(),
@@ -866,7 +868,7 @@ mod tests {
);
poh_recorder.tick();
assert_eq!(poh_recorder.tick_cache.len(), 1);
poh_recorder.reset(Hash::default(), 0, Some((4, 4)));
poh_recorder.reset(bank0, Some((4, 4)));
assert_eq!(poh_recorder.tick_cache.len(), 0);
}
Blockstore::destroy(&ledger_path).unwrap();
@@ -884,7 +886,7 @@ mod tests {
let (mut poh_recorder, _entry_receiver, _record_receiver) = PohRecorder::new(
0,
prev_hash,
0,
bank.clone(),
Some((4, 4)),
bank.ticks_per_slot(),
&Pubkey::default(),
@@ -894,14 +896,7 @@ mod tests {
Arc::new(AtomicBool::default()),
);
let start = Arc::new(Instant::now());
let working_bank = WorkingBank {
bank,
start,
min_tick_height: 2,
max_tick_height: 3,
};
poh_recorder.set_working_bank(working_bank);
poh_recorder.set_bank(&bank);
assert!(poh_recorder.working_bank.is_some());
poh_recorder.clear_bank();
assert!(poh_recorder.working_bank.is_none());
@@ -916,47 +911,58 @@ mod tests {
let blockstore = Blockstore::open(&ledger_path)
.expect("Expected to be able to open database ledger");
let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(2);
let bank = Arc::new(Bank::new_for_tests(&genesis_config));
let prev_hash = bank.last_blockhash();
let bank0 = Arc::new(Bank::new_for_tests(&genesis_config));
let prev_hash = bank0.last_blockhash();
let (mut poh_recorder, entry_receiver, _record_receiver) = PohRecorder::new(
0,
prev_hash,
0,
bank0.clone(),
Some((4, 4)),
bank.ticks_per_slot(),
bank0.ticks_per_slot(),
&Pubkey::default(),
&Arc::new(blockstore),
&Arc::new(LeaderScheduleCache::new_from_bank(&bank)),
&Arc::new(LeaderScheduleCache::new_from_bank(&bank0)),
&Arc::new(PohConfig::default()),
Arc::new(AtomicBool::default()),
);
let start = Arc::new(Instant::now());
let working_bank = WorkingBank {
bank: bank.clone(),
start,
min_tick_height: 2,
max_tick_height: 3,
};
poh_recorder.set_working_bank(working_bank);
poh_recorder.tick();
poh_recorder.tick();
//tick height equal to min_tick_height
//no tick has been sent
assert_eq!(poh_recorder.tick_cache.last().unwrap().1, 2);
bank0.fill_bank_with_ticks();
let bank1 = Arc::new(Bank::new_from_parent(&bank0, &Pubkey::default(), 1));
// Set a working bank
poh_recorder.set_bank(&bank1);
// Tick until poh_recorder.tick_height == working bank's min_tick_height
let num_new_ticks = bank1.tick_height() - poh_recorder.tick_height();
println!("{} {}", bank1.tick_height(), poh_recorder.tick_height());
assert!(num_new_ticks > 0);
for _ in 0..num_new_ticks {
poh_recorder.tick();
}
// Check that poh_recorder.tick_height == working bank's min_tick_height
let min_tick_height = poh_recorder.working_bank.as_ref().unwrap().min_tick_height;
assert_eq!(min_tick_height, bank1.tick_height());
assert_eq!(poh_recorder.tick_height(), min_tick_height);
//poh_recorder.tick height == working bank's min_tick_height,
// so no ticks should have been flushed yet
assert_eq!(poh_recorder.tick_cache.last().unwrap().1, num_new_ticks);
assert!(entry_receiver.try_recv().is_err());
// all ticks are sent after height > min
let tick_height_before = poh_recorder.tick_height();
poh_recorder.tick();
assert_eq!(poh_recorder.tick_height, 3);
assert_eq!(poh_recorder.tick_height, tick_height_before + 1);
assert_eq!(poh_recorder.tick_cache.len(), 0);
let mut num_entries = 0;
while let Ok((wbank, (_entry, _tick_height))) = entry_receiver.try_recv() {
assert_eq!(wbank.slot(), bank.slot());
assert_eq!(wbank.slot(), bank1.slot());
num_entries += 1;
}
assert_eq!(num_entries, 3);
assert!(poh_recorder.working_bank.is_none());
// All the cached ticks, plus the new tick above should have been flushed
assert_eq!(num_entries, num_new_ticks + 1);
}
Blockstore::destroy(&ledger_path).unwrap();
}
@@ -973,7 +979,7 @@ mod tests {
let (mut poh_recorder, entry_receiver, _record_receiver) = PohRecorder::new(
0,
prev_hash,
0,
bank.clone(),
Some((4, 4)),
bank.ticks_per_slot(),
&Pubkey::default(),
@@ -983,30 +989,29 @@ mod tests {
Arc::new(AtomicBool::default()),
);
poh_recorder.tick();
poh_recorder.tick();
poh_recorder.tick();
poh_recorder.tick();
assert_eq!(poh_recorder.tick_cache.last().unwrap().1, 4);
assert_eq!(poh_recorder.tick_height, 4);
// Tick further than the bank's max height
for _ in 0..bank.max_tick_height() + 1 {
poh_recorder.tick();
}
assert_eq!(
poh_recorder.tick_cache.last().unwrap().1,
bank.max_tick_height() + 1
);
assert_eq!(poh_recorder.tick_height, bank.max_tick_height() + 1);
let start = Arc::new(Instant::now());
let working_bank = WorkingBank {
bank,
start,
min_tick_height: 2,
max_tick_height: 3,
};
poh_recorder.set_working_bank(working_bank);
poh_recorder.set_bank(&bank);
poh_recorder.tick();
assert_eq!(poh_recorder.tick_height, 5);
assert_eq!(poh_recorder.tick_height, bank.max_tick_height() + 2);
assert!(poh_recorder.working_bank.is_none());
let mut num_entries = 0;
while entry_receiver.try_recv().is_ok() {
num_entries += 1;
}
assert_eq!(num_entries, 3);
// Should only flush up to bank's max tick height, despite the tick cache
// having many more entries
assert_eq!(num_entries, bank.max_tick_height());
}
Blockstore::destroy(&ledger_path).unwrap();
}
@@ -1018,35 +1023,37 @@ mod tests {
let blockstore = Blockstore::open(&ledger_path)
.expect("Expected to be able to open database ledger");
let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(2);
let bank = Arc::new(Bank::new_for_tests(&genesis_config));
let prev_hash = bank.last_blockhash();
let bank0 = Arc::new(Bank::new_for_tests(&genesis_config));
let prev_hash = bank0.last_blockhash();
let (mut poh_recorder, entry_receiver, _record_receiver) = PohRecorder::new(
0,
prev_hash,
0,
bank0.clone(),
Some((4, 4)),
bank.ticks_per_slot(),
bank0.ticks_per_slot(),
&Pubkey::default(),
&Arc::new(blockstore),
&Arc::new(LeaderScheduleCache::new_from_bank(&bank)),
&Arc::new(LeaderScheduleCache::new_from_bank(&bank0)),
&Arc::new(PohConfig::default()),
Arc::new(AtomicBool::default()),
);
let start = Arc::new(Instant::now());
let working_bank = WorkingBank {
bank: bank.clone(),
start,
min_tick_height: 2,
max_tick_height: 3,
};
poh_recorder.set_working_bank(working_bank);
poh_recorder.tick();
bank0.fill_bank_with_ticks();
let bank1 = Arc::new(Bank::new_from_parent(&bank0, &Pubkey::default(), 1));
poh_recorder.set_bank(&bank1);
// Let poh_recorder tick up to bank1.tick_height() - 1
for _ in 0..bank1.tick_height() - 1 {
poh_recorder.tick()
}
let tx = test_tx();
let h1 = hash(b"hello world!");
assert!(poh_recorder
.record(bank.slot(), h1, vec![tx.into()])
.is_err());
// We haven't yet reached the minimum tick height for the working bank,
// so record should fail
assert_matches!(
poh_recorder.record(bank1.slot(), h1, vec![tx.into()]),
Err(PohRecorderError::MinHeightNotReached)
);
assert!(entry_receiver.try_recv().is_err());
}
Blockstore::destroy(&ledger_path).unwrap();
@@ -1064,7 +1071,7 @@ mod tests {
let (mut poh_recorder, _entry_receiver, _record_receiver) = PohRecorder::new(
0,
prev_hash,
0,
bank.clone(),
Some((4, 4)),
bank.ticks_per_slot(),
&Pubkey::default(),
@@ -1074,21 +1081,20 @@ mod tests {
Arc::new(AtomicBool::default()),
);
let start = Arc::new(Instant::now());
let working_bank = WorkingBank {
bank: bank.clone(),
start,
min_tick_height: 1,
max_tick_height: 2,
};
poh_recorder.set_working_bank(working_bank);
poh_recorder.tick();
assert_eq!(poh_recorder.tick_cache.len(), 1);
assert_eq!(poh_recorder.tick_height, 1);
poh_recorder.set_bank(&bank);
let tx = test_tx();
let h1 = hash(b"hello world!");
// Fulfills min height criteria for a successful record
assert_eq!(
poh_recorder.tick_height(),
poh_recorder.working_bank.as_ref().unwrap().min_tick_height
);
// However we hand over a bad slot so record fails
let bad_slot = bank.slot() + 1;
assert_matches!(
poh_recorder.record(bank.slot() + 1, h1, vec![tx.into()]),
poh_recorder.record(bad_slot, h1, vec![tx.into()]),
Err(PohRecorderError::MaxHeightReached)
);
}
@@ -1102,43 +1108,50 @@ mod tests {
let blockstore = Blockstore::open(&ledger_path)
.expect("Expected to be able to open database ledger");
let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(2);
let bank = Arc::new(Bank::new_for_tests(&genesis_config));
let prev_hash = bank.last_blockhash();
let bank0 = Arc::new(Bank::new_for_tests(&genesis_config));
let prev_hash = bank0.last_blockhash();
let (mut poh_recorder, entry_receiver, _record_receiver) = PohRecorder::new(
0,
prev_hash,
0,
bank0.clone(),
Some((4, 4)),
bank.ticks_per_slot(),
bank0.ticks_per_slot(),
&Pubkey::default(),
&Arc::new(blockstore),
&Arc::new(LeaderScheduleCache::new_from_bank(&bank)),
&Arc::new(LeaderScheduleCache::new_from_bank(&bank0)),
&Arc::new(PohConfig::default()),
Arc::new(AtomicBool::default()),
);
let start = Arc::new(Instant::now());
let working_bank = WorkingBank {
bank: bank.clone(),
start,
min_tick_height: 1,
max_tick_height: 2,
};
poh_recorder.set_working_bank(working_bank);
poh_recorder.tick();
assert_eq!(poh_recorder.tick_cache.len(), 1);
assert_eq!(poh_recorder.tick_height, 1);
bank0.fill_bank_with_ticks();
let bank1 = Arc::new(Bank::new_from_parent(&bank0, &Pubkey::default(), 1));
poh_recorder.set_bank(&bank1);
// Record up to exactly min tick height
let min_tick_height = poh_recorder.working_bank.as_ref().unwrap().min_tick_height;
while poh_recorder.tick_height() < min_tick_height {
poh_recorder.tick();
}
assert_eq!(poh_recorder.tick_cache.len() as u64, min_tick_height);
// Check record succeeds on boundary condition where
// poh_recorder.tick height == poh_recorder.working_bank.min_tick_height
assert_eq!(poh_recorder.tick_height, min_tick_height);
let tx = test_tx();
let h1 = hash(b"hello world!");
assert!(poh_recorder
.record(bank.slot(), h1, vec![tx.into()])
.record(bank1.slot(), h1, vec![tx.into()])
.is_ok());
assert_eq!(poh_recorder.tick_cache.len(), 0);
//tick in the cache + entry
let (_bank, (e, _tick_height)) = entry_receiver.recv().expect("recv 1");
assert!(e.is_tick());
let (_bank, (e, _tick_height)) = entry_receiver.recv().expect("recv 2");
for _ in 0..min_tick_height {
let (_bank, (e, _tick_height)) = entry_receiver.recv().unwrap();
assert!(e.is_tick());
}
let (_bank, (e, _tick_height)) = entry_receiver.recv().unwrap();
assert!(!e.is_tick());
}
Blockstore::destroy(&ledger_path).unwrap();
@@ -1156,7 +1169,7 @@ mod tests {
let (mut poh_recorder, entry_receiver, _record_receiver) = PohRecorder::new(
0,
prev_hash,
0,
bank.clone(),
Some((4, 4)),
bank.ticks_per_slot(),
&Pubkey::default(),
@@ -1166,27 +1179,20 @@ mod tests {
Arc::new(AtomicBool::default()),
);
let start = Arc::new(Instant::now());
let working_bank = WorkingBank {
bank: bank.clone(),
start,
min_tick_height: 1,
max_tick_height: 2,
};
poh_recorder.set_working_bank(working_bank);
poh_recorder.tick();
poh_recorder.tick();
assert_eq!(poh_recorder.tick_height, 2);
poh_recorder.set_bank(&bank);
let num_ticks_to_max = bank.max_tick_height() - poh_recorder.tick_height;
for _ in 0..num_ticks_to_max {
poh_recorder.tick();
}
let tx = test_tx();
let h1 = hash(b"hello world!");
assert!(poh_recorder
.record(bank.slot(), h1, vec![tx.into()])
.is_err());
let (_bank, (entry, _tick_height)) = entry_receiver.recv().unwrap();
assert!(entry.is_tick());
let (_bank, (entry, _tick_height)) = entry_receiver.recv().unwrap();
assert!(entry.is_tick());
for _ in 0..num_ticks_to_max {
let (_bank, (entry, _tick_height)) = entry_receiver.recv().unwrap();
assert!(entry.is_tick());
}
}
Blockstore::destroy(&ledger_path).unwrap();
}
@@ -1198,36 +1204,52 @@ mod tests {
let blockstore = Blockstore::open(&ledger_path)
.expect("Expected to be able to open database ledger");
let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(2);
let bank = Arc::new(Bank::new_for_tests(&genesis_config));
let prev_hash = bank.last_blockhash();
let bank0 = Arc::new(Bank::new_for_tests(&genesis_config));
let prev_hash = bank0.last_blockhash();
let (mut poh_recorder, entry_receiver, _record_receiver) = PohRecorder::new(
0,
prev_hash,
0,
bank0.clone(),
Some((4, 4)),
bank.ticks_per_slot(),
bank0.ticks_per_slot(),
&Pubkey::default(),
&Arc::new(blockstore),
&Arc::new(LeaderScheduleCache::new_from_bank(&bank)),
&Arc::new(LeaderScheduleCache::new_from_bank(&bank0)),
&Arc::new(PohConfig::default()),
Arc::new(AtomicBool::default()),
);
let start = Arc::new(Instant::now());
let working_bank = WorkingBank {
bank,
start,
min_tick_height: 2,
max_tick_height: 3,
};
poh_recorder.set_working_bank(working_bank);
poh_recorder.tick();
poh_recorder.tick();
assert_eq!(poh_recorder.tick_height, 2);
bank0.fill_bank_with_ticks();
let bank1 = Arc::new(Bank::new_from_parent(&bank0, &Pubkey::default(), 1));
poh_recorder.set_bank(&bank1);
// Check we can make two ticks without hitting min_tick_height
let remaining_ticks_to_min =
poh_recorder.working_bank.as_ref().unwrap().min_tick_height
- poh_recorder.tick_height();
for _ in 0..remaining_ticks_to_min {
poh_recorder.tick();
}
assert_eq!(poh_recorder.tick_height, remaining_ticks_to_min);
assert_eq!(
poh_recorder.tick_cache.len(),
remaining_ticks_to_min as usize
);
assert!(poh_recorder.working_bank.is_some());
// Drop entry receiver, and try to tick again. Because
// the reciever is closed, the ticks will not be drained from the cache,
// and the working bank will be cleared
drop(entry_receiver);
poh_recorder.tick();
// Check everything is cleared
assert!(poh_recorder.working_bank.is_none());
assert_eq!(poh_recorder.tick_cache.len(), 3);
// Extra +1 for the tick that happened after the drop of the entry receiver.
assert_eq!(
poh_recorder.tick_cache.len(),
remaining_ticks_to_min as usize + 1
);
}
Blockstore::destroy(&ledger_path).unwrap();
}
@@ -1238,10 +1260,12 @@ mod tests {
{
let blockstore = Blockstore::open(&ledger_path)
.expect("Expected to be able to open database ledger");
let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(2);
let bank = Arc::new(Bank::new_for_tests(&genesis_config));
let (mut poh_recorder, _entry_receiver, _record_receiver) = PohRecorder::new(
0,
Hash::default(),
0,
bank.clone(),
Some((4, 4)),
DEFAULT_TICKS_PER_SLOT,
&Pubkey::default(),
@@ -1253,8 +1277,7 @@ mod tests {
poh_recorder.tick();
poh_recorder.tick();
assert_eq!(poh_recorder.tick_cache.len(), 2);
let hash = poh_recorder.poh.lock().unwrap().hash;
poh_recorder.reset(hash, 0, Some((4, 4)));
poh_recorder.reset(bank, Some((4, 4)));
assert_eq!(poh_recorder.tick_cache.len(), 0);
}
Blockstore::destroy(&ledger_path).unwrap();
@@ -1266,10 +1289,12 @@ mod tests {
{
let blockstore = Blockstore::open(&ledger_path)
.expect("Expected to be able to open database ledger");
let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(2);
let bank = Arc::new(Bank::new_for_tests(&genesis_config));
let (mut poh_recorder, _entry_receiver, _record_receiver) = PohRecorder::new(
0,
Hash::default(),
0,
bank.clone(),
Some((4, 4)),
DEFAULT_TICKS_PER_SLOT,
&Pubkey::default(),
@@ -1281,7 +1306,7 @@ mod tests {
poh_recorder.tick();
poh_recorder.tick();
assert_eq!(poh_recorder.tick_cache.len(), 2);
poh_recorder.reset(poh_recorder.tick_cache[0].0.hash, 0, Some((4, 4)));
poh_recorder.reset(bank, Some((4, 4)));
assert_eq!(poh_recorder.tick_cache.len(), 0);
}
Blockstore::destroy(&ledger_path).unwrap();
@@ -1295,10 +1320,12 @@ mod tests {
{
let blockstore = Blockstore::open(&ledger_path)
.expect("Expected to be able to open database ledger");
let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(2);
let bank = Arc::new(Bank::new_for_tests(&genesis_config));
let (mut poh_recorder, _entry_receiver, _record_receiver) = PohRecorder::new(
0,
Hash::default(),
0,
bank.clone(),
Some((4, 4)),
DEFAULT_TICKS_PER_SLOT,
&Pubkey::default(),
@@ -1313,7 +1340,7 @@ mod tests {
poh_recorder.tick();
assert_eq!(poh_recorder.tick_cache.len(), 4);
assert_eq!(poh_recorder.tick_height, 4);
poh_recorder.reset(hash(b"hello"), 0, Some((4, 4))); // parent slot 0 implies tick_height of 3
poh_recorder.reset(bank, Some((4, 4))); // parent slot 0 implies tick_height of 3
assert_eq!(poh_recorder.tick_cache.len(), 0);
poh_recorder.tick();
assert_eq!(poh_recorder.tick_height, DEFAULT_TICKS_PER_SLOT + 1);
@@ -1332,7 +1359,7 @@ mod tests {
let (mut poh_recorder, _entry_receiver, _record_receiver) = PohRecorder::new(
0,
Hash::default(),
0,
bank.clone(),
Some((4, 4)),
bank.ticks_per_slot(),
&Pubkey::default(),
@@ -1341,15 +1368,10 @@ mod tests {
&Arc::new(PohConfig::default()),
Arc::new(AtomicBool::default()),
);
let start = Arc::new(Instant::now());
let working_bank = WorkingBank {
bank,
start,
min_tick_height: 2,
max_tick_height: 3,
};
poh_recorder.set_working_bank(working_bank);
poh_recorder.reset(hash(b"hello"), 0, Some((4, 4)));
poh_recorder.set_bank(&bank);
assert_eq!(bank.slot(), 0);
poh_recorder.reset(bank, Some((4, 4)));
assert!(poh_recorder.working_bank.is_none());
}
Blockstore::destroy(&ledger_path).unwrap();
@@ -1368,7 +1390,7 @@ mod tests {
PohRecorder::new_with_clear_signal(
0,
Hash::default(),
0,
bank.clone(),
None,
bank.ticks_per_slot(),
&Pubkey::default(),
@@ -1386,7 +1408,7 @@ mod tests {
}
#[test]
fn test_poh_recorder_reset_start_slot() {
fn test_poh_recorder_record_sets_start_slot() {
solana_logger::setup();
let ledger_path = get_tmp_ledger_path!();
{
@@ -1403,7 +1425,7 @@ mod tests {
let (mut poh_recorder, _entry_receiver, _record_receiver) = PohRecorder::new(
0,
prev_hash,
0,
bank.clone(),
Some((4, 4)),
bank.ticks_per_slot(),
&Pubkey::default(),
@@ -1413,18 +1435,11 @@ mod tests {
Arc::new(AtomicBool::default()),
);
let end_slot = 3;
let max_tick_height = (end_slot + 1) * ticks_per_slot;
let start = Arc::new(Instant::now());
let working_bank = WorkingBank {
bank: bank.clone(),
start,
min_tick_height: 1,
max_tick_height,
};
poh_recorder.set_bank(&bank);
poh_recorder.set_working_bank(working_bank);
for _ in 0..max_tick_height {
// Simulate ticking much further than working_bank.max_tick_height
let max_tick_height = poh_recorder.working_bank.as_ref().unwrap().max_tick_height;
for _ in 0..3 * max_tick_height {
poh_recorder.tick();
}
@@ -1434,8 +1449,11 @@ mod tests {
.record(bank.slot(), h1, vec![tx.into()])
.is_err());
assert!(poh_recorder.working_bank.is_none());
// Make sure the starting slot is updated
assert_eq!(poh_recorder.start_slot, end_slot);
// Even thought we ticked much further than working_bank.max_tick_height,
// the `start_slot` is still the slot of the last workign bank set by
// the earlier call to `poh_recorder.set_bank()`
assert_eq!(poh_recorder.start_slot(), bank.slot());
}
Blockstore::destroy(&ledger_path).unwrap();
}
@@ -1455,7 +1473,7 @@ mod tests {
let (mut poh_recorder, _entry_receiver, _record_receiver) = PohRecorder::new(
0,
prev_hash,
0,
bank.clone(),
None,
bank.ticks_per_slot(),
&Pubkey::default(),
@@ -1513,17 +1531,17 @@ mod tests {
let blockstore = Blockstore::open(&ledger_path)
.expect("Expected to be able to open database ledger");
let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(2);
let bank = Arc::new(Bank::new_for_tests(&genesis_config));
let prev_hash = bank.last_blockhash();
let bank0 = Arc::new(Bank::new_for_tests(&genesis_config));
let prev_hash = bank0.last_blockhash();
let (mut poh_recorder, _entry_receiver, _record_receiver) = PohRecorder::new(
0,
prev_hash,
0,
bank0.clone(),
None,
bank.ticks_per_slot(),
bank0.ticks_per_slot(),
&Pubkey::default(),
&Arc::new(blockstore),
&Arc::new(LeaderScheduleCache::new_from_bank(&bank)),
&Arc::new(LeaderScheduleCache::new_from_bank(&bank0)),
&Arc::new(PohConfig::default()),
Arc::new(AtomicBool::default()),
);
@@ -1532,23 +1550,24 @@ mod tests {
assert!(!poh_recorder.reached_leader_slot().0);
// Test that with no next leader slot in reset(), we don't reach the leader slot
poh_recorder.reset(bank.last_blockhash(), 0, None);
assert_eq!(bank0.slot(), 0);
poh_recorder.reset(bank0.clone(), None);
assert!(!poh_recorder.reached_leader_slot().0);
// Provide a leader slot one slot down
poh_recorder.reset(bank.last_blockhash(), 0, Some((2, 2)));
poh_recorder.reset(bank0.clone(), Some((2, 2)));
let init_ticks = poh_recorder.tick_height();
// Send one slot worth of ticks
for _ in 0..bank.ticks_per_slot() {
for _ in 0..bank0.ticks_per_slot() {
poh_recorder.tick();
}
// Tick should be recorded
assert_eq!(
poh_recorder.tick_height(),
init_ticks + bank.ticks_per_slot()
init_ticks + bank0.ticks_per_slot()
);
let parent_meta = SlotMeta {
@@ -1564,7 +1583,9 @@ mod tests {
assert!(!poh_recorder.reached_leader_slot().0);
// reset poh now. we should immediately be leader
poh_recorder.reset(bank.last_blockhash(), 1, Some((2, 2)));
let bank1 = Arc::new(Bank::new_from_parent(&bank0, &Pubkey::default(), 1));
assert_eq!(bank1.slot(), 1);
poh_recorder.reset(bank1.clone(), Some((2, 2)));
let (reached_leader_slot, grace_ticks, leader_slot, ..) =
poh_recorder.reached_leader_slot();
assert!(reached_leader_slot);
@@ -1573,10 +1594,10 @@ mod tests {
// Now test that with grace ticks we can reach leader slot
// Set the leader slot one slot down
poh_recorder.reset(bank.last_blockhash(), 1, Some((3, 3)));
poh_recorder.reset(bank1.clone(), Some((3, 3)));
// Send one slot worth of ticks ("skips" slot 2)
for _ in 0..bank.ticks_per_slot() {
for _ in 0..bank1.ticks_per_slot() {
poh_recorder.tick();
}
@@ -1584,7 +1605,7 @@ mod tests {
assert!(!poh_recorder.reached_leader_slot().0);
// Send the grace ticks
for _ in 0..bank.ticks_per_slot() / GRACE_TICKS_FACTOR {
for _ in 0..bank1.ticks_per_slot() / GRACE_TICKS_FACTOR {
poh_recorder.tick();
}
@@ -1592,21 +1613,24 @@ mod tests {
let (reached_leader_slot, grace_ticks, leader_slot, ..) =
poh_recorder.reached_leader_slot();
assert!(reached_leader_slot);
assert_eq!(grace_ticks, bank.ticks_per_slot() / GRACE_TICKS_FACTOR);
assert_eq!(grace_ticks, bank1.ticks_per_slot() / GRACE_TICKS_FACTOR);
assert_eq!(leader_slot, 3);
// Let's test that correct grace ticks are reported
// Set the leader slot one slot down
poh_recorder.reset(bank.last_blockhash(), 2, Some((4, 4)));
let bank2 = Arc::new(Bank::new_from_parent(&bank1, &Pubkey::default(), 2));
poh_recorder.reset(bank2.clone(), Some((4, 4)));
// send ticks for a slot
for _ in 0..bank.ticks_per_slot() {
for _ in 0..bank1.ticks_per_slot() {
poh_recorder.tick();
}
// We are not the leader yet, as expected
assert!(!poh_recorder.reached_leader_slot().0);
poh_recorder.reset(bank.last_blockhash(), 3, Some((4, 4)));
let bank3 = Arc::new(Bank::new_from_parent(&bank2, &Pubkey::default(), 3));
assert_eq!(bank3.slot(), 3);
poh_recorder.reset(bank3.clone(), Some((4, 4)));
// without sending more ticks, we should be leader now
let (reached_leader_slot, grace_ticks, leader_slot, ..) =
@@ -1618,11 +1642,12 @@ mod tests {
// Let's test that if a node overshoots the ticks for its target
// leader slot, reached_leader_slot() will return true, because it's overdue
// Set the leader slot one slot down
poh_recorder.reset(bank.last_blockhash(), 4, Some((5, 5)));
let bank4 = Arc::new(Bank::new_from_parent(&bank3, &Pubkey::default(), 4));
poh_recorder.reset(bank4.clone(), Some((5, 5)));
// Overshoot ticks for the slot
let overshoot_factor = 4;
for _ in 0..overshoot_factor * bank.ticks_per_slot() {
for _ in 0..overshoot_factor * bank4.ticks_per_slot() {
poh_recorder.tick();
}
@@ -1630,7 +1655,7 @@ mod tests {
let (reached_leader_slot, grace_ticks, leader_slot, ..) =
poh_recorder.reached_leader_slot();
assert!(reached_leader_slot);
assert_eq!(grace_ticks, overshoot_factor * bank.ticks_per_slot());
assert_eq!(grace_ticks, overshoot_factor * bank4.ticks_per_slot());
assert_eq!(leader_slot, 9);
}
Blockstore::destroy(&ledger_path).unwrap();
@@ -1648,7 +1673,7 @@ mod tests {
let (mut poh_recorder, _entry_receiver, _record_receiver) = PohRecorder::new(
0,
prev_hash,
0,
bank.clone(),
None,
bank.ticks_per_slot(),
&Pubkey::default(),
@@ -1661,13 +1686,14 @@ mod tests {
// Test that with no leader slot, we don't reach the leader tick
assert!(!poh_recorder.would_be_leader(2 * bank.ticks_per_slot()));
poh_recorder.reset(bank.last_blockhash(), 0, None);
assert_eq!(bank.slot(), 0);
poh_recorder.reset(bank.clone(), None);
assert!(!poh_recorder.would_be_leader(2 * bank.ticks_per_slot()));
// We reset with leader slot after 3 slots
let bank_slot = bank.slot() + 3;
poh_recorder.reset(bank.last_blockhash(), 0, Some((bank_slot, bank_slot)));
poh_recorder.reset(bank.clone(), Some((bank_slot, bank_slot)));
// Test that the node won't be leader in next 2 slots
assert!(!poh_recorder.would_be_leader(2 * bank.ticks_per_slot()));
@@ -1699,7 +1725,7 @@ mod tests {
let (mut poh_recorder, _entry_receiver, _record_receiver) = PohRecorder::new(
0,
bank.last_blockhash(),
0,
bank.clone(),
Some((2, 2)),
bank.ticks_per_slot(),
&Pubkey::default(),

View File

@@ -357,7 +357,6 @@ impl PohService {
mod tests {
use {
super::*,
crate::poh_recorder::WorkingBank,
rand::{thread_rng, Rng},
solana_ledger::{
blockstore::Blockstore,
@@ -396,27 +395,24 @@ mod tests {
});
let exit = Arc::new(AtomicBool::new(false));
let ticks_per_slot = bank.ticks_per_slot();
let leader_schedule_cache = Arc::new(LeaderScheduleCache::new_from_bank(&bank));
let blockstore = Arc::new(blockstore);
let (poh_recorder, entry_receiver, record_receiver) = PohRecorder::new(
bank.tick_height(),
prev_hash,
bank.slot(),
bank.clone(),
Some((4, 4)),
bank.ticks_per_slot(),
ticks_per_slot,
&Pubkey::default(),
&Arc::new(blockstore),
&Arc::new(LeaderScheduleCache::new_from_bank(&bank)),
&blockstore,
&leader_schedule_cache,
&poh_config,
exit.clone(),
);
let poh_recorder = Arc::new(Mutex::new(poh_recorder));
let start = Arc::new(Instant::now());
let working_bank = WorkingBank {
bank: bank.clone(),
start,
min_tick_height: bank.tick_height(),
max_tick_height: std::u64::MAX,
};
let ticks_per_slot = bank.ticks_per_slot();
let bank_slot = bank.slot();
// specify RUN_TIME to run in a benchmark-like mode
// to calibrate batch size
@@ -441,7 +437,7 @@ mod tests {
// send some data
let mut time = Measure::start("record");
let _ = poh_recorder.lock().unwrap().record(
bank.slot(),
bank_slot,
h1,
vec![tx.clone()],
);
@@ -478,7 +474,7 @@ mod tests {
hashes_per_batch,
record_receiver,
);
poh_recorder.lock().unwrap().set_working_bank(working_bank);
poh_recorder.lock().unwrap().set_bank(&bank);
// get some events
let mut hashes = 0;